File size: 2,882 Bytes
11486ce
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { assertAgentAuthorized } from '@/lib/agent-auth';
import { createRequestId } from '@/lib/agent-state-store';
import { AgentApiError, agentErrorResponse, normalizeAgentError } from '@/lib/api-error-response';
import {
    FeedbackValidationError,
    feedbackRecordToResponse,
    getFeedbackStore,
    normalizeFeedbackTargetId
} from '@/lib/feedback-store';
import { NextRequest, NextResponse } from 'next/server';

const MAX_PAGE_REQUEST_IDS = 50;

export async function POST(request: NextRequest) {
    const requestId = createRequestId();
    try {
        assertAgentAuthorized(request.headers);
        const body = await readJsonBody(request);
        const ids = readPageRequestIds(body.ids);
        const store = await getFeedbackStore();
        const targets = ids.map((id) => ({ targetType: 'page_request' as const, targetId: id }));
        const feedback = await store.listFeedbackByTargets(targets);
        return NextResponse.json(
            {
                targets: ids.map((id) => ({ type: 'page_request', id })),
                feedback: feedback.map(feedbackRecordToResponse)
            },
            { headers: { 'X-Request-Id': requestId } }
        );
    } catch (error) {
        if (error instanceof FeedbackValidationError) {
            return agentErrorResponse(
                new AgentApiError({
                    code: 'validation_error',
                    message: error.message,
                    status: 422,
                    retryable: false
                }),
                requestId
            );
        }
        return agentErrorResponse(normalizeAgentError(error), requestId);
    }
}

async function readJsonBody(request: NextRequest): Promise<Record<string, unknown>> {
    try {
        return readBodyObject(await request.json());
    } catch (error) {
        if (error instanceof SyntaxError) {
            throw new FeedbackValidationError('请求体必须是有效 JSON。');
        }
        throw error;
    }
}

function readBodyObject(value: unknown): Record<string, unknown> {
    if (typeof value !== 'object' || value === null || Array.isArray(value)) {
        throw new FeedbackValidationError('请求体必须是 JSON 对象。');
    }
    return value as Record<string, unknown>;
}

function readPageRequestIds(value: unknown): string[] {
    if (!Array.isArray(value)) {
        throw new FeedbackValidationError('ids 必须是数组。');
    }
    if (value.some((id) => typeof id !== 'string')) {
        throw new FeedbackValidationError('ids 数组必须只包含字符串 ID。');
    }
    const uniqueIds = Array.from(new Set(value.map(normalizeFeedbackTargetId)));
    if (uniqueIds.length === 0 || uniqueIds.length > MAX_PAGE_REQUEST_IDS) {
        throw new FeedbackValidationError(`ids 必须包含 1 到 ${MAX_PAGE_REQUEST_IDS} 个元素。`);
    }
    return uniqueIds;
}