From 443a7781e541a2193d200fad9a02e9b2a9097f2a Mon Sep 17 00:00:00 2001 From: kdletters <61648117+kdletters@users.noreply.github.com> Date: Fri, 1 May 2026 17:56:16 +0800 Subject: [PATCH] Deploy admin web with release bundle --- apps/admin-web/package.json | 4 +- docs/prd/ADMIN_WEB_CONSOLE_PRD_2026-04-30.md | 5 +- ...B_CONSOLE_TECHNICAL_SOLUTION_2026-04-30.md | 20 +++-- ...ND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md | 12 ++- package.json | 4 +- scripts/admin-web-build.mjs | 73 ++++++++++++++++++ scripts/build-gate.mjs | 75 ++++++++++++------- scripts/deploy-rust-remote.sh | 45 +++++++++-- scripts/jenkins-deploy-release.sh | 2 +- 9 files changed, 189 insertions(+), 51 deletions(-) create mode 100644 scripts/admin-web-build.mjs diff --git a/apps/admin-web/package.json b/apps/admin-web/package.json index 7704e562..883c0688 100644 --- a/apps/admin-web/package.json +++ b/apps/admin-web/package.json @@ -5,8 +5,8 @@ "type": "module", "scripts": { "dev": "vite --host 127.0.0.1", - "build": "tsc --noEmit && vite build", - "typecheck": "tsc --noEmit", + "build": "node ../../scripts/admin-web-build.mjs build", + "typecheck": "node ../../scripts/admin-web-build.mjs typecheck", "preview": "vite preview --host 127.0.0.1" }, "dependencies": { diff --git a/docs/prd/ADMIN_WEB_CONSOLE_PRD_2026-04-30.md b/docs/prd/ADMIN_WEB_CONSOLE_PRD_2026-04-30.md index 49f0a93a..8135e459 100644 --- a/docs/prd/ADMIN_WEB_CONSOLE_PRD_2026-04-30.md +++ b/docs/prd/ADMIN_WEB_CONSOLE_PRD_2026-04-30.md @@ -12,6 +12,7 @@ 2. 管理数据、业务规则、权限校验和写操作继续统一走 `server-rs/crates/api-server`。 3. v1 只接管已有管理能力:管理员登录、当前管理员信息、服务/数据库概览、受控 API 调试、兑换码管理、注册邀请码管理。 4. 保持管理端清爽、可扫读、移动端可用,不在界面堆大段规则说明。 +5. 发布包内由 Web 网关把独立后台前端挂到同域 `/admin/`;Rust `api-server` 自身仍不恢复旧的 `GET /admin` 内嵌页面。 ## 2. 用户与使用场景 @@ -66,7 +67,7 @@ 3. 不新增 SpacetimeDB 表结构。 4. 不实现完整用户管理、作品审核、资产审核、充值订单后台。 5. 不实现多角色权限体系、管理员 refresh session、多端会话管理。 -6. 不保留 `GET /admin` 同源内嵌页面作为正式后台入口。 +6. 不保留 Rust `api-server` 的 `GET /admin` 同源内嵌页面作为正式后台入口;部署态 `/admin/` 只允许由独立后台前端静态产物承接。 ## 4. 页面与交互要求 @@ -144,7 +145,7 @@ API 调试页是受控接口调试台,不是通用代理工具: 5. API 调试页可通过后端调试接口访问 `/healthz`。 6. 兑换码管理页可创建/更新、停用兑换码,并展示返回记录。 7. 邀请码管理页可创建/更新注册邀请码,并展示返回记录。 -8. `GET /admin` 保持 404,不恢复旧内嵌页面。 +8. 直连 Rust `api-server` 时 `GET /admin` 保持 404,不恢复旧内嵌页面;通过发布包 Web 网关访问 `/admin/` 时返回独立后台前端。 9. `npm run check:encoding` 通过。 ## 7. 首版任务拆解 diff --git a/docs/technical/ADMIN_WEB_CONSOLE_TECHNICAL_SOLUTION_2026-04-30.md b/docs/technical/ADMIN_WEB_CONSOLE_TECHNICAL_SOLUTION_2026-04-30.md index 9bc76210..ab0a0294 100644 --- a/docs/technical/ADMIN_WEB_CONSOLE_TECHNICAL_SOLUTION_2026-04-30.md +++ b/docs/technical/ADMIN_WEB_CONSOLE_TECHNICAL_SOLUTION_2026-04-30.md @@ -4,13 +4,13 @@ 对应 PRD:[后台管理独立前端工程 PRD](../prd/ADMIN_WEB_CONSOLE_PRD_2026-04-30.md) -落地状态:`2026-04-30` 已创建 `apps/admin-web` 独立前端工程,包含登录、总览、API 调试、兑换码管理和注册邀请码管理首版页面;根工程已补 `admin-web:*` 转发脚本。 +落地状态:`2026-04-30` 已创建 `apps/admin-web` 独立前端工程,包含登录、总览、API 调试、兑换码管理和注册邀请码管理首版页面;根工程已补 `admin-web:*` 转发脚本。`2026-05-01` 起,根构建与 Ubuntu 发布包会同步构建后台前端,并在发布包 Web 网关中以同域 `/admin/` 暴露。 ## 1. 结论 后台管理端采用独立前端工程,路径固定为 `apps/admin-web`。它只负责 UI 表现、输入采集、请求发起和结果渲染;所有鉴权、聚合、写操作、SpacetimeDB 访问和业务校验继续收口在 `server-rs/crates/api-server`。 -本方案接管旧 `api-server` 内嵌 HTML/CSS/JS 页面,旧 `GET /admin` 不再挂载。后续后台入口由独立前端工程部署产物承接。 +本方案接管旧 `api-server` 内嵌 HTML/CSS/JS 页面,Rust `api-server` 直连时旧 `GET /admin` 不再挂载。部署态后台入口由发布包内 `web-server.mjs` 承接:`/admin/` 返回独立前端静态产物,`/admin/api/*` 继续反代到 `api-server`。 ## 2. 工程结构 @@ -384,12 +384,14 @@ export interface ProfileInviteCodeAdminResponse { ### 7.2 构建部署 -首版构建产物由独立后台工程输出到 `apps/admin-web/dist`。部署可以选择: +当前发布形态固定为同域 `/admin/`: -1. 独立静态站点域名,例如 `https://admin.example.com`。 -2. 与主站同域不同路径,由网关把后台静态资源和 `/admin/api/*` 分别路由到正确目标。 +1. 本地单独执行 `npm run admin-web:build` 时,后台构建产物默认输出到 `apps/admin-web/dist`。 +2. 根工程执行 `npm run build` 时,会先构建主前端,再构建后台前端;任一构建失败或输出 warning 都会让构建门禁失败。 +3. Ubuntu 发布包执行 `npm run deploy:rust:remote` 时,后台前端以 Vite `--base /admin/` 构建到发布包 `web/admin/`。 +4. 发布包 `web-server.mjs` 对 `/admin` 返回 301 到 `/admin/`,对 `/admin/` 与 `/admin/*` 提供后台 SPA fallback,对 `/admin/api/*` 优先反代到 `api-server`。 -无论哪种方式,`server-rs` 仍然是唯一管理 API 后端。 +该形态不新增后台静态端口和后台专用后端。`server-rs` 仍然是唯一管理 API 后端,后台前端不直连 SpacetimeDB。 ### 7.3 后台工程脚本 @@ -399,8 +401,8 @@ export interface ProfileInviteCodeAdminResponse { { "scripts": { "dev": "vite --host 127.0.0.1", - "build": "tsc --noEmit && vite build", - "typecheck": "tsc --noEmit", + "build": "node ../../scripts/admin-web-build.mjs build", + "typecheck": "node ../../scripts/admin-web-build.mjs typecheck", "preview": "vite preview --host 127.0.0.1" } } @@ -408,6 +410,8 @@ export interface ProfileInviteCodeAdminResponse { 如果后续接入根 npm workspace,再在根 `package.json` 增加转发脚本;本轮不要为了后台工程强行重排现有前端脚本。 +当前工程没有启用 npm workspace,因此后台构建脚本必须从仓库根目录调用 root toolchain。`scripts/admin-web-build.mjs` 统一执行 `tsc --noEmit -p apps/admin-web/tsconfig.json` 与 Vite 构建,避免 `npm --prefix apps/admin-web` 在子目录找不到 `tsc`。 + 当前根工程同步提供以下转发脚本: 1. `npm run admin-web:dev` diff --git a/docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md b/docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md index 2dbc5d62..c56afa75 100644 --- a/docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md +++ b/docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md @@ -137,11 +137,11 @@ npm run deploy:rust:remote 1. 在仓库根目录创建 `build/`。 2. 在 `build/` 下创建当前时间命名的目标目录,例如 `build/20260422-153000/`。 -3. 使用 Vite 构建前端 release 到目标目录的 `web/`。 +3. 使用 Vite 构建主前端 release 到目标目录的 `web/`,并构建后台管理前端 release 到 `web/admin/`;后台构建固定使用 `/admin/` 作为 Vite base。 4. 执行 `cargo build -p api-server --release --target x86_64-unknown-linux-gnu --manifest-path server-rs/Cargo.toml`,并把 `api-server` 复制到目标目录。 5. 执行 `cargo build -p spacetime-module --release --target wasm32-unknown-unknown --manifest-path server-rs/Cargo.toml`,并把 `spacetime_module.wasm` 复制到目标目录。 6. 把仓库根目录的 `.env` 与 `.env.local` 分别复制到目标目录根部和目标目录的 `web/` 下;复制后统一移除 UTF-8 BOM 与 CRLF,并把 `GENARRATIVE_SPACETIME_DATABASE` 覆盖为本次 `--database` 参数,避免 Jenkins 工作区里残留的旧 `.env.local` 覆盖发布包目标库。 -7. 在目标目录写入 `web-server.mjs`,用于托管 `web/` 并把 `/api/*`、`/generated-*`、`/healthz` 反代到本包内的 `api-server`。 +7. 在目标目录写入 `web-server.mjs`,用于托管 `web/` 与 `web/admin/`;其中 `/admin` 跳转到 `/admin/`,`/admin/` 提供后台 SPA,`/admin/api/*`、`/api/*`、`/generated-*`、`/healthz` 反代到本包内的 `api-server`。 8. 在目标目录写入 `start.sh` 与 `stop.sh`;`start.sh` 会先按 `KEY=value` 子集加载发布目录根部的 `.env`、`.env.local`,兼容 UTF-8 BOM 与 CRLF,再回退到构建时通过 `--database`、`--api-port`、`--web-host`、`--web-port`、`--spacetime-host`、`--spacetime-port` 写入的默认值,其中 Web 默认只监听 `127.0.0.1`;并默认导出 `NO_COLOR=1` 与 `CARGO_TERM_COLOR=never`,避免 ANSI 控制码写入日志文件;同时按 Ubuntu 发布环境使用发布目录内 `.spacetimedb/` 作为 root-dir,不再额外设置 `--data-dir`,启动前先执行 `sync_ubuntu_spacetime_install`,优先从 `/usr/.local/share/spacetime/bin//spacetimedb-cli` 或 `$HOME/.local/share/spacetime/bin//spacetimedb-cli` 同步到 `.spacetimedb/bin/current/spacetimedb-cli`,当前线上 `spacetime` 入口为 `/usr/local/bin/spacetime`;启动参数为 `spacetime --root-dir ./.spacetimedb start --edition standalone --listen-addr :`,探活必须确认 `server ping` 输出包含 `Server is online:`;普通启动先无清库发布,若 publish 输出可判定为 schema 冲突,则自动导出旧库、清库发布新 wasm、导入回灌;如果以 `--clear-database` 启动,则内部 `spacetime publish` 会追加 `-c=on-conflict`,代表人工确认清库,不触发自动回灌。 9. 默认执行 `scp -r -i ~\.ssh\dsk.pem build/ ubuntu@82.157.175.59:/home/ubuntu/genarrative/` 上传发布包。 @@ -157,7 +157,9 @@ build// ├─ .env.local ├─ web/ │ ├─ .env -│ └─ .env.local +│ ├─ .env.local +│ └─ admin/ +│ └─ index.html ├─ api-server ├─ spacetime_module.wasm ├─ migration-bootstrap-secret.txt @@ -178,6 +180,8 @@ npm run build:rust:ubuntu -- --database genarrative-dev --web-host 127.0.0.1 --w npm run build:rust:ubuntu -- --skip-upload ``` +`--skip-web-build` 会同时跳过主前端和后台管理前端构建,仅用于调试已有发布包内容;正式发布不应使用该参数。 + 目标服务器启动: ```bash @@ -186,7 +190,7 @@ cd build/ ./stop.sh ``` -如果后续通过 Jenkins 的部署脚本把发布包覆盖到固定部署目录,部署阶段默认只替换 `web/`、`api-server`、`spacetime_module.wasm`、`migration-bootstrap-secret.txt`、`scripts/`、`.env*`、`start.sh`、`stop.sh`、`web-server.mjs`、`README.md` 等发布产物;文件产物使用普通复制,`web/`、`scripts/` 等目录产物递归复制,不会删除部署目录中的 `.spacetimedb/`、`logs/`、`run/`、`deploy-state/`、`database-migrations/` 这类运行态目录。 +如果后续通过 Jenkins 的部署脚本把发布包覆盖到固定部署目录,部署阶段默认只替换 `web/`、`api-server`、`spacetime_module.wasm`、`migration-bootstrap-secret.txt`、`scripts/`、`.env*`、`start.sh`、`stop.sh`、`web-server.mjs`、`README.md` 等发布产物;后台管理前端位于 `web/admin/`,随 `web/` 一并覆盖。文件产物使用普通复制,`web/`、`scripts/` 等目录产物递归复制,不会删除部署目录中的 `.spacetimedb/`、`logs/`、`run/`、`deploy-state/`、`database-migrations/` 这类运行态目录。 安全边界: diff --git a/package.json b/package.json index 4aa815fe..6cbe731e 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,8 @@ "dev:rust:logs": "node scripts/run-bash-script.mjs scripts/spacetime-logs-local.sh", "dev:web": "node scripts/dev-web-rust.mjs", "admin-web:dev": "npm --prefix apps/admin-web run dev --", - "admin-web:build": "npm --prefix apps/admin-web run build --", - "admin-web:typecheck": "npm --prefix apps/admin-web run typecheck --", + "admin-web:build": "node scripts/admin-web-build.mjs build", + "admin-web:typecheck": "node scripts/admin-web-build.mjs typecheck", "admin-web:preview": "npm --prefix apps/admin-web run preview --", "spacetime:publish:maincloud": "node scripts/run-bash-script.mjs scripts/spacetime-publish-maincloud.sh", "spacetime:generate": "node scripts/generate-spacetime-bindings.mjs", diff --git a/scripts/admin-web-build.mjs b/scripts/admin-web-build.mjs new file mode 100644 index 00000000..2b1620f5 --- /dev/null +++ b/scripts/admin-web-build.mjs @@ -0,0 +1,73 @@ +import {spawnSync} from 'node:child_process'; +import {dirname, resolve} from 'node:path'; +import {fileURLToPath} from 'node:url'; + +const scriptDir = dirname(fileURLToPath(import.meta.url)); +const repoRoot = resolve(scriptDir, '..'); +const adminWebDir = resolve(repoRoot, 'apps/admin-web'); +const adminTsconfigPath = resolve(adminWebDir, 'tsconfig.json'); +const adminViteConfigPath = resolve(adminWebDir, 'vite.config.ts'); +const tscBinPath = resolve(repoRoot, 'node_modules/typescript/bin/tsc'); +const viteCliPath = resolve(scriptDir, 'vite-cli.mjs'); + +const command = process.argv[2] ?? 'build'; +const extraArgs = process.argv.slice(3); + +function usage() { + console.error('用法: node scripts/admin-web-build.mjs [vite-build-args...]'); +} + +function runNodeScript(label, args) { + console.log(`[admin-web] ${label}`); + const result = spawnSync(process.execPath, args, { + cwd: repoRoot, + encoding: 'utf8', + }); + + if (result.stdout) { + process.stdout.write(result.stdout); + } + + if (result.stderr) { + process.stderr.write(result.stderr); + } + + if (result.error) { + console.error(`[admin-web] ${label} failed to start: ${result.error.message}`); + process.exit(1); + } + + if (result.signal) { + console.error(`[admin-web] ${label} was terminated by signal ${result.signal}`); + process.exit(1); + } + + if ((result.status ?? 0) !== 0) { + process.exit(result.status ?? 1); + } +} + +function runTypecheck() { + runNodeScript('typecheck', [ + tscBinPath, + '--noEmit', + '-p', + adminTsconfigPath, + ]); +} + +if (command === 'typecheck') { + runTypecheck(); +} else if (command === 'build') { + runTypecheck(); + runNodeScript('vite build', [ + viteCliPath, + 'build', + '--config', + adminViteConfigPath, + ...extraArgs, + ]); +} else { + usage(); + process.exit(1); +} diff --git a/scripts/build-gate.mjs b/scripts/build-gate.mjs index d5e7244d..70142ee4 100644 --- a/scripts/build-gate.mjs +++ b/scripts/build-gate.mjs @@ -2,39 +2,60 @@ import {spawnSync} from 'node:child_process'; import {fileURLToPath} from 'node:url'; const viteCliPath = fileURLToPath(new URL('./vite-cli.mjs', import.meta.url)); -const args = [viteCliPath, 'build', ...process.argv.slice(2)]; +const adminWebBuildPath = fileURLToPath(new URL('./admin-web-build.mjs', import.meta.url)); +const forwardedArgs = process.argv.slice(2); -const result = spawnSync(process.execPath, args, { - cwd: process.cwd(), - encoding: 'utf8', -}); - -if (result.stdout) { - process.stdout.write(result.stdout); -} - -if (result.stderr) { - process.stderr.write(result.stderr); -} - -if ((result.status ?? 0) !== 0) { - process.exit(result.status ?? 1); -} - -const warningPattern = /\bwarn(?:ing)?\b/i; -const ignoredWarningPatterns = [ - /ExperimentalWarning/u, +const results = [ + runBuildStep('web', [viteCliPath, 'build', ...forwardedArgs]), + runBuildStep('admin-web', [adminWebBuildPath, 'build']), ]; -const warningLines = `${result.stdout ?? ''}\n${result.stderr ?? ''}` - .split(/\r?\n/u) - .map(line => line.trim()) - .filter(line => line.length > 0) - .filter(line => warningPattern.test(line)) - .filter(line => !ignoredWarningPatterns.some(pattern => pattern.test(line))); +const failedResult = results.find(result => result.error || result.signal || (result.status ?? 0) !== 0); +if (failedResult) { + if (failedResult.error) { + console.error(`Build gate failed to start a build step: ${failedResult.error.message}`); + } else if (failedResult.signal) { + console.error(`Build gate step was terminated by signal ${failedResult.signal}`); + } + process.exit(failedResult.status ?? 1); +} + +const warningLines = results.flatMap((result) => collectWarningLines(result)); if (warningLines.length > 0) { console.error('Build gate failed because warnings were emitted:'); [...new Set(warningLines)].forEach(line => console.error(`- ${line}`)); process.exit(1); } + +function runBuildStep(label, args) { + console.log(`[build-gate] ${label}`); + const result = spawnSync(process.execPath, args, { + cwd: process.cwd(), + encoding: 'utf8', + }); + + if (result.stdout) { + process.stdout.write(result.stdout); + } + + if (result.stderr) { + process.stderr.write(result.stderr); + } + + return result; +} + +function collectWarningLines(result) { + const warningPattern = /\bwarn(?:ing)?\b/i; + const ignoredWarningPatterns = [ + /ExperimentalWarning/u, + ]; + + return `${result.stdout ?? ''}\n${result.stderr ?? ''}` + .split(/\r?\n/u) + .map(line => line.trim()) + .filter(line => line.length > 0) + .filter(line => warningPattern.test(line)) + .filter(line => !ignoredWarningPatterns.some(pattern => pattern.test(line))); +} diff --git a/scripts/deploy-rust-remote.sh b/scripts/deploy-rust-remote.sh index 22bd13d8..c8dfdfef 100644 --- a/scripts/deploy-rust-remote.sh +++ b/scripts/deploy-rust-remote.sh @@ -310,6 +310,7 @@ fi TARGET_DIR="${BUILD_ROOT}/${BUILD_NAME}" WEB_DIR="${TARGET_DIR}/web" +ADMIN_WEB_DIR="${WEB_DIR}/admin" API_BINARY_SOURCE="${SERVER_RS_DIR}/target/x86_64-unknown-linux-gnu/release/api-server" WASM_SOURCE="${SERVER_RS_DIR}/target/wasm32-unknown-unknown/release/spacetime_module.wasm" @@ -364,6 +365,12 @@ if [[ "${SKIP_WEB_BUILD}" -ne 1 ]]; then cd "${REPO_ROOT}" node scripts/vite-cli.mjs build --outDir "${WEB_DIR}" --emptyOutDir ) + + echo "[deploy:rust] 构建后台 Vite release -> ${ADMIN_WEB_DIR}" + ( + cd "${REPO_ROOT}" + MSYS2_ARG_CONV_EXCL="--base=" node scripts/admin-web-build.mjs build --base=/admin/ --outDir "${ADMIN_WEB_DIR}" --emptyOutDir + ) fi if [[ "${SKIP_API_BUILD}" -ne 1 ]]; then @@ -421,11 +428,14 @@ import {fileURLToPath} from 'node:url'; const releaseDir = path.dirname(fileURLToPath(import.meta.url)); const webRoot = path.join(releaseDir, 'web'); +const adminWebRoot = path.join(webRoot, 'admin'); const webHost = process.env.GENARRATIVE_WEB_HOST || '127.0.0.1'; const webPort = Number(process.env.GENARRATIVE_WEB_PORT || '3000'); const apiTarget = new URL(process.env.GENARRATIVE_API_TARGET || 'http://127.0.0.1:8082'); const indexPath = path.join(webRoot, 'index.html'); +const adminIndexPath = path.join(adminWebRoot, 'index.html'); const proxyPrefixes = [ + '/admin/api', '/api/', '/api', '/generated-character-drafts', @@ -466,11 +476,11 @@ function sendFile(response, filePath) { .pipe(response); } -function serveStatic(request, response, pathname) { +function serveStaticFromRoot(response, pathname, rootDir, fallbackIndexPath) { const decodedPath = decodeURIComponent(pathname); const relativePath = decodedPath === '/' ? '/index.html' : decodedPath; - const filePath = path.normalize(path.join(webRoot, relativePath)); - const safeRelativePath = path.relative(webRoot, filePath); + const filePath = path.normalize(path.join(rootDir, relativePath)); + const safeRelativePath = path.relative(rootDir, filePath); if (safeRelativePath.startsWith('..') || path.isAbsolute(safeRelativePath)) { response.writeHead(403, {'content-type': 'text/plain; charset=utf-8'}); @@ -480,12 +490,21 @@ function serveStatic(request, response, pathname) { const resolvedFilePath = fs.existsSync(filePath) && fs.statSync(filePath).isFile() ? filePath - : indexPath; + : fallbackIndexPath; response.writeHead(200, {'content-type': contentTypeFor(resolvedFilePath)}); sendFile(response, resolvedFilePath); } +function serveStatic(request, response, pathname) { + serveStaticFromRoot(response, pathname, webRoot, indexPath); +} + +function serveAdminStatic(response, pathname) { + const adminPath = pathname === '/admin/' ? '/' : pathname.replace(/^\/admin/u, ''); + serveStaticFromRoot(response, adminPath, adminWebRoot, adminIndexPath); +} + function proxyToApi(request, response) { const targetUrl = new URL(request.url || '/', apiTarget); const proxyRequest = http.request( @@ -522,6 +541,17 @@ const server = http.createServer((request, response) => { return; } + if (url.pathname === '/admin') { + response.writeHead(301, {location: '/admin/'}); + response.end(); + return; + } + + if (url.pathname === '/admin/' || url.pathname.startsWith('/admin/')) { + serveAdminStatic(response, url.pathname); + return; + } + serveStatic(request, response, url.pathname); }); @@ -1189,7 +1219,7 @@ cat >"${TARGET_DIR}/README.md" <<'EOF' ## 内容 - \`.env\` / \`.env.local\`:从仓库根目录复制的环境文件,同时各保留一份到 \`web/\` -- \`web/\`:Vite release 静态资源 +- \`web/\`:主前端 Vite release 静态资源,\`web/admin/\` 为后台管理前端静态资源 - \`api-server\`:x86_64-unknown-linux-gnu release 可执行文件 - \`spacetime_module.wasm\`:wasm32-unknown-unknown release 模块 - \`migration-bootstrap-secret.txt\`:本发布包 wasm 编译时注入的迁移引导密钥;服务器 \`start.sh\` 发布时会显示,迁移授权完成后可删除 @@ -1211,6 +1241,11 @@ cat >"${TARGET_DIR}/README.md" <<'EOF' 默认启动会先尝试无清库发布;如果 SpacetimeDB 返回 schema 冲突,\`start.sh\` 会把旧库导出到 \`database-migrations//\`,随后清库发布新 wasm,并用 \`--replace-existing\` 导入回灌。 +## 入口 + +- 主站:\`http://:/\` +- 后台:\`http://:/admin/\` + ## 环境变量 - 启动时会先加载发布目录根部的 \`.env\` 与 \`.env.local\`,再回退到脚本内默认值。 diff --git a/scripts/jenkins-deploy-release.sh b/scripts/jenkins-deploy-release.sh index 3e3155c4..91d49534 100644 --- a/scripts/jenkins-deploy-release.sh +++ b/scripts/jenkins-deploy-release.sh @@ -10,7 +10,7 @@ usage() { 说明: 1. 如果部署目录已有旧版本且存在 stop.sh,则先执行旧版本 stop.sh。 2. 仅删除并替换发布产物文件或目录,保留部署目录中的运行数据目录。 - 3. 把指定发布目录中的白名单产物复制覆盖到部署目录。 + 3. 把指定发布目录中的白名单产物复制覆盖到部署目录,后台前端随 web/admin/ 一并覆盖。 4. 如指定 --clear-database,则以清库模式执行新版本 start.sh。 5. 默认允许新版本 start.sh 在 schema 冲突时自动导出、清库发布、导入回灌。 6. 最后执行新版本 start.sh。