Commit Graph
5 Commits
Author SHA1 Message Date
laoyang 74beaf1163 附件限制全部支持填 0 = 不限制 (#4)
用户要求:附件存储里所有限制项(几张图、几个视频、大小、总量、保留天数、
每小时次数)都能填 0,0 表示没有任何限制。

## 最危险的地方:填 0 原本是「全禁」不是「不限」

两处逻辑把 0 判成了禁止,语义正好相反:

1. `fb_rate_check()` —— `count < limit`,limit=0 时 `0 < 0` 为假
   ⇒ 每小时上传次数填 0 = 一次都不许传
2. 每帖数量校验 —— `count > max`,max=0 时传一个就超限
   ⇒ 每帖图片填 0 = 一张都不许传

都改成「limit <= 0 直接放行」。

## 校验区间与 input 下限全部放开到 0

| 字段 | 原下限 | 现下限 |
|---|---|---|
| image_max_per_post | 0(但界面上限 20) | 0,上限 1000 |
| video_max_per_post | 0(上限 10) | 0,上限 1000 |
| image_max_bytes | 0.2 MB | 0,上限 1024 MB |
| video_max_bytes | 1 MB | 0,上限 10240 MB |
| total_cap_bytes | 10 MB | 0,上限 1048576 MB |
| retention_days | 1 天 | 0,上限 36500 |
| upload_per_hour | 1 次 | 0,上限 100000 |
| image_max_side | 480 | **保持 480** —— 这是压缩目标边长,不是限制项 |

服务端 `max($range[0], ...)` 不再把 0 拉回下限;input 的 min 一并改 0,
否则浏览器直接拦住连提交都到不了后端。

## 语义冲突:0=不限与「0=关闭该类上传」冲突

原先 `fb_upload_kind_on()` 靠「数量上限是否为 0」判断开关,
0 现在表示不限个数,两者冲突。

按用户选择:**新增独立总开关** `upload.image_on` / `upload.video_on`(默认开),
`fb_upload_kind_on()` 改看开关,不再看数量。
两个新键已加入 `fb_setting_keys()` 白名单(这个坑踩过多次)。

## 前端同步

- `maxCount > 0` 才拦,0 不拦
- 提示文案:数量 0 显示「数量不限」、大小 0 显示「大小不限」,
  不再出现「最多 0 张」这种荒唐文案

## 风险提示(按用户选择用文字告知,不阻止保存)

单张图片/单个视频/全站总量填 0 时,字段下方提示「填 0 时注意:磁盘可能被写满」。

## 验证
0 专项 31/31,关键几条:
- 七项输入框 min 均为 0,image_max_side 保持 480
- 填 0 后数据库里就是 0,没被拉回下限
- **填 0 后行为是放行**:每小时放行、数量不限、总量放行、不清理
- 数量填 0 时上传仍是开的(靠独立开关)
- 取消勾选开关后确实关闭,且与数量填 0 互不干扰
- 非 0 值全部正确(4 张 / 2 个 / 5MB / 50MB / 1024MB / 30 天 / 60 次)

回归全绿:全新安装 33/33、附件 22/22、老基线 25/25、安装向导 60/60、约束检查 20/20。
2026-11-02 08:55:23 +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 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