From 07dd8a1c22e55c0c638ddd43ff2be58c2dcd6c62 Mon Sep 17 00:00:00 2001 From: Gslmao Date: Thu, 18 Jun 2026 11:13:01 +0530 Subject: [PATCH 1/7] logic seperation from request endpoint --- src/app/api/request/route.ts | 15 +++------------ src/lib/services/paper.ts | 12 ++++++++++++ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/app/api/request/route.ts b/src/app/api/request/route.ts index 104b25cd..2aa769f7 100644 --- a/src/app/api/request/route.ts +++ b/src/app/api/request/route.ts @@ -1,24 +1,15 @@ -import { connectToDatabase } from "@/lib/database/mongoose"; -import PaperRequest from "@/db/paperRequest"; import { success, failure } from "@/lib/utils/response"; +import { createPaperRequest } from "@/lib/services/paper" export async function POST(req: Request) { try { - await connectToDatabase(); - const body = (await req.json()) as { - subject: string; - exam: string; - slot: string; - year: string; - }; - - const { subject, exam, slot, year } = body; + const {subject, exam, slot, year} = await req.json() if (!subject || !exam || !slot || !year) { return failure("All fields are required.", 400); } - const newRequest = await PaperRequest.create({ subject, exam, slot, year }); + const newRequest = createPaperRequest({subject, exam, slot, year}); return success({ message: "Paper request submitted successfully!", request: newRequest }, "Created", 201); } catch (error) { console.error("Error creating paper request:", error); diff --git a/src/lib/services/paper.ts b/src/lib/services/paper.ts index b2fd1463..6dd9eaf8 100644 --- a/src/lib/services/paper.ts +++ b/src/lib/services/paper.ts @@ -4,6 +4,14 @@ import { escapeRegExp } from "@/lib/utils/regex"; import { extractUniqueValues } from "@/lib/utils/paper-aggregation"; import { connectToDatabase } from "../database/mongoose"; import CourseCount from "@/db/course"; +import PaperRequest from "@/db/paperRequest"; + +type CreatePaperRequestInput = { + subject: string; + exam: string; + slot: string; + year: string; +} export async function getPapersBySubject(subject: string) { if (!subject){ @@ -47,4 +55,8 @@ export async function getCourseCounts(){ })); return formatted; +} + +export async function createPaperRequest({ subject, exam, slot, year} : CreatePaperRequestInput){ + return await PaperRequest.create({ subject, exam, slot, year }); } \ No newline at end of file From e51cefff2a027a3e072313034597871e66dc77c9 Mon Sep 17 00:00:00 2001 From: Gslmao Date: Thu, 18 Jun 2026 11:14:41 +0530 Subject: [PATCH 2/7] logic seperation from upcoing-papers endpoint --- src/app/api/upcoming-papers/route.ts | 11 ++++------- src/lib/services/subject.ts | 8 ++++++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/app/api/upcoming-papers/route.ts b/src/app/api/upcoming-papers/route.ts index 1ba624f7..6633d04a 100644 --- a/src/app/api/upcoming-papers/route.ts +++ b/src/app/api/upcoming-papers/route.ts @@ -1,17 +1,14 @@ import { connectToDatabase } from "@/lib/database/mongoose"; -import UpcomingSubject from "@/db/upcoming-paper"; import { success, failure } from "@/lib/utils/response"; +import { getUpcomingSubjects } from "@/lib/services/subject"; export const dynamic = "force-dynamic"; export async function GET() { try { await connectToDatabase(); - const selectedSubjects = await UpcomingSubject.find() - .sort({ _id: 1 }) - .limit(16) - .lean(); - + const selectedSubjects = await getUpcomingSubjects(); + if (selectedSubjects.length === 0) { return failure("No selected papers found.", 404); } @@ -21,4 +18,4 @@ export async function GET() { console.error("Error fetching papers:", error); return failure("Failed to fetch papers.", 500); } -} +} \ No newline at end of file diff --git a/src/lib/services/subject.ts b/src/lib/services/subject.ts index e59c072e..f4293fcc 100644 --- a/src/lib/services/subject.ts +++ b/src/lib/services/subject.ts @@ -3,6 +3,7 @@ import { type IRelatedSubject } from "@/interface"; import { escapeRegExp } from "@/lib/utils/regex"; import { Course } from "@/db/course"; import RelatedSubject from "@/db/relatedSubjects"; +import UpcomingSubject from "@/db/upcoming-paper"; export async function getCourseList(){ await connectToDatabase(); @@ -17,4 +18,11 @@ export async function getRelatedSubjects(subject: string) { }); return subjects[0]?.related_subjects ?? []; +} + +export async function getUpcomingSubjects() { + return await UpcomingSubject.find() + .sort({ _id: 1 }) + .limit(16) + .lean(); } \ No newline at end of file From 259417a844fe19516c674beae2be449f9ba0c188 Mon Sep 17 00:00:00 2001 From: Gslmao Date: Thu, 18 Jun 2026 11:19:00 +0530 Subject: [PATCH 3/7] logic seperation from upload endpoint --- src/app/api/upload/route.ts | 73 ++++++--------------------------- src/lib/services/upload.ts | 82 +++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 61 deletions(-) create mode 100644 src/lib/services/upload.ts diff --git a/src/app/api/upload/route.ts b/src/app/api/upload/route.ts index 67d4fb50..008545dc 100644 --- a/src/app/api/upload/route.ts +++ b/src/app/api/upload/route.ts @@ -1,84 +1,35 @@ import { connectToDatabase } from "@/lib/database/mongoose"; -import { PaperAdmin } from "@/db/papers"; -import { createPDFfromImages, compressPDF } from "@/lib/storage/pdf"; -import { uploadPDF, uploadThumbnail } from "@/lib/storage/gcp"; import { success, failure } from "@/lib/utils/response"; +import { uploadPaper } from "@/lib/services/upload"; export const runtime = "nodejs"; -const MAX_COMPRESSED_PDF_SIZE = 5 * 1024 * 1024; // 5MB compressed -const COMPRESS_THRESHOLD = 5 * 1024 * 1024; // 5MB - export async function POST(req: Request) { try { await connectToDatabase(); const formData = await req.formData(); const files = formData.getAll("files").filter(Boolean) as File[]; const isPdf = formData.get("isPdf") === "true"; - const thumb = formData.get("thumbnail") as File | null; + const thumbnail = formData.get("thumbnail") as File | null; + const campus = formData.get("campus") as string | null; if (files.length === 0) { return failure("No files received.", 400); } - let pdfBytes: Uint8Array; - if (isPdf) { - if (!files[0]) { - return failure("No PDF file provided.", 400); - } - - const rawPdfBytes = new Uint8Array(await files[0].arrayBuffer()); - if (rawPdfBytes.length > COMPRESS_THRESHOLD) { - const compressedPdfBytes = await compressPDF(rawPdfBytes); - pdfBytes = compressedPdfBytes.length <= rawPdfBytes.length - ? compressedPdfBytes - : rawPdfBytes; + const result = await uploadPaper({ files, isPdf, thumbnail, campus }); - if (pdfBytes.length > MAX_COMPRESSED_PDF_SIZE) { - return failure( - "PDF is too large after compression. The compressed file must be under 5MB.", - 413, - ); - } - } else { - pdfBytes = rawPdfBytes; - } - } else { - pdfBytes = await createPDFfromImages(files); - if (pdfBytes.length > MAX_COMPRESSED_PDF_SIZE) { - return failure( - "Generated PDF is too large after compression. Please upload fewer or smaller images.", - 413, - ); - } + if (!result.success) { + return failure(result.message, result.status); } - const buffer = Buffer.from(pdfBytes); - - const file_url = await uploadPDF("unapproved", buffer); - - let thumbnail_url: string | null = null; - if (thumb) { - const thumbBuffer = Buffer.from(await thumb.arrayBuffer()); - thumbnail_url = await uploadThumbnail(thumbBuffer, file_url); - } - - const paper = new PaperAdmin({ - file_url, - thumbnail_url, - campus: formData.get("campus"), - subject: null, - slot: null, - year: null, - exam: null, - semester: null, - ambiguous_tags: [], - }); - await paper.save(); - - return success({ file_url, thumbnail_url }, "Created", 201); + return success( + { file_url: result.file_url, thumbnail_url: result.thumbnail_url }, + "Created", + 201, + ); } catch (error) { console.error(error); return failure("Failed to upload papers", 500); } -} +} \ No newline at end of file diff --git a/src/lib/services/upload.ts b/src/lib/services/upload.ts new file mode 100644 index 00000000..4daa91f2 --- /dev/null +++ b/src/lib/services/upload.ts @@ -0,0 +1,82 @@ +import { PaperAdmin } from "@/db/papers"; +import { createPDFfromImages, compressPDF } from "@/lib/storage/pdf"; +import { uploadPDF, uploadThumbnail } from "@/lib/storage/gcp"; + +const MAX_COMPRESSED_PDF_SIZE = 5 * 1024 * 1024; // 5MB compressed +const COMPRESS_THRESHOLD = 5 * 1024 * 1024; // 5MB + +type UploadPaperInput = { + files: File[]; + isPdf: boolean; + thumbnail: File | null; + campus: string | null; +}; + +type UploadPaperResult = + | { success: true; file_url: string; thumbnail_url: string | null } + | { success: false; message: string; status: number }; + +export async function uploadPaper({ + files, + isPdf, + thumbnail, + campus, +}: UploadPaperInput): Promise { + let pdfBytes: Uint8Array; + + if (isPdf) { + if (!files[0]) { + return { success: false, message: "No PDF file provided.", status: 400 }; + } + + const rawPdfBytes = new Uint8Array(await files[0].arrayBuffer()); + if (rawPdfBytes.length > COMPRESS_THRESHOLD) { + const compressedPdfBytes = await compressPDF(rawPdfBytes); + pdfBytes = + compressedPdfBytes.length <= rawPdfBytes.length ? compressedPdfBytes : rawPdfBytes; + + if (pdfBytes.length > MAX_COMPRESSED_PDF_SIZE) { + return { + success: false, + message: "PDF is too large after compression. The compressed file must be under 5MB.", + status: 413, + }; + } + } else { + pdfBytes = rawPdfBytes; + } + } else { + pdfBytes = await createPDFfromImages(files); + if (pdfBytes.length > MAX_COMPRESSED_PDF_SIZE) { + return { + success: false, + message: "Generated PDF is too large after compression. Please upload fewer or smaller images.", + status: 413, + }; + } + } + + const buffer = Buffer.from(pdfBytes); + const file_url = await uploadPDF("unapproved", buffer); + + let thumbnail_url: string | null = null; + if (thumbnail) { + const thumbBuffer = Buffer.from(await thumbnail.arrayBuffer()); + thumbnail_url = await uploadThumbnail(thumbBuffer, file_url); + } + + const paper = new PaperAdmin({ + file_url, + thumbnail_url, + campus, + subject: null, + slot: null, + year: null, + exam: null, + semester: null, + ambiguous_tags: [], + }); + await paper.save(); + + return { success: true, file_url, thumbnail_url }; +} From 264218c60121b04097d10d73f0f93ad4298df84f Mon Sep 17 00:00:00 2001 From: Gslmao Date: Thu, 18 Jun 2026 11:24:23 +0530 Subject: [PATCH 4/7] logic seperation from selected-papers endpoint --- src/app/api/selected-papers/route.ts | 9 +++------ src/lib/services/paper.ts | 4 ++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/app/api/selected-papers/route.ts b/src/app/api/selected-papers/route.ts index ba02d775..0a5a7a11 100644 --- a/src/app/api/selected-papers/route.ts +++ b/src/app/api/selected-papers/route.ts @@ -1,14 +1,11 @@ -import { connectToDatabase } from "@/lib/database/mongoose"; -import Paper from "@/db/papers"; import { success, failure } from "@/lib/utils/response"; +import { getSelectedPapers } from "@/lib/services/paper"; export const dynamic = "force-dynamic"; export async function GET() { try { - await connectToDatabase(); - - const selectedPapers = await Paper.find({ isSelected: true }).limit(8); + const selectedPapers = await getSelectedPapers(); if (selectedPapers.length === 0) { return failure("No selected papers found.", 404); @@ -18,4 +15,4 @@ export async function GET() { console.error("Error fetching papers:", error); return failure("Failed to fetch papers.", 500); } -} +} \ No newline at end of file diff --git a/src/lib/services/paper.ts b/src/lib/services/paper.ts index 6dd9eaf8..4fde39c8 100644 --- a/src/lib/services/paper.ts +++ b/src/lib/services/paper.ts @@ -59,4 +59,8 @@ export async function getCourseCounts(){ export async function createPaperRequest({ subject, exam, slot, year} : CreatePaperRequestInput){ return await PaperRequest.create({ subject, exam, slot, year }); +} + +export async function getSelectedPapers() { + return await Paper.find({ isSelected: true }).limit(8); } \ No newline at end of file From cb8e4d149360abe170943093d8bc3a16eafe6dcd Mon Sep 17 00:00:00 2001 From: Gslmao Date: Thu, 18 Jun 2026 11:42:43 +0530 Subject: [PATCH 5/7] logic seperation from user-papers endpoint --- src/app/api/user-papers/route.ts | 13 ++----------- src/lib/services/paper.ts | 12 ++++++++++++ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/app/api/user-papers/route.ts b/src/app/api/user-papers/route.ts index 7a48d7dc..d1fbe152 100644 --- a/src/app/api/user-papers/route.ts +++ b/src/app/api/user-papers/route.ts @@ -1,23 +1,14 @@ -import { connectToDatabase } from "@/lib/database/mongoose"; -import Paper from "@/db/papers"; import { type StoredSubjects } from "@/interface"; -import { transformPapersToSubjectSlots } from "@/lib/services/paper-transform"; +import { getPapersBySubjects } from "@/lib/services/paper" import { success, failure } from "@/lib/utils/response"; export const dynamic = "force-dynamic"; export async function POST(req: Request) { try { - await connectToDatabase(); const subjects = (await req.json()) as StoredSubjects; - const usersPapers = await Paper.find({ - subject: { $in: subjects }, - }); - - console.log("Fetched user papers:", usersPapers); - - const transformedPapers = transformPapersToSubjectSlots(usersPapers); + const transformedPapers = getPapersBySubjects(subjects) return success(transformedPapers); } catch (error) { diff --git a/src/lib/services/paper.ts b/src/lib/services/paper.ts index 4fde39c8..3903ad2b 100644 --- a/src/lib/services/paper.ts +++ b/src/lib/services/paper.ts @@ -5,6 +5,8 @@ import { extractUniqueValues } from "@/lib/utils/paper-aggregation"; import { connectToDatabase } from "../database/mongoose"; import CourseCount from "@/db/course"; import PaperRequest from "@/db/paperRequest"; +import { type StoredSubjects } from "@/interface"; +import { transformPapersToSubjectSlots } from "@/lib/services/paper-transform"; type CreatePaperRequestInput = { subject: string; @@ -63,4 +65,14 @@ export async function createPaperRequest({ subject, exam, slot, year} : CreatePa export async function getSelectedPapers() { return await Paper.find({ isSelected: true }).limit(8); +} + +export async function getPapersBySubjects(subjects: StoredSubjects) { + await connectToDatabase(); + + const usersPapers = await Paper.find({ + subject: { $in: subjects }, + }); + + return transformPapersToSubjectSlots(usersPapers); } \ No newline at end of file From 8e189c310b971e0116d7980b04fa0d35267be966 Mon Sep 17 00:00:00 2001 From: Gslmao Date: Thu, 18 Jun 2026 12:39:43 +0530 Subject: [PATCH 6/7] minor fixes --- src/app/api/paper-by-id/[id]/route.ts | 2 ++ src/app/api/request/route.ts | 2 +- src/app/api/upcoming-papers/route.ts | 2 -- src/app/api/upload/route.ts | 2 -- src/app/api/user-papers/route.ts | 2 +- src/lib/services/paper.ts | 2 ++ src/lib/services/subject.ts | 1 + src/lib/services/upload.ts | 3 +++ 8 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/app/api/paper-by-id/[id]/route.ts b/src/app/api/paper-by-id/[id]/route.ts index 243754ea..28b88c4f 100644 --- a/src/app/api/paper-by-id/[id]/route.ts +++ b/src/app/api/paper-by-id/[id]/route.ts @@ -2,6 +2,8 @@ import { Types } from "mongoose"; import { getPaperById } from "@/lib/services/paper"; import { success, failure } from "@/lib/utils/response" +export const dynamic = "force-dynamic"; + export async function GET(req: Request, { params }: { params: { id: string } }) { try { const { id } = params; diff --git a/src/app/api/request/route.ts b/src/app/api/request/route.ts index 2aa769f7..8a3a3bf9 100644 --- a/src/app/api/request/route.ts +++ b/src/app/api/request/route.ts @@ -9,7 +9,7 @@ export async function POST(req: Request) { return failure("All fields are required.", 400); } - const newRequest = createPaperRequest({subject, exam, slot, year}); + const newRequest = await createPaperRequest({subject, exam, slot, year}); return success({ message: "Paper request submitted successfully!", request: newRequest }, "Created", 201); } catch (error) { console.error("Error creating paper request:", error); diff --git a/src/app/api/upcoming-papers/route.ts b/src/app/api/upcoming-papers/route.ts index 6633d04a..96e81406 100644 --- a/src/app/api/upcoming-papers/route.ts +++ b/src/app/api/upcoming-papers/route.ts @@ -1,4 +1,3 @@ -import { connectToDatabase } from "@/lib/database/mongoose"; import { success, failure } from "@/lib/utils/response"; import { getUpcomingSubjects } from "@/lib/services/subject"; @@ -6,7 +5,6 @@ export const dynamic = "force-dynamic"; export async function GET() { try { - await connectToDatabase(); const selectedSubjects = await getUpcomingSubjects(); if (selectedSubjects.length === 0) { diff --git a/src/app/api/upload/route.ts b/src/app/api/upload/route.ts index 008545dc..471b5cb0 100644 --- a/src/app/api/upload/route.ts +++ b/src/app/api/upload/route.ts @@ -1,4 +1,3 @@ -import { connectToDatabase } from "@/lib/database/mongoose"; import { success, failure } from "@/lib/utils/response"; import { uploadPaper } from "@/lib/services/upload"; @@ -6,7 +5,6 @@ export const runtime = "nodejs"; export async function POST(req: Request) { try { - await connectToDatabase(); const formData = await req.formData(); const files = formData.getAll("files").filter(Boolean) as File[]; const isPdf = formData.get("isPdf") === "true"; diff --git a/src/app/api/user-papers/route.ts b/src/app/api/user-papers/route.ts index d1fbe152..8473ff56 100644 --- a/src/app/api/user-papers/route.ts +++ b/src/app/api/user-papers/route.ts @@ -8,7 +8,7 @@ export async function POST(req: Request) { try { const subjects = (await req.json()) as StoredSubjects; - const transformedPapers = getPapersBySubjects(subjects) + const transformedPapers = await getPapersBySubjects(subjects) return success(transformedPapers); } catch (error) { diff --git a/src/lib/services/paper.ts b/src/lib/services/paper.ts index 3903ad2b..8f424efb 100644 --- a/src/lib/services/paper.ts +++ b/src/lib/services/paper.ts @@ -60,10 +60,12 @@ export async function getCourseCounts(){ } export async function createPaperRequest({ subject, exam, slot, year} : CreatePaperRequestInput){ + await connectToDatabase(); return await PaperRequest.create({ subject, exam, slot, year }); } export async function getSelectedPapers() { + await connectToDatabase(); return await Paper.find({ isSelected: true }).limit(8); } diff --git a/src/lib/services/subject.ts b/src/lib/services/subject.ts index f4293fcc..593139b9 100644 --- a/src/lib/services/subject.ts +++ b/src/lib/services/subject.ts @@ -21,6 +21,7 @@ export async function getRelatedSubjects(subject: string) { } export async function getUpcomingSubjects() { + await connectToDatabase(); return await UpcomingSubject.find() .sort({ _id: 1 }) .limit(16) diff --git a/src/lib/services/upload.ts b/src/lib/services/upload.ts index 4daa91f2..9492c580 100644 --- a/src/lib/services/upload.ts +++ b/src/lib/services/upload.ts @@ -1,6 +1,7 @@ import { PaperAdmin } from "@/db/papers"; import { createPDFfromImages, compressPDF } from "@/lib/storage/pdf"; import { uploadPDF, uploadThumbnail } from "@/lib/storage/gcp"; +import { connectToDatabase } from "@/lib/database/mongoose"; const MAX_COMPRESSED_PDF_SIZE = 5 * 1024 * 1024; // 5MB compressed const COMPRESS_THRESHOLD = 5 * 1024 * 1024; // 5MB @@ -22,6 +23,8 @@ export async function uploadPaper({ thumbnail, campus, }: UploadPaperInput): Promise { + await connectToDatabase(); + let pdfBytes: Uint8Array; if (isPdf) { From 08246c7304d5f4c2dbd3c4e739cea63208ce8f0c Mon Sep 17 00:00:00 2001 From: Gslmao Date: Thu, 18 Jun 2026 12:42:54 +0530 Subject: [PATCH 7/7] fix: ESLint unsafe any type error --- src/app/api/request/route.ts | 3 ++- src/lib/services/paper.ts | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/api/request/route.ts b/src/app/api/request/route.ts index 8a3a3bf9..a0fbf2c6 100644 --- a/src/app/api/request/route.ts +++ b/src/app/api/request/route.ts @@ -1,9 +1,10 @@ import { success, failure } from "@/lib/utils/response"; import { createPaperRequest } from "@/lib/services/paper" +import type { CreatePaperInputType } from "@/lib/services/paper" export async function POST(req: Request) { try { - const {subject, exam, slot, year} = await req.json() + const {subject, exam, slot, year} = (await req.json()) as CreatePaperInputType if (!subject || !exam || !slot || !year) { return failure("All fields are required.", 400); diff --git a/src/lib/services/paper.ts b/src/lib/services/paper.ts index 8f424efb..4eb52f43 100644 --- a/src/lib/services/paper.ts +++ b/src/lib/services/paper.ts @@ -8,7 +8,7 @@ import PaperRequest from "@/db/paperRequest"; import { type StoredSubjects } from "@/interface"; import { transformPapersToSubjectSlots } from "@/lib/services/paper-transform"; -type CreatePaperRequestInput = { +export interface CreatePaperInputType { subject: string; exam: string; slot: string; @@ -59,7 +59,7 @@ export async function getCourseCounts(){ return formatted; } -export async function createPaperRequest({ subject, exam, slot, year} : CreatePaperRequestInput){ +export async function createPaperRequest({ subject, exam, slot, year} : CreatePaperInputType){ await connectToDatabase(); return await PaperRequest.create({ subject, exam, slot, year }); }