Exocore-Backend / dist /auth /admin.js
ChoruYt's picture
Upload 48 files
8b74df2 verified
Raw
History Blame Contribute Delete
7.79 kB
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.tokenVerifyHandler = tokenVerifyHandler;
exports.setRoleHandler = setRoleHandler;
exports.muteHandler = muteHandler;
exports.banHandler = banHandler;
const drive_1 = require("../services/drive");
const auditStore_1 = require("../services/auditStore");
const owners_1 = require("../utils/owners");
function sanitize(u) {
const { pass: _p, verifyOtp: _o, ...safe } = u;
return safe;
}
async function userByToken(token) {
if (!token)
return null;
const all = await (0, drive_1.getAllUsers)();
return all.find(u => u.token === token) ?? null;
}
async function userByUsername(username) {
const all = await (0, drive_1.getAllUsers)();
// Backend stores usernames with a leading "@" (e.g. "@jonellcc").
// Normalize so callers can pass either "jonellcc" or "@jonellcc".
const norm = username.startsWith("@") ? username : `@${username}`;
return all.find(u => u.username === norm || u.username === username) ?? null;
}
/** GET /exocore/api/auth/token-verify?token=... → returns sanitized user. */
async function tokenVerifyHandler(req, res) {
try {
const token = String(req.query.token || req.body?.token || "").trim();
if (!token)
return res.status(400).json({ success: false, message: "Missing token" });
if (!(0, drive_1.isCacheReady)())
return res.status(503).json({ success: false, message: "warming up" });
const u = await userByToken(token);
if (!u)
return res.status(401).json({ success: false, message: "invalid token" });
return res.json({ success: true, user: sanitize(u) });
}
catch (e) {
return res.status(500).json({ success: false, message: e?.message });
}
}
/** POST /exocore/api/admin/role { token, target, role } */
async function setRoleHandler(req, res) {
try {
const { token, target, role } = (req.body || {});
if (!token || !target || !role)
return res.status(400).json({ success: false, message: "token/target/role required" });
if (!["owner", "admin", "mod", "user"].includes(role))
return res.status(400).json({ success: false, message: "invalid role" });
const caller = await userByToken(token);
if (!caller)
return res.status(401).json({ success: false, message: "invalid token" });
const tgt = await userByUsername(target);
if (!tgt)
return res.status(404).json({ success: false, message: "target not found" });
// Email-pinned owners can never be demoted.
if ((0, owners_1.roleForEmail)(tgt.email) === "owner" && role !== "owner") {
return res.status(403).json({ success: false, message: "cannot demote a pinned owner" });
}
if (!(0, owners_1.canAssignRole)(String(caller.role || "user"), String(tgt.role || "user"), role)) {
return res.status(403).json({ success: false, message: "insufficient privileges" });
}
const folderId = await (0, drive_1.getUserFolder)(tgt.username);
if (!folderId)
return res.status(404).json({ success: false, message: "target folder missing" });
const updated = { ...tgt, role };
await (0, drive_1.writeUserDb)(folderId, updated);
(0, auditStore_1.appendAudit)({ by: caller.username, action: "role:set", target: tgt.username, meta: { from: tgt.role || "user", to: role } });
return res.json({ success: true, user: sanitize(updated) });
}
catch (e) {
return res.status(500).json({ success: false, message: e?.message });
}
}
/** POST /exocore/api/admin/mute { token, target, minutes, reason? }
* minutes: number > 0 sets restrictedUntil; 0 lifts the mute. */
async function muteHandler(req, res) {
try {
const { token, target, minutes, reason } = (req.body || {});
if (!token || !target || minutes === undefined) {
return res.status(400).json({ success: false, message: "token/target/minutes required" });
}
const caller = await userByToken(token);
if (!caller)
return res.status(401).json({ success: false, message: "invalid token" });
const tgt = await userByUsername(target);
if (!tgt)
return res.status(404).json({ success: false, message: "target not found" });
if ((0, owners_1.roleForEmail)(tgt.email) === "owner") {
return res.status(403).json({ success: false, message: "cannot mute a pinned owner" });
}
if (!(0, owners_1.canModerate)(String(caller.role || "user"), String(tgt.role || "user"))) {
return res.status(403).json({ success: false, message: "insufficient privileges" });
}
const restrictedUntil = (typeof minutes === "number" && minutes > 0)
? Date.now() + minutes * 60 * 1000 : null;
const folderId = await (0, drive_1.getUserFolder)(tgt.username);
if (!folderId)
return res.status(404).json({ success: false, message: "target folder missing" });
const updated = { ...tgt, restrictedUntil };
await (0, drive_1.writeUserDb)(folderId, updated);
(0, auditStore_1.appendAudit)({
by: caller.username,
action: restrictedUntil === null ? "mute:lift" : "mute:apply",
target: tgt.username,
meta: { restrictedUntil, reason: reason || null },
});
return res.json({ success: true, user: sanitize(updated) });
}
catch (e) {
return res.status(500).json({ success: false, message: e?.message });
}
}
/** POST /exocore/api/admin/ban { token, target, days, reason? }
* days: number of days (>0), 0 = unban, "perm" = permanent. */
async function banHandler(req, res) {
try {
const { token, target, days, reason } = (req.body || {});
if (!token || !target || days === undefined) {
return res.status(400).json({ success: false, message: "token/target/days required" });
}
const caller = await userByToken(token);
if (!caller)
return res.status(401).json({ success: false, message: "invalid token" });
const tgt = await userByUsername(target);
if (!tgt)
return res.status(404).json({ success: false, message: "target not found" });
if ((0, owners_1.roleForEmail)(tgt.email) === "owner") {
return res.status(403).json({ success: false, message: "cannot ban a pinned owner" });
}
if (!(0, owners_1.canModerate)(String(caller.role || "user"), String(tgt.role || "user"))) {
return res.status(403).json({ success: false, message: "insufficient privileges" });
}
let bannedUntil = null;
if (days === "perm")
bannedUntil = -1;
else if (typeof days === "number" && days > 0)
bannedUntil = Date.now() + days * 86400 * 1000;
else
bannedUntil = null; // unban
const folderId = await (0, drive_1.getUserFolder)(tgt.username);
if (!folderId)
return res.status(404).json({ success: false, message: "target folder missing" });
const updated = { ...tgt, bannedUntil, banReason: reason || null };
await (0, drive_1.writeUserDb)(folderId, updated);
(0, auditStore_1.appendAudit)({
by: caller.username,
action: bannedUntil === null ? "ban:lift" : "ban:apply",
target: tgt.username,
meta: { bannedUntil, reason: reason || null },
});
return res.json({ success: true, user: sanitize(updated) });
}
catch (e) {
return res.status(500).json({ success: false, message: e?.message });
}
}