// lib/httpUtils.js // Admin guard -- JSON-aware (ARCHITECTURE.md 6.4, folded review fix) // // Uniform error envelope: { error: { code, message, ...context } }, where // code in { forbidden, not_found, version_conflict, name_taken, active_theme, // builtin_immutable, bad_format, too_large, uncompilable, // bad_request, compile_failed, activation_failed, initializing }. const db = require("@saltcorn/data/db"); const { ROLE_ADMIN, API_BASE } = require("./constants"); function isAdminReq(req) { // mirror core isAdmin (utils.js:85-100) return !!(req.user && req.user.role_id === ROLE_ADMIN && req.user.tenant === db.getTenantSchema()); } function wantsJson(req) { return req.xhr || (req.path || "").startsWith(API_BASE) // API routes ALWAYS answer JSON (fixes multipart-import misclassify) || (req.get("accept") || "").includes("application/json") || (req.get("content-type") || "").includes("application/json"); } function guardAdmin(req, res) { if (isAdminReq(req)) return true; if (wantsJson(req)) res.status(req.user ? 403 : 401).json({ error: { code: "forbidden", message: "Must be admin" } }); else res.redirect(req.user ? "/" : `/auth/login?dest=${encodeURIComponent(req.originalUrl)}`); return false; } function jsonError(res, status, code, message, extra) { res.status(status).json({ error: { code, message, ...(extra || {}) } }); } module.exports = { isAdminReq, wantsJson, guardAdmin, jsonError };