Add blog with backend-backed comments, reactions, and RSS#110
Open
KaliCZ wants to merge 3 commits into
Open
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
[...lang]/blog/, sharedBlogPostLayout,/rss.xmlendpoint, 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.Kalandra.Blogbackend 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.BlogControllerwith[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.Test plan
BlogApiTestscover 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.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
/api/...directly. That works in dev (Vite proxies) but would break in production where the API is on a different origin. They now usePUBLIC_API_URLlike every other API caller in the codebase. Same fix applied to the localtest:e2e:buildscript so it builds withPUBLIC_API_URL=http://localhost:5000to match CI.[Required]fromAddBlogCommentRequest. Framework auto-validation was short-circuiting before the controller's own check, returning aContent(PascalCase) key with the generic message rather than the stablecontent/ContentRequiredthe frontend looks up.🤖 Generated with Claude Code