7.4 KiB
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)
-
User navigates to: /blog/some-post-slug
-
Next.js resolves the route: app/(site)/blog/[slug]/page.tsx
-
The page component receives: params.slug
-
The page calls: getPostBySlug(slug) from lib/posts.ts
-
lib/posts.ts:
- Reads the corresponding MDX file from content/posts
- Parses frontmatter metadata (title, date, etc.)
- Returns structured post data
-
The page component renders:
- Shared layout (TopBar)
- Post metadata
- MDX content as React components
-
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
- Calls backend endpoint
Important detail:
- Server-side fetch must forward cookies explicitly
- credentials: "include" is NOT sufficient in Server Components
Login Flow (End-to-End)
-
User navigates to: http://localhost:3000/admin
-
Admin page fetches: GET http://localhost:8080/api/me
-
If not authenticated:
- Backend returns 401
- Next.js redirects to /login (frontend route)
-
Frontend /login page redirects to backend: GET http://localhost:8080/login-redirect?redirect=http://localhost:3000/admin
-
Backend:
- Stores redirect target in session
- Redirects to /login (without query params)
-
Spring Security default login page is shown
-
User submits credentials
-
On successful login:
- Custom successHandler reads redirect from session
- User is redirected to: http://localhost:3000/admin
-
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