Merge remote-tracking branch 'origin/main' into fix/settings-dotted-keys
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
# feature/svc-db-bind 任务卡
|
||||
|
||||
任务主记录:<PR 链接,task-finish 时填写>;状态:进行中;owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 3836ea88e3954878f257b7708ae5ccef947ccd79
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-svc-db-bind(worktree);分支:feature/svc-db-bind
|
||||
- 授权与交付终点:实现"服务↔数据库绑定"(svc.sh 服务级 db 子命令 + info/list 显示绑定)与
|
||||
"服务配置文件更新/即时生效"(config 子命令),并修复 adjust_args_for_bin 单横线误判;
|
||||
终点 = PR 合入 main(用户指示完成后一并合入;附 precheck / task-sync / 测试证据)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
目标:
|
||||
- **数据库绑定**:agent 服务可绑定/切换/解除数据库:`svc.sh <name> db [pg名 [--db X] | --clear]
|
||||
[--no-restart]`;缺省查看当前绑定与可用实例。绑定 = DSN 写入持久化 exec= + dbref=<pg>/<库>
|
||||
登记,实例运行中缺库自动建,服务运行中自动重启生效;--clear 回内嵌 SQLite 档。
|
||||
- **配置文件更新**:`svc.sh <name> config [file.json |--clear] [--no-restart]`;缺省查看当前
|
||||
配置文件(掩码内容 + `agent-system config explain -config` 视角的生效值)。切换前经二进制
|
||||
schema 强校验(未知键/非法值拒绝,不把服务重启进坏配置),通过后把 --config <绝对路径>
|
||||
写入 exec= + conffile= 登记,运行中自动重启即时生效(agent 无 SIGHUP 热重载,重启秒级);
|
||||
--clear 移除回命令行参数形态。绑定后改文件内容 + restart(或重跑 config 同文件)即生效。
|
||||
- **显示层全链路**:list 表新增 DB 列(含配置文件 dsn 反查);status 详情新增"数据库""配置
|
||||
文件"行;info 数据库节新增"绑定服务"反向映射、配置节附配置文件内容快照(敏感值掩码,
|
||||
--reveal 明文);无 dbref 的旧服务按 exec= --dsn 或配置文件 dsn 的 host:port 反查匹配实例
|
||||
(外部 DSN 显示 外部@host:port)。
|
||||
- **修复既有缺陷**:adjust_args_for_bin 以 grep '--dsn' 检测旗标支持,Go flag 帮助打印单横线
|
||||
`-dsn`,导致现役二进制的 --dsn/--auth 在 deploy 重建参数时被误删——改为"去横线旗标名 +
|
||||
1-2 横线"匹配。
|
||||
- record_state 跨重启保留 dbref/conffile;svc_del_key 辅助;db create 尾部提示绑定命令;
|
||||
usage/usage_short/help(lifecycle/config/db)/docs/svc.md(§2/§4.5/§4.6/§6/§10.1)同步。
|
||||
|
||||
非目标:不改 agent 二进制与部署/生命周期语义;不做 static/postgres 的绑定;不做运行中热切换
|
||||
(切换经重启生效);不做配置文件的编辑器(内容仍由用户维护,svc.sh 负责校验/切换/生效)。
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 3836ea88e3954878f257b7708ae5ccef947ccd79 | 3836ea88e3954878f257b7708ae5ccef947ccd79 | 2026-10-04T13:23:33Z |
|
||||
|
||||
## 结果与验证(完成后填写)
|
||||
|
||||
- 结果:已实现(数据库绑定与配置文件更新两块目标全部落地;`<name> db`/`<name> config`
|
||||
服务级动作与顶层 `db` 命令、`--config` 旗标并存无冲突)。
|
||||
- 验证(worktree runtime 实测,真实 agent 二进制 + PostgreSQL 18,2026-10-04):
|
||||
- `bash -n` 通过;
|
||||
- 数据库绑定:`t-app db bpg` → exec= 写入 --dsn、dbref=bpg/agent、自动重启生效;
|
||||
**record_state 重写 svc.env 后 dbref 保留**;PG 的 agent 库出现 **21 张表**(服务真实
|
||||
落库,非仅配置);`--db t2` 切换自动建库(t2 库 21 张表);`--clear` 干净回内嵌档;
|
||||
`--no-restart` 只改登记;错误路径(未登记实例/多余参数/postgres 服务)均 exit 2;
|
||||
- 配置文件更新:坏文件(未知键 bogus)被 schema 校验拒绝且不动 exec、不重启;合法文件
|
||||
绑定后 cmdline 含 `--config <abs>`、conffile= 跨 record_state 保留;**配置文件内的 dsn
|
||||
真实生效**(错误密码版本在日志中报 PG 认证失败=配置确已应用;正确密码版连上 PG、
|
||||
agent 库 21 张表);内容修改后重跑 `config <同一文件>` 再校验并重启(pid 更新);
|
||||
`--clear` 移除 --config/conffile 并重启;错误路径(文件不存在/双文件)exit 2;
|
||||
- db + config 共存:exec= 同时带 --dsn 与 --config,重启健康;
|
||||
- 显示层:list DB 列(含"仅配置文件带 dsn"场景的反查 bpg/agent)、`config` 查看视图
|
||||
(JSON 掩码 `"dsn": "***"` + explain 生效值 dsn 掩码)、status"数据库/配置文件"行、
|
||||
info 配置节文件内容快照、info 数据库节"绑定服务";手删 dbref 模拟旧部署 → 反查仍显示;
|
||||
- adjust 修复:正例(帮助含 `-dsn` 判支持)与负例(无 dsn 判不支持)均正确;
|
||||
- 回归:db dsn(掩码)/ db ls / info / history(新增 `set ok db bind →` / `config →` /
|
||||
`config unbind` 事件)/ --help 正常;测试现场(t-app/bpg、临时 JSON)已清除;
|
||||
- 正式验收(含配置文件更新提交):`make test` 于 head ecfed7b exit 0
|
||||
(boundaries/pins/libs/core/backend/frontend/ui/agentcmd 全过);另有前次 843495e 全过记录。
|
||||
- 正式验收:`make test` exit 0(test-boundaries/pins/libs/core/backend/frontend/ui/agentcmd 全过)。
|
||||
- 影响与剩余项:list 表加宽一列;老脚本无兼容性问题(dbref 缺省视为未绑定);
|
||||
发现并修复的 adjust_args_for_bin 缺陷影响既有 `deploy --dsn/--auth` 路径(此前被静默丢弃)。
|
||||
剩余项无。
|
||||
| 同步前验收 | 3836ea88e3954878f257b7708ae5ccef947ccd79 | 843495e0d0618b900d727e38570ff61451068c9b | 2026-10-04T13:30:59Z |
|
||||
| 同步前验收 | 3836ea88e3954878f257b7708ae5ccef947ccd79 | dc7a255f314b1afa35c25a2d0086845945520170 | 2026-10-04T13:43:45Z |
|
||||
+60
-10
@@ -64,10 +64,13 @@ scripts/svc.sh db create pg1 --port 8890 # initdb,只登记不启动
|
||||
scripts/svc.sh pg1 start # pg_ctl 启动 + 自动建库 + pg_isready
|
||||
scripts/svc.sh prod deploy --from dev@<sha> \
|
||||
--dsn "$(scripts/svc.sh db dsn pg1 --reveal)" # DSN 直接组合进部署
|
||||
scripts/svc.sh prod db pg1 --db prod # 或事后绑定/切换(缺库自动建,重启生效)
|
||||
scripts/svc.sh prod config .svc/prod.json # 指定/更换配置文件(校验+重启即时生效)
|
||||
|
||||
# 日常操作
|
||||
scripts/svc.sh list # 全服务总览
|
||||
scripts/svc.sh list # 全服务总览(含 DB 绑定列)
|
||||
scripts/svc.sh info # 综合报告:运行服务/数据库/配置快照
|
||||
scripts/svc.sh prod db # 查看 prod 的数据库绑定与可用实例
|
||||
scripts/svc.sh pg1 restart # 重启(复用上次配置)
|
||||
scripts/svc.sh prod logs 100 # 跟日志
|
||||
scripts/svc.sh history # 事件追溯
|
||||
@@ -216,6 +219,48 @@ scripts/svc.sh rm <name> --purge # 连 runtime/<name>/ 整个目录一起删
|
||||
|
||||
只管理本脚本登记过的服务;未登记名字会明确报错。
|
||||
|
||||
### 4.5 db(agent 服务的数据库绑定/切换)
|
||||
|
||||
```bash
|
||||
scripts/svc.sh <agent名> db # 查看当前绑定 + 可用实例
|
||||
scripts/svc.sh <agent名> db <pg名> [--db X] # 绑定/切换数据库
|
||||
scripts/svc.sh <agent名> db <pg名> --no-restart # 只改登记,下次 start 生效
|
||||
scripts/svc.sh <agent名> db --clear # 解除绑定(回内嵌 SQLite 档)
|
||||
```
|
||||
|
||||
每个 agent 服务都可以对应一个数据库实例中的库;绑定后 DSN 持久化在 `svc.env`
|
||||
(`exec=` 的 `--dsn` + `dbref=<pg名>/<库名>`,权限 600),跨重启保留。
|
||||
|
||||
- **缺库自动建**:实例运行中且目标库不存在时自动 `CREATE DATABASE`(幂等);
|
||||
实例未运行且目标库非其默认库 → 提示先 `start` 实例;
|
||||
- **运行中自动重启生效**(复用新 `exec=`);`--no-restart` 只写登记;
|
||||
- **旧产物预检**:当前二进制不识别 `--dsn` 时警告(绑定已登记但重启按内嵌档跑);
|
||||
- 展示:`list` 的 **DB 列**、`status` 的"数据库"行、`info` 数据库节的**绑定服务**行
|
||||
(反向映射:哪些服务指向该实例)都能看到服务↔数据库对应关系;
|
||||
- 未用本命令、直接 `deploy --dsn` 的服务,显示层会按 DSN 的 host:port 反查匹配已
|
||||
登记实例(显示 `<pg名>/<库>`;匹配不上显示 `外部@host:port`);
|
||||
- `--clear` 后服务回到内嵌 SQLite 档(数据在原 `--data` 目录,切回即见旧数据)。
|
||||
|
||||
### 4.6 config(agent 服务的配置文件更新/生效)
|
||||
|
||||
```bash
|
||||
scripts/svc.sh <agent名> config # 查看当前配置文件(掩码内容 + agent 视角生效值)
|
||||
scripts/svc.sh <agent名> config <file.json> # 更换使用的配置文件(校验通过才切换)
|
||||
scripts/svc.sh <agent名> config <file> --no-restart # 只改登记,下次 start 生效
|
||||
scripts/svc.sh <agent名> config --clear # 移除(回命令行参数形态)
|
||||
```
|
||||
|
||||
配置文件是 agent 二进制的 `--config`(JSON,schema 严格校验;可用
|
||||
`agent-system config explain -config <file>` 预览每个键的来源与生效值)。
|
||||
|
||||
- **切换前强校验**:文件必须存在且通过二进制的 schema 校验(未知键/非法值直接拒绝,
|
||||
不会把服务重启进坏配置);旧产物不识别 `--config` 时警告;
|
||||
- **即时生效**:运行中的服务自动重启应用新配置(agent 无 SIGHUP 热重载,重启秒级);
|
||||
绑定后日常改配置 = 编辑文件 + `restart`(或重跑 `config <同一文件>` 再校验一遍);
|
||||
- 持久化:`exec=` 的 `--config <绝对路径>` + `conffile=` 键(跨重启保留,600);
|
||||
- 展示:`status`/`info <name>` 的"配置文件"行、`info` 配置节的文件内容快照
|
||||
(敏感值掩码,`info --reveal` 显示明文);文件丢失时明确告警并提示恢复或 `--clear`。
|
||||
|
||||
## 5. 命令参考:部署来源
|
||||
|
||||
`deploy --from` 支持三种来源(**缺省 = `dev` 远端最新**),agent 与 static 共用:
|
||||
@@ -287,12 +332,15 @@ scripts/svc.sh list [--tag T] # status 等价
|
||||
扫描 `runtime/*/svc.env` 枚举全部已登记服务:
|
||||
|
||||
```
|
||||
NAME KIND STATE PID ADDR SOURCE TAGS
|
||||
pg1 postgres 运行中 3115294 127.0.0.1:8890 postgres@local core,local
|
||||
prod agent 运行中 3117059 127.0.0.1:8791 release@0.1.0 stable
|
||||
ui-qa static 未运行 - 127.0.0.1:9054 dir@/tmp/static-demo ui
|
||||
NAME KIND STATE PID ADDR DB SOURCE TAGS
|
||||
pg1 postgres 运行中 3115294 127.0.0.1:8890 - postgres@local core,local
|
||||
prod agent 运行中 3117059 127.0.0.1:8791 pg1/prod release@0.1.0 stable
|
||||
ui-qa static 未运行 - 127.0.0.1:9054 - dir@/tmp/static-demo ui
|
||||
```
|
||||
|
||||
DB 列:agent 服务当前绑定的 `<pg名>/<库名>`(见 §4.5;外部 DSN 显示 `外部@host:port`,
|
||||
未绑定显示 `-`)。
|
||||
|
||||
STATE 的判定:pidfile 存在且 `/proc/<pid>/cmdline` 匹配登记的 `pidcheck` 标记
|
||||
(agent=二进制路径、postgres=pgdata 路径、static=`--directory <目录>`)。进程被人手动
|
||||
杀掉后 STATE 自动落回"未运行",不会误报。
|
||||
@@ -315,10 +363,10 @@ scripts/svc.sh info [name] [--tag T] [--reveal]
|
||||
|
||||
报告分节(整体走 stdout,可直接 `> snapshot.txt` 整页保存):
|
||||
|
||||
1. **服务**——`list` 同款表格 + 汇总行(`N 已登记 / M 运行中(agent a、postgres p、static s)`);
|
||||
1. **服务**——`list` 同款表格(含 **DB 绑定列**)+ 汇总行(`N 已登记 / M 运行中(agent a、postgres p、static s)`);
|
||||
2. **数据库**——每个 postgres 实例一屏:运行状态/地址、版本/用户/认证/默认库/来源、
|
||||
数据目录与体量、备份份数与最近一份;**运行中的实例直接列出内部数据库及大小**
|
||||
(未运行则注明内部库不可见);
|
||||
**绑定服务**(哪些 agent 服务指向该实例,见 §4.5)、数据目录与体量、备份份数与
|
||||
最近一份;**运行中的实例直接列出内部数据库及大小**(未运行则注明内部库不可见);
|
||||
3. **配置**——逐服务 `runtime/<name>/svc.env` 的文件路径、权限与逐行内容快照,加上
|
||||
全局层:`.svc.env`(自动加载层)、本次 `--config/--env` 文件、shell 已导出的
|
||||
`SVC_*` 变量;
|
||||
@@ -549,7 +597,9 @@ scripts/svc.sh prod deploy --env .env.prod # 等价
|
||||
| `version` | 产物版本串(`agent-system version` 输出) |
|
||||
| `started` | 最近启动时间 |
|
||||
| `pidcheck` | 进程判定标记(见 §6.1) |
|
||||
| `exec` | agent 上次完整启动参数 |
|
||||
| `exec` | agent 上次完整启动参数(含 `--dsn`,密码在其中) |
|
||||
| `dbref` | agent 数据库绑定 `<pg名>/<库名>`(`<name> db` 命令写入,跨重启保留;见 §4.5) |
|
||||
| `conffile` | agent 配置文件绝对路径(`<name> config` 命令写入,跨重启保留;见 §4.6) |
|
||||
| `user` / `password` / `dbname` / `authmode` / `pgdata` | postgres 专属 |
|
||||
| `htdocs` | static 专属 |
|
||||
|
||||
@@ -563,7 +613,7 @@ scripts/svc.sh prod deploy --env .env.prod # 等价
|
||||
| `deploy` | begin / ok / fail | agent/static 部署全程 |
|
||||
| `start` | ok / fail / warn | 启动(fail 含"进程即退/健康检查超时";warn 为进程起但探活未过) |
|
||||
| `stop` | ok / fail | 停止(含 pg_ctl fast / SIGTERM 兜底说明) |
|
||||
| `set` | ok | 登记变更(`addr 旧 → 新`) |
|
||||
| `set` | ok | 登记变更(`addr 旧 → 新`;`db bind → <pg>/<库>` / `db unbind`) |
|
||||
| `backup` | ok / fail | 备份(文件、大小) |
|
||||
| `restore` | ok / fail | 恢复(库、文件、表数) |
|
||||
| `clone` | ok / fail | 克隆(物理/在线) |
|
||||
|
||||
+390
-11
@@ -5,6 +5,8 @@
|
||||
#
|
||||
# ════════════════════ 命令一览 ════════════════════
|
||||
# scripts/svc.sh <name> [action] [参数...] 服务级:deploy(默认)|start|stop|restart|status|logs [N]
|
||||
# |db [pg名|--clear](agent 数据库绑定/切换)
|
||||
# |config [file|--clear](agent 配置文件更新/生效)
|
||||
# scripts/svc.sh list [--tag T] 服务总览(status 等价)
|
||||
# scripts/svc.sh info [name] [--tag T] [--reveal] 综合报告(只读):服务/数据库/配置快照/最近事件
|
||||
# scripts/svc.sh set <name> [--port N --apply] [--tag T] 变更登记(换端口/标签)
|
||||
@@ -39,6 +41,10 @@
|
||||
# --kind agent|static deploy 时指定类型(postgres 用 db create 建)
|
||||
# --dir PATH static 直接服务既有目录(不构建)
|
||||
# --plugins/--dsn/--auth/--mock/--data agent 运行参数
|
||||
# <name> db <pg名> [--db X] [--no-restart] agent 绑定/切换数据库(实例运行中缺库自动建;
|
||||
# --clear 解绑回内嵌 SQLite;无参查看当前绑定与可用实例)
|
||||
# <name> config <file.json> [--no-restart] agent 更换使用的配置文件(schema 校验通过才切换,
|
||||
# 运行中自动重启即时生效;--clear 移除;无参查看当前配置与生效值)
|
||||
# --model-base/--model-key/--model-name 与 --light-*/--tavily-keys 启动后播种设置
|
||||
# -- <剩余参数> 原样透传给 agent-system serve(escape hatch)
|
||||
# db create:--user U(默认 agent)--password P(缺省自动生成)--db NAME --auth-mode scram-sha-256|md5
|
||||
@@ -69,6 +75,11 @@
|
||||
# scripts/svc.sh set pg1 --port 8895 --apply # 换端口并重启生效
|
||||
# scripts/svc.sh db backup pg1 && scripts/svc.sh db backups pg1
|
||||
# scripts/svc.sh db clone pg1 pg2 --port 8896 # 物理克隆一套到新端口
|
||||
# scripts/svc.sh dev db pg1 --db dev # 把 dev 服务绑定到 pg1/dev 库(自动重启)
|
||||
# scripts/svc.sh dev db pg2 --no-restart # 切换但不重启(下次 start 生效)
|
||||
# scripts/svc.sh dev db --clear # 解绑回内嵌 SQLite 档
|
||||
# scripts/svc.sh dev config .svc/dev.json # 更换配置文件(校验+重启即时生效)
|
||||
# scripts/svc.sh dev config # 查看当前配置文件与生效值
|
||||
# scripts/svc.sh db copy pg1 pg2 --db app # 把 pg1 的 app 库搬到 pg2
|
||||
# scripts/svc.sh db restore pg2 --file <dump> --db app --force
|
||||
# scripts/svc.sh list --tag core && scripts/svc.sh history
|
||||
@@ -123,6 +134,8 @@ svc.sh —— 本机服务管理:后端(agent) / 前端(static) / 数据库(po
|
||||
scripts/svc.sh <name> deploy [--from dev@<sha>] 部署并启动(缺省=远端 main 最新)
|
||||
scripts/svc.sh <name> start|stop|restart|logs [N] 生命周期与日志
|
||||
scripts/svc.sh <name> status 服务详情(版本/来源/DSN 等)
|
||||
scripts/svc.sh <name> db [pg名|--clear] agent 数据库绑定/切换(缺省查看)
|
||||
scripts/svc.sh <name> config [file|--clear] agent 配置文件更新/即时生效(缺省查看)
|
||||
scripts/svc.sh db create pg1 --port 8890 建数据库
|
||||
scripts/svc.sh db dsn|backup|backups|restore|clone|copy|ls|set pg1 ... 数据库运维
|
||||
scripts/svc.sh set pg1 --port 8895 --apply 换端口(--tag 改标签)
|
||||
@@ -253,6 +266,14 @@ EOF
|
||||
scripts/svc.sh <name> start 启动(agent/static/postgres 分别分派;已在运行则提示)
|
||||
scripts/svc.sh <name> stop 停止(agent/static: TERM→KILL;postgres: pg_ctl -m fast)
|
||||
scripts/svc.sh <name> restart 停止 + 启动(裸 restart 复用上次配置)
|
||||
scripts/svc.sh <name> db [pg名|--clear] [--db X] agent 数据库绑定:缺省查看当前
|
||||
绑定与可用实例;给 pg名=绑定/切换(DSN 写入 exec=,dbref= 登记,
|
||||
实例运行中缺库自动建,服务运行中自动重启生效);--clear 解绑回内嵌
|
||||
SQLite 档;--no-restart 只改登记
|
||||
scripts/svc.sh <name> config [file|--clear] [--no-restart] agent 配置文件:缺省查看当前
|
||||
文件与生效值;给 file=更换(agent 二进制 --config,JSON schema 校验
|
||||
通过才切换,运行中自动重启即时生效);--clear 移除回命令行参数形态。
|
||||
绑定后改文件内容 + restart 即生效(agent 无热重载,重启秒级)
|
||||
scripts/svc.sh <name> logs [N] 跟随日志(默认 50 行起;如 svc.sh pg1 logs 200)
|
||||
scripts/svc.sh <name> status 单服务详情(kind/状态/地址/来源/版本/标签/DSN/目录)
|
||||
scripts/svc.sh list [--tag T] 全服务表格:NAME KIND STATE PID ADDR SOURCE TAGS
|
||||
@@ -308,6 +329,14 @@ EOF
|
||||
连接其他服务:DSN 可直接组合进 agent 部署——
|
||||
scripts/svc.sh prod deploy --from dev@<sha> --dsn "$(scripts/svc.sh db dsn pg1 --reveal)"
|
||||
(旧版本产物不识别 --dsn 时会自动省略并提示。)
|
||||
更常用:让已部署的 agent 服务绑定/切换数据库(不用重新 deploy)——
|
||||
scripts/svc.sh prod db pg1 # 绑定 pg1 默认库(运行中自动重启生效)
|
||||
scripts/svc.sh prod db pg1 --db prod # 绑定 pg1/prod 库(缺库自动建)
|
||||
scripts/svc.sh prod db pg2 --no-restart # 切换实例但暂不重启(下次 start 生效)
|
||||
scripts/svc.sh prod db # 查看当前绑定 + 可用实例
|
||||
scripts/svc.sh prod db --clear # 解绑回内嵌 SQLite 档
|
||||
绑定写入 svc.env(exec= 的 --dsn + dbref=,600);info / list 的 DB 列与数据库节
|
||||
"绑定服务"行展示服务↔实例对应关系。
|
||||
|
||||
密码管理:密码明文存 runtime/<name>/svc.env(600);--password 未给时自动生成
|
||||
(head -c 18 /dev/urandom)。auth-mode 默认 scram-sha-256(pg_hba 全线)。
|
||||
@@ -375,6 +404,11 @@ dotenv 格式:KEY=VALUE,支持 # 注释、成对引号、export 前缀;只
|
||||
- 裸 start/restart 复用 exec=(上次完整参数);任何显式参数(或 --config 文件、
|
||||
SVC_* 变量)出现即整体重建参数
|
||||
- tags 随每次登记合并去重,跨重启保留
|
||||
|
||||
服务级配置文件(与上述 svc.sh dotenv 层不同——这是 agent 二进制自己的 JSON 配置):
|
||||
scripts/svc.sh <name> config [file|--clear] 更换/移除/查看(见 help lifecycle);
|
||||
切换前 schema 校验(agent-system config explain -config <file>),运行中自动重启
|
||||
即时生效;登记于 svc.env conffile=,info/status 可见。
|
||||
EOF
|
||||
return 0 ;;
|
||||
examples)
|
||||
@@ -625,14 +659,17 @@ svc_merge_tags() {
|
||||
}
|
||||
|
||||
# record_state EXTRA_KEY=VALUE...:把当前态+持久化配置写入 svc.env(600)。
|
||||
# 基本键:kind/env/pid/addr/tags/source/version/started/pidcheck/exec;
|
||||
# kind 特有键(user/password/dbname/authmode/pgdata/htdocs...)经参数追加。
|
||||
# 基本键:kind/env/pid/addr/tags/source/version/started/pidcheck/exec + dbref/
|
||||
# conffile(跨重启保留的数据库绑定与配置文件);kind 特有键(user/password/dbname/
|
||||
# authmode/pgdata/htdocs...)经参数追加。
|
||||
record_state() {
|
||||
# 先读完旧文件再开重定向(块内 {} > svc.env 会先截断文件,读旧值必须在其之前)。
|
||||
local pid prev_src merged_tags
|
||||
local pid prev_src prev_dbref prev_conffile merged_tags
|
||||
pid="$(read_pid)"
|
||||
prev_src=""
|
||||
[ -f "$ENV_DIR/svc.env" ] && prev_src="$(svc_get source)"
|
||||
prev_dbref="$(svc_get dbref)"
|
||||
prev_conffile="$(svc_get conffile)"
|
||||
SVC_SOURCE="${SRC_LABEL:-${prev_src:-}}"
|
||||
merged_tags="$(svc_merge_tags)"
|
||||
mkdir -p "$ENV_DIR"
|
||||
@@ -647,6 +684,8 @@ record_state() {
|
||||
printf 'started=%s\n' "$(date -Is)"
|
||||
printf 'pidcheck=%s\n' "${SVC_PIDCHECK:-}"
|
||||
printf 'exec=%s\n' "${SVC_EXEC:-}"
|
||||
[ -n "$prev_dbref" ] && printf 'dbref=%s\n' "$prev_dbref"
|
||||
[ -n "$prev_conffile" ] && printf 'conffile=%s\n' "$prev_conffile"
|
||||
[ $# -gt 0 ] && printf '%s\n' "$@"
|
||||
} > "$ENV_DIR/svc.env"
|
||||
chmod 600 "$ENV_DIR/svc.env"
|
||||
@@ -769,14 +808,17 @@ agent_has_explicit_cfg() {
|
||||
}
|
||||
|
||||
# adjust_args_for_bin BIN ARGS:按二进制 serve 用法过滤旧产物不认识的 --dsn/--auth。
|
||||
# 匹配按"去横线的旗标名 + 1-2 个横线"(Go flag 帮助打印单横线 -dsn,2026-10-04 实测
|
||||
# 旧实现 grep -- '--dsn' 永不命中,把现役二进制的 --dsn/--auth 误当不支持而丢弃)。
|
||||
adjust_args_for_bin() {
|
||||
local bin="$1" args="$2" out
|
||||
out="$("$bin" serve -h 2>&1 || true)"
|
||||
local filtered="" w drop_next=0
|
||||
local filtered="" w drop_next=0 flag
|
||||
for w in $args; do
|
||||
case "$w" in
|
||||
--dsn|--auth)
|
||||
if printf '%s\n' "$out" | grep -q -- "$w"; then
|
||||
flag="${w#--}"
|
||||
if printf '%s\n' "$out" | grep -Eq -- "(^|[[:space:]])-{1,2}${flag}([[:space:]]|=|$)"; then
|
||||
filtered="$filtered $w"
|
||||
else
|
||||
drop_next=1
|
||||
@@ -1412,6 +1454,7 @@ db_create() {
|
||||
hist_event create ok "" "user=$user db=$dbname authmode=$authmode port=${ENV_ADDR##*:}"
|
||||
c_ok "[$ENV_NAME] 数据库已创建(未启动):scripts/svc.sh $ENV_NAME start"
|
||||
c_info "[$ENV_NAME] 连接串:scripts/svc.sh db dsn $ENV_NAME --reveal(密码存 svc.env,权限 600)"
|
||||
c_info "[$ENV_NAME] 服务绑定:scripts/svc.sh <agent名> db $ENV_NAME [--db X](运行中自动重启生效)"
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -1541,6 +1584,16 @@ svc_set_key() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# svc_del_key KEY:从当前服务 svc.env 删除单键(键不存在则无操作)。
|
||||
svc_del_key() {
|
||||
local key="$1"
|
||||
[ -f "$ENV_DIR/svc.env" ] || return 0
|
||||
{ grep -v "^$key=" "$ENV_DIR/svc.env" || true; } > "$ENV_DIR/svc.env.tmp"
|
||||
mv "$ENV_DIR/svc.env.tmp" "$ENV_DIR/svc.env"
|
||||
chmod 600 "$ENV_DIR/svc.env"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 显式参数版 psql/dump/restore:clone/copy 需要同时操作两个实例,不依赖全局 ENV_*。
|
||||
pg_run() { # HOST PORT USER PASS DB SQL [tA]
|
||||
local host="$1" port="$2" user="$3" pass="$4" db="$5" sql="$6" ta="${7:-}" pgbin
|
||||
@@ -1860,6 +1913,259 @@ db_ls() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# mask_json TEXT:JSON 文本里键名含 key/pass/secret/token/dsn(不区分大小写)的
|
||||
# 字符串值掩为 ***(配置文件内容快照用)。输出带尾换行。
|
||||
mask_json() {
|
||||
printf '%s\n' "$1" | sed -E 's/("[^"]*(key|pass|secret|token|dsn)[^"]*"[[:space:]]*:[[:space:]]*")[^"]*"/\1***"/Ig'
|
||||
}
|
||||
|
||||
# ---- agent 服务的配置文件(<name> config ...)----
|
||||
|
||||
# svc_config [FILE|--clear] [--no-restart]:查看 / 更新 / 移除 agent 服务使用的
|
||||
# 配置文件(agent 二进制的 --config,JSON,schema 严格校验)。更新 = 校验通过后
|
||||
# 把 --config <绝对路径> 写入持久化 exec= 并登记 conffile=,运行中自动重启生效
|
||||
# (agent 无 SIGHUP 热重载,重启即"及时生效",秒级);--clear 移除回命令行参数形态。
|
||||
svc_config() {
|
||||
[ "$SVC_KIND" = agent ] || { c_err "[$ENV_NAME] kind=$SVC_KIND 无配置文件绑定(仅 agent 服务)"; return 2; }
|
||||
[ -f "$ENV_DIR/svc.env" ] || { c_err "[$ENV_NAME] 无登记(先 deploy)"; return 2; }
|
||||
local target="" clear=0 restart=1
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--clear) clear=1; shift ;;
|
||||
--no-restart) restart=0; shift ;;
|
||||
-h|--help)
|
||||
cat >&2 <<EOF
|
||||
[$ENV_NAME] 配置文件:
|
||||
scripts/svc.sh $ENV_NAME config 查看当前配置文件与生效值
|
||||
scripts/svc.sh $ENV_NAME config <file.json> [--no-restart] 更新使用的配置文件
|
||||
(schema 校验通过才切换;运行中自动重启生效)
|
||||
scripts/svc.sh $ENV_NAME config --clear [--no-restart] 移除(回命令行参数形态)
|
||||
提示:配置文件绑定后,改文件内容 + restart 即生效;config <同一文件> 也会校验并重启。
|
||||
EOF
|
||||
return 0 ;;
|
||||
-*) c_err "[$ENV_NAME] config 未知参数:$1(可用:<文件> | --clear,--no-restart)"; return 2 ;;
|
||||
*) [ -z "$target" ] || { c_err "[$ENV_NAME] config 只接受一个文件路径"; return 2; }
|
||||
target="$1"; shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 当前配置文件(conffile= 优先;旧服务回退 exec= 的 --config)。
|
||||
local cur=""
|
||||
cur="$(svc_get conffile)"
|
||||
[ -z "$cur" ] && cur="$(printf '%s' "$(svc_get exec)" | sed -n 's/.*--config \([^ ]*\).*/\1/p')"
|
||||
|
||||
# 无参:查看(文件状态 + 掩码内容 + 二进制视角的生效值)。
|
||||
if [ -z "$target" ] && [ "$clear" = 0 ]; then
|
||||
if [ -n "$cur" ]; then
|
||||
printf ' [%s] 配置文件:%s\n' "$ENV_NAME" "$cur"
|
||||
if [ -f "$cur" ]; then
|
||||
printf ' 权限 %s;内容(敏感值掩码):\n' "$(info_file_mode "$cur")"
|
||||
mask_json "$(cat "$cur")" | sed 's/^/ /'
|
||||
if [ -x "$ENV_BIN" ] && "$ENV_BIN" config explain -config "$cur" >/dev/null 2>&1; then
|
||||
printf ' 生效值(agent 视角,敏感值掩码):\n'
|
||||
"$ENV_BIN" config explain -config "$cur" 2>/dev/null \
|
||||
| sed -E 's#(postgres(ql)?://[^:/@[:space:]]+:)[^@[:space:]]+@#\1***@#g; s/^/ /'
|
||||
else
|
||||
printf ' (当前产物无法校验该文件:config explain 不可用或文件不合法)\n'
|
||||
fi
|
||||
else
|
||||
printf ' 文件不存在!重启将失败——请恢复文件或 config --clear\n'
|
||||
fi
|
||||
else
|
||||
printf ' [%s] 配置文件:无(命令行参数形态;绑定:config <file.json>)\n' "$ENV_NAME"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 移除。
|
||||
if [ "$clear" = 1 ]; then
|
||||
[ -n "$target" ] && { c_err "--clear 不接受文件参数"; return 2; }
|
||||
local exec_old new_exec
|
||||
exec_old="$(svc_get exec)"
|
||||
if printf '%s' "$exec_old" | grep -q -- '--config '; then
|
||||
new_exec="$(printf '%s' "$exec_old" | sed -E 's/(^| )--config [^ ]+//; s/ +/ /g; s/^ //; s/ $//')"
|
||||
svc_set_key exec "$new_exec"
|
||||
fi
|
||||
svc_del_key conffile
|
||||
hist_event set ok "$(read_pid)" "config unbind"
|
||||
c_ok "[$ENV_NAME] 已移除配置文件(回命令行参数形态)"
|
||||
if [ "$restart" = 1 ] && [ -n "$(read_pid)" ] && pid_alive "$(read_pid)"; then
|
||||
do_stop; do_start || { c_err "[$ENV_NAME] 重启失败——移除已登记,排查后 start"; return 1; }
|
||||
else
|
||||
c_info "[$ENV_NAME] 未运行或 --no-restart——下次 start 生效"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 更新。
|
||||
[ -f "$target" ] || { c_err "配置文件不存在:$target"; return 2; }
|
||||
local abs
|
||||
abs="$(cd "$(dirname "$target")" && pwd)/$(basename "$target")"
|
||||
if [ -x "$ENV_BIN" ]; then
|
||||
if ! "$ENV_BIN" config explain -config "$abs" >/dev/null 2>&1; then
|
||||
c_err "[$ENV_NAME] 配置文件未通过 schema 校验:$abs"
|
||||
"$ENV_BIN" config explain -config "$abs" >&2 || true
|
||||
c_err "(键与取值以 config explain 为准;先修正文件再切换)"
|
||||
return 2
|
||||
fi
|
||||
if ! "$ENV_BIN" serve -h 2>&1 | grep -Eq -- "(^|[[:space:]])-{1,2}config([[:space:]]|=|$)"; then
|
||||
c_warn "[$ENV_NAME] 当前产物不识别 --config(旧版本二进制)——绑定已登记,但重启将忽略该参数"
|
||||
fi
|
||||
fi
|
||||
local exec_old new_exec
|
||||
exec_old="$(svc_get exec)"
|
||||
if printf '%s' "$exec_old" | grep -q -- '--config '; then
|
||||
new_exec="$(printf '%s' "$exec_old" | sed -E "s|--config [^ ]+|--config $abs|")"
|
||||
else
|
||||
new_exec="${exec_old:+$exec_old }--config $abs"
|
||||
fi
|
||||
svc_set_key exec "$new_exec"
|
||||
svc_set_key conffile "$abs"
|
||||
hist_event set ok "$(read_pid)" "config → $abs"
|
||||
c_ok "[$ENV_NAME] 配置文件已更新:$abs(schema 校验通过;svc.env 600 保存)"
|
||||
if [ "$restart" = 1 ] && [ -n "$(read_pid)" ] && pid_alive "$(read_pid)"; then
|
||||
c_info "[$ENV_NAME] 重启以应用新配置……"
|
||||
do_stop
|
||||
do_start || { c_err "[$ENV_NAME] 重启失败——绑定已登记,排查日志后 scripts/svc.sh $ENV_NAME start"; return 1; }
|
||||
c_ok "[$ENV_NAME] 新配置已生效:$abs"
|
||||
else
|
||||
c_info "[$ENV_NAME] 未运行或 --no-restart——下次 start 生效"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# ---- agent 服务的数据库绑定(<name> db ...)----
|
||||
|
||||
# svc_db_bind [pg名] [--db X] [--clear] [--no-restart]:查看 / 绑定切换 / 解除
|
||||
# agent 服务的数据库。绑定 = 目标实例的 DSN 写入持久化 exec=(--dsn,密码 600 存放)
|
||||
# 并登记 dbref=<pg>/<库>;实例运行中时确保目标库存在(缺则建);服务运行中则自动
|
||||
# 重启生效(--no-restart 只改登记)。--clear 解除绑定(回内嵌 SQLite 档)。
|
||||
svc_db_bind() {
|
||||
[ "$SVC_KIND" = agent ] || { c_err "[$ENV_NAME] kind=$SVC_KIND 无数据库绑定(仅 agent 服务)"; return 2; }
|
||||
[ -f "$ENV_DIR/svc.env" ] || { c_err "[$ENV_NAME] 无登记(先 deploy)"; return 2; }
|
||||
local target="" clear=0 restart=1
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--clear) clear=1; shift ;;
|
||||
--no-restart) restart=0; shift ;;
|
||||
--db) need_value "$@"; CLI_DB_NAME="$2"; shift 2 ;;
|
||||
-h|--help)
|
||||
cat >&2 <<EOF
|
||||
[$ENV_NAME] 数据库绑定:
|
||||
scripts/svc.sh $ENV_NAME db 查看当前绑定与可用实例
|
||||
scripts/svc.sh $ENV_NAME db <pg名> [--db X] [--no-restart] 绑定/切换(运行中自动重启生效)
|
||||
scripts/svc.sh $ENV_NAME db --clear [--no-restart] 解除绑定(回内嵌 SQLite 档)
|
||||
EOF
|
||||
return 0 ;;
|
||||
-*) c_err "[$ENV_NAME] db 未知参数:$1(可用:<pg名> | --clear,--db X,--no-restart)"; return 2 ;;
|
||||
*) [ -z "$target" ] || { c_err "[$ENV_NAME] db 只接受一个 postgres 服务名"; return 2; }
|
||||
target="$1"; shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 无参:查看当前绑定 + 可用实例。
|
||||
if [ -z "$target" ] && [ "$clear" = 0 ]; then
|
||||
local ref dsn pg st
|
||||
ref="$(db_ref_of "$ENV_NAME")"
|
||||
if [ -n "$ref" ]; then
|
||||
printf ' [%s] 数据库绑定:%s\n' "$ENV_NAME" "$ref"
|
||||
dsn="$(printf '%s' "$(svc_get exec)" | sed -n 's/.*--dsn \([^ ]*\).*/\1/p')"
|
||||
[ -n "$dsn" ] && printf ' DSN %s\n' "$(mask_dsn "$dsn")"
|
||||
else
|
||||
printf ' [%s] 数据库绑定:无(内嵌 SQLite 档,data=%s)\n' "$ENV_NAME" "$ENV_DATA"
|
||||
fi
|
||||
printf ' 可用实例:\n'
|
||||
local found=0
|
||||
while IFS= read -r pg; do
|
||||
st="$(svc_pid_state "$pg")"
|
||||
printf ' %-14s %-7s %s\n' "$pg" \
|
||||
"$(if [ "${st%% *}" = alive ]; then printf 运行中; else printf 未运行; fi)" \
|
||||
"$(env_kv_of "$pg" addr)"
|
||||
found=1
|
||||
done < <(registered_names | while IFS= read -r n; do [ "$(svc_kind "$n")" = postgres ] && printf '%s\n' "$n"; done)
|
||||
[ "$found" = 0 ] && printf ' (无;scripts/svc.sh db create <name> 创建)\n'
|
||||
printf ' 切换:scripts/svc.sh %s db <pg名> [--db X];解除:--clear\n' "$ENV_NAME"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 解除绑定。
|
||||
if [ "$clear" = 1 ]; then
|
||||
[ -n "$target" ] && { c_err "--clear 不接受服务名参数"; return 2; }
|
||||
local exec_old new_exec
|
||||
exec_old="$(svc_get exec)"
|
||||
if printf '%s' "$exec_old" | grep -q -- '--dsn '; then
|
||||
new_exec="$(printf '%s' "$exec_old" | sed -E 's/(^| )--dsn [^ ]+//; s/ +/ /g; s/^ //; s/ $//')"
|
||||
svc_set_key exec "$new_exec"
|
||||
fi
|
||||
svc_del_key dbref
|
||||
hist_event set ok "$(read_pid)" "db unbind"
|
||||
c_ok "[$ENV_NAME] 已解除数据库绑定(内嵌 SQLite 档;数据目录 $ENV_DATA)"
|
||||
if [ "$restart" = 1 ] && [ -n "$(read_pid)" ] && pid_alive "$(read_pid)"; then
|
||||
do_stop; do_start || { c_err "[$ENV_NAME] 重启失败——解绑已登记,排查后 start"; return 1; }
|
||||
c_ok "[$ENV_NAME] 已重启(内嵌 SQLite 档)"
|
||||
else
|
||||
c_info "[$ENV_NAME] 未运行或 --no-restart——下次 start 生效"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 绑定/切换。
|
||||
valid_env "$target" || { c_err "非法服务名:$target"; return 2; }
|
||||
[ -f "$RUNTIME/$target/svc.env" ] || { c_err "[$target] 未登记(scripts/svc.sh db create $target)"; return 2; }
|
||||
[ "$(svc_kind "$target")" = postgres ] || { c_err "[$target] kind=$(svc_kind "$target") 不是 postgres 实例"; return 2; }
|
||||
local taddr thost tport tuser tpass regdb tdb dsn st
|
||||
taddr="$(env_kv_of "$target" addr)"
|
||||
thost="${taddr%:*}"; tport="${taddr##*:}"
|
||||
tuser="$(env_kv_of "$target" user)"
|
||||
tpass="$(env_kv_of "$target" password)"
|
||||
regdb="$(env_kv_of "$target" dbname)"
|
||||
tdb="${CLI_DB_NAME:-$regdb}"
|
||||
{ [ -n "$tuser" ] && [ -n "$tpass" ]; } || { c_err "[$target] 登记缺 user/password(重新 db create)"; return 2; }
|
||||
# 实例运行中:确保目标库存在(缺则建,幂等)。
|
||||
st="$(svc_pid_state "$target")"
|
||||
if [ "${st%% *}" = alive ]; then
|
||||
if ! pg_run "$thost" "$tport" "$tuser" "$tpass" postgres "SELECT 1 FROM pg_database WHERE datname='$tdb'" tA | grep -q 1; then
|
||||
if pg_run "$thost" "$tport" "$tuser" "$tpass" postgres "CREATE DATABASE \"$tdb\"" >/dev/null; then
|
||||
c_ok "[$target] 已建库:$tdb"
|
||||
else
|
||||
c_warn "[$target] 建库 $tdb 未成功——绑定继续,请手动建库后重启服务"
|
||||
fi
|
||||
fi
|
||||
elif [ "$tdb" != "$regdb" ]; then
|
||||
c_warn "[$target] 未运行——库 $tdb 暂未创建(先 start $target 再重跑,或绑定默认库 $regdb)"
|
||||
fi
|
||||
dsn="postgresql://$tuser:$tpass@$thost:$tport/$tdb"
|
||||
# 产物支持预检(旧二进制不识别 --dsn 时提示;重启复用 exec 不会自动过滤)。
|
||||
if [ -x "$ENV_BIN" ]; then
|
||||
local adj
|
||||
adj="$(adjust_args_for_bin "$ENV_BIN" "--dsn $dsn")"
|
||||
[ "$adj" != "--dsn $dsn" ] && \
|
||||
c_warn "[$ENV_NAME] 当前产物不识别 --dsn(旧版本二进制)——绑定已登记,但重启后将按内嵌档运行"
|
||||
fi
|
||||
# 写持久化配置:exec= 的 --dsn 替换或追加;dbref= 登记。
|
||||
local exec_old new_exec
|
||||
exec_old="$(svc_get exec)"
|
||||
if printf '%s' "$exec_old" | grep -q -- '--dsn '; then
|
||||
new_exec="$(printf '%s' "$exec_old" | sed -E "s|--dsn [^ ]+|--dsn $dsn|")"
|
||||
else
|
||||
new_exec="${exec_old:+$exec_old }--dsn $dsn"
|
||||
fi
|
||||
svc_set_key exec "$new_exec"
|
||||
svc_set_key dbref "$target/$tdb"
|
||||
hist_event set ok "$(read_pid)" "db bind → $target/$tdb"
|
||||
c_ok "[$ENV_NAME] 数据库绑定:$target/$tdb(DSN $(mask_dsn "$dsn");svc.env 600 保存,info 可查)"
|
||||
# 运行中:重启生效(start 复用新 exec=)。
|
||||
if [ "$restart" = 1 ] && [ -n "$(read_pid)" ] && pid_alive "$(read_pid)"; then
|
||||
c_info "[$ENV_NAME] 重启以切换数据库……"
|
||||
do_stop
|
||||
do_start || { c_err "[$ENV_NAME] 重启失败——绑定已登记,排查日志后 scripts/svc.sh $ENV_NAME start"; return 1; }
|
||||
c_ok "[$ENV_NAME] 已切换数据库:$target/$tdb(生效中)"
|
||||
else
|
||||
c_info "[$ENV_NAME] 未运行或 --no-restart——下次 start 生效"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
cmd_set() {
|
||||
local name="${1:-}"
|
||||
[ -n "$name" ] || { c_err "用法:scripts/svc.sh set <name> [--port N] [--host H] [--apply] [--tag T...]"; exit 2; }
|
||||
@@ -2015,10 +2321,40 @@ registered_names() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# db_ref_of NAME:agent 服务的数据库绑定标识(list/status/info 共用)。
|
||||
# dbref= 优先;否则从 exec= 的 --dsn 或配置文件(conffile=)的 dsn 键提取 host:port
|
||||
# 匹配已登记 postgres 实例(兼容直接 --dsn/--config 部署、尚未登记 dbref 的服务);
|
||||
# 都匹配不上则回 外部@host:port。
|
||||
db_ref_of() {
|
||||
local name="$1" ref dsn hostport pg addr cf
|
||||
ref="$(env_kv_of "$name" dbref)"
|
||||
if [ -n "$ref" ]; then printf '%s' "$ref"; return 0; fi
|
||||
dsn="$(printf '%s' "$(env_kv_of "$name" exec)" | sed -n 's/.*--dsn \([^ ]*\).*/\1/p')"
|
||||
if [ -z "$dsn" ]; then
|
||||
cf="$(env_kv_of "$name" conffile)"
|
||||
[ -z "$cf" ] && cf="$(printf '%s' "$(env_kv_of "$name" exec)" | sed -n 's/.*--config \([^ ]*\).*/\1/p')"
|
||||
if [ -n "$cf" ] && [ -f "$cf" ]; then
|
||||
dsn="$(grep -oE '"dsn"[[:space:]]*:[[:space:]]*"[^"]*"' "$cf" 2>/dev/null | head -1 | sed -E 's/.*"([^"]*)"$/\1/')"
|
||||
fi
|
||||
fi
|
||||
[ -n "$dsn" ] || return 0
|
||||
hostport="$(printf '%s' "$dsn" | sed -n 's#.*@\([^/?]*\).*#\1#p')"
|
||||
while IFS= read -r pg; do
|
||||
[ "$(svc_kind "$pg")" = postgres ] || continue
|
||||
addr="$(env_kv_of "$pg" addr)"
|
||||
if [ "$addr" = "$hostport" ]; then
|
||||
printf '%s/%s' "$pg" "${dsn##*/}"
|
||||
return 0
|
||||
fi
|
||||
done < <(registered_names)
|
||||
printf '外部@%s' "$hostport"
|
||||
return 0
|
||||
}
|
||||
|
||||
global_list() {
|
||||
c_info "服务总览(root: $ROOT)"
|
||||
printf ' %-12s %-9s %-7s %-8s %-20s %-24s %s\n' NAME KIND STATE PID ADDR SOURCE TAGS
|
||||
local names=() d name kind addr src tags st
|
||||
printf ' %-12s %-9s %-7s %-8s %-20s %-16s %-24s %s\n' NAME KIND STATE PID ADDR DB SOURCE TAGS
|
||||
local names=() d name kind addr src tags st dbref
|
||||
while IFS= read -r name; do names+=("$name"); done < <(registered_names)
|
||||
for name in ${names[@]+"${names[@]}"}; do
|
||||
d="$RUNTIME/$name"
|
||||
@@ -2030,10 +2366,13 @@ global_list() {
|
||||
case ",$tags," in *",${LIST_TAG},"*) ;; *) continue ;; esac
|
||||
fi
|
||||
st="$(svc_pid_state "$name")"
|
||||
dbref="-"
|
||||
[ "$kind" = agent ] && dbref="$(db_ref_of "$name")"
|
||||
[ -n "$dbref" ] || dbref="-"
|
||||
if [ "${st%% *}" = alive ]; then
|
||||
printf ' %-12s %-9s %-7s %-8s %-20s %-24s %s\n' "$name" "$kind" "运行中" "${st#alive }" "$addr" "${src:--}" "${tags:--}"
|
||||
printf ' %-12s %-9s %-7s %-8s %-20s %-16s %-24s %s\n' "$name" "$kind" "运行中" "${st#alive }" "$addr" "$dbref" "${src:--}" "${tags:--}"
|
||||
else
|
||||
printf ' %-12s %-9s %-7s %-8s %-20s %-24s %s\n' "$name" "$kind" "未运行" "-" "$addr" "${src:--}" "${tags:--}"
|
||||
printf ' %-12s %-9s %-7s %-8s %-20s %-16s %-24s %s\n' "$name" "$kind" "未运行" "-" "$addr" "$dbref" "${src:--}" "${tags:--}"
|
||||
fi
|
||||
done
|
||||
[ "${#names[@]}" -eq 0 ] && c_info "(暂无已登记服务;db create / deploy 会登记)"
|
||||
@@ -2057,6 +2396,13 @@ env_status_detail() {
|
||||
[ -n "$src" ] && printf ' 来源 %s 版本 %s\n' "$src" "${ver:--}"
|
||||
[ -n "$tags" ] && printf ' 标签 %s\n' "$tags"
|
||||
case "$kind" in
|
||||
agent)
|
||||
local dbref cfile
|
||||
dbref="$(db_ref_of "$ENV_NAME")"
|
||||
printf ' 数据库 %s\n' "${dbref:-无(内嵌 SQLite 档;绑定:<name> db <pg名>)}"
|
||||
cfile="$(svc_get conffile)"
|
||||
[ -z "$cfile" ] && cfile="$(printf '%s' "$(svc_get exec)" | sed -n 's/.*--config \([^ ]*\).*/\1/p')"
|
||||
printf ' 配置文件 %s\n' "${cfile:-无(命令行参数形态;<name> config <file> 绑定)}" ;;
|
||||
postgres)
|
||||
printf ' 库 %s 用户 %s\n' "$(svc_get dbname)" "$(svc_get user)"
|
||||
printf ' DSN postgresql://%s:***@%s:%s/%s(db dsn %s --reveal 看密码)\n' \
|
||||
@@ -2185,6 +2531,14 @@ info_pg_block() {
|
||||
fi
|
||||
printf ' 版本 %s 用户 %s 认证 %s 默认库 %s 来源 %s\n' \
|
||||
"${ver:--}" "${user:--}" "${auth:--}" "${db:--}" "${src:--}"
|
||||
# 反向绑定:哪些 agent 服务当前指向本实例。
|
||||
local bound="" bn bref
|
||||
while IFS= read -r bn; do
|
||||
[ "$(svc_kind "$bn")" = agent ] || continue
|
||||
bref="$(db_ref_of "$bn")"
|
||||
case "$bref" in "$name/"*) bound="${bound:+$bound、}$bn(${bref#*/})" ;; esac
|
||||
done < <(registered_names)
|
||||
printf ' 绑定服务 %s\n' "${bound:-无(绑定:scripts/svc.sh <agent名> db $name)}"
|
||||
[ -n "$pgdata" ] && printf ' 数据目录 %s(%s)\n' "$pgdata" "$(info_du "$pgdata")"
|
||||
local nb=0 bsize="" latest="" f
|
||||
if [ -d "$d/backups" ]; then
|
||||
@@ -2216,11 +2570,27 @@ info_pg_block() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# info_config_block NAME:单服务 svc.env 内容快照(文件路径+权限+逐行内容)。
|
||||
# info_config_block NAME:单服务 svc.env 内容快照(文件路径+权限+逐行内容),
|
||||
# agent 服务另附其配置文件(conffile)的掩码内容快照。
|
||||
info_config_block() {
|
||||
local name="$1" f="$RUNTIME/$1/svc.env"
|
||||
printf ' [%s] %s(权限 %s,kind=%s)\n' "$name" "$f" "$(info_file_mode "$f")" "$(svc_kind "$name")"
|
||||
info_snapshot_lines "$f"
|
||||
local cf
|
||||
cf="$(env_kv_of "$name" conffile)"
|
||||
[ -z "$cf" ] && cf="$(printf '%s' "$(env_kv_of "$name" exec)" | sed -n 's/.*--config \([^ ]*\).*/\1/p')"
|
||||
if [ -n "$cf" ]; then
|
||||
if [ -f "$cf" ]; then
|
||||
printf ' ── 配置文件 %s(权限 %s)──\n' "$cf" "$(info_file_mode "$cf")"
|
||||
if [ "$CLI_REVEAL" = 1 ]; then
|
||||
cat "$cf" | sed 's/^/ /'
|
||||
else
|
||||
mask_json "$(cat "$cf")" | sed 's/^/ /'
|
||||
fi
|
||||
else
|
||||
printf ' ── 配置文件 %s:文件不存在(重启将失败,恢复或 %s config --clear)──\n' "$cf" "$name"
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -2566,11 +2936,20 @@ main() {
|
||||
# 动作词可省略(省略 = deploy);flags 可直接跟随服务名。
|
||||
local action="deploy"
|
||||
case "${1:-}" in
|
||||
deploy|stop|start|restart|status|logs) action="$1"; shift ;;
|
||||
deploy|stop|start|restart|status|logs|db|config) action="$1"; shift ;;
|
||||
esac
|
||||
if [ "$action" = logs ] && [ "${1:-}" != "" ] && [[ "$1" =~ ^[0-9]+$ ]]; then
|
||||
CLI_LOGS_N="$1"; shift
|
||||
fi
|
||||
# db / config 子命令:自带参数解析(位置参数不走 parse_args;未知 flags 由其自行拒绝)。
|
||||
if [ "$action" = db ] || [ "$action" = config ]; then
|
||||
env_config "$ENV_NAME"
|
||||
case "$action" in
|
||||
db) svc_db_bind "$@" || exit $? ;;
|
||||
config) svc_config "$@" || exit $? ;;
|
||||
esac
|
||||
exit 0
|
||||
fi
|
||||
parse_args "$@"
|
||||
if { [ -n "$CLI_FROM" ] || [ -n "$CLI_PICK" ] || [ "$CLI_DRYRUN" = 1 ] || [ -n "$CLI_DIR" ]; } && [ "$action" != deploy ]; then
|
||||
c_err "--from/--pick/--dry-run/--dir 仅用于 deploy 动作"
|
||||
|
||||
Reference in New Issue
Block a user