@@ -6,7 +6,14 @@ import { createLogger } from '@/lib/logs/console/logger'
66import { getStorageProvider , isUsingCloudStorage } from '@/lib/uploads'
77import { getBlobServiceClient } from '@/lib/uploads/blob/blob-client'
88import { getS3Client , sanitizeFilenameForMetadata } from '@/lib/uploads/s3/s3-client'
9- import { BLOB_CONFIG , BLOB_KB_CONFIG , S3_CONFIG , S3_KB_CONFIG } from '@/lib/uploads/setup'
9+ import {
10+ BLOB_CHAT_CONFIG ,
11+ BLOB_CONFIG ,
12+ BLOB_KB_CONFIG ,
13+ S3_CHAT_CONFIG ,
14+ S3_CONFIG ,
15+ S3_KB_CONFIG ,
16+ } from '@/lib/uploads/setup'
1017import { createErrorResponse , createOptionsResponse } from '@/app/api/files/utils'
1118
1219const logger = createLogger ( 'PresignedUploadAPI' )
@@ -17,7 +24,7 @@ interface PresignedUrlRequest {
1724 fileSize : number
1825}
1926
20- type UploadType = 'general' | 'knowledge-base'
27+ type UploadType = 'general' | 'knowledge-base' | 'chat'
2128
2229class PresignedUrlError extends Error {
2330 constructor (
@@ -72,7 +79,11 @@ export async function POST(request: NextRequest) {
7279
7380 const uploadTypeParam = request . nextUrl . searchParams . get ( 'type' )
7481 const uploadType : UploadType =
75- uploadTypeParam === 'knowledge-base' ? 'knowledge-base' : 'general'
82+ uploadTypeParam === 'knowledge-base'
83+ ? 'knowledge-base'
84+ : uploadTypeParam === 'chat'
85+ ? 'chat'
86+ : 'general'
7687
7788 if ( ! isUsingCloudStorage ( ) ) {
7889 throw new StorageConfigError (
@@ -118,14 +129,19 @@ async function handleS3PresignedUrl(
118129 uploadType : UploadType
119130) {
120131 try {
121- const config = uploadType === 'knowledge-base' ? S3_KB_CONFIG : S3_CONFIG
132+ const config =
133+ uploadType === 'knowledge-base'
134+ ? S3_KB_CONFIG
135+ : uploadType === 'chat'
136+ ? S3_CHAT_CONFIG
137+ : S3_CONFIG
122138
123139 if ( ! config . bucket || ! config . region ) {
124140 throw new StorageConfigError ( `S3 configuration missing for ${ uploadType } uploads` )
125141 }
126142
127143 const safeFileName = fileName . replace ( / \s + / g, '-' ) . replace ( / [ ^ a - z A - Z 0 - 9 . - ] / g, '_' )
128- const prefix = uploadType === 'knowledge-base' ? 'kb/' : ''
144+ const prefix = uploadType === 'knowledge-base' ? 'kb/' : uploadType === 'chat' ? 'chat/' : ''
129145 const uniqueKey = `${ prefix } ${ Date . now ( ) } -${ uuidv4 ( ) } -${ safeFileName } `
130146
131147 const sanitizedOriginalName = sanitizeFilenameForMetadata ( fileName )
@@ -137,6 +153,8 @@ async function handleS3PresignedUrl(
137153
138154 if ( uploadType === 'knowledge-base' ) {
139155 metadata . purpose = 'knowledge-base'
156+ } else if ( uploadType === 'chat' ) {
157+ metadata . purpose = 'chat'
140158 }
141159
142160 const command = new PutObjectCommand ( {
@@ -156,14 +174,22 @@ async function handleS3PresignedUrl(
156174 )
157175 }
158176
159- const servePath = `/api/files/serve/s3/${ encodeURIComponent ( uniqueKey ) } `
177+ // For chat images, use direct S3 URLs since they need to be permanently accessible
178+ // For other files, use serve path for access control
179+ const finalPath =
180+ uploadType === 'chat'
181+ ? `https://${ config . bucket } .s3.${ config . region } .amazonaws.com/${ uniqueKey } `
182+ : `/api/files/serve/s3/${ encodeURIComponent ( uniqueKey ) } `
160183
161184 logger . info ( `Generated ${ uploadType } S3 presigned URL for ${ fileName } (${ uniqueKey } )` )
185+ logger . info ( `Presigned URL: ${ presignedUrl } ` )
186+ logger . info ( `Final path: ${ finalPath } ` )
162187
163188 return NextResponse . json ( {
164189 presignedUrl,
190+ uploadUrl : presignedUrl , // Make sure we're returning the uploadUrl field
165191 fileInfo : {
166- path : servePath ,
192+ path : finalPath ,
167193 key : uniqueKey ,
168194 name : fileName ,
169195 size : fileSize ,
@@ -187,7 +213,12 @@ async function handleBlobPresignedUrl(
187213 uploadType : UploadType
188214) {
189215 try {
190- const config = uploadType === 'knowledge-base' ? BLOB_KB_CONFIG : BLOB_CONFIG
216+ const config =
217+ uploadType === 'knowledge-base'
218+ ? BLOB_KB_CONFIG
219+ : uploadType === 'chat'
220+ ? BLOB_CHAT_CONFIG
221+ : BLOB_CONFIG
191222
192223 if (
193224 ! config . accountName ||
@@ -198,7 +229,7 @@ async function handleBlobPresignedUrl(
198229 }
199230
200231 const safeFileName = fileName . replace ( / \s + / g, '-' ) . replace ( / [ ^ a - z A - Z 0 - 9 . - ] / g, '_' )
201- const prefix = uploadType === 'knowledge-base' ? 'kb/' : ''
232+ const prefix = uploadType === 'knowledge-base' ? 'kb/' : uploadType === 'chat' ? 'chat/' : ''
202233 const uniqueKey = `${ prefix } ${ Date . now ( ) } -${ uuidv4 ( ) } -${ safeFileName } `
203234
204235 const blobServiceClient = getBlobServiceClient ( )
@@ -231,7 +262,12 @@ async function handleBlobPresignedUrl(
231262
232263 const presignedUrl = `${ blockBlobClient . url } ?${ sasToken } `
233264
234- const servePath = `/api/files/serve/blob/${ encodeURIComponent ( uniqueKey ) } `
265+ // For chat images, use direct Blob URLs since they need to be permanently accessible
266+ // For other files, use serve path for access control
267+ const finalPath =
268+ uploadType === 'chat'
269+ ? blockBlobClient . url
270+ : `/api/files/serve/blob/${ encodeURIComponent ( uniqueKey ) } `
235271
236272 logger . info ( `Generated ${ uploadType } Azure Blob presigned URL for ${ fileName } (${ uniqueKey } )` )
237273
@@ -244,12 +280,14 @@ async function handleBlobPresignedUrl(
244280
245281 if ( uploadType === 'knowledge-base' ) {
246282 uploadHeaders [ 'x-ms-meta-purpose' ] = 'knowledge-base'
283+ } else if ( uploadType === 'chat' ) {
284+ uploadHeaders [ 'x-ms-meta-purpose' ] = 'chat'
247285 }
248286
249287 return NextResponse . json ( {
250288 presignedUrl,
251289 fileInfo : {
252- path : servePath ,
290+ path : finalPath ,
253291 key : uniqueKey ,
254292 name : fileName ,
255293 size : fileSize ,
0 commit comments