Commit c1659544 by luoqi

chore(deploy): 新环境上线脚本化(gen-env / first-import)+ README 精简 + ASR 默认不部署

- deploy/gen-env.sh:生成 .env —— JWT 密钥就地强随机、生产开关一把设、
  业务值从环境变量注入、缺失报 __FILL_ME__;防覆盖已有(FORCE=1)。
- deploy/first-import.sh:数据首灌一条龙(seed → 存量 cold-import → 重算画像/计划),
  支持 DRY_RUN、ALLOW_PARTIAL 守卫。
- deploy/README.md:精简为维护部署手册(日常/新环境/运维/回滚/监控 5 节),
  systemd 长流程折叠;明确代码根任意目录(脚本相对定位,不写死 /opt/pac)。
- docker-compose.prod.yml:pac-asr 加 profile=asr → 默认不启(省 2G,可 --profile asr 恢复)。
- deploy-prod.sh:默认 SERVICES 去掉 pac-asr,改 WITH_ASR=1 才带上。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
parent 6e42a683
# PAC 部署运维 # PAC 部署运维手册
> W4 末:试部署阶段手动 ssh + 跑脚本;W5+ 接 CI/CD 自动化 生产用 **docker-compose 模式**(单服务器隔离)。所有部署/运维走 `deploy/` 下三个脚本,不手敲 compose
## 两种部署模式 - 代码根:**任意目录**(clone 到哪都行 —— 脚本按自身位置定位仓库,compose 用相对 context,均不写死路径;下文示例里的 `/opt/pac` 只是举例,换成你的路径即可)
- 配置:`apps/pac-service/.env` + `apps/pac-web/.env`(单一真相,`chmod 600`,永不进 git)
- compose 文件:`docker-compose.prod.yml`
| 模式 | 适用 | 文件 | | 脚本 | 干什么 | 幂等 |
|---|---|---| |---|---|---|
| **systemd 裸跑** | 宿主有 node/pnpm,想直接 systemctl 管理 | `deploy.sh` + `systemd/*.service` | | `deploy/gen-env.sh` | 生成 `.env`(密钥随机 + 生产开关 + 业务值注入 + 缺失报告) | 拒覆盖已有(`FORCE=1` 强制) |
| **docker-compose** | 宿主只装 docker,想完全隔离 | `docker-compose.prod.yml`(项目根) | | `deploy/deploy-prod.sh` | 拉码 + build + 重建容器 + 硬验证(镜像/迁移/health) | ✅ 反复跑 |
| `deploy/first-import.sh` | 数据首灌:seed + 存量 + 重算画像/计划 | ✅ 可重跑 |
两种模式共用同一份 `apps/pac-service/.env` + `apps/pac-web/.env`(单一真相)。 ---
### compose 模式启动(推荐共享服务器)
```bash ## 1. 日常:代码更新部署(最常用)
# 1. 填配置
cp apps/pac-service/.env.example apps/pac-service/.env # POSTGRES_*/JWT_*/DEEPSEEK_*/DW_*
cp apps/pac-web/.env.example apps/pac-web/.env # NEXT_PUBLIC_API_BASE_URL
# 2. 起所有容器
docker compose -f docker-compose.prod.yml \
--env-file apps/pac-service/.env \
--env-file apps/pac-web/.env \
up -d --build
# 3. 验证
docker compose -f docker-compose.prod.yml ps
curl http://127.0.0.1:3101/health
curl http://127.0.0.1:3100
```
日常操作:
```bash ```bash
# 更新代码 + 重建 + 验证(推荐 —— 确定性部署脚本) ssh <server> && cd /opt/pac
# ⚠️ 不要直接用 `up -d --build`:compose 有已知缺陷(docker/compose#9308 #9259),
# 会出现"镜像建了但容器不切新镜像"(2026-06-10 实测中招)。脚本显式 build +
# force-recreate + 部署后硬验证(镜像ID/迁移/health),任一不过即失败。
bash deploy/deploy-prod.sh bash deploy/deploy-prod.sh
# (不推荐)手动等价步骤
git pull && docker compose -f docker-compose.prod.yml --env-file apps/pac-service/.env --env-file apps/pac-web/.env up -d --build --force-recreate
# 看日志
docker compose -f docker-compose.prod.yml logs -f pac-service
# 只重启某个服务(不重建 image)
docker compose -f docker-compose.prod.yml restart pac-service
# 进容器调试
docker compose -f docker-compose.prod.yml exec pac-service sh
``` ```
⚠️ 改了 `NEXT_PUBLIC_*` 必须 `up -d --build`(build-time inline);改后端 .env 只要 `restart pac-service` `git pull` → build → force-recreate → 验证(镜像 ID 一致 / 迁移无 pending / health 200 / web 200),任一不过即失败。只动代码,**不碰数据**。期间 service 秒级重启。
---
## systemd 模式文件清单
| 文件 | 用途 | > 改了 `NEXT_PUBLIC_*`(前端 build-time 变量)必须走本脚本重 build;只改后端 `.env` 用 `docker compose -f docker-compose.prod.yml restart pac-service` 即可。
|---|---|
| `deploy.sh` | 一键部署 — `bash deploy.sh staging|production` |
| `systemd/pac-service.service` | NestJS 后端 systemd unit 模板 |
| `systemd/pac-web.service` | Next.js 前端 systemd unit 模板 |
`apps/pac-docs/content/docs/operations/deployment.mdx` 配合看 — 那份讲数据 / 监控,本目录讲部署机制。
--- ---
## systemd 模式 SOP(staging / production 通用) ## 2. 新环境从零上线(换服务器 / 首次生产)
### 1. 服务器基础环境
```bash
# Ubuntu 22.04 假设
sudo apt update && sudo apt install -y curl git docker.io
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
sudo npm i -g pnpm@9
# 创建专用账号
sudo useradd -m -s /bin/bash pac
sudo mkdir -p /var/log/pac && sudo chown pac:pac /var/log/pac
```
### 2. 起 Postgres + Redis(docker 跑法,简单部署够用) 前置:装 docker;起或接托管 Postgres+Redis;`git clone``/opt/pac`
```bash ```bash
sudo docker run -d --name pac-postgres --restart unless-stopped \ cd /opt/pac
-p 127.0.0.1:5532:5432 \
-e POSTGRES_USER=pac \
-e POSTGRES_PASSWORD=<强密码> \
-e POSTGRES_DB=pac \
-v pac-pg-data:/var/lib/postgresql/data \
postgres:15
sudo docker run -d --name pac-redis --restart unless-stopped \
-p 127.0.0.1:6479:6379 \
-v pac-redis-data:/data \
redis:7-alpine
```
生产建议用托管 RDS / 云 Redis(本地 docker 数据风险)。
### 3. 拉代码 + 配 env
```bash
sudo mkdir -p /opt/pac && sudo chown pac:pac /opt/pac
sudo -u pac git clone <your-repo> /opt/pac
# 后端 env
sudo -u pac cp /opt/pac/apps/pac-service/.env.example /opt/pac/apps/pac-service/.env
sudo -u pac vim /opt/pac/apps/pac-service/.env
# → 改 NODE_ENV / DATABASE_URL / REDIS_URL / JWT_* / DW_* / DEEPSEEK_API_KEY
# → staging 还要 PAC_ENABLE_MOCK_LOGIN=true / PAC_INCREMENTAL_CRON=30 3 * * *
# → production 要 PAC_ENABLE_MOCK_LOGIN=false / 自己的 prod 配置
sudo chmod 600 /opt/pac/apps/pac-service/.env
# 前端 env(NEXT_PUBLIC_API_BASE_URL build-time 嵌入)
sudo -u pac vim /opt/pac/apps/pac-web/.env
# NEXT_PUBLIC_API_BASE_URL=https://pac-staging.your-domain.com
# (staging / prod 必须各 build 一次,产物不可互换)
```
### 4. 安装 systemd unit
```bash
sudo cp /opt/pac/deploy/systemd/pac-service.service /etc/systemd/system/
sudo cp /opt/pac/deploy/systemd/pac-web.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable pac-service pac-web
```
允许 `pac` 用户 systemctl restart 这两个 service(deploy.sh 需要):
```bash
sudo visudo -f /etc/sudoers.d/pac
# 加一行:
# pac ALL=(root) NOPASSWD: /bin/systemctl restart pac-service, /bin/systemctl restart pac-web
```
### 5. 首次部署 # ① 生成 env —— 业务值一次传进去,JWT 密钥就地随机,生产开关自动设
PAC_DOMAIN=pac.<域名> \
POSTGRES_PASSWORD='<强密码>' REDIS_URL='redis://redis:6379' \
DW_CLICKHOUSE_URL='http://<ch>:8123' DW_CLICKHOUSE_USERNAME='jvs_pac' DW_CLICKHOUSE_PASSWORD='<密码>' \
DEEPSEEK_API_KEY='sk-...' ALERT_WEBHOOK_URL='https://qyapi.weixin.qq.com/...' \
bash deploy/gen-env.sh
# 末尾报 __FILL_ME__ 的项要补齐(或带全环境变量 + FORCE=1 重跑)
```bash # ② 部署 App
sudo -u pac bash /opt/pac/deploy/deploy.sh staging bash deploy/deploy-prod.sh
# 或
sudo -u pac bash /opt/pac/deploy/deploy.sh production
```
### 6. 验证 # ③ 数据首灌(增量自动注册,不用管)
HOST=jvs-dw DRY_RUN=1 bash deploy/first-import.sh # 先 dry-run 验翻译
HOST=jvs-dw bash deploy/first-import.sh # 真灌前:去掉 manifest cohort 的 LIMIT/OFFSET
```bash # ④ 给宿主一把生产 appSecret(换票用,交给对方后端)
curl http://localhost:3101/health # 应返 {"status":"ok"} 或类似 docker compose -f docker-compose.prod.yml exec pac-service \
curl http://localhost:3100 # Next.js 首页 HTML node dist/cli/host-admin.cli.js rotate-app-secret --host=jvs-dw
journalctl -u pac-service -n 50 -f # 看日志
journalctl -u pac-web -n 50 -f
``` ```
浏览器开 `https://<your-domain>/plans` — staging 应看到"快速登录"对话框。 **必须换成生产值**:`PAC_DOMAIN``DATABASE_URL`/`POSTGRES_PASSWORD``REDIS_URL``DW_CLICKHOUSE_*``DEEPSEEK_API_KEY``ALERT_WEBHOOK_URL`
**红线**:JWT 两把密钥切勿复用其他环境(gen-env 已随机);生产 `PAC_ENABLE_MOCK_LOGIN=false`(gen-env 已设,开着=任何人可假冒登录)。PG 密码含 `@:/?#&` 会破坏 `DATABASE_URL`,用 URL 安全字符或直接传完整 `DATABASE_URL=`
--- ---
## 日常部署(代码改了重新部署) ## 3. 常用运维
```bash ```bash
ssh deploy@<server> C="docker compose -f docker-compose.prod.yml"
sudo -u pac bash /opt/pac/deploy/deploy.sh staging
$C ps # 容器状态
$C logs -f pac-service # 跟日志
$C logs pac-service | grep -E "ERROR|dw-lag|会话失效" # 错误 / 滞后 / 鉴权告警
$C restart pac-service # 只重启后端(不重建)
$C exec pac-service sh # 进容器
# 手动跑数据 CLI(按需)
$C exec pac-service node dist/cli/sync-incremental.cli.js # 手动补一次增量
$C exec pac-service node dist/cli/recompute-persona.cli.js --host=jvs-dw
$C exec pac-service node dist/cli/refresh-clinic-names.cli.js # 刷诊所名字典
``` ```
5-10 分钟完成,期间 service 短暂中断(restart 瞬时)。零停机部署需要 blue-green,W5+ 上 **增量**由 pac-service 进程内调度器按各 `data/<host>/manifest.yaml``incremental_cron` 自动注册(jvs-dw = 每天 08:15 沪),部署后自动生效,无需外部 cron
--- ---
## 数据冷启 / 首次 DW 灌库 ## 4. 回滚
```bash
sudo -u pac bash -c 'cd /opt/pac/apps/pac-service && pnpm cold-import -- --dir=./data/jvs-dw'
# 生产首跑前:务必把 manifest.yaml 里 cohort 的 LIMIT 100 OFFSET 100 去掉(13 万患者全量)
```
完成后:
```bash ```bash
sudo -u pac bash -c 'cd /opt/pac/apps/pac-service && pnpm recompute-persona -- --host=jvs-dw' cd /opt/pac
sudo -u pac bash -c 'cd /opt/pac/apps/pac-service && pnpm recompute-plans -- --host=jvs-dw' git log --oneline -10
git checkout <last-good-sha>
bash deploy/deploy-prod.sh
``` ```
之后 cron(02:30)自动维护增量,见 `apps/pac-docs/content/docs/operations/deployment.mdx` §三 ⚠️ **DB schema 只前向**:Prisma 迁移不回退,回滚老代码可能跟新 schema 不兼容。生产改 schema 前先评估前向兼容
--- ---
## 回滚 ## 5. 监控
```bash - **健康**:`curl -s localhost:3101/pac/v1/health`(应 200)
cd /opt/pac - **每日日报**:PAC哨兵 企微群(`ALERT_WEBHOOK_URL`)每天推,看 `摄入 近7日失败 0` = 绿灯
git log --oneline -10 # 看历史 - **实时告警**:摄入失败 / DW 滞后 / 嵌入首开会话失效,自动进同一企微群
git checkout <last-known-good-sha> - **同步成功率**:`$C exec pac-service node -e "…"``sync_logs`,或看 `/admin/queues`(Bull Board)
sudo -u pac bash /opt/pac/deploy/deploy.sh staging # 同 deploy 流程,只是 git pull 被 checkout 替换
```
⚠️ **DB schema 不会回退** — Prisma migration 是只前向,回滚老代码可能跟新 schema 不兼容。 数据/监控细节见 `apps/pac-docs/content/docs/operations/deployment.mdx`
真要回滚 schema 得手写补 migration。生产 schema 变更前评估前向兼容。
--- ---
## 监控 / 告警 <details><summary>附:systemd 裸跑模式(仅历史 / 特殊场景)</summary>
详见 `apps/pac-docs/content/docs/operations/deployment.mdx` §四。关键: 宿主不想用 docker、直接 systemctl 管理时用 `deploy.sh` + `systemd/*.service`。生产统一走 compose,此模式不再维护,需要时见 git 历史或该目录旧文件。
- `journalctl -u pac-service -f | grep -E "ERROR|dw-lag"` 看错误 + 滞后告警 </details>
- `psql -U pac -d pac -c "SELECT status, count(*) FROM sync_logs WHERE started_at > now()-'1d'::interval GROUP BY status;"` 看同步成功率
- `/admin/queues` Bull Board 看队列
...@@ -24,7 +24,9 @@ main() { ...@@ -24,7 +24,9 @@ main() {
local COMPOSE=(docker compose -f docker-compose.prod.yml local COMPOSE=(docker compose -f docker-compose.prod.yml
--env-file apps/pac-service/.env --env-file apps/pac-web/.env) --env-file apps/pac-service/.env --env-file apps/pac-web/.env)
local SERVICES=(pac-migrate pac-service pac-web pac-asr) local SERVICES=(pac-migrate pac-service pac-web)
# 语音 ASR 可选(compose profile=asr),默认不部署;要语音助手用 WITH_ASR=1 bash deploy/deploy-prod.sh
[[ "${WITH_ASR:-0}" == "1" ]] && SERVICES+=(pac-asr)
if [[ "${1:-}" != "--no-pull" ]]; then if [[ "${1:-}" != "--no-pull" ]]; then
log "git pull --ff-only" log "git pull --ff-only"
......
#!/usr/bin/env bash
# ═══════════════════════════════════════════════════════════════════════
# first-import.sh — 新环境数据首灌(compose 模式)
# seed(Host 配置) → cold-import(存量) → recompute(画像 + 计划)
#
# 前置:deploy/deploy-prod.sh 已跑过(容器在跑、migration 已 deploy、.env 已配全)。
# 增量不用管:pac-service 进程内调度器会按各 manifest 的 incremental_cron 自动注册。
#
# 用法:
# bash deploy/first-import.sh # host=jvs-dw 全量首灌
# HOST=jvs-dw DRY_RUN=1 bash deploy/first-import.sh # 只 dry-run 存量(不写库,验翻译)
# HOST=jvs-dw ALLOW_PARTIAL=1 bash deploy/first-import.sh # 明知 manifest 有 LIMIT 仍继续(试灌)
# ═══════════════════════════════════════════════════════════════════════
set -euo pipefail
ROOT="${PAC_REPO_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
cd "$ROOT"
HOST="${HOST:-jvs-dw}"
COMPOSE=(docker compose -f docker-compose.prod.yml)
EX() { "${COMPOSE[@]}" exec -T pac-service "$@"; }
echo "▶ 数据首灌 host=$HOST dry_run=${DRY_RUN:-0}"
# 0) 前置:pac-service 在跑
if ! "${COMPOSE[@]}" ps pac-service 2>/dev/null | grep -qiE 'up|running'; then
echo "✗ pac-service 没在跑。先 bash deploy/deploy-prod.sh。" >&2
exit 1
fi
# 1) manifest 存在 + 全量提醒(cohort 试跑限量没去掉会只灌部分患者)
if ! EX sh -c "test -f ./data/$HOST/manifest.yaml"; then
echo "✗ 容器内无 data/$HOST/manifest.yaml(镜像里没这个 host 的配置)。" >&2
exit 1
fi
if EX sh -c "grep -nqiE 'limit|offset' ./data/$HOST/manifest.yaml"; then
echo "⚠ manifest 里发现 LIMIT/OFFSET —— 可能是 cohort 试跑限量,全量首灌前应去掉:"
EX sh -c "grep -niE 'limit|offset' ./data/$HOST/manifest.yaml" || true
if [[ "${ALLOW_PARTIAL:-0}" != "1" && "${DRY_RUN:-0}" != "1" ]]; then
echo "✗ 已中止,防止只灌部分数据。确认要限量试灌请加 ALLOW_PARTIAL=1;或去掉限量后重跑。" >&2
exit 1
fi
fi
# 2) seed —— Host 行(appId/secret、orgAliases、clinicNames)。upsert,可重复跑。
echo "▶ [1/4] seed Host 配置(upsert)…"
EX pnpm prisma:seed
# 3) 存量 cold-import
if [[ "${DRY_RUN:-0}" == "1" ]]; then
echo "▶ [2/4] cold-import --dry-run(只读 + 翻译预览,不写库)…"
EX node --max-old-space-size=8192 dist/cli/cold-import.cli.js --dir="./data/$HOST" --dry-run
echo "✓ dry-run 完成。去掉 DRY_RUN 再跑真灌。"
exit 0
fi
echo "▶ [2/4] cold-import 全量(耗时随数据量;jvs-dw 13 万患者约数十分钟)…"
EX node --max-old-space-size=8192 dist/cli/cold-import.cli.js --dir="./data/$HOST"
# 4) 重算画像 + 计划(存量灌完必须补,否则患者进不了召回池)
echo "▶ [3/4] recompute-persona…"
EX node --max-old-space-size=8192 dist/cli/recompute-persona.cli.js --host="$HOST"
echo "▶ [4/4] recompute-plans…"
EX node --max-old-space-size=8192 dist/cli/recompute-plans.cli.js --host="$HOST"
echo
echo "✓ 首灌完成。增量无需手动 —— 进程内调度器已按 manifest 注册 cron。"
echo " 验证增量已注册: ${COMPOSE[*]} logs pac-service | grep '注册 host=$HOST'"
echo " 验证数据在库: 次日看 PAC哨兵 日报,或 /admin/queues"
#!/usr/bin/env bash
# ═══════════════════════════════════════════════════════════════════════
# gen-env.sh — 生成生产/staging 环境文件(pac-service/.env + pac-web/.env)
#
# 做的事:
# 1. 从 .env.example 复制出 .env(已存在则拒绝,防清掉已配好的密钥;FORCE=1 强制)
# 2. 就地强随机生成 JWT_SECRET / JWT_REFRESH_SECRET(不外传,不复用其他环境)
# 3. 一把设好生产硬开关(NODE_ENV / mock 关 / 滞后阈值 / 监控 cron 等)
# 4. 业务值从环境变量注入(见下);未提供的留占位 __FILL_ME__ 并在末尾报告
# 5. chmod 600,并列出仍需手填的项
#
# 用法(在目标服务器仓库根或 deploy/ 下跑):
# PAC_DOMAIN=pac.example.com \
# POSTGRES_PASSWORD='...' REDIS_URL='redis://...:6379' \
# DW_CLICKHOUSE_URL='http://...:8123' DW_CLICKHOUSE_USERNAME='jvs_pac' DW_CLICKHOUSE_PASSWORD='...' \
# DEEPSEEK_API_KEY='sk-...' ALERT_WEBHOOK_URL='https://qyapi.weixin.qq.com/...' \
# ENV_KIND=production \
# bash deploy/gen-env.sh
#
# 只想先生成密钥+开关、业务值随后手填:直接 `bash deploy/gen-env.sh`,末尾按 __FILL_ME__ 补。
# ═══════════════════════════════════════════════════════════════════════
set -euo pipefail
ROOT="${PAC_REPO_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
SVC="$ROOT/apps/pac-service/.env"
SVC_EXAMPLE="$ROOT/apps/pac-service/.env.example"
WEB="$ROOT/apps/pac-web/.env"
ENV_KIND="${ENV_KIND:-production}" # production | staging
PH="__FILL_ME__"
command -v openssl >/dev/null || { echo "✗ 需要 openssl(生成密钥)" >&2; exit 1; }
command -v perl >/dev/null || { echo "✗ 需要 perl(安全写值)" >&2; exit 1; }
[[ -f "$SVC_EXAMPLE" ]] || { echo "✗ 找不到 $SVC_EXAMPLE" >&2; exit 1; }
if [[ -f "$SVC" && "${FORCE:-0}" != "1" ]]; then
echo "✋ $SVC 已存在。要重建请先备份(cp .env .env.bak)并加 FORCE=1 重跑。" >&2
exit 1
fi
cp "$SVC_EXAMPLE" "$SVC"
# set_kv KEY VALUE FILE —— 值经 %ENV 传入,perl 不解释特殊字符(URL/密码里的 /&@:? 都安全)
set_kv() {
local key="$1" val="$2" file="$3"
if grep -qE "^${key}=" "$file"; then
_K="$key" _V="$val" perl -i -pe 's/^\Q$ENV{_K}\E=.*/"$ENV{_K}=$ENV{_V}"/e' "$file"
else
printf '%s=%s\n' "$key" "$val" >> "$file"
fi
}
# ── ① 就地强随机密钥 ──
set_kv JWT_SECRET "$(openssl rand -hex 32)" "$SVC"
set_kv JWT_REFRESH_SECRET "$(openssl rand -hex 32)" "$SVC"
# ── ② 生产硬开关 ──
set_kv NODE_ENV "$ENV_KIND" "$SVC"
set_kv LOG_LEVEL info "$SVC"
if [[ "$ENV_KIND" == "production" ]]; then
set_kv PAC_ENABLE_MOCK_LOGIN false "$SVC" # ⭐ 生产必关:开着=任何人可假冒任意客服
else
set_kv PAC_ENABLE_MOCK_LOGIN true "$SVC" # staging 演示要开
fi
set_kv PAC_LAG_WARN_HOURS 24 "$SVC"
set_kv PAC_LAG_ERROR_HOURS 48 "$SVC"
set_kv PAC_LAG_MONITOR_CRON "0 * * * *" "$SVC"
set_kv PAC_STALE_SCAN_CRON "0 2 * * *" "$SVC"
set_kv PAC_INCREMENTAL_CRON "" "$SVC" # 留空=自动发现各 manifest 的 incremental_cron
set_kv PAC_WX_AIBOT_DEMO_PUSH "" "$SVC" # 生产必空:不暴露裸推送端点
# ── ③ 业务值:环境变量有则注入,无则占位 ──
inject() { # inject KEY ENVVAR_VALUE
local key="$1" v="${2:-}"
set_kv "$key" "${v:-$PH}" "$SVC"
}
inject POSTGRES_PASSWORD "${POSTGRES_PASSWORD:-}"
inject REDIS_URL "${REDIS_URL:-}"
inject DW_CLICKHOUSE_URL "${DW_CLICKHOUSE_URL:-}"
inject DW_CLICKHOUSE_DATABASE "${DW_CLICKHOUSE_DATABASE:-dw_group}"
inject DW_CLICKHOUSE_USERNAME "${DW_CLICKHOUSE_USERNAME:-}"
inject DW_CLICKHOUSE_PASSWORD "${DW_CLICKHOUSE_PASSWORD:-}"
inject DEEPSEEK_API_KEY "${DEEPSEEK_API_KEY:-}"
inject ALERT_WEBHOOK_URL "${ALERT_WEBHOOK_URL:-}"
# DATABASE_URL:显式给了就用;否则若给了库密码,按 compose 内部网络(host=postgres)拼一个
if [[ -n "${DATABASE_URL:-}" ]]; then
set_kv DATABASE_URL "$DATABASE_URL" "$SVC"
elif [[ -n "${POSTGRES_PASSWORD:-}" ]]; then
set_kv DATABASE_URL "postgresql://pac:${POSTGRES_PASSWORD}@postgres:5432/pac?schema=public" "$SVC"
else
set_kv DATABASE_URL "$PH" "$SVC"
fi
# 域名:一处给,三处用(后端 CORS/深链前缀 + 前端 API base)
if [[ -n "${PAC_DOMAIN:-}" ]]; then
set_kv CORS_ORIGINS "https://${PAC_DOMAIN}" "$SVC"
set_kv PAC_WEB_BASE_URL "https://${PAC_DOMAIN}" "$SVC"
printf 'NEXT_PUBLIC_API_BASE_URL=https://%s\n' "$PAC_DOMAIN" > "$WEB"
else
set_kv CORS_ORIGINS "$PH" "$SVC"
set_kv PAC_WEB_BASE_URL "$PH" "$SVC"
printf 'NEXT_PUBLIC_API_BASE_URL=%s\n' "$PH" > "$WEB"
fi
chmod 600 "$SVC" "$WEB"
echo "✓ 已生成:"
echo " $SVC"
echo " $WEB"
echo "✓ JWT 密钥已就地强随机;生产开关已设(NODE_ENV=$ENV_KIND, mock=$([[ $ENV_KIND == production ]] && echo false || echo true))"
echo
MISSING=$(grep -nE "$PH" "$SVC" "$WEB" || true)
if [[ -n "$MISSING" ]]; then
echo "⚠ 仍需手填(占位 $PH),补完才能上线:"
echo "$MISSING" | sed 's/^/ /'
echo
echo " 可重跑本脚本并用环境变量注入(需 FORCE=1),或直接编辑上面文件。"
else
echo "✓ 无占位残留,业务值已全部注入。"
fi
echo
echo "下一步:bash deploy/deploy-prod.sh → bash deploy/first-import.sh(见 README「新环境上线」)"
...@@ -86,7 +86,11 @@ services: ...@@ -86,7 +86,11 @@ services:
# 语音听写 ASR — SenseVoice-small(CPU)。模型不进镜像: # 语音听写 ASR — SenseVoice-small(CPU)。模型不进镜像:
# 服务器准备 ${PAC_MODELS_DIR:-../pac-models}/sensevoice/{model.int8.onnx,tokens.txt} # 服务器准备 ${PAC_MODELS_DIR:-../pac-models}/sensevoice/{model.int8.onnx,tokens.txt}
# ⚠️ 可选服务,默认【不启】(profile=asr)。只有用语音助手才需要:
# 启用 → docker compose --profile asr -f docker-compose.prod.yml up -d
# 不启用时 pac-service 的 PAC_ASR_URL 指向的服务不在,transcribe 端点会失败(不影响其它功能/启动)。
pac-asr: pac-asr:
profiles: ["asr"]
build: build:
context: . context: .
dockerfile: apps/asr-sensevoice/Dockerfile dockerfile: apps/asr-sensevoice/Dockerfile
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment