11import { NextResponse } from "next/server" ;
2- import { connectToDatabase } from "@/lib/database/mongoose" ;
3- import TagReport from "@/db/tagReport" ;
4- import { Ratelimit } from "@upstash/ratelimit" ;
5- import { redis } from "@/lib/utils/redis" ;
6-
7- const exams : string [ ] = [ "CAT-1" , "CAT-2" , "FAT" , "Model CAT-1" , "Model CAT-2" , "Model FAT" ]
8-
9- interface ReportedFieldInput {
10- field : string ;
11- value ?: string ;
12- }
13- interface ReportTagBody {
14- paperId ?: string ;
15- reportedFields ?: unknown ;
16- comment ?: unknown ;
17- reporterEmail ?: unknown ;
18- reporterId ?: unknown ;
19- }
20-
21- const ALLOWED_FIELDS = [ "subject" , "courseCode" , "exam" , "slot" , "year" ] ;
22-
23- function getRateLimit ( ) {
24- return new Ratelimit ( {
25- redis,
26- limiter : Ratelimit . slidingWindow ( 3 , "1 h" ) , //per id - 3 request - per hour
27- analytics : true ,
28- } ) ;
29- }
30- function getClientIp ( req : Request & { ip ?: string } ) : string {
31- const xff = req . headers . get ( "x-forwarded-for" ) ;
32- if ( typeof xff === "string" && xff . length > 0 ) {
33- return xff . split ( "," ) [ 0 ] ?. trim ( ) ?? "" ;
34- }
35- const xri = req . headers . get ( "x-real-ip" ) ;
36- if ( typeof xri === "string" && xri . length > 0 ) {
37- return xri ;
38- }
39- return "0.0.0.0" ;
40- }
2+ import { reportTag , ReportTagBody } from "@/lib/services/report" ;
3+ import { rateLimitCheck } from "@/lib/utils/rate-limiter" ;
4+ import { customErrorHandler } from "@/lib/utils/error" ;
415
426export async function POST ( req : Request & { ip ?: string } ) {
437 try {
44- await connectToDatabase ( ) ;
45- const ratelimit = getRateLimit ( ) ;
468 const body = ( await req . json ( ) ) as ReportTagBody ;
479 const paperId = typeof body . paperId === "string" ? body . paperId : undefined ;
4810
@@ -52,71 +14,15 @@ export async function POST(req: Request & { ip?: string }) {
5214 { status : 400 }
5315 ) ;
5416 }
55- const ip = getClientIp ( req ) ;
56- const key = `${ ip } ::${ paperId } ` ;
57- const { success } = await ratelimit . limit ( key ) ;
58-
59- if ( ! success ) {
60- return NextResponse . json (
61- { error : "Rate limit exceeded for reporting." } ,
62- { status : 429 }
63- ) ;
64- }
65- const MAX_REPORTS_PER_PAPER = 5 ;
66- const count = await TagReport . countDocuments ( { paperId } ) ;
67-
68- if ( count >= MAX_REPORTS_PER_PAPER ) {
69- return NextResponse . json (
70- { error : "Received many reports; we are currently working on it." } ,
71- { status : 429 }
72- ) ;
73- }
74- const reportedFields : ReportedFieldInput [ ] = Array . isArray ( body . reportedFields )
75- ? body . reportedFields
76- . map ( ( r ) : ReportedFieldInput | null => {
77- if ( ! r || typeof r !== "object" ) return null ;
78-
79- const field = typeof ( r as { field ?: unknown } ) . field === "string" ? ( r as { field : string } ) . field . trim ( ) : "" ;
80- const value = typeof ( r as { value ?: unknown } ) . value === "string" ? ( r as { value : string } ) . value . trim ( ) : undefined ;
81- return field ? { field, value } : null ;
82- } )
83- . filter ( ( r ) : r is ReportedFieldInput => r !== null ) :[ ] ;
84-
85- for ( const rf of reportedFields ) {
86- if ( ! ALLOWED_FIELDS . includes ( rf . field ) ) {
87- return NextResponse . json (
88- { error : `Invalid field: ${ rf . field } ` } ,
89- { status : 400 }
90- ) ;
91- }
92- if ( rf . field === "exam" && rf . value ) {
93- if ( ! exams . some ( e => e . toLowerCase ( ) === rf . value ?. toLowerCase ( ) ) ) {
94- return NextResponse . json (
95- { error : `Invalid exam value: ${ rf . value } ` } ,
96- { status : 400 }
97- ) ;
98- }
99- }
100- }
101-
102- const newReport = await TagReport . create ( {
103- paperId,
104- reportedFields,
105- comment : typeof body . comment === "string" ? body . comment : undefined ,
106- reporterEmail : typeof body . reporterEmail === "string" ? body . reporterEmail : undefined ,
107- reporterId : typeof body . reporterId === "string" ? body . reporterId : undefined ,
108-
109- } ) ;
17+ await rateLimitCheck ( req , paperId ) ;
18+ const newReport = await reportTag ( paperId , body ) ;
11019
11120 return NextResponse . json (
11221 { message : "Report submitted." , report : newReport } ,
11322 { status : 201 }
11423 ) ;
11524 } catch ( err ) {
11625 console . error ( err ) ;
117- return NextResponse . json (
118- { error : "Failed to submit tag report." } ,
119- { status : 500 }
120- ) ;
26+ return customErrorHandler ( err , "Failed to submit tag report." ) ;
12127 }
12228}
0 commit comments