9 Commits

Author SHA1 Message Date
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