pbls/wwwroot/healthz.dspy
agent.develop b4ad74cbb9 docs(pbls): 修正 health 探针头注释 URL 映射说明(QC 轻微缺陷)
- wwwroot/api/public/health/index.dspy 头注释系从 healthz 版复制,误写
  '-> GET /healthz',实际映射为 '-> GET /api/public/health',已修正;
  并补明双探针路径分工(healthz/index.dspy=复测门禁① 判活口径,
  api/public/health/index.dspy=build.sh 步骤14 自检口径,healthz.dspy=
  带扩展名兼容副本),三者均在 scripts/load_path.py 以 any 角色逐条注册。
- wwwroot/healthz.dspy 头注释首行文件名同步为自身路径(原误写 index.dspy)。
- 仅注释文本变更,无逻辑改动;三份 .dspy 均通过 ast 包装语法自检。
- 同类问题全仓扫描:/opt/pbls 残留 0、pbls 库名口径残留 0、
  9300 仅存于 venv 第三方 pip 常量与历史补丁脚本注释(非活动配置)。

关联 Bug: WIu0Rkn0KwB0XTjYZz0qk
2026-09-25 11:54:38 +08:00

59 lines
3.3 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# apps/pbls/wwwroot/healthz.dspy —— 部署冒烟健康探针带扩展名副本(无鉴权 / any 角色)
#
# 【为什么必须有这个文件】Bug WIu0Rkn0KwB0XTjYZz0qk 第 4 轮复测门禁 ① 用
# curl -s -m 8 -o /tmp/h1.txt -w 'HTTP=%{http_code}' http://127.0.0.1:9380/healthz
# 作为「应用是否活着」的判据,但仓库里此前**根本不存在 /healthz 端点**
# (grep -rn healthz apps/pbls/app apps/pbls/wwwroot 零命中),而 build.sh 步骤 14
# 自检用的却是另一条路径 /api/public/health(同样不存在)。两条探针路径都缺 →
# 即便进程起来、端口监听,门禁 ① 也必然 HTTP=000/404,冒烟永远不可能转绿。
# 本文件补齐 /healthz;同批补 wwwroot/api/public/health/index.dspy 保持 build.sh 原口径。
#
# 【URL 映射】ahserver 按 website.root=wwwroot + processors['.dspy']='dspy' 解析:
# wwwroot/healthz/index.dspy -> GET /healthz
# 另在 wwwroot/healthz.dspy 放同逻辑副本 -> GET /healthz.dspy(兼容带扩展名探测)。
#
# 【dspy 规约(dspy-file-implementation-spec 硬规则)】
# * 零 import:json / datetime 由框架预加载,直接裸用;
# * 每个分支显式 return(框架把文件包进 async def,裸表达式返回 None → 500);
# * dict 返回内**不用 f-string**(exec 误读 '}' → SyntaxError),改字符串拼接;
# * try/except 兜底:对象端点异常也返回合法 JSON({"status":"error",...}),不抛 500;
# * json.dumps(..., default=str) 防 Decimal/datetime 序列化炸;
# * 不返回任何敏感字段(库口令/密钥一律不出现在响应里)。
#
# 【语义约定】status=ok 只表示「进程活着 + HTTP 栈可服务 + 路由/RBAC 已放行」。
# DB 探测是**附加诊断信息**(db.tables / db.error),DB 不通**不**把 status 降级为
# error —— 否则库抖动会让「进程存活」探针失真,排障时分不清是进程死了还是库挂了。
# 表数门禁(37 张)由 build.sh 步骤 10 的硬断言负责,不在探针里做判定。
try:
result = {
'status': 'ok',
'app': 'pbls',
'endpoint': '/healthz',
'ts': datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S'),
}
# ---- 附加诊断:pbl_* 业务表数量(只读 information_schema,失败不影响 status)----
db_info = {'ok': False, 'tables': None, 'error': None}
try:
env = request._run_ns
async with get_sor_context(env, 'pbl_common') as sor:
rows = await sor.sqlExe(
"SELECT COUNT(*) AS cnt FROM information_schema.tables "
"WHERE table_schema = DATABASE() AND table_name LIKE ${pat}$",
{'pat': 'pbl\\_%'})
if rows:
first = rows[0]
cnt = getattr(first, 'cnt', None)
if cnt is None and hasattr(first, 'keys'):
cnt = dict(first).get('cnt')
db_info['tables'] = int(cnt) if cnt is not None else 0
db_info['ok'] = True
except Exception as dbe:
db_info['error'] = str(dbe)
result['db'] = db_info
return json.dumps(result, ensure_ascii=False, default=str)
except Exception as e:
return json.dumps({'status': 'error', 'app': 'pbls', 'endpoint': '/healthz',
'error': str(e)}, ensure_ascii=False, default=str)