147 lines
4.1 KiB
TypeScript
147 lines
4.1 KiB
TypeScript
|
|
'use server';
|
||
|
|
|
||
|
|
import { z } from 'zod';
|
||
|
|
import { logAdminAction } from '@/lib/audit-logger';
|
||
|
|
|
||
|
|
// --- Validation Schemas ---
|
||
|
|
|
||
|
|
const suspendSchema = z.object({
|
||
|
|
userId: z.string(),
|
||
|
|
reason: z.string().min(10, "Reason must be at least 10 characters"),
|
||
|
|
duration: z.enum(['24h', '7d', '30d', 'indefinite']),
|
||
|
|
});
|
||
|
|
|
||
|
|
const banSchema = z.object({
|
||
|
|
userId: z.string(),
|
||
|
|
reason: z.string().min(10),
|
||
|
|
});
|
||
|
|
|
||
|
|
const ticketSchema = z.object({
|
||
|
|
userId: z.string(),
|
||
|
|
summary: z.string().min(5),
|
||
|
|
});
|
||
|
|
|
||
|
|
// --- Authorization Helper ---
|
||
|
|
|
||
|
|
async function verifyAdmin() {
|
||
|
|
// TODO: Integrate with Auth.js session check
|
||
|
|
// const session = await auth();
|
||
|
|
// if (session?.user?.role !== 'admin') throw new Error("Unauthorized");
|
||
|
|
|
||
|
|
// Mock successful admin check
|
||
|
|
return { id: 'admin-1', role: 'admin' };
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- Server Actions ---
|
||
|
|
|
||
|
|
export async function resetPassword(userId: string) {
|
||
|
|
try {
|
||
|
|
const admin = await verifyAdmin();
|
||
|
|
|
||
|
|
// Simulate DB call
|
||
|
|
await new Promise(resolve => setTimeout(resolve, 800));
|
||
|
|
|
||
|
|
await logAdminAction({
|
||
|
|
actorId: admin.id,
|
||
|
|
action: 'RESET_PASSWORD',
|
||
|
|
targetId: userId,
|
||
|
|
});
|
||
|
|
|
||
|
|
return { success: true, message: "Recovery email sent to user." };
|
||
|
|
} catch (error) {
|
||
|
|
return { success: false, message: "Failed to reset password." };
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function impersonateUser(userId: string) {
|
||
|
|
try {
|
||
|
|
const admin = await verifyAdmin();
|
||
|
|
|
||
|
|
await logAdminAction({
|
||
|
|
actorId: admin.id,
|
||
|
|
action: 'IMPERSONATE_USER',
|
||
|
|
targetId: userId,
|
||
|
|
});
|
||
|
|
|
||
|
|
// In a real app, you'd set a cookie here
|
||
|
|
// cookies().set('impersonate_id', userId);
|
||
|
|
|
||
|
|
return { success: true, redirectUrl: '/dashboard' };
|
||
|
|
} catch (error) {
|
||
|
|
return { success: false, message: "Failed to start impersonation session." };
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function terminateSessions(userId: string) {
|
||
|
|
try {
|
||
|
|
const admin = await verifyAdmin();
|
||
|
|
|
||
|
|
// Simulate DB call to delete sessions
|
||
|
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||
|
|
|
||
|
|
await logAdminAction({
|
||
|
|
actorId: admin.id,
|
||
|
|
action: 'TERMINATE_SESSIONS',
|
||
|
|
targetId: userId,
|
||
|
|
});
|
||
|
|
|
||
|
|
return { success: true, message: "All active sessions terminated." };
|
||
|
|
} catch (error) {
|
||
|
|
return { success: false, message: "Failed to terminate sessions." };
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function toggleSuspension(formData: FormData) {
|
||
|
|
try {
|
||
|
|
const admin = await verifyAdmin();
|
||
|
|
|
||
|
|
const rawData = {
|
||
|
|
userId: formData.get('userId'),
|
||
|
|
reason: formData.get('reason'),
|
||
|
|
duration: formData.get('duration'),
|
||
|
|
};
|
||
|
|
|
||
|
|
const validated = suspendSchema.safeParse(rawData);
|
||
|
|
if (!validated.success) {
|
||
|
|
return { success: false, message: validated.error.errors[0].message };
|
||
|
|
}
|
||
|
|
|
||
|
|
// Simulate DB update
|
||
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||
|
|
|
||
|
|
await logAdminAction({
|
||
|
|
actorId: admin.id,
|
||
|
|
action: 'SUSPEND_USER',
|
||
|
|
targetId: validated.data.userId,
|
||
|
|
details: { reason: validated.data.reason, duration: validated.data.duration },
|
||
|
|
});
|
||
|
|
|
||
|
|
return { success: true, message: `User suspended for ${validated.data.duration}.` };
|
||
|
|
} catch (error) {
|
||
|
|
return { success: false, message: "Failed to suspend user." };
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function createSupportTicket(userId: string, summary: string) {
|
||
|
|
try {
|
||
|
|
const admin = await verifyAdmin();
|
||
|
|
|
||
|
|
const validated = ticketSchema.safeParse({ userId, summary });
|
||
|
|
if (!validated.success) return { success: false, message: "Invalid input" };
|
||
|
|
|
||
|
|
// Simulate DB insert
|
||
|
|
await new Promise(resolve => setTimeout(resolve, 600));
|
||
|
|
|
||
|
|
await logAdminAction({
|
||
|
|
actorId: admin.id,
|
||
|
|
action: 'CREATE_TICKET',
|
||
|
|
targetId: userId,
|
||
|
|
details: { summary },
|
||
|
|
});
|
||
|
|
|
||
|
|
return { success: true, message: "Support ticket #T-1234 created." };
|
||
|
|
} catch (error) {
|
||
|
|
return { success: false, message: "Failed to create ticket." };
|
||
|
|
}
|
||
|
|
}
|