- 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>
26 lines
841 B
JavaScript
26 lines
841 B
JavaScript
const activityDb = require('./activity.db')
|
|
|
|
const logger = require('../../utils/logger')('activity')
|
|
|
|
/**
|
|
* Record an admin action. `detail` may be an object (stored as JSON). Never throws
|
|
* into the request path — logging must not break the action it records.
|
|
*/
|
|
async function log({ req, userId, action, detail }) {
|
|
try {
|
|
const resolvedUserId = userId ?? (req && req.user ? req.user.id : null)
|
|
const ip = req ? req.ip : null
|
|
const detailStr =
|
|
detail == null ? null : typeof detail === 'string' ? detail : JSON.stringify(detail)
|
|
await activityDb.insert({ userId: resolvedUserId, action, detail: detailStr, ip })
|
|
} catch (err) {
|
|
logger.error(`failed to record action "${action}"`, { error: err.message })
|
|
}
|
|
}
|
|
|
|
async function list(opts) {
|
|
return activityDb.list(opts)
|
|
}
|
|
|
|
module.exports = { log, list }
|