47 changed files with 3993 additions and 479 deletions
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@ |
|||
{"id":"dev","timestamp":1782613791473} |
|||
@ -0,0 +1 @@ |
|||
{"id":"dev","timestamp":1782613791473,"prerendered":[]} |
|||
@ -0,0 +1,17 @@ |
|||
{ |
|||
"date": "2026-06-28T02:29:52.480Z", |
|||
"preset": "nitro-dev", |
|||
"framework": { |
|||
"name": "nuxt", |
|||
"version": "4.4.8" |
|||
}, |
|||
"versions": { |
|||
"nitro": "2.13.4" |
|||
}, |
|||
"dev": { |
|||
"pid": 21165, |
|||
"workerAddress": { |
|||
"socketPath": "/var/folders/1w/40ft_rjj7qx8xn21kb1lj51r0000gn/T/nitro-worker-21165-1-1-132.sock" |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
{ |
|||
"_hash": "EtElel9LL0Jf8LAcFEfkc2DWVGnfU9BxHYuI4FliSL8", |
|||
"project": { |
|||
"rootDir": "/Users/liwei/work/go/yunyan/yunyan-sas/apps/admin" |
|||
}, |
|||
"versions": { |
|||
"nuxt": "4.4.8" |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
{ |
|||
"pid": 21165, |
|||
"startedAt": 1782613791026, |
|||
"command": "dev", |
|||
"cwd": "/Users/liwei/work/go/yunyan/yunyan-sas/apps/admin", |
|||
"port": 3000, |
|||
"hostname": "127.0.0.1", |
|||
"url": "http://127.0.0.1:3000" |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
{ |
|||
"hash": "35ac884a", |
|||
"configHash": "5e84f24f", |
|||
"lockfileHash": "ec044b5a", |
|||
"browserHash": "0dbf3739", |
|||
"optimized": { |
|||
"errx": { |
|||
"src": "../../../../.pnpm/errx@0.1.0/node_modules/errx/dist/index.js", |
|||
"file": "errx.js", |
|||
"fileHash": "54268792", |
|||
"needsInterop": false |
|||
} |
|||
}, |
|||
"chunks": {} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
// node_modules/.pnpm/errx@0.1.0/node_modules/errx/dist/index.js
|
|||
var IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[a-z]:[/\\]/i; |
|||
var LINE_RE = /^\s+at (?:(?<function>[^)]+) \()?(?<source>[^)]+)\)?$/u; |
|||
var SOURCE_RE = /^(?<source>.+):(?<line>\d+):(?<column>\d+)$/u; |
|||
function captureRawStackTrace() { |
|||
if (!Error.captureStackTrace) { |
|||
return; |
|||
} |
|||
const stack = new Error(); |
|||
Error.captureStackTrace(stack); |
|||
return stack.stack; |
|||
} |
|||
function captureStackTrace() { |
|||
const stack = captureRawStackTrace(); |
|||
return stack ? parseRawStackTrace(stack) : []; |
|||
} |
|||
function parseRawStackTrace(stacktrace) { |
|||
const trace = []; |
|||
for (const line of stacktrace.split("\n")) { |
|||
const parsed = LINE_RE.exec(line)?.groups; |
|||
if (!parsed) { |
|||
continue; |
|||
} |
|||
if (!parsed.source) { |
|||
continue; |
|||
} |
|||
const parsedSource = SOURCE_RE.exec(parsed.source)?.groups; |
|||
if (parsedSource) { |
|||
Object.assign(parsed, parsedSource); |
|||
} |
|||
if (IS_ABSOLUTE_RE.test(parsed.source)) { |
|||
parsed.source = `file://${parsed.source}`; |
|||
} |
|||
if (parsed.source === import.meta.url) { |
|||
continue; |
|||
} |
|||
for (const key of ["line", "column"]) { |
|||
if (parsed[key]) { |
|||
parsed[key] = Number(parsed[key]); |
|||
} |
|||
} |
|||
trace.push(parsed); |
|||
} |
|||
return trace; |
|||
} |
|||
export { |
|||
captureRawStackTrace, |
|||
captureStackTrace, |
|||
parseRawStackTrace |
|||
}; |
|||
//# sourceMappingURL=errx.js.map
|
|||
@ -0,0 +1,7 @@ |
|||
{ |
|||
"version": 3, |
|||
"sources": ["../../../../.pnpm/errx@0.1.0/node_modules/errx/dist/index.js"], |
|||
"sourcesContent": ["const IS_ABSOLUTE_RE = /^[/\\\\](?![/\\\\])|^[/\\\\]{2}(?!\\.)|^[a-z]:[/\\\\]/i;\nconst LINE_RE = /^\\s+at (?:(?<function>[^)]+) \\()?(?<source>[^)]+)\\)?$/u;\nconst SOURCE_RE = /^(?<source>.+):(?<line>\\d+):(?<column>\\d+)$/u;\nfunction captureRawStackTrace() {\n if (!Error.captureStackTrace) {\n return;\n }\n const stack = new Error();\n Error.captureStackTrace(stack);\n return stack.stack;\n}\nfunction captureStackTrace() {\n const stack = captureRawStackTrace();\n return stack ? parseRawStackTrace(stack) : [];\n}\nfunction parseRawStackTrace(stacktrace) {\n const trace = [];\n for (const line of stacktrace.split(\"\\n\")) {\n const parsed = LINE_RE.exec(line)?.groups;\n if (!parsed) {\n continue;\n }\n if (!parsed.source) {\n continue;\n }\n const parsedSource = SOURCE_RE.exec(parsed.source)?.groups;\n if (parsedSource) {\n Object.assign(parsed, parsedSource);\n }\n if (IS_ABSOLUTE_RE.test(parsed.source)) {\n parsed.source = `file://${parsed.source}`;\n }\n if (parsed.source === import.meta.url) {\n continue;\n }\n for (const key of [\"line\", \"column\"]) {\n if (parsed[key]) {\n parsed[key] = Number(parsed[key]);\n }\n }\n trace.push(parsed);\n }\n return trace;\n}\n\nexport { captureRawStackTrace, captureStackTrace, parseRawStackTrace };\n"], |
|||
"mappings": ";AAAA,IAAM,iBAAiB;AACvB,IAAM,UAAU;AAChB,IAAM,YAAY;AAClB,SAAS,uBAAuB;AAC9B,MAAI,CAAC,MAAM,mBAAmB;AAC5B;AAAA,EACF;AACA,QAAM,QAAQ,IAAI,MAAM;AACxB,QAAM,kBAAkB,KAAK;AAC7B,SAAO,MAAM;AACf;AACA,SAAS,oBAAoB;AAC3B,QAAM,QAAQ,qBAAqB;AACnC,SAAO,QAAQ,mBAAmB,KAAK,IAAI,CAAC;AAC9C;AACA,SAAS,mBAAmB,YAAY;AACtC,QAAM,QAAQ,CAAC;AACf,aAAW,QAAQ,WAAW,MAAM,IAAI,GAAG;AACzC,UAAM,SAAS,QAAQ,KAAK,IAAI,GAAG;AACnC,QAAI,CAAC,QAAQ;AACX;AAAA,IACF;AACA,QAAI,CAAC,OAAO,QAAQ;AAClB;AAAA,IACF;AACA,UAAM,eAAe,UAAU,KAAK,OAAO,MAAM,GAAG;AACpD,QAAI,cAAc;AAChB,aAAO,OAAO,QAAQ,YAAY;AAAA,IACpC;AACA,QAAI,eAAe,KAAK,OAAO,MAAM,GAAG;AACtC,aAAO,SAAS,UAAU,OAAO,MAAM;AAAA,IACzC;AACA,QAAI,OAAO,WAAW,YAAY,KAAK;AACrC;AAAA,IACF;AACA,eAAW,OAAO,CAAC,QAAQ,QAAQ,GAAG;AACpC,UAAI,OAAO,GAAG,GAAG;AACf,eAAO,GAAG,IAAI,OAAO,OAAO,GAAG,CAAC;AAAA,MAClC;AAAA,IACF;AACA,UAAM,KAAK,MAAM;AAAA,EACnB;AACA,SAAO;AACT;", |
|||
"names": [] |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
{ |
|||
"type": "module" |
|||
} |
|||
Binary file not shown.
@ -0,0 +1,13 @@ |
|||
2026/06/28 10:29:27.276 INFO sys log Init success ! |
|||
2026/06/28 10:29:27.276 INFO sys event Init success ! |
|||
2026/06/28 10:29:28.801 INFO console: sys.postgres(主库)初始化成功 |
|||
2026/06/28 10:29:28.802 INFO console: sys.redis 初始化成功 |
|||
2026/06/28 10:29:28.803 INFO console: sys.nats 初始化成功 |
|||
2026/06/28 10:29:28.803 INFO console: sys.cos 初始化成功 |
|||
2026/06/28 10:29:28.803 INFO 服务[console-d908e1s4kvnl2senkasg] 初始化完成! |
|||
2026/06/28 10:29:28.803 INFO 服务[console-d908e1s4kvnl2senkasg:1.0.0.0] 启动完成! |
|||
2026/06/28 10:29:33.457 INFO console.stat: 已创建 JetStream 流 STATS(subject=stats.event) |
|||
2026/06/28 10:29:33.457 INFO console.stat: 订阅前 JetStream 流 STATS 当前消息数=0 subjects=[stats.event] |
|||
2026/06/28 10:29:33.458 INFO console.stat: 统计快照消费已启动 subject=stats.event stream=STATS durable=console-stats |
|||
2026/06/28 10:29:33.458 INFO console: HTTP 服务监听 :8080(静态目录 ./consoleweb) |
|||
2026/06/28 10:29:33.458 DEBUG 启动模块【console】 |
|||
@ -0,0 +1,74 @@ |
|||
# console 管理后台部署 |
|||
|
|||
把单服务 **console**(lego single 服务,维护 app_registry、跨应用统计)打成镜像 `console-a11`, |
|||
推送到私有仓库 `docker-registry.ideapsound.com`,再到目标机拉取重启。依赖 PostgreSQL / Redis / NATS |
|||
由目标环境共享提供(在 `.env` 里填地址/容器名),不在此 compose 内搭建。 |
|||
|
|||
> 约定(与 voitrans 一致):仓库**只提交模板**(`*.example`)。部署脚本会把**模板 + `docker-compose.yml`** 同步到服务器, |
|||
> 但**绝不上传/覆盖**你在服务器上填好的真实 `.env` / `confs/console.yaml`(首次需登录服务器照模板复制并填值)。 |
|||
|
|||
## 文件 |
|||
|
|||
| 文件 | 说明 | |
|||
|---|---| |
|||
| `../../apps/services/Dockerfile.console` | 镜像构建(编译 console 二进制 + entrypoint + 内置 consoleweb 静态),构建上下文 = `apps/services/` | |
|||
| `../../apps/services/entrypoint.console.sh` | 容器入口 | |
|||
| `docker-compose.yml` | 运行镜像 `console-a11`,挂 `./confs`、`./log`,`.env` 注入容器(此文件由你放到服务器) | |
|||
| `confs/console.yaml.example` | console 配置**模板**;真实 `console.yaml` 被 gitignore,密钥/端点用 `${VAR}` 由 `.env` 注入 | |
|||
| `env/env.example` | 运行时环境**模板**(只一份,不分环境);真实 `.env` 被 gitignore | |
|||
| `dev-deploy.sh` | **开发**:构建 dev 镜像 → 推送 → 远端拉取重启(固定 tag `dev-latest`) | |
|||
| `prod-build.sh` | **生产①**:构建带版本号镜像并推送(`:版本` + `:latest`),不部署 | |
|||
| `prod-deploy.sh` | **生产②**:部署指定版本到生产机(单机,无区域),支持回滚 | |
|||
|
|||
## 用法 |
|||
|
|||
### 本地(local) |
|||
|
|||
本机直接起容器(用本目录的 `.env` 与 `confs/`,自行从 `*.example` 复制填写): |
|||
|
|||
```bash |
|||
cd deploy/console |
|||
cp env/env.example .env # 填真实值 |
|||
cp confs/console.yaml.example confs/console.yaml |
|||
docker compose --env-file .env up -d # 本机起 console-a11 |
|||
``` |
|||
|
|||
### 服务器准备(dev / prod,一次性) |
|||
|
|||
脚本每次会同步 `docker-compose.yml` + `*.example` 模板到目标机。**首次**需登录服务器把模板复制成真实文件并填值 |
|||
(`cp env.example .env`、`cp confs/console.yaml.example confs/console.yaml`,dev-deploy 缺真实文件时也会打印这两条命令): |
|||
|
|||
``` |
|||
<部署目录>/ # dev 默认 /home/work/console-a11,prod 默认 /opt/yunyan/console |
|||
├── docker-compose.yml # 从本目录拷贝 |
|||
├── .env # 照 env/env.example 填(POSTGRES_DSN/REDIS_*/NATS_URL/ |
|||
│ # CONSOLE_TOKEN_KEY/CONSOLE_ADMIN_*/FIELD_ENCRYPT_KEY 等) |
|||
├── confs/console.yaml # 照 confs/console.yaml.example 填 |
|||
└── log/ # 日志目录 |
|||
``` |
|||
|
|||
### 部署 |
|||
|
|||
```bash |
|||
cd deploy/console |
|||
./dev-deploy.sh # 开发:构建 dev-latest → 推送 → 远端拉取重启 |
|||
./prod-build.sh 0.1.1 # 生产①:构建并推送 console-a11:0.1.1 (+latest) |
|||
./prod-deploy.sh 0.1.1 # 生产②:部署 0.1.1 到生产机 |
|||
./prod-deploy.sh rollback # 生产回滚到上一版本(与当前版本来回切换) |
|||
``` |
|||
|
|||
> 改了服务器上的 `.env` 后,必须经 `up`(脚本里的 `docker compose up -d`)**重建容器**才生效,`restart` 不会重读环境变量。 |
|||
> 生产版本/回滚记录存服务器上的 `.deploy_tag` / `.deploy_tag.prev`,不动你维护的 `.env`。 |
|||
|
|||
## 需要填写 / 提供的东西 |
|||
|
|||
- **部署目标与凭据**:写死在各脚本顶部。`dev-deploy.sh` 的 `DEV_HOST`、`prod-deploy.sh` 的 `DEPLOY_HOST`; |
|||
仓库账号 `admin` / 密码已写死;`DEPLOY_KEY` 默认 `~/.ssh/id_rsa`。 |
|||
- ⚠️ 本目录脚本纳入 git,**公开仓库前务必清空** `*_HOST` 与 `REGISTRY_PASS`。 |
|||
- **依赖端点**:填服务器 `.env` 的 `POSTGRES_DSN / REDIS_* / NATS_URL`,以及 `CONSOLE_TOKEN_KEY`、 |
|||
超管账号 `CONSOLE_ADMIN_ACCOUNT/PASSWORD`、字段加密 `FIELD_ENCRYPT_KEY`、站点名 `CONSOLE_SITE_NAME`。 |
|||
|
|||
## 端口 |
|||
|
|||
console HTTP `8080`(容器内,对外经反代/NPM 暴露)。依赖的 `postgres / redis / nats` 须可达 |
|||
(同 `DOCKER_NETWORK`,默认 `1panel-network`,用容器名,或在 `.env` 填外部地址)。 |
|||
@ -1,184 +0,0 @@ |
|||
#!/usr/bin/env bash |
|||
# <服务> 部署脚本(每个服务目录各一份,服务名取自所在目录名) |
|||
# |
|||
# 用法: ./deploy.sh <env> [action] (在 deploy/<服务>/ 下运行) |
|||
# env : local | dev | prod |
|||
# action : dev/prod → deploy(默认) | up | build | pull | down | restart | logs | ps |
|||
# local → up(默认) | build | pull | down | restart | logs | ps |
|||
# |
|||
# deploy = 构建推送镜像 + 同步配置 + 远程 pull + up(一键发布,仅 dev/prod) |
|||
# up = 远程:仅同步配置 + 远程 pull + up(用已推送镜像);本机:起容器 |
|||
# |
|||
# 例(在 deploy/console/ 下): |
|||
# ./deploy.sh prod # 一键发布生产 console |
|||
# ./deploy.sh prod logs # 看生产 console 日志 |
|||
# ./deploy.sh local up # 本机起 console |
|||
# ./deploy.sh dev build # 只构建推送 dev 镜像 |
|||
set -euo pipefail |
|||
|
|||
SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # 本服务部署目录(deploy/<服务>) |
|||
SERVICE="$(basename "$SVC_DIR")" # 服务名 = 目录名(console / admin) |
|||
REPO_ROOT="$(cd "$SVC_DIR/../.." && pwd)" # 仓库根(deploy/<服务>/../..) |
|||
|
|||
blue() { printf "\033[34m%s\033[0m\n" "$*"; } |
|||
green() { printf "\033[32m%s\033[0m\n" "$*"; } |
|||
red() { printf "\033[31m%s\033[0m\n" "$*"; } |
|||
|
|||
# ╔══════════════════════════════════════════════════════════════════════╗ |
|||
# ║ 部署目标 / 私有仓库配置 —— 写死在这里,改这里即可(按环境) ║ |
|||
# ║ console、admin 同机不同目录;DEPLOY_DIR 自动 = /opt/yunyan/<服务名> ║ |
|||
# ║ 注意:本文件纳入 git,下面的密码/IP 会随仓库提交(私有仓库内可接受) ║ |
|||
# ╚══════════════════════════════════════════════════════════════════════╝ |
|||
deploy_profile() { |
|||
DEPLOY_HOST=""; REGISTRY_PASS="" # 占位,local 用不到;dev/prod 在下面填 |
|||
# —— 各环境通用默认 —— |
|||
REGISTRY=docker-registry.ideapsound.com # 私有镜像仓库地址 |
|||
REGISTRY_USER=admin # 仓库账号 |
|||
DEPLOY_PORT=22 # 服务器 SSH 端口 |
|||
DEPLOY_USER=root # 服务器 SSH 用户 |
|||
DEPLOY_KEY=~/.ssh/id_rsa # 本机访问服务器的 SSH 私钥路径 |
|||
case "$ENV_NAME" in |
|||
dev) |
|||
DEPLOY_HOST="" # ← 开发服务器 IP(必填) |
|||
REGISTRY_PASS="" # ← 私有仓库密码(必填) |
|||
;; |
|||
prod) |
|||
DEPLOY_HOST="" # ← 生产服务器 IP(必填) |
|||
REGISTRY_PASS="" # ← 私有仓库密码(必填) |
|||
;; |
|||
local) ;; # 本机不走 SSH,无需填 |
|||
esac |
|||
DEPLOY_DIR="/opt/yunyan/$SERVICE" # 远程部署目录(自动按服务名) |
|||
} |
|||
|
|||
usage() { |
|||
cat >&2 <<EOF |
|||
用法: ./deploy.sh <env> [action] (在 deploy/$SERVICE/ 下运行) |
|||
env : local | dev | prod |
|||
action : dev/prod → deploy(默认) | up | build | pull | down | restart | logs | ps |
|||
local → up(默认) | build | pull | down | restart | logs | ps |
|||
例: |
|||
./deploy.sh prod # 一键发布生产 $SERVICE |
|||
./deploy.sh prod logs # 看生产 $SERVICE 日志 |
|||
./deploy.sh local up # 本机起 $SERVICE |
|||
./deploy.sh dev build # 只构建推送 dev 镜像 |
|||
EOF |
|||
exit 1 |
|||
} |
|||
|
|||
# ── 参数解析与校验 ── |
|||
ENV_NAME="${1:-}"; ACTION="${2:-}" |
|||
[ -n "$ENV_NAME" ] || usage |
|||
[ -f "$SVC_DIR/docker-compose.yml" ] || { red "✗ $SERVICE 目录缺 docker-compose.yml(deploy.sh 须放在服务目录内)"; exit 1; } |
|||
case "$ENV_NAME" in |
|||
local|dev|prod) ;; |
|||
*) red "✗ 未知环境: ${ENV_NAME}(应为 local|dev|prod)"; usage ;; |
|||
esac |
|||
# 默认动作:远程发布默认 deploy,本机默认 up |
|||
[ -n "$ACTION" ] || { [ "$ENV_NAME" = local ] && ACTION=up || ACTION=deploy; } |
|||
|
|||
# 从 ENV_FILE 安全读取单个变量值(去除可能的外层双引号) |
|||
_envget() { |
|||
{ grep -E "^$1=" "$ENV_FILE" || true; } | head -1 | cut -d= -f2- | sed -e 's/^"//' -e 's/"$//' |
|||
} |
|||
|
|||
# 载入 env/<环境>.env —— 现在只放容器运行时配置(DSN / Redis / NATS / 密钥 / 端口 / TAG / 网络)。 |
|||
# 部署目标与仓库凭据已写死在 deploy_profile,不再放 env 文件。脚本只需从这里取 compose 仍要的 TAG/网络。 |
|||
load_env() { |
|||
ENV_FILE="$SVC_DIR/env/$ENV_NAME.env" |
|||
if [ ! -f "$ENV_FILE" ]; then |
|||
red "✗ 缺少 $SERVICE/env/$ENV_NAME.env —— 请先从 $SERVICE/env/$ENV_NAME.env.example 复制并填写" |
|||
exit 1 |
|||
fi |
|||
TAG="$(_envget TAG)"; TAG="${TAG:-latest}" |
|||
DOCKER_NETWORK="$(_envget DOCKER_NETWORK)"; DOCKER_NETWORK="${DOCKER_NETWORK:-1panel-network}" |
|||
} |
|||
|
|||
# 生成 compose 用 .env = 服务运行时 env 文件 + 注入写死的 REGISTRY(镜像地址替换用) |
|||
# 先滤掉源文件里可能残留的 REGISTRY 行,保证唯一、不依赖 compose 的覆盖顺序。 |
|||
gen_env() { |
|||
{ grep -v '^REGISTRY=' "$ENV_FILE" || true; } > "$SVC_DIR/.env" |
|||
printf 'REGISTRY=%s\n' "$REGISTRY" >> "$SVC_DIR/.env" |
|||
} |
|||
|
|||
# 构建并推送本服务镜像(复用仓库根目录 build.sh,目标 = 服务名)。 |
|||
# local 环境只构建不推送(PUSH=0),dev/prod 构建并推送到私有仓库。 |
|||
build_push() { |
|||
local push=1; [ "$ENV_NAME" = local ] && push=0 |
|||
blue ">>> 构建$([ "$push" = 1 ] && echo 推送) $SERVICE 镜像 (TAG=${TAG:-latest}, PUSH=$push)" |
|||
TAG="${TAG:-latest}" REGISTRY="$REGISTRY" PUSH="$push" "$REPO_ROOT/build.sh" "$SERVICE" |
|||
} |
|||
|
|||
ensure_network_local() { |
|||
docker network inspect "$DOCKER_NETWORK" >/dev/null 2>&1 || docker network create "$DOCKER_NETWORK" |
|||
} |
|||
|
|||
# 本机 docker compose(gen_env 落为同目录 .env,既做变量替换又做容器 env_file) |
|||
compose_local() { |
|||
gen_env |
|||
ensure_network_local |
|||
( cd "$SVC_DIR" && docker compose --env-file .env "$@" ) |
|||
} |
|||
|
|||
# ── 远程 SSH 基础 ── |
|||
_ssh() { ssh -p "${DEPLOY_PORT:-22}" -i "${DEPLOY_KEY/#\~/$HOME}" -o StrictHostKeyChecking=accept-new "${DEPLOY_USER:-root}@${DEPLOY_HOST}" "$@"; } |
|||
_scp() { scp -P "${DEPLOY_PORT:-22}" -i "${DEPLOY_KEY/#\~/$HOME}" -o StrictHostKeyChecking=accept-new "$@"; } |
|||
_dest() { echo "${DEPLOY_USER:-root}@${DEPLOY_HOST}:$1"; } |
|||
|
|||
# 远程发布:同步配置 + 远程 login + pull + up |
|||
remote_deploy() { |
|||
: "${DEPLOY_HOST:?请在 deploy.sh 顶部 deploy_profile 的 $ENV_NAME 段填 DEPLOY_HOST}" |
|||
gen_env |
|||
|
|||
blue ">>> 同步 $SERVICE 配置到 ${DEPLOY_USER:-root}@${DEPLOY_HOST}:${DEPLOY_DIR}" |
|||
_ssh "mkdir -p '$DEPLOY_DIR'" |
|||
_scp "$SVC_DIR/docker-compose.yml" "$(_dest "$DEPLOY_DIR/docker-compose.yml")" |
|||
_scp "$SVC_DIR/.env" "$(_dest "$DEPLOY_DIR/.env")" |
|||
# 带 confs/ 的服务(console)一并同步配置并预建 log 目录 |
|||
if [ -d "$SVC_DIR/confs" ]; then |
|||
_ssh "mkdir -p '$DEPLOY_DIR/confs' '$DEPLOY_DIR/log'" |
|||
_scp -r "$SVC_DIR/confs/." "$(_dest "$DEPLOY_DIR/confs/")" |
|||
fi |
|||
|
|||
blue ">>> 远程拉起 $SERVICE" |
|||
[ -n "${REGISTRY_PASS:-}" ] && \ |
|||
_ssh "docker login '$REGISTRY' -u '${REGISTRY_USER:-admin}' -p '$REGISTRY_PASS'" |
|||
_ssh "docker network inspect '$DOCKER_NETWORK' >/dev/null 2>&1 || docker network create '$DOCKER_NETWORK'" |
|||
_ssh "cd '$DEPLOY_DIR' && docker compose --env-file .env pull && docker compose --env-file .env up -d" |
|||
} |
|||
|
|||
# 远程运维(down/restart/logs/ps/pull) |
|||
remote_compose() { |
|||
: "${DEPLOY_HOST:?请在 deploy.sh 顶部 deploy_profile 的 $ENV_NAME 段填 DEPLOY_HOST}" |
|||
_ssh "cd '$DEPLOY_DIR' && docker compose --env-file .env $*" |
|||
} |
|||
|
|||
# ── 分发 ── |
|||
deploy_profile # 写死的部署目标 / 仓库凭据 |
|||
load_env # env 文件里的运行时配置(TAG / 网络) |
|||
|
|||
if [ "$ENV_NAME" = local ]; then |
|||
case "$ACTION" in |
|||
up) compose_local up -d ;; |
|||
down) compose_local down ;; |
|||
restart) compose_local restart ;; |
|||
logs) compose_local logs -f --tail=120 ;; |
|||
ps) compose_local ps ;; |
|||
pull) compose_local pull ;; |
|||
build) build_push ;; |
|||
*) red "未知操作: ${ACTION}(local 支持 up|down|restart|logs|ps|pull|build)"; exit 1 ;; |
|||
esac |
|||
else |
|||
case "$ACTION" in |
|||
deploy) build_push; remote_deploy ;; |
|||
up) remote_deploy ;; |
|||
build) build_push ;; |
|||
pull) remote_compose pull ;; |
|||
down) remote_compose down ;; |
|||
restart) remote_compose restart ;; |
|||
ps) remote_compose ps ;; |
|||
logs) remote_compose logs -f --tail=120 ;; |
|||
*) red "未知操作: ${ACTION}(dev/prod 支持 deploy|up|build|pull|down|restart|logs|ps)"; exit 1 ;; |
|||
esac |
|||
fi |
|||
green "✓ $SERVICE $ENV_NAME $ACTION 完成" |
|||
@ -0,0 +1,76 @@ |
|||
#!/usr/bin/env bash |
|||
# console 开发环境:构建开发镜像 → 推送 → 远端拉取重启。 |
|||
# |
|||
# 单服务 console 打成镜像 console-a11,推到私有仓库后远端拉取重启。 |
|||
# 仅做镜像部署:服务器上的 docker-compose.yml / .env / confs/ 由你自行维护 |
|||
# (照着仓库里的 env/env.example、confs/console.yaml.example 在服务器上放好真实文件),本脚本不上传任何配置。 |
|||
# |
|||
# 用法(在 deploy/console/ 下): |
|||
# ./dev-deploy.sh # 构建并发布 console-a11:dev-latest |
|||
# |
|||
# 开发版镜像始终使用固定 tag dev-latest,每次构建覆盖同一 tag。 |
|||
set -euo pipefail |
|||
|
|||
SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # deploy/console |
|||
REPO_ROOT="$(cd "$SVC_DIR/../.." && pwd)" # 仓库根 |
|||
BUILD_CONTEXT="$REPO_ROOT/apps/services" # Go 构建上下文 |
|||
DOCKERFILE="$BUILD_CONTEXT/Dockerfile.console" |
|||
|
|||
blue() { printf "\033[34m%s\033[0m\n" "$*"; } |
|||
green() { printf "\033[32m%s\033[0m\n" "$*"; } |
|||
red() { printf "\033[31m%s\033[0m\n" "$*"; } |
|||
|
|||
# ╔══════════════════════════════════════════════════════════════════════╗ |
|||
# ║ 开发部署目标 / 私有仓库凭据 —— 写死在此(本文件纳入 git,私有仓库内可接受) ║ |
|||
# ║ ⚠️ 公开仓库前务必清空 DEV_HOST / REGISTRY_PASS ║ |
|||
# ╚══════════════════════════════════════════════════════════════════════╝ |
|||
IMAGE_NAME="console-a11" # 业务镜像名 |
|||
REGISTRY="docker-registry.ideapsound.com" # 私有镜像仓库 |
|||
REGISTRY_USER="admin" # 仓库账号 |
|||
REGISTRY_PASS="li13451234" # 仓库密码 |
|||
|
|||
DEV_HOST="124.222.232.218" # ← 开发服务器 IP(必填) |
|||
DEV_PORT=22 |
|||
DEV_USER=root |
|||
DEV_KEY=/Users/liwei/Documents/key/earphone.pem # 本机访问开发服务器的 SSH 私钥 |
|||
DEV_DIR="/home/work/console-a11" # 远端部署目录(其中 docker-compose.yml/.env/confs 由你自己维护) |
|||
|
|||
PLATFORM="${PLATFORM:-linux/amd64}" |
|||
TAG="dev-latest" # 开发版固定 tag,每次构建覆盖同一 dev-latest |
|||
FULL_IMAGE="$REGISTRY/$IMAGE_NAME:$TAG" |
|||
|
|||
[ -f "$DOCKERFILE" ] || { red "✗ 缺少 $DOCKERFILE"; exit 1; } |
|||
: "${DEV_HOST:?请在 dev-deploy.sh 顶部填 DEV_HOST(开发服务器 IP)}" |
|||
|
|||
# ── 远程 SSH ── |
|||
_ssh() { ssh -p "$DEV_PORT" -i "${DEV_KEY/#\~/$HOME}" -o StrictHostKeyChecking=accept-new "$DEV_USER@$DEV_HOST" "$@"; } |
|||
_scp() { scp -P "$DEV_PORT" -i "${DEV_KEY/#\~/$HOME}" -o StrictHostKeyChecking=accept-new "$@"; } |
|||
_dest() { echo "$DEV_USER@$DEV_HOST:$1"; } |
|||
|
|||
# 1) 构建并推送镜像 |
|||
blue ">>> 构建镜像 $FULL_IMAGE (context=$BUILD_CONTEXT)" |
|||
echo "$REGISTRY_PASS" | docker login "$REGISTRY" -u "$REGISTRY_USER" --password-stdin |
|||
docker build --network=host --platform "$PLATFORM" -f "$DOCKERFILE" -t "$FULL_IMAGE" "$BUILD_CONTEXT" |
|||
docker push "$FULL_IMAGE" |
|||
|
|||
# 2) 同步「模板 + compose」到服务器(只传模板,绝不覆盖你维护的真实 .env / confs/console.yaml) |
|||
blue ">>> 同步模板与 compose → $DEV_USER@$DEV_HOST:$DEV_DIR" |
|||
_ssh "mkdir -p '$DEV_DIR/confs' '$DEV_DIR/log'" |
|||
_scp "$SVC_DIR/docker-compose.yml" "$(_dest "$DEV_DIR/docker-compose.yml")" |
|||
_scp "$SVC_DIR/confs/console.yaml.example" "$(_dest "$DEV_DIR/confs/console.yaml.example")" |
|||
_scp "$SVC_DIR/env/env.example" "$(_dest "$DEV_DIR/env.example")" |
|||
|
|||
# 3) 远端拉取并重启(用你在服务器维护的真实 .env / confs/console.yaml) |
|||
blue ">>> 远端拉取并重启" |
|||
_ssh "echo '$REGISTRY_PASS' | docker login '$REGISTRY' -u '$REGISTRY_USER' --password-stdin >/dev/null" |
|||
_ssh "docker network inspect 1panel-network >/dev/null 2>&1 || docker network create 1panel-network" |
|||
if ! _ssh "test -f '$DEV_DIR/.env' && test -f '$DEV_DIR/confs/console.yaml'"; then |
|||
red "⚠ 服务器缺少真实 .env 或 confs/console.yaml —— 模板已同步,请登录服务器按模板填好后重跑本脚本:" |
|||
echo " ssh -i $DEV_KEY $DEV_USER@$DEV_HOST" |
|||
echo " cd $DEV_DIR && cp env.example .env && cp confs/console.yaml.example confs/console.yaml" |
|||
echo " # 编辑 .env 与 confs/console.yaml 填真实值,然后重跑 ./dev-deploy.sh" |
|||
exit 0 |
|||
fi |
|||
_ssh "cd '$DEV_DIR' && REGISTRY='$REGISTRY' TAG='$TAG' docker compose --env-file .env pull && REGISTRY='$REGISTRY' TAG='$TAG' docker compose --env-file .env up -d && docker image prune -f" |
|||
|
|||
green "✓ console dev 已发布: $FULL_IMAGE → $DEV_HOST:$DEV_DIR" |
|||
@ -1,26 +0,0 @@ |
|||
# console 服务 · dev 开发环境(复制为 dev.env 后填写真实值) |
|||
|
|||
# ── 镜像(仓库地址 REGISTRY 已写死在 deploy.sh,这里不填)── |
|||
TAG=dev |
|||
PLATFORM=linux/amd64 |
|||
DOCKER_NETWORK=yunyan-net |
|||
|
|||
# ── 依赖服务(目标环境共享提供,填地址;密码特殊字符 URL 编码 & → %26)── |
|||
POSTGRES_DSN=postgresql://user:password@host:5432/dbname?sslmode=require |
|||
REDIS_ADDR=redis:6379 |
|||
REDIS_PASSWORD= |
|||
NATS_URL=nats://nats:4222 |
|||
|
|||
# ── COS(可选)── |
|||
COS_SECRET_ID= |
|||
COS_SECRET_KEY= |
|||
|
|||
# ── Console 服务配置 ── |
|||
CONSOLE_TOKEN_KEY=dev-change-me |
|||
CONSOLE_ADMIN_ACCOUNT=admin |
|||
CONSOLE_ADMIN_PASSWORD=change-me |
|||
CONSOLE_SITE_NAME=云言 SaaS 管理平台(开发) |
|||
FIELD_ENCRYPT_KEY=dev-change-me-to-random-key |
|||
|
|||
# 部署目标(DEPLOY_HOST/USER/KEY/DIR)与仓库凭据(REGISTRY_USER/PASS)已写死在 |
|||
# deploy.sh 顶部的 deploy_profile(dev 段),不在此文件填。 |
|||
@ -0,0 +1,27 @@ |
|||
# console 服务运行时环境模板(只一份,不分 local/dev/prod)。 |
|||
# 用法:在目标机上复制为 .env 并填真实值;本地起则在本目录复制为 .env。 |
|||
# 真实 .env 已 gitignore,勿提交。 |
|||
|
|||
# ── 镜像(仓库地址 REGISTRY 已写死在脚本里,这里不填)── |
|||
TAG=latest # 生产建议固定版本号(如 0.1.1)便于回滚;dev 用 dev-latest |
|||
PLATFORM=linux/amd64 |
|||
DOCKER_NETWORK=1panel-network # compose 外部网络名,须与目标机一致 |
|||
|
|||
# ── 依赖服务(目标环境共享提供,填地址;密码特殊字符需 URL 编码 & → %26)── |
|||
POSTGRES_DSN=postgresql://user:password@host:5432/dbname?sslmode=require |
|||
REDIS_ADDR=redis:6379 |
|||
REDIS_PASSWORD= |
|||
NATS_URL=nats://nats:4222 |
|||
|
|||
# ── COS(可选)── |
|||
COS_SECRET_ID= |
|||
COS_SECRET_KEY= |
|||
|
|||
# ── Console 服务配置(密钥务必强随机)── |
|||
CONSOLE_TOKEN_KEY= |
|||
CONSOLE_ADMIN_ACCOUNT=admin |
|||
CONSOLE_ADMIN_PASSWORD= |
|||
CONSOLE_SITE_NAME=云言 SaaS 管理平台 |
|||
FIELD_ENCRYPT_KEY= |
|||
|
|||
# 部署目标(DEPLOY_HOST/USER/KEY/DIR)与仓库凭据(REGISTRY_USER/PASS)写死在 dev-deploy.sh / prod-deploy.sh 顶部,不在此填。 |
|||
@ -1,23 +0,0 @@ |
|||
# console 服务 · local 本机环境(复制为 local.env 后使用) |
|||
|
|||
# ── 镜像(仓库地址 REGISTRY 已写死在 deploy.sh,这里不填)── |
|||
TAG=latest |
|||
PLATFORM=linux/amd64 |
|||
DOCKER_NETWORK=1panel-network |
|||
|
|||
# ── 依赖服务(本机/共享环境提供,仅填连接地址,不在本服务搭建)── |
|||
POSTGRES_DSN=postgresql://user:password@host:5432/dbname?sslmode=require |
|||
REDIS_ADDR=redis:6379 |
|||
REDIS_PASSWORD= |
|||
NATS_URL=nats://nats:4222 |
|||
|
|||
# ── COS(可选)── |
|||
COS_SECRET_ID= |
|||
COS_SECRET_KEY= |
|||
|
|||
# ── Console 服务配置 ── |
|||
CONSOLE_TOKEN_KEY=console-secret-change-me |
|||
CONSOLE_ADMIN_ACCOUNT=admin |
|||
CONSOLE_ADMIN_PASSWORD=change-me |
|||
CONSOLE_SITE_NAME=云言 SaaS 管理平台 |
|||
FIELD_ENCRYPT_KEY=change-me-to-random-key |
|||
@ -1,26 +0,0 @@ |
|||
# console 服务 · prod 生产环境(复制为 prod.env 后填写真实值) |
|||
|
|||
# ── 镜像(仓库地址 REGISTRY 已写死在 deploy.sh,这里不填)── |
|||
TAG=latest # 生产建议固定版本号,如 v1.2.0,便于回滚 |
|||
PLATFORM=linux/amd64 |
|||
DOCKER_NETWORK=yunyan-net |
|||
|
|||
# ── 依赖服务(目标环境共享提供,填地址;密码特殊字符 URL 编码 & → %26)── |
|||
POSTGRES_DSN=postgresql://user:password@host:5432/dbname?sslmode=require |
|||
REDIS_ADDR=redis:6379 |
|||
REDIS_PASSWORD= |
|||
NATS_URL=nats://nats:4222 |
|||
|
|||
# ── COS(可选)── |
|||
COS_SECRET_ID= |
|||
COS_SECRET_KEY= |
|||
|
|||
# ── Console 服务配置(密钥务必强随机)── |
|||
CONSOLE_TOKEN_KEY=prod-change-me-to-strong-secret |
|||
CONSOLE_ADMIN_ACCOUNT=admin |
|||
CONSOLE_ADMIN_PASSWORD=change-me |
|||
CONSOLE_SITE_NAME=云言 SaaS 管理平台 |
|||
FIELD_ENCRYPT_KEY=prod-change-me-to-strong-random-key |
|||
|
|||
# 部署目标(DEPLOY_HOST/USER/KEY/DIR)与仓库凭据(REGISTRY_USER/PASS)已写死在 |
|||
# deploy.sh 顶部的 deploy_profile(prod 段),不在此文件填。 |
|||
@ -0,0 +1,44 @@ |
|||
#!/usr/bin/env bash |
|||
# console 生产环境:仅「构建带版本号的镜像并推送」,不部署。 |
|||
# |
|||
# 单服务 console 打成镜像 console-a11,同时推送 :<version> 与 :latest 两个 tag。 |
|||
# 部署用 ./prod-deploy.sh <version>。 |
|||
# |
|||
# 用法(在 deploy/console/ 下): |
|||
# ./prod-build.sh <version> # version 必须形如 X.Y.Z,如 0.1.1 |
|||
set -euo pipefail |
|||
|
|||
SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
|||
REPO_ROOT="$(cd "$SVC_DIR/../.." && pwd)" |
|||
BUILD_CONTEXT="$REPO_ROOT/apps/services" |
|||
DOCKERFILE="$BUILD_CONTEXT/Dockerfile.console" |
|||
|
|||
blue() { printf "\033[34m%s\033[0m\n" "$*"; } |
|||
green() { printf "\033[32m%s\033[0m\n" "$*"; } |
|||
red() { printf "\033[31m%s\033[0m\n" "$*"; } |
|||
|
|||
# ── 私有仓库凭据(写死,公开仓库前清空 REGISTRY_PASS)── |
|||
IMAGE_NAME="console-a11" |
|||
REGISTRY="docker-registry.ideapsound.com" |
|||
REGISTRY_USER="admin" |
|||
REGISTRY_PASS="li13451234" |
|||
PLATFORM="${PLATFORM:-linux/amd64}" |
|||
|
|||
VERSION="${1:-}" |
|||
[ -n "$VERSION" ] || { red "用法: ./prod-build.sh <version>(如 0.1.1)"; exit 1; } |
|||
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then |
|||
red "✗ version 必须形如 0.1.1,当前: $VERSION"; exit 1 |
|||
fi |
|||
[ -f "$DOCKERFILE" ] || { red "✗ 缺少 $DOCKERFILE"; exit 1; } |
|||
|
|||
IMG_VER="$REGISTRY/$IMAGE_NAME:$VERSION" |
|||
IMG_LATEST="$REGISTRY/$IMAGE_NAME:latest" |
|||
|
|||
blue ">>> 构建生产镜像 $IMG_VER (+latest) context=$BUILD_CONTEXT" |
|||
echo "$REGISTRY_PASS" | docker login "$REGISTRY" -u "$REGISTRY_USER" --password-stdin |
|||
docker build --network=host --platform "$PLATFORM" -f "$DOCKERFILE" -t "$IMG_VER" -t "$IMG_LATEST" "$BUILD_CONTEXT" |
|||
docker push "$IMG_VER" |
|||
docker push "$IMG_LATEST" |
|||
|
|||
green "✓ 已推送 $IMG_VER 与 $IMG_LATEST" |
|||
echo " 部署:./prod-deploy.sh $VERSION" |
|||
@ -0,0 +1,87 @@ |
|||
#!/usr/bin/env bash |
|||
# console 生产环境:部署指定镜像版本到生产机,支持回滚。console 为单机管理后台,无区域之分。 |
|||
# |
|||
# 镜像由 ./prod-build.sh 预先构建推送;本脚本只做「远端拉取 + 重启」。 |
|||
# 仅做镜像部署:服务器上的 docker-compose.yml / .env / confs/ 由你自行维护 |
|||
# (照着仓库里的 env/env.example、confs/console.yaml.example 在服务器上放好真实文件),本脚本不上传任何配置。 |
|||
# |
|||
# 用法(在 deploy/console/ 下): |
|||
# ./prod-deploy.sh <tag> # 部署指定版本,如 ./prod-deploy.sh 0.1.1 |
|||
# ./prod-deploy.sh rollback # 回滚到上一次部署版本(与当前版本来回切换) |
|||
set -euo pipefail |
|||
|
|||
SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
|||
|
|||
blue() { printf "\033[34m%s\033[0m\n" "$*"; } |
|||
green() { printf "\033[32m%s\033[0m\n" "$*"; } |
|||
red() { printf "\033[31m%s\033[0m\n" "$*"; } |
|||
|
|||
# ╔══════════════════════════════════════════════════════════════════════╗ |
|||
# ║ 生产部署目标 / 私有仓库凭据 —— 写死在此(本文件纳入 git)。公开仓库前清空 HOST/PASS。 ║ |
|||
# ╚══════════════════════════════════════════════════════════════════════╝ |
|||
IMAGE_NAME="console-a11" |
|||
REGISTRY="docker-registry.ideapsound.com" |
|||
REGISTRY_USER="admin" |
|||
REGISTRY_PASS="li13451234" |
|||
DOCKER_NETWORK="1panel-network" # compose 外部网络名(与服务器一致) |
|||
|
|||
DEPLOY_HOST="" # ← 生产服务器 IP(必填) |
|||
DEPLOY_PORT=22 |
|||
DEPLOY_USER=root |
|||
DEPLOY_KEY=~/.ssh/id_rsa |
|||
DEPLOY_DIR="/opt/yunyan/console" # 远端部署目录(其中 docker-compose.yml/.env/confs 由你自己维护) |
|||
|
|||
# ── 参数 ── |
|||
ARG1="${1:-}" |
|||
[ -n "$ARG1" ] || { red "用法: ./prod-deploy.sh <tag|rollback>(如 0.1.1)"; exit 1; } |
|||
if [ "$ARG1" = "rollback" ]; then MODE="rollback"; NEW_TAG=""; else MODE="deploy"; NEW_TAG="$ARG1"; fi |
|||
|
|||
: "${DEPLOY_HOST:?请在 prod-deploy.sh 顶部填 DEPLOY_HOST(生产服务器 IP)}" |
|||
|
|||
_ssh() { ssh -p "$DEPLOY_PORT" -i "${DEPLOY_KEY/#\~/$HOME}" -o StrictHostKeyChecking=accept-new "$DEPLOY_USER@$DEPLOY_HOST" "$@"; } |
|||
_scp() { scp -P "$DEPLOY_PORT" -i "${DEPLOY_KEY/#\~/$HOME}" -o StrictHostKeyChecking=accept-new "$@"; } |
|||
_dest() { echo "$DEPLOY_USER@$DEPLOY_HOST:$1"; } |
|||
|
|||
# 同步「模板 + compose」(只传模板,绝不覆盖你维护的真实 .env / confs/console.yaml) |
|||
blue ">>> 同步模板与 compose → $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_DIR" |
|||
_ssh "mkdir -p '$DEPLOY_DIR/confs' '$DEPLOY_DIR/log'" |
|||
_scp "$SVC_DIR/docker-compose.yml" "$(_dest "$DEPLOY_DIR/docker-compose.yml")" |
|||
_scp "$SVC_DIR/confs/console.yaml.example" "$(_dest "$DEPLOY_DIR/confs/console.yaml.example")" |
|||
_scp "$SVC_DIR/env/env.example" "$(_dest "$DEPLOY_DIR/env.example")" |
|||
|
|||
# 远端拉取并重启(不上传真实配置):tag 历史存服务器上独立的 .deploy_tag,不动你维护的 .env。 |
|||
blue ">>> 远端部署($MODE${NEW_TAG:+ $NEW_TAG}) → $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_DIR" |
|||
_ssh bash -s -- "$DEPLOY_DIR" "$MODE" "$NEW_TAG" "$REGISTRY/$IMAGE_NAME" "$REGISTRY" "$REGISTRY_USER" "$REGISTRY_PASS" "$DOCKER_NETWORK" <<'REMOTE' |
|||
set -e |
|||
DIR="$1"; MODE="$2"; NEW_TAG="$3"; IMG_PREFIX="$4"; REG="$5"; REG_USER="$6"; REG_PASS="$7"; NET="$8" |
|||
|
|||
[ -f "$DIR/.env" ] || { echo "✗ 缺少 $DIR/.env(请照 env.example 在服务器填好真实 .env)"; exit 1; } |
|||
[ -f "$DIR/confs/console.yaml" ] || { echo "✗ 缺少 $DIR/confs/console.yaml(请照 confs/console.yaml.example 在服务器填好)"; exit 1; } |
|||
[ -f "$DIR/docker-compose.yml" ] || { echo "✗ 缺少 $DIR/docker-compose.yml"; exit 1; } |
|||
|
|||
STATE="$DIR/.deploy_tag" # 当前部署 tag(独立文件,不动用户的 .env) |
|||
PREVF="$DIR/.deploy_tag.prev" # 上一次部署 tag |
|||
CUR_TAG="$([ -f "$STATE" ] && cat "$STATE" || true)" |
|||
CUR_PREV="$([ -f "$PREVF" ] && cat "$PREVF" || true)" |
|||
|
|||
if [ "$MODE" = "rollback" ]; then |
|||
[ -n "$CUR_PREV" ] || { echo "没有上一次版本记录(.deploy_tag.prev),无法回滚"; exit 1; } |
|||
FINAL_TAG="$CUR_PREV"; FINAL_PREV="$CUR_TAG" # 与当前版本来回切换 |
|||
else |
|||
FINAL_TAG="$NEW_TAG" |
|||
if [ -n "$CUR_TAG" ] && [ "$CUR_TAG" != "$NEW_TAG" ]; then FINAL_PREV="$CUR_TAG"; else FINAL_PREV="$CUR_PREV"; fi |
|||
fi |
|||
|
|||
echo "$REG_PASS" | docker login "$REG" -u "$REG_USER" --password-stdin >/dev/null |
|||
docker network inspect "$NET" >/dev/null 2>&1 || docker network create "$NET" |
|||
docker pull "$IMG_PREFIX:$FINAL_TAG" |
|||
# REGISTRY/TAG 通过 inline 环境变量传给 compose(优先级高于 --env-file),不改写用户的 .env |
|||
cd "$DIR" && REGISTRY="$REG" TAG="$FINAL_TAG" docker compose --env-file .env up -d |
|||
docker image prune -f |
|||
|
|||
echo "$FINAL_TAG" > "$STATE" |
|||
[ -n "$FINAL_PREV" ] && echo "$FINAL_PREV" > "$PREVF" || true |
|||
echo "已部署版本 TAG=$FINAL_TAG (上一次=${FINAL_PREV:-无})" |
|||
REMOTE |
|||
|
|||
green "✓ console $MODE 完成" |
|||
@ -1,37 +0,0 @@ |
|||
# voitrans · 生产-日本 容器运行时配置(REGISTRY 由 prod-deploy.sh 注入) |
|||
|
|||
TAG=latest |
|||
PLATFORM=linux/amd64 |
|||
DOCKER_NETWORK=1panel-network |
|||
|
|||
CLUSTER_TAG=voitrans-a11-jp |
|||
ETCD_SERVERS=etcd:2379 |
|||
|
|||
# ── 存储(按日本生产实际填写)── |
|||
DB_ADMIN_DSN=root:CHANGE_ME@tcp(mysql:3306)/voitrans?charset=utf8mb4&parseTime=True&loc=Local |
|||
DB_DEVICE_DSN=postgresql://postgres:CHANGE_ME@postgres:5432/postgres?sslmode=require |
|||
DB_DEVICE_READ_DSN= |
|||
REDIS_ADDR=redis:6379 |
|||
REDIS_PASSWORD=CHANGE_ME |
|||
REDIS_DB=1 |
|||
REDIS_KEY_PREFIX=VoitransJP |
|||
|
|||
NATS_URL=nats://nats:4222 |
|||
|
|||
ANALYZE_APP_NAME=voitrans-a11-jp |
|||
ANALYZE_REGION=2 # 区域编码见 pb.Region(海外) |
|||
|
|||
LOG_DEBUG=false |
|||
GATEWAY_TOKEN_KEY=CHANGE_ME |
|||
# v2 配置接口响应加密 AES key(16/24/32 字节),须与 Flutter ApiCrypto._key 完全一致;留空=不加密 |
|||
GATEWAY_ENCRYPT_KEY= |
|||
API_ADMIN_ACCOUNT=admin |
|||
API_ADMIN_PASSWORD=CHANGE_ME |
|||
API_SITE_NAME=Voitrans |
|||
API_CURRENCY_TYPE=1 # 海外计费美元 |
|||
MCP_ADDR=http://localhost:7300 |
|||
|
|||
AZURE_TRANSLATE_KEY= |
|||
TIMER_DOUBAO_APIKEY= |
|||
TIMER_DOUBAO_BOTID= |
|||
TIMER_DOUBAO_ROLE= |
|||
@ -1,38 +0,0 @@ |
|||
# voitrans · 生产-上海 容器运行时配置(REGISTRY 由 prod-deploy.sh 注入) |
|||
# 依赖的 etcd/mysql/redis/nats 用容器名或区域内网地址连接,按实际填写。 |
|||
|
|||
TAG=latest # 实际部署版本由 prod-deploy.sh <区域> <tag> 覆盖 |
|||
PLATFORM=linux/amd64 |
|||
DOCKER_NETWORK=1panel-network |
|||
|
|||
CLUSTER_TAG=voitrans-a11-sh # 各区域独立集群标签,避免跨区域互相发现 |
|||
ETCD_SERVERS=etcd:2379 |
|||
|
|||
# ── 存储(按上海生产实际填写)── |
|||
DB_ADMIN_DSN=root:CHANGE_ME@tcp(mysql:3306)/voitrans?charset=utf8mb4&parseTime=True&loc=Local |
|||
DB_DEVICE_DSN=postgresql://postgres:CHANGE_ME@postgres:5432/postgres?sslmode=require |
|||
DB_DEVICE_READ_DSN= |
|||
REDIS_ADDR=redis:6379 |
|||
REDIS_PASSWORD=CHANGE_ME |
|||
REDIS_DB=1 |
|||
REDIS_KEY_PREFIX=VoitransSH |
|||
|
|||
NATS_URL=nats://nats:4222 |
|||
|
|||
ANALYZE_APP_NAME=voitrans-a11-sh # 须与 console 注册表应用名一致 |
|||
ANALYZE_REGION=1 # 区域编码见 pb.Region(国内) |
|||
|
|||
LOG_DEBUG=false |
|||
GATEWAY_TOKEN_KEY=CHANGE_ME |
|||
# v2 配置接口响应加密 AES key(16/24/32 字节),须与 Flutter ApiCrypto._key 完全一致;留空=不加密 |
|||
GATEWAY_ENCRYPT_KEY= |
|||
API_ADMIN_ACCOUNT=admin |
|||
API_ADMIN_PASSWORD=CHANGE_ME |
|||
API_SITE_NAME=Voitrans |
|||
API_CURRENCY_TYPE=0 |
|||
MCP_ADDR=http://localhost:7300 |
|||
|
|||
AZURE_TRANSLATE_KEY= |
|||
TIMER_DOUBAO_APIKEY= |
|||
TIMER_DOUBAO_BOTID= |
|||
TIMER_DOUBAO_ROLE= |
|||
@ -1,37 +0,0 @@ |
|||
# voitrans · 生产-新加坡 容器运行时配置(REGISTRY 由 prod-deploy.sh 注入) |
|||
|
|||
TAG=latest |
|||
PLATFORM=linux/amd64 |
|||
DOCKER_NETWORK=1panel-network |
|||
|
|||
CLUSTER_TAG=voitrans-a11-sg |
|||
ETCD_SERVERS=etcd:2379 |
|||
|
|||
# ── 存储(按新加坡生产实际填写)── |
|||
DB_ADMIN_DSN=root:CHANGE_ME@tcp(mysql:3306)/voitrans?charset=utf8mb4&parseTime=True&loc=Local |
|||
DB_DEVICE_DSN=postgresql://postgres:CHANGE_ME@postgres:5432/postgres?sslmode=require |
|||
DB_DEVICE_READ_DSN= |
|||
REDIS_ADDR=redis:6379 |
|||
REDIS_PASSWORD=CHANGE_ME |
|||
REDIS_DB=1 |
|||
REDIS_KEY_PREFIX=VoitransSG |
|||
|
|||
NATS_URL=nats://nats:4222 |
|||
|
|||
ANALYZE_APP_NAME=voitrans-a11-sg |
|||
ANALYZE_REGION=2 # 区域编码见 pb.Region(海外) |
|||
|
|||
LOG_DEBUG=false |
|||
GATEWAY_TOKEN_KEY=CHANGE_ME |
|||
# v2 配置接口响应加密 AES key(16/24/32 字节),须与 Flutter ApiCrypto._key 完全一致;留空=不加密 |
|||
GATEWAY_ENCRYPT_KEY= |
|||
API_ADMIN_ACCOUNT=admin |
|||
API_ADMIN_PASSWORD=CHANGE_ME |
|||
API_SITE_NAME=Voitrans |
|||
API_CURRENCY_TYPE=1 |
|||
MCP_ADDR=http://localhost:7300 |
|||
|
|||
AZURE_TRANSLATE_KEY= |
|||
TIMER_DOUBAO_APIKEY= |
|||
TIMER_DOUBAO_BOTID= |
|||
TIMER_DOUBAO_ROLE= |
|||
Loading…
Reference in new issue