Skip to content

Add blog with backend-backed comments, reactions, and RSS#110

Open
KaliCZ wants to merge 3 commits into
mainfrom
claude/blog-comments-reactions-rss-x5Z7g
Open

Add blog with backend-backed comments, reactions, and RSS#110
KaliCZ wants to merge 3 commits into
mainfrom
claude/blog-comments-reactions-rss-x5Z7g

Conversation

@KaliCZ
Copy link
Copy Markdown
Owner

@KaliCZ KaliCZ commented May 2, 2026

Summary

  • New blog domain: post pages under [...lang]/blog/, shared BlogPostLayout, /rss.xml endpoint, listing index, and a metadata contract (pubDate, updatedDate, tags, draft, canonicalUrl) that Generate sitemap from blog post metadata #92 and Emit JSON-LD structured data for blog posts (comments, reactions, dateModified) #109 will both reuse.
  • New Kalandra.Blog backend module: append-only event streams per post slug for comments and reactions (separate UUIDv5 streams so high-volume reactions don't share a replay path with comments), reaction toggle add/remove semantics on replay.
  • New BlogController with [Authorize] POST endpoints and anonymous GETs, plus a parallel API error enum (AddBlogCommentError, BlogSlugError) so the wire contract stays a stable enum name the frontend's i18n map can key off.
  • First post: Shipping StrongTypes 1.0, pulling diagrams straight from the upstream repo.

Test plan

  • Backend integration tests: 12 cases in BlogApiTests cover auth gates (401 on POST without auth, 200 on GET), comment add → list flow, slug isolation, whitespace trim, invalid-slug 400, reaction toggle add/remove with multi-user accounting, anonymous viewer correctness, and the wire contract for the emoji enum.
  • Signed-out E2E: blog index lists posts and links to RSS, post page renders chrome with the signed-out hint, RSS feed includes the post.
  • Signed-in E2E (blog-flow.spec.ts): reaction toggle (add → pressed + count +1; toggle off → unpressed + back to start), comment post (appears with display name, persists across reload).

Notes

  • Closes Add blog section with emoji reactions and comments #91
  • Spotted and fixed during the merge: blog components were calling /api/... directly. That works in dev (Vite proxies) but would break in production where the API is on a different origin. They now use PUBLIC_API_URL like every other API caller in the codebase. Same fix applied to the local test:e2e:build script so it builds with PUBLIC_API_URL=http://localhost:5000 to match CI.
  • Validation contract: dropped [Required] from AddBlogCommentRequest. Framework auto-validation was short-circuiting before the controller's own check, returning a Content (PascalCase) key with the generic message rather than the stable content / ContentRequired the frontend looks up.
  • Follow-up: Generate sitemap from blog post metadata #92 (sitemap from blog metadata) and Emit JSON-LD structured data for blog posts (comments, reactions, dateModified) #109 (JSON-LD structured data so search engines see comment/reaction counts) both build on the metadata glob this PR introduces.

🤖 Generated with Claude Code

claude and others added 3 commits April 25, 2026 08:18
Closes #91

- Backend: new Kalandra.Blog domain with append-only event streams per
  slug for comments and reactions, separate UUIDv5 streams to keep
  high-volume events out of each other's replay path. Toggle semantics
  for reactions decide add vs. remove from the replayed stream.
- API: BlogController with [Authorize] on POST endpoints (only
  signed-in users comment or react) and anonymous GET endpoints, plus a
  parallel API error enum to keep the wire contract stable.
- Frontend: Astro blog pages under [...lang]/blog/, with a shared
  BlogPostLayout, vanilla-JS BlogReactions and BlogComments components
  that talk to the backend, optimistic toggles, and auth-dialog hooks
  for signed-out viewers.
- StrongTypes 1.0 release post pulls the diagrams straight from the
  upstream repo.
- /rss.xml is a hand-rolled Astro endpoint reading the same metadata
  the listing uses (no third-party RSS package).
Conflicts in Navbar.astro and pages.spec.ts resolved:
- Navbar: keep main's dropdown structure with Projects parent + children,
  add Blog as a top-level item between Projects and Hire Me. Drop the
  obsolete `isPageActive` helper duplication.
- pages.spec.ts: keep both sets of route checks (main's projects /
  strong-types alongside the branch's blog index, post, and RSS tests).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Blog frontend components were calling `/api/...` directly. That only
  works in dev (Vite proxies /api → backend); built dists need the
  PUBLIC_API_URL prefix to reach a cross-origin backend. Match the
  pattern used by the rest of the codebase: pass apiUrl through a
  data-api-url attribute and prepend it to every fetch.

- Drop [Required] from AddBlogCommentRequest. The framework's automatic
  model-state validation was short-circuiting before the controller's
  own check ran, returning a `Content` (PascalCase) error key with the
  generic message instead of the stable `content` / `ContentRequired`
  enum the frontend's i18n map looks up. Controller now owns the
  emptiness check; MaxLength stays as a hard cap. Null-safe the trim.

- Wire PUBLIC_API_URL=http://localhost:5000 into root `test:e2e:build`
  via cross-env so the locally-served dist can reach the backend the
  same way CI builds it. Without this every authenticated E2E test
  silently 404s on /api/* against the static serve.

- Add E2E coverage for the signed-in blog flows: reaction toggle (add,
  pressed state, count +1; toggle off returns to start) and comment
  post (appears in list with display name, persists across reload).
  Pre-existing pages.spec.ts only covered the signed-out chrome.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add blog section with emoji reactions and comments

2 participants