@@ -12,10 +12,12 @@ import {
1212 BLOB_CONFIG ,
1313 BLOB_COPILOT_CONFIG ,
1414 BLOB_KB_CONFIG ,
15+ BLOB_PROFILE_PICTURES_CONFIG ,
1516 S3_CHAT_CONFIG ,
1617 S3_CONFIG ,
1718 S3_COPILOT_CONFIG ,
1819 S3_KB_CONFIG ,
20+ S3_PROFILE_PICTURES_CONFIG ,
1921} from '@/lib/uploads/setup'
2022import { validateFileType } from '@/lib/uploads/validation'
2123import { createErrorResponse , createOptionsResponse } from '@/app/api/files/utils'
@@ -30,7 +32,7 @@ interface PresignedUrlRequest {
3032 chatId ?: string
3133}
3234
33- type UploadType = 'general' | 'knowledge-base' | 'chat' | 'copilot'
35+ type UploadType = 'general' | 'knowledge-base' | 'chat' | 'copilot' | 'profile-pictures'
3436
3537class PresignedUrlError extends Error {
3638 constructor (
@@ -96,7 +98,9 @@ export async function POST(request: NextRequest) {
9698 ? 'chat'
9799 : uploadTypeParam === 'copilot'
98100 ? 'copilot'
99- : 'general'
101+ : uploadTypeParam === 'profile-pictures'
102+ ? 'profile-pictures'
103+ : 'general'
100104
101105 if ( uploadType === 'knowledge-base' ) {
102106 const fileValidationError = validateFileType ( fileName , contentType )
@@ -121,6 +125,21 @@ export async function POST(request: NextRequest) {
121125 }
122126 }
123127
128+ // Validate profile picture requirements
129+ if ( uploadType === 'profile-pictures' ) {
130+ if ( ! sessionUserId ?. trim ( ) ) {
131+ throw new ValidationError (
132+ 'Authenticated user session is required for profile picture uploads'
133+ )
134+ }
135+ // Only allow image uploads for profile pictures
136+ if ( ! isImageFileType ( contentType ) ) {
137+ throw new ValidationError (
138+ 'Only image files (JPEG, PNG, GIF, WebP, SVG) are allowed for profile picture uploads'
139+ )
140+ }
141+ }
142+
124143 if ( ! isUsingCloudStorage ( ) ) {
125144 throw new StorageConfigError (
126145 'Direct uploads are only available when cloud storage is enabled'
@@ -185,7 +204,9 @@ async function handleS3PresignedUrl(
185204 ? S3_CHAT_CONFIG
186205 : uploadType === 'copilot'
187206 ? S3_COPILOT_CONFIG
188- : S3_CONFIG
207+ : uploadType === 'profile-pictures'
208+ ? S3_PROFILE_PICTURES_CONFIG
209+ : S3_CONFIG
189210
190211 if ( ! config . bucket || ! config . region ) {
191212 throw new StorageConfigError ( `S3 configuration missing for ${ uploadType } uploads` )
@@ -200,6 +221,8 @@ async function handleS3PresignedUrl(
200221 prefix = 'chat/'
201222 } else if ( uploadType === 'copilot' ) {
202223 prefix = `${ userId } /`
224+ } else if ( uploadType === 'profile-pictures' ) {
225+ prefix = `${ userId } /`
203226 }
204227
205228 const uniqueKey = `${ prefix } ${ uuidv4 ( ) } -${ safeFileName } `
@@ -219,6 +242,9 @@ async function handleS3PresignedUrl(
219242 } else if ( uploadType === 'copilot' ) {
220243 metadata . purpose = 'copilot'
221244 metadata . userId = userId || ''
245+ } else if ( uploadType === 'profile-pictures' ) {
246+ metadata . purpose = 'profile-pictures'
247+ metadata . userId = userId || ''
222248 }
223249
224250 const command = new PutObjectCommand ( {
@@ -239,9 +265,9 @@ async function handleS3PresignedUrl(
239265 )
240266 }
241267
242- // For chat images and knowledge base files, use direct URLs since they need to be accessible by external services
268+ // For chat images, knowledge base files, and profile pictures , use direct URLs since they need to be accessible by external services
243269 const finalPath =
244- uploadType === 'chat' || uploadType === 'knowledge-base'
270+ uploadType === 'chat' || uploadType === 'knowledge-base' || uploadType === 'profile-pictures'
245271 ? `https://${ config . bucket } .s3.${ config . region } .amazonaws.com/${ uniqueKey } `
246272 : `/api/files/serve/s3/${ encodeURIComponent ( uniqueKey ) } `
247273
@@ -285,7 +311,9 @@ async function handleBlobPresignedUrl(
285311 ? BLOB_CHAT_CONFIG
286312 : uploadType === 'copilot'
287313 ? BLOB_COPILOT_CONFIG
288- : BLOB_CONFIG
314+ : uploadType === 'profile-pictures'
315+ ? BLOB_PROFILE_PICTURES_CONFIG
316+ : BLOB_CONFIG
289317
290318 if (
291319 ! config . accountName ||
@@ -304,6 +332,8 @@ async function handleBlobPresignedUrl(
304332 prefix = 'chat/'
305333 } else if ( uploadType === 'copilot' ) {
306334 prefix = `${ userId } /`
335+ } else if ( uploadType === 'profile-pictures' ) {
336+ prefix = `${ userId } /`
307337 }
308338
309339 const uniqueKey = `${ prefix } ${ uuidv4 ( ) } -${ safeFileName } `
@@ -339,10 +369,10 @@ async function handleBlobPresignedUrl(
339369
340370 const presignedUrl = `${ blockBlobClient . url } ?${ sasToken } `
341371
342- // For chat images, use direct Blob URLs since they need to be permanently accessible
372+ // For chat images and profile pictures , use direct Blob URLs since they need to be permanently accessible
343373 // For other files, use serve path for access control
344374 const finalPath =
345- uploadType === 'chat'
375+ uploadType === 'chat' || uploadType === 'profile-pictures'
346376 ? blockBlobClient . url
347377 : `/api/files/serve/blob/${ encodeURIComponent ( uniqueKey ) } `
348378
@@ -362,6 +392,9 @@ async function handleBlobPresignedUrl(
362392 } else if ( uploadType === 'copilot' ) {
363393 uploadHeaders [ 'x-ms-meta-purpose' ] = 'copilot'
364394 uploadHeaders [ 'x-ms-meta-userid' ] = encodeURIComponent ( userId || '' )
395+ } else if ( uploadType === 'profile-pictures' ) {
396+ uploadHeaders [ 'x-ms-meta-purpose' ] = 'profile-pictures'
397+ uploadHeaders [ 'x-ms-meta-userid' ] = encodeURIComponent ( userId || '' )
365398 }
366399
367400 return NextResponse . json ( {
0 commit comments