import { defineComponent, computed, ref, reactive, watch, mergeProps, useSSRContext } from 'vue'; import { ssrRenderAttrs, ssrInterpolate, ssrRenderClass, ssrRenderStyle, ssrRenderAttr, ssrRenderList, ssrRenderComponent, ssrIncludeBooleanAttr, ssrLooseContain, ssrLooseEqual, ssrRenderDynamicModel } from 'vue/server-renderer'; import { u as useApi } from './useApi-D_CttG2t.mjs'; import { u as useToast } from './useToast-CS21XT61.mjs'; import { u as useAuthStore } from './auth-QFkCoU1C.mjs'; import { _ as _export_sfc, b as useRoute$1, a as useRouter } from './server.mjs'; import './useLoading-D4zrSa2m.mjs'; import 'pinia'; import '../nitro/nitro.mjs'; import 'node:http'; import 'node:https'; import 'node:events'; import 'node:buffer'; import 'node:fs'; import 'node:path'; import 'node:crypto'; import 'node:url'; import '../routes/renderer.mjs'; import 'vue-bundle-renderer/runtime'; import 'unhead/server'; import 'devalue'; import 'unhead/utils'; import 'perfect-debounce'; const _sfc_main$2 = /* @__PURE__ */ defineComponent({ __name: "AppDeployTab", __ssrInlineRender: true, props: { appName: {}, defaultImage: {}, deployments: {}, envs: {} }, emits: ["refresh", "configure"], setup(__props, { expose: __expose, emit: __emit }) { const props = __props; useApi(); useToast(); useAuthStore(); useRouter(); const REGIONS = ["中国", "美国", "东亚", "东南亚", "南亚", "中东", "欧洲", "俄罗斯", "非洲", "南美", "北美", "大洋洲", "巴西", "印度"]; function dbName(dsn) { const m = (dsn || "").match(/\/([^/?]+)\?/); return m ? m[1] : dsn ? "(自定义)" : "未配置"; } function isPinned(tag) { const t = (tag || "").trim(); return !!t && t !== "latest"; } const envSavingId = ref(0); const formDlg = ref(null); const dIsEditing = ref(false); const dMode = ref("existing"); const dForm = reactive({ id: 0, name: "", region: "", env_id: 0, base_url: "", service_dsn: "", deploy_dir: "", image_name: "", image_tag: "", remark: "", enabled: true }); const testPassed = ref(false); const testing = ref(false); const testResult = reactive({ show: false, apiOk: false, apiMsg: "", dbOk: false, dbMsg: "" }); const availableRegions = computed(() => { const used = new Set(props.deployments.filter((d) => d.id !== dForm.id).map((d) => d.region).filter(Boolean)); return REGIONS.filter((r) => !used.has(r) || r === dForm.region); }); function resetTest() { testPassed.value = false; testResult.show = false; } function openNew() { dIsEditing.value = false; dMode.value = "existing"; Object.assign(dForm, { id: 0, name: "", region: "", env_id: 0, base_url: "", service_dsn: "", deploy_dir: "", image_name: props.defaultImage, image_tag: "", remark: "", enabled: true }); resetTest(); formDlg.value?.showModal(); } ref(null); ref(null); const up = reactive({ id: 0, label: "", envName: "", deployDir: "", tag: "", tags: [], tagMode: "list", tagsLoading: false, tagsError: "", log: "", show: false, running: false, ok: false, canRun: false, btnText: "开始升级" }); ref(null); const az = reactive({ appId: 0, label: "", start: "", end: "", mode: "", steps: [], curIndex: 0, curTotal: 0, curName: "", pct: 0, errMsg: "", report: null, running: false, btnText: "开始分析" }); __expose({ openNew }); return (_ctx, _push, _parent, _attrs) => { _push(`
区域部署
每个区域一份部署,各自连自己的业务库。「升级」会 SSH 到绑定的环境节点,在部署目录执行 docker compose pull & up -d。
`); if (!__props.deployments.length) { _push(`
该应用还没有部署,点右上「+ 部署新环境」添加。
`); } else { _push(``); } _push(``); ssrRenderList(__props.deployments, (d) => { _push(`
部署${ssrInterpolate(d.name || "部署#" + d.id)}`); if (d.status === "pending") { _push(`待部署`); } else { _push(`已接入`); } if (!d.enabled) { _push(`停用`); } else { _push(``); } _push(`
#${ssrInterpolate(d.id)} · ${ssrInterpolate(d.region || "未指定区域")} · 业务库 ${ssrInterpolate(dbName(d.service_dsn))}
环境节点
当前版本
${ssrInterpolate(d.image_tag || "latest")}`); if (!isPinned(d.image_tag)) { _push(`未固定`); } else { _push(``); } _push(`
部署目录
${ssrInterpolate(d.deploy_dir || "未填")}
`); }); _push(`
`); }; } }); const _sfc_setup$2 = _sfc_main$2.setup; _sfc_main$2.setup = (props, ctx) => { const ssrContext = useSSRContext(); (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/AppDeployTab.vue"); return _sfc_setup$2 ? _sfc_setup$2(props, ctx) : void 0; }; const __nuxt_component_0 = /* @__PURE__ */ Object.assign(_export_sfc(_sfc_main$2, [["__scopeId", "data-v-b8dfd02c"]]), { __name: "AppDeployTab" }); const _sfc_main$1 = /* @__PURE__ */ defineComponent({ __name: "AppModuleTab", __ssrInlineRender: true, props: { appId: {}, depName: {} }, setup(__props) { useApi(); useToast(); const catalog = ref([]); const curModule = ref(""); const values = reactive({}); const dataModules = reactive(/* @__PURE__ */ new Set()); const show = reactive({}); const loading = ref(false); const saving = ref(false); const dbError = ref(""); const curDef = computed(() => catalog.value.find((m) => m.module === curModule.value) || null); const cur = computed(() => values[curModule.value] || {}); const groups = computed(() => { const order = []; const map = /* @__PURE__ */ new Map(); for (const m of catalog.value) { if (!map.has(m.group)) { map.set(m.group, []); order.push(m.group); } map.get(m.group).push(m); } return order.map((name) => ({ name, modules: map.get(name) })); }); return (_ctx, _push, _parent, _attrs) => { _push(`
业务功能配置
登录 / 短信 / 邮件 / 支付等应用独立配置,保存在「${ssrInterpolate(__props.depName)}」这个部署的业务库(不进公共库)。密钥字段加密落库,显示时脱敏。
`); if (dbError.value) { _push(`
无法连接应用业务库:${ssrInterpolate(dbError.value)}
待部署的应用先完成部署、或检查业务库 DSN 连通后再配置。
`); } else { _push(``); } if (loading.value) { _push(`
`); } else { _push(`
`); if (curDef.value) { _push(`
${ssrInterpolate(curDef.value.name)}
`); if (curDef.value.desc) { _push(`
${ssrInterpolate(curDef.value.desc)}
`); } else { _push(``); } _push(`
`); if (dataModules.has(curModule.value)) { _push(``); } else { _push(``); } _push(`
`); ssrRenderList(curDef.value.fields, (f) => { _push(`
`); if (f.type === "bool") { _push(``); } else if (f.type === "select") { _push(``); } else if (f.type === "textarea") { _push(``); } else if (f.type === "password") { _push(`
`); } else { _push(``); } if (f.desc) { _push(`${ssrInterpolate(f.desc)}`); } else { _push(``); } _push(`
`); }); _push(`
`); } else { _push(`
暂无可配置的业务模块。
`); } _push(`
`); } _push(``); }; } }); const _sfc_setup$1 = _sfc_main$1.setup; _sfc_main$1.setup = (props, ctx) => { const ssrContext = useSSRContext(); (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/AppModuleTab.vue"); return _sfc_setup$1 ? _sfc_setup$1(props, ctx) : void 0; }; const __nuxt_component_1 = /* @__PURE__ */ Object.assign(_export_sfc(_sfc_main$1, [["__scopeId", "data-v-ff0e4b71"]]), { __name: "AppModuleTab" }); const _sfc_main = /* @__PURE__ */ defineComponent({ __name: "[name]", __ssrInlineRender: true, setup(__props) { const { consoleApi } = useApi(); const { error } = useToast(); const route = useRoute$1(); useRouter(); const appName = computed(() => String(route.params.name || "")); const product = ref(null); const deployments = ref([]); const envs = ref([]); const loading = ref(false); const tab = ref("deps"); const curDep = ref(0); const deployTab = ref(null); const okCount = computed(() => deployments.value.filter((d) => d.status !== "pending").length); const pendingCount = computed(() => deployments.value.filter((d) => d.status === "pending").length); const curDepName = computed(() => { const d = deployments.value.find((x) => x.id === curDep.value); return d ? d.name || "部署#" + d.id : ""; }); const versionSpread = computed(() => { const tags = [...new Set(deployments.value.filter((d) => d.status !== "pending").map((d) => d.image_tag || "latest"))]; return tags.length ? tags.join(" · ") : "—"; }); async function loadAll() { loading.value = true; try { const [ps, ds, es] = await Promise.all([ consoleApi("products/list", {}), consoleApi("apps/list", {}), consoleApi("envs/list", {}) ]); product.value = (ps ?? []).find((p) => p.name === appName.value) ?? null; deployments.value = (ds ?? []).filter((d) => d.app_name === appName.value); envs.value = es ?? []; if (!deployments.value.some((d) => d.id === curDep.value)) { const first = deployments.value.find((d) => d.status !== "pending") ?? deployments.value[0]; curDep.value = first?.id ?? 0; } } catch (e) { error(e?.message ?? "加载应用详情失败"); } finally { loading.value = false; } } function onConfigure(id) { curDep.value = id; tab.value = "modules"; } ref(null); const pForm = reactive({ id: 0, name: "", image_name: "", remark: "", enabled: true }); watch(appName, loadAll); return (_ctx, _push, _parent, _attrs) => { const _component_AppDeployTab = __nuxt_component_0; const _component_AppModuleTab = __nuxt_component_1; _push(`
/${ssrInterpolate(appName.value)}
`); if (loading.value) { _push(`
`); } else if (!product.value) { _push(`

找不到应用「${ssrInterpolate(appName.value)}」

`); } else { _push(`
${ssrInterpolate(product.value.name)} ${ssrInterpolate(product.value.enabled ? "启用" : "停用")}🐳 ${ssrInterpolate(product.value.image_name || "未设置镜像")}
${ssrInterpolate(product.value.remark || "—")}
部署
${ssrInterpolate(deployments.value.length)}个区域
已接入
${ssrInterpolate(okCount.value)}
待部署
${ssrInterpolate(pendingCount.value)}
版本分布
${ssrInterpolate(versionSpread.value)}
`); if (tab.value === "modules") { _push(`
配置目标
`); ssrRenderList(deployments.value, (d) => { _push(``); }); _push(``); if (!deployments.value.length) { _push(`该应用还没有部署,无处存放配置`); } else { _push(``); } _push(`
配置存在该部署自己的业务库里,各区域互不影响
`); } else { _push(``); } _push(ssrRenderComponent(_component_AppDeployTab, { style: tab.value === "deps" ? null : { display: "none" }, ref_key: "deployTab", ref: deployTab, "app-name": appName.value, "default-image": product.value.image_name || "", deployments: deployments.value, envs: envs.value, onRefresh: loadAll, onConfigure }, null, _parent)); if (tab.value === "modules" && curDep.value) { _push(ssrRenderComponent(_component_AppModuleTab, { key: "m" + curDep.value, "app-id": curDep.value, "dep-name": curDepName.value }, null, _parent)); } else { _push(``); } if (tab.value === "modules" && !curDep.value) { _push(`
先在「部署环境」里创建一个部署,才能配置。
`); } else { _push(``); } _push(``); } _push(``); }; } }); const _sfc_setup = _sfc_main.setup; _sfc_main.setup = (props, ctx) => { const ssrContext = useSSRContext(); (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("pages/apps/[name].vue"); return _sfc_setup ? _sfc_setup(props, ctx) : void 0; }; const _name_ = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-3e1700b0"]]); export { _name_ as default }; //# sourceMappingURL=_name_-3YudcsGQ.mjs.map