markdisk-cms is a file-based publishing engine: markdown files are the model, Express route handlers are the controller, and JavaScript templates with Tailwind CSS are the view.
content/blog/**/*.md → services/blog.js (parse, cache)
→ routes/blog.js (HTTP handlers)
→ templates/blog-layout.js (HTML)
No editorial database. SQLite is used only for optional lead-magnet email signups.
markdisk-cms/
├── server.js # Express entry
├── config.js # Site + sections + feature flags
├── content/
│ ├── authors.json # Author profiles for /author/:slug
│ ├── about.md # Static pages
│ └── blog/ # Posts (hierarchical folders)
├── routes/
│ ├── blog.js # Main site routes
│ └── leadMagnet.js # Email gate API
├── services/
│ ├── blog.js # Post loading, markdown, related posts
│ ├── contentLinker.js # Internal link injection
│ ├── indexNow.js # Search engine ping
│ └── leadMagnet/ # Gate + SQLite + SES
├── templates/ # HTML layout fragments
├── public/ # CSS + client scripts
└── data/ # Runtime manifests (sitemap snapshot, link cache)
config.js defines a tree of section keys. Posts map to sections via frontmatter section: or folder path (news/announcements/my-post.md → news/announcements).
URLs:
- Parent:
/section/news - Child:
/section/news/announcements - Article:
/news/announcements/welcome-to-markdisk
Tag-based scoring in services/blog.js:
- +3 per matching tag
- +2 same top-level category
- +1 same subcategory type
Posts with emailGate: true split body at a configured section count. routes/leadMagnet.js accepts email signups; services/leadMagnet/ses.js sends PDF links when AWS credentials are set.
When INDEXNOW_KEY and INDEXNOW_HOST are set, /indexnow compares the current URL set to data/sitemapSnapshot.json and pings IndexNow participants on changes.