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 全通过。
This commit is contained in:
2026-09-28 22:12:28 +08:00
parent cede838b5e
commit 1401ad899b
12 changed files with 866 additions and 246 deletions
+24
View File
@@ -115,6 +115,30 @@ SQL
$pdo->exec('CREATE INDEX IF NOT EXISTS idx_att_thread ON attachments(thread_id, seq)');
$pdo->exec('CREATE INDEX IF NOT EXISTS idx_att_reply ON attachments(reply_id)');
$pdo->exec('CREATE INDEX IF NOT EXISTS idx_att_age ON attachments(created_at)');
// 代理读取要按对象键反查(media.php 的鉴权),必须有索引否则每次读图都全表扫描
$pdo->exec('CREATE INDEX IF NOT EXISTS idx_att_key ON attachments(oss_key)');
/* ---- 待入库附件 ----
* 私有桶 + 内网 Endpoint 下,附件一律经 media.php 代理输出,而代理必须鉴权。
* 但「上传成功」发生在「表单提交入库」之前 —— 前端上传完就要立刻显示缩略图,
* 此时 attachments 表里还没有这条记录。若只认 attachments 表,缩略图必然 403。
* 所以上传成功时先在这张表登记一条,media.php 认「已入库 或 本人刚上传的待入库」,
* 提交表单后由 fb_attach_save() 转正,过期条目由 cron.php 清理。
*/
$pdo->exec(<<<'SQL'
CREATE TABLE IF NOT EXISTS pending_attachments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
oss_key TEXT NOT NULL,
kind TEXT NOT NULL DEFAULT 'image',
mime TEXT,
size INTEGER NOT NULL DEFAULT 0,
ip_hash TEXT,
created_at INTEGER NOT NULL
);
SQL
);
$pdo->exec('CREATE INDEX IF NOT EXISTS idx_patt_key ON pending_attachments(oss_key)');
$pdo->exec('CREATE INDEX IF NOT EXISTS idx_patt_age ON pending_attachments(created_at)');
$pdo->exec(<<<'SQL'
CREATE TABLE IF NOT EXISTS rate_limit (