269 Commits

Author SHA1 Message Date
xfy
ec2f3e313e refactor(comments): remove consented_at column from migration and model 2026-06-11 13:24:00 +08:00
xfy
59d5b9222a refactor(comments): remove privacy consent checkbox and consented param 2026-06-11 13:22:57 +08:00
xfy
358dff152d fix(error): use Debug formatting in AppError logs for full error chain visibility 2026-06-11 13:19:18 +08:00
xfy
c10134c32e fix(comments): convert confirm_with_message Result to Option for WASM compatibility 2026-06-11 13:15:17 +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
efa41b42c2 feat(auth): add loading state to login/register forms and update AGENTS.md 2026-06-11 10:44:27 +08:00
xfy
f9d23d1eed refactor(ui): redesign with warm palette, sage accent, and consistent theme vars
- Warm editorial color palette (#faf9f6 light / #141416 dark)
- Sage green accent (#5c7a5e) for nav, links, buttons, tags
- Replace all hardcoded hex colors with CSS theme utilities
- Serif font (system Georgia) only on header logo
- Better hover states: scale, brightness, color transitions
- Accent-colored focus rings on inputs
- Fix language mixing: Back to Home → 返回首页
- No external font dependencies
2026-06-11 10:29:11 +08:00
xfy
fce16288b5 chore: symlink CLAUDE.md to AGENTS.md 2026-06-11 10:01:01 +08:00
xfy
fcb5a127d4 feat(skills): add multi-style design skills from taste-skill repo 2026-06-11 09:53:15 +08:00
xfy
ccd564312b feat: hash session tokens and enforce configurable session limit per user 2026-06-11 09:45:56 +08:00
xfy
5be0cab525 feat: add hash_token function for SHA-256 session token hashing 2026-06-11 09:44:01 +08:00
xfy
81555d920e refactor: sessions table stores token hash instead of plaintext 2026-06-11 09:42:32 +08:00
xfy
1fa379fe4a chore: add sha2 and hex dependencies for session token hashing 2026-06-11 09:42:08 +08:00
xfy
9cf6a7e4e6 fix(admin): replace use_delayed_loading with DelayedSkeleton for posts page
Use the same skeleton pattern as Home/Archives/Search pages.
Skeleton shows immediately (static gray blocks) instead of opacity-0 blank,
eliminating white flash during fast loads.
2026-06-10 18:15:46 +08:00
xfy
265eb15887 feat(admin): add pagination to posts management page
- list_posts API now accepts page/per_page params with LIMIT/OFFSET
- Add /admin/posts/:page route with PostsPage component
- Pagination component with prev/next navigation (20 posts/page)
- Update dashboard to use new list_posts(1, 5) signature
2026-06-10 18:03:36 +08:00
xfy
373d4f09ed refactor: clean up write page formatting and remove separator line
- Reorder imports to group wasm32-gated items together
- Format struct pattern matches for readability
- Remove metadata/editor separator line for cleaner visual flow
2026-06-10 17:50:07 +08:00
xfy
d3c5653808 feat: redesign admin write page with fixed-height editor layout
- Redesign write page UI with cleaner, more immersive editing experience
- Use CSS variables for theme-aware styling (paper-theme colors)
- Make editor fill remaining viewport height with internal scrolling
- Move action bar below editor (left-aligned, non-floating)
- Fix layout: page no longer scrolls, only editor content scrolls
- Update write skeleton to match new layout
- Update admin layout to use h-dvh for write routes to prevent page scroll
- Fix Dioxus.toml dev resource loading to include style.css

Fixes from review:
- Add missing relative positioning for loading overlay
- Use consistent root_class in unauthenticated state
- Use h-dvh instead of h-screen for mobile viewport compatibility
- Ensure skeleton matches actual component layout classes
2026-06-10 17:41:31 +08:00
xfy
24f00184a5 删除 public/tiptap/editor.css 构建产物 2026-06-10 16:51:51 +08:00
xfy
b1a5fdcb6e chore: release v0.2.0 v0.2.0 2026-06-10 15:46:11 +08:00
xfy
2f433e8f11 feat: add 404 Not Found page
- Add NotFound component with elegant design
- Register catch-all route in router
- Add skeleton placeholder for NotFound route
- Remove unused design-taste-frontend skill
2026-06-10 15:43:05 +08:00
xfy
a59a58b41d Add frontend-design skill, remove design-taste-frontend 2026-06-10 15:24:33 +08:00
xfy
5e2487a067 fix: gate helper imports with #[cfg(feature = "server")] in posts API
Server-only helper functions (get_current_admin_user, row_to_post_list,
row_to_post_full, sync_tags, clean_tags) are defined behind
#[cfg(feature = "server")] in helpers.rs, but their imports in consumer
files were not gated, causing WASM build failures.
2026-06-10 14:49:54 +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
bd9053132b refactor(admin): use explicit field ignores and separate error/loading arms 2026-06-10 14:38:52 +08:00
xfy
7df69be5e8 feat(skills): replace frontend-design with design-taste-frontend 2026-06-10 14:36:30 +08:00
xfy
31b83cd449 chore: adapt admin pages to new PostListResponse shape 2026-06-10 14:35:51 +08:00
xfy
a2ec043c59 chore: adapt search page to new PostListResponse shape 2026-06-10 14:30:57 +08:00
xfy
4015412b41 feat: display accurate total in tag detail page 2026-06-10 14:25:54 +08:00
xfy
2ade51bd0f feat: display accurate total in archives page 2026-06-10 14:22:18 +08:00
xfy
a2b0c759af feat: use total count for accurate home page pagination 2026-06-10 14:18:22 +08:00
xfy
44eba24121 feat: return total count from post list server functions 2026-06-10 14:14:04 +08:00
xfy
116f3281a4 feat: update post list cache to store total count 2026-06-10 13:59:41 +08:00
xfy
72c1efa566 feat: add total field to PostListResponse 2026-06-10 13:57:06 +08:00
xfy
b1385efd5e refactor(api): split posts.rs into module directory
- Move posts.rs (891 lines) to posts/ with 11 submodules
- Extract shared tag sync logic into helpers::sync_tags
- Extract tag cleaning into helpers::clean_tags
- All external import paths unchanged via mod.rs re-exports
- Zero behavioral changes, pure structural refactor
2026-06-10 13:45:21 +08:00
xfy
2574e1e315 fix(api): gate AppError helper methods behind server feature
tracing is an optional dependency unavailable in WASM builds.
The constructors are only called from server-side code anyway.
2026-06-10 13:21:37 +08:00
xfy
bf401e4916 fix(api): correct AppError variant semantics for transaction/query errors
- client.transaction() failures: db_conn -> tx (logs correct category)
- auth session INSERT: db_conn -> query
- delete_post UPDATE: tx -> query (no transaction context)
2026-06-10 13:19:57 +08:00
xfy
a3fa602df2 refactor(api): unify error handling with AppError enum
Replace scattered ServerFnError::new("...") inline calls and utils.rs
helper functions with a single AppError enum that provides:
- Automatic tracing::error logging for system errors
- Safe generic messages to frontend (no SQL/connection detail leaks)
- Typed error variants (Unauthorized, Forbidden, NotFound, etc.)
- 5 unit tests for message passthrough and detail hiding

Removes src/api/utils.rs entirely; all error creation now goes through
src/api/error.rs.
2026-06-10 13:16:06 +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
4c534eeb4c fix(posts): invalidate tag and old slug caches on create/update 2026-06-09 17:15:29 +08:00
xfy
91caf3f8bd feat(posts): invalidate caches on write operations 2026-06-09 17:09:17 +08:00
xfy
887aacb5f1 fix(posts): check cache before auth, skip caching None slugs 2026-06-09 17:06:53 +08:00
xfy
4c44b62e3d feat(posts): add caching to read operations 2026-06-09 17:01:12 +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
xfy
9452c196fa docs: document why image crate excludes WebP decoder 2026-06-09 16:14:28 +08:00
xfy
c08d35856e test: add WebP config parsing and clamping tests 2026-06-09 16:04:26 +08:00
xfy
05cf2f79ea refactor: extract WebP encode helper to reduce duplication 2026-06-09 15:59:02 +08:00