laoyang
|
e408dc78b8
|
表单不再预填示例数据,示例只留 placeholder (#3)
用户反馈:输入框里预填的「示例值」点进去得先删干净才能填自己的,不合理。
必须分清两类,否则会误删真实配置:
- A 类 真实配置(从数据库读出来的)→ 保留
- B 类 出厂示例(代码/配置文件里写死的)→ 清空,示例移 placeholder
本次处理的 B 类:
1. 安装页「网站名称」
原先回显 config.example.php 里的出厂名「轻反馈」,用户以为要保留。
现在首次进入为空,仅在表单校验失败重来时回填用户刚才填的内容。
2. 安装页「管理员用户名」
原先预填 admin,现在为空,placeholder 提示「例如 admin」。
3. 后台「对象键前缀」
原先显示 fb(来自 config 默认值),现在为空,placeholder「留空即用 fb」。
4. 后台「关闭通知邮件标题」
原先显示模板「关于你反馈的问题《%s》」,现在为空,placeholder 给出模板。
## 新增 fb_form_val()
config.example.php 里给的出厂默认值和「用户填过的值」在 fb_cfg() 里合并后
分不出来 —— 表单永远显示得出内容。这个函数按「数据库里有没有这条设置」判定:
设过就回显(哪怕值恰好等于默认),没设过就当未自定义、显示空。
## 顺带修复一个真 bug
close_mail_subject 原先留空会存成空字符串,fb_cfg() 返回空串后
sprintf() 得到空邮件标题 —— 用户清空模板就发不出有主题的邮件。
改成留空即删除该设置,回落到默认模板。
## 验证
#3 专项 20/20(含:真实配置保存后必须回显、清空后必须回落默认且不存空串);
回归全绿:全新安装 33/33、安装向导 60/60、老基线 25/25、约束检查 20/20。
|
2026-11-01 16:53:52 +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
|
b835afcd04
|
data/uploads 改为由 PHP 自动创建并自补防护,不依赖部署账号建目录 (#1)
## 问题
部署时用 btops 建 data/ 与 uploads/,属主成了 btops:btops,Web 用户 www 写不进去,
安装向导直接报「目录不可写」,还得请人用 root 手工 chown —— 这不该让用户干。
## 修法:让 PHP 自己建
PHP 以 Web 用户身份运行,它建出来的目录属主天生就是 www,不需要任何 chown。
- 部署脚本不再创建 data/ uploads/(只建 assets/ lib/)
- 首次访问时 fb_db_init() 自动 mkdir data/,并立刻补防护文件
- uploads/ 由 fb_local_ensure_dir() 同样自动建 + 补防护
- 安装向导的环境体检 mkdir 之后也立刻补防护,不留空窗期
## 新增 data/ 防护文件自动生成
原先只有 uploads/ 有自动防护,data/ 没有 —— 数据库目录裸奔。
新增 fb_db_ensure_guard(),写 .htaccess(拒绝直读,禁 db/db-wal/db-shm/sqlite/php)
+ index.html 占位,带 `# fb-data-guard: vN` 版本标记,规则升级时整份重写。
## 顺带确认
站点根 .htaccess 已有全局兜底(拒绝 .db/.log/.md/.bak 等直读 + Options -Indexes),
即使某个目录暂时缺 .htaccess,/data/ 和 /data/feedback.db 也是 403。
现在是双保险,不依赖单点。
验证(WSL 真实环境):
- 全新安装 33/33、老基线 25/25、附件专项 22/22、安装向导 60/60
- data/ 与 uploads/ 均由 PHP 自动创建,属主即运行用户
- 两者 .htaccess 自动生成并带正确版本标记(v1 / v2)
|
2026-11-01 15:07:53 +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
|
9b822d2d9a
|
安装向导:加管理员用户名,精简为填表即装、装完进首页 (#1 #2)
## 管理员账号(用户名 + 口令)
原先只有口令,没有用户名。现在两者都有:
- 用户名词入库 settings 的 admin.username(2~32 位,字母数字下划线点连字符)
- 口令仍是 bcrypt 哈希存 admin.password_hash,明文不落任何文件
- 登录校验 fb_admin_verify_login():用户名或口令任一不对都返回同一句
「用户名或口令不正确」,且用户名错时也照常跑一次 password_verify,避免用耗时探测账号
- 向后兼容:老部署只设了口令没用户名时,仍可只填口令登录,
并在登录页提示补一个用户名
## 安装向导精简
按用户要求「没数据就初始化,填完进主页」:
- 必填只剩 4 项:网站名称、管理员用户名、口令、确认口令
- 站点描述 / 官方邮箱 / 站点根地址改为 hidden(可留空自动探测)
- 密钥、令牌原本就自动生成
- 安装成功后展示网站名称与用户名,2 秒自动跳首页,也有按钮
- 按钮文案改为「完成安装,进入首页」
## 修掉一个真实 bug
首页把软件说明当成站点描述,导致「软件有 note 时站点描述被顶掉」。
两者是不同的东西:站点描述是全局设置必须始终显示,软件说明另起一行补充。
老基线 D6「首页描述变为新描述」就是被这个 bug 弄失败的。
## 另一个坑
admin.username 忘了加进 fb_setting_keys() 白名单,fb_set_setting() 会静默丢弃,
表现为「安装时填了用户名,装完却没有」。已补。
验证:
- 全新安装流程 32/32(真空站:无 config.php、无 data/*.db)
- 老基线 25/25、附件专项 22/22、安装向导 60/60
- PHP 8.5 全部入口 0 弃用
|
2026-11-01 12:39:20 +08:00 |
|
laoyang
|
55d2dfa46b
|
后台管理界面:软件/分类/状态可配,反馈可改挂归属 (#1)
## 新增 lib/admin_taxonomy.php
- 软件:增删改、排序、启停;新增时自动预置 Bug/功能建议/其他 三个分类
- 分类:在选定软件下增删改、排序、启停
- 状态:改名、改「给用户看的话」、排序、启停;标识不可改(历史数据靠它对应)
- 反馈改挂:th_move,可把反馈移到别的软件/分类,跨软件的非法分类会被拒
## 安全与边界
- 所有动作走 CSRF 校验(沿用现有机制)
- 不能删最后一个软件 / 没有分类的软件至少要留一个启用分类
- 有反馈挂着的软件或分类不能删,提示改为停用(停用后前台消失、数据保留)
- th_move 加入 fb_admin_is_taxonomy_act 白名单,否则会被「问题不存在」静默吞掉
## admin.php 接线
- 引入模块 + 动作分发 + tab 白名单加 taxonomy/statuses + 导航两项
- 前置检查放行不依赖 $t 的动作(否则新增软件报「问题不存在」)
- 全部问题列表:加软件筛选、分类跟着软件走;修掉三处仍在用旧
fb_statuses()/fb_categories() 的遍历(那会渲染出错)
- 详情页加「归属」表单,软件徽章可显示
## 其他文件
- api.php:状态/分类改数据库;新增 ?sw= / ?software_id= / ?cid= 过滤,
老的 ?category=<slug> 仍可用(在所属软件内解析)
- thread.php:详情页显示软件与分类徽章
- config.example.php:移除 categories / statuses,改由数据库管理
## 修掉两个真实 bug
1. 移除配置后出厂分类兜底只剩 4 个,漏了「同步问题」—— 建库后核对数量才发现
2. th_move 不在白名单,POST 后返回 200 却静默不生效
验证:后台 e2e 29/29;改挂与联动 13/13;PHP 8.5 全部入口 0 弃用;
19 个 PHP 文件语法全过。
|
2026-11-01 12:03:11 +08:00 |
|
laoyang
|
59f7466ae4
|
多软件归属:软件→分类两级,配置全部入库 (#1)
原先分类写死在 config.example.php,后台改不了;也没有「软件」概念。
现在改为数据库驱动的两级结构,后台可配(管理界面待做)。
## 数据层(lib/db.php)
- 新增 software / categories / statuses 三张表
- categories.software_id 外键:每个软件有自己的分类集合,可同名互不干扰
- threads 加 software_id / category_id;老 category 文本列保留不删(可回退)
- fb_db_migrate_taxonomy() 幂等迁移:建表 → 空表灌初始数据 → 回填老帖子
## 迁移实测
- 老 5 个分类(使用问题/Bug 反馈/功能建议/同步问题/其他)原样落进默认软件
- 老帖子按 category slug 回填 category_id,匹配不到的留 NULL
## 状态(5 个)
待处理 / 处理中 / 已解决 / 已关闭 / 不予考虑(wontfix)
每个状态带 user_note「给用户看的话」,后台可改,将进邮件正文。
待处理 = 刚提交:您提交的反馈已收到,我们正在处理中。
## 前台
- index.php:软件切换条 + 分类 chip(一级软件、二级分类)
- new.php:选软件、分类下拉随软件变、服务端校验分类归属
- URL:?sw=<slug>&cat=<slug>;老链接 ?cat=xxx 解析不到时退回全部,200 不 404
## 界面一致性
按用户要求去掉「单软件时隐藏软件选择器」的逻辑:
所有界面一致,配一个就列一个,配多个就列多个。
## 兼容
fb_categories() 现在必须传软件 id;fb_statuses() 返回行数组,
另有 fb_status_keys() 取 key=>name 映射。
验证:WSL 内临时实例实测——单软件显示 1 个软件条;两软件各显示
自己的分类集合与帖子,互不串;无 PHP 警告/致命错误。
|
2026-11-01 11:35:55 +08:00 |
|
laoyang
|
6135e7b890
|
兼容 PHP 8.5:消除全部弃用调用
PHP 8.5 把 curl_close()/finfo_close() 这类"空操作函数"标为弃用,
并把预定义局部变量 http_response_header 标为弃用(编译期判定,@ 与三元短路都压不住)。
- 句柄关闭收进 fb_curl_close()/fb_finfo_close():仅 PHP < 8.0 真正调用,
新版本直接跳过(8.0 起句柄随引用计数释放),保留 7.4 兼容
- 响应头读取改用 http_get_last_response_headers()(8.4+);
老版本经 get_defined_vars() 从作用域变量表取,源码不再出现已弃用标识符
- 新 API 是全局状态,用「请求前基线」比对,避免把上一次的残留头当成本次结果
- 安装体检与 README 的推荐版本区间更新为 8.1~8.5
验证:PHP 8.5.12 下五套测试全绿(25/25、22/22、60/60、7/7、反向 4 组全被抓住),
error_log 零条弃用记录;PHP 8.4.25 同样全绿。
|
2026-10-23 08:23:55 +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
|
1401ad899b
|
OSS 附件改为私有桶+内网Endpoint+服务器代理架构
背景:桶为私有、且只用内网 Endpoint(外网不可达),浏览器既无法直传、
也无法直连。原「浏览器直传 + 签名URL直连」两条路都失效,改为全站代理。
lib/oss.php:
- endpoint 自动转内网(*-internal),use_internal_endpoint 开关控制,内网走 http
- 移除浏览器直传凭证(PostObject),改为服务器端 PUT(流式 fb_oss_put_file)
- 新增 fb_oss_stream_get 流式读取(回调式,支持 Range,不整文件进内存)
- 签名支持 x-oss-process 子资源 + Authorization 头签名(Content-Type 入签)
- 新增 fb_oss_probe 连通性自检、fb_oss_error_text 错误归因(含跨地域提示)
media.php(新增): 附件代理输出,鉴权边界=对象键须在本站附件表(或本人待入库
登记)且帖子对访客可见,防止本站沦为能读整个共用桶的开放代理;透传 Range。
upload.php: local/oss 统一走两步(领键→action=put),OSS 模式由服务器代理 PUT。
lib/db.php: 新增 pending_attachments 表 + idx_att_key 索引(迁移幂等自动建)。
lib/attach.php: 待入库登记(mark/clear/mine/gc),解决上传后入库前的预览鉴权。
cron.php: 清理过期待入库登记 + 桶连通性自检输出。
admin.php: 设置页加「使用内网 Endpoint」开关、存储体检显示实际连接地址与探测按钮。
README/config: 全面更新为代理架构(无需CORS、无需自定义域名、同地域要求)。
验证:本地 e2e 25/25 + 附件专项 13/13(含防开放代理/Range/内网改写);
线上真机上传→内网PUT→media.php代理读回→Range 全通过。
|
2026-09-28 22:12:28 +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 |
|