Initial commit: UOMysticmoon backend (Express + MariaDB + JWT)

- Layered API (router -> controller -> model -> db), serverlinkr pattern
- Public / auth / admin route groups; posts, wiki, settings, users, activity models
- JWT httpOnly-cookie auth (Secure auto-detected: LAN HTTP + Pangolin HTTPS)
- Site LIVE/MAINTENANCE mode with admin preview bypass
- Dual file+console logging (info/warn/error/debug) + HTTP access logs
- Docker Compose (app + MariaDB), schema.sql + seed, .env.example
- Verified end-to-end against MariaDB (27/27 smoke checks)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-26 20:58:32 -05:00
commit eef79e2403
41 changed files with 4195 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
const postsDb = require('./posts.db')
// URL category (kebab) <-> DB enum value.
const CATEGORY_MAP = {
news: 'news',
'five-on-friday': 'five_on_friday',
newsletter: 'newsletter',
screenshots: 'screenshot',
}
const URL_CATEGORIES = Object.keys(CATEGORY_MAP)
const DB_CATEGORIES = Object.values(CATEGORY_MAP)
function toDbCategory(urlCategory) {
return CATEGORY_MAP[urlCategory] || null
}
function isValidUrlCategory(urlCategory) {
return Boolean(CATEGORY_MAP[urlCategory])
}
function isValidDbCategory(dbCategory) {
return DB_CATEGORIES.includes(dbCategory)
}
async function listPublished(urlCategory) {
return postsDb.listPublished(toDbCategory(urlCategory))
}
async function getPublished(urlCategory, idOrSlug) {
const id = Number.isInteger(Number(idOrSlug)) ? Number(idOrSlug) : -1
return postsDb.findPublished(toDbCategory(urlCategory), id, String(idOrSlug))
}
async function listAll(dbCategory) {
return postsDb.listAll(dbCategory || null)
}
async function getById(id) {
return postsDb.findById(id)
}
async function create(post) {
const id = await postsDb.insert(post)
return postsDb.findById(id)
}
async function update(id, fields) {
await postsDb.update(id, fields)
return postsDb.findById(id)
}
async function setPublished(id, published) {
const current = await postsDb.findById(id)
if (!current) return null
const fields = { published: published ? 1 : 0 }
// Stamp published_at the first time a post goes live.
if (published && !current.published_at) fields.published_at = new Date()
await postsDb.update(id, fields)
return postsDb.findById(id)
}
async function remove(id) {
return postsDb.remove(id)
}
async function counts() {
const rows = await postsDb.countByCategory()
return rows.reduce((acc, row) => {
acc[row.category] = Number(row.c)
return acc
}, {})
}
module.exports = {
URL_CATEGORIES,
DB_CATEGORIES,
toDbCategory,
isValidUrlCategory,
isValidDbCategory,
listPublished,
getPublished,
listAll,
getById,
create,
update,
setPublished,
remove,
counts,
}