05c01ccebe
fix(session): serialize session-limit enforcement with row lock
...
login 的 COUNT→DELETE→INSERT 改为事务内执行,并对 users 行加 FOR UPDATE,
串行化同一用户的并发登录,消除超出 MAX_SESSIONS_PER_USER 的竞态(M1)。
锁定 users 行而非 sessions 表,粒度最小;commit 后无残留 DB 操作。
2026-06-18 13:27:49 +08:00
53bfb1b7c0
feat(session): invalidate all sessions on role/status change via generation
...
users 表加 session_generation 列。get_user_by_token 缓存命中后回查 DB
generation,不匹配则逐出缓存并视为未登录,消除降级/封禁用户的权限残留
窗口(H2)。新增 invalidate_user_sessions 用于 bump generation,当前仓库
无运行时角色变更入口,作为未来用户管理功能的基础设施预留。
- 迁移 012:ADD COLUMN IF NOT EXISTS session_generation INT DEFAULT 0
- User/SessionUser 同步加字段,From<User> 如实传递
- 缓存校验走主键查询,亚毫秒级
2026-06-18 13:26:09 +08:00
82a3c12940
feat(security): add Origin-based CSRF protection for write endpoints
...
对所有 POST/PUT/PATCH/DELETE 请求校验 Origin(回退 Referer)等于本站,
堵住 login CSRF 与未来 GET 化写接口的盲区(SameSite=Lax 覆盖不到)。
- 新增 src/api/csrf.rs:纯函数 origin 解析(不引入 url crate)+ axum 中间件
- 挂载到 upload 路由与 Dioxus app 路由(最外层,先于超时/压缩)
- APP_BASE_URL 配置可信域名;未设置时回退 Host + X-Forwarded-Proto
- GET/OPTIONS 放行;拿不到本站 origin 时放行避免误杀
- 9 个单测覆盖写方法识别、origin 标准化、默认端口省略、头解析回退
2026-06-18 13:22:59 +08:00
00478e4a1a
perf(rate-limit): use lenient bucket when client IP is unknown
...
TRUSTED_PROXY_COUNT=0(默认)时,Dioxus server function 拿不到 TCP 对端,
get_client_ip 返回 "unknown",所有匿名请求共用严格桶(1 req/s, burst 5),
正常用户的高频请求被误杀。check_strict_limit 现在对 unknown IP 改走
宽松桶(30 req/s, burst 100,可通过 RATE_LIMIT_UNKNOWN_* 调整)。
配好反向代理后走真实 IP,仍命中严格桶。新增 2 个 serial 测试锁定两路行为。
2026-06-18 11:28:06 +08:00
9986d1ce4e
perf(auth): compile email regex once via LazyLock
...
auth.rs 与 comments/helpers.rs 原先每次校验都 Regex::new 重新编译。
改为 LazyLock 全局静态,正则只编译一次。
2026-06-18 11:25:15 +08:00
3d187382cc
perf(sanitizer): staticize allowlists with LazyLock to avoid per-call allocation
...
default_allowed_tags / clean_content_tags / default_allowed_schemes
原本每次调用都新建 HashSet 并逐个 insert;sanitize() 还 clone 一份。
改为 LazyLock 静态集合,SanitizerConfig 直接持有 &'static 引用,
评论白名单 COMMENT_ALLOWED_TAGS 在默认集合上派生。18 个 sanitizer
测试全部通过,XSS/URL 过滤行为不变。
2026-06-18 11:23:30 +08:00
753525fb41
perf(upload): offload GIF/WebP raw image validation to spawn_blocking
...
GIF 走 image::load_from_memory 会完整解码动画帧,在 async 上下文阻塞
worker。移到 spawn_blocking,JoinError 兜底任务失败。
2026-06-18 11:19:34 +08:00
033b89ccb8
perf(posts): offload Markdown rendering to spawn_blocking
...
create/update/rebuild 三处 Markdown 渲染(含 syntect 高亮)移到阻塞
线程池。rebuild 用 spawn_blocking 的 JoinError 替代 catch_unwind 捕获
渲染 panic,避免单条记录拖垮整批。
2026-06-18 11:18:36 +08:00
45e92795de
refactor(markdown): unify parser options across TOC and HTML passes
...
原先第一遍用 Options::all() 收集 heading,第二遍只用 ENABLE_TABLES
生成 HTML,两遍对扩展语法的处理不一致。统一为 Options::all()。
2026-06-18 11:16:58 +08:00
62600a6687
perf(auth): offload Argon2 hash/verify to spawn_blocking
...
Argon2 是 memory-hard 计算,登录/注册时同步执行会阻塞 Tokio worker
数百毫秒。改为在阻塞线程池执行,JoinError 兜底任务 panic。
2026-06-18 11:16:13 +08:00
79cb809010
perf(posts): add optional pagination to get_posts_by_tag, fix total count
...
- get_posts_by_tag 现接受 page/per_page 可选参数;两者均 None 时返回全部
(上限 200,用于无翻页 UI 的标签详情页),均提供时走标准分页。
- 修正 total:不再用 posts.len(),改为真实 COUNT(*),即使被 LIMIT 截断
也返回完整计数。
- 新增 CacheKey::PostsByTagPage 分页缓存键,与不分页键 PostsByTag 共存。
- 前端 tags.rs 传 (None, None) 保持原全部展示行为。
2026-06-18 11:14:56 +08:00
3ee39d910c
refactor(ssr): remove unused per-slug/per-tag generation counters
2026-06-18 10:30:07 +08:00
a71da7473d
perf(image): remove redundant Vec clone before spawn_blocking
2026-06-18 10:10:44 +08:00
36554af5f5
perf(image): store cached image data as Bytes to avoid Vec cloning
2026-06-18 10:03:35 +08:00
411e565465
docs(cache,search): update module and function comments
2026-06-18 09:55:45 +08:00
518b4e5d64
refactor(auth): store SessionUser instead of full User in session cache
2026-06-18 09:55:00 +08:00
c780247d17
feat(posts): invalidate search cache on writes
2026-06-17 17:21:01 +08:00
c40a771989
feat(search): cache search results with short TTL
2026-06-17 17:20:54 +08:00
1d216faa2f
feat(auth): add in-memory session cache
2026-06-17 17:20:47 +08:00
75b8f80631
fix(posts): wrap empty_trash in transaction and lock rows for precise invalidation
2026-06-17 17:05:07 +08:00
2d8f1e0d98
refactor(posts): avoid redundant cache invalidation in bulk fallback paths
2026-06-17 16:59:24 +08:00
c77f751377
refactor(posts): apply precise invalidation limit to batch_restore_posts
2026-06-17 16:59:18 +08:00
ca212a2aab
fix(posts): lock rows and read metadata in transaction for delete/purge/restore
2026-06-17 16:58:54 +08:00
1092fbb3ce
fix(posts): ensure rebuild invalidates tag posts cache
2026-06-17 16:44:32 +08:00
c03093fc8b
refactor(posts): use precise cache invalidation in rebuild
2026-06-17 16:37:20 +08:00
bb34e2c36e
refactor(posts): use precise cache invalidation in trash operations
2026-06-17 16:37:20 +08:00
82b070e7ac
refactor(posts): use precise cache invalidation in create/update/delete
2026-06-17 16:37:20 +08:00
15e7e2578d
refactor(utils): extract reading_time helper
2026-06-17 16:25:22 +08:00
9f41ea7354
refactor(posts): remove orphan row_to_post_list and use row_to_post_full for get_post_by_id
2026-06-17 16:24:38 +08:00
1eedab8f21
perf(posts): remove content_md from list/search SQL, read stored word counts
2026-06-17 16:06:27 +08:00
a6f08d5d3f
feat(db): add word_count and reading_time columns to posts
2026-06-17 16:06:21 +08:00
51e20980db
perf(stats): combine three COUNT queries into one conditional aggregation
2026-06-17 15:51:29 +08:00
170c021b37
feat(cache): add PostListItem DTO and use it in list/tag/search caches
2026-06-17 15:51:19 +08:00
31d5a99d2a
Merge cleanup/redundancies: remove dead comment code and duplicate DB indexes
CI / check (push) Failing after 7m33s
CI / build (push) Has been skipped
2026-06-17 15:24:47 +08:00
abdfd2e3f9
fix: make If-None-Match comparison RFC 7232 compliant
2026-06-17 14:14:37 +08:00
27ce878771
feat: support If-None-Match 304 for image responses
2026-06-17 14:08:45 +08:00
21b665f41d
feat: add Cache-Control and ETag headers for image responses
2026-06-17 13:59:17 +08:00
fe6e5bd045
perf: offload image decode/resize/encode to spawn_blocking
2026-06-17 13:52:05 +08:00
7bad3ce382
fix: make ConnectInfo optional in serve_image to prevent 500
2026-06-17 13:40:17 +08:00
07cb0a5b0d
refactor(comments): stop invalidating removed comment count cache in updates
2026-06-17 11:45:49 +08:00
a7fb8405c3
refactor(comments): stop invalidating removed comment count cache
2026-06-17 11:43:16 +08:00
a4954a6c1b
refactor(comments): remove unused CommentCountResponse and PendingCommentsResponse
2026-06-17 11:39:12 +08:00
fc9fce1f4d
refactor(comments): remove dead re-exports from comments module
2026-06-17 11:30:14 +08:00
472d8e91fa
refactor(comments): remove unused get_comment_count server function
2026-06-17 11:26:50 +08:00
0545412cf3
refactor(comments): remove unused get_pending_comments server function
2026-06-17 11:23:00 +08:00
476fad27e5
fix(minify): preserve Dioxus hydration markers and remove unsafe per-URL cache
...
- Keep Dioxus SSR comments (<!--#-->, <!--node-id<N>-->, <!--placeholder-->)
during HTML minification to avoid hydration failures.
- Drop the per-URL minify cache in the Axum middleware: the same URL can
render differently for logged-in vs anonymous users, so URL-level caching
risks leaking admin UI into public responses.
- Let the middleware handle HTML minification once instead of minifying in
markdown rendering as well.
- Consolidate CSS comment stripping into the existing minify_css path and
add a benchmark for html_minify.
2026-06-17 10:43:09 +08:00
449a545886
security: fix critical issues from repository review
...
CI / build (push) Has been cancelled
CI / check (push) Has been cancelled
P0 blockers:
- Fix migration numbering conflict and duplicate indexes
- Change comments.post_id FK to ON DELETE CASCADE
- Restrict public post detail endpoint to published posts only
- Fix rate-limiting IP extraction and fallback to ConnectInfo
- Harden HTML sanitizer: deny unknown URL schemes, restrict data URIs
- Remove session token from login response body
- Enforce image pixel/dimension limits on upload and serving
P1 high-risk:
- Validate uploads by magic bytes and decode GIF/WebP
- Add pagination/rate-limiting to search, tag posts, and comments
- Make first-admin registration and slug uniqueness check atomic
- HTML-escape comment author fields
- Improve HTML minify cache key and skip admin/error responses
- Add mobile navigation menu
P2 accessibility/quality:
- Associate form labels with inputs
- Key PostDetail article by slug to re-init scripts on navigation
- Improve image viewer keyboard accessibility
- Make theme toggle SSR-friendly and add aria-label
- Invalidate slug 404 cache on create and pending count on new comment
- Deduplicate tags case-insensitively
P3 cleanup:
- Remove unused tower-http dependency, expand make clean
- Configure DB pool timeouts and verified recycling
- Run background cleanup tasks immediately on startup
- Use SHA-256 for stable disk cache keys
- Log DB errors with Display instead of Debug
- Update README migration instructions
All tests pass (321), clippy clean, dx check clean.
2026-06-17 10:34:14 +08:00
bd659c5b4f
perf: add HTML/CSS minification for SSR responses and highlight CSS
...
CI / check (push) Failing after 5m54s
CI / build (push) Has been skipped
- Add lol_html-based HTML whitespace minifier (utils/html_minify.rs)
that collapses whitespace and strips comments while preserving
<pre>, <code>, <textarea>, <script>, <style> content
- Add Axum middleware (middleware/minify_html.rs) that minifies
text/html responses with per-URL moka cache (256 entries, 300s TTL)
- Wire middleware into the server router in main.rs
- Add CSS minifier to generate_highlight_css build step
- Apply minify_html to rendered markdown output and TOC
- Add http-body-util dependency for body collection in middleware
2026-06-17 09:49:44 +08:00
1a56c0cd3f
test: gate AppError helper tests behind server feature
CI / build (push) Has been cancelled
CI / check (push) Has been cancelled
2026-06-16 17:28:43 +08:00
2dcd300930
build: allow dead_code on CommentCountResponse for WASM builds
2026-06-16 17:12:35 +08:00