Understanding the layout before you touch anything saves a lot of confusion later.

blog-doc/
├── index.js                   # Entry point — app wiring & server start
├── update.js                  # Runnable script — pull latest app version
├── package.json
├── .env                       # Optional — PORT override
├── app/
│   ├── content/
│   │   ├── pages/             # Page .md files (slug-as-filename)
│   │   ├── posts/             # Post .md files (slug-as-filename)
│   │   └── images/            # Uploaded images
│   ├── data/
│   │   ├── settings.json      # Site-wide settings
│   │   ├── menus.json         # Menu definitions
│   │   ├── active-theme.json  # Currently active theme ID
│   │   └── search.json        # Auto-generated search index
│   └── themes/
│       └── {themeId}/         # One directory per installed theme
│           ├── theme.json     # Theme manifest
│           ├── screenshot.*   # Required preview image
│           ├── templates/     # STE HTML templates
│           └── assets/        # CSS, JS, fonts, images
├── admin/
│   ├── assets/                # Admin CSS, JS, fonts
│   ├── functions/             # Pure data/business logic (no HTTP)
│   │   ├── admin-bar.js       # Dev-only admin bar middleware
│   │   ├── build.js           # Static site generator
│   │   ├── data.js            # File I/O helpers, PATHS, slug utilities
│   │   ├── frontmatter.js     # YAML frontmatter serialization
│   │   ├── search.js          # Search index generator
│   │   └── site-data.js       # Content assembly for templates
│   ├── routes/                # All route handler modules
│   └── views/                 # Admin HTML templates (STE)
└── _site/                     # Generated static site (git-ignored)

Key directories

app/content/

This is where your content lives. Files are plain Markdown with YAML frontmatter. The filename (without .md) is the slug — about.md becomes /about.

app/data/

Plain JSON configuration files. You never need to edit these by hand — the admin GUI writes to them — but they're human-readable and easily version-controlled.

app/themes/

Each installed theme is a subfolder here. The active theme is tracked in active-theme.json by its folder name (the theme id).

admin/

The admin application itself. routes/ handles HTTP, functions/ is pure business logic with no HTTP dependencies. The clean separation means you can read and test build.js or site-data.js without spinning up the server.

Module responsibilities

Module Location Responsibility
admin-bar.js admin/functions/ Admin bar injected into live theme responses
build.js admin/functions/ Static site generator — pure, idempotent function
data.js admin/functions/ All file I/O, PATHS constants, slug utilities
frontmatter.js admin/functions/ Serialize form fields back to clean .md files
search.js admin/functions/ Generate and write search.json
site-data.js admin/functions/ Assemble template data objects per route

The adminBarMiddleware wraps res.end on every non-/admin request, checks for text/html content type, and injects the floating admin bar before </body>. The bar is absent from static builds. No changes to themes or templates are required.

_site/

Created on demand by the build engine. Add it to .gitignore. Every build wipes it and starts fresh.

Architecture layers

Route registration order in index.js is intentional and must be preserved:

// 1. Admin GUI routes    → /admin/*
registerAdminUIRoutes(app)

// 2. Admin API routes    → /admin/api/*
const apiRouter = new LiteNode("__NO_STATIC_DIR__")
// ... register all API routes on apiRouter ...
app.nest("/admin/api", apiRouter)

// 3. Admin bar middleware (dev only — never runs during static build)
app.use(adminBarMiddleware)

// 4. Live theme routes   → /* (registered last — catch-all)
registerLiveTheme(app)

The live theme is registered last because its catch-all /:pagename route would swallow every other route if registered first.

The three main layers

Layer Base path Purpose
Admin GUI /admin/* Browser-based content management
Admin API /admin/api/* JSON REST API consumed by the GUI
Live theme /* Serves the active theme dynamically

All three share the same Node.js process and the same filesystem. There is no authentication — Blog-Doc is a local tool.

Updating Blog-Doc

update.js updates Blog-Doc to the latest version by pulling the newest code from the repository and refreshing dependencies.

Run this from your project root to update Blog-Doc whenever a new version is released:

node update.js

or

npm run update

Your content and data are never touched by this script.
The following are gitignored and therefore completely safe:

Folder Contents
app/content/ Pages, posts, and uploaded images.
app/data/ Site settings, navigation menus, and the active theme.
app/themes/ Installed themes (excluding the bundled default theme).