feat: 「自动获取」改用豆瓣数据源,海报自动下载入库

此前的 OMDb 数据源必须用英文片名,国产片覆盖极差。改用豆瓣公开接口后
中文片名直接可搜,冷门片/老片/剧集也能命中。

fetch_movie.php(全量重写)
- 搜索走 movie.douban.com/j/subject_suggest;详情走移动端
  m.douban.com/rexxar/api/v2/movie/<id>(PC 详情页有反爬 302,故用移动端接口)
- 海报下载后写入 uploads/posters/,文件名沿用内容指纹规则
- 新增登录鉴权:未登录返回 401(此前该接口完全公开)
- 启用 TLS 证书校验(原为 CURLOPT_SSL_VERIFYPEER=false)
- 魔数兜底 + 12MB 上限:挡掉反爬返回的 HTML 页面与异常大响应
- old_poster 残留清理:重复点击时删掉上一张,且仅删「未被数据库引用」的文件
- 接口地址支持环境变量覆盖,便于用 mock 做端到端测试

lib/helpers.php
- 新增 isValidStoredKey():校验表单回传的存储键,防目录穿越与越权引用

add.php
- 前端改用新字段(豆瓣评分 / 地区 / 又名),并把 poster_key 放进隐藏域回传
- 提交时「手动上传的海报优先」,没有才采用自动获取已入库的海报

README.md
- 同步为豆瓣数据源说明,移除已过时的 OMDb API Key 与 SSL 校验两条安全提示

验证
- 本地 mock 豆瓣端到端:33/33 通过
- 既有存储回归套件:196/196 通过
- A/B 归一化比对:除 add.php 按预期变化外,其余 45 个响应完全一致
- 线上实测:流浪地球 / 哪吒之魔童闹海 均抓取成功,海报落盘为合法 JPEG
This commit is contained in:
2026-09-21 23:05:40 +08:00
parent 294c34bbc2
commit ddf2eeafaa
4 changed files with 416 additions and 100 deletions
+18
View File
@@ -71,6 +71,24 @@ function relPathOfDir($absDir) {
return rtrim($rel, '/') . '/';
}
// 校验一个表单回传的存储键是否合法:必须严格落在 $prefix 目录下,
// 且文件名不含任何路径分隔符 / 特殊字符。
//
// 用途:自动获取海报后,前端会把服务端抓取入库的 poster_key 回传;
// 这个键来自客户端,不可信任——要防止「目录穿越」和「引用别的目录下的图」
// 之类的越权操作。校验通过后仍应用 storageExists() 确认文件真实存在。
function isValidStoredKey($key, $prefix) {
$key = ltrim((string)$key, '/');
$prefix = rtrim((string)$prefix, '/') . '/';
if ($key === '' || strpos($key, $prefix) !== 0) {
return false;
}
$name = substr($key, strlen($prefix));
return $name !== ''
&& strpos($name, '/') === false
&& (bool)preg_match('/^[A-Za-z0-9._-]+$/', $name);
}
// 删除已保存的文件(本地与云端按当前存储方式处理,不存在时静默跳过)
function deleteStoredFile($relative_path) {
if (empty($relative_path)) return false;