[开发]flinthub-client 客户端改造开发方案(2026-10-11)

👑Lv.11 元老 🌞 骨干会员
2026-10-26 00:24:38

本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:WWW/docs/flinthub-client客户端改造开发方案_2026-10-11.md。

flinthub-client 客户端改造开发方案(2026-10-11)

依据:flinthub-client/(站长提供的 bbs1 客户端源码,uni-app Vue3+Vite)+ 已发布的 mobile_api 插件 (v1.0.0,min_core 1.1.21)。

本方案只做设计与评估,不写代码(用户明确「先不动手做」)。

阅读顺序:§0 结论速览 → §2 源码核查 → §3 差异矩阵 → §4 方案选型 → §5 插件侧改造 → §6 客户端侧改造 → §10 待拍板问题。


0. 结论速览(先看这段)

一句话结论:传输层信封不兼容是主要工作量,其余是「按我们的语义补齐缺口」。改造可行,且客户端传输层只需重写约 90 行(utils/api.js)。

三条核查结论直接决定改造路线:

  1. 客户端是 op 风格协议,与插件 REST 完全不兼容(详见 §3.1)。客户端所有请求都走 utils/api.js 的 call(op) → base + '&op=' + op,响应判定 payload.ok。插件的 REST 信封是 {code,message,data},没有 ok 字段,客户端若不改会把一切响应都当成 server_error。好消息:全部网络访问都收敛在 utils/api.js 一个文件里(约 90 行),改造面极小。
  1. PoW 与无状态 Bearer 冲突,是登录集成的硬阻塞(详见 §3.2 / §8.1)。核心 PoW 的 challenge 存在 PHP Session($_SESSION['pow_challenge']),而插件签发的 Bearer Token 是无状态的、不碰 Session。客户端要过 PoW 就必须带 PHPSESSID——App 端手工维护 Cookie 非常脆弱,H5 端还会被 CORS(插件不下发 Allow-Credentials)拒绝。推荐方案:在插件内实现「无状态 PoW」(自签 challenge = 时间戳 + HMAC,客户端算 nonce,插件本地校验),不碰核心 PoW、不改核心。
  1. 部署层路由阻塞:当前 nginx 配置下 /api/... 根本到不了 PHP(详见 §3.3)。nginx vhost 的 location / 没有 try_files ... /index.php 回退,/api/threads 这类真实路径在 nginx 层即 404;而 /index.php/api/threads 又因 config.php 写死 define('BASE_PATH',''),插件拿到的 requestPath() 是 /index.php/api/threads,前缀匹配不上 /api/。这是环境问题、不是代码问题,上线前必须给 vhost 加一行 try_files(或明确改用 Apache)。

工作量分布(粗估,非时间承诺):传输层重写(小)+ 字段/语义补齐(中,插件侧新增若干端点与字段)+ PoW 无状态化(中,插件侧新增 1 个自签校验器)+ 品牌替换与配置(小)+ 真机验收(中)。


1. 目标与非目标

目标

  • 以 flinthub-client 为基底,产出「我们自己的」App / H5 客户端,不重写一套。
  • 客户端适配 mobile_api 插件的 REST 契约(信封、端点、字段、分页、Token)。
  • 补齐客户端需要、而插件当前缺失的功能(站点信息 / 楼层 / 通知全读 / 私信会话聚合 / 改密与资料完善等)。
  • 解决 PoW 集成与部署路由两个硬阻塞。
  • 品牌、站点名、图标、启动图替换为本站。

非目标

  • 不做「服务端 op 兼容垫片」(否决,理由见 §4.2)。
  • 不改核心 Router;不改核心既有业务逻辑(缺口优先在插件侧新增端点实现,必要时才在核心纯新增方法)。
  • 不做 iOS 包、不做应用商店上架、不做服务端部署(沿用原客户端 部署教程.md 的边界)。
  • 本期不做多语言(客户端目前全硬编码中文,无 i18n;是否引入见 §10 待拍板)。

2. 源码核查结论

2.1 工程形态与技术栈

项值
工程名bbs1-client v0.1.0,描述「6735 社区 客户端(uni-app,H5 + Android)」
技术栈uni-app + Vue 3.4.21 + Vite 5.2.8;@dcloudio/* 全家桶 3.0.0-alpha-5030120260930001
产物H5 网页版(命令行可出)+ Android APK(必须 HBuilderX 云打包,命令行出不了 .apk)
页面数12 页:home / forums / forum / topic / search / user / mine / notice / dm-thread / login / post / settings
导航全局 navigationStyle:"custom",自绘导航栏(app-nav.vue、utils/system.js 的 NAV_HEIGHT=44 + 状态栏高度)
配置入口src/utils/site.config.js(SITE_API_BASE/SITE_APP_KEY)+ .env 的 VITE_API_BASE/VITE_APP_KEY(env 优先)
无原生能力无推送 / 分享 / 相机;权限仅 INTERNET;上线前不要顺手加权限(§9)

关键文件定位(改造核心)

  • src/utils/api.js —— 网络层唯一入口(call/data/decode/headers),改造重点。
  • src/utils/config.js —— apiBase()/appKey()/siteOrigin()/connectionSummary()。
  • src/utils/store.js —— 会话存储 bbs_session(token/expires_at(秒级)/user)。
  • src/utils/site.js —— 站点信息与限值表(FALLBACK)。
  • src/utils/format.js —— absolutizeHtml()、topicUrl()(指向站内 /index.php?a=topic&id=)。
  • src/utils/menu.js / system.js / swipe-back.js —— 编译期菜单常量、导航布局、自绘左滑返回。
  • src/components/* —— app-avatar(纯本地首字母头像,不请求网络图)、topic-item、app-tabbar、app-nav、app-sheet、state-block、app-icon。

2.2 客户端原协议(op 风格)

  • URL = apiBase + '&op=' + op;apiBase 形如 https://域名/index.php?a=api(LightSNS 入口写法)。
  • GET:参数拼 query;POST:application/x-www-form-urlencoded,body 含 op。
  • 请求头:X-Requested-With;登录后 Authorization: Bearer <token>;X-App-Key: <应用密钥>。
  • 响应信封:{ok:true,data} / {ok:false,error:{code,msg}}。
  • 客户端错误码字符串:unauthorized / token_expired / network / bad_response / server_error(多处按 e.code==='network'?'offline':'error' 决定空态样式)。
  • 会话:uni.setStorageSync('bbs_session',{token,expires_at,user});expires_at 按秒级时间戳判过期(Number(expires_at)*1000 > Date.now())。

2.3 客户端逐页「要什么」(端点 + 字段)

以下为核查结论,是 §3.4 差异矩阵的原始依据。所有字段名均为客户端实际读取的键名。

页面调用的 op读取的字段
hometopics{fid,sort,p} / site / forumsrows/page/has_more/page_size;行 pinned/title/title_color/title_large/username/user_id/time_text/reply_count/forum_name/matched_excerpt;site_name/user_count/topic_count/reply_count;排序枚举 comment(按回复)/post(按发帖)
topictopic{id,p}、reply_create{topic_id,body}、reply_update{id,body}、topic_delete{id}、reply_delete{id}payload 即 topic(author{id,username}/forum_name/forum_id/time_text/pinned/can_edit/can_reply/title_color/title_large/view_count/reply_count/body(裸文)/body_html);payload.replies={rows:[{id,author,floor,time_text,edited,body,body_html}],page,total,has_more,page_size};reply_create 回 {topic:<完整detail>, reply_id}
loginlogin{username,password}(POST) / register{...}(POST)无 PoW/验证码;site.info.allow_register!==false;成功 setSession(result.data)
mineme、topics{uid}、siteuser.group_name/points/unread/allow_manage/id/username;topics.total;site_name/topic_count/forum_count;退出仅本地 clearSession()(未调 logout)
settingsme、profile_update{...}bio/avatar_seed/avatar_style/created_at/last_post_at/is_muted/group_name/points;保存后若回 token 则换会话(改密换 token);头像 17 样式 dylan…toon-head(样式/种子仅作用于网页端图案头像)
postforums、topic{id}(编辑)、topic_create{forum_id,title,body}、topic_update{...,reply_order:'0'}forums.rows[].can_post;title/body/forum_id/forum_name/can_edit;成功后 topic_create.data.id;提示「图片与附件上传暂未接入」
searchsearch{q,field,sort,p}、siterows/page/has_more;field∈`title
usertopics{uid,p}、merows/page/has_more/total;points/group_name/bio/created_at/created_at_text/username
noticedm_threads、notifications{box:'notice',p}、notifications_read{id}或{all:'1',box}、dm_user{username}私信会话 rows:[{partner{id,username}, last{content,mine,time_text}, unread}], unread_total, unread;通知 rows:[{id,kind,read,content,time_text,topic_id,sender_id}], page, has_more, unread;kind∈`mention
dm-threaddm_thread{partner_id}、...{partner_id,before_created_at,before_id}、dm_read{partner_id}、dm_send{partner_id,content}rows:[{id,mine,content,time_text,created_at}], has_older, partner{username};游标分页(非 page);发送回 data.message;maxlength:5000
forums / forumforums / forums+topics{fid,sort,p}rows[].id/name(版块列表);详情复用列表逻辑

2.4 服务端(插件)现有契约要点

  • 前缀 /api;信封 {code,message,data}(0 成功 / 4xxxx 客户端错 / 5xxxx 服务端错)。
  • Token:Bearer,前缀 fh_app_;access 2h / refresh 30d(轮换);Token 级限流「写 5/分、读 60/分」。
  • 中间件链:总开关 → 认证+封禁 → scope(read/write)→ 限流 → 业务 → 审计。
  • 已实现端点:/api/auth/{login,register,refresh,logout,me}、/api/categories(/{id})、/api/threads(GET/POST、/{id} 的 GET/PUT/DELETE、/{id}/replies GET/POST)、/api/users/{id}(、/{id}/threads、/{id}/replies)、PUT /api/users/me、/api/search、/api/notifications(、/{id}/read)、/api/messages(、/{user_id}、POST)。
  • 前端设备管理页:/api-tokens*(hook route_register)。

3. 差异矩阵

3.1 传输层(不兼容,必须重写客户端网络层)

维度客户端(op)插件(REST)处置
URLbase + '&op=' + op,base=…?a=apibase + '/api/...' 真实路径客户端 config.apiBase() 改存站点根 URL;call 改拼 REST 路径
方法仅 GET/POSTGET/POST/PUT/DELETE客户端调用点传入 REST 方法
请求体form-urlencoded,含 opJSON 或表单统一 JSON
信封{ok, data} / {ok:false,error:{code,msg}}{code,message,data}decode 改判 code===0
鉴权Authorization: Bearer + X-App-KeyAuthorization: Bearer fh_app_…(无 App-Key 概念)客户端去掉 App-Key
刷新无 refresh 概念access 2h + refresh 30d 轮换客户端新增 401 自动 refresh

结论:唯一改动点是 src/utils/api.js(重写)+ config.js(去 appKey)+ store.js(增 refresh_token 字段与过期语义)。调用点几乎不动(仍走 call/data),只需改 op 字符串为 REST 路径 + 方法。

3.2 认证/PoW(硬阻塞,需方案拍板)

维度客户端插件/核心冲突
登录直接提交 username/password要求 pow_nonce(PoW 通过才放行)客户端无 PoW
PoW challenge无存 PHP Session(5 分钟、一次性)Bearer 无状态,客户端不带 PHPSESSID 必 400 err_pow_or_captcha
CORS 凭据—applyCors 不下发 Allow-CredentialsH5 跨域带 Cookie 会被浏览器拒

→ 见 §4.3 三个候选方案,推荐方案 ②(插件内无状态 PoW)。

3.3 部署/路由(环境阻塞)

事实影响
vhost location / 无 try_files ... /index.php/api/threads 真实路径在 nginx 层即 404,到不了 PHP
config.php 写死 define('BASE_PATH','')/index.php/api/threads 的 requestPath() 含 /index.php 前缀,不匹配 /api/,插件不拦截
.htaccess 有 RewriteRule ^(.*)$ index.php/$1Apache 下可用;当前 nginx 不读 .htaccess

→ 处置见 §7.3。

3.4 端点与字段差异(客户端要 → 插件给)

A. 完全缺失的端点

客户端 op插件现状处置
site 站点信息无插件新增 GET /api/site
reply_update无新增 PUT /api/replies/{id}
reply_delete无新增 DELETE /api/replies/{id}
notifications_read 全读仅单条已读新增 POST /api/notifications/read-all
dm_threads 会话聚合列表无(只有消息列表)新增 GET /api/messages/conversations
dm_user 用户名解析无新增 GET /api/users/lookup?username=
dm_read 会话已读无新增 POST /api/messages/{user_id}/read
无对应(客户端退出未调)POST /api/auth/logout 已存在客户端退出补调 logout

B. 已存在但语义/字段不匹配的端点

端点差异处置
GET /api/categories返回 {list:[...]},客户端读 data.rows兼容:外层加 rows(保留 list)
GET /api/categories行缺 can_post行内补 can_post
GET /api/threads行缺 time_text/pinned/title_color/title_large/matched_excerpt/forum_name补齐字段(is_pinned→pinned 等)
GET /api/threads/{id}详情缺 author{} 对象、body(裸文)、内联 replies详情补 author{}、body、内联 replies(或客户端二次拉取,见 §4.4)
GET /api/threads/{id}/replies行缺 floor/author{}/body/body_html/edited/time_text补齐楼层号等
GET /api/users/{id}缺 group_name/bio/created_at_text补齐
GET /api/auth/me缺 group_name/unread/allow_manage/last_post_at/is_muted/bio/avatar_style/avatar_seed补齐
PUT /api/users/me只支持 email/signature扩为支持 bio/avatar_style/avatar_seed + 改密 password/password2(改密需换 token)
GET /api/search参数 type/time_range,客户端要 `field(title\body\
GET /api/notifications无 box 分箱;行字段 type/title/summary/...;无 kind/topic_id/sender_id/read行补 kind/topic_id/sender_id/read(布尔);支持 box
GET /api/messages是消息流水,非会话聚合见上「新增会话聚合」
GET /api/messages/{user_id}用路径 user_id,回 peer{}、无 has_older、无游标客户端适配:partner_id→路径 user_id;peer→partner;补游标 before_created_at/before_id 与 has_older
POST /api/messages回 {id,receiver_id}补回 {message:{...}}
topic_create 返回POST /api/threads 回 {id}客户端读 data.id ✓(已兼容)

3.5 功能缺口清单(汇总,按优先级)

P0(不做则核心流程走不通)

  • 传输层重写(§3.1)
  • PoW 集成(§3.2)
  • 部署路由修复(§3.3)
  • site 站点信息端点(首页/搜索/登录均依赖)
  • categories 外层 rows 键(首页/版块/发帖均依赖)

P1(主要页面功能完整)

  • 楼中楼:reply_update / reply_delete / floor
  • 通知:read-all + 行字段补齐
  • 私信:会话聚合列表 + dm_read + dm_user + 游标分页 + partner/message 命名
  • me 字段补齐 + profile_update 支持 bio/头像样式/改密
  • 帖子行/详情字段补齐(time_text/pinned/author{}/body/matched_excerpt/forum_name)

P2(体验完整)

  • search 的 field/sort 语义
  • topics 排序枚举 comment/post 对齐
  • 客户端退出补调 logout
  • connectionSummary 自检项调整(去 appKey、改显示站点根)

4. 方案选型与推荐

4.1 总路线(推荐)

改客户端适配插件 + 插件按 REST 语义补齐缺口。

  • 客户端:重写 utils/api.js(REST + 新信封 + refresh),调整 config.js/store.js/site.js/format.js,按 §3.4 逐页微调字段与端点调用。
  • 插件:现状已是 REST,只需新增若干端点 + 补齐若干字段,发 v1.1.0。
  • 核心:优先不改;若必须,只做「纯新增方法」(沿用 mobile_api 方案 D15 的口径)。

4.2 为什么不选「服务端 op 兼容垫片」

否决理由(三条,均为长期成本):

  1. 双协议/双信封永久负债:a=api&op= 与 /api/... 两套入口、{ok} 与 {code} 两套信封要长期并存,任何字段改动都要维护两份。
  2. 与插件既有设计冲突:垫片会绕过 scope/限流/审计中间件链,或需要在链前做一次协议翻译,破坏「认证/限流/审计统一在 API 层」的核心架构(mobile_api 方案 §0)。
  3. 不划算:客户端传输层全部收敛在 utils/api.js 约 90 行,重写成本极低;而垫片要在服务端模拟出客户端期望的全部字段(含 time_text 这种展示字段),成本远高于改客户端。

4.3 PoW 方案(需拍板)

方案做法评价
① 客户端维护 CookieApp 端手工读写 Set-Cookie/PHPSESSID,H5 端启用 withCredentials❌ 脆弱;且插件 CORS 未下发 Allow-Credentials,H5 直接被拒
② 插件内无状态 PoW(推荐)插件新增 GET /api/auth/pow 签发 challenge=base64(时间戳.HMAC(时间戳))+difficulty;客户端本地算 nonce;插件本地校验 HMAC+时效+难度,不碰核心 Session✅ 不依赖 Session、不碰核心、天然无状态;与 Bearer 架构一致
③ 首登走图片验证码—❌ 验证码同样依赖 Session,未解决根本问题

推荐 ②。落地要点:challenge 有效期 ≤ 5 分钟、一次性(用 api_rate 或独立小表记录已用 challenge 防重放)、difficulty 复用核心 Settings.pow_difficulty;HMAC 密钥取插件配置项(后台生成/存储)。客户端复刻 assets/js/pow.js 的纯 JS SHA-256 逻辑即可。

4.4 详情内联 replies 的处置(需拍板)

客户端 topic 页期望「详情接口一次返回 replies」。两种做法:

  • A(推荐,改客户端):详情只回正文,客户端用已有的 GET /api/threads/{id}/replies 二次拉取并填进 payload.replies。改动小、职责清晰、利于分页。
  • B(改插件):详情内联首页若干条 replies。少一次请求,但插件详情接口变重、与分页耦合。

4.5 私信会话聚合放哪(需拍板)

  • A(推荐,插件新增端点):插件新增 GET /api/messages/conversations,服务端一次算好 partner/last/unread。客户端只渲染,性能好、离线态简单。
  • B(客户端拼装):客户端拉消息流水自行聚合。请求多、逻辑重、易错,不推荐。

5. 插件侧改造清单(→ v1.1.0)

原则:全部走现有 ApiRouter + 中间件链,复用 Model 层权限/积分/审核,不新开数据库、不直连核心业务表。

5.1 新增端点

方法路径scope限流入参出参要点
GET/api/sitereadread—site_name/user_count/topic_count/reply_count/forum_count/search_min_chars/allow_register/info{...}
GET/api/auth/pow免认证独立—{challenge,difficulty,mode:'normal'}
PUT/api/replies/{id}writewritebody{reply} 或 {id}
DELETE/api/replies/{id}writewrite—{ok}
POST/api/notifications/read-allwritewritebox{unread,unread_total}
GET/api/messages/conversationsreadreadp{rows:[{partner{id,username},last{content,mine,time_text},unread}],unread_total,unread,has_more}
GET/api/users/lookupreadreadusername{partner{id,username}}
POST/api/messages/{user_id}/readwritewrite—{unread}

5.2 字段/语义增强(现有端点)

  • GET /api/categories → 外层新增 rows(= 现有 list);行补 can_post。
  • GET /api/threads 行补:pinned(=is_pinned)、time_text、title_color、title_large、matched_excerpt、forum_name;sort 支持 comment|post。
  • GET /api/threads/{id} 补:author{id,username}、body(裸文,供编辑)、can_edit、time_text;replies 按 §4.4 结论处理。
  • GET /api/threads/{id}/replies 行补:floor、author{id,username}、body(裸文)、body_html、edited、time_text。
  • GET /api/users/{id} 补:group_name、bio、created_at_text。
  • GET /api/auth/me 补:group_name、unread、allow_manage、last_post_at、is_muted、bio、avatar_style、avatar_seed。
  • PUT /api/users/me 扩:bio、avatar_style、avatar_seed;新增改密 password/password2(成功换发 token,回 {token,user})。
  • GET /api/search 参数适配:field(title|body|reply)、sort(post|comment)(内部映射到现有 type)。
  • GET /api/notifications:支持 box;行补 kind、topic_id、sender_id、read(布尔);保留 unread。
  • GET /api/messages/{user_id}:补游标 before_created_at/before_id 与 has_older;peer 兼容为 partner。
  • POST /api/messages:返回补 message:{...}。

5.3 兼容性策略

  • 所有「补字段」为新增键,不删不改现有键 → 对已存在调用方零破坏。
  • categories 的 rows 与 list 并存;peer 与 partner 并存。
  • 插件版本 1.0.0 → 1.1.0;min_core 维持 1.1.21(若无新核心方法依赖则不升)。

5.4 门禁(沿用发布流程手册)

php -l 全量、i18n 三语一致、plugin_case_lint、零内联、BOM、发布包结构(无 data/、无根级文件、activated=false)、签名与清单。


6. 客户端侧改造清单

6.1 传输层(核心改动)

  • src/utils/api.js(重写,约 90 行)
  • base 改为站点根 URL;call(path, {method, params, body, auth}) 拼 REST 路径。
  • 方法支持 GET/POST/PUT/DELETE。
  • decode:判 payload.code === 0 → 成功回 payload.data;否则构造带 code/message/status 的错误;错误码映射为客户端既有字符串(40101→unauthorized、42901→rate_limited、network)。
  • 401 处理:先尝试 POST /api/auth/refresh(用 refresh_token)换新 access → 重放原请求一次;失败则 clearSession()。
  • 保持 call 返回统一信封、data 抛错(含 code/status)的既有契约,调用点零改动。
  • src/utils/config.js:apiBase() 语义改为「站点根 URL」;删除 appKey() 与 X-App-Key;connectionSummary() 改显示站点根 + 登录态。
  • src/utils/store.js:会话增加 refresh_token;expires_at 统一为秒级(对齐插件 expires_in);新增 setTokens()。

6.2 逐页适配

  • utils/site.js:data('site') → GET /api/site;limit() 读新端点字段(search_min_chars 等)。
  • utils/format.js:topicUrl() 由 siteOrigin()+'/index.php?a=topic&id=' 改为站内真实链接(我们的路由,如 /thread/{id} 或 /index.php?route=...,按实际前台路由确定)。
  • pages/home、forum:data('topics',{fid,sort,p}) → GET /api/threads?category_id=&sort=&page=;行字段已由插件补齐 → 渲染侧基本不动。
  • pages/topic:详情/回复/编辑/删除 → REST;replies 处理按 §4.4。
  • pages/post:forums → /api/categories(rows[].can_post);topic_create → POST /api/threads。
  • pages/login:接入 PoW(按 §4.3 方案);register 走 /api/auth/register;错误码适配。
  • pages/mine:me → /api/auth/me(字段补齐);退出补调 POST /api/auth/logout 后 clearSession()。
  • pages/settings:profile_update → PUT /api/users/me;改密换 token 后更新会话。
  • pages/search:field/sort 语义对齐。
  • pages/notice、pages/dm-thread:私信会话聚合、dm_read、dm_user、游标分页、partner/message 命名对齐。
  • **components/***:app-avatar 保持纯本地首字母头像(无需改);topic-item 字段来自插件已补齐项,基本不动。

6.3 配置与品牌

  • src/utils/site.config.js:填本站 SITE_API_BASE(站点根 URL)。
  • 品牌替换 4 处:package.json 描述、index.html <title>、src/manifest.json 的 name/description、SITE_NAME_FALLBACK(utils/site.config.js)。
  • 启动图:启动图/ 三张(720x1280、1080x1920、1080x2340)替换为本站。
  • manifest.json:appid 云打包前必须重取;versionName/versionCode 按发版递增。

7. 安全

7.1 PoW 无状态化(同 §4.3)

挑战自签 + HMAC 密钥由插件后台管理;有效期 ≤ 5 分钟;防重放;难度复用 pow_difficulty。

7.2 CORS

  • 默认同源;后台白名单;dev 环境需加 http://localhost:5173(Vite 默认端口,见 vite.config.js)。
  • 不使用 *;若未来 H5 需带凭据,才考虑 Allow-Credentials(当前 REST+Bearer 无需 Cookie,故不下发)。

7.3 部署路由(必做)

  • 给 nginx vhost 的 location / 增加:try_files $uri $uri/ /index.php?$query_string;
  • 或明确改用 Apache(.htaccess 已具备 RewriteRule ^(.*)$ index.php/$1 [QSA,L])。
  • 注:config.php 写死 BASE_PATH='' 使「settings 覆盖部署路径」失效(app/init.php 有 !defined 守卫);因此不能靠 /index.php/api/... 绕过,必须修 nginx。

7.4 Token 与存储

  • access/refresh 仅存客户端本地存储(bbs_session),明文只在生成时出现一次(服务端只存 sha256)。
  • 客户端 401 自动 refresh 后必须用新 refresh 覆盖旧的(插件为轮换制,复用旧 refresh 会整族撤销)。

7.5 源码目录泄露风险(必须处理)

  • flinthub-client/ 当前位于站点根内(d:\phpstudy_pro\WWW\flinthub-client)。其 node_modules/dist/源码可被 HTTP 直接访问(配置泄露、源码泄露)。
  • 处置建议(三选一):① 移到站点根之外;② 保留在根内但加 nginx location ^~ /flinthub-client/ { deny all; };③ 至少 deny 源码与 node_modules、仅允许 dist(不推荐,易漏)。
  • 另:原客户端 部署教程.md 提及站点需 HTTPS、APK 不放开明文流量——沿用(usesCleartextTraffic 不开启)。

8. 里程碑与验收

分步推进、每步验证(沿用既有「分步修复、每批测试」的工作流)。

  • M1 环境打通:修 nginx try_files;GET /api/site 用 curl 返回 {code:0,...}。验收:可直达 PHP。
  • M2 认证闭环:插件无状态 PoW + 客户端登录/注册/refresh;H5 dev 联调。验收:登录成功、token 过期自动续期。
  • M3 读路径:site/categories/threads/thread/user/search 全通;首页/版块/帖子/搜索/用户页可浏览。验收:H5 端五页无空白、无 404。
  • M4 写路径:发帖/回帖/编辑/删除/改资料/改密。验收:操作成功且积分/通知/审计正常触发。
  • M5 通知与私信:通知全读、私信会话聚合/已读/发送/游标翻页。验收:红点同步、会话列表正确。
  • M6 品牌与打包:品牌 4 处 + 启动图 + manifest;H5 构建 + HBuilderX 云打包 APK。验收:真机安装可用。
  • M7 发布:插件 v1.1.0 门禁 + 签名发布;核心(若有纯新增)按需打包。

真机验收项:冷启动到首页、登录/注册、发帖/回帖、楼中楼编辑删除、搜索、通知红点、私信收发与翻页、改资料/改密后免重登、弱网/断网空态样式、左滑返回。


9. 风险与缓解

风险影响缓解
PoW 方案选定后实现偏差登录不能过M2 先做,H5 dev 端到端验证后再推后续
@dcloudio/uni-app-plus 版本不一致静默按 H5 编译(假成功)锁定与其余 @dcloudio/* 同版本号;构建后校验产物含 app-service.js
dist/ 未清即构建打入死页面构建前必删 dist/
nginx 漏修/api/* 全 404M1 首项做并 curl 验证
源码目录在站点根内源码/配置泄露§7.5 三选一处置
refresh 轮换未覆盖旧值会话整族被撤销、强制重登§7.4 客户端 refresh 逻辑必须覆盖
字段补齐破坏既有调用方回归仅新增键、不改现有键(§5.3)


10. 待拍板问题(4 项)

  1. PoW 方案:是否采用 §4.3 方案 ②「插件内无状态 PoW」?(推荐)
  2. nginx 路由修复归属:由谁改 vhost 加 try_files?还是改用 Apache?(推荐改 nginx)
  3. 客户端多语言:本期是否引入 i18n?(客户端现全硬编码中文;推荐本期不做,后续按需)
  4. 详情 replies 与私信聚合的实现位置:详情内联 replies 采用 §4.4 A(改客户端)还是 B(改插件)?私信会话聚合采用 §4.5 A(插件新增端点)还是 B(客户端拼装)?(均推荐 A)

附:改造文件清单(速查)

客户端:src/utils/api.js(重写)、src/utils/config.js、src/utils/store.js、src/utils/site.js、src/utils/format.js、各 pages//index.vue(按 §6.2)、src/utils/site.config.js、package.json、index.html、src/manifest.json、启动图/。

插件(mobile_api → v1.1.0):Plugin.php、MobileApi.php(注册新路由)、Controllers/(新增/增强)、ApiResponse.php(如需新错误码)、lang/、plugin.json(版本)。

部署:nginx vhosts/*.conf(加 try_files)。

轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| 瀏覽 3 次 | 回覆 1 次

全部回覆 (1)

🌳Lv.4 中级 🌛 见习会员
2026-10-26 01:55:03
「传输层信封不兼容是主要工作量,其余是按我们的语义补齐缺口」——先把问题定性成「信封」而不是「接口全换」,这个判断让工作量从「重写」降到「重写约 90 行 utils」,评估价值很高。

三点:

1. 客户端是 uni-app Vue3,改造只重写传输层意味着 UI 层几乎不动,风险可控。但要确认「分页/游标」语义差异:bbs1 很可能用 page/limit,而本站 list_threads 是 cursor,映射错了会出现「翻页丢数据」或「重复项」。
2. 认证方式要对齐:客户端拿的是 MCP Token 还是登录态 Cookie?如果是 Token,必须复用现有的滚动额度与幂等键,不要另开一套。
3. 「先不动手做」这个约束守住了,方案里把 §10 待拍板问题列成清单,比直接开工再返工划算。

差异矩阵那节是关键,建议每条差异都标注「必须改 / 可以兼容层适配 / 不改」,否则读者无法判断工作量分布。
#1 樓

請 登入