laoyang
|
9b971a5b92
|
下拉框改为自绘列表;修复新建软件缺排序字段
用户两个反馈:
① 「排序那个怎么删了?排序还得留着呀」
② 「所有下拉框拉下来之后全是灰色,不好看,重新设计」
一、排序字段(是误解,但确实有缺陷)
排序字段没有被删,一直在 if ($esw) 分支里渲染。
用户看到"没有",是因为他打开的是「添加软件」——
新建时 $esw 为空,整块不渲染,所以确实没有这一项。
这是真实缺陷:新建软件时无法指定排序。
修法:
- 把排序字段移出 if ($esw),新建/编辑都显示
- 新建时默认值 = 当前最大 sort_order + 1(排到最后)
- 后端 sw_add 改为优先用表单值(用 isset 判断,
用户明确填 0 是合法值——排最前面,不能被当成"没填")
与「下载地址」不同:排序是真实会被用到的字段
(前台软件列表、顶栏三横线菜单都按它排),不能删。
二、下拉框重设计
为什么必须自绘:原生 select 的展开列表由浏览器绘制,
CSS 只能改 option 的底色,改不了圆角/行高/hover 高亮,
各平台样式还不一样(Windows 是灰方框)。用户说的"全是灰色"就是这个。
但不重写 HTML:全站 16 个 select,逐个改容易漏,
也会破坏 onchange 提交这类内联逻辑。
做法:原生 select 保留(视觉隐藏,仍参与提交与 change 事件),
JS 在旁生成按钮 + 浮层列表。
- 浮层用 position:fixed 挂 body,避免被 overflow:hidden 裁(踩过)
- 位置自动避让视口,下方不够翻上方
- 支持键盘:上下键 Home End Enter Esc
- 选中项带对勾
- 无 JS 时原生 select 仍可用
配色踩坑:选中项最初用 var(--brand-soft),而该变量会被软件主题色覆盖 ——
同步日历主题色 #ff3f44 时 --brand-soft 成了 #ffe8e9(粉红),
下拉里一片粉很难看。下拉是工具性控件,不该跟软件换色,
改用中性色:#eef1f5 选中 / #f1f3f6 hover,四个软件下完全一致。
验证(真实页面实测):
- 2 个 select 均被增强,原生已隐藏
- 面板白底 rgb(255,255,255)、圆角 11px、双层柔和阴影、7 个列表项
- 选中项 rgb(238,241,245)、hover rgb(241,243,246)、带对勾
- 四个软件(#0057b8/#ff3f44/#a556f7/#3571fb)下选中项底色完全相同,
不再随主题色变粉
- 键盘下方向键三次 → 高亮「处理中」,底色正确
- 选择后 selectedIndex/value 同步、面板自动关闭、change 事件已触发
- 浮层 fixed 定位、完整可见、不被裁切
- 排序字段 3 处(软件/分类/状态)齐全;5 个路由 200
|
2026-11-04 19:27:01 +08:00 |
|
laoyang
|
27614d8ca8
|
统一全部表单控件尺寸与外观;删除下载地址字段
用户:「输入框、按钮、下拉框特别丑,一点设计感都没有,高度太随意,
全面检查一下。还有那个下载链接要它干什么?」
一、表单控件(这是主要工作量)
原来样式散在 6 处(.field / .filter-row / .filters / .mail-test / .admin 各一套),
padding 有 8/9/12px 三种,select 与 input 高度不等,并排放参差不齐。
改成一套体系:
- CSS 变量统一尺度:--ctl-h 38px / --ctl-h-sm 32px / --ctl-h-lg 42px,
统一圆角 9px、统一左右内边距 12px、统一边框与阴影
- 高度用「固定 height + line-height 居中」而不是 padding 撑
- 🔴 选择器必须**宽松**(按 type 通吃,不用 .field 之类限定)。
第一版限制了容器,导致后台零散布局里的裸 select/input 只有 19/21px,
和旁边 38px 按钮并排,正是用户说的高度随意。
放开后实测:19px → 38px,全部一致
- 下拉框自绘箭头(内联 SVG),不再用各平台不一的原生箭头,右侧留 34px
- 搜索框加左侧放大镜图标,并去掉 Chrome 自带清除按钮
- 聚焦统一 brand 光晕,悬停边框加深
- 按钮也改固定 height(原来 padding 撑出 38.3px,与输入框差 0.3px)
- .btn.sm 必须显式改 height,否则 padding 被 height 吃掉、小按钮还是 38px
文件选择按钮:原生控件重做成真按钮
<label class=file-btn> 包住 input[type=file]
input 用 clip 视觉隐藏(不能用 display:none,那样点不到也聚焦不了)
按钮内显示「选择图片」+ 图标 + 选中后的文件名
二、删除「下载地址」字段
它唯一用途:状态改为「已解决」时把邮件按钮换成「下载最新版本」。
收益小、维护成本高(每个软件都要填一条 URL)。
- 后台表单项删除
- mailer 里对应的判断分支也删掉,按钮统一「查看详情」
- ⚠️ 数据库 download_url 列**保留不删**:删列不可逆,万一要恢复还得迁一次
验证(用控件总览测试页 + 真实页面实测):
10 类控件高度全部 38px(文本/数字/下拉/搜索/文件按钮/主按钮/小按钮…),
文本域 110px,同一行垂直中心完全一致(935.3)
裸 select/input 从 19/21px 修正到 38px
下拉箭头 SVG 生效、右内边距 34px;搜索框放大镜生效、左内边距 36px
真实三个页面(提交页/列表页/详情页)逐一检查:
没有 checkbox/radio/file 被误改,所有可见控件高度收敛为 38(文本域 110)
下载地址字段:admin.php 输出与源码中均无 download_url 表单项
5 个路由 200
|
2026-11-04 19:17:20 +08:00 |
|
laoyang
|
a2a3306169
|
修色板被裁切 + 让人看得出能选全色域
用户两个反馈:
① 色板直接被挡住了一半
② 它不是完全的颜色,好像只有蓝色
① 根因:色板用 position:absolute,而它在
<details class="tax-form-box"> 里面,那个容器有 overflow:hidden。
色相条正好在面板最底部 —— 被裁掉了,所以看不见。
改 position:fixed,直接相对视口定位,任何祖先 overflow 都管不到。
位置改由 JS 按触发按钮坐标算,并处理三种情况:
右边超出→左移;下方不够→翻上方;都放不下→贴视口底边
另加 resize/scroll 监听,避免滚动后色板漂走。
② 不是 bug,是 ① 的连带后果:
HSV 色板本来就只显示当前色相的深浅变化,换颜色靠色相条。
色相条被裁掉后,用户只看到一片蓝,自然以为只有蓝色。
修好裁切后已实测 6 个色相各不相同:
0°红 60°黄 120°绿 180°青 240°蓝 300°品红
同时把色相条从 14px 加高到 18px 并加内描边,
让它一眼就能认出是光谱条,而不是一条灰线。
验证(用真实后台容器结构复刻测试页,details 带 overflow:hidden):
面板四边 elementFromPoint 都能命中自己 → 未被任何容器裁切
色相条可见、高 18px;面板总高 367px
滚到页面底部再开面板仍完整可见
6 个色相取样互不相同,色板能覆盖全色域
实际取色 #0ce779 正常
4 个路由 200
|
2026-11-04 19:01:04 +08:00 |
|
laoyang
|
338a35cacd
|
主题色改为完整色板:画布 342x200,加明度条,删除右侧预设圆点
用户:「还是不对呀,需要一个完整的色板,右侧那些默认颜色就不要了。」
两个改动:
1. 删掉右侧那排 12 个预设圆点($presets 变量、CSS、JS 同步逻辑一并删)。
色板本身能取任意颜色,再摆一排样例是重复,还挤占横向空间。
2. 色板做成「完整」的:
面板 248×246 → 372×357
画布 226×150 → 342×200(太小拖着不好定位颜色)
新增明度条,底色随当前色相实时刷新 ——
否则永远是灰阶,用户看不出拖到那里会得到什么颜色
面板加标题「选择颜色」,按钮文案「确定」→「完成」
顺带简化光标定位:
原来 cursor 挂在 .cp 里,要靠两个 getBoundingClientRect 相减算偏移;
现在改挂到 .cp-sv-wrap(与 canvas 同级),坐标直接相对 canvas 算,
少一层偏移、少一个坑。
验证(浏览器实测):
.color-preset 残留 0 个、.color-presets 容器 0 个
面板 372×357、画布 342×200,完整在视口内
色相 200° → 明度条背景 linear-gradient(#000, rgb(0,170,255)),确实跟着变
画布 80%/20% 处取色 → #2996cc,光标落在画布范围内
明度拖到 50% → #1a5e80(同色相压暗,符合预期)
手输 #2E7D5B → 隐藏字段 #2e7d5b,点完成关闭
4 个路由 200
|
2026-11-04 18:57:18 +08:00 |
|
laoyang
|
ad1b0eedf4
|
主题色改为自绘取色面板(任意取色);图标上传加即时预览与状态提示
一、颜色不够丰富 → 点击弹取色面板
用户:「还是不行,颜色还是不够丰富。让用户选择吧:点击之后弹出一个颜色选择器。」
原来只有一个原生 <input type=color> 小方块,用户不知道能点,
而且系统面板各浏览器样式不一、只能取纯色。
改成自绘面板:
色相/饱和度渐变板(Canvas)+ 明度滑块 + 十六进制输入 + 确定
12 个预设圆点降级为快捷方式,真正的能力是任意取色
交互:点色块弹出(下方空间不够自动翻上方)、板内拖动实时更新、
十六进制可手输、点确定或面板外关闭、Esc 关闭
⚠️ 光标定位踩了两个坑,都写进注释了:
① 不能用 canvas.offsetLeft(相对 offsetParent,受父级 padding 影响,
实测算出 236px 超出画布宽度 226px);要用两个 getBoundingClientRect 相减
② canvas 显示尺寸受 CSS width:100% 影响,≠ 内部像素 220×150,
必须用 rect.width 而不是 canvas.width
二、图标上传没反馈
用户:「上传的图标没有显示,也没有上传成功、失败之类的提示信息。」
后端逻辑完整(格式/大小/尺寸/存储模式都校验了),问题在**前端零反馈**:
裸 <input type=file>,选完文件什么都不发生,而提示条在页面顶部容易被错过。
加了 bindIconUpload():
① 选中文件立刻 FileReader 本地预览,不等提交
② 显示文件名 + 尺寸 + 大小,让用户确认选对了
③ 选之前就拦掉明显不合格的(格式/超 2MB/小于 32×32/空文件)
④ 非正方形给出警告(显示时会被裁切)
⚠️ 前端校验只是体验优化,绝不能当安全边界 ——
真正的校验仍在 PHP 的 fb_icon_store_upload() 里。
验证(用独立测试页 + 浏览器实测,不登录后台、不动数据):
面板初始隐藏 → 点击后 248×246 出现
Canvas 四角像素分别为灰白/蓝/黑 → 渐变确实画出来了
板上取色 → 隐藏字段 #2178d9 同步到按钮文字/十六进制框/预览色
点预设 → #2B822E;手输 → #a020f0;色相滑条 120° → #20f020
光标中心 (238,54) 落在画布区域 (11..237, 11..161) 内
4 个路由 200
|
2026-11-04 18:52:45 +08:00 |
|
laoyang
|
26d00ea981
|
后台顶栏去掉「我要反馈」「三横线选软件」「问题列表」
用户问:「管理后台还需要我要反馈这个按钮吗?还需要右上角那三个横线的设置按钮吗?
还需要拉下来后选择不同的软件吗?这些是不是都不需要了呀?」
确认都不需要,而且原来确实都在:
后台调 fb_layout_head() 时没传 software,走「无软件」分支,
结果顶栏照样渲染了问题列表 / 我要反馈 / 三横线,三横线里还列着
「轻反馈、同步日历、密码生成器、反馈测试」四个软件 —— 在后台点它毫无意义。
理由:
① 「我要反馈」是给访客的入口,管理员不该从后台跳去前台表单;
② 后台是跨软件视图(待审核/全部问题本身就带软件筛选器),
再放切软件菜单是重复的,切了也不会改变后台视图;
③ 「问题列表」依赖 ?sw= 当前软件上下文,后台没有这个上下文,
点了只会跳到前台某个软件,属于误导。
改法:fb_layout_head() 新增 [admin] 开关,admin.php 两处
(管理登录、管理后台主界面)都传 admin=true。
后台顶栏只留:站点标识(回首页)+「去前台看看」。
唯一的后台出口是「去前台看看」,管理员偶尔需要核对前台实际效果。
验证:
后台顶栏链接只剩 brand-mini(href=./) 和 navlink(href=index.php, 去前台看看);
sw-toggle / sw-panel 关键字均已不存在。
前台顶栏三样仍在(问题列表 / 我要反馈 / 三横线),未被波及。
6 个路由全 200。
|
2026-11-03 21:11:51 +08:00 |
|
laoyang
|
974dd52367
|
去掉软件标签的描边;修「全部问题」列表分类标签渲染成空
两个问题:
一、两个标签中间那道「线」
不是 CSS 分隔符 —— 是 .badge.sw(软件徽章)带 border:1px solid var(--swc-line),
而 .badge.cat / .badge.st 都没有边框。并排显示时那道描边看起来
就像凭空插了一条分隔线。去掉边框,三个标签统一为无边框胶囊。
二、反馈类型(Bug / 功能建议)在后台「全部问题」列表里不显示
🔴 根因:fb_category_label() 参数是**数字 id**,内部会 (int) 化。
而全部问题列表传的是 $t["category"](老 slug 字段,值为 bug),
(int)bug === 0 → 函数返回空串 → 渲染出一个**空的** badge:
标签框在、里面什么都没有,看起来就像「类型没了」。
修法:
- 新增 fb_category_label_by_slug($softwareId, $slug) 给老数据兜底
(slug 在不同软件下可能重名,必须带 software_id)
- 全部问题列表改用 category_id 优先、slug 兜底
- 前台列表同样处理,并且**取不到名字就不渲染这个标签**,
绝不再输出空胶囊
- 在 fb_category_label() 的注释里写明「必须传数字 id」,
避免下次再踩
验证:
函数对照跑通:#4 category_id=6 → Bug;#5 category_id=7 → 功能建议;
旧写法 fb_category_label("bug") → (空),确认这就是根因。
浏览器实测详情页三个标签 border-top-width 全部 0px / style none,
圆角都是 999px,描边已消除。
前台列表两个条目分类标签分别渲染出 Bug / 功能建议,不再是空。
7 个路由全 200。
|
2026-11-03 20:50:00 +08:00 |
|
laoyang
|
dcf510ef28
|
后台三处列表/详情:标题顶头独占一行,标签下移(与前台统一)
用户:「管理后台的待审核和全部问题那个列表里面也是一样的。
标题应该顶头,然后在标题下面另起一行,写上来自于哪一个软件、
它的状态是什么、它是什么分类。点到一个具体的反馈里面也是一样的,
标题顶头,然后把同步日历、bug、待处理往下一行。管理后台也是全部按照这种方式去处理。」
改三处:
1. fb_admin_pending_row(待审核列表)
prow-top(标签在前+标题)→ prow-head(标题独占一行)+ prow-tags(软件/分类/状态)
2. 全部问题列表
item-top → item-head + item-tags
🔴 同时补上软件名(fb_software_chip)—— 后台是跨软件视图,
前台某个软件内部不需要显示软件名(满页同一个),后台必须显示。
3. 后台详情页头部
item-top → item-head + item-tags,标题字号 19px 略大于列表
样式新增 .prow-head / .prow-tags(.prow-top 保留给操作日志,
日志条目只有「动作+编号」,单行反而合适)。
验证(真实渲染 + 浏览器测量,不登录后台、不动数据):
待审核行 标题 top=87 left=222 / 标签 top=119 left=222 → 标签在下方、左对齐 0px 偏差
全部问题 标题 top=318 left=223 / 标签 top=351 left=223 → 同上
详情头 标题 top=541 left=228 / 标签 top=575 left=228 → 同上
另外静态检查:三处的 head 段内均无状态标签,tags 段内均含软件/分类/状态。
前台 5 个路由 200,未受影响。
|
2026-11-03 18:58:04 +08:00 |
|
laoyang
|
82f4fa3c49
|
顶栏图标改为与「标题+简介」整体垂直居中
用户:「图标应该在标题和它的简介的正中间…现在图标和上面的标题是对齐的,
把图标往下放一放,让标题和它下面的简介合起来和图标高度一样,
两个对齐上下。」
根因是结构问题,不是调 padding 能解决的:
以前图标和标题被 fb_software_badge() 包在同一个 .sw-badge 里(单行结构),
note 挂在它外面下面一层 ⇒ 图标天然只能对齐第一行文字,永远做不到两行居中。
改结构为「左图标 + 右文字列」两栏:
<a class="brand-mini">
<span class="brand-ic">图标</span> ← flex:none 30×30
<span class="brand-txt"> ← flex column
<span class="brand-title">标题</span>
<span class="brand-mini-note">简介</span>
</span>
</a>
外层 .brand-mini 用 align-items:center ⇒ 图标中线对齐文字列中线。
图标不再走 fb_software_badge(),改用 fb_software_icon() / icon_key 自己拼,
因为 badge 那个函数是把图标和名字绑死在一行的。
实测中线偏差:
桌面 1280:图标 18→48(中线 33.0),文字列 13.8→52.2(中线 33.0)→ 偏差 0.0px
手机 390:图标 17→43(中线 30.0),文字列 12.3→47.7(中线 30.0)→ 偏差 0.0px
首页(无软件时)仍是原来的 logo-mark + brand-text,未受影响;6 个路由全 200。
|
2026-11-03 15:21:41 +08:00 |
|
laoyang
|
0ebf250c76
|
去掉内容区与顶栏重复的标题行和我要反馈按钮;简介移到顶栏标题下灰色小字
用户:上面已有标题和「我要反馈」,下面又出现一次,完全重复。
「下面这一行不要了…把那个软件简介放在最上面『同步日历反馈系统』下面那个位置,
用个灰色的。我要反馈按钮也不要了,其他内容开始往前提,更简洁。」
改法:
- index.php 删掉整个 page-head 行(软件图标+名字 / 简介 / 我要反馈大按钮)
→ 列表内容直接从筛选器开始,上提一整行(实测内容起始 y=109)
- 顶栏品牌位改成两行:
行1 图标 + 「XX 反馈系统」(软件主题色 17px)
行2 一句话简介(software.note,灰色 #6b7280,12.5px,移动端 11.5px)
- 简介左对齐到标题文字(padding-left 41px = 图标 32 + gap 9),不是贴图标左边
- 顶栏高度从固定 66px 改为 min-height + padding,两行也放得下(实测 67px)
⚠️ 「我要反馈」按钮去掉了 hide-sm:
列表页那个大按钮删掉后,顶栏这个是全站唯一入口,
如果手机上隐藏掉,手机端就没有提交入口了。
清理 .page-sw-id 死样式。
验证:桌面/手机下简介都在标题下方、颜色灰色、内容区无重复、我要反馈只 1 个且两宽度都可见;
8 个路由全部 200;详情页顶栏同样显示简介。
|
2026-11-03 15:05:37 +08:00 |
|
laoyang
|
c09a9875a3
|
标题收到最上顶头,标签下移一行;顶栏标题回当前软件;去掉跨软件引导
用户三个问题:
一、软件名/分类/状态三个标签挤在标题前面,把标题推到右边很难看
列表页:item-top 里 badges 在前、标题在后 → 标题被挤到右边
详情页:同一个毛病
改法(两处一致):
行1 标题(顶头、独占一行)
行2 标签(分类/状态;详情页还有软件名)
行3 摘要 / 作者信息
列表页新增 .item-head + .item-tags;详情页新增 .subject-head + .subject-tags。
⚠️ .item-top 保留 —— admin.php 有三处内部列表还在用,删了后台会塌。
二、顶栏「XX 反馈系统」点完跳首页
用户:「点完之后应该还是进入这个反馈系统,而不是进到首页。首页没有太大作用,
只是演示说明用的。正常使用都是每个软件独立,要让用户觉得
这套反馈系统就是专门为这个软件做的。」
改法:brand-mini 的 href 从 "./" 改为 fb_software_list_url()。
三、软件内部不该引导去其他软件
用户:「在一个反馈系统里面,我要反馈、问题列表、左上角那个链接,
全部都是这个软件相关的内容,跟其他软件没关系。」
- 提交成功页删掉整个「也可以去其他软件反馈」区块及其 CSS
- 删掉成功页的「返回首页」按钮(已有「看该软件其他反馈」,重复且无意义)
- 跨软件只剩顶栏三横线菜单一个入口
验证:列表页/详情页 DOM 顺序均为 标题→标签→作者(实测 item-title 58 < item-tags 171;
subject-title 49 < subject-tags 151 < item-meta 592);
浏览器实测标题 top=282 标签 top=315(列表)、155→194(详情),标签确实在下方;
标题左边缘距卡片边 18/23px,就是卡片自身 padding,没有额外缩进;
顶栏标题 href=index.php?sw=synccalendar,不再指向根路径;全站 8 个路由 200。
|
2026-11-03 13:43:15 +08:00 |
|
laoyang
|
0dc8033c6b
|
本人回复自己的反馈不发通知信
用户:「他自己回复他自己的反馈,不要再给他发邮件了 ——
他自己说的,他发给他自己,不是很奇怪吗?但如果是别人回复他的,就可以发。」
原逻辑在 mailer 里有个「自己通知自己」判断,但写成了:
!empty($extra["is_admin"]) === false
双重否定绕,读起来要停一下才知道在判什么,而且没有覆盖空格差异。
改法:
- 改成直接判 $isAdminNotice,条件意图一眼可见
- 邮箱比较前 trim + strcasecmp,覆盖 大小写不同 / 前后空格 两种写法
- 拦下时写 notify_log 并给出明确原因「本人回复自己的反馈,不发信」,
以后排查「为什么没收到信」能直接看到
验证(5 种情形 + 完整 DB 路径):
同邮箱 → 拦下;大小写不同 → 拦下;带空格 → 拦下;
别人邮箱 → 正常发;管理员邮箱 → 正常通知提交人。
另在库里真插一条本人回复走 fb_notify_event,同样被拦下。
关于「带 token 能否修改原反馈」:确认后决定不做。
理由:反馈一旦公开,别人已看过/回复过,改了会让讨论对不上号;
补充信息用回复完全够用。现状只能看和回复。
|
2026-11-03 10:48:07 +08:00 |
|
laoyang
|
8763e8e17f
|
回复框去掉管理员口令与处理状态;带 token 时昵称/邮箱锁定为只读
用户三个问题,本质是同一件事:token 就是身份凭证,那就不该再让表单声明身份。
一、去掉管理员口令 / 处理状态
用户:「回复这个地方,下面有个管理员口令,还有处理状态,都不要填了,
只保留回复内容,加上人机验证就可以了」
- 删掉表单里这两块,删掉 admin-pw/admin-status 的 CSS 与 bindAdminToggle 的 JS
- 管理员身份只认后台登录态(fb_is_admin()),不再从公开表单收口令
- 状态流转只在后台详情页那排按钮里做
二、带 token 时昵称/邮箱锁定为只读
用户:「如果他带着 Token 进来之后邮箱又让他改了,我们又没办法知道谁是谁了」
- 有 token = 已证明是提交人本人,表单不再让填昵称/邮箱,
改为显示只读的「回复身份」块(原昵称 + 原邮箱)
三、服务端强制(关键)
🔴 前端设成 hidden 是不够的 —— hidden 可以被改:
谁拿到链接、改个 email 提交,这条反馈的联系人就变成他的了。
所以服务端在 isOwnerView 时**直接丢弃 POST 里的 name/email**,
一律以库里那条帖子的 author_name / author_email 为准。
身份校验必须在服务端做。
验证(这次确保请求真的通过人机验证,不是被验证码挡住造成的假通过):
题目「2 乘以 7」答 14 → 跳转到 #r3,回复确实入库;
提交的是 黑客改名 / attacker@evil.example,
实际入库 name=jervis email=zeussun@msn.com —— 伪造的两个字段都被丢弃。
另外:无 token 的普通访客仍可自由填昵称邮箱;全站已无「管理员口令」残留。
测试回复已清理,reply_count 已同步。
|
2026-11-03 10:02:57 +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
|
2f28b1e3a9
|
下拉菜单改成极简小浮窗(参考家书 FamilyBook 的 popup 做法)
用户第三次指出这个按钮不对,原话:
「你不用做那么复杂,就是参考一下 FamilyBook 里边那个设置按钮,
非常简单的,点了之后就拉下来一个很窄的一个框,
把所有的内容都铺在上面就可以了」
去看了 FamilyBook 的实现:顶栏那颗按钮是 bindSheet + SheetSize.FIT_CONTENT,
即「内容多高就多高」的小浮层,不是整幅面板。
改法:
- 面板从「left:0 right:0 铺满宽度 + 遮罩 + 锁滚动」
改成「贴按钮右下、min-width 172px、内容撑高、圆角 12px 小浮窗」
- 删掉 sw-mask 遮罩、删掉 body overflow 锁定
- 面板移进 .topbar .wrap(flex 内),以它为定位参照,右对齐按钮
- 删掉菜单里「选择软件」标题(按钮本身已经说明用途)
- 条目字号降到 14px、图标 18px、内边距收紧,贴合窄框
- 「当前」标记从胶囊缩成 6px 小圆点,窄框里不占地方
- 按钮简化:去掉边框与三横线变×的动效,只留 hover 浅底
实测(CDP 真实浏览器):下拉框 172×138px,仅占视口宽 13.7%,
右边缘与按钮相差 4px;390px 与 1280px 两种宽度下各 15 项断言全过
(浮窗非整幅、无遮罩、未锁滚动、贴按钮下方、右对齐、
高度由内容决定、有圆角边框、条目纵排同x、y递增、点外部可关)。
|
2026-11-03 09:25:41 +08:00 |
|
laoyang
|
82484298e4
|
标题位置修正:顶栏即「XX反馈系统」,内容区只留图标+软件名
用户指出三处问题:
1. 最上面标题应显示「同步日历反馈系统」,而不是光秃秃的「同步日历」
2. 内容区那个重复的「同步日历反馈系统」不要再要了,
那里只要「图标 + 同步日历」
3. 内容区下面多了一个裸露的 ?> 字符
4. 图标和标题文字都要再大一点
改动:
- 顶栏改为 fb_software_badge(sw, brand, fb_software_site_title(sw))
图标 56px、文字 26px,成为页面唯一主标题
- 删除内容区的 h1.page-h1-sw(重复标题)
- 内容区改为 badge(lg):图标 40px + 文字 30px
- 清除 index.php 里多余的 ?> (上次编辑留下的残渣)
- badge 新增 brand 尺寸档与 参数,可覆盖显示文字
- 顶栏高度 72->84px 容纳更大的图标;窄屏仍有降级
验证:顶栏 title/文字正确、已无 page-h1-sw、
无裸露 ?>、内容区只显示「同步日历」、5 处图标全走 media.php
且均为有效 PNG、三个软件页与全部路由 200。
|
2026-11-03 08:34:03 +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
|
872baf4153
|
修复:图标存储未跟随后台配置,本地模式下图标显示不出来
用户指出图标应当与附件同一套逻辑:管理员在后台配 local 就存本机、
配 oss 就存 OSS,前台都一样走 media.php 代理。
原实现把图标键硬编码成 icons/…(不带存储前缀),导致:
- fb_attach_is_local() 恒为 false
- 本机模式下文件确实写进了 uploads/icons/,
但 media.php 把它当 OSS 对象去读 → 503,图标完全显示不出来
修法:图标键与附件完全同规则,只是路径里多一段 icons/
local 模式 → local/icons/YYYY/MM/<hex>.<ext> 落 uploads/icons/
oss 模式 → <oss.prefix>/icons/YYYY/MM/<hex>.<ext> 落私有桶
前台地址两种模式一致(media.php?k=…),换存储方式旧图标不会失效。
同时:
- fb_icon_is_key/ok 改为同时识别两种前缀
- fb_icon_allowed_ext 移到 attach.php(media.php 与校验都会用,
不能依赖 admin_taxonomy.php 被加载)
- 写入时加断言:键前缀必须与当前存储模式一致,脱钩就报错,
不再静默存到错误位置
验证(线上真实环境,两种模式各跑一遍):
- local:键带 local/icons/ 前缀、写入 uploads/icons/ 成功、
media.php 读回 200 image/png 501B、PNG 魔数正确、删除后 404
- oss:键带 fb/icons/ 前缀、is_local=false、URL 拼接正确
- 旧 bug 形态(icons/… 无前缀)现在被明确拒绝 400,不再误判成 OSS
- 测试后配置已复原为 oss,临时文件与诊断入口全部清理
|
2026-11-03 08:19:17 +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
|
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
|
0365098fa4
|
修复:空格式白名单反而拒绝一切文件;定位「填 0 时正文图传不上」真因 (#4)
## 一、定位用户报的「提交反馈里图片不显示」
用户先说正文图不显示、回复的却显示,随后反馈「现在可以了,但之前确实不行」。
真因在 #4 已修掉,但当时没写清楚机制:
for (var i = 0; i < files.length; i++) {
if (countOf(kind) >= maxCount) {
alert("最多上传 " + maxCount + " 个");
break; // ← 整批文件一个都不传
}
}
数量上限填 0(本意不限)时,countOf 是 0、maxCount 是 0,
`0 >= 0` 为真 ⇒ 弹「最多上传 0 个」⇒ break 跳出整个循环 ⇒
选中几张就全部丢弃。上传区看起来"正常"(只是弹了个框),
提交后正文自然没有图。
已在线上核实用户新发的那条:aid=2 tid=2 reply_id=NULL,
附件正确挂在主题正文,详情页 .atts 渲染出 1 个 img,media.php 返回 200 / 225KB。
## 二、顺带修同一段里的第二个同类陷阱
var allowed = (box.getAttribute("data-accept") || "").split(",");
if (allowed.length && allowed.indexOf(file.type) < 0) { ... 拒绝 ... }
`"".split(",")` 返回 `[""]`,长度是 1 不是 0。
所以「未配置格式白名单」时,allowed.length 判真、indexOf 永远 -1
⇒ 空列表反而拒绝一切文件,这与"没配置=不限格式"的意图相反。
修法:split 后 filter 掉空项。实测:
normal : allowed=["image/jpeg","image/png"] len=2 blocked=false
empty : allowed=[""] len=1 blocked=true ← 旧代码
当前 UI 没有清空 mimes 的入口,现网触发不到,但属于同一类
「空/0 被当成最严格限制」的坑,一并修掉。
## 三、资源版本 14 → 15
🔴 上一轮就是改了 app.js 却忘升版本,浏览器一直用缓存旧 JS,
导致修复看起来"没生效"。这次改完立刻升,三处引用
(layout.php 的 css/js、install.php 的 css)都走 FB_ASSET_V 常量。
## 四、新增专项与反向验证
`_tooling/feedlite/fb_attach_zero_js.py`:19 项。
关键设计:数量判断条件**从源码正则抽取**后注入 node harness,
不在测试里硬编码正确写法 —— 否则修复被回退时测试照旧全过,成了假通过。
反向验证(故意把修复回退):
- 正常版本 19/19
- 回退版本 12/19,B1/B2/B4/B6/B9 全部 FAIL,能准确定位
⚠️ 第一版 harness 的 countOf 返回固定值,导致 B3/B8("传着传着到上限")
假通过。改成动态读递增计数后才是真验证。
回归:全新安装 33/33、附件 22/22、老基线 25/25、约束检查 20/20。
|
2026-11-02 10:42:03 +08:00 |
|
laoyang
|
9701291b4e
|
修复:改了 app.js 却没升资源版本,浏览器一直用旧 JS (#4)
用户报「图片最多上传 0 个,请先移除已上传的文件」——JS 弹的。
## 根因:不是逻辑没写对,是新代码根本没加载
#4 已经把拦截改成 `maxCount > 0 && countOf(kind) >= maxCount`,
但**忘了把 FB_ASSET_V 加 1**。页面仍然输出 app.js?v=13,
浏览器直接从缓存读旧的 app.js(旧的判断是 `countOf >= maxCount`,
maxCount=0 时 `0 >= 0` 为真,一个都不许传)。
## 修法
FB_ASSET_V 13 → 14。这一改同时覆盖 style.css 与 app.js,
用户下次刷新就会拉到新文件。
## 新增检查脚本
`_tooling/feedlite/fb_asset_version_check.py`:
对比「最后一次改 assets/ 的提交」与「最后一次改 FB_ASSET_V 的提交」,
前者更新就报错。改前端资源却忘升版本这类问题肉眼极难发现,
必须用规则卡住。
用脚本自查当前仓库,确认本提交修完后转为合规。
## 教训
改 assets/ 下任何文件 → **必须同时把 lib/bootstrap.php 的 FB_ASSET_V 加 1**,
这是硬配套动作,写在 README 里但这次没执行到位。
|
2026-11-02 09:10:15 +08:00 |
|
laoyang
|
f1a81cbaa1
|
RAM 角色自动探测 + 邮件配置界面 + 每个软件独立主题色 (#1 #2)
## 1. ECS RAM 角色改为自动探测
原先要用户去阿里云控制台查「实例 RAM 角色名」再手填,纯属多余 ——
机器自己知道答案。
新增 fb_oss_detect_instance_role():读元数据
GET /latest/meta-data/ram/security-credentials/(末尾带斜杠)拿到角色名列表。
- 兼容单行 / 多行 / JSON 数组三种返回
- 未配置角色时 fb_oss_instance_creds() 自动探测,不再报「未配置」
- 后台改成只读展示「已绑定:xxx」,手填入口收进「高级」折叠里
- 保存时留空 = 自动探测后写入,不存空值
- 探测失败给出可操作原因(没绑角色 / 不在 ECS 上 / 元数据被禁用)
## 2. 邮件配置界面(原先完全缺失)
lib/mailer.php 有完整 SMTP 实现,但后台一个输入框都没有,
只能改 config.php —— 用户装完根本找不到地方配。
根因有两层:
- fb_setting_keys() 白名单里没有任何 smtp.* / notify.* 键,
就算加了表单,fb_set_setting() 也会把值全部丢弃
- 设置页只有「站点信息」「附件存储」两个区块
补上:白名单 15 个键 + 后台「邮件通知」区块
- SMTP 服务器/端口/加密方式(SSL/STARTTLS/不加密)
- 登录账号/密码(留空不修改,避免每次重填)
- 发件地址/显示名、超时、SSL 校验
- 通知规则:回复通知、每次回复都通知、新反馈通知管理员、发信冷却
- 「发送测试邮件」按钮,AJAX 发完就地显示成功或失败原因,
不用保存再靠猜
## 3. 每个软件一个主题色
需求:从短链接进不同软件,颜色不同,一眼区分。
- software 表加 color 字段(幂等迁移)
- 全站配色靠 --brand / --brand-600 / --brand-soft 三个变量,
按软件覆写这三个即可,不用为每个软件写一套 CSS
- fb_color_variants() 由主色推出深一档(×0.85)与浅一档(往白混 88%)
- 未配色的软件按 id 从 8 色调色板取默认色,保证同站不撞色
- 后台:颜色选择器(原生取色 + 8 个预设 + 用默认)
- 前台:index / new / thread 都按当前软件注入主题色,
meta theme-color 也跟着变
验证:三功能专项 30/30;新旧回归全过
(全新安装 33/33、老基线 25/25、附件 22/22、安装向导 60/60、约束检查 20/20)
|
2026-11-01 16:39:09 +08:00 |
|
laoyang
|
1434a6dfb6
|
修复:英文标识留空真能自动生成;表单必填前端就地提示不刷新 (#1)
## 问题 1:文案说「留空自动生成」,实际却报错
界面上英文标识写的是「留空自动生成」,且不带红星;但纯中文名
(如「同步日历」)经 slugify 过滤后是空串,服务端照旧报「请填写英文标识」。
文案与行为自相矛盾,用户不明所以。
根因:fb_sw_slugify() 只保留 ASCII,中文全被过滤掉。
修法(不靠加红星了事):给 slugify 加兜底前缀 + 序号
- 软件:中文名 → sw<序号>(如 sw2)
- 分类:中文名 → cat<序号>
- 编辑时留空 = 保持原标识,不因改名把已有网址打散
- 纯英文名行为不变(FeedLite → feedlite)
实测:同步日历 → sw1 / 我的软件 → sw2 / FeedLite → feedlite
## 问题 2:必填项没填,整页刷新才看到错误
原先完全靠服务端校验:提交 → POST → 整页重刷 → 页面顶部弹一条错误。
用户视角就是「什么都没填,页面闪了一下」。
新增 bindInlineValidate()(assets/app.js):
- 给 form 加 data-validate 即启用,零配置
- 就地校验:字段下方显示红框 + 原因,聚焦并滚到可见
- 用户一改动立刻撤掉提示,不一直红着
- 规则取自原生 required / minlength / pattern / type=email
🔴 关键实现细节:用**捕获阶段**监听,并调 stopImmediatePropagation()。
因为 bindNoDoubleSubmit 会在 submit 时禁用按钮 5 秒,若校验排在它之后,
被拦下的表单按钮会被锁住,用户以为点坏了。所以校验必须先跑且拦住它。
## 服务端仍保留兜底
JS 被绕过(禁用脚本、直接构造请求)时,后端照样拒绝空用户名、短口令。
## 顺带收敛资源版本
app.js?v=5 与 style.css?v=N 原先各自写死,已统一到 FB_ASSET_V(升到 11)。
加上前一轮的 layout.php / install.php,现在全站只有 bootstrap.php 一处定义。
验证:
- 标识生成与前端校验专项 21/21
- 全新安装 33/33、老基线 25/25、附件专项 22/22
- 安装向导 60/60、后台管理 30/30
|
2026-11-01 15:21:27 +08:00 |
|
laoyang
|
7a578fac47
|
静态资源版本收敛到 FB_ASSET_V 常量 (#1)
原先 layout.php 与 install.php 各写一份 style.css?v=N,改样式时只改一处,
另一处会漂移 —— 实际就发生了:首页 v=10、安装页还是 v=7,两页样式不一致。
现在统一在 lib/bootstrap.php 定义 FB_ASSET_V,两处都引用它。
以后改 assets 下的 css/js,只改这一个数字。
|
2026-11-01 12:47:41 +08:00 |
|
laoyang
|
5006d54be3
|
附件读取统一走 media.php 代理,真实地址不再暴露
无论附件存在本机 uploads/ 还是 OSS 私有桶,前台给浏览器的地址现在完全一致:
media.php?k=<对象键>。界面上不再出现 uploads/ 真实路径,也不出现任何 bucket /
endpoint / 签名痕迹。
- lib/attach.php:新增 fb_attach_proxy_url(),fb_attach_url() 与
fb_attach_thumb_url() 的本机分支一并改走代理(地址不落库、渲染现算,
对历史附件立即生效,无需迁移)。
- uploads/ 整目录禁止 Web 直读:附件只能经 media.php 出,真实物理路径
即便被猜到也取不到,同时绕不开 media.php 的可见性鉴权。
- 规则版本化自动升级:.htaccess 内埋 `# fb-local-guard: vN`,
旧版规则在任意请求时由 PHP(目录属主)整份重写。之所以不能靠部署覆盖:
部署账号对 uploads/ 无写权限,只有程序自己能改。
- 随包 uploads/.htaccess 改为程序产出的逐字节副本,并加断言防漂移
(否则线上会被自动升级覆盖,随包那份形同虚设)。
- 顺带修复真实源码泄漏:config.php.bak_<时间戳> 这类「非 .php 结尾、
但含源码」的备份文件此前可被公开下载。加固根 .htaccess:
去掉正则结尾锚点、补 .php[.\-_] 兜底、点名拒绝 config.php / oss.sts.php。
- media.php / upload.php / admin.php / README.md:文档与响应头同步。
验证:php -l 19 文件 0 失败;主回归 25/25、附件专项 22/22、安装向导 60/60、
统一代理渲染 7/7、反向验证 V1-V4 全被抓住;线上冒烟 30/30、
线上统一代理真机 13/13(OSS 私有桶 + 内网 endpoint 下上传→代理读回→
Range 206→伪造键 403)。
|
2026-09-28 23:33:45 +08:00 |
|
laoyang
|
27187b38f1
|
页脚入口与安装向导;加固管理员口令存储
需求落实:
1. 页脚常驻「管理后台」链接,不用再手敲 admin.php。
2. 页脚「由 FeedLite 驱动」中的 FeedLite 改为超链接(指向开源仓库);
后台新增「页脚」设置组,可关闭整句(关闭后只留管理后台链接),也可改链接地址。
3. 管理员口令存储方式在后台如实展示:数据库 settings.admin.password_hash,
存 bcrypt 单向哈希(不可逆),明文不落盘。
4. 新增安装向导 install.php:data/ 下没有数据库时自动 302 引导;
体检环境 -> 填站点信息与口令 -> 生成 config.php -> 建库建表 -> 完成。
顺带修掉一个真实安全隐患:
config.php 里的出厂占位口令 CHANGE_ME_admin 原先可被当作有效口令登录后台
(等于公开仓库里就写着线上后门口令)。现改为占位/空值一律拒绝,
未设口令时后台明确提示去安装向导,不再含糊报「口令不正确」。
实现要点:
- 新增 fb_cfg_file():只读配置文件、不叠加 settings 表、绝不触达数据库。
「是否已安装」以数据库文件是否存在为判据,而 fb_cfg() 读 settings 会触发
fb_db() 就地建库 —— 那会把安装态自己造出来,所以判定必须走不碰库的通道。
- bootstrap 只在已安装时建库;未安装则 302 到 install.php(CLI 下报错退出)。
- fb_settings_flat() 在未安装时直接返回空,杜绝任何隐式建库。
- install.php 用文本级替换生成 config.php,保留全部注释与 OSS/SMTP 段落;
生成的 PHP 用 token_get_all(TOKEN_PARSE) 校验后才落盘。
- 页脚链接只放行 http/https,后台保存时校验(防 javascript: 伪协议)。
- style.css 页脚行改 flex,版本号 v6 -> v7。
验证:
- php -l 全量通过;现有回归 25/25、附件专项 13/13 保持通过。
- 安装向导专项 60/60(含「明文口令未写入任何文件」「数据库字节里搜不到明文」)。
- 反向验证:撤销三处修复后对应断言确实失败(断言非装饰)。
- 几何检查:本机 15/15 + 页脚专项全过;线上真实 URL 15/15 + 页脚专项全过。
- 线上真机冒烟 22/22。
|
2026-09-28 22:53:55 +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 |
|