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
- 1. Overview
- 2. Architecture
- 3. Design principles & decisions
- 4. Monorepo package structure
- 5. Runtime & ports
- 6. Data flow & dependency rules
- 7. Maintainability
- 8. Extensibility
- 9. Technical choices
- 10. Cost & multi-platform
- 11. Server (backend API)
- 12. Web (admin SPA)
- 13. Themes (visitor frontend)
- 14. Toolkit (shared contract layer)
- 15. CLI (orchestration)
- 16. Auth & security
- 17. Configuration
- 18. Deployment
- 19. Local development
- 20. Plugins
- 21. Desktop client
- 22. Evolution & roadmap
- 23. Acceptance criteria
- 24. References
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 |
| 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 |
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
| 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 | β |
- 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
All trade-offs follow this priority:
flowchart LR
M[Maintainability] --> E[Extensibility]
E --> T[Technical fit]
T --> C[Low cost]
| 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 |
| 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 |
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 pluginsreactpress/
βββ 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
| 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 |
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
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
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
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
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
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
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.
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 |
| Generated (no hand edits) | Hand-written |
|---|---|
toolkit/api/* |
toolkit/react/hooks/* |
toolkit/types/* |
toolkit/admin/components/* |
| OpenAPI spec | Feature Module business UI |
Modeled after WordPress add_action / add_filter, constrained by TypeScript manifests.
| 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.
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.
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 |
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.
| 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 |
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'.
| 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.
| 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 |
| 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.
| 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 |
| 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.
| 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 |
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:
- Default: responsive web (zero extra engineering)
- Optional: Electron desktop (same
web/dist) - Optional: PWA caches shell static assets only
- Future: Capacitor wraps
web/distwithout rewriting UI
| 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 |
| Layer | Technology |
|---|---|
| Framework | NestJS 6 |
| ORM | TypeORM 0.2 |
| Database | MySQL (default); SQLite for desktop local mode (DB_TYPE=sqlite) |
| Auth | Passport + JWT, API Key |
| Docs | Swagger at /api |
| Other | helmet, compression, rate-limit, log4js, nodemailer, ali-oss |
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.
User, Article, ArticleRevision, Category, Tag, Comment, Page, Knowledge, File, Setting, SMTP, Search, View, ApiKey, Webhook.
- Global prefix:
/api(SERVER_API_PREFIXconfigurable) - Unified response:
{ statusCode, success, data }(except/health)
- First install: Express wizard in
main.ts(/test-db,/installwrites.env) β NestJS bootstrap - Normal / production: Direct
starter.tsbootstrap
| 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 |
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/
| 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 }).
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.
- 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
Since 3.0, visitor sites are independent Next.js packages under themes/ (replacing legacy client/).
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 | ReactPress |
|---|---|
style.css header |
theme.json |
functions.php |
pages/_app.tsx β createThemeApp() |
| Template hierarchy | theme.json β templates + pages/* |
| Customizer | appearance.sections + Formily + useThemeMod |
| 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 |
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
See themes/README.md.
Single API contract layer for the platform; generated from server OpenAPI.
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/
| 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 |
pnpm run generate:swagger # server β swagger.json
pnpm run build:toolkit # regenerate api/typesSee toolkit/README.md.
Published as @fecommunity/reactpress β zero-config project lifecycle.
| Command | Description |
|---|---|
reactpress init |
Init project (.env + .reactpress/config.json; --local = SQLite) |
reactpress dev |
Full-stack dev (API + web + theme + Docker MySQL) |
reactpress dev --api-only |
API only (headless) |
reactpress dev --web-only |
Admin + API |
reactpress build / start |
Production build / start |
reactpress doctor / status |
Diagnostics / status |
reactpress plugin list/install |
Plugin registry |
reactpress theme list/add |
Theme catalog |
reactpress desktop dev |
Desktop dev (SQLite + Admin + Electron) |
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.
- Login:
POST /api/auth/loginβ token (4h default) - Protected routes:
@UseGuards(JwtAuthGuard)+ Bearer - Roles:
@Roles('admin')+RolesGuard(admin / visitor)
- Header:
X-API-KeyorAuthorization: Bearer <key> - Scopes:
read/write
- GitHub OAuth:
POST /api/auth/github - Passwords: bcrypt via
User.comparePassword()
.reactpress/config.json is the source of truth; .env is synced on init. --local uses SQLite with config.local.json / env.local.default.
| 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 |
- App processes on host (
pnpm dev) - Docker for MySQL + nginx only
- nginx forwards to host via
host.docker.internal
| Mode | Notes |
|---|---|
| PM2 | pnpm build β pnpm start |
| Docker | MySQL container + nginx; API can run on host |
| Vercel | Theme / Admin static deploy |
| Path | Target |
|---|---|
/ |
Theme :3001 |
/admin/ |
Admin :3000 |
/api |
API :3002 |
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
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 β themesAfter API changes:
pnpm run generate:swagger && pnpm run build:toolkitThemes handle presentation; plugins handle logic. Server-side Hooks extend business rules; optional Admin UI via slots.
Covered in Β§8 Extensibility and plugins/README.md.
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]
| 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 |
| 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 |
| 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) |
| 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 |
| 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.
| 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/ |
| 2.x | 3.0 |
|---|---|
Monolithic client/ Next (incl. /admin) |
web/ Admin SPA + themes/ visitor |
| Multiple HTTP layers | Unified toolkit client |
| Manual setup | CLI init + dev |
| 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 |
- Standalone
servernpm package deprecated β use CLI bundled API dev:clientscript name retained; starts active theme- Plugin npm catalog, marketplace, Desktop auto-update β future 4.x iterations
| Domain | Status |
|---|---|
| Content, media, appearance, system settings | β |
| Plugins (Hook + Registry + Admin slots) | β 4.0 |
| Desktop (Electron + SQLite) | β 4.0 MVP |
| Knowledge base | β server module |
| 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 |
- README.md β quick start (English)
- README-zh_CN.md β quick start (Chinese)
- docs/migration-3-to-4.md β 3.x β 4.0 migration
- docs/ β Docusaurus tutorials
- themes/README.md β theme development
- plugins/README.md β plugin development
- desktop/README.md β desktop client
- toolkit/README.md β SDK usage
- cli/README.md β CLI reference