Files
WatchLink/src/lib/admin-actions.ts
MrSphay e883f99664
All checks were successful
Release Dry Run / release-dry-run (push) Successful in 1m36s
Template Compliance / compliance (push) Successful in 5s
Build / build (push) Successful in 12m7s
Fix Prisma JSON typing in V1 actions
2026-05-15 23:45:32 +02:00

99 lines
3.4 KiB
TypeScript

"use server";
import { randomBytes } from "node:crypto";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
import { Prisma } from "@prisma/client";
import { prisma } from "./prisma";
import { requireCurrentUser, userIsAdmin } from "./session";
async function requireAdmin() {
const user = await requireCurrentUser();
if (!userIsAdmin(user)) redirect("/dashboard");
return user;
}
export async function disableUser(formData: FormData) {
const admin = await requireAdmin();
const userId = String(formData.get("userId") || "");
if (!userId || userId === admin.id) return;
await prisma.user.update({ where: { id: userId }, data: { disabledAt: new Date() } });
await audit(admin.id, "admin.user.disable", { userId });
revalidateAdmin();
}
export async function enableUser(formData: FormData) {
const admin = await requireAdmin();
const userId = String(formData.get("userId") || "");
if (!userId) return;
await prisma.user.update({ where: { id: userId }, data: { disabledAt: null } });
await audit(admin.id, "admin.user.enable", { userId });
revalidateAdmin();
}
export async function grantAdminRole(formData: FormData) {
const admin = await requireAdmin();
const userId = String(formData.get("userId") || "");
if (!userId) return;
const role = await prisma.role.upsert({
where: { name: "admin" },
update: {},
create: { name: "admin", description: "Full system administrator" }
});
await prisma.userRole.upsert({
where: { userId_roleId: { userId, roleId: role.id } },
update: {},
create: { userId, roleId: role.id }
});
await audit(admin.id, "admin.user.role.grant", { userId, role: "admin" });
revalidateAdmin();
}
export async function revokeAdminRole(formData: FormData) {
const admin = await requireAdmin();
const userId = String(formData.get("userId") || "");
if (!userId || userId === admin.id) return;
const role = await prisma.role.findUnique({ where: { name: "admin" } });
if (!role) return;
await prisma.userRole.deleteMany({ where: { userId, roleId: role.id } });
await audit(admin.id, "admin.user.role.revoke", { userId, role: "admin" });
revalidateAdmin();
}
export async function createInstanceInvite(formData: FormData) {
const admin = await requireAdmin();
const roomId = String(formData.get("roomId") || "") || null;
const expiresDays = Number(formData.get("expiresDays") || 0);
const expiresAt = Number.isFinite(expiresDays) && expiresDays > 0 ? new Date(Date.now() + expiresDays * 24 * 60 * 60 * 1000) : null;
const invite = await prisma.invite.create({
data: {
code: randomBytes(12).toString("base64url"),
creatorId: admin.id,
roomId,
expiresAt
}
});
await audit(admin.id, "admin.invite.create", { inviteId: invite.id, roomId });
revalidateAdmin();
redirect("/admin?tab=Invites&saved=1");
}
export async function revokeInvite(formData: FormData) {
const admin = await requireAdmin();
const inviteId = String(formData.get("inviteId") || "");
if (!inviteId) return;
await prisma.invite.update({ where: { id: inviteId }, data: { status: "REVOKED" } });
await audit(admin.id, "admin.invite.revoke", { inviteId });
revalidateAdmin();
}
async function audit(actorId: string, action: string, metadata: Prisma.InputJsonObject) {
await prisma.auditEvent.create({ data: { actorId, action, metadata } });
}
function revalidateAdmin() {
revalidatePath("/admin");
revalidatePath("/dashboard");
revalidatePath("/rooms");
}