import * as z from 'zod'; import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; export default defineEventHandler(async (event) => { await protectRoute(event); const result = await readValidatedBody(event, (body) => z .object({ file: z.object({ name: z.string(), mimeType: z.string(), }), }) .safeParse(body), ); if (!result.success) { throw createError({ statusCode: 400, message: result.error.issues[0]!.message, }); } const config = useRuntimeConfig(); const { file } = result.data; const s3 = new S3Client({ region: config.s3.region || 'us-east-1', endpoint: config.s3.endpoint!, credentials: { accessKeyId: config.s3.accessKeyId!, secretAccessKey: config.s3.secret!, }, forcePathStyle: true, requestChecksumCalculation: 'WHEN_REQUIRED', responseChecksumValidation: 'WHEN_REQUIRED', }); const key = `veridian__uploads/${Date.now()}-${file.name.replace(/[^a-zA-Z0-9.]/g, '_')}-${event.context.user!.id}` const command = new PutObjectCommand({ ACL: 'public-read', Bucket: config.s3.bucket!, Key: key, ContentType: file.mimeType, }); try { const url = await getSignedUrl(s3, command, { expiresIn: 600, signableHeaders: new Set(['content-type']), }); return { url, assetUrl: `/api/files/${key}`, }; } catch (error) { console.error('Failed to generate presigned URL:', error); throw createError({ statusCode: 500, statusMessage: 'Failed to generate presigned URL', data: { code: 'INTERNAL_ERROR', ok: false, } }); } })