summaryrefslogtreecommitdiff
path: root/Documentation/source/architecture
diff options
context:
space:
mode:
authorBertrand Yuan <189593334+bertyuan@users.noreply.github.com>2026-04-27 20:54:16 +0800
committerGitHub <noreply@github.com>2026-04-27 20:54:16 +0800
commit85b6fb59db5fe1112c58eff9d02ae4f9c9b6456d (patch)
tree8c83fc84bd9547630e6733929dec77e102e055a8 /Documentation/source/architecture
parent658798b3a2378bb6df16cfbb16d707c6fb719e1e (diff)
parentdbb5e791f0c228369605d126dd590962ebe1eddc (diff)
Merge pull request #21 from bertyuan/Documentationv1.2
docs: add comprehensive documentation for operations
Diffstat (limited to 'Documentation/source/architecture')
-rw-r--r--Documentation/source/architecture/content-model.rst65
-rw-r--r--Documentation/source/architecture/data-and-auth.rst72
-rw-r--r--Documentation/source/architecture/distribution.rst45
-rw-r--r--Documentation/source/architecture/index.rst15
-rw-r--r--Documentation/source/architecture/risks.rst55
-rw-r--r--Documentation/source/architecture/system-overview.rst61
6 files changed, 313 insertions, 0 deletions
diff --git a/Documentation/source/architecture/content-model.rst b/Documentation/source/architecture/content-model.rst
new file mode 100644
index 0000000..12ed092
--- /dev/null
+++ b/Documentation/source/architecture/content-model.rst
@@ -0,0 +1,65 @@
+Content Model
+=============
+
+Payload CMS is configured in ``payload.config.ts`` and collection definitions
+live under ``src/payload/collections``.
+
+Collections
+-----------
+
+.. list-table::
+ :header-rows: 1
+ :widths: 18 58
+
+ * - Collection
+ - Purpose
+ * - ``Posts``
+ - Blog entries with slug, summary, rich text body, status, tags,
+ publication time, author, and optional featured image.
+ * - ``Users``
+ - Admin users for Payload and shared user representation.
+ * - ``Media``
+ - Uploaded images and assets used by posts and the admin.
+
+Post Lifecycle
+--------------
+
+Posts are authored in Payload and exposed publicly only when their status is
+``published``. Public queries in ``src/lib/payload-posts.ts`` consistently
+filter on this status before returning data to pages, feeds, search, or tag
+views.
+
+The normalized frontend shape is ``BlogPost``. It includes:
+
+* ``id`` and ``slug`` for stable identity;
+* ``url`` for public route generation;
+* ``title`` and ``description`` for listing and metadata;
+* ``content`` for Lexical rich text rendering;
+* ``image`` for cover or preview assets;
+* ``author`` and dates for byline display;
+* ``tags`` for topic navigation.
+
+Tag Strategy
+------------
+
+Tags are currently embedded in post documents and counted in memory by
+``getAllTags``. This is simple and sufficient for a small blog. If content
+volume grows, tag aggregation should move closer to the database or a cached
+index.
+
+MDX Content
+-----------
+
+The ``content`` directory is processed by Fumadocs through ``source.config.ts``.
+Its frontmatter includes date, author, tags, and optional image fields. Treat
+MDX content as local authored pages, distinct from Payload-managed posts.
+
+Generated Types
+---------------
+
+Payload writes generated TypeScript types to ``payload-types.ts``. Regenerate
+them after collection schema changes:
+
+.. code-block:: bash
+
+ pnpm payload:generate
diff --git a/Documentation/source/architecture/data-and-auth.rst b/Documentation/source/architecture/data-and-auth.rst
new file mode 100644
index 0000000..44736bd
--- /dev/null
+++ b/Documentation/source/architecture/data-and-auth.rst
@@ -0,0 +1,72 @@
+Data, Auth, and Comments
+========================
+
+Database Ownership
+------------------
+
+The repository uses PostgreSQL for both CMS data and application-owned data.
+The separation is logical:
+
+* Payload stores CMS data in the ``payload`` schema.
+* Application auth and comments use Drizzle schema objects and ``blog_*``
+ tables.
+
+This keeps CMS-managed structures separate from reader interaction data and
+allows application migrations to evolve without coupling every change to
+Payload internals.
+
+Authentication
+--------------
+
+Authentication is configured in ``src/server/auth/index.ts`` with better-auth
+and the Drizzle adapter. Current providers:
+
+* Google OAuth;
+* GitHub OAuth.
+
+The user model includes an additional ``role`` field with a default value of
+``user``. Session lookup is wrapped by ``getSession`` so route and component
+code does not need to call the better-auth API directly.
+
+Comments
+--------
+
+Comments are configured in ``src/server/comments/config.ts``. The Fuma Comments
+storage adapter receives the Drizzle database connection and these schemas:
+
+* ``comments``;
+* ``rates``;
+* ``roles``;
+* ``users``.
+
+The comment API route is mounted under ``/api/comments/[...comment]``. Frontend
+comment rendering belongs on post detail pages, while moderation and role
+policy should remain server-controlled.
+
+Migration Commands
+------------------
+
+.. list-table::
+ :header-rows: 1
+ :widths: 26 50
+
+ * - Command
+ - Use
+ * - ``pnpm db:generate``
+ - Generate Drizzle migration files after schema changes.
+ * - ``pnpm db:migrate``
+ - Apply Drizzle migrations.
+ * - ``pnpm db:push``
+ - Push schema changes directly during local development.
+ * - ``pnpm payload:migrate``
+ - Run Payload migrations.
+ * - ``pnpm payload:migrate:create``
+ - Create a new Payload migration.
+
+Implementation Rule
+-------------------
+
+When adding data-backed features, decide first whether the data is editorial or
+application-owned. Editorial content should be modeled in Payload collections.
+Account, interaction, moderation, and operational records should be modeled in
+Drizzle.
diff --git a/Documentation/source/architecture/distribution.rst b/Documentation/source/architecture/distribution.rst
new file mode 100644
index 0000000..80b87d0
--- /dev/null
+++ b/Documentation/source/architecture/distribution.rst
@@ -0,0 +1,45 @@
+Search, Feeds, and Metadata
+===========================
+
+The application exposes several distribution surfaces in addition to HTML
+pages. These endpoints help readers, search clients, crawlers, and social
+previews consume the site.
+
+Search
+------
+
+The search index endpoint lives at ``src/app/(main)/api/search/route.ts`` and
+is served from ``/api/search``. It is intended for the Fumadocs search UI and
+should return normalized, public, published content only.
+
+Feeds
+-----
+
+The feed route lives at ``src/app/(main)/rss.xml/route.ts`` and is exposed as
+``/rss.xml``. Keep this canonical path consistent in page metadata, sitemap
+configuration, and external documentation.
+
+Open Graph Images
+-----------------
+
+Dynamic image generation is implemented under:
+
+* ``src/app/(main)/banner.png`` for site-level banner output;
+* ``src/app/(main)/og/[...slug]`` for post-specific Open Graph output.
+
+The font JSON assets in these folders support image rendering. Treat them as
+runtime assets for the route handlers.
+
+Metadata
+--------
+
+Metadata helpers live in ``src/lib/metadata.ts`` and page-level JSON-LD
+rendering is handled by ``src/components/json-ld.tsx``. New public routes
+should define metadata intentionally instead of relying on generic defaults.
+
+Sitemap
+-------
+
+``next-sitemap.config.cjs`` controls sitemap generation after application
+builds. Generated ``public/robots.txt`` and ``public/sitemap*.xml`` are ignored
+by Git and should be regenerated by the build pipeline.
diff --git a/Documentation/source/architecture/index.rst b/Documentation/source/architecture/index.rst
new file mode 100644
index 0000000..34d2630
--- /dev/null
+++ b/Documentation/source/architecture/index.rst
@@ -0,0 +1,15 @@
+Architecture
+============
+
+This section documents how the major subsystems fit together: the public
+frontend, Payload CMS, database-backed application services, content rendering,
+and distribution endpoints.
+
+.. toctree::
+ :maxdepth: 2
+
+ system-overview
+ content-model
+ data-and-auth
+ distribution
+ risks
diff --git a/Documentation/source/architecture/risks.rst b/Documentation/source/architecture/risks.rst
new file mode 100644
index 0000000..604bc78
--- /dev/null
+++ b/Documentation/source/architecture/risks.rst
@@ -0,0 +1,55 @@
+Risks and Technical Debt
+========================
+
+This page tracks known architectural risks and follow-up work. Keep entries
+short, actionable, and tied to repository evidence.
+
+Current Risk Register
+---------------------
+
+.. list-table::
+ :header-rows: 1
+ :widths: 10 26 28 30
+
+ * - Priority
+ - Finding
+ - Evidence
+ - Recommendation
+ * - P0
+ - Email utility type dependency should be verified.
+ - ``src/lib/resend.ts`` imports project-local types.
+ - Keep type sources explicit and covered by type checks.
+ * - P0
+ - Payload secret fallback is unsafe for production.
+ - ``payload.config.ts`` falls back to ``your-secret-key``.
+ - Fail fast in production when ``PAYLOAD_SECRET`` is missing.
+ * - P1
+ - RSS canonical path must stay consistent.
+ - Route is ``/rss.xml``.
+ - Use the same path in metadata, README, docs, and sitemap config.
+ * - P1
+ - In-memory aggregation can become expensive.
+ - Search, RSS, and tag counts read batches of posts.
+ - Add caching or database-side aggregation when content volume grows.
+ * - P1
+ - Canonical URL configuration needs production hardening.
+ - Deployment URL handling depends on environment conventions.
+ - Prefer an explicit public site URL for production.
+ * - P2
+ - Comment role governance needs policy tests.
+ - Comment storage includes roles.
+ - Define moderation behavior before adding privileged comment actions.
+
+Roadmap
+-------
+
+Phase 1: correctness and configuration hardening
+ Fix production secret handling, verify email types, and align feed paths.
+
+Phase 2: scalability
+ Cache expensive public indexes, move tag aggregation closer to storage, and
+ add clear revalidation boundaries.
+
+Phase 3: governance
+ Add architecture decision records, route-level integration tests, and CI
+ checks for type safety, tests, linting, and documentation links.
diff --git a/Documentation/source/architecture/system-overview.rst b/Documentation/source/architecture/system-overview.rst
new file mode 100644
index 0000000..c876096
--- /dev/null
+++ b/Documentation/source/architecture/system-overview.rst
@@ -0,0 +1,61 @@
+System Overview
+===============
+
+Layered View
+------------
+
+.. list-table::
+ :header-rows: 1
+ :widths: 22 34 44
+
+ * - Layer
+ - Main Files
+ - Responsibility
+ * - Presentation
+ - ``src/app/(main)``, ``src/components``
+ - Render public pages, route handlers, shared UI, rich text, and layout.
+ * - Content access
+ - ``src/lib/payload-posts.ts``
+ - Query Payload and normalize CMS documents into frontend-friendly types.
+ * - CMS admin
+ - ``payload.config.ts``, ``src/payload``, ``src/app/(payload)``
+ - Define editorial collections and expose the admin experience.
+ * - Auth and comments
+ - ``src/server/auth``, ``src/server/comments``
+ - Authenticate users and persist comment data.
+ * - Data
+ - ``src/server/db``, ``src/migrations``
+ - Own Drizzle schema, connection setup, and application migrations.
+ * - Content pipeline
+ - ``content``, ``source.config.ts``
+ - Build local MDX content through Fumadocs.
+
+Request Flow: Reading a Post
+----------------------------
+
+1. A route under ``src/app/(main)/(home)/posts/[slug]`` receives a slug.
+2. The server component calls ``getPostBySlug``.
+3. ``src/lib/payload-posts.ts`` obtains a Payload client and queries
+ ``posts`` where ``status`` is ``published``.
+4. The raw Payload document is transformed into ``BlogPost``.
+5. The page renders rich text content and mounts client-side interactions such
+ as sharing and comments.
+
+Request Flow: Writing a Comment
+-------------------------------
+
+1. The post detail page renders the Fuma Comments client component.
+2. The client calls ``/api/comments/[...comment]``.
+3. The route delegates to the Fuma Comments Next.js handler.
+4. better-auth validates the session.
+5. The Drizzle adapter writes comment, rate, and role data to application-owned
+ tables.
+
+Boundaries
+----------
+
+Payload owns editorial content and media metadata. The application owns auth,
+comments, UI state, route behavior, and integration logic. Keep that boundary
+clear when adding features: editorial fields belong in Payload collections;
+reader interaction and account behavior belong in ``src/server`` and the app
+routes.