feat: add message attachments (wip)
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const key = getRouterParam(event, 'key');
|
||||
if (!key) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Missing file key' });
|
||||
}
|
||||
|
||||
const config = useRuntimeConfig();
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await s3.send(new GetObjectCommand({
|
||||
Bucket: config.s3.bucket!,
|
||||
Key: key,
|
||||
}));
|
||||
|
||||
// 3. Set the correct headers so the browser knows what it's receiving
|
||||
setHeaders(event, {
|
||||
'Content-Type': response.ContentType || 'application/octet-stream',
|
||||
'Content-Length': response.ContentLength?.toString() || '',
|
||||
'Cache-Control': 'public, max-age=3600', // Optional: cache for 1 hour
|
||||
});
|
||||
|
||||
// 4. Return the body as a stream directly to the client
|
||||
return response.Body;
|
||||
} catch (error: any) {
|
||||
if (error.name === 'NoSuchKey') {
|
||||
throw createError({ statusCode: 404, statusMessage: 'File not found' });
|
||||
}
|
||||
throw createError({ statusCode: 500, statusMessage: 'Error fetching file' });
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,72 @@
|
||||
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: `${process.env.NUXT_PUBLIC_URL}/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,
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user