generated from corrad-software/corrad-af-2024
Merged AWS upload functionality
Uploading now works with AWS. Documentation can be found in dms-api.md at the root folder.
This commit is contained in:
parent
9333c7085a
commit
384d571997
@ -322,29 +322,37 @@ const performUpload = async () => {
|
|||||||
const file = selectedFiles.value[i];
|
const file = selectedFiles.value[i];
|
||||||
const metadata = fileMetadata.value[i];
|
const metadata = fileMetadata.value[i];
|
||||||
|
|
||||||
// Simulate upload progress
|
// First get the signed URL
|
||||||
for (let progress = 0; progress <= 100; progress += 10) {
|
|
||||||
uploadProgress.value = Math.round(((i * 100) + progress) / selectedFiles.value.length);
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 100));
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(file);
|
|
||||||
|
|
||||||
// Here you would implement actual file upload
|
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('fileName', metadata.name);
|
formData.append('fileName', metadata.name);
|
||||||
formData.append('file', file);
|
formData.append('fileType', file.type);
|
||||||
|
|
||||||
console.log(formData);
|
const signedUrlResponse = await fetch('/api/dms/upload-file', {
|
||||||
|
|
||||||
const response = await fetch('/api/dms/upload-file', {
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
// Let browser automatically set headers for multipart/form-data
|
|
||||||
body: formData,
|
body: formData,
|
||||||
});
|
});
|
||||||
console.log(response);
|
|
||||||
// console.log('Uploading:', file.name, 'with metadata:', metadata);
|
const signedUrlData = await signedUrlResponse.json();
|
||||||
|
|
||||||
|
if (!signedUrlResponse.ok || !signedUrlData.signedUrl) {
|
||||||
|
throw new Error(signedUrlData.message || 'Failed to get signed URL');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload directly to S3 using the signed URL
|
||||||
|
const uploadResponse = await fetch(signedUrlData.signedUrl, {
|
||||||
|
method: 'PUT',
|
||||||
|
body: file,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': file.type
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!uploadResponse.ok) {
|
||||||
|
throw new Error(`Failed to upload file ${metadata.name} to S3`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update progress
|
||||||
|
uploadProgress.value = Math.round(((i + 1) * 100) / selectedFiles.value.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
success(`Successfully uploaded ${selectedFiles.value.length} file(s)`);
|
success(`Successfully uploaded ${selectedFiles.value.length} file(s)`);
|
||||||
|
43
dms-api.md
43
dms-api.md
@ -115,4 +115,45 @@
|
|||||||
"message": "DMS settings updated successfully",
|
"message": "DMS settings updated successfully",
|
||||||
"data": { "settingID": 1 }
|
"data": { "settingID": 1 }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
### POST /api/dms/upload-file
|
||||||
|
- **Description**: Generates a signed URL for direct file upload to S3
|
||||||
|
- **Method**: POST
|
||||||
|
- **Content-Type**: multipart/form-data
|
||||||
|
- **Request Body**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"fileName": "example.pdf",
|
||||||
|
"fileType": "application/pdf"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Required Fields**: fileName, fileType
|
||||||
|
- **Response Example**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": 200,
|
||||||
|
"message": "Signed URL generated for file: example.pdf",
|
||||||
|
"signedUrl": "https://bucket-name.s3.region.amazonaws.com/..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Error Responses**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": 400,
|
||||||
|
"message": "Missing required fields { fileName, fileType }"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": 500,
|
||||||
|
"message": "Failed to generate signed URL",
|
||||||
|
"error": "Error details..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Notes**:
|
||||||
|
- The signed URL expires after 60 seconds
|
||||||
|
- Use the returned signedUrl with a PUT request to upload the file directly to S3
|
||||||
|
- Include the file's Content-Type header when uploading to S3
|
@ -1,21 +1,19 @@
|
|||||||
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3"; // ES Modules import
|
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
|
||||||
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
||||||
import { readMultipartFormData } from 'h3';
|
import { readMultipartFormData } from 'h3';
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
|
// Create S3 client with config
|
||||||
const parts = [];
|
const client = new S3Client({
|
||||||
|
region: process.env.NUXT_AWS_REGION,
|
||||||
// Allow headers for specific origins
|
credentials: {
|
||||||
// setHeader(event, 'Access-Control-Allow-Origin', 'http://localhost:3000');
|
accessKeyId: process.env.NUXT_AWS_ACCESS_KEY_ID,
|
||||||
// setHeader(event, 'Access-Control-Allow-Methods', 'POST, OPTIONS');
|
secretAccessKey: process.env.NUXT_AWS_SECRET_ACCESS_KEY
|
||||||
// setHeader(event, 'Access-Control-Allow-Headers', 'Content-Type');
|
}
|
||||||
|
});
|
||||||
if (event.method === 'OPTIONS') {
|
|
||||||
return new Response(null, { status: 204 });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const parts = await readMultipartFormData(event, { maxSize: 20 * 1024 * 1024});
|
const parts = await readMultipartFormData(event, { maxSize: 1024 * 1024 }); // 1MB limit since we're only handling metadata
|
||||||
|
|
||||||
if (!parts) {
|
if (!parts) {
|
||||||
return {
|
return {
|
||||||
@ -23,61 +21,45 @@ export default defineEventHandler(async (event) => {
|
|||||||
message: "Bad request. No parts found."
|
message: "Bad request. No parts found."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract form data
|
||||||
|
const fileNamePart = parts.find(p => p.name === "fileName");
|
||||||
|
const fileTypePart = parts.find(p => p.name === "fileType");
|
||||||
|
|
||||||
|
if (!fileNamePart || !fileTypePart) {
|
||||||
|
return {
|
||||||
|
status: 400,
|
||||||
|
message: "Missing required fields { fileName, fileType }"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileName = fileNamePart.data.toString();
|
||||||
|
const fileType = fileTypePart.data.toString();
|
||||||
|
|
||||||
|
// Generate a unique key using timestamp and filename
|
||||||
|
const uploadKey = `uploads/${Date.now()}-${fileName}`;
|
||||||
|
|
||||||
|
const command = new PutObjectCommand({
|
||||||
|
Bucket: process.env.NUXT_AWS_BUCKET,
|
||||||
|
Key: uploadKey,
|
||||||
|
ContentType: fileType,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Generate signed URL
|
||||||
|
const signedUrl = await getSignedUrl(client, command, { expiresIn: 60 });
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
message: `Signed URL generated for file: ${fileName}`,
|
||||||
|
signedUrl
|
||||||
|
};
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to read multi-part data:', error);
|
console.error('Error processing request:', error);
|
||||||
return {
|
return {
|
||||||
status: 500,
|
status: 500,
|
||||||
message: "Failed to read multi-part data"
|
message: "Failed to generate signed URL",
|
||||||
}
|
error: error.message
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Hello from the backend!");
|
|
||||||
|
|
||||||
const S3_Config = {
|
|
||||||
region: process.env.NUXT_AWS_REGION,
|
|
||||||
credentials: {
|
|
||||||
accessKeyId: process.env.NUXT_AWS_ACCESS_KEY_ID,
|
|
||||||
secretAccessKey: process.env.NUXT_AWS_SECRET_ACCESS_KEY
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create S3 client with config
|
|
||||||
const client = new S3Client(S3_Config);
|
|
||||||
|
|
||||||
// const { fileName, file } = await readMultipartFormData(event);
|
|
||||||
|
|
||||||
const fileNamePart = parts.find(p => p.name === "fileName");
|
|
||||||
const filePart = parts.find(p => p.name === "file");
|
|
||||||
|
|
||||||
if (!fileNamePart || !filePart) {
|
|
||||||
return {
|
|
||||||
status: 400,
|
|
||||||
message: "Missing required fields { fileName, file }"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileName = fileNamePart.data.toString();
|
|
||||||
const file = filePart.data;
|
|
||||||
|
|
||||||
const input = {
|
|
||||||
Bucket: process.env.NUXT_AWS_BUCKET,
|
|
||||||
Key: fileName,
|
|
||||||
Body: file,
|
|
||||||
ContentType: filePart.type
|
|
||||||
}
|
|
||||||
|
|
||||||
const command = new PutObjectCommand(input);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await client.send(command);
|
|
||||||
console.log(response);
|
|
||||||
return response;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
return {
|
|
||||||
status: 500,
|
|
||||||
message: "Failed to upload file to S3",
|
|
||||||
error: error
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
Loading…
x
Reference in New Issue
Block a user