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,84 @@
const { query } = require('../../utils/db')
const COLS =
'id, category, title, slug, excerpt, body, image_url, published, author_id, created_at, updated_at, published_at'
// Published posts for a category, newest first — public feed.
async function listPublished(category) {
return query(
`SELECT ${COLS} FROM posts WHERE category = ? AND published = 1 ` +
'ORDER BY COALESCE(published_at, created_at) DESC, id DESC',
[category],
)
}
// All posts for a category (admin), newest first.
async function listAll(category) {
if (category) {
return query(`SELECT ${COLS} FROM posts WHERE category = ? ORDER BY id DESC`, [category])
}
return query(`SELECT ${COLS} FROM posts ORDER BY id DESC`)
}
async function findById(id) {
const rows = await query(`SELECT ${COLS} FROM posts WHERE id = ? LIMIT 1`, [id])
return rows[0] || null
}
async function findPublished(category, id, slug) {
const rows = await query(
`SELECT ${COLS} FROM posts WHERE category = ? AND published = 1 AND (id = ? OR slug = ?) LIMIT 1`,
[category, id, slug],
)
return rows[0] || null
}
async function insert(post) {
const res = await query(
'INSERT INTO posts (category, title, slug, excerpt, body, image_url, published, author_id, published_at) ' +
'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
[
post.category,
post.title,
post.slug || null,
post.excerpt || null,
post.body || null,
post.image_url || null,
post.published ? 1 : 0,
post.author_id || null,
post.published ? new Date() : null,
],
)
return res.insertId
}
async function update(id, fields) {
const cols = []
const params = []
for (const [key, val] of Object.entries(fields)) {
cols.push(`${key} = ?`)
params.push(val)
}
if (cols.length === 0) return
params.push(id)
await query(`UPDATE posts SET ${cols.join(', ')} WHERE id = ?`, params)
}
async function remove(id) {
return query('DELETE FROM posts WHERE id = ?', [id])
}
async function countByCategory() {
return query('SELECT category, COUNT(*) AS c FROM posts GROUP BY category')
}
module.exports = {
listPublished,
listAll,
findById,
findPublished,
insert,
update,
remove,
countByCategory,
}