Files
voyage/apps/public-web/ARCHITECTURE.md

7.4 KiB
Raw Blame History

VOYAGE Blog Architecture Documentation

Purpose

This document describes the architecture of the VOYAGE public blog, including folder structure, routing, data flow, and rendering logic.

A new developer should be able to understand how blog posts are loaded, render routes work, and where to extend the system after reading this file.

High-Level Overview

The blog is implemented using the Next.js App Router with:

  • File-based routing
  • Dynamic route segments
  • MDX-based content
  • A shared public layout

Key principles:

  • Blog content lives outside the app router (content/posts)
  • Routing is derived from folder structure
  • Rendering happens in async server components
  • Shared UI is colocated in components/

Monorepo Context

This repository is a monorepo containing multiple applications.

Relevant app for the blog:

  • apps/public-web → Public website & blog

Other apps (not covered here):

  • workspace-api
  • workspace-ui

Folder Structure (Current)

apps/public-web/ │ ├── app/ │ ├── (site)/ → Public site route group │ │ ├── layout.tsx → Shared layout (TopBar, globals) │ │ ├── page.tsx → Homepage │ │ ├── about/ │ │ │ └── page.tsx → Static About page │ │ ├── blog/ │ │ │ ├── layout.tsx → Blog-specific layout (optional) │ │ │ ├── page.tsx → Blog index (list of posts) │ │ │ └── [slug]/ │ │ │ └── page.tsx → Dynamic blog post page │ │ └── admin/ │ │ └── page.tsx → Admin-only page (protected) │ │ │ ├── global.css → Global styles │ └── layout.tsx → Root layout │ ├── components/ │ └── shell/ │ └── TopBar.tsx → Shared navigation bar │ ├── visuals/ │ └── ImageSphereSketch.tsx → Creative / visual components │ ├── content/ │ └── posts/ │ ├── YYYY-MM-DD-title.mdx → Blog post source files │ └── ... │ ├── lib/ │ └── posts.ts → Blog data loading & parsing logic │ ├── public/ │ └── blog/ │ └── visuals/ │ ├── first.jpg │ ├── second.jpg │ └── ...

Routing Logic

Routing is defined entirely by the folder structure.

Static routes:

  • / → app/(site)/page.tsx
  • /about → app/(site)/about/page.tsx
  • /blog → app/(site)/blog/page.tsx

Dynamic routes:

  • /blog/[slug] → app/(site)/blog/[slug]/page.tsx

Admin route:

  • /admin → app/(site)/admin/page.tsx (protected)

The [slug] directory defines a dynamic route parameter that is passed to the page component as params.slug.

Data Flow (How a Blog Post Is Rendered)

  1. User navigates to: /blog/some-post-slug

  2. Next.js resolves the route: app/(site)/blog/[slug]/page.tsx

  3. The page component receives: params.slug

  4. The page calls: getPostBySlug(slug) from lib/posts.ts

  5. lib/posts.ts:

    • Reads the corresponding MDX file from content/posts
    • Parses frontmatter metadata (title, date, etc.)
    • Returns structured post data
  6. The page component renders:

    • Shared layout (TopBar)
    • Post metadata
    • MDX content as React components
  7. If the slug does not exist:

    • notFound() is triggered

Core Files Explained

app/(site)/blog/[slug]/page.tsx

  • Async Server Component
  • Receives dynamic params (slug)
  • Loads post data via lib/posts
  • Handles invalid slugs with notFound()
  • Renders full blog post view

app/(site)/blog/page.tsx

  • Blog index page
  • Loads all posts via lib/posts
  • Renders list / preview of posts

lib/posts.ts

  • Central data access layer for blog content
  • Handles file system access and MDX parsing
  • Keeps routing and rendering logic clean

content/posts/*.mdx

  • Source of truth for blog content
  • File name defines the slug
  • Frontmatter stores metadata
  • Body is rendered as MDX

components/shell/TopBar.tsx

  • Shared navigation component
  • Used across all public pages
  • Ensures consistent layout and navigation

public/blog/visuals/

  • Static images for blog posts
  • Served directly by Next.js
  • Referenced in MDX or page components

Admin Authentication & Security

The blog includes an admin-only section used for internal tools (e.g. editor preview, drafts, future CMS features).

Authentication is not handled by Next.js, but delegated to the existing Spring Boot backend (workspace-api).

Key principles:

  • Public blog remains fully accessible without login
  • Admin routes require a valid backend session
  • Session-based authentication (JSESSIONID)
  • No JWT, no duplicate auth system

Admin Route Protection (Frontend)

Route:

  • /admin → app/(site)/admin/page.tsx

Protection strategy:

  • Implemented as an async Server Component guard
  • On each request:
    • Calls backend endpoint /api/me
    • Forwards cookies manually
    • If response is 401 → redirect to /login
    • If response is 200 → render admin UI

Important detail:

  • Server-side fetch must forward cookies explicitly
  • credentials: "include" is NOT sufficient in Server Components

Login Flow (End-to-End)

  1. User navigates to: http://localhost:3000/admin

  2. Admin page fetches: GET http://localhost:8080/api/me

  3. If not authenticated:

    • Backend returns 401
    • Next.js redirects to /login (frontend route)
  4. Frontend /login page redirects to backend: GET http://localhost:8080/login-redirect?redirect=http://localhost:3000/admin

  5. Backend:

    • Stores redirect target in session
    • Redirects to /login (without query params)
  6. Spring Security default login page is shown

  7. User submits credentials

  8. On successful login:

  9. Admin page loads successfully

Backend Endpoints Involved

/api/me

  • Returns current authenticated user
  • 200 → logged in
  • 401 → not authenticated
  • Never redirects (API-safe)

/login

  • Spring Security default login page
  • HTML form-based login

/login-redirect

  • Helper endpoint
  • Stores redirect target in session
  • Avoids unsupported query params on /login

Why This Architecture

  • Single source of truth for authentication (Spring)
  • No duplicate auth logic in frontend
  • Clean separation:
    • Public content → no auth
    • Admin tools → backend session
  • Works with SSR and Server Components
  • Production-ready pattern for multi-app monorepos

How to Extend (Future)

  • Admin editor UI
  • Draft / preview mode
  • Role-based admin features
  • CMS integration
  • Protected preview links

Current Status

  • App Router fully set up
  • Dynamic blog slugs working
  • Shared public layout integrated
  • MDX content loading stable
  • Admin auth flow stable and tested
  • Ready for styling, animations, and feature extensions