29 Commits

Author SHA1 Message Date
xfy
65a7b1226f style: 全项目格式规范化(cargo fmt)+ Docker daemon 断连容错
- 全项目 .rs 统一 cargo fmt 排版(换行/缩进/参数分行/导入排序)
- infra/docker: DOCKER_CLIENT 连接失败不 panic,降级返回 None
  (博客不依赖 Docker,panic=abort 下会导致整个进程崩溃)
- infra/mod: 去除多余空行
- database/mod: 模块声明按字母序重新排序
2026-07-15 17:22:06 +08:00
xfy
8da678b3b4 refactor(cache): 抽取 invalidate_post_metadata() 封装 4 行失效组合
文章写操作后总要一起失效 lists/tags/stats/search 四项元数据缓存。
新增 invalidate_post_metadata() 封装这个固定组合,替换 create/delete/
update/trash 中的 8 处重复 4 行序列。

保留定向失效(invalidate_post_by_slug / invalidate_tag_posts_for)
和批量回退路径(invalidate_all_post_caches + invalidate_search_results)
的显式调用——它们是按实际涉及数据细粒度控制的,不应被封装吞掉。
2026-07-14 14:44:00 +08:00
xfy
9c3f2242ba feat(stats): add trash count to PostStats for posts page badge
为合并回收站到 /admin/posts 的 tab 设计铺路:PostStats 新增 trash 字段,
统计 deleted_at IS NOT NULL 的文章数,供文章列表页「回收站」tab 角标使用。

- stats.rs: 条件聚合查询增加 trash 列,server/非 server 构造同步补字段
- models/post.rs: PostStats 加 pub trash: i64
- cache.rs: post_stats_cache_roundtrip 测试 fixture 补 trash 字段
2026-07-08 16:40:32 +08:00
xfy
c4c490b881 feat(admin): add server status tab (sysinfo host metrics + cache hit rate)
新增 sysinfo 依赖(optional+server),sysinfo_sampler 后台采样任务
(SYSINFO_SAMPLE_SECS 可配,默认 0.5s)周期刷新 CPU/内存/磁盘/load 到 RwLock
快照,get_server_status 只读快照零成本。cache.rs 给 9 个缓存加 AtomicU64
hit/miss 计数 + cache_stats() 聚合命中率。

前端 ServerStatusTab:应用内指标(运行时间/连接池/会话/CPU/内存/磁盘/load)+
缓存命中率表 + 刷新按钮 + 自动刷新开关(500ms/1s/2s/5s/手动,默认手动)。
2026-06-29 18:45:02 +08:00
xfy
373498870a style: apply cargo fmt to workspace
Some checks failed
CI / check (push) Failing after 5m35s
CI / build (push) Has been skipped
2026-06-29 13:47:40 +08:00
xfy
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
xfy
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
xfy
411e565465 docs(cache,search): update module and function comments 2026-06-18 09:55:45 +08:00
xfy
518b4e5d64 refactor(auth): store SessionUser instead of full User in session cache 2026-06-18 09:55:00 +08:00
xfy
c40a771989 feat(search): cache search results with short TTL 2026-06-17 17:20:54 +08:00
xfy
1d216faa2f feat(auth): add in-memory session cache 2026-06-17 17:20:47 +08:00
xfy
7bd02d0ea9 perf(cache): run tag post invalidations concurrently 2026-06-17 16:59:29 +08:00
xfy
c528936abb feat(cache): add invalidate_tag_posts_for helper 2026-06-17 16:37:20 +08:00
xfy
170c021b37 feat(cache): add PostListItem DTO and use it in list/tag/search caches 2026-06-17 15:51:19 +08:00
xfy
d1c9cea683 refactor(cache): remove unused per-post comment count cache 2026-06-17 11:52:57 +08:00
xfy
2caca3a48d fix(dead_code): 清理假阳性与真死代码
Some checks failed
CI / build (push) Has been cancelled
CI / check (push) Has been cancelled
- settings.rs: retention 常量与 clamp_retention 仅服务端使用,加 #[cfg(feature = "server")]
- comment.rs: 删除未使用的 Comment 结构体
- cache.rs: 删除未使用的 invalidate_all_comment_caches 及其测试

验证: cargo check (server/wasm), cargo test (311 passed), dx check, cargo clippy
2026-06-16 17:48:47 +08:00
xfy
054d7450f7 feat(model): Post 新增 deleted_at 字段并用 try_get 兼容现有查询 2026-06-16 12:29:37 +08:00
xfy
2db652137d docs(models, db, cache): 补充中文注释 2026-06-12 18:56:56 +08:00
xfy
041cdf4102 test(cache): mark cache tests serial to fix parallel flakiness 2026-06-12 17:40:15 +08:00
xfy
294d60afab style: format rust code
Some checks failed
CI / build (push) Has been cancelled
CI / check (push) Has been cancelled
2026-06-12 17:14:31 +08:00
xfy
04737300e6 feat(comments): add complete comment system with guest commenting, moderation, and admin UI
Implements a fully self-built comment system for the blog:

Data layer:
- comments table with BIGSERIAL PK, parent_id self-reference (ON DELETE SET NULL),
  depth tracking (max 20), status workflow (pending/approved/spam/trash),
  content hashing for dedup, GDPR consent tracking, IP/UA storage with auto-purge
- 5 partial indexes optimized for read patterns
- updated_at auto-trigger

API (9 Dioxus server functions):
- Public: get_comments, get_comment_count, create_comment
- Admin: get_pending_comments, get_pending_count, get_all_comments,
  approve_comment (with ancestor auto-approval), spam_comment, trash_comment,
  batch_update_comment_status

Security:
- Function-level rate limiting (1/sec, burst 5) via FullstackContext IP extraction
- Input validation (name, email, URL scheme, content length, consent)
- Parent chain validation (must be approved, same post)
- Strict comment Markdown renderer (headings→strong, no img/id/data URIs, nofollow links)
- Honeypot anti-spam field
- 5-minute dedup window via SHA-256 content hash

Frontend:
- CommentSection with SuspenseBoundary isolation
- Flat-list rendering with depth-based CSS indentation (responsive)
- Gravatar via cravatar.cn (server-computed, email never exposed)
- Inline reply forms (one-at-a-time via Signal)
- Admin action buttons (approve/spam/delete) visible per-comment
- CommentForm with privacy consent, Markdown hint, loading states

Admin:
- /admin/comments page with status tabs, batch operations, pagination
- Pending count badge on admin dashboard

Infrastructure:
- Shared get_current_admin_user moved from posts/helpers to auth module
- COMMENT_LIMITER rate limiter tier
- Moka caches (60s TTL for comments, 10s for pending count)
- IP/UA purge background task (daily, 90-day retention)
2026-06-11 12:34:26 +08:00
xfy
311ddbe204 perf(cache): cache COUNT(*) result separately to avoid redundant queries
- Add TotalPublishedPosts cache key for reusing total count across pages
- list_published_posts now checks total cache before running COUNT(*)
- Add note to get_posts_by_tag about total = posts.len() assumption
- Remove unused invalidate_total_published_posts helper
2026-06-10 14:44:53 +08:00
xfy
116f3281a4 feat: update post list cache to store total count 2026-06-10 13:59:41 +08:00
xfy
b7220c28ef Fix WebpError trait impls missing #[cfg(feature = "server")] 2026-06-09 18:30:10 +08:00
xfy
263771e403 test(cache): fix invalidation test isolation 2026-06-09 17:24:14 +08:00
xfy
73b4d28135 test(cache): use unique key in roundtrip test to avoid parallelism conflict 2026-06-09 17:20:03 +08:00
xfy
3041535cf7 test(cache): add cache key and roundtrip tests 2026-06-09 17:18:39 +08:00
xfy
0b594ff719 refactor(cache): use CacheKey directly, remove unnecessary async 2026-06-09 16:56:41 +08:00
xfy
62e2045b35 feat: add cache module with moka-backed post/tag/stats caching
- Create src/cache.rs with CacheKey enum, moka cache instances,
  getter/setter functions, and invalidation helpers.
- TTLs: post list 60s, tags 300s, single post 600s, stats 60s,
  tag posts 120s.
- Register mod cache in src/main.rs.
- All cache internals gated behind #[cfg(feature = "server")]
2026-06-09 16:47:17 +08:00