diff --git a/src/app/api/paper-by-id/[id]/route.ts b/src/app/api/paper-by-id/[id]/route.ts index 243754e..28b88c4 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 104b25c..a0fbf2c 100644 --- a/src/app/api/request/route.ts +++ b/src/app/api/request/route.ts @@ -1,24 +1,16 @@ -import { connectToDatabase } from "@/lib/database/mongoose"; -import PaperRequest from "@/db/paperRequest"; 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 { - 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()) as CreatePaperInputType if (!subject || !exam || !slot || !year) { return failure("All fields are required.", 400); } - const newRequest = await PaperRequest.create({ 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/selected-papers/route.ts b/src/app/api/selected-papers/route.ts index ba02d77..0a5a7a1 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/app/api/upcoming-papers/route.ts b/src/app/api/upcoming-papers/route.ts index 1ba624f..96e8140 100644 --- a/src/app/api/upcoming-papers/route.ts +++ b/src/app/api/upcoming-papers/route.ts @@ -1,17 +1,12 @@ -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 +16,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/app/api/upload/route.ts b/src/app/api/upload/route.ts index 67d4fb5..471b5cb 100644 --- a/src/app/api/upload/route.ts +++ b/src/app/api/upload/route.ts @@ -1,84 +1,33 @@ -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/app/api/user-papers/route.ts b/src/app/api/user-papers/route.ts index 7a48d7d..8473ff5 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 = await getPapersBySubjects(subjects) return success(transformedPapers); } catch (error) { diff --git a/src/lib/services/paper.ts b/src/lib/services/paper.ts index b2fd146..4eb52f4 100644 --- a/src/lib/services/paper.ts +++ b/src/lib/services/paper.ts @@ -4,6 +4,16 @@ 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"; +import { type StoredSubjects } from "@/interface"; +import { transformPapersToSubjectSlots } from "@/lib/services/paper-transform"; + +export interface CreatePaperInputType { + subject: string; + exam: string; + slot: string; + year: string; +} export async function getPapersBySubject(subject: string) { if (!subject){ @@ -47,4 +57,24 @@ export async function getCourseCounts(){ })); return formatted; +} + +export async function createPaperRequest({ subject, exam, slot, year} : CreatePaperInputType){ + await connectToDatabase(); + return await PaperRequest.create({ subject, exam, slot, year }); +} + +export async function getSelectedPapers() { + await connectToDatabase(); + 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 diff --git a/src/lib/services/subject.ts b/src/lib/services/subject.ts index e59c072..593139b 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,12 @@ export async function getRelatedSubjects(subject: string) { }); return subjects[0]?.related_subjects ?? []; +} + +export async function getUpcomingSubjects() { + await connectToDatabase(); + return await UpcomingSubject.find() + .sort({ _id: 1 }) + .limit(16) + .lean(); } \ 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 0000000..9492c58 --- /dev/null +++ b/src/lib/services/upload.ts @@ -0,0 +1,85 @@ +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 + +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 { + await connectToDatabase(); + + 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 }; +}