Files
Web_BAI_Manage_ApiServer/pocket-base/spec/changes.2026-03-23-pocketbase-hooks-auth-hardening.md

13 KiB
Raw Blame History

OpenSpec 变更记录PocketBase Hooks 认证链路加固

日期

  • 2026-03-23

范围

本次变更覆盖 pocket-base/ 下 PocketBase hooks 项目的微信登录、平台注册/登录、资料更新、token 刷新、认证落库、错误可观测性、索引策略、字典管理、附件管理、文档管理、文档操作历史、页面辅助操作、健康检查版本探针、OpenAPI 鉴权与统一响应规范。


一、认证模型调整

1. 认证体系

  • 保持 PocketBase 原生 auth token 作为唯一正式认证令牌。
  • 登录与刷新响应统一为项目标准结构:codemsgdata,认证成功时额外返回顶层 token
  • authMethod 统一使用空字符串 '',避免触发不必要的 MFA / login alerts 校验。

2. Header 规则

  • 正式认证 Header 为:Authorization: Bearer <token>
  • 非标准 Header Open-Authorization 不属于本项目接口定义。
  • users_wx_openid Header 已从 active hooks 鉴权链路移除。

二、身份字段与数据模型约束

1. openid 作为唯一业务身份锚点

  • tbl_auth_users 统一保留 openid 作为全平台身份锚点。
  • 微信用户:openid = 微信 openid
  • 平台用户:openid = 服务端生成的 GUID
  • 业务逻辑中不再使用 users_wx_openid
  • 用户查询、token 刷新、资料更新均基于 auth record 的 openid

2. auth 集合兼容字段

由于 tbl_auth_users 当前为 PocketBase auth 集合,登录注册时为兼容 PocketBase 原生 auth 校验,新增以下兼容策略:

  • email 使用占位格式:
    • 微信用户:<openid>@wechat.local
    • 平台用户:<openid>@manage.local
  • 自动生成随机密码
  • 自动补齐 passwordConfirm

说明:

  • 占位 email 仅用于满足 auth 集合保存条件,不代表用户真实邮箱。
  • 业务主身份仍然是 openid

3. 自定义字段可空策略

  • tbl_auth_users 的自定义字段目标约束为:除 openid 外,其余业务字段均允许为空。
  • 已将 schema 脚本中的 user_id 改为非必填。
  • 其余业务字段保持非必填。

三、查询与排序修复

1. 移除无意义的 created 排序

在 hooks 查询中,以下查询原先使用 '-created' 排序:

  • openid 查询用户
  • company_id 查询公司
  • users_phone 查询重复手机号

该写法在 PocketBase 当前运行场景下触发:

  • invalid sort field "created"

现已统一移除排序参数,改为空排序字符串,因为这些查询本质上均为精确匹配或去重检查,不依赖排序。


四、错误可观测性增强

1. 登录路由显式错误响应

POST /api/wechat/login 新增局部 try/catch

  • 保留业务状态码
  • 返回 { code, msg, data }
  • 写入 logger.error('微信登录失败', ...)

2. 全局错误包装顺序修正

  • routerUse(...) 全局错误包装提前到路由注册前。
  • 统一兼容 err.statusCode / err.status

3. auth 保存失败透传

新增 saveAuthUserRecord(record) 包装 $app.save(record)

  • 失败时统一抛出 保存认证用户失败
  • 附带 originalMessageoriginalData

目的:

  • 避免 PocketBase 默认 Something went wrong while processing your request. 吞掉具体原因。

五、数据库索引策略修复

1. users_phone 索引调整

原设计:

  • users_phone 唯一索引

问题:

  • 新用户注册阶段手机号为空,多个空值会触发唯一约束冲突,导致注册失败。

现调整为:

  • users_phone 普通索引

说明:

  • 手机号唯一性改由业务逻辑在资料完善阶段校验。
  • 允许多个未完善资料用户以空手机号存在。

六、接口契约同步结果

当前 active PocketBase hooks 契约如下:

  • POST /api/system/test-helloworld
  • POST /api/system/health
  • POST /api/system/refresh-token
  • POST /api/platform/register
  • POST /api/platform/login
  • POST /api/wechat/login
  • POST /api/wechat/profile
  • POST /api/dictionary/list
  • POST /api/dictionary/detail
  • POST /api/dictionary/create
  • POST /api/dictionary/update
  • POST /api/dictionary/delete
  • POST /api/attachment/list
  • POST /api/attachment/detail
  • POST /api/attachment/upload
  • POST /api/attachment/delete
  • POST /api/document/list
  • POST /api/document/detail
  • POST /api/document/create
  • POST /api/document/update
  • POST /api/document/delete
  • POST /api/document-history/list

其中平台用户链路补充为:

POST /api/platform/register

  • body 必填:users_nameusers_phonepasswordpasswordConfirmusers_picture
  • 自动生成 GUID 并写入统一身份字段 openid
  • 写入 users_idtype = ManagePlatform
  • 成功时返回统一结构:codemsgdata,并在顶层额外返回 token

POST /api/platform/login

  • body 必填:login_accountpassword
  • 仅允许 users_idtype = ManagePlatform
  • 前端使用邮箱或手机号 + 密码提交
  • 服务端先通过 PocketBase auth-with-password 校验身份,再由当前 hooks 进程签发正式 token
  • 成功时返回统一结构:codemsgdata,并在顶层额外返回 token

其中:

POST /api/wechat/login

  • body 必填:users_wx_code
  • 自动以微信 code 换取微信侧 openid 并写入统一身份字段
  • 若不存在 auth 用户则尝试创建 tbl_auth_users 记录
  • 写入 users_idtype = WeChat
  • 成功时返回统一结构:codemsgdata,并在顶层额外返回 token

POST /api/wechat/profile

  • Authorization
  • 基于当前 auth record 的 openid 定位用户
  • 服务端用 users_phone_code 换取手机号后保存

POST /api/system/refresh-token

  • body 可选:users_wx_code(允许为空)
  • Authorization 可选:
    • 若 token 仍有效:基于当前 auth record 续签
    • 若 token 已过期:回退到微信 code 重签流程
  • 若 token 已过期且未提供 users_wx_code,返回:token已过期请上传users_wx_code
  • 返回统一结构:codemsgdata,并在顶层额外返回新 token
  • 属于系统级通用认证能力,不限定为微信专属接口

字典管理接口

新增 dictionary 分类接口,统一要求平台管理用户访问:

  • POST /api/dictionary/list
    • 支持按 dict_name 模糊搜索
    • 返回字典全量信息,并将 dict_word_enumdict_word_descriptiondict_word_sort_order 组装为 items
  • POST /api/dictionary/detail
    • dict_name 查询单条字典
  • POST /api/dictionary/create
    • 新增字典,system_dict_id 自动生成,dict_name 唯一
  • POST /api/dictionary/update
    • original_dict_name / dict_name 更新字典
  • POST /api/dictionary/delete
    • dict_name 真删除字典

说明:

  • dict_word_enumdict_word_descriptiondict_word_sort_order 已统一改为 JSON 字符串持久化,其中 dict_word_sort_order 已改为 text 类型。
  • 查询时统一聚合为:items: [{ enum, description, sortOrder }]

附件管理接口

新增 attachment 分类接口,统一要求平台管理用户访问:

  • POST /api/attachment/list
    • 支持按 attachments_idattachments_filename 模糊搜索
    • 支持按 attachments_status 过滤
    • 返回附件元数据以及 PocketBase 文件流链接 attachments_url
  • POST /api/attachment/detail
    • attachments_id 查询单个附件
    • 返回文件流链接与下载链接
  • POST /api/attachment/upload
    • 使用 multipart/form-data
    • 文件字段固定为 attachments_link
    • 上传成功后自动生成 attachments_id
    • 自动写入 attachments_owner = 当前用户 openid
  • POST /api/attachment/delete
    • attachments_id 真删除附件
    • 若该附件已被 tbl_document.document_imagedocument_video 引用,则拒绝删除

说明:

  • tbl_attachments.attachments_link 为 PocketBase file 字段,保存实际文件本体。
  • 对外查询时会额外补充:
    • attachments_url
    • attachments_download_url

文档管理接口

新增 document 分类接口,统一要求平台管理用户访问:

  • POST /api/document/list
    • 支持按 document_iddocument_titledocument_subtitledocument_summarydocument_keywords 模糊搜索
    • 支持按 document_statusdocument_type 过滤
    • 返回时会自动联查 tbl_attachments
    • 额外补充:
      • document_image_url
      • document_video_url
      • document_image_attachment
      • document_video_attachment
  • POST /api/document/detail
    • document_id 查询单条文档
    • 返回与附件表联动解析后的文件流链接
  • POST /api/document/create
    • 新增文档
    • document_id 可不传,由服务端自动生成
    • document_imagedocument_video 必须传入已存在的 attachments_id
    • 成功后会写入一条文档操作历史,类型为 create
  • POST /api/document/update
    • document_id 更新文档
    • 若传入附件字段,则会校验对应 attachments_id 是否存在
    • 成功后会写入一条文档操作历史,类型为 update
  • POST /api/document/delete
    • document_id 真删除文档
    • 删除前会写入一条文档操作历史,类型为 delete

说明:

  • document_imagedocument_video 当前保存的是 attachments_id,不是 PocketBase 文件字段。
  • 文档查询时通过 attachments_id -> tbl_attachments 反查实际文件,并返回可直接访问的数据流链接。
  • document_owner 语义为“上传者 openid”。

文档操作历史接口

新增 document-history 分类接口,统一要求平台管理用户访问:

  • POST /api/document-history/list
    • 不传 document_id 时返回全部文档历史
    • 传入 document_id 时仅返回该文档历史
    • 结果按创建时间倒序排列

说明:

  • 操作历史表为 tbl_document_operation_history
  • 当前由文档新增、修改、删除接口自动写入
  • 主要字段为:
    • doh_document_id
    • doh_operation_type
    • doh_user_id
    • doh_current_count
    • doh_remark

七、页面与运维辅助能力新增

1. PocketBase 页面

当前页面入口:

  • /pb/manage
  • /pb/manage/login
  • /pb/manage/dictionary-manage
  • /pb/manage/document-manage

页面能力:

  • 首页支持跳转到子页面
  • 字典管理页支持:
    • Bearer Token 粘贴与本地保存
    • dict_name 模糊搜索
    • 指定字典查询
    • 行内编辑基础字段
    • 弹窗编辑枚举项
    • 新增 / 删除字典
    • 返回主页
  • 文档管理页支持:
    • 先上传附件到 tbl_attachments
    • 再把返回的 attachments_id 写入 tbl_document.document_image / document_video
    • 新增文档
    • 查询文档列表
    • 直接展示 PocketBase 文件流链接
    • 删除文档

说明:

  • 原页面 page-b.js 已替换为 document-manage.js
  • 页面实际走的接口链路为:
    • /api/attachment/upload
    • /api/document/create
    • /api/document/list
    • /api/document/delete

2. 健康检查版本探针

POST /api/system/health 新增:

  • data.version

用途:

  • 通过修改 APP_VERSION 判断 hooks 是否已成功部署并生效

配置来源:

  • 进程环境变量 APP_VERSION
  • runtime.js

八、OpenAPI 与 Apifox 调试策略调整

1. 统一返回结构

所有对外接口统一返回:

  • code
  • msg
  • data

认证成功类接口额外返回:

  • token

不再返回以下顶层字段:

  • record
  • meta

2. 鉴权文档策略

OpenAPI 文档中已取消 bearerAuth 鉴权组件与接口级重复 Authorization 参数。

统一约定:

  • 在 Apifox 环境中配置全局 HeaderAuthorization: Bearer {{token}}
  • 不再依赖文档中的 Bearer 组件自动注入

此举目的是:

  • 避免接口页重复出现局部 Authorization
  • 统一依赖环境变量完成鉴权注入

九、当前已知边界

  1. tbl_auth_users 为 PocketBase auth 集合,因此仍受 PocketBase auth 内置规则影响。
  2. 自定义字段“除 openid 外均可空”已在脚本层按目标放宽,但 auth 集合结构更新仍可能触发 PocketBase 服务端限制。
  3. 若线上仍返回 PocketBase 默认 400需要确保最新 hooks 已部署并重启生效。
  4. 平台登录通过回源 PocketBase REST 完成密码校验,因此 POCKETBASE_API_URL 必须配置为 PocketBase 进程/容器内部可达地址,不应使用外部 HTTPS 域名。
  5. Apifox 环境中需自行维护全局 HeaderAuthorization: Bearer {{token}},否则鉴权接口不会自动携带 token。

十、归档建议

部署时至少同步以下文件:

  • pocket-base/bai-api-main.pb.js
  • pocket-base/bai-web-main.pb.js
  • pocket-base/bai_api_pb_hooks/
  • pocket-base/bai_web_pb_hooks/
  • pocket-base/spec/openapi.yaml
  • script/pocketbase.js

并在 PocketBase 环境中执行 schema 同步后重启服务,再进行接口验证。

建议归档后的发布核验顺序:

  1. POST /api/system/health:确认 data.version
  2. POST /api/platform/login:确认返回统一结构与顶层 token
  3. POST /api/dictionary/list:确认鉴权与字典接口可用

十一、归档状态

  • 已将本轮字典管理、PocketBase 页面、统一响应、平台登录兼容修复、健康检查版本探针、OpenAPI/Apifox 鉴权策略调整并入本变更记录。
  • 本记录可作为当前 PocketBase hooks 阶段性归档基线继续维护。