@@ -22,11 +22,32 @@ const SPONSOR_CURRENCY = "USD";
2222export const sponsorRouter = router ( {
2323 // upload image to cloudinary (public, no auth required)
2424 uploadImage : publicProcedure
25- . input ( z . object ( { file : z . string ( ) } ) )
25+ . input (
26+ z . object ( {
27+ file : z
28+ . string ( )
29+ . refine (
30+ ( val ) => {
31+ // Accept data URLs and raw base64 strings; size under 5MB
32+ const isDataUrl = / ^ d a t a : .* ; b a s e 6 4 , / . test ( val ) ;
33+ const base64Payload = isDataUrl ? val . split ( "," ) [ 1 ] ?? "" : val ;
34+ // Base64 size approximation: bytes = (length * 3) / 4
35+ const base64SizeBytes = Math . floor ( ( base64Payload . length * 3 ) / 4 ) ;
36+ const under5MB = base64SizeBytes > 0 && base64SizeBytes < 5 * 1024 * 1024 ;
37+ // If data URL, ensure it's an image MIME type
38+ const mimeOk = ! isDataUrl || / ^ d a t a : i m a g e \/ ( p n g | j p e ? g | w e b p ) ; b a s e 6 4 , / . test ( val ) ;
39+ return under5MB && mimeOk ;
40+ } ,
41+ { message : "file must be an image data URL or base64 under 5MB (png/jpg/jpeg/webp)" }
42+ ) ,
43+ } )
44+ )
2645 . mutation ( async ( { input } : { input : { file : string } } ) => {
2746 try {
2847 const result = await cloudinary . uploader . upload ( input . file , {
2948 folder : "opensox/sponsors" ,
49+ resource_type : "image" ,
50+ allowed_formats : [ "jpg" , "jpeg" , "png" , "webp" ] ,
3051 } ) ;
3152 return { url : result . secure_url } ;
3253 } catch ( error ) {
0 commit comments