@@ -14,9 +14,19 @@ import crypto from "crypto";
1414import { paymentService } from "./services/payment.service.js" ;
1515import { verifyToken } from "./utils/auth.js" ;
1616import { SUBSCRIPTION_STATUS } from "./constants/subscription.js" ;
17+ import multer from "multer" ;
18+ import { v2 as cloudinary } from "cloudinary" ;
19+ import { handleRazorpayWebhook } from "./webhooks.js" ;
1720
1821dotenv . config ( ) ;
1922
23+ // Configure Cloudinary (kept local to this route)
24+ cloudinary . config ( {
25+ cloud_name : process . env . CLOUDINARY_CLOUD_NAME || "" ,
26+ api_key : process . env . CLOUDINARY_API_KEY || "" ,
27+ api_secret : process . env . CLOUDINARY_API_SECRET || "" ,
28+ } ) ;
29+
2030const app = express ( ) ;
2131const PORT = process . env . PORT || 4000 ;
2232const CORS_ORIGINS = process . env . CORS_ORIGINS
@@ -62,8 +72,9 @@ const apiLimiter = rateLimit({
6272
6373// Request size limits (except for webhook - needs raw body)
6474app . use ( "/webhook/razorpay" , express . raw ( { type : "application/json" } ) ) ;
65- app . use ( express . json ( { limit : "50mb" } ) ) ;
66- app . use ( express . urlencoded ( { limit : "50mb" , extended : true } ) ) ;
75+ // Reduce global JSON/urlencoded limits to prevent DoS
76+ app . use ( express . json ( { limit : "5mb" } ) ) ;
77+ app . use ( express . urlencoded ( { limit : "5mb" , extended : true } ) ) ;
6778
6879// CORS configuration
6980const corsOptions : CorsOptionsType = {
@@ -98,6 +109,61 @@ app.get("/test", apiLimiter, (req: Request, res: Response) => {
98109 res . status ( 200 ) . json ( { status : "ok" , message : "Test endpoint is working" } ) ;
99110} ) ;
100111
112+ // Secure multipart upload setup with strict validation
113+ const upload = multer ( {
114+ storage : multer . memoryStorage ( ) , // avoid temp files; stream to Cloudinary
115+ limits : {
116+ fileSize : 10 * 1024 * 1024 , // 10MB per-file limit for this endpoint
117+ files : 1 ,
118+ } ,
119+ fileFilter : ( _req , file , cb ) => {
120+ const allowed = [ "image/png" , "image/jpeg" , "image/jpg" , "image/webp" ] ;
121+ if ( ! allowed . includes ( file . mimetype ) ) {
122+ return cb ( new Error ( "Invalid file type" ) ) ;
123+ }
124+ cb ( null , true ) ;
125+ } ,
126+ } ) ;
127+
128+ // Dedicated upload endpoint that only accepts multipart/form-data
129+ app . post (
130+ "/upload/sponsor-image" ,
131+ apiLimiter ,
132+ ( req , res , next ) => {
133+ if ( ! req . is ( "multipart/form-data" ) ) {
134+ return res . status ( 415 ) . json ( { error : "Unsupported Media Type. Use multipart/form-data." } ) ;
135+ }
136+ next ( ) ;
137+ } ,
138+ upload . single ( "file" ) ,
139+ async ( req : Request , res : Response ) => {
140+ try {
141+ if ( ! req . file ) {
142+ return res . status ( 400 ) . json ( { error : "No file provided" } ) ;
143+ }
144+
145+ // Stream upload to Cloudinary
146+ const folder = "opensox/sponsors" ;
147+ const result = await new Promise < any > ( ( resolve , reject ) => {
148+ const stream = cloudinary . uploader . upload_stream ( { folder } , ( error , uploadResult ) => {
149+ if ( error ) return reject ( error ) ;
150+ resolve ( uploadResult ) ;
151+ } ) ;
152+ stream . end ( req . file . buffer ) ;
153+ } ) ;
154+
155+ return res . status ( 200 ) . json ( {
156+ url : result . secure_url ,
157+ bytes : req . file . size ,
158+ mimetype : req . file . mimetype ,
159+ } ) ;
160+ } catch ( err : any ) {
161+ const isLimit = err ?. message ?. toLowerCase ( ) ?. includes ( "file too large" ) ;
162+ return res . status ( isLimit ? 413 : 400 ) . json ( { error : err . message || "Upload failed" } ) ;
163+ }
164+ }
165+ ) ;
166+
101167// Slack Community Invite Endpoint (Protected)
102168app . get ( "/join-community" , apiLimiter , async ( req : Request , res : Response ) => {
103169 try {
@@ -153,8 +219,6 @@ app.get("/join-community", apiLimiter, async (req: Request, res: Response) => {
153219 }
154220} ) ;
155221
156- import { handleRazorpayWebhook } from "./webhooks.js" ;
157-
158222// Razorpay Webhook Handler
159223app . post ( "/webhook/razorpay" , handleRazorpayWebhook ) ;
160224
0 commit comments