Spaces:
Running
Running
| ; | |
| 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 }); | |
| } | |
| } | |