"use server"; import { db } from "@/lib/db"; import { detectedObjects, adminModeration, marketplaceMatches, } from "@/lib/db/schema"; import { eq, and } from "drizzle-orm"; import { getAdminSession } from "@/lib/admin"; import { revalidatePath } from "next/cache"; import * as Sentry from "@sentry/nextjs"; export interface AdminCorrectionData { objectName?: string; category?: string; marketplaceLinkOverrides?: { matchId: string; affiliateUrl: string }[]; } export async function adminCorrectDetection( detectionId: string, data: AdminCorrectionData ) { try { const session = await getAdminSession(); if (!session) { return { success: false, error: "Unauthorized: admin access required" }; } // 1. Fetch current detection for audit snapshot const current = await db.query.detectedObjects.findFirst({ where: eq(detectedObjects.id, detectionId), }); if (!current) { return { success: false, error: "Detection not found" }; } // Fetch current marketplace matches for complete audit snapshot const currentMatches = await db .select({ id: marketplaceMatches.id, affiliateUrl: marketplaceMatches.affiliateUrl, marketplace: marketplaceMatches.marketplace, }) .from(marketplaceMatches) .where(eq(marketplaceMatches.objectId, detectionId)); const originalValues = { objectName: current.objectName, category: current.category, status: current.status, marketplaceMatches: currentMatches.map(m => ({ id: m.id, affiliateUrl: m.affiliateUrl, marketplace: m.marketplace, })), }; // 2. Start transaction for atomicity return await db.transaction(async (tx) => { // 3. Update detected_objects await tx .update(detectedObjects) .set({ objectName: data.objectName ?? current.objectName, category: (data.category as any) ?? current.category, status: "pending_review", // Moves to creator queue updatedAt: new Date(), }) .where(eq(detectedObjects.id, detectionId)); // 4. Update marketplace links if provided if (data.marketplaceLinkOverrides && data.marketplaceLinkOverrides.length > 0) { for (const override of data.marketplaceLinkOverrides) { await tx .update(marketplaceMatches) .set({ affiliateUrl: override.affiliateUrl, updatedAt: new Date() }) .where( and( eq(marketplaceMatches.id, override.matchId), eq(marketplaceMatches.objectId, detectionId) ) ); } } // 5. Insert audit log await tx.insert(adminModeration).values({ id: crypto.randomUUID(), detectionId, adminId: session.user.id, action: "corrected", originalValues, correctedValues: { ...data, correctedAt: new Date().toISOString(), }, trainAiFlag: true, }); revalidatePath("/admin/moderation"); revalidatePath("/dashboard/moderation"); return { success: true }; }); } catch (error) { Sentry.captureException(error, { tags: { action: "admin-correct-detection", detection_id: detectionId }, }); console.error("Admin correction failed:", error); return { success: false, error: "Failed to apply admin correction" }; } }