Commit Graph
23 Commits
Author SHA1 Message Date
qing 9ccb9cbdca feat(gateway): migrate configuration to confs service (v0.7.0)
Migrate gateway from local -i ini file to the unified confs configuration
service, following the same two-layer pattern as Python services (dnfs/db/static):

- Add confs/configs/gateway.ini with all existing settings plus new
  [Downstream] section for db/captcha/smtp host:port (previously hardcoded).
- Conf.hpp: add fromString() to parse ini from text (confs response),
  env()/env_int() static helpers for environment variables, public default
  constructor, and empty-filename guard in file constructor.
- DbC.hpp: add init(host, port) static method and static _host/_port members,
  replacing hardcoded "127.0.0.1":8005. Defaults preserved for backward compat.
- main.cpp:
  * Rewrite config loading: bootstrap CONFS_HOST/CONFS_PORT from env
    (default 127.0.0.1:8010), fetch gateway.ini via pymsg from confs
    (retry_on_refused=true), parse with ConfIni::fromString().
  * -i flag is now optional; when provided, reads local ini (dev/debug mode).
  * Add downstream host/port globals (_DB_HOST/_DB_PORT, _CAPTCHA_HOST/_CAPTCHA_PORT,
    _SMTP_HOST/_SMTP_PORT, _CONFS_HOST/_CONFS_PORT), populated from [Downstream].
  * Environment variable overrides (highest priority): LISTEN_ADDR/LISTEN_PORT,
    DB_HOST/DB_PORT, CAPTCHA_HOST/CAPTCHA_PORT, SMTP_HOST/SMTP_PORT.
  * Config integrity validation: required fields (Server.address/port,
    Proxy.default, Downstream.db_host/db_port) cause startup failure if missing.
  * Initialize DbC::init() and logs().set_endpoint() after config load.
  * Replace all hardcoded "127.0.0.1":8005/8008/8009 in captcha/smtp/db calls
    with configurable globals.
  * Add downstream service info to startup log output.
  * Fix: -v/--version flag now actually prints version and exits.
  * Bump version to 0.7.0.
- _run.sh: remove -i example.ini default; now starts without args (confs mode),
  passes "$@" through for optional -i override.
- Update README with new config loading docs, Downstream config section,
  updated directory structure, and v0.7.0 changelog.
2026-08-01 13:55:18 +08:00
qing e7a2877422 feat(dnfs): delegate Token verification to gateway /whoami
Replace the insecure local base64 decode (with FIXME comment about
missing signature verification) with proper remote RSA signature
verification through gateway's /whoami endpoint:

- Add [gateway] config section (url, timeout) to dnfs.ini with defaults.
- Support GATEWAY_URL environment variable override for Docker.
- decode_token() now POSTs to gateway /whoami with Authorization: Bearer
  <token>; gateway verifies RSA signature and expiry, returns login/user/
  is_admin. Network/gateway errors fail-closed with 503.
- Add @_handle_exp decorators to delete_note() and update_note() which
  were missing exception handling wrappers (consistent with other routes).
- docker-compose.yml: add GATEWAY_URL, CONFS_HOST, CONFS_PORT env vars
  plus host.docker.internal extra_hosts so dnfs container can reach
  host-side gateway and confs services.
2026-08-01 13:42:59 +08:00
qing d883dc620e refactor(pymsg): 新增AsyncRpcQueue通用异步队列,log_client基于此重构
- pymsg新增AsyncRpcQueue:提供内存队列+后台线程+攒批处理的通用异步基础设施,
  支持两种模式:简单RPC模式(client+method+serialize)和自定义handler模式
- confs/dnfs/static的log_client.py重构为基于AsyncRpcQueue的薄封装,
  保持API完全向后兼容(push/info/error/warning/debug/critical + 全局logs单例)
- 消除了三份log_client副本中重复的队列/线程/攒批逻辑(约150行/份),
  代码量净减少约50行
- LogClient的行为与旧版一致:逐条INSERT到db、fail-fast网络错误、队列满丢最旧
2026-08-01 12:59:33 +08:00
qing 101aed48be feat(pymsg): RpcClient 新增 retry_on_refused 阻塞等待模式,解决微服务启动顺序依赖
pymsg 客户端:
- MsgClient.call() 新增 retry_on_refused/retry_interval 参数
  - retry_on_refused=True 时,ConnectionRefusedError 不抛出,按 retry_interval(默认1s)阻塞重试
  - 重试期间每5秒向 stderr 打印等待提示,避免日志刷屏
  - 仅对 ConnectionRefusedError 重试;TimeoutError/ConnectionError/RpcError 仍立即抛出
  - 默认 retry_on_refused=False,完全向后兼容
- RpcClient 构造函数新增同名关键字参数,call() 支持单次调用覆盖
- 新增 time 模块导入
- 更新 README 文档(新增"启动等待模式"章节和参数表)

各服务(db/dnfs/static):
- _fetch_from_confs() 改用 retry_on_refused=True,移除手动 ConnectionRefusedError 捕获和 sys.exit
- 保留 RpcError(如配置文件不存在)的友好错误处理和 sys.exit(1)
- 启动时不再因 confs 未就绪而退出:pymsg 自动阻塞等待 confs 启动后自动连接成功
- 启动顺序从"刚性要求 confs 先启动"变为"不严格要求启动顺序"
- log_client 保持默认 retry_on_refused=False,后台线程不阻塞(fail-safe 丢弃日志)
- 更新各服务 README 和 confs README 的启动顺序描述

测试验证:
- 基本 call() 正常工作
- retry_on_refused=False 连接拒绝立即抛 ConnectionRefusedError
- retry_on_refused=True 阻塞等待(延迟1.5s启动服务后成功连接,重试日志正常输出)
2026-08-01 12:47:08 +08:00
qing 0ae107f953 refactor(static): 删除默认配置与本地回退逻辑,confs 改为强依赖
- 配置加载从三层(内置默认值→confs→本地config.ini回退)改为两层(confs 强依赖→环境变量覆盖)
- confs 连接失败直接 sys.exit(1),不再静默使用内置默认值或回退本地 config.ini
- file_config 初始值改为空字符串白名单模板,移除所有内置默认值
- 新增配置完整性校验:server.host/port、directory.root、cors.allowed_origins 必须非空
- ssl 段保留合理默认(enabled=false、cert/key 默认文件名),不强制配置
- 区分错误类型:连接拒绝/RpcError/通用异常分别打印明确错误信息到 stderr
- confs 连接地址改为引导配置(环境变量 CONFS_HOST/CONFS_PORT 优先,默认 127.0.0.1:8010)
- 移除 --config 命令行参数(本地 config.ini 回退已删除)
- 删除 static/config.ini 本地回退文件
- 更新 _run.sh 移除 --config 参数
- 更新 static/README.md 与 confs/README.md 文档
- confs 集成关系描述更新为"confs 最先启动"
2026-08-01 12:38:51 +08:00
qing 84adec4722 refactor(dnfs): 删除默认配置逻辑,confs 改为强依赖
- 配置加载从三层(默认值→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 文档
2026-08-01 12:33:51 +08:00
qing 2cbb93b3b7 refactor(db): 删除 confs 连接失败时使用默认配置的 fail-safe 逻辑
- 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 → ...
2026-08-01 12:29:55 +08:00
qing 6ebd544619 feat(db): 迁移 db 配置到 confs 统一配置服务
- 新增 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 文档
2026-08-01 12:23:21 +08:00
qing c06eb19f64 refix 2026-08-01 11:47:14 +08:00
qing 4e190953c0 feat(confs): integrate dnfs service with centralized config management
- Add dnfs.ini to confs/configs/ with server, database, ai_review, confs sections
- Refactor dnfs.py to use three-layer fail-safe config loading (defaults → confs → env vars)
- Unify timeouts for DB RPC and AI review calls via configuration
- Add _merge_ini() whitelist merging to prevent unexpected config injection
- Environment variables have highest priority for Docker networking (db/ai_review hostnames)
- Document config loading mechanism in both confs/README.md and dnfs/README.md

Follows the same pattern as static service integration; confs failure falls back
gracefully to built-in defaults + environment variables.
2026-08-01 04:13:39 +08:00
qing 53ce69c8dd docs(confs): update README for P1 fixes and clarify RpcClient usage
- Update title/description: pymsg RpcServer/TCP instead of plain pymsg/TCP
- Add new feature entries: cache copy return, list symlink guard, strict
  param validation, ConfigParser interpolation=None, Slowloris protection
- Fix Python call examples: RpcClient.call() returns data directly, not
  wrapped in {'success':..., 'data':...}; add RpcError try/except example
- Clarify reload semantics: empty string/non-string name returns error;
  only omitted name or explicit null triggers full cache clear
- Add parameter validation rules for get (name/key type checks) and reload
- Document INI % literal behavior (interpolation disabled)
- Add '返回值隔离' to cache mechanism section
- Expand security mechanism table: cache anti-tamper, list symlink guard,
  strict param validation, INI safe parsing
- Add docs/ directory and audit report reference to project structure
- Add notes about correct reload usage, INI % handling, and return copy
- Add audit report cross-reference in 注意事项
2026-08-01 03:46:39 +08:00
qing 90761a723d docs(confs): fill in commit hashes for fixed issues in audit report 2026-08-01 03:43:01 +08:00
qing b53d4999ce fix(confs): harden parameter validation and prevent cache/info leaks
P1 fixes:
- P1-1 _handle_reload: empty string/0/false/falsy name no longer clears all
  caches; only explicit null/omitted name triggers full cache clear; non-string
  name raises ConfigError instead of being silently str()-converted
- P1-2 ConfigStore.get(): return dict(entry) shallow copy instead of direct
  cache reference to prevent callers from mutating the internal cache
- P1-3 list(): resolve symlinks via os.path.realpath() and skip entries whose
  real path escapes configs/ root; prevents leaking external file metadata
  (size, mtime) through directory-traversing symlinks
- P1-4 _handle_get key: validate key is a string (isinstance check); reject
  non-string/empty key with clear ConfigError instead of misleading str()
  conversion leading to "key not found" errors

P2 fixes:
- P2-1 ConfigParser: use interpolation=None to disable %(key)s interpolation,
  preventing InterpolationError on config values containing bare % characters
- P2-2 argparse: update description from "pymsg/TCP" to "pymsg RpcServer/TCP"
  to match module docstring

Also update SECURITY_AUDIT_20260801.md to mark issues 9-14 as fixed and
refresh the security mechanisms summary table; all P0/P1 issues are now resolved.
2026-08-01 03:42:46 +08:00
qing e4e9968d14 docs(confs): add security audit report 20260801
- Audit covers confs.py, log_client.py, startup scripts, and pymsg/rpc.py dependency
- Documents 8 issues fixed during this audit cycle (P0-P2) including Slowloris DoS,
  OOM, thread death, TOCTOU race, and log_client hardening
- Lists 4 remaining P1 issues (reload empty string clears all cache, mutable cache
  reference leak, list() symlink metadata leak, key type validation), 4 P2 issues,
  and 6 P3 issues with root cause, risk analysis, and fix suggestions
- Security mechanisms summary table for quick reference
- Follows project convention: gateway/docs/SECURITY_AUDIT_YYYYMMDD.md
2026-08-01 03:37:22 +08:00
qing 9efb96ea66 fix(confs): eliminate TOCTOU race in ConfigStore path resolution
- Split _resolve() into _validate_name() (pure string checks, lock-free
  fast-fail) and _resolve_locked() (filesystem checks, must be called
  under lock)
- Perform all filesystem operations (realpath → isfile → stat → open)
  inside self._lock in get(), eliminating the window between path
  validation and file open where a symlink swap could escape the
  configs directory
- Apply same fix to reload(): validate name outside lock, resolve path
  inside lock to prevent TOCTOU during targeted reload
- Hold lock during list() directory traversal so the `cached` flag
  (which reads self._cache) is consistent with directory state; wrap
  os.stat() in try/except to tolerate files disappearing mid-traversal
- Update docstring and README:
  - Protocol description corrected to pymsg RpcServer/TCP
  - Python client examples updated from legacy MsgClient to RpcClient
  - Security section now documents TOCTOU mitigation and Slowloris
    protection (added by earlier pymsg fix)
2026-08-01 03:32:19 +08:00
qing 1b9c727e80 fix(log_client): harden background flush thread against exceptions
- Wrap _flush_loop main body in outer try/except to prevent any
  unexpected exception (network errors, serialization errors, etc.)
  from killing the background thread (which would cause unbounded
  queue growth and eventual OOM)
- Wrap _send() call in inner try/except as final safety net
- Wrap RpcClient() construction in try/except (connection failures
  during client creation were previously unhandled)
- Add _safe_json() helper: unserializable extra fields fall back to
  repr()/str() instead of raising TypeError that could kill thread
- Generate timestamp at push() (enqueue time) instead of _send() time,
  so timestamps reflect actual event time even when queue is backed up
- Replace unreliable time.sleep(0.6) in flush() with _flush_done Event
  for proper synchronization between caller and background thread
- Add _QUEUE_MAXSIZE=10000 with drop-oldest policy to prevent unbounded
  memory growth even in pathological cases
- Use put_nowait with Full handling to ensure push() never blocks
  business threads
- Sync fix to all 4 copies: confs/static/smtp/dnfs
2026-08-01 03:23:19 +08:00
qing da7f11d352 fix(confs): add MAX_CONFIG_SIZE (1 MiB) to prevent OOM on oversized configs
- Add MAX_CONFIG_SIZE constant (1 MiB) to limit single config file size
- Check file size via os.stat() before reading into memory
- Oversized files raise ConfigError with descriptive message instead of
  being fully read (which could cause OOM with multi-GB files)
- Use single os.stat() call to get both mtime and size atomically
- Update README: document size limit in security mechanisms and notes
- Existing small configs (ini/json/yaml, typically <10KB) are unaffected
2026-08-01 03:15:06 +08:00
qing eb506e5cbf docs(confs): 更新 README 反映 static 已接入现状
新增"已接入的服务"表格,记录 static 的三层 fail-safe 接入方式
及实现细节;注意事项改为指向集成章节,避免重复描述。
2026-08-01 02:46:58 +08:00
qing 0ef96c28f6 docs(confs): 新增服务 README
含功能特性、协议路由、缓存/安全机制、集成关系与项目结构
2026-08-01 02:38:20 +08:00
qing 287cab189d feat(static): 配置接入 confs 统一配置服务
- fsys.py 启动时优先从 confs(127.0.0.1:8010)拉取 static.ini,
  支持 CONFS_HOST/CONFS_PORT 环境变量覆盖
- confs 不可用时 fail-safe 回退本地 config.ini
- static.ini 纳入 confs/configs/ 统一分发
2026-08-01 02:30:14 +08:00
qing 7d7e3dc10a fix(confs): 删除错放的 .env 文件
confs.py 不读取 .env(无 dotenv 依赖),该文件实为 SMTP 凭据,
被误放入 confs 目录且会导致 python-dotenv 解析报错。
2026-08-01 02:20:46 +08:00
qing 55fbe2f1f1 feat(confs): 统一配置服务(pymsg/TCP :8010)
- confs.py 重写为统一配置服务:get/list/reload/health 路由,
  内存缓存 + mtime 自动重载,路径遍历防护,ini/json 按键提取
- 复制 log_client.py,新增 configs/example.ini 示例配置
- 注册到 core/init.txt(db 之后启动)与 _inst.sh 安装序列
2026-08-01 02:15:04 +08:00
qing e248290860 统一配置模块 2026-08-01 01:54:25 +08:00