300 lines
7.7 KiB
Markdown
300 lines
7.7 KiB
Markdown
VOYAGE – Blog & Admin Architecture Documentation
|
||
===============================================
|
||
|
||
Purpose
|
||
-------
|
||
This document describes the architecture of the VOYAGE public blog
|
||
and its internal admin system.
|
||
|
||
It covers folder structure, routing, data flow, authentication,
|
||
and the admin UI shell.
|
||
|
||
A new developer should be able to understand:
|
||
- how blog posts are loaded and rendered
|
||
- how routing works (public + admin)
|
||
- how authentication is handled
|
||
- where and how to extend the system
|
||
|
||
|
||
High-Level Overview
|
||
-------------------
|
||
The VOYAGE public site is implemented using the **Next.js App Router**
|
||
inside a monorepo, with authentication delegated to a **Spring Boot backend**.
|
||
|
||
Key technologies:
|
||
- Next.js (App Router, Server Components)
|
||
- Spring Boot (session-based auth)
|
||
- Tailwind CSS (UI styling)
|
||
- File-based content (TXT / MD / MDX)
|
||
|
||
Core principles:
|
||
- Public content is always accessible
|
||
- Admin tools are protected via backend session
|
||
- No duplicate authentication logic
|
||
- Clear separation between public site and admin system
|
||
|
||
|
||
Monorepo Context
|
||
----------------
|
||
This repository is a monorepo containing multiple applications.
|
||
|
||
Relevant apps:
|
||
- apps/public-web → Public website, blog, and admin UI
|
||
- apps/workspace-api → Spring Boot backend (auth, API, DB)
|
||
|
||
Other apps (not covered here):
|
||
- workspace-ui
|
||
|
||
|
||
Folder Structure (Current)
|
||
--------------------------
|
||
|
||
apps/public-web/
|
||
│
|
||
├── app/
|
||
│ ├── (site)/ → Public site route group
|
||
│ │ ├── layout.tsx → Shared public layout (TopBar, globals)
|
||
│ │ ├── page.tsx → Homepage
|
||
│ │ ├── about/
|
||
│ │ │ └── page.tsx → Static About page
|
||
│ │ ├── blog/
|
||
│ │ │ ├── layout.tsx → Blog-specific layout
|
||
│ │ │ ├── page.tsx → Blog index (list of posts)
|
||
│ │ │ └── [slug]/
|
||
│ │ │ └── page.tsx → Dynamic blog post page
|
||
│ │ └── admin/
|
||
│ │ ├── layout.tsx → Admin auth guard (server-side)
|
||
│ │ ├── page.tsx → Admin dashboard (sidebar + header)
|
||
│ │ └── posts/
|
||
│ │ └── page.tsx → Admin posts manager
|
||
│ │
|
||
│ ├── global.css → Tailwind + global styles
|
||
│ └── layout.tsx → Root layout (imports global.css)
|
||
│
|
||
├── components/
|
||
│ └── shell/
|
||
│ └── TopBar.tsx → Shared public navigation bar
|
||
│
|
||
├── visuals/
|
||
│ └── ImageSphereSketch.tsx → Creative / visual components
|
||
│
|
||
├── content/
|
||
│ └── posts/
|
||
│ ├── YY-MM-DD-title.txt → Blog post source files (current)
|
||
│ ├── YYYY-MM-DD-title.md → (optional / supported)
|
||
│ └── YYYY-MM-DD-title.mdx → (optional / supported)
|
||
│
|
||
├── 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.
|
||
|
||
Public routes:
|
||
- / → app/(site)/page.tsx
|
||
- /about → app/(site)/about/page.tsx
|
||
- /blog → app/(site)/blog/page.tsx
|
||
- /blog/[slug] → app/(site)/blog/[slug]/page.tsx
|
||
|
||
Admin routes:
|
||
- /admin → Admin dashboard (protected)
|
||
- /admin/posts → Admin post manager (protected)
|
||
|
||
Dynamic route parameters:
|
||
- `[slug]` is derived from the filename in `content/posts`
|
||
|
||
|
||
Blog Data Flow (Rendering a Post)
|
||
---------------------------------
|
||
|
||
1. User navigates to:
|
||
/blog/some-post-slug
|
||
|
||
2. Next.js resolves:
|
||
app/(site)/blog/[slug]/page.tsx
|
||
|
||
3. Page receives:
|
||
params.slug
|
||
|
||
4. The page calls:
|
||
getPostBySlug(slug) from lib/posts.ts
|
||
|
||
5. lib/posts.ts:
|
||
- Reads a file from content/posts
|
||
- Supports .txt, .md, .mdx
|
||
- Strips date prefix from filename
|
||
- Returns structured post data
|
||
|
||
6. Page renders:
|
||
- Shared public layout
|
||
- Post metadata
|
||
- Parsed content
|
||
|
||
7. If the slug does not exist:
|
||
- notFound() is triggered
|
||
|
||
|
||
Admin System Overview
|
||
---------------------
|
||
|
||
The admin system is an **internal tool**, not a CMS yet.
|
||
|
||
Current capabilities:
|
||
- Admin dashboard UI
|
||
- Post listing / detection
|
||
- Backend session verification
|
||
- Logout handling
|
||
|
||
Admin UI principles:
|
||
- Clean, minimal, internal-tool aesthetic
|
||
- Sidebar + large system header
|
||
- No duplicated navigation actions
|
||
- Tailwind-based styling
|
||
|
||
|
||
Admin Authentication & Security
|
||
--------------------------------
|
||
|
||
Authentication is **not handled by Next.js**.
|
||
|
||
Instead, it is delegated to the Spring Boot backend (`workspace-api`).
|
||
|
||
Key principles:
|
||
- Single source of truth for auth (Spring)
|
||
- Session-based authentication (JSESSIONID)
|
||
- No JWT
|
||
- No duplicate auth logic in frontend
|
||
- Admin routes require a valid backend session
|
||
|
||
|
||
Admin Route Protection (Frontend)
|
||
---------------------------------
|
||
|
||
Protection is implemented in:
|
||
|
||
app/(site)/admin/layout.tsx
|
||
|
||
Strategy:
|
||
- Admin layout is an async Server Component
|
||
- On every request:
|
||
- Calls backend endpoint `/api/me`
|
||
- Forwards incoming cookies manually
|
||
- If response is 401 → redirect to /login
|
||
- If response is 200 → render admin UI
|
||
- Optional role check (ROLE_ADMIN)
|
||
|
||
Important technical detail:
|
||
- Server-side fetch MUST forward cookies explicitly
|
||
- Using headers().get("cookie") (async)
|
||
- credentials: "include" is NOT sufficient in Server Components
|
||
|
||
|
||
Login Flow (End-to-End)
|
||
----------------------
|
||
|
||
1. User navigates to:
|
||
http://localhost:3000/admin
|
||
|
||
2. Admin layout fetches:
|
||
GET http://localhost:8080/api/me
|
||
|
||
3. If not authenticated:
|
||
- Backend returns 401
|
||
- Next.js redirects to /login (frontend)
|
||
|
||
4. Frontend /login redirects to backend:
|
||
GET http://localhost:8080/login-redirect?redirect=http://localhost:3000/admin
|
||
|
||
5. Backend:
|
||
- Stores redirect target in session
|
||
- Redirects to /login (no query params)
|
||
|
||
6. Spring Security default login page is shown
|
||
|
||
7. User submits credentials
|
||
|
||
8. On successful login:
|
||
- Custom success handler reads redirect from session
|
||
- Redirects user back to:
|
||
http://localhost:3000/admin
|
||
|
||
9. Admin UI loads successfully
|
||
|
||
|
||
Backend Endpoints Involved
|
||
-------------------------
|
||
|
||
/api/me
|
||
- Returns current authenticated user
|
||
- 200 → logged in
|
||
- 401 → not authenticated
|
||
- Never redirects
|
||
|
||
/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
|
||
|
||
/logout
|
||
- Invalidates session
|
||
- Clears JSESSIONID cookie
|
||
|
||
|
||
Admin Posts Manager
|
||
-------------------
|
||
|
||
Route:
|
||
- /admin/posts
|
||
|
||
Responsibilities:
|
||
- Reads files from content/posts on the server
|
||
- Detects available posts (.txt / .md / .mdx)
|
||
- Derives slugs from filenames
|
||
- Displays debug info (detected paths, files)
|
||
- Links to public blog pages
|
||
|
||
This is intentionally read-only for now.
|
||
|
||
|
||
Why This Architecture
|
||
---------------------
|
||
- Clear separation of concerns
|
||
- Public content stays simple and fast
|
||
- Admin tools are protected and internal
|
||
- No auth duplication
|
||
- SSR-safe and production-ready
|
||
- Scales cleanly to future CMS features
|
||
|
||
|
||
How to Extend (Future)
|
||
----------------------
|
||
- Admin post editor UI
|
||
- Draft / preview mode
|
||
- Role-based admin tools
|
||
- Publishing workflow
|
||
- CMS integration
|
||
- Protected preview links
|
||
|
||
|
||
Current Status
|
||
--------------
|
||
- App Router fully set up
|
||
- Public blog routing stable
|
||
- Content loading via filesystem stable
|
||
- Tailwind styling active
|
||
- Admin auth flow stable and tested
|
||
- Admin dashboard + posts manager implemented
|
||
- Ready for further UX polish and feature extensions |