Complete WatchLink V1 realtime features
This commit is contained in:
97
src/lib/admin-actions.ts
Normal file
97
src/lib/admin-actions.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
"use server";
|
||||
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { redirect } from "next/navigation";
|
||||
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: Record<string, unknown>) {
|
||||
await prisma.auditEvent.create({ data: { actorId, action, metadata } });
|
||||
}
|
||||
|
||||
function revalidateAdmin() {
|
||||
revalidatePath("/admin");
|
||||
revalidatePath("/dashboard");
|
||||
revalidatePath("/rooms");
|
||||
}
|
||||
Reference in New Issue
Block a user