[admin] Add moderation/admin management endpoints

This commit is contained in:
Grant 2024-07-13 10:02:23 -06:00
parent b755e87868
commit 013f5b8bb5
4 changed files with 174 additions and 2 deletions

View File

@ -139,6 +139,10 @@ Enum AuditLogAction {
CANVAS_FREEZE
CANVAS_UNFREEZE
CANVAS_AREA_UNDO
USER_MOD
USER_UNMOD
USER_ADMIN
USER_UNADMIN
}
Ref: Pixel.userId > User.sub

View File

@ -0,0 +1,12 @@
-- AlterEnum
-- This migration adds more than one value to an enum.
-- With PostgreSQL versions 11 and earlier, this is not possible
-- in a single migration. This can be worked around by creating
-- multiple migrations, each migration adding only one value to
-- the enum.
ALTER TYPE "AuditLogAction" ADD VALUE 'USER_MOD';
ALTER TYPE "AuditLogAction" ADD VALUE 'USER_UNMOD';
ALTER TYPE "AuditLogAction" ADD VALUE 'USER_ADMIN';
ALTER TYPE "AuditLogAction" ADD VALUE 'USER_UNADMIN';

View File

@ -26,8 +26,8 @@ model User {
profile_url String?
lastTimeGainStarted DateTime @default(now()) // the time base used to determine the amount of stack the user should gain
pixelStack Int @default(0) // amount of pixels stacked for this user
undoExpires DateTime? // when the undo for the most recent pixel expires at
pixelStack Int @default(0) // amount of pixels stacked for this user
undoExpires DateTime? // when the undo for the most recent pixel expires at
isAdmin Boolean @default(false)
isModerator Boolean @default(false)
@ -157,6 +157,10 @@ enum AuditLogAction {
CANVAS_FREEZE
CANVAS_UNFREEZE
CANVAS_AREA_UNDO
USER_MOD
USER_UNMOD
USER_ADMIN
USER_UNADMIN
}
model AuditLog {

View File

@ -679,6 +679,158 @@ app.post("/user/:sub/notice", async (req, res) => {
res.json({ success: true });
});
/**
* Mark a user as a moderator
*
* @param :sub User ID
*/
app.put("/user/:sub/moderator", async (req, res) => {
let user: User;
try {
user = await User.fromSub(req.params.sub);
} catch (e) {
if (e instanceof UserNotFound) {
res.status(404).json({ success: false, error: "User not found" });
} else {
res.status(500).json({ success: false, error: "Internal error" });
}
return;
}
await prisma.user.update({
where: { sub: user.sub },
data: {
isModerator: true,
},
});
await user.update(true);
const adminUser = (await User.fromAuthSession(req.session.user!))!;
const auditLog = await AuditLog.Factory(adminUser.sub)
.doing("USER_MOD")
.reason(req.header("X-Audit") || null)
.withComment(`Made ${user.sub} a moderator`)
.create();
res.json({ success: true, auditLog });
});
/**
* Unmark a user as a moderator
*
* @param :sub User ID
*/
app.delete("/user/:sub/moderator", async (req, res) => {
let user: User;
try {
user = await User.fromSub(req.params.sub);
} catch (e) {
if (e instanceof UserNotFound) {
res.status(404).json({ success: false, error: "User not found" });
} else {
res.status(500).json({ success: false, error: "Internal error" });
}
return;
}
await prisma.user.update({
where: { sub: user.sub },
data: {
isModerator: false,
},
});
await user.update(true);
const adminUser = (await User.fromAuthSession(req.session.user!))!;
const auditLog = await AuditLog.Factory(adminUser.sub)
.doing("USER_UNMOD")
.reason(req.header("X-Audit") || null)
.withComment(`Removed ${user.sub} as moderator`)
.create();
res.json({ success: true, auditLog });
});
/**
* Mark a user as an admin
*
* @param :sub User ID
*/
app.put("/user/:sub/admin", async (req, res) => {
let user: User;
try {
user = await User.fromSub(req.params.sub);
} catch (e) {
if (e instanceof UserNotFound) {
res.status(404).json({ success: false, error: "User not found" });
} else {
res.status(500).json({ success: false, error: "Internal error" });
}
return;
}
await prisma.user.update({
where: { sub: user.sub },
data: {
isAdmin: true,
},
});
await user.update(true);
const adminUser = (await User.fromAuthSession(req.session.user!))!;
const auditLog = await AuditLog.Factory(adminUser.sub)
.doing("USER_ADMIN")
.reason(req.header("X-Audit") || null)
.withComment(`Added ${user.sub} as admin`)
.create();
res.json({ success: true, auditLog });
});
/**
* Unmark a user as an admin
*
* @param :sub User ID
*/
app.delete("/user/:sub/admin", async (req, res) => {
let user: User;
try {
user = await User.fromSub(req.params.sub);
} catch (e) {
if (e instanceof UserNotFound) {
res.status(404).json({ success: false, error: "User not found" });
} else {
res.status(500).json({ success: false, error: "Internal error" });
}
return;
}
await prisma.user.update({
where: { sub: user.sub },
data: {
isAdmin: false,
},
});
await user.update(true);
const adminUser = (await User.fromAuthSession(req.session.user!))!;
const auditLog = await AuditLog.Factory(adminUser.sub)
.doing("USER_UNADMIN")
.reason(req.header("X-Audit") || null)
.withComment(`Removed ${user.sub} as admin`)
.create();
res.json({ success: true, auditLog });
});
app.get("/instance/:domain/ban", async (req, res) => {
// get ban information