Commit Graph
10 Commits
Author SHA1 Message Date
laoyang 8b3e49e091 邮件:带私人链接时统一加「这个链接只有您能打开,请不要转发」提醒
用户要求:每次发这种链接,都要在链接上/下给他一个提示 ——
这个链接是独有的,不要转给别人、不要让别人看到,是跟踪反馈的唯一凭证。

实现:在 fb_mail_layout() 里加 $privNoteHtml,
layout 传 personal=true 才渲染,位置在「按钮之后、纯文本链接之前」。
这样只要新增邮件是发给提交人的,标一下就自动带上,不会漏。

覆盖 7 处(全部发给提交人的):
  回复通知 / 状态变更 / 审核通过 / 转为不公开 / 关闭说明 / 提交回执 / 兜底
不覆盖 1 处(管理员通知,按钮指向后台,不是私人凭证)

提醒文案(斟酌过,讲清缘由而不只是禁止):
  标题:这个链接只有您能打开,请不要转发
  正文:它等同于您在这条反馈上的身份凭证 —— 谁拿到这个链接,
        谁就能查看和处理这条反馈。请只自己使用,不要发给别人,
        也不要发到公开的地方。如果不需要继续跟踪,删掉这封邮件即可。
  → 说清「是什么」而不是只喊「别外传」,用户才会真的照做;
    补一句退路(删掉邮件即可),避免像是免责声明。

验证:7 类邮件全部含提醒且都在页脚之前;管理员通知不含;
像素级检查确认视觉顺序为「蓝色按钮 y=719 → 黄色提醒块 y=874..982」,
即提醒在按钮下方、正文区内,不是埋在邮件底部。
2026-11-03 10:08:49 +08:00
laoyang 8bcee46b8a 首页改介绍页;列表只服务当前软件;新增UUID追踪让提交人看得到未审核反馈
用户提的两个核心问题:

一、首页不该列全部问题
  场景:这套系统是给某个软件配反馈入口、把地址贴进那软件的「意见反馈」里。
  访客总是从某个软件过来的,看别的软件的问题对他没意义。
  ⇒ / 不带 sw 时渲染介绍页(特点介绍 + 「点右上角设置按钮选软件」+ 软件卡片)
     带 sw 才是该软件的问题列表,检索/筛选/分类全在本软件范围内

二、提交后未审核,用户点不进去(真实缺陷)
  邮件里给了「点击查看您刚才提交的反馈」,但 thread.php 只在 visible=1 时
  才给看,未审核的一律 404 / 只看到「等待审核」静态页 —— 链接等于白给。
  ⇒ threads 加 track_token(32位hex = 128位熵),提交时生成:
     - 邮件里所有链接一律带 ?t=<token>(不再是裸 id)
     - 带对 token:可看完整详情、可回复,哪怕未公开/已关闭
     - 没 token 或 token 错:对外一律 404,不泄露存在性
     - 公开列表仍用 id 链接(公开内容谁都能看,用于避免重复提交)

实现细节:
- threads.track_token + 唯一索引;幂等迁移,存量数据自动回填
- fb_track_new_token / fb_track_token_ok(hash_equals 防时序探测)
  / fb_thread_viewable / fb_thread_link(thread, personal)
- thread.php 权限从 fb_thread_public 改为 fb_thread_viewable
- 带 token 看自己未公开的反馈时,顶部给「私人跟踪链接」提示条
- 回复表单与回复后跳转都保留 token,否则回复完就失去访问权
- 提交完成后直接带 token 跳详情页,不再只给静态等待页
- close 类邮件补上「查看这条反馈」按钮(原来完全没链接)
- 顶栏「全部问题」改为「问题列表」且只在选中软件后显示
- 详情/提交页「返回问题列表」按所属软件返回,不再回介绍页
- new.php 无 sw 时不再默认落到第一个软件,改为让用户先选
  (默默替选会让反馈进错软件、通知发错人)

验证:首页 0 条问题卡片且未泄露任何标题、三个软件入口齐全;
未公开#4 无 token 404 / 错 token 404 / 正确 token 200 且显示完整详情
(不再卡在「等待审核」)、提示条与表单 token 都在;
已公开#2 无 token 可看;5 类邮件(回执/通过/回复/状态/关闭)链接全带 token;
两套回归各 30+ 项断言全过。
2026-11-03 09:54:12 +08:00
laoyang 314a1cf2a5 新增「未公开」状态;待审核改列表式,只留通过并公开
用户要求:
1. 待审核/全部问题都用列表形式
2. 待审核去掉「关闭说明」整块,后面只加一个「通过并公开」按钮
3. 标题本身就是进管理页的链接,去掉「进入管理」
4. 「未公开」应当是一种状态:提交后初始状态不是「待处理」而是「未公开」

状态流转(用户定义):
  提交 → 未公开
    ├─ 通过并公开 → 待处理 → 处理中 → 已解决 → 已关闭
    └─ 直接关闭   → 已关闭(不经过待处理)
  每次状态变更、每次有人回复,都给提问人发信

实现:
- statuses 新增 hidden「未公开」,sort=0 排最前,其余顺延
  幂等迁移:老库检测到没有 hidden 就整体后移一位再插入,
  并把 visible=0 且 status=open 的存量数据归到 hidden
- new.php 初始状态按可见性决定:未公开→hidden,公开→open
  (以前一律写 open,后台「待审核」里显示的全是「待处理」,状态反映不出事实)
- approve 动作:visible=1 且 hidden→open,并用更新后的行渲染邮件
- 详情页「通过并公开」按钮原来传原状态(未公开时通过后仍是未公开),改为固定传 open
- 待审核查询改为 status=hidden(兼容老数据 visible=0 且 open),
  不再用 visible=0 AND status<>closed —— 那个条件会把「已关闭但未公开」也收进来
- 新增 fb_admin_pending_row() 列表行渲染:标题即链接、右侧单个按钮
- 删除 fb_admin_thread_card() 及「关闭说明」表单
- 新增 .plist/.prow 列表样式与 .st-hidden 状态徽标(中性灰)
- mailer 补 hidden 状态的文案,避免落到兜底

验证(线上真实数据):6 个状态齐全、提交落 hidden、
待审核能查到且不含已公开、通过后转 open 并移出待审核、
后续 doing/resolved/closed 推进正常、6 个状态邮件均能渲染、
待审核行结构 10 项断言全过(无关闭说明/无 note/无进入管理/标题可点)。
测试数据已清理,用户原有 4 条数据完好。
2026-11-03 09:09:40 +08:00
laoyang db8c1969af 文案:全站「你」改为「您」
用户要求邮件模板统一用敬语,确认后网页也一并改。

范围:
- lib/mailer.php  61 处(全部邮件模板:回执/回复/状态/审核/关闭/不公开/新反馈)
- new.php          6 处(提交页提示、占位文字)
- thread.php       4 处(提交成功提示、回复框占位)
- lib/layout.php   1 处(页脚隐私说明)
- admin.php        5 处(测试邮件正文、关闭邮件主题模板、通知规则说明、
                          先审后显说明)

已核查无「您们」等机械替换产生的别扭表达;
邮件模板渲染后「你」零残留、「您」77 处,文案通顺。
线上四个页面可见文字均无「你」,全站路由 200。
2026-11-03 09:00:05 +08:00
laoyang 669ab9e43e 修复图标显示不出来;标题改XX反馈系统;图标去掉背景色
一、图标显示不出来(根因)
用户上传图标时用的是修复前的代码,数据库里 icon_key 存的是
icons/2026/11/xxx.png —— 没有 local/ 也没有 oss.prefix 前缀。
上一轮我把键校验收紧成「必须带存储前缀」,于是这些键被判非法,
fb_icon_url() 返回空字符串,图标自然显示不出来。
而三个文件其实都真实存在于 OSS 上(13KB / 391KB / 379KB)。

修法两层:
1. fb_icon_key_ok / fb_icon_is_key 兼容历史无前缀形态,
   这些是有效资产,不能因为键格式不规范就让图标消失
2. 数据迁移:把对象复制到 feedlite/icons/… 并把数据库键改成规范形态,
   已对三个软件全部执行并验证(新键 200,大小与源文件一致)

二、标题改为「XX 反馈系统」
- 页头大标题 = 同步日历反馈系统(之前只有「同步日历」)
- 浏览器标签同步改
- 软件名本身含「反馈」时不叠加,避免「轻反馈反馈系统」

三、图标不要背景色
- 去掉色块底衬与圆角投影,直接「图标 + 文字」并排
- 前端 badge、提交页上下文条、邮件头部三处同步
- md 尺寸调到 28px,与 26px 标题文字视觉匹配

验证:三个软件页标题正确、图标全部 200 image/png、
全部页面 200、迁移后新键可读且大小一致。
2026-11-03 08:27:03 +08:00
laoyang 835c8c05da 软件图标支持上传图片:存 OSS/本机,media.php 代理读取
用户要求图标可以自己上传一张真实图片,而不是只能填一个字符。

设计:字符和图片并存,图片优先
- software 加 icon_key(图片对象键),icon(字符)保留作兜底
- 有图片用图片,删掉图片自动退回字符,任何时刻都不会没有图标
- 前端 badge 与邮件头部同一套判断,页面和邮件永远一致

存储与读取
- 图标走独立键空间 icons/YYYY/MM/<16hex>.<ext>
  不复用附件那套:附件必须挂在 thread 下才给读,图标不属于任何帖子
- 写:仅管理员(后台动作链)+ finfo 真实类型校验 + 尺寸下限 + 2MB 上限
- 读:media.php 对 icons/ 只校验键形状与文件存在(图标本就公开),
  写入侧仍然严格,不会让本站变成能读整个桶的开放代理
- 优先用 media.php?k= 代理;OSS 下顺便借图片处理缩放,省下行

顺手修掉两个真实安全问题
1. fb_icon_key_ok 原来用 [a-z0-9]{2,5} 匹配扩展名,.php 正好能过
   (3 个字母)—— 测试反向验证时发现,已改为图片扩展名白名单
2. fb_upload_key_ok 有同样的问题,附件键也能构造出 .php
   虽然真上传还有 finfo 兜底,但键本身就不该长这样,一并收紧
3. fb_icon_delete 原来用 @ 吞掉错误,删除失败仍返回「已保存」,
   用户会以为旧图清了、实际留了个可访问的孤儿图。改为返回 bool 并如实提示

验证:键校验 7 类非法输入全拒;图标写入 OSS 成功、media.php 读回 200 image/png、
PNG 魔数正确、缩放生效;越权读取(穿越/.php/未入库附件/不存在)全部按预期拒绝;
表单 enctype/文件框/accept/预览渲染齐全;仿真 PHP 伪图与 SVG 均被拒;
16x16 小图被拒。
2026-11-03 08:13:21 +08:00
laoyang 738c6c09ff 多软件区分:邮件按软件换色+图标+软件名;补提交回执邮件
用户要求:多软件并存时,用「颜色 + 图标 + 名字」三重区分,
邮件也要跟着所属软件变,一眼能看出是哪款软件的来信。

1. software 表加 icon / download_url 字段(幂等迁移)
   - icon 存单个字符(emoji / 汉字 / 字母),不存图片路径:
     前端与邮件用同一个字符渲染,天然一致,不用再管一套上传与权限
   - 没配 icon 时取名首字兜底,任何地方都不出现空图标
   - 后台软件编辑加图标快选(12 个常用 emoji)

2. 邮件配色改为跟随软件(fb_mail_brand 取 fb_software_colors)
   - 头部色条、按钮、引用块色条、徽标全部跟着软件走
   - 邮件头部改为「图标色块 + 软件名」,下面再小字标站点名
   - 主题行统一加【软件名】前缀,不点开就能分辨

3. 新增 receipt 事件:提交后立即给提问人发「已收到、待审核、请耐心等待」
   - 独立开关 notify.receipt_enabled(与「回复通知」是两件事)
   - 新增 7 处调用前的静默 return 也一并写日志

4. reply 邮件补上下文:原提问摘要 + 本次回复,像博客评论通知
5. status=resolved 文案改为「已完成并上线」,配了下载地址时给下载按钮

验证:多软件配色/图标/前缀全部通过(含反向验证),XSS 转义通过,
空图标兜底通过;页面顶栏、页头、选择器图标渲染正确。
2026-11-03 08:00:50 +08:00
laoyang e43f2ce67a 修复:邮件通知两处静默失效(测试暴露)
症状:notify_log 全表为空 → 用户报「完全没发邮件」,后台查不到任何线索。

1. 前 4 个前置判断(开关/收件人/自己回自己/冷却)直接 return 不写日志,
   等于「为什么没发」完全无痕。改为任何退出路径都写 notify_log,
   并带上可读原因。

2. new 事件(新反馈通知管理员)收件人只取 admin_email,该值为空时整条
   通知静默失效 —— 管理员永远收不到新反馈信,界面也无任何提示。
   改为回落 smtp.user / smtp.from;后台勾了「通知管理员」却没填邮箱时
   当场显示警告。

验证:走 fb_notify_event 全事件测试,6 个事件全部写日志、new 能送达。
2026-11-03 07:31:35 +08:00
laoyang 4033170ad7 修复:跨 tab 保存互相清空配置;补齐全流程邮件通知与统一模板 (#2)
## 一、用户报「我明明配过 SMTP,怎么全丢了、信也发不出」

**没有重置数据库**(installed_at 仍是 11-01 15:14,oss.*/site_name/admin.username 全在)。

真因是我上一轮「邮件拆独立 tab」挖的坑:站点设置与邮件通知**共用 act=settings**,
而保存函数把 smtp 字段无条件写库:

    foreach ($smtpText as $k) {
        fb_set_setting($k, trim((string)$post($k, \x27\x27)));   // 没提交 → 空串 → 覆盖
    }

在站点设置页保存附件设置时,该表单没有 smtp_* 字段,$post() 取到空串,
于是 host/user/from 被清成空。smtp.pass 因为单独做了「留空=不修改」而幸存,
所以现场是 pass 有值、其余全空 —— 这个组合本身就是 bug 的指纹。

修法:新增 $has() 判断「本次请求是否真的提交了该字段」,没提交就跳过。
按 tab 分两组保护:
- $isSiteForm:站点信息/页脚/附件/OSS,逐字段 has() 双保险
- $isMailForm:SMTP/通知开关,整段 + 逐字段

checkbox 尤其要保护:未提交会被当成「取消勾选」,
在站点设置页存一次就会把「有人回复时通知」全关掉。

## 二、notify.admin_new_thread 是假的(用户说「提交了反馈收不到邮件」)

这个设置项:白名单里有、后台有勾选框,**但没有任何代码实现它**。
grep 只命中三处(白名单/保存/界面),没有一处真正发信。

已在 new.php 提交成功后实现:查新帖 → 取所属软件名 → fb_notify_event(\x27new\x27)。

## 三、补齐全部事件,统一入口

此前只有 reply 和 close 两个事件,且两套模板风格不一致。
新增 fb_notify_event() 统一入口,处理「开关→收件人→节流→发信→日志」。

覆盖 8 类事件:reply / status / approve / hide / close / new。
- status 按状态给不同文案(待处理/处理中/已解决/关闭/不予考虑),
  「不予考虑」语气克制、必须带理由并留申诉入口
- approve / hide 原先完全不发信,现在补上
- 状态没变化时不发信(不打扰用户)

## 四、邮件模板重做

抽出 fb_mail_layout() 统一外壳,所有事件共用:
- table + 内联样式(Outlook/QQ邮箱/Gmail 兼容)
- 600px 卡片、顶部主色条、圆角徽标、字段表、引用块、callout、CTA 按钮
- 响应式(已验 375px 无溢出、无裁切)
- preheader 摘要、纯文本兜底链接、收件人提示

## 五、后台状态按钮(用户要求)

问题详情页顶部一排按钮,点一下立刻生效并自动发信:
「通过并公开」「处理中」「已解决」「不予考虑」「撤下」
- 当前状态对应的按钮置灰
- 「不予考虑」展开内联面板填理由(不用 window.prompt)
- 新增 setstatus 动作;「通过并公开」会同时置可见

## 六、notify_log 加 reason 列

原表只有 reply_id,无法区分事件类型。加 reason 并幂等迁移;
老库 reply_id 是 NOT NULL,非回复事件用 0 表示。

## 验证

邮件专项 73/73:
- 8 类事件模板全部完整 HTML、table 布局、徽标、页脚、无 script
- 8 个主题互不相同;状态徽标文案正确
- 总开关关闭时 admin_new_thread 仍可独立生效
- 节流只作用于 reply/status,close 不受影响
- SMTP 未启用时留失败日志(含 reason 与原因)
- C1-C5:站点页保存后 smtp.host/user/notify.enabled 均未被清空;
  邮件页保存后 oss.bucket 未被清空

反向验证:完整回退两层保护后 C1/C2/C3 全部 FAIL
(host 从 smtp.qiye.aliyun.com 变成空、notify.enabled 从 1 变 0),
证明测试能真正抓到该 bug,不是假通过。

回归:全新安装 33/33、老基线 25/25、附件 22/22。
2026-11-02 22:57:00 +08:00
laoyang cede838b5e FeedLite 初始提交:通用轻量级反馈系统(PHP + SQLite,零依赖)
- 前台:问题列表 / 详情 / 发布与回复(趣味问答验证码、蜜罐、IP 限流)
- 后台:先审后显、站点设置(站点标题/描述、附件存储、OSS 参数)、操作日志
- 附件:关闭 / 本机硬盘 / 阿里云 OSS 浏览器直传(ECS 实例 RAM 角色)三种模式
- 品牌资源:全套图标(favicon.ico / SVG / PNG 各尺寸 / manifest)
- 安全:CSRF、session 加固、.htaccess 屏蔽敏感文件、口令仅存哈希
- 配置:config.example.php 为模板,config.php 不入库
2026-09-28 20:34:37 +08:00