Skip to content

Add blog section with emoji reactions and comments #91

@KaliCZ

Description

@KaliCZ

Problem

No place on the site to publish longer-form writing. Blogging on Medium/dev.to hands content ownership and SEO to a third party, and a personal blog on the site reinforces it as an engineering showcase (the site itself is part of the portfolio).

Proposed change

Build blog posts as first-class Astro pages, not a CMS. Each post is its own .astro file under frontend/src/pages/[...lang]/blog/ with exported metadata (title, pubDate, updatedDate, summary, tags, draft flag). Git is the source of truth for post content.

Scope

  • Post pages: one .astro file per post, exporting metadata constants. Shared BlogPostLayout for nav, header, SEO tags.
  • Index page: /blog listing all non-draft posts, sorted by pubDate, using import.meta.glob over the posts directory.
  • RSS feed: /rss.xml endpoint fed from the same glob. Summary-only feed (description, not content:encoded) to drive readers to the site.
  • Reactions: emoji reactions per post (like / heart / insightful / etc.), backed by our own backend.
  • Comments: threaded comments per post, backed by our own backend.

Reactions + comments via our backend

Reactions and comments are served by a new module in our existing .NET backend, not a third-party widget. Rationale: the site is an engineering showcase and the backend is part of the portfolio — adding a domain that exercises event sourcing, auth, and a typed wire contract is on-theme. Giscus / Utterances were considered and rejected for that reason.

Shape of the backend module:

  • New Kalandra.Blog domain with append-only event streams per post slug for comments and reactions. Separate streams (UUIDv5 from slug) so high-volume reaction events don't share a replay path with comments.
  • Reaction toggle semantics decide add vs. remove from the replayed stream.
  • API: BlogController with [Authorize] on POST endpoints (only signed-in users comment or react) and anonymous GETs. Parallel API error enum to keep the wire contract stable.
  • Frontend wrappers: vanilla-JS BlogReactions and BlogComments Astro components that call the backend, with optimistic toggles and auth-dialog hooks for signed-out viewers.

Cross-posting

Each post is the canonical URL. Selective cross-posting to dev.to/Medium later with rel=canonical pointing back here is a follow-up, not part of this issue.

Files

  • frontend/src/pages/[...lang]/blog/index.astro — new, post list
  • frontend/src/pages/[...lang]/blog/<slug>.astro — new, one file per post
  • frontend/src/components/BlogPostLayout.astro — new, shared post chrome
  • frontend/src/pages/rss.xml.ts — new, RSS endpoint
  • frontend/src/components/BlogReactions.astro / BlogComments.astro — new, talk to the backend
  • frontend/src/blog/{posts.ts,types.ts} — new, metadata glob + contract
  • frontend/src/i18n/{cs,en}/blog.json — new, translations for blog UI chrome
  • backend/src/Kalandra.Blog/** — new domain module (events, commands, queries, registration)
  • backend/src/Kalandra.Api/** — new BlogController + DTOs

Out of scope

  • Sitemap handling (Generate sitemap from blog post metadata #92 — trivially reuses the same metadata glob once this lands)
  • Markdown/MDX authoring format (pure .astro pages are sufficient; MDX can be added later if authoring ergonomics suffer)
  • Cross-posting automation

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions