cba5d9c3ac
Sticky sidebar with back-to-app link. Overview: user counts, recipe photos, AI calls/month. User management with role/tier editing. Audit log (all admin actions). Storage page with photo breakdown by tier. AI config showing DB vs .env key source. Site settings page to override .env values at runtime (encrypted in DB).
57 lines
1.8 KiB
TypeScript
57 lines
1.8 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { requireAdmin } from "@/lib/api-auth";
|
|
import { db, users, auditLogs, eq } from "@epicure/db";
|
|
import { randomUUID } from "crypto";
|
|
|
|
interface RouteContext {
|
|
params: Promise<{ id: string }>;
|
|
}
|
|
|
|
export async function PATCH(req: NextRequest, { params }: RouteContext) {
|
|
const { session, response } = await requireAdmin();
|
|
if (response) return response;
|
|
|
|
const { id } = await params;
|
|
const body = await req.json() as { role?: string; tier?: string };
|
|
const { role, tier } = body;
|
|
|
|
const validRoles = ["user", "moderator", "admin"] as const;
|
|
const validTiers = ["free", "pro"] as const;
|
|
|
|
if (role !== undefined && !validRoles.includes(role as typeof validRoles[number])) {
|
|
return NextResponse.json({ error: "Invalid role" }, { status: 400 });
|
|
}
|
|
if (tier !== undefined && !validTiers.includes(tier as typeof validTiers[number])) {
|
|
return NextResponse.json({ error: "Invalid tier" }, { status: 400 });
|
|
}
|
|
|
|
const updateData: Partial<{ role: "user" | "moderator" | "admin"; tier: "free" | "pro"; updatedAt: Date }> = {
|
|
updatedAt: new Date(),
|
|
};
|
|
if (role) updateData.role = role as "user" | "moderator" | "admin";
|
|
if (tier) updateData.tier = tier as "free" | "pro";
|
|
|
|
const [updated] = await db
|
|
.update(users)
|
|
.set(updateData)
|
|
.where(eq(users.id, id))
|
|
.returning({ id: users.id, role: users.role, tier: users.tier });
|
|
|
|
if (!updated) {
|
|
return NextResponse.json({ error: "User not found" }, { status: 404 });
|
|
}
|
|
|
|
// Write audit log
|
|
await db.insert(auditLogs).values({
|
|
id: randomUUID(),
|
|
userId: session!.user.id,
|
|
action: "admin.user.update",
|
|
targetType: "user",
|
|
targetId: id,
|
|
metadata: JSON.stringify({ role, tier }),
|
|
createdAt: new Date(),
|
|
});
|
|
|
|
return NextResponse.json({ user: updated });
|
|
}
|