File size: 2,532 Bytes
ceb943f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
"use server";

import { db } from "@/lib/db";
import {
    detectedObjects,
    adminModeration,
} from "@/lib/db/schema";
import { eq } from "drizzle-orm";
import { getAdminSession } from "@/lib/admin";
import { revalidatePath } from "next/cache";
import * as Sentry from "@sentry/nextjs";

export type ReasonCode =
    | "wrong_object"
    | "wrong_category"
    | "false_positive"
    | "unclear_image"
    | "duplicate"
    | "out_of_scope";

export async function adminMarkIncorrect(
    detectionId: string,
    reasonCode: ReasonCode,
    notes?: string
) {
    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" };
        }

        const originalValues = {
            objectName: current.objectName,
            category: current.category,
            status: current.status,
        };

        // 2. Start transaction
        return await db.transaction(async (tx) => {
            // 3. Update detected_objects status to rejected
            await tx
                .update(detectedObjects)
                .set({
                    status: "rejected",
                    updatedAt: new Date(),
                })
                .where(eq(detectedObjects.id, detectionId));

            // 4. Insert audit log
            await tx.insert(adminModeration).values({
                id: crypto.randomUUID(),
                detectionId,
                adminId: session.user.id,
                action: "marked_incorrect",
                reasonCode: reasonCode as any,
                originalValues,
                correctedValues: notes ? { notes } : null,
                trainAiFlag: true,
            });

            revalidatePath("/admin/moderation");

            return { success: true };
        });
    } catch (error) {
        Sentry.captureException(error, {
            tags: {
                action: "admin-mark-incorrect",
                detection_id: detectionId,
                reason_code: reasonCode
            },
        });
        console.error("Admin marking incorrect failed:", error);
        return { success: false, error: "Failed to mark detection as incorrect" };
    }
}