缩略图懒生成 + 评论登录限流:时间线不再拉原图
- 新增 /uploads/thumb/{key}?w=:首访从存储端读一次原图,标准库盒均值缩放后
落盘 data/.thumbnail_cache(按内容哈希命名,天然失效);失败写 .failed 冷却
一小时,非图片/超大一律 302 回原图,前端无感。原图路由改用 ServeContent,
补上视频拖动进度条所需的 Range 支持
- 前端 thumbURL/thumbifyHtml 接入两套 UI 的时间线配图、长文封面与短文正文,
并补 loading=lazy;详情页与灯箱仍用原图。/api/site 改为
{settings, uploads_public_base},让前端识别哪些直链属于自家存储
- 后台登录的滑动窗口限速器抽成 internal/ratelimit 共享包(调用面不变),
新增:读者登录失败按 IP 20 次/10 分钟、评论写入按读者 5 条/分钟
(站主豁免,且只计成功写入)
This commit is contained in:
1 parent
b6342a622d
commit
6108aca34c
17 files changed
+822
-71
No files matched your search
@@ -6,6 +6,40 @@ export function sanitizeHtml(html) {
|
||||
return DOMPurify.sanitize(html || '')
|
||||
}
|
||||
|
||||
// ---------- 缩略图 ----------
|
||||
// 时间线只展示小图:把我们存储上的图片直链改写为 /uploads/thumb/{key}?w=,
|
||||
// 后端懒生成并永久缓存。识别规则:以 uploads_public_base 开头的直链,
|
||||
// 或同源的 /uploads/ 相对路径(本地存储兜底)。外链(图床/头像)原样返回。
|
||||
// base 由 site.js 拉到站点设置后经 setThumbBase 注入(不走 import 避免循环依赖)。
|
||||
const IMG_EXT = /\.(jpe?g|png|gif)([?#].*)?$/i
|
||||
let thumbBase = ''
|
||||
|
||||
export function setThumbBase(base) {
|
||||
thumbBase = (base || '').replace(/\/+$/, '')
|
||||
}
|
||||
|
||||
export function thumbURL(u, w = 480) {
|
||||
if (!u || typeof u !== 'string') return u
|
||||
let key = ''
|
||||
if (thumbBase && u.startsWith(thumbBase + '/')) key = u.slice(thumbBase.length + 1)
|
||||
else if (u.startsWith('/uploads/') && !u.startsWith('/uploads/thumb/')) key = u.slice('/uploads/'.length)
|
||||
else return u
|
||||
if (!IMG_EXT.test(key)) return u
|
||||
return '/uploads/thumb/' + key + '?w=' + w
|
||||
}
|
||||
|
||||
// 时间线正文 HTML(短文全文 / 长文 600 字符截断)里的 <img> 换缩略图 + 补懒加载。
|
||||
// 只在展示层重写,库里存的正文不动。截断的 HTML 可能带半截标签,
|
||||
// 正则只匹配完整的 <img ...src="..."> ,坏尾巴交给 sanitize/浏览器容错。
|
||||
export function thumbifyHtml(html, w = 480) {
|
||||
if (!html) return html
|
||||
return html.replace(/<img\b[^>]*?>/gi, (tag) => {
|
||||
let out = tag.replace(/\bsrc="([^"]*)"/i, (m, src) => 'src="' + thumbURL(src, w) + '"')
|
||||
if (!/\bloading=/i.test(out)) out = out.replace(/^<img/i, '<img loading="lazy"')
|
||||
return out
|
||||
})
|
||||
}
|
||||
|
||||
// 用 Intl.DateTimeFormat — locale 感知,未来要 i18n 只换 locale 即可
|
||||
const longDateFmt = new Intl.DateTimeFormat('zh-CN', {
|
||||
year: 'numeric',
|
||||
|
||||
Reference in new issue
Block a user