11'use server' ;
22
33import { BUCKET_NAME , s3Client } from '@/app/s3' ;
4+ import { auth } from '@/utils/auth' ;
45import { logger } from '@/utils/logger' ;
56
67// This log will run as soon as the module is loaded.
@@ -10,8 +11,8 @@ import { GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
1011import { getSignedUrl } from '@aws-sdk/s3-request-presigner' ;
1112import { AttachmentEntityType , AttachmentType , db } from '@db' ;
1213import { revalidatePath } from 'next/cache' ;
14+ import { headers } from 'next/headers' ;
1315import { z } from 'zod' ;
14- import { authActionClient } from '../safe-action' ;
1516
1617function mapFileTypeToAttachmentType ( fileType : string ) : AttachmentType {
1718 const type = fileType . split ( '/' ) [ 0 ] ;
@@ -38,19 +39,18 @@ const uploadAttachmentSchema = z.object({
3839 pathToRevalidate : z . string ( ) . optional ( ) ,
3940} ) ;
4041
41- export const uploadFile = authActionClient
42- . inputSchema ( uploadAttachmentSchema )
43- . metadata ( {
44- name : 'uploadFile' ,
45- track : {
46- event : 'File Uploaded' ,
47- channel : 'server' ,
48- } ,
49- } )
50- . action ( async ( { parsedInput, ctx } ) => {
51- const { fileName, fileType, fileData, entityId, entityType, pathToRevalidate } = parsedInput ;
52- const { session } = ctx ;
53- const organizationId = session . activeOrganizationId ;
42+ export const uploadFile = async ( input : z . infer < typeof uploadAttachmentSchema > ) => {
43+ logger . info ( `[uploadFile] Starting upload for ${ input . fileName } ` ) ;
44+ try {
45+ const { fileName, fileType, fileData, entityId, entityType, pathToRevalidate } =
46+ uploadAttachmentSchema . parse ( input ) ;
47+
48+ const session = await auth . api . getSession ( { headers : await headers ( ) } ) ;
49+ const organizationId = session ?. session . activeOrganizationId ;
50+
51+ if ( ! organizationId ) {
52+ throw new Error ( 'Not authorized - no organization found' ) ;
53+ }
5454
5555 logger . info ( `[uploadFile] Starting upload for ${ fileName } in org ${ organizationId } ` ) ;
5656
@@ -66,51 +66,55 @@ export const uploadFile = authActionClient
6666 const sanitizedFileName = fileName . replace ( / [ ^ a - z A - Z 0 - 9 . - ] / g, '_' ) ;
6767 const key = `${ organizationId } /attachments/${ entityType } /${ entityId } /${ timestamp } -${ sanitizedFileName } ` ;
6868
69- try {
70- logger . info ( `[uploadFile] Uploading to S3 with key: ${ key } ` ) ;
71- const putCommand = new PutObjectCommand ( {
72- Bucket : BUCKET_NAME ,
73- Key : key ,
74- Body : fileBuffer ,
75- ContentType : fileType ,
76- } ) ;
77- await s3Client . send ( putCommand ) ;
78- logger . info ( `[uploadFile] S3 upload successful for key: ${ key } ` ) ;
79-
80- logger . info ( `[uploadFile] Creating attachment record in DB for key: ${ key } ` ) ;
81- const attachment = await db . attachment . create ( {
82- data : {
83- name : fileName ,
84- url : key ,
85- type : mapFileTypeToAttachmentType ( fileType ) ,
86- entityId : entityId ,
87- entityType : entityType ,
88- organizationId : organizationId ,
89- } ,
90- } ) ;
91- logger . info ( `[uploadFile] DB record created with id: ${ attachment . id } ` ) ;
92-
93- logger . info ( `[uploadFile] Generating signed URL for key: ${ key } ` ) ;
94- const getCommand = new GetObjectCommand ( {
95- Bucket : BUCKET_NAME ,
96- Key : key ,
97- } ) ;
98- const signedUrl = await getSignedUrl ( s3Client , getCommand , {
99- expiresIn : 900 ,
100- } ) ;
101- logger . info ( `[uploadFile] Signed URL generated for key: ${ key } ` ) ;
102-
103- if ( pathToRevalidate ) {
104- revalidatePath ( pathToRevalidate ) ;
105- }
106-
107- return {
69+ logger . info ( `[uploadFile] Uploading to S3 with key: ${ key } ` ) ;
70+ const putCommand = new PutObjectCommand ( {
71+ Bucket : BUCKET_NAME ,
72+ Key : key ,
73+ Body : fileBuffer ,
74+ ContentType : fileType ,
75+ } ) ;
76+ await s3Client . send ( putCommand ) ;
77+ logger . info ( `[uploadFile] S3 upload successful for key: ${ key } ` ) ;
78+
79+ logger . info ( `[uploadFile] Creating attachment record in DB for key: ${ key } ` ) ;
80+ const attachment = await db . attachment . create ( {
81+ data : {
82+ name : fileName ,
83+ url : key ,
84+ type : mapFileTypeToAttachmentType ( fileType ) ,
85+ entityId : entityId ,
86+ entityType : entityType ,
87+ organizationId : organizationId ,
88+ } ,
89+ } ) ;
90+ logger . info ( `[uploadFile] DB record created with id: ${ attachment . id } ` ) ;
91+
92+ logger . info ( `[uploadFile] Generating signed URL for key: ${ key } ` ) ;
93+ const getCommand = new GetObjectCommand ( {
94+ Bucket : BUCKET_NAME ,
95+ Key : key ,
96+ } ) ;
97+ const signedUrl = await getSignedUrl ( s3Client , getCommand , {
98+ expiresIn : 900 ,
99+ } ) ;
100+ logger . info ( `[uploadFile] Signed URL generated for key: ${ key } ` ) ;
101+
102+ if ( pathToRevalidate ) {
103+ revalidatePath ( pathToRevalidate ) ;
104+ }
105+
106+ return {
107+ success : true ,
108+ data : {
108109 ...attachment ,
109110 signedUrl,
110- } ;
111- } catch ( error ) {
112- logger . error ( `[uploadFile] Error during upload process for key ${ key } :` , error ) ;
113- // Re-throw the error to be handled by the safe action client
114- throw error ;
115- }
116- } ) ;
111+ } ,
112+ } as const ;
113+ } catch ( error ) {
114+ logger . error ( `[uploadFile] Error during upload process:` , error ) ;
115+ return {
116+ success : false ,
117+ error : error instanceof Error ? error . message : 'An unknown error occurred.' ,
118+ } as const ;
119+ }
120+ } ;
0 commit comments