Skip to content

Latest commit

Β 

History

History
1120 lines (859 loc) Β· 45.3 KB

File metadata and controls

1120 lines (859 loc) Β· 45.3 KB

ReactPress System Architecture

ReactPress 4.0 β€” modern full-stack CMS / blog publishing platform on React
Core principle: Admin manages content Β· Themes manage presentation Β· Plugins manage logic Β· API manages data Β· Toolkit manages contracts


Table of contents


1. Overview

ReactPress is a WordPress-like content platform with a clear separation of concerns:

Domain Capabilities
Content Articles, categories, tags, comments, static pages
Media Upload, media library, storage (local / OSS)
Appearance Theme install / activate / preview, site customization
Extensions Plugin install / enable / configure
System Users & permissions, site settings, import/export, analytics

Non-functional goals

Goal Target
Admin responsiveness Shell stays mounted; route switches feel < 100ms; list views cache on revisit
Visitor SEO Core pages SSR/ISR; Lighthouse SEO β‰₯ 90
Multi-device One responsive web app for desktop / tablet / mobile
Data consistency All frontends access API only through toolkit

2. Architecture

ReactPress uses a Monorepo + multi-process model: content management, visitor presentation, and API services are decoupled; toolkit keeps types and contracts aligned.

flowchart TB
  subgraph Presentation["Presentation (replaceable, extensible)"]
    Web["web β€” Admin SPA"]
    Desktop["desktop β€” Electron shell (loads web/dist)"]
    Theme["themes/* β€” Visitor SSR"]
    PluginUI["plugins/*/src/admin β€” Plugin Admin slots"]
  end

  subgraph Contract["Contract (stable, shared)"]
    Toolkit["toolkit<br/>api Β· types Β· react Β· admin Β· theme Β· plugin"]
  end

  subgraph Platform["Platform (cannot be bypassed)"]
    Server["server β€” REST Β· auth Β· Hook Β· extension registry"]
    CLI["cli β€” process orchestration Β· scaffolding"]
    DB[(MySQL / SQLite)]
  end

  Web --> Toolkit
  Desktop --> Web
  Theme --> Toolkit
  PluginUI --> Toolkit
  Toolkit -->|HTTP /api| Server
  Server -->|Hook| PluginServer["plugins/*/dist β€” Server modules"]
  Server --> DB
  CLI --> Web
  CLI --> Theme
  CLI --> Server
  CLI --> Desktop
Loading

Responsibility matrix

Package Single responsibility Rendering SEO
server Business rules, persistence, auth, extension lifecycle β€” β€”
web Admin UI Vite CSR SPA No
themes/ Visitor site Next.js SSR/SSG/ISR Yes
toolkit API client, types, React integration, extension schemas β€” β€”
plugins/ Incremental logic (Hook + optional Admin UI) Server + Admin slots Plugin-dependent
desktop/ Electron shell, local API orchestration, IPC Loads web/dist No
cli Local dev / deploy orchestration β€” β€”
docs Project docs (Docusaurus) SSG β€”

Architecture red lines

  • No visitor pages in Admin; no admin routes in themes (new themes must follow this)
  • All frontends (web / themes / plugins) depend on toolkit only for API access
  • server must not depend on any frontend package

3. Design principles & decisions

All trade-offs follow this priority:

flowchart LR
  M[Maintainability] --> E[Extensibility]
  E --> T[Technical fit]
  T --> C[Low cost]
Loading
Principle Meaning How it lands
Maintainability Change one place, test one place, clear boundaries Layering + Feature Modules + single API client + OpenAPI codegen
Extensibility Core changes rarely; third parties can attach Registry + Hook + manifest contracts
Technical fit Match tech to scenario; avoid stack bloat Admin = SPA, public pages = SSR, business logic in Server
Low cost Few processes, few repos, little duplication Monorepo + shared toolkit; responsive web instead of native apps

Key decision summary

Decision Choice Maintainability Extensibility Fit Cost
API access toolkit as sole entry β˜…β˜…β˜… β˜…β˜… β˜…β˜…β˜… Low
Admin Vite SPA β˜…β˜… β˜…β˜… β˜…β˜…β˜… Low
Visitor Next.js SSR/ISR β˜…β˜… β˜…β˜…β˜… β˜…β˜…β˜… Medium
Module layout Feature Module + Registry β˜…β˜…β˜… β˜…β˜…β˜… β˜…β˜… Low
Plugins Hook + manifest β˜…β˜… β˜…β˜…β˜… β˜…β˜…β˜… Medium
Themes Separate process + theme.json β˜…β˜… β˜…β˜…β˜… β˜…β˜…β˜… Medium
List state URL searchParams β˜…β˜…β˜… β˜…β˜… β˜…β˜…β˜… Low
Multi-device Responsive web + Electron shell β˜…β˜…β˜… β˜…β˜… β˜…β˜…β˜… Medium
Types OpenAPI codegen β˜…β˜…β˜… β˜…β˜… β˜…β˜…β˜… Low

4. Monorepo package structure

Managed with pnpm workspace (pnpm-workspace.yaml):

packages:
  - 'cli' # Global CLI (@fecommunity/reactpress)
  - 'server' # NestJS API
  - 'web' # Admin SPA
  - 'desktop' # Electron shell
  - 'docs' # Docusaurus docs site
  - 'toolkit' # Shared API contract layer
  - 'themes' # Theme registry
  - 'themes/*' # Official theme templates
  - 'plugins' # Plugin registry
  - 'plugins/*' # Official plugins

Repository tree (core)

reactpress/
β”œβ”€β”€ cli/                 # CLI with bundled server
β”œβ”€β”€ server/              # NestJS API source
β”œβ”€β”€ web/                 # Vite Admin SPA
β”œβ”€β”€ desktop/             # Electron (local SQLite + Admin SPA)
β”œβ”€β”€ toolkit/             # OpenAPI SDK + React integration
β”œβ”€β”€ themes/
β”‚   β”œβ”€β”€ hello-world/     # Starter theme (local)
β”‚   └── theme-starter/   # npm official theme catalog anchor
β”œβ”€β”€ plugins/
β”‚   β”œβ”€β”€ hello-world/     # Auto summary plugin
β”‚   β”œβ”€β”€ seo/             # SEO enhancement
β”‚   └── image-optimizer/ # Image batch optimization
β”œβ”€β”€ docs/                # Docusaurus
β”œβ”€β”€ public/              # Marketing / brand assets
β”œβ”€β”€ scripts/             # Build, deploy, smoke tests
β”œβ”€β”€ docker-compose.*.yml
β”œβ”€β”€ nginx*.conf
β”œβ”€β”€ .reactpress/         # Runtime: active-theme.json, runtime/, plugins/
└── package.json

npm package mapping

Directory npm package Notes
cli/ @fecommunity/reactpress 4.0 main package; global reactpress command
web/ @fecommunity/reactpress-web Admin SPA
server/ @fecommunity/reactpress-server Monorepo source; standalone npm deprecated β€” use CLI bundled API
toolkit/ @fecommunity/reactpress-toolkit Shared SDK
themes/hello-world @fecommunity/reactpress-template-hello-world Starter theme

5. Runtime & ports

CLI orchestrates independent processes in local development:

Process Default port Stack Notes
web 3000 Vite + React Admin entry
active theme 3001 Next.js Current visitor theme
server 3002 NestJS REST API (prefix /api)
preview theme 3003 Next.js Admin iframe preview of non-active theme
MySQL 3306 MySQL 5.7 Full-stack dev / default production persistence
desktop local API 3002 NestJS + SQLite Embedded API in pnpm dev:desktop (same port as standard API)
nginx (optional) 80 / 8080 nginx Unified reverse proxy

Three core processes (Admin, theme, API) deploy and scale independently β€” traffic patterns differ, so separation beats a monolithic Next app.

flowchart LR
  Browser["Browser"]
  Nginx["nginx :80"]
  Web["web :3000"]
  Theme["theme :3001"]
  Preview["preview :3003"]
  API["server :3002"]
  DB[(MySQL :3306)]

  Browser -->|"/admin"| Nginx
  Browser -->|"/"| Nginx
  Nginx -->|"/admin/"| Web
  Nginx -->|"/"| Theme
  Nginx -->|"/api"| API
  Web -->|"/api proxy"| API
  Theme -->|toolkit HTTP| API
  Preview -->|toolkit HTTP| API
  API --> DB
Loading

6. Data flow & dependency rules

Typical request paths

Admin write:

web page β†’ toolkit createClient() β†’ POST /api/article β†’ server ArticleService β†’ DB

Visitor read:

theme getServerSideProps β†’ toolkit fetchSingleArticle() β†’ GET /api/article/:id β†’ server β†’ DB

Theme management:

web Appearance β†’ GET /api/extension/themes β†’ server ThemeService
  β†’ Setting.globalSetting (activeTheme / mods)
  β†’ .reactpress/active-theme.json
  β†’ CLI restarts Next.js theme process

Sequence: publish article

sequenceDiagram
  participant U as Admin
  participant W as web SPA
  participant T as toolkit
  participant S as server
  participant H as Hook / plugin

  U->>W: Edit and publish
  W->>T: useMutation β†’ api.article.update
  T->>S: PUT /article/:id
  S->>H: applyFilters('article.beforePublish')
  H-->>S: mutated payload
  S->>S: persist
  S->>H: doAction('article.afterPublish')
  S-->>T: 200 + data
  T-->>W: invalidateQueries
  W-->>U: success notification
Loading

Sequence: visitor article page

sequenceDiagram
  participant V as Visitor
  participant Th as theme Next.js
  participant T as toolkit/theme
  participant S as server

  V->>Th: GET /article/hello-world
  Th->>T: fetchArticle (SSR)
  T->>S: GET /article/by-slug/hello-world
  S-->>T: article + meta
  T-->>Th: typed data
  Th-->>V: full HTML + JSON-LD
Loading

Dependency rules (hard constraints)

web / themes / plugins  β†’  toolkit only
toolkit                 β†’  HTTP + stdlib only (no Ant Design / Next deps)
server                  β†’  no frontend packages
plugins/server          β†’  server Hook + DI interfaces only
cli                     β†’  orchestrates server/web/themes; not imported by business code

7. Maintainability

Single data entry: toolkit

Problem: Multiple hand-rolled HTTP layers β†’ type drift, inconsistent errors, N places to change on API updates.

Solution: One client factory for the whole platform:

export function createClient(options: ClientOptions) {
  const http = createHttpClient(options);
  return {
    article: new Article(http),
    file: new File(http),
    extension: new Extension(http),
    // … mirrors server controllers
  };
}

Benefits:

  • Server API change β†’ run codegen β†’ TypeScript errors pinpoint callers
  • Error codes, auth, retry logic written once
  • New modules (web / theme / plugin) with zero HTTP boilerplate

Feature Modules (vertical slices)

Each domain is self-contained:

web/src/modules/article/
β”œβ”€β”€ index.ts          # public export: register(admin)
β”œβ”€β”€ routes.tsx        # TanStack Router routes
β”œβ”€β”€ pages/            # thin pages composing hooks + components
β”œβ”€β”€ components/       # module-private UI
β”œβ”€β”€ hooks/            # data + URL state
β”œβ”€β”€ schemas/          # Zod form + API boundary validation
└── permissions.ts    # module permission declarations

Forbidden between modules: direct import of another module's internal components.
Allowed: Registry for menus/settings/permissions; toolkit hooks for shared server data.

URL as state

List filters, pagination, and sort live in URL searchParams:

/article?page=2&status=published&sort=-createdAt&keyword=react
Benefit Why
Shareable Admins copy links to restore views
Testable E2E does not depend on component state
Cacheable React Query uses URL params as queryKey
Device-agnostic Desktop / mobile share the same data logic

Codegen boundaries

Generated (no hand edits) Hand-written
toolkit/api/* toolkit/react/hooks/*
toolkit/types/* toolkit/admin/components/*
OpenAPI spec Feature Module business UI

8. Extensibility

Modeled after WordPress add_action / add_filter, constrained by TypeScript manifests.

Extension model

Type Extends Carrier
Theme Visitor UI Independent Next.js package + theme.json
Plugin Business logic + optional Admin UI Server module + optional src/admin/index.ts

Hooks (in-process, can mutate) vs Webhooks (outbound HTTP) are separate.

Manifest contracts

theme.json (flat structure β€” templates at root):

{
  "id": "hello-world",
  "name": "Hello World",
  "version": "1.0.0",
  "requires": ">=3.5.0",
  "templates": {
    "home": "pages/index.tsx",
    "single": "pages/article/[id].tsx",
    "archive": "pages/category/[category].tsx"
  },
  "supports": { "menus": ["primary", "footer"], "darkMode": true }
}

plugin.json:

{
  "id": "seo",
  "name": "SEO Enhancement",
  "version": "1.0.0",
  "server": { "module": "./dist/index.js" },
  "admin": {
    "slots": { "subscribe": ["article.editor.meta.afterSummary"] }
  },
  "settings": { "schema": { "type": "object" } }
}

Schemas live in toolkit/extension; CLI validates on install β€” invalid packages fail at startup, not at runtime.

Server Hook

interface HookService {
  applyFilters<T>(name: string, value: T, ctx?: unknown): Promise<T>;
  doAction(name: string, payload?: unknown): Promise<void>;
}
Hook When
article.beforePublish Mutate fields before publish
article.afterPublish Notify, index after publish
comment.beforeCreate Spam filter
setting.beforeSave Validate extension config

Admin Registry

interface AdminModule {
  id: string;
  register(ctx: AdminContext): void;
}

interface AdminContext {
  menu: MenuRegistry;
  settings: SettingsRegistry;
  permissions: PermissionRegistry;
  routes: RouteRegistry;
}

Core modules and plugins use the same API β€” new official features = new module + register(), no Shell edits.

Theme switching strategy

Phase Strategy Rationale
MVP (current) Update activeTheme + restart theme process Simple, stable SSR, no runtime federation
Later Hot swap / multi-theme preview Only when product requires it

Permission model

type Permission =
  | 'article:read'
  | 'article:write'
  | 'article:publish'
  | 'media:manage'
  | 'page:manage'
  | 'user:manage'
  | 'setting:manage'
  | 'extension:manage';
  • Server: Guard checks JWT + Permission
  • Web: usePermission() + route-level <AuthGuard permission="…" />
  • Plugins: manifest declares permissions; merged into roles on activate

String capabilities beat hard-coded role === 'admin'.

Plugin three-layer model

Layer Path Role
Registry plugins/ + plugins/package.json What can be installed
Materialized .reactpress/plugins/{id}/ Installed copy with dist/
Active Setting.globalSetting.plugins Enabled list + per-plugin config
Action Effect
Install Materialize to .reactpress/plugins/
Enable Hot-load server.module β†’ register(hooks, ctx)
Disable Remove hooks; optional deactivate()
Config JSON Schema validation then reload

Built-in plugins: hello-world, seo, image-optimizer. See plugins/README.md.


9. Technical choices

Rendering by scenario

Scenario Tech Why
Admin Vite + React SPA No SEO; small CSR bundle, fast HMR, static deploy
Visitor theme Next.js SSR/SSG/ISR Crawlers and social previews need full HTML
API NestJS REST Mature modules; OpenAPI codegen chain

Not chosen:

Approach Why not
Admin on Next.js No SSR/RSC benefit; extra routing + server complexity
Admin + theme in one app Coupled responsibilities, bundle bloat, cannot deploy separately
GraphQL instead of REST Existing Swagger pipeline; GraphQL adds schema maintenance
Micro-frontends (qiankun, etc.) Team/scale mismatch; Registry + dynamic import is enough

Admin frontend stack

Layer Choice Role
Build Vite+ (vp dev/build) Fast dev, native ESM
Routing TanStack Router Type-safe, file routes, searchParams first-class
Server state TanStack Query Cache, retry, optimistic mutations
Client state Zustand (auth/settings only) Light persistence; avoid global store abuse
UI Ant Design 6 Complete admin components, responsive grid
Validation Zod Unified form + API boundary

State split: URL for list state Β· React Query for server data Β· Zustand for session/UI prefs.

Admin performance

Technique Mechanism
Persistent shell Layout route stays mounted; only <Outlet /> swaps
Route-level code split Each module is its own chunk
Lazy heavy deps Rich text, charts via React.lazy()
List cache staleTime: 30s for instant back-navigation
Prefetch Sidebar hover preloads next route chunk

Visitor SEO

Page type Mode
Home, article, archives ISR revalidate: 60
About, privacy SSG
Search SSR
Comment submit CSR island

toolkit/theme provides fetchArticle, buildPageMeta, buildJsonLd β€” theme authors call helpers, not SEO boilerplate.


10. Cost & multi-platform

Cost model

Cost type Control strategy
Development Monorepo + toolkit reuse; Feature Module templates for CRUD
Operations Admin static hosting; theme = standard Next deploy; API single process
Multi-device Responsive web β€” no native iOS/Android Admin
Extensions manifest + Registry β€” no core PR for third-party features
Learning curve Stack converges on React + Nest; theme authors need Next + toolkit only

Responsive web (one codebase, three viewports)

Breakpoints align with Ant Design (single standard across repo):

Breakpoint Width Admin Theme
< md < 768px Drawer nav; table β†’ cards Single column
md–lg 768–992px Collapsed sidebar Two columns
β‰₯ lg β‰₯ 992px Fixed sidebar + wide table Sidebar + main

Shared components in toolkit/admin: ResponsiveTable, ResponsiveFilterToolbar, ResponsiveFormModal.

Principle: API has no device fields; differences are UI-only.

Progressive path:

  1. Default: responsive web (zero extra engineering)
  2. Optional: Electron desktop (same web/dist)
  3. Optional: PWA caches shell static assets only
  4. Future: Capacitor wraps web/dist without rewriting UI

What we deliberately skip (cost control)

Skip Reason
Native mobile Admin app Responsive web covers most ops
Electron-embedded duplicate Admin UI Shell only loads web/dist
Plugin marketplace sandbox (v1) Local dir + admin trust model is enough
Theme runtime federation Separate process + restart is simpler
Multi-DB / multi-tenant (v1) Single-site CMS first
Custom ORM / UI library TypeORM + Ant Design

11. Server (backend API)

Stack

Layer Technology
Framework NestJS 6
ORM TypeORM 0.2
Database SQLite (default for reactpress init / desktop); MySQL optional (embedded-docker / external)
Auth Passport + JWT, API Key
Docs Swagger at /api
Other helmet, compression, rate-limit, log4js, nodemailer, ali-oss

Module layout

server/src/modules/
β”œβ”€β”€ article/ category/ tag/ comment/ page/ file/   # content
β”œβ”€β”€ user/ auth/                                      # identity
β”œβ”€β”€ setting/ smtp/                                   # config
β”œβ”€β”€ view/ search/ knowledge/                         # data
β”œβ”€β”€ extension/                                       # theme + plugin lifecycle
β”œβ”€β”€ hook/                                            # Action/Filter registry
β”œβ”€β”€ api-key/ webhook/ health/
└── …

Each module: thin controller β†’ service (business + Hook calls) β†’ entity. extension manages install/activate state, not domain business logic.

Domain entities (15)

User, Article, ArticleRevision, Category, Tag, Comment, Page, Knowledge, File, Setting, SMTP, Search, View, ApiKey, Webhook.

API conventions

  • Global prefix: /api (SERVER_API_PREFIX configurable)
  • Unified response: { statusCode, success, data } (except /health)

Startup paths

  1. First install: Express wizard in main.ts (/test-db, /install writes .env) β†’ NestJS bootstrap
  2. Normal / production: Direct starter.ts bootstrap

12. Web (admin SPA)

Stack

Category Technology
Build Vite+
UI React 18 + Ant Design 6
Routing TanStack Router (file routes)
Data TanStack Query + Zustand (auth persist)
Editor Monaco + Showdown (Markdown)
i18n i18next
Testing MSW + Playwright E2E

Directory structure

web/src/
β”œβ”€β”€ routes/              # TanStack file routes
β”‚   β”œβ”€β”€ login/
β”‚   └── _auth/           # authenticated routes
β”‚       β”œβ”€β”€ dashboard/ article/ media/ page/
β”‚       β”œβ”€β”€ appearance/ settings/ plugins/ data/
β”œβ”€β”€ modules/             # feature domains (mirror routes)
β”œβ”€β”€ shell/               # bootstrap, permissions, Admin Registry
β”œβ”€β”€ shared/ components/ mocks/ stores/ hooks/ i18n/

Admin route map

Module Route APIs
Dashboard / view, article stats
Articles /article, /article/editor/:id? article, category, tag
Comments /article/comment comment
Media /media file
Pages /page, /page/editor/:id? page
Appearance /appearance/themes, /appearance/customize extension, setting
Plugins /plugins, /plugins/:id/settings extension
Users /users, /profile user
Settings /settings/:tab setting, smtp, api-key, webhook
Data /data/analytics, /data/export, /data/import view, search, export

Settings use routes (not tab query params). Plugins insert tabs via settings.registerTab({ id, title, path, permission }).

Module registration example

export const articleModule: AdminModule = {
  id: 'article',
  register({ menu, permissions }) {
    menu.register({
      id: 'content',
      title: 'Content',
      children: [
        { id: 'article.list', title: 'Articles', path: '/article' },
        { id: 'article.new', title: 'New article', path: '/article/editor' },
        { id: 'article.comment', title: 'Comments', path: '/article/comment' },
      ],
    });
    permissions.register(['article:read', 'article:write', 'article:publish']);
  },
};

Shell bootstrap() registers core modules, then loads active plugins. Menu order uses sort, not import order.

API connection

  • Dev: VITE_API_BASE_URL=/api, Vite proxy to :3002
  • Client: getToolkitClient() β†’ @fecommunity/reactpress-toolkit/react
  • Mock: VITE_AUTH_MODE=mock + MSW
  • Live: VITE_AUTH_MODE=server

13. Themes (visitor frontend)

Since 3.0, visitor sites are independent Next.js packages under themes/ (replacing legacy client/).

Package structure (hello-world)

themes/hello-world/
β”œβ”€β”€ theme.json           # manifest (id, templates, customizer)
β”œβ”€β”€ pages/_app.tsx       # createThemeApp(manifest)
β”œβ”€β”€ pages/index.tsx, article/[id].tsx, …
β”œβ”€β”€ components/ styles/ next.config.js

WordPress mapping

WordPress ReactPress
style.css header theme.json
functions.php pages/_app.tsx β†’ createThemeApp()
Template hierarchy theme.json β†’ templates + pages/*
Customizer appearance.sections + Formily + useThemeMod

Official themes

Theme Source Role
hello-world local Minimal Pages Router starter
reactpress-theme-starter npm (theme-starter anchor) Full theme: search, knowledge base, comments, dark mode

Theme lifecycle

sequenceDiagram
  participant Admin as web Admin
  participant API as server ThemeService
  participant FS as filesystem
  participant CLI as cli theme-dev
  participant Next as Next.js theme

  Admin->>API: POST /extension/themes/install
  API->>FS: copy themes/{id} β†’ .reactpress/runtime/{id}
  Admin->>API: POST /extension/themes/activate
  API->>FS: write active-theme.json
  CLI->>Next: start theme :3001
  Admin->>API: beginPreviewSession
  API->>FS: write preview-theme.json
  CLI->>Next: start preview :3003
Loading

See themes/README.md.


14. Toolkit (shared contract layer)

Single API contract layer for the platform; generated from server OpenAPI.

Structure

toolkit/src/
β”œβ”€β”€ api/ types/          # generated from Swagger
β”œβ”€β”€ react/               # createClient(), resolveApiBaseUrl(), runtime detection
β”œβ”€β”€ theme/ ui/           # SSR helpers, headless components
β”œβ”€β”€ admin/ plugin/       # Registry, plugin SDK
β”œβ”€β”€ extension/           # theme.json / plugin.json JSON Schema
β”œβ”€β”€ config/ utils/

Export paths

Path Use
@fecommunity/reactpress-toolkit Main entry
@fecommunity/reactpress-toolkit/react React client factory
@fecommunity/reactpress-toolkit/theme Theme SSR
@fecommunity/reactpress-toolkit/plugin/server Plugin Hook SDK
@fecommunity/reactpress-toolkit/plugin/admin Plugin Admin registration

Regenerate

pnpm run generate:swagger   # server β†’ swagger.json
pnpm run build:toolkit      # regenerate api/types

See toolkit/README.md.


15. CLI (orchestration)

Published as @fecommunity/reactpress β€” zero-config project lifecycle.

Core commands (global npm CLI)

Command Description
reactpress init Init project and start (SQLite by default)
reactpress doctor Diagnostics (Node, ports, DB, processes)
reactpress logs Tail API logs
reactpress stop Stop API and theme processes

Removed from the global CLI (use Admin UI or Monorepo scripts instead): dev, theme, plugin, build, start, config, db, desktop, …

Monorepo / contributor commands

Available from a repository checkout (via pnpm scripts / internal CLI):

Command / script Description
pnpm dev Full-stack development
pnpm build / pnpm pm2 Production build / start
reactpress theme add Install theme from npm catalog
reactpress plugin install Install plugin from registry
pnpm dev:desktop Desktop dev (SQLite + Admin + Electron)

CLI layout (4.0 TypeScript)

cli/
β”œβ”€β”€ bin/           # thin entry β†’ out/bin/
β”œβ”€β”€ src/           # TypeScript source
β”œβ”€β”€ out/           # compiled (gitignored)
β”‚   β”œβ”€β”€ bin/ core/ ui/
β”‚   └── lib/       # dev/build/docker/theme/plugin orchestration
β”œβ”€β”€ server/        # bundled NestJS runtime for npm
└── templates/     # init scaffolds

Server resolution: monorepo server/ if present, else cli/server/ bundled copy.

See cli/README.md.


16. Auth & security

JWT (admin / user sessions)

  • Login: POST /api/auth/login β†’ token (4h default)
  • Protected routes: @UseGuards(JwtAuthGuard) + Bearer
  • Roles: @Roles('admin') + RolesGuard (admin / visitor)

API Key (headless / integrations)

  • Header: X-API-Key or Authorization: Bearer <key>
  • Scopes: read / write

Other

  • GitHub OAuth: POST /api/auth/github
  • Passwords: bcrypt via User.comparePassword()

17. Configuration

.reactpress/config.json is the source of truth; .env is written on init. End-user default is SQLite (database.mode: embedded-sqlite). MySQL / Docker is optional.

File Purpose
.reactpress/config.json Project config
.reactpress/active-theme.json Active theme id
.reactpress/preview-theme.json Preview theme id
.reactpress/runtime/{id}/ Materialized theme copy
.reactpress/plugins/{id}/ Materialized plugin copy
.env DB, ports, secrets
Variable Default
SERVER_PORT 3002
REACTPRESS_API_URL http://localhost:3002/api

18. Deployment

Development (recommended)

  • App processes on host (pnpm dev)
  • Docker for MySQL + nginx only
  • nginx forwards to host via host.docker.internal

Production options

Mode Notes
PM2 pnpm build β†’ pnpm start
Docker MySQL container + nginx; API can run on host
Vercel Theme / Admin static deploy

nginx routes (dev)

Path Target
/ Theme :3001
/admin/ Admin :3000
/api API :3002

19. Local development

flowchart LR
  subgraph init [First time]
    A[pnpm install] --> B[reactpress init]
    B --> C[.env + .reactpress/config.json]
  end
  subgraph dev [Daily]
    D[pnpm dev] --> E[toolkit build]
    E --> F[server :3002]
    F --> G[web :3000]
    G --> H[theme :3001]
  end
  init --> dev
Loading
pnpm install
pnpm dev              # API + Admin + theme + MySQL
pnpm dev:web:local    # Admin + SQLite API (no Docker)
pnpm dev:desktop      # Electron + SQLite
pnpm build:plugins    # compile official plugins
pnpm build            # toolkit β†’ server β†’ web β†’ themes

After API changes:

pnpm run generate:swagger && pnpm run build:toolkit

20. Plugins

Themes handle presentation; plugins handle logic. Server-side Hooks extend business rules; optional Admin UI via slots.

Covered in Β§8 Extensibility and plugins/README.md.


21. Desktop client

Electron shell loads the same Admin SPA as the browser β€” no duplicate business UI.

flowchart TB
  subgraph DesktopApp["desktop/ β€” Electron"]
    Main[Main: window Β· tray Β· IPC Β· local API spawn]
    Preload[Preload: contextBridge]
    Renderer[Renderer: web/dist]
  end
  Renderer --> Toolkit[toolkit]
  Toolkit --> API[server :3002]
Loading
Layer Responsibility
web All Admin UI (same as browser)
toolkit API client, auth, getRuntime() / getDesktopApi()
desktop Main/Preload only: window, IPC, SQLite API spawn, config
server REST API; local mode spawned by Main, remote mode connects externally

Modes

Mode Description
Local (default) Main spawns embedded API (SQLite, default :3002); default admin / admin
Remote Connect to existing ReactPress API; sync local content to remote site

Load modes

Mode Use case
A. Bundled (production) file:// or custom protocol β†’ web/dist/index.html
B. Remote URL Load https://admin.example.com (enterprise intranet)
C. Dev http://localhost:3000 (Vite dev server)

Security (Electron)

Rule Required
contextIsolation: true Yes
nodeIntegration: false in renderer Yes
Preload whitelist IPC channels Yes
webSecurity: true Yes
Remote URL allowlist When using mode B

Desktop roadmap

Phase Content Status
D0 Scaffold; dev loads Vite; prod loads web/dist βœ… 4.0
D1 Local SQLite, remote API switch, login, macOS/Windows packages βœ… 4.0 MVP
D1+ Local β†’ remote content sync βœ… 4.0
D2 Tray, shortcuts, native notifications Planned
D3 electron-updater Planned

Why Electron over Tauri: mature updater/tray/builder ecosystem; Chromium matches Admin stack; shell is swappable β€” web + toolkit stay unchanged.

See desktop/README.md.


22. Evolution & roadmap

4.0 vs 3.x

3.x 4.0
No official plugin runtime Hook + manifest + Admin slots; built-in hello-world, seo, image-optimizer
Web Admin only Optional Electron desktop (SQLite local mode)
hello-world–centric themes npm catalog (theme-starter anchor)
CLI pure JS lib/ TypeScript src/ β†’ out/

3.0 vs 2.x

2.x 3.0
Monolithic client/ Next (incl. /admin) web/ Admin SPA + themes/ visitor
Multiple HTTP layers Unified toolkit client
Manual setup CLI init (auto-start)

Implementation phases (historical)

Step Deliverable Status
1–2 toolkit client + web Shell + auth βœ… 3.x
3–4 article module template + core CRUD βœ… 3.x
5–6 server extension/hook + appearance/plugins UI βœ… 3.x–4.0
7 theme.json + CLI theme commands βœ… 3.x
8–9 responsive components + import/export βœ… 3.x
10 Electron desktop shell βœ… 4.0 MVP
11 Plugin Hook + Admin slots βœ… 4.0

Known legacy / future work

  • Standalone server npm package deprecated β€” use CLI bundled API
  • dev:client script name retained; starts active theme
  • Plugin npm catalog, marketplace, Desktop auto-update β†’ future 4.x iterations

Feature coverage

Domain Status
Content, media, appearance, system settings βœ…
Plugins (Hook + Registry + Admin slots) βœ… 4.0
Desktop (Electron + SQLite) βœ… 4.0 MVP
Knowledge base βœ… server module

23. Acceptance criteria

Dimension Standard
Maintainability New CRUD module ≀ one directory + one register(); API changes = server + codegen only
Extensibility Official SEO plugin mounts menu + Hook without core edits
Performance Admin route switch < 100ms; theme Lighthouse SEO β‰₯ 90
Multi-device No horizontal scroll at 390px; core flows pass E2E on three viewports
Consistency web / themes / plugins have no custom HTTP clients
Desktop Packaged app shows same Admin as browser; no forked Admin source

24. References