11import { NextResponse } from "next/server" ;
22import { PDFDocument } from "pdf-lib" ;
33import { connectToDatabase } from "@/lib/mongoose" ;
4- import cloudinary from "cloudinary" ;
5- import type { CloudinaryUploadResult } from "@/interface" ;
64import { PaperAdmin } from "@/db/papers" ;
5+ import { Storage } from "@google-cloud/storage" ;
6+
7+ interface GCPCredentials {
8+ type : string ;
9+ project_id : string ;
10+ private_key_id : string ;
11+ private_key : string ;
12+ client_email : string ;
13+ client_id : string ;
14+ auth_uri : string ;
15+ token_uri : string ;
16+ auth_provider_x509_cert_url : string ;
17+ client_x509_cert_url : string ;
18+ universe_domain ?: string ;
19+ }
720
8- cloudinary . v2 . config ( {
9- cloud_name : process . env . NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME ,
10- api_key : process . env . CLOUDINARY_API_KEY ,
11- api_secret : process . env . CLOUDINARY_SECRET ,
12- } ) ;
13-
14- const config1 = {
15- cloud_name : process . env . NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME_1 ,
16- api_key : process . env . CLOUDINARY_API_KEY_1 ,
17- api_secret : process . env . CLOUDINARY_SECRET_1 ,
18- } ;
21+ // Initialize GCP Storage
22+ const credentials : GCPCredentials = JSON . parse (
23+ process . env . GOOGLE_APPLICATION_CREDENTIALS_JSON ?? "{}" ,
24+ ) as GCPCredentials ;
1925
20- const config2 = {
21- cloud_name : process . env . NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME_2 ,
22- api_key : process . env . CLOUDINARY_API_KEY_2 ,
23- api_secret : process . env . CLOUDINARY_SECRET_2 ,
24- } ;
26+ const storage = new Storage ( {
27+ projectId : process . env . GOOGLE_CLOUD_PROJECT ,
28+ credentials,
29+ } ) ;
2530
26- const cloudinaryConfigs = [ config1 , config2 ] ;
31+ const bucketName = process . env . GOOGLE_CLOUD_BUCKET ?? "" ;
32+ const bucket = storage . bucket ( bucketName ) ;
2733
2834export async function POST ( req : Request ) {
2935 try {
30- if ( ! process . env . NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET ) {
31- return NextResponse . json (
32- { message : "ServerMisconfiguration" } ,
33- { status : 500 } ,
34- ) ;
35- }
3636 await connectToDatabase ( ) ;
37- const count : number = await PaperAdmin . countDocuments ( ) ;
38- const configIndex = count % cloudinaryConfigs . length ;
39- console . log ( configIndex ) ;
40- cloudinary . v2 . config ( cloudinaryConfigs [ configIndex ] ) ;
4137
42- const uploadPreset = process . env . NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET ;
4338 const formData = await req . formData ( ) ;
44- const files : File [ ] = formData . getAll ( "files" ) as File [ ] ;
39+ const files = formData . getAll ( "files" ) as File [ ] ;
4540 const isPdf = formData . get ( "isPdf" ) === "true" ;
41+ const thumb = formData . get ( "thumbnail" ) as File | null ;
4642
47- let pdfData = "" ;
48-
49- if ( isPdf && files . length > 0 && files [ 0 ] ) {
50- const pdfFile = files [ 0 ] ;
51- const pdfBytes = await pdfFile . arrayBuffer ( ) ;
52- const pdfBuffer = Buffer . from ( pdfBytes ) ;
53- pdfData = pdfBuffer . toString ( "base64" ) ;
54- } else if ( files . length > 0 ) {
55- const pdfBytes = await CreatePDF ( files ) ;
56- const pdfBuffer = Buffer . from ( pdfBytes ) ;
57- pdfData = pdfBuffer . toString ( "base64" ) ;
58- }
43+ console . log ( "Received thumbnail:" , thumb ? thumb . name : "NULL" ) ;
5944
60- let final_url : string | undefined = "" ;
61- let public_id_cloudinary : string | undefined = "" ;
6245
6346 if ( ! files || files . length === 0 ) {
64- return NextResponse . json (
65- { error : "No files received." } ,
66- { status : 400 } ,
67- ) ;
47+ return NextResponse . json ( { error : "No files received." } , { status : 400 } ) ;
6848 }
6949
70- if ( ! isPdf ) {
71- try {
72- if ( ! process . env . NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET ) {
73- return ;
74- }
75-
76- const mergedPdfBytes = await CreatePDF ( files ) ;
77- [ public_id_cloudinary , final_url ] = await uploadPDFFile (
78- mergedPdfBytes ,
79- uploadPreset ,
80- ) ;
81- } catch ( error ) {
82- console . error ( "Error creating PDF:" , error ) ;
83- return NextResponse . json (
84- { error : "Failed to process PDF" } ,
85- { status : 500 } ,
86- ) ;
87- }
50+ let pdfBytes : ArrayBuffer ;
51+ if ( isPdf && files [ 0 ] ) {
52+ pdfBytes = await files [ 0 ] . arrayBuffer ( ) ;
8853 } else {
89- [ public_id_cloudinary , final_url ] = await uploadPDFFile (
90- files [ 0 ] ! ,
91- uploadPreset ,
92- ) ;
54+ pdfBytes = await createPDFfromImages ( files ) ;
9355 }
9456
95- const paper = new PaperAdmin ( {
96- cloudinary_index : configIndex ,
57+ const { file_url, thumbnail_url } = await uploadToGCS ( pdfBytes , thumb ) ;
9758
98- public_id_cloudinary,
99- final_url,
100- thumbnail_url : null ,
59+ // Save in MongoDB
60+ const paper = new PaperAdmin ( {
61+ file_url,
62+ thumbnail_url,
10163 subject : null ,
10264 slot : null ,
10365 year : null ,
10466 exam : null ,
10567 semester : null ,
106- campus : null ,
68+ campus : formData . get ( "campus" ) ,
10769 ambiguous_tags : [ ] ,
10870 } ) ;
109-
11071 await paper . save ( ) ;
111- return NextResponse . json ( { status : "success" } , { status : 201 } ) ;
72+
73+ return NextResponse . json (
74+ { status : "success" , file_url, thumbnail_url } ,
75+ { status : 201 } ,
76+ ) ;
11277 } catch ( error ) {
11378 console . error ( error ) ;
11479 return NextResponse . json (
@@ -118,63 +83,52 @@ export async function POST(req: Request) {
11883 }
11984}
12085
121- async function uploadPDFFile ( file : File | ArrayBuffer , uploadPreset : string ) {
122- let bytes ;
123- if ( file instanceof File ) {
124- bytes = await file . arrayBuffer ( ) ;
125- } else {
126- bytes = file ;
86+ async function uploadToGCS ( bytes : ArrayBuffer , thumbFile ?: File | null ) {
87+ const buffer = Buffer . from ( bytes ) ;
88+
89+ const pdfFilename = `papers/${ Date . now ( ) } -${ Math . random ( )
90+ . toString ( 36 )
91+ . substring ( 2 ) } .pdf`;
92+ await bucket . file ( pdfFilename ) . save ( buffer , {
93+ resumable : false ,
94+ contentType : "application/pdf" ,
95+ } ) ;
96+ const file_url = `https://storage.googleapis.com/${ bucketName } /${ pdfFilename } ` ;
97+
98+ let thumbnail_url : string | null = null ;
99+ if ( thumbFile ) {
100+ const thumbBuffer = Buffer . from ( await thumbFile . arrayBuffer ( ) ) ;
101+ const thumbFilename = pdfFilename . replace ( ".pdf" , ".png" ) ;
102+ await bucket . file ( thumbFilename ) . save ( thumbBuffer , {
103+ resumable : false ,
104+ contentType : "image/png" ,
105+ } ) ;
106+ thumbnail_url = `https://storage.googleapis.com/${ bucketName } /${ thumbFilename } ` ;
127107 }
128- return uploadFile ( bytes , uploadPreset , "application/pdf" ) ;
129- }
130-
131- async function uploadFile (
132- bytes : ArrayBuffer ,
133- uploadPreset : string ,
134- fileType : string ,
135- ) {
136- try {
137- const buffer = Buffer . from ( bytes ) ;
138- const dataUrl = `data:${ fileType } ;base64,${ buffer . toString ( "base64" ) } ` ;
139-
140- const uploadResult = ( await cloudinary . v2 . uploader . unsigned_upload (
141- dataUrl ,
142- uploadPreset ,
143- ) ) as CloudinaryUploadResult ;
144108
145- return [ uploadResult . public_id , uploadResult . secure_url ] ;
146- } catch ( e ) {
147- throw e ;
148- }
109+ return { file_url, thumbnail_url } ;
149110}
150111
151- async function CreatePDF ( orderedFiles : File [ ] ) {
112+ async function createPDFfromImages ( files : File [ ] ) {
152113 const pdfDoc = await PDFDocument . create ( ) ;
153114
154- for ( const file of orderedFiles ) {
155- const fileBlob = new Blob ( [ file ] ) ;
156- const imgBytes = Buffer . from ( await fileBlob . arrayBuffer ( ) ) ;
115+ for ( const file of files ) {
116+ const imgBytes = Buffer . from ( await file . arrayBuffer ( ) ) ;
157117 let img ;
158- if ( file instanceof File ) {
159- if ( file . type === "image/png" ) {
160- img = await pdfDoc . embedPng ( imgBytes ) ;
161- } else if ( file . type === "image/jpeg" || file . type === "image/jpg" ) {
162- img = await pdfDoc . embedJpg ( imgBytes ) ;
163- } else {
164- continue ;
165- }
166- const page = pdfDoc . addPage ( [ img . width , img . height ] ) ;
167- page . drawImage ( img , {
168- x : 0 ,
169- y : 0 ,
170- width : img . width ,
171- height : img . height ,
172- } ) ;
173- }
118+ if ( file . type === "image/png" ) {
119+ img = await pdfDoc . embedPng ( imgBytes ) ;
120+ } else if ( file . type === "image/jpeg" || file . type === "image/jpg" ) {
121+ img = await pdfDoc . embedJpg ( imgBytes ) ;
122+ } else continue ;
123+
124+ const page = pdfDoc . addPage ( [ img . width , img . height ] ) ;
125+ page . drawImage ( img , { x : 0 , y : 0 , width : img . width , height : img . height } ) ;
174126 }
175127
176128 const mergedPdfBytes = await pdfDoc . save ( ) ;
177129 const ab = new ArrayBuffer ( mergedPdfBytes . byteLength ) ;
178130 new Uint8Array ( ab ) . set ( mergedPdfBytes ) ;
179131 return ab ;
180132}
133+
134+ export const runtime = "nodejs" ;
0 commit comments