推荐节点
- +关于本站
++ DNFS 树状笔记是一个以树状结构组织内容的笔记社区:每个节点都可以生长出子节点, + 像树枝一样层层延展,让知识自然成体系。支持 Markdown 文本、链接卡片、图片与文件分享, + 注册登录后即可创建属于你自己的知识树。 +
From c06eb19f64a3063b341c92620d0b037274cb10aa Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Sat, 1 Aug 2026 11:47:14 +0800 Subject: [PATCH 01/22] refix --- aibox/aibox.py | 2 +- confs/_run.sh | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/aibox/aibox.py b/aibox/aibox.py index 2ee8098..26270e0 100644 --- a/aibox/aibox.py +++ b/aibox/aibox.py @@ -1168,7 +1168,7 @@ class ConfigManager: "api": { "deepseek_api_url": "https://api.deepseek.com/chat/completions", "deepseek_model": "deepseek-v4-flash", - "reasoning_enabled": false, + "reasoning_enabled": False, "api_key_env": "DEEPSEEK_API_KEY", "timeout": 120, "temperature": 0.7 diff --git a/confs/_run.sh b/confs/_run.sh index f8815d1..32c010d 100755 --- a/confs/_run.sh +++ b/confs/_run.sh @@ -2,5 +2,4 @@ cd "$(dirname "$0")" || exit source ./venv/bin/activate #exec ./smtp.bin -sleep 2 exec python3 confs.py From 6ebd5446196bb2636bc41d5abe993c85a975ce51 Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Sat, 1 Aug 2026 12:23:21 +0800 Subject: [PATCH 02/22] =?UTF-8?q?feat(db):=20=E8=BF=81=E7=A7=BB=20db=20?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=88=B0=20confs=20=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 confs/configs/db.ini 集中托管 db 服务配置(server/database/logs/confs) - dbv2.py 采用三层 fail-safe 加载模式:内置默认值 → confs(db.ini,优先)→ 环境变量覆盖(最高优先级,Docker 兼容) - _merge_ini() 白名单合并,仅覆盖已知 section/key,ConfigParser(interpolation=None) 禁用插值 - db 先于 confs 启动时连接失败静默 fail-safe,启动时打印配置来源 - db_tools.py 运维工具保持独立,直接读 DATABASE_URL 环境变量,不依赖 confs - 更新 confs/README.md 与 db/README.md 文档 --- confs/README.md | 13 ++++++-- confs/configs/db.ini | 16 +++++++++ db/README.md | 58 +++++++++++++++++++++++++++++--- db/dbv2.py | 78 +++++++++++++++++++++++++++++++++++++++----- 4 files changed, 150 insertions(+), 15 deletions(-) create mode 100644 confs/configs/db.ini diff --git a/confs/README.md b/confs/README.md index e1b232f..686d31d 100644 --- a/confs/README.md +++ b/confs/README.md @@ -237,6 +237,7 @@ except RpcError as e: |------|---------|---------| | [static](../static/) | [static.ini](configs/static.ini) | 三层 fail-safe:内置默认值 → confs(优先)→ 本地 `config.ini` 回退 | | [dnfs](../dnfs/) | [dnfs.ini](configs/dnfs.ini) | 三层 fail-safe:内置默认值 → confs(优先)→ 环境变量覆盖 | +| [db](../db/) | [db.ini](configs/db.ini) | 三层 fail-safe:内置默认值 → confs(优先)→ 环境变量覆盖(最高优先级,Docker 兼容) | static 的接入实现见 [static/fsys.py](../static/fsys.py): - `_fetch_from_confs()` 通过 `RpcClient` 调 `get` 路由取 `static.ini`,3 秒超时 @@ -249,7 +250,14 @@ dnfs 的接入实现见 [dnfs/dnfs.py](../dnfs/dnfs.py): - 支持的环境变量:`LISTEN_ADDR`/`LISTEN_PORT`/`DB_HOST`/`DB_PORT`/`REVIEW_URL`/`CONFS_HOST`/`CONFS_PORT` - Docker 环境通过环境变量传入服务名(db/ai_review),覆盖 confs 中的 127.0.0.1 默认值 -其他服务(gateway / waf / ai_review / smtp / db)尚未接入,仍读各自本地配置。 +db 的接入实现见 [db/dbv2.py](../db/dbv2.py): +- 三层加载顺序:内置默认值 → confs(db.ini)→ 环境变量覆盖(优先级最高) +- 配置项涵盖:`[server]` host/port/workers、`[database]` url、`[logs]` retention_days/max_records/cleanup_interval、`[confs]` host/port +- 支持的环境变量:`LISTEN_ADDR`/`LISTEN_PORT`/`WORKERS`/`DATABASE_URL`/`CONFS_HOST`/`CONFS_PORT` +- 注意:db 先于 confs 启动(启动顺序 db → confs → ai_review/...),db 启动时若 confs 不可用会静默 fail-safe 使用默认值,配置来源会打印到控制台 +- 独立运维工具 `db_tools.py`(backup/export/import 等)仍直接读 `DATABASE_URL` 环境变量,不依赖 confs,避免运维脚本依赖服务就绪 + +其他服务(gateway / waf / ai_review / smtp)尚未接入,仍读各自本地配置。 ## 项目结构 @@ -260,7 +268,8 @@ confs/ ├── configs/ # 配置文件存放目录(被分发对象) │ ├── example.ini # 示例配置 │ ├── static.ini # static 服务配置(已接入分发) -│ └── dnfs.ini # dnfs 服务配置(已接入分发) +│ ├── dnfs.ini # dnfs 服务配置(已接入分发) +│ └── db.ini # db 服务配置(已接入分发) ├── docs/ │ └── SECURITY_AUDIT_20260801.md # 安全审计报告 ├── _set.sh # 初始化脚本(建 venv + 安装依赖) diff --git a/confs/configs/db.ini b/confs/configs/db.ini new file mode 100644 index 0000000..bd2a2d3 --- /dev/null +++ b/confs/configs/db.ini @@ -0,0 +1,16 @@ +[server] +host = 0.0.0.0 +port = 8005 +workers = 4 + +[database] +url = sqlite:///database.db + +[logs] +retention_days = 30 +max_records = 1000000 +cleanup_interval = 3600 + +[confs] +host = 127.0.0.1 +port = 8010 diff --git a/db/README.md b/db/README.md index 67b6baa..0397c2f 100644 --- a/db/README.md +++ b/db/README.md @@ -25,10 +25,60 @@ ./_run.sh # venv 中执行 python3 dbv2.py ``` -服务监听 `tcp://127.0.0.1:8005`。生产环境由 core(Nidus)按 init.txt 统一拉起,守护靠 systemd。 +服务监听 `tcp://0.0.0.0:8005`(默认)。生产环境由 core(Nidus)按 init.txt 统一拉起,守护靠 systemd。 **注意**:`execute` 路由依赖表已存在(通过反射获取结构)。建表见 `create_sqlite3.sql`(Users / Notes;Logs 表由服务启动时自建)。 +## 配置管理 + +db 已接入 [confs](../confs/) 统一配置服务,采用三层 fail-safe 加载模式(与 static/dnfs 一致): + +1. **内置默认值** — 硬编码在 `dbv2.py` 中,保证服务在任何环境都能启动 +2. **confs 统一配置服务**(优先)— 通过 pymsg RpcClient 从 `confs` 拉取 `db.ini` +3. **环境变量覆盖**(最高优先级)— Docker/部署环境通过环境变量覆盖,保持向后兼容 + +### confs 配置文件(db.ini) + +配置文件托管于 [confs/configs/db.ini](../confs/configs/db.ini): + +```ini +[server] +host = 0.0.0.0 +port = 8005 +workers = 4 + +[database] +url = sqlite:///database.db + +[logs] +retention_days = 30 +max_records = 1000000 +cleanup_interval = 3600 + +[confs] +host = 127.0.0.1 +port = 8010 +``` + +### 环境变量(最高优先级) + +| 环境变量 | 说明 | 对应 confs 配置项 | +|---------|------|------------------| +| `LISTEN_ADDR` | 监听地址 | `[server] host` | +| `LISTEN_PORT` | 监听端口 | `[server] port` | +| `WORKERS` | 回调并发线程数 | `[server] workers` | +| `DATABASE_URL` | 数据库连接 URL | `[database] url` | +| `CONFS_HOST` | confs 服务地址 | `[confs] host` | +| `CONFS_PORT` | confs 服务端口 | `[confs] port` | + +### 启动顺序注意 + +根据 micro_servers 的启动顺序:**db → confs → ai_review/...**,db 先于 confs 启动。因此: +- db 启动时若 confs 尚未就绪,`_fetch_from_confs()` 连接失败会静默 fail-safe,使用内置默认值 + 环境变量正常启动 +- 启动时控制台会打印配置来源:`配置来源: confs 统一配置服务 (db.ini)` 或 `警告: confs 不可用,使用内置默认值`,最后 `start()` 前会再次打印最终来源 +- 独立运维工具 `db_tools.py`(backup/export/import/vacuum 等)仍直接读 `DATABASE_URL` 环境变量,不依赖 confs 服务就绪 +- Docker 环境通过 `docker-entrypoint.sh` 传入环境变量(见 [Dockerfile](Dockerfile)),保持与之前完全兼容,无需额外配置 + ## 协议与 API ### 传输协议(pymsg) @@ -232,10 +282,10 @@ python3 _view_db.sqlite3.py -s "SELECT service, COUNT(*) FROM Logs GROUP BY serv ## 切换数据库 -只需修改环境变量 `DATABASE_URL`,并安装对应驱动: +推荐方式:修改 confs 的 [db.ini](../confs/configs/db.ini) 中 `[database] url`,然后通过 confs `reload` 路由重载(或重启 db)。也可通过环境变量 `DATABASE_URL` 覆盖(Docker 部署时常用): -- SQLite(默认):`sqlite:///database.db` -- PostgreSQL:`postgresql://user:pass@localhost/dbname`(需 `pip install psycopg2`) +- SQLite(默认):`sqlite:///database.db`(相对路径)或 `sqlite:////app/data/database.db`(绝对路径,Docker 中使用) +- PostgreSQL:`postgresql://user:pass@localhost/dbname`(需 `pip install psycopg2-binary`) - MySQL:`mysql+pymysql://user:pass@localhost/dbname`(需 `pip install pymysql`) SQLAlchemy 会自动生成适配各数据库的 SQL 方言,无需修改接口调用。 diff --git a/db/dbv2.py b/db/dbv2.py index 771b206..ccd2121 100644 --- a/db/dbv2.py +++ b/db/dbv2.py @@ -27,6 +27,7 @@ dbv2 — 基于 pymsg 协议(MsgServer)的数据库服务。 响应中的日期时间一律序列化为 ISO 8601(T 分隔)。 """ +import configparser import json import os import threading @@ -47,9 +48,70 @@ import sys as _sys _sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) from pymsg import RpcServer # noqa: E402 -# ── 数据库配置 ────────────────────────────────────────────────────────── +# ── 配置加载(三层 fail-safe:内置默认值 → confs → 环境变量覆盖)────── +# 加载顺序:内置默认值 → confs 统一配置服务(db.ini,优先); +# confs 不可用时 fail-safe 使用默认值;环境变量优先级最高(Docker 兼容)。 -DATABASE_URL = os.environ.get("DATABASE_URL", "sqlite:///database.db") +_db_config = { + 'server': {'host': '0.0.0.0', 'port': '8005', 'workers': '4'}, + 'database': {'url': 'sqlite:///database.db'}, + 'logs': { + 'retention_days': '30', + 'max_records': '1000000', + 'cleanup_interval': '3600', + }, + 'confs': {'host': '127.0.0.1', 'port': '8010'}, +} + + +def _merge_ini(text): + """把 ini 文本合并进 _db_config(仅覆盖已知的 section/key)。""" + ini = configparser.ConfigParser(interpolation=None) + ini.read_string(text) + for section in _db_config: + if ini.has_section(section): + for key, value in ini[section].items(): + if key in _db_config[section]: + _db_config[section][key] = value + + +def _fetch_from_confs(): + """从 confs 统一配置服务拉取 db.ini 全文,失败返回 None(fail-safe)。""" + try: + from pymsg import RpcClient + host = os.environ.get('CONFS_HOST', _db_config['confs']['host']) + port = int(os.environ.get('CONFS_PORT', _db_config['confs']['port'])) + data = RpcClient(host, port, 'db', timeout=3).call( + 'get', {'name': 'db.ini'}) + return data.get('content') + except Exception: + return None + + +_confs_text = _fetch_from_confs() +_config_source = 'built-in defaults' +if _confs_text is not None: + _merge_ini(_confs_text) + _config_source = 'confs (db.ini)' + print(f'[dbv2] 配置来源: confs 统一配置服务 (db.ini)') +else: + print(f'[dbv2] 警告: confs 不可用,使用内置默认值') + +# 环境变量覆盖(最高优先级,保持 Docker 兼容) +if os.environ.get('LISTEN_ADDR'): + _db_config['server']['host'] = os.environ['LISTEN_ADDR'] +if os.environ.get('LISTEN_PORT'): + _db_config['server']['port'] = os.environ['LISTEN_PORT'] +if os.environ.get('WORKERS'): + _db_config['server']['workers'] = os.environ['WORKERS'] +if os.environ.get('DATABASE_URL'): + _db_config['database']['url'] = os.environ['DATABASE_URL'] + +# 解析为最终常量 +DATABASE_URL = _db_config['database']['url'] +DEFAULT_RETENTION_DAYS = int(_db_config['logs']['retention_days']) +DEFAULT_MAX_RECORDS = int(_db_config['logs']['max_records']) +DEFAULT_CLEANUP_INTERVAL = int(_db_config['logs']['cleanup_interval']) # ── SQLite 连接配置(针对 Docker/多线程环境优化)────────────────────── @@ -76,10 +138,6 @@ def _configure_sqlite(dbapi_connection, connection_record): MAX_MESSAGE_LEN = 8192 # message 上限 8KB MAX_EXTRA_LEN = 4096 # extra 上限 4KB -DEFAULT_RETENTION_DAYS = 30 -DEFAULT_MAX_RECORDS = 1_000_000 -DEFAULT_CLEANUP_INTERVAL = 3600 # 秒 - #: where/values 中的哨兵值:服务端当前 UTC 时间 NOW_SENTINEL = "$now" @@ -456,17 +514,19 @@ class DBServer: if __name__ == "__main__": import argparse - default_addr = os.environ.get("LISTEN_ADDR", "0.0.0.0") - default_port = int(os.environ.get("LISTEN_PORT", "8005")) + default_addr = _db_config['server']['host'] + default_port = int(_db_config['server']['port']) + default_workers = int(_db_config['server']['workers']) parser = argparse.ArgumentParser(description="dbv2 — pymsg 数据库服务") parser.add_argument("--addr", default=default_addr, help="监听地址") parser.add_argument("--port", type=int, default=default_port, help="监听端口") - parser.add_argument("--workers", type=int, default=4, + parser.add_argument("--workers", type=int, default=default_workers, help="回调并发线程数(0=同步串行模式)") args = parser.parse_args() server = DBServer(args.addr, args.port, workers=args.workers) + print(f'[dbv2] 配置来源: {_config_source}') try: server.start() except KeyboardInterrupt: From 2cbb93b3b72a3832aeb5a72902a8dc819d3e6faa Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Sat, 1 Aug 2026 12:29:55 +0800 Subject: [PATCH 03/22] =?UTF-8?q?refactor(db):=20=E5=88=A0=E9=99=A4=20conf?= =?UTF-8?q?s=20=E8=BF=9E=E6=8E=A5=E5=A4=B1=E8=B4=A5=E6=97=B6=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E9=BB=98=E8=AE=A4=E9=85=8D=E7=BD=AE=E7=9A=84=20fail-s?= =?UTF-8?q?afe=20=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - confs 改为 db 启动的强依赖:连接失败直接 sys.exit(1),不再静默使用默认值 - 配置加载从三层(默认值→confs→环境变量)改为两层(confs→环境变量覆盖) - _db_config 初始值改为空字符串白名单模板,移除内置默认值 - 新增配置完整性校验:confs 返回后检查所有必需 section/key 非空,缺失则报错退出 - 区分错误类型:连接拒绝/RpcError/通用异常分别打印明确的错误信息到 stderr - confs 连接地址改为引导配置(环境变量优先,默认 127.0.0.1:8010),不再从 db.ini 读取 - 更新 db/README.md 与 confs/README.md,说明启动顺序调整为 confs → db → ... --- confs/README.md | 12 ++++---- db/README.md | 38 +++++++++++++------------ db/dbv2.py | 73 +++++++++++++++++++++++++++++++++---------------- 3 files changed, 77 insertions(+), 46 deletions(-) diff --git a/confs/README.md b/confs/README.md index 686d31d..3e3e74a 100644 --- a/confs/README.md +++ b/confs/README.md @@ -227,7 +227,7 @@ except RpcError as e: ## 集成关系 -- 由 `core`(`PMC`)通过 `init.txt` 中的 `bash /opt/micro_servers/confs/_run.sh` 拉起并守护(在 db 之后、ai_review 之前启动) +- 由 `core`(`PMC`)通过 `init.txt` 中的 `bash /opt/micro_servers/confs/_run.sh` 拉起并守护(需先于 db 启动,db 对 confs 有强依赖) - 各内部服务通过 pymsg `RpcClient` 调 `get` 路由按需获取配置,替代本地散落的 ini/json/yaml - 自身日志通过 `log_client.py` 异步推送至 `db:8005` 的 Logs 表(与其他服务一致) @@ -237,7 +237,7 @@ except RpcError as e: |------|---------|---------| | [static](../static/) | [static.ini](configs/static.ini) | 三层 fail-safe:内置默认值 → confs(优先)→ 本地 `config.ini` 回退 | | [dnfs](../dnfs/) | [dnfs.ini](configs/dnfs.ini) | 三层 fail-safe:内置默认值 → confs(优先)→ 环境变量覆盖 | -| [db](../db/) | [db.ini](configs/db.ini) | 三层 fail-safe:内置默认值 → confs(优先)→ 环境变量覆盖(最高优先级,Docker 兼容) | +| [db](../db/) | [db.ini](configs/db.ini) | confs 强依赖(confs 不可用时 db 直接退出)→ 环境变量覆盖(最高优先级,Docker 兼容) | static 的接入实现见 [static/fsys.py](../static/fsys.py): - `_fetch_from_confs()` 通过 `RpcClient` 调 `get` 路由取 `static.ini`,3 秒超时 @@ -251,10 +251,12 @@ dnfs 的接入实现见 [dnfs/dnfs.py](../dnfs/dnfs.py): - Docker 环境通过环境变量传入服务名(db/ai_review),覆盖 confs 中的 127.0.0.1 默认值 db 的接入实现见 [db/dbv2.py](../db/dbv2.py): -- 三层加载顺序:内置默认值 → confs(db.ini)→ 环境变量覆盖(优先级最高) -- 配置项涵盖:`[server]` host/port/workers、`[database]` url、`[logs]` retention_days/max_records/cleanup_interval、`[confs]` host/port +- 两层加载顺序:confs(db.ini,强依赖)→ 环境变量覆盖(优先级最高) +- confs 连接失败或 db.ini 缺失必需配置项时,db 直接 `sys.exit(1)` 终止,不使用默认值 +- 配置项涵盖:`[server]` host/port/workers、`[database]` url、`[logs]` retention_days/max_records/cleanup_interval +- confs 连接地址为引导配置:环境变量 `CONFS_HOST`/`CONFS_PORT` 优先,默认 `127.0.0.1:8010` - 支持的环境变量:`LISTEN_ADDR`/`LISTEN_PORT`/`WORKERS`/`DATABASE_URL`/`CONFS_HOST`/`CONFS_PORT` -- 注意:db 先于 confs 启动(启动顺序 db → confs → ai_review/...),db 启动时若 confs 不可用会静默 fail-safe 使用默认值,配置来源会打印到控制台 +- 启动顺序:confs 必须先于 db 启动(confs → db → ai_review/...) - 独立运维工具 `db_tools.py`(backup/export/import 等)仍直接读 `DATABASE_URL` 环境变量,不依赖 confs,避免运维脚本依赖服务就绪 其他服务(gateway / waf / ai_review / smtp)尚未接入,仍读各自本地配置。 diff --git a/db/README.md b/db/README.md index 0397c2f..cc3da2d 100644 --- a/db/README.md +++ b/db/README.md @@ -31,11 +31,10 @@ ## 配置管理 -db 已接入 [confs](../confs/) 统一配置服务,采用三层 fail-safe 加载模式(与 static/dnfs 一致): +db 已接入 [confs](../confs/) 统一配置服务,confs 是 db 启动的**强依赖**: -1. **内置默认值** — 硬编码在 `dbv2.py` 中,保证服务在任何环境都能启动 -2. **confs 统一配置服务**(优先)— 通过 pymsg RpcClient 从 `confs` 拉取 `db.ini` -3. **环境变量覆盖**(最高优先级)— Docker/部署环境通过环境变量覆盖,保持向后兼容 +1. **confs 统一配置服务** — 必须成功从 confs 拉取 `db.ini`,否则 db 启动失败直接退出(exit code 1) +2. **环境变量覆盖**(最高优先级)— Docker/部署环境通过环境变量覆盖 confs 中的值 ### confs 配置文件(db.ini) @@ -54,13 +53,15 @@ url = sqlite:///database.db retention_days = 30 max_records = 1000000 cleanup_interval = 3600 - -[confs] -host = 127.0.0.1 -port = 8010 ``` -### 环境变量(最高优先级) +### 引导配置(confs 连接地址) + +confs 自身的连接地址不通过 confs 获取(避免循环依赖),通过以下方式确定(优先级从高到低): +- 环境变量 `CONFS_HOST` / `CONFS_PORT` +- 默认值 `127.0.0.1:8010` + +### 环境变量(最高优先级,覆盖 confs 配置值) | 环境变量 | 说明 | 对应 confs 配置项 | |---------|------|------------------| @@ -68,16 +69,19 @@ port = 8010 | `LISTEN_PORT` | 监听端口 | `[server] port` | | `WORKERS` | 回调并发线程数 | `[server] workers` | | `DATABASE_URL` | 数据库连接 URL | `[database] url` | -| `CONFS_HOST` | confs 服务地址 | `[confs] host` | -| `CONFS_PORT` | confs 服务端口 | `[confs] port` | +| `CONFS_HOST` | confs 服务地址(引导配置) | — | +| `CONFS_PORT` | confs 服务端口(引导配置) | — | -### 启动顺序注意 +### 启动顺序要求 -根据 micro_servers 的启动顺序:**db → confs → ai_review/...**,db 先于 confs 启动。因此: -- db 启动时若 confs 尚未就绪,`_fetch_from_confs()` 连接失败会静默 fail-safe,使用内置默认值 + 环境变量正常启动 -- 启动时控制台会打印配置来源:`配置来源: confs 统一配置服务 (db.ini)` 或 `警告: confs 不可用,使用内置默认值`,最后 `start()` 前会再次打印最终来源 -- 独立运维工具 `db_tools.py`(backup/export/import/vacuum 等)仍直接读 `DATABASE_URL` 环境变量,不依赖 confs 服务就绪 -- Docker 环境通过 `docker-entrypoint.sh` 传入环境变量(见 [Dockerfile](Dockerfile)),保持与之前完全兼容,无需额外配置 +**confs 必须先于 db 启动**(启动顺序调整为:confs → db → ai_review/...)。若 confs 不可用: +- db 打印致命错误到 stderr(包含 confs 地址和具体错误原因),以 exit code 1 终止 +- 不会静默使用默认值启动 +- 配置完整性校验:若 db.ini 中缺少必需的 section/key 且无对应环境变量覆盖,同样报错退出 +- 启动成功时控制台打印:`配置来源: confs 统一配置服务 (db.ini @ host:port)` + +> 独立运维工具 `db_tools.py`(backup/export/import/vacuum 等)仍直接读 `DATABASE_URL` 环境变量,不依赖 confs 服务就绪。 +> Docker 环境通过 `docker-entrypoint.sh` 传入环境变量(见 [Dockerfile](Dockerfile)),需确保 `CONFS_HOST`/`CONFS_PORT` 指向 confs 服务地址。 ## 协议与 API diff --git a/db/dbv2.py b/db/dbv2.py index ccd2121..b0123af 100644 --- a/db/dbv2.py +++ b/db/dbv2.py @@ -48,19 +48,25 @@ import sys as _sys _sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) from pymsg import RpcServer # noqa: E402 -# ── 配置加载(三层 fail-safe:内置默认值 → confs → 环境变量覆盖)────── -# 加载顺序:内置默认值 → confs 统一配置服务(db.ini,优先); -# confs 不可用时 fail-safe 使用默认值;环境变量优先级最高(Docker 兼容)。 +# ── 配置加载(两层:confs 强依赖 → 环境变量覆盖)────────────────────── +# confs 是 db 启动的强依赖:必须成功从 confs 拉取 db.ini,否则直接退出。 +# 环境变量优先级最高(Docker 兼容),可覆盖 confs 中的配置值。 +# confs 自身的连接地址(host/port)通过引导配置确定:环境变量 CONFS_HOST/CONFS_PORT +# 优先,缺省使用 127.0.0.1:8010(与其他服务一致的约定)。 +# 引导配置:confs 连接地址(不依赖 confs 本身,可通过环境变量覆盖) +_confs_host = os.environ.get('CONFS_HOST', '127.0.0.1') +_confs_port = int(os.environ.get('CONFS_PORT', '8010')) + +# 白名单结构:_merge_ini 仅覆盖此处列出的 section/key,防止意外字段 _db_config = { - 'server': {'host': '0.0.0.0', 'port': '8005', 'workers': '4'}, - 'database': {'url': 'sqlite:///database.db'}, + 'server': {'host': '', 'port': '', 'workers': ''}, + 'database': {'url': ''}, 'logs': { - 'retention_days': '30', - 'max_records': '1000000', - 'cleanup_interval': '3600', + 'retention_days': '', + 'max_records': '', + 'cleanup_interval': '', }, - 'confs': {'host': '127.0.0.1', 'port': '8010'}, } @@ -76,26 +82,32 @@ def _merge_ini(text): def _fetch_from_confs(): - """从 confs 统一配置服务拉取 db.ini 全文,失败返回 None(fail-safe)。""" + """从 confs 统一配置服务拉取 db.ini 全文,失败直接终止进程。""" + from pymsg import RpcClient, RpcError # noqa: E402 try: - from pymsg import RpcClient - host = os.environ.get('CONFS_HOST', _db_config['confs']['host']) - port = int(os.environ.get('CONFS_PORT', _db_config['confs']['port'])) - data = RpcClient(host, port, 'db', timeout=3).call( - 'get', {'name': 'db.ini'}) + client = RpcClient(_confs_host, _confs_port, 'db', timeout=5) + data = client.call('get', {'name': 'db.ini'}) return data.get('content') - except Exception: - return None + except (ConnectionRefusedError, ConnectionError, OSError) as e: + print(f'[dbv2] 致命错误: 无法连接 confs 服务 ({_confs_host}:{_confs_port}): {e}', + file=_sys.stderr) + print(f'[dbv2] 请确认 confs 服务已启动并监听 {_confs_host}:{_confs_port}', + file=_sys.stderr) + _sys.exit(1) + except RpcError as e: + print(f'[dbv2] 致命错误: 从 confs 获取 db.ini 失败: {e}', + file=_sys.stderr) + _sys.exit(1) + except Exception as e: + print(f'[dbv2] 致命错误: confs 配置拉取异常: {e}', + file=_sys.stderr) + _sys.exit(1) _confs_text = _fetch_from_confs() -_config_source = 'built-in defaults' -if _confs_text is not None: - _merge_ini(_confs_text) - _config_source = 'confs (db.ini)' - print(f'[dbv2] 配置来源: confs 统一配置服务 (db.ini)') -else: - print(f'[dbv2] 警告: confs 不可用,使用内置默认值') +_merge_ini(_confs_text) +_config_source = 'confs (db.ini)' +print(f'[dbv2] 配置来源: confs 统一配置服务 (db.ini @ {_confs_host}:{_confs_port})') # 环境变量覆盖(最高优先级,保持 Docker 兼容) if os.environ.get('LISTEN_ADDR'): @@ -107,6 +119,19 @@ if os.environ.get('WORKERS'): if os.environ.get('DATABASE_URL'): _db_config['database']['url'] = os.environ['DATABASE_URL'] +# 配置完整性校验 +_required = { + 'server': ['host', 'port', 'workers'], + 'database': ['url'], + 'logs': ['retention_days', 'max_records', 'cleanup_interval'], +} +for section, keys in _required.items(): + for key in keys: + if not _db_config[section][key]: + print(f'[dbv2] 致命错误: 配置项 [{section}] {key} 缺失(db.ini 中未配置且无环境变量覆盖)', + file=_sys.stderr) + _sys.exit(1) + # 解析为最终常量 DATABASE_URL = _db_config['database']['url'] DEFAULT_RETENTION_DAYS = int(_db_config['logs']['retention_days']) From 84adec47224bc3d556af3a560d84357d7464892f Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Sat, 1 Aug 2026 12:33:51 +0800 Subject: [PATCH 04/22] =?UTF-8?q?refactor(dnfs):=20=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E9=85=8D=E7=BD=AE=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?confs=20=E6=94=B9=E4=B8=BA=E5=BC=BA=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 配置加载从三层(默认值→confs→环境变量)改为两层(confs 强依赖→环境变量覆盖) - confs 连接失败直接 sys.exit(1),不再静默使用内置默认值 - _dnfs_config 初始值改为空字符串白名单模板,移除内置默认值 - 新增配置完整性校验:confs 返回后检查所有必需 section/key 非空 - 区分错误类型:连接拒绝/RpcError/通用异常分别打印明确错误信息到 stderr - confs 连接地址改为引导配置(环境变量优先,默认 127.0.0.1:8010),移除 dnfs.ini 中 [confs] 段 - 启动顺序调整为 confs → db → dnfs → ai_review/...(dnfs 同时依赖 confs 和 db) - 更新 dnfs/README.md 与 confs/README.md 文档 --- confs/README.md | 9 ++++-- confs/configs/dnfs.ini | 4 --- dnfs/README.md | 45 +++++++++++++++++---------- dnfs/dnfs.py | 70 ++++++++++++++++++++++++++++-------------- 4 files changed, 81 insertions(+), 47 deletions(-) diff --git a/confs/README.md b/confs/README.md index 3e3e74a..f9295fb 100644 --- a/confs/README.md +++ b/confs/README.md @@ -236,7 +236,7 @@ except RpcError as e: | 服务 | 配置文件 | 接入方式 | |------|---------|---------| | [static](../static/) | [static.ini](configs/static.ini) | 三层 fail-safe:内置默认值 → confs(优先)→ 本地 `config.ini` 回退 | -| [dnfs](../dnfs/) | [dnfs.ini](configs/dnfs.ini) | 三层 fail-safe:内置默认值 → confs(优先)→ 环境变量覆盖 | +| [dnfs](../dnfs/) | [dnfs.ini](configs/dnfs.ini) | confs 强依赖(confs 不可用时 dnfs 直接退出)→ 环境变量覆盖(最高优先级,Docker 兼容) | | [db](../db/) | [db.ini](configs/db.ini) | confs 强依赖(confs 不可用时 db 直接退出)→ 环境变量覆盖(最高优先级,Docker 兼容) | static 的接入实现见 [static/fsys.py](../static/fsys.py): @@ -246,9 +246,12 @@ static 的接入实现见 [static/fsys.py](../static/fsys.py): - 配置来源会记录到日志(`logs.info/warning`),便于排查实际用了哪一层 dnfs 的接入实现见 [dnfs/dnfs.py](../dnfs/dnfs.py): -- 三层加载顺序:内置默认值 → confs(dnfs.ini)→ 环境变量覆盖(优先级最高) +- 两层加载顺序:confs(dnfs.ini,强依赖)→ 环境变量覆盖(优先级最高) +- confs 连接失败或 dnfs.ini 缺失必需配置项时,dnfs 直接 `sys.exit(1)` 终止,不使用默认值 +- 配置项涵盖:`[server]` host/port、`[database]` host/port/timeout、`[ai_review]` url/timeout +- confs 连接地址为引导配置:环境变量 `CONFS_HOST`/`CONFS_PORT` 优先,默认 `127.0.0.1:8010` - 支持的环境变量:`LISTEN_ADDR`/`LISTEN_PORT`/`DB_HOST`/`DB_PORT`/`REVIEW_URL`/`CONFS_HOST`/`CONFS_PORT` -- Docker 环境通过环境变量传入服务名(db/ai_review),覆盖 confs 中的 127.0.0.1 默认值 +- 启动顺序:confs → db → dnfs → ai_review/...(dnfs 同时依赖 confs 和 db) db 的接入实现见 [db/dbv2.py](../db/dbv2.py): - 两层加载顺序:confs(db.ini,强依赖)→ 环境变量覆盖(优先级最高) diff --git a/confs/configs/dnfs.ini b/confs/configs/dnfs.ini index 8288444..fd47bbd 100644 --- a/confs/configs/dnfs.ini +++ b/confs/configs/dnfs.ini @@ -10,7 +10,3 @@ timeout = 10 [ai_review] url = http://127.0.0.1:8007/review timeout = 10 - -[confs] -host = 127.0.0.1 -port = 8010 diff --git a/dnfs/README.md b/dnfs/README.md index 1413830..2201130 100644 --- a/dnfs/README.md +++ b/dnfs/README.md @@ -215,27 +215,32 @@ curl -X POST http://localhost:8006/update_note \ ## 配置 -dnfs 已接入 confs 统一配置服务,采用**三层 fail-safe** 加载模式(与 static 服务一致): +dnfs 已接入 confs 统一配置服务,confs 是 dnfs 启动的**强依赖**: -**加载顺序**:内置默认值 → confs 统一配置服务(dnfs.ini,优先)→ 环境变量覆盖(最高优先级) +1. **confs 统一配置服务** — 必须成功从 confs 拉取 `dnfs.ini`,否则 dnfs 启动失败直接退出(exit code 1) +2. **环境变量覆盖**(最高优先级)— Docker/部署环境通过环境变量覆盖 confs 中的值 ### 配置文件 集中托管在 [confs/configs/dnfs.ini](../confs/configs/dnfs.ini),由 confs 服务分发: -| Section | Key | 默认值 | 说明 | -|---------|-----|--------|------| -| `[server]` | `host` | `127.0.0.1` | 监听地址 | -| `[server]` | `port` | `8006` | 监听端口 | -| `[database]` | `host` | `127.0.0.1` | db 服务地址 | -| `[database]` | `port` | `8005` | db 服务端口 | -| `[database]` | `timeout` | `10` | RPC 调用超时(秒) | -| `[ai_review]` | `url` | `http://127.0.0.1:8007/review` | AI 审核接口地址 | -| `[ai_review]` | `timeout` | `10` | AI 审核请求超时(秒) | -| `[confs]` | `host` | `127.0.0.1` | confs 服务地址 | -| `[confs]` | `port` | `8010` | confs 服务端口 | +| Section | Key | 说明 | +|---------|-----|------| +| `[server]` | `host` | 监听地址 | +| `[server]` | `port` | 监听端口 | +| `[database]` | `host` | db 服务地址 | +| `[database]` | `port` | db 服务端口 | +| `[database]` | `timeout` | RPC 调用超时(秒) | +| `[ai_review]` | `url` | AI 审核接口地址 | +| `[ai_review]` | `timeout` | AI 审核请求超时(秒) | -### 环境变量(Docker 环境必需,优先级最高) +### 引导配置(confs 连接地址) + +confs 自身的连接地址不通过 confs 获取(避免循环依赖),通过以下方式确定(优先级从高到低): +- 环境变量 `CONFS_HOST` / `CONFS_PORT` +- 默认值 `127.0.0.1:8010` + +### 环境变量(最高优先级,覆盖 confs 配置值) 环境变量覆盖配置文件值,容器化部署时通过 docker-compose.yml 传入服务名: @@ -246,10 +251,16 @@ dnfs 已接入 confs 统一配置服务,采用**三层 fail-safe** 加载模 | `DB_HOST` | `db` | db 服务主机名(docker-compose 服务名) | | `DB_PORT` | `8005` | db 服务端口 | | `REVIEW_URL` | `http://ai_review:8007/review` | ai_review 服务地址 | -| `CONFS_HOST` | - | 可选,confs 容器化后使用 | -| `CONFS_PORT` | - | 可选,confs 容器化后使用 | +| `CONFS_HOST` | `confs` | confs 服务主机名(引导配置) | +| `CONFS_PORT` | `8010` | confs 服务端口(引导配置) | -配置来源会记录到日志(`logs.info/warning`),便于排查实际用了哪一层。本地裸机运行时 confs 不可用会自动回退到内置默认值 + 环境变量。 +### 启动顺序要求 + +**confs 必须先于 dnfs 启动**(启动顺序:confs → db → dnfs → ai_review/...)。若 confs 不可用: +- dnfs 打印致命错误到 stderr(包含 confs 地址和具体错误原因),以 exit code 1 终止 +- 不会静默使用默认值启动 +- 配置完整性校验:若 dnfs.ini 中缺少必需的 section/key 且无对应环境变量覆盖,同样报错退出 +- 启动成功时控制台打印:`配置来源: confs (dnfs.ini @ host:port)` ## 前端 diff --git a/dnfs/dnfs.py b/dnfs/dnfs.py index ff78c71..177e271 100755 --- a/dnfs/dnfs.py +++ b/dnfs/dnfs.py @@ -33,15 +33,21 @@ from log_client import logs sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) from pymsg import RpcClient, RpcError # noqa: E402 -# ==================== 配置加载(三层 fail-safe) ==================== -# 加载顺序:内置默认值 → confs 统一配置服务(dnfs.ini) → 环境变量覆盖 -# 环境变量优先级最高(Docker 环境使用服务名 db/ai_review 而非 127.0.0.1) +# ==================== 配置加载(两层:confs 强依赖 → 环境变量覆盖) ==================== +# confs 是 dnfs 启动的强依赖:必须成功从 confs 拉取 dnfs.ini,否则直接退出。 +# 环境变量优先级最高(Docker 环境使用服务名 db/ai_review 而非 127.0.0.1)。 +# confs 自身的连接地址通过引导配置确定:环境变量 CONFS_HOST/CONFS_PORT 优先, +# 缺省使用 127.0.0.1:8010(与其他服务一致的约定)。 +# 引导配置:confs 连接地址(不依赖 confs 本身) +_confs_host = os.environ.get('CONFS_HOST', '127.0.0.1') +_confs_port = int(os.environ.get('CONFS_PORT', '8010')) + +# 白名单结构:_merge_ini 仅覆盖此处列出的 section/key,防止意外字段 _dnfs_config = { - 'server': {'host': '127.0.0.1', 'port': '8006'}, - 'database': {'host': '127.0.0.1', 'port': '8005', 'timeout': '10'}, - 'ai_review': {'url': 'http://127.0.0.1:8007/review', 'timeout': '10'}, - 'confs': {'host': '127.0.0.1', 'port': '8010'}, + 'server': {'host': '', 'port': ''}, + 'database': {'host': '', 'port': '', 'timeout': ''}, + 'ai_review': {'url': '', 'timeout': ''}, } @@ -57,23 +63,29 @@ def _merge_ini(text): def _fetch_from_confs(): - """从 confs 统一配置服务拉取 dnfs.ini 全文,失败返回 None(fail-safe)。""" + """从 confs 统一配置服务拉取 dnfs.ini 全文,失败直接终止进程。""" try: - host = os.environ.get('CONFS_HOST', _dnfs_config['confs']['host']) - port = int(os.environ.get('CONFS_PORT', _dnfs_config['confs']['port'])) - data = RpcClient(host, port, 'dnfs', timeout=3).call( + data = RpcClient(_confs_host, _confs_port, 'dnfs', timeout=5).call( 'get', {'name': 'dnfs.ini'}) return data.get('content') - except Exception: - return None + except (ConnectionRefusedError, ConnectionError, OSError) as e: + print(f'[dnfs] 致命错误: 无法连接 confs 服务 ({_confs_host}:{_confs_port}): {e}', + file=sys.stderr) + print(f'[dnfs] 请确认 confs 服务已启动并监听 {_confs_host}:{_confs_port}', + file=sys.stderr) + sys.exit(1) + except RpcError as e: + print(f'[dnfs] 致命错误: 从 confs 获取 dnfs.ini 失败: {e}', + file=sys.stderr) + sys.exit(1) + except Exception as e: + print(f'[dnfs] 致命错误: confs 配置拉取异常: {e}', + file=sys.stderr) + sys.exit(1) _confs_text = _fetch_from_confs() -if _confs_text is not None: - _merge_ini(_confs_text) - logs.info("dnfs", "配置来源: confs 统一配置服务 (dnfs.ini)") -else: - logs.warning("dnfs", "confs 不可用,使用内置默认值 + 环境变量") +_merge_ini(_confs_text) # 环境变量覆盖(优先级最高,Docker 环境需要) if os.environ.get('LISTEN_ADDR'): @@ -86,20 +98,32 @@ if os.environ.get('DB_PORT'): _dnfs_config['database']['port'] = os.environ['DB_PORT'] if os.environ.get('REVIEW_URL'): _dnfs_config['ai_review']['url'] = os.environ['REVIEW_URL'] -if os.environ.get('CONFS_HOST'): - _dnfs_config['confs']['host'] = os.environ['CONFS_HOST'] -if os.environ.get('CONFS_PORT'): - _dnfs_config['confs']['port'] = os.environ['CONFS_PORT'] + +# 配置完整性校验 +_required = { + 'server': ['host', 'port'], + 'database': ['host', 'port', 'timeout'], + 'ai_review': ['url', 'timeout'], +} +for section, keys in _required.items(): + for key in keys: + if not _dnfs_config[section][key]: + print(f'[dnfs] 致命错误: 配置项 [{section}] {key} 缺失(dnfs.ini 中未配置且无环境变量覆盖)', + file=sys.stderr) + sys.exit(1) # 提取配置常量 +_DB_TIMEOUT = int(_dnfs_config['database']['timeout']) _DBV2_ADDR = _dnfs_config['database']['host'] _DBV2_PORT = int(_dnfs_config['database']['port']) -_DB_TIMEOUT = int(_dnfs_config['database']['timeout']) _REVIEW_SERVER_URL = _dnfs_config['ai_review']['url'] _REVIEW_TIMEOUT = int(_dnfs_config['ai_review']['timeout']) _ADDR = _dnfs_config['server']['host'] _PORT = int(_dnfs_config['server']['port']) +_config_source = f'confs (dnfs.ini @ {_confs_host}:{_confs_port})' +print(f'[dnfs] 配置来源: {_config_source}') + # 管理员判定已上移至 gateway:登录时命中 [Admin] users 白名单即在 Token payload # 写入 is_admin=true,本服务通过 info.get('is_admin') 读取,不再本地维护名单。 # 旧 Token 无此字段,按非管理员处理(安全方向)。 From 1a5e9f7064898c27444ffed8b4795a0ac286635c Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Sat, 1 Aug 2026 12:34:53 +0800 Subject: [PATCH 05/22] =?UTF-8?q?feat(static):=20=E9=A6=96=E9=A1=B5?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=99=BB=E5=BD=95=E6=80=81=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E4=B8=8E=E8=BF=9B=E5=85=A5=E7=AC=94=E8=AE=B0=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - index.html: lang 改为 zh-CN,新增"关于本站"介绍段落,按钮改为有 id 的登录/注册/进入/退出四按钮(默认隐藏进入/退出) - 新增 index.js: POST /whoami 检测登录态(HttpOnly Cookie 自动携带),已登录则显示用户名+进入笔记/退出按钮,未登录显示登录/注册 - 退出按钮调用 POST /logout 后刷新页面 --- static/public/index.html | 24 +++++++++++++++++----- static/public/js/index.js | 42 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/static/public/index.html b/static/public/index.html index 608b0f5..d86131d 100644 --- a/static/public/index.html +++ b/static/public/index.html @@ -1,5 +1,5 @@ - +
@@ -24,6 +24,13 @@ margin-left: 5px; margin-right: 5px; } + .site-intro { + max-width: 640px; + margin: 0 auto; + font-size: 0.95rem; + line-height: 1.9; + color: var(--fg-secondary); + }+ DNFS 树状笔记是一个以树状结构组织内容的笔记社区:每个节点都可以生长出子节点, + 像树枝一样层层延展,让知识自然成体系。支持 Markdown 文本、链接卡片、图片与文件分享, + 注册登录后即可创建属于你自己的知识树。 +