generated from corrad-software/corrad-af-2024
Compare commits
2 Commits
40cf8ebab5
...
0ba58a1efb
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0ba58a1efb | ||
![]() |
ffec2a43ee |
33
package-lock.json
generated
33
package-lock.json
generated
@ -7,6 +7,7 @@
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.828.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.830.0",
|
||||
"@babel/eslint-parser": "^7.19.1",
|
||||
"@codemirror/lang-html": "^6.4.3",
|
||||
"@codemirror/lang-javascript": "^6.1.6",
|
||||
@ -818,6 +819,24 @@
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/s3-request-presigner": {
|
||||
"version": "3.830.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.830.0.tgz",
|
||||
"integrity": "sha512-IEiSJfuf/hcf9WvCmvV35ci1yGcV3IYKS0e6l5xPRLYDNBrAWao9j8mrp0N4WCD3Nr+3xZRY5JglEFAH6CN3OQ==",
|
||||
"dependencies": {
|
||||
"@aws-sdk/signature-v4-multi-region": "3.826.0",
|
||||
"@aws-sdk/types": "3.821.0",
|
||||
"@aws-sdk/util-format-url": "3.821.0",
|
||||
"@smithy/middleware-endpoint": "^4.1.11",
|
||||
"@smithy/protocol-http": "^5.1.2",
|
||||
"@smithy/smithy-client": "^4.4.3",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/signature-v4-multi-region": {
|
||||
"version": "3.826.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.826.0.tgz",
|
||||
@ -888,6 +907,20 @@
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/util-format-url": {
|
||||
"version": "3.821.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.821.0.tgz",
|
||||
"integrity": "sha512-h+xqmPToxDrZ0a7rxE1a8Oh4zpWfZe9oiQUphGtfiGFA6j75UiURH5J3MmGHa/G4t15I3iLLbYtUXxvb1i7evg==",
|
||||
"dependencies": {
|
||||
"@aws-sdk/types": "3.821.0",
|
||||
"@smithy/querystring-builder": "^4.0.4",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/util-locate-window": {
|
||||
"version": "3.804.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz",
|
||||
|
@ -26,6 +26,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.828.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.830.0",
|
||||
"@babel/eslint-parser": "^7.19.1",
|
||||
"@codemirror/lang-html": "^6.4.3",
|
||||
"@codemirror/lang-javascript": "^6.1.6",
|
||||
|
168
pages/test.vue
168
pages/test.vue
@ -1,94 +1,124 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
definePageMeta({
|
||||
title: "API Test Page"
|
||||
});
|
||||
|
||||
const message = ref('');
|
||||
const formData = ref({
|
||||
name: '',
|
||||
animal: ''
|
||||
});
|
||||
const selectedFile = ref(null);
|
||||
const isUploading = ref(false);
|
||||
|
||||
async function testApi() {
|
||||
try {
|
||||
const response = await $fetch('/api/test/test-response', {
|
||||
method: 'POST'
|
||||
});
|
||||
message.value = JSON.stringify(response, null, 2);
|
||||
} catch (error) {
|
||||
message.value = 'Error: ' + error.message;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleFileUpload() {
|
||||
if (!selectedFile.value) {
|
||||
message.value = 'Please select a file first';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
isUploading.value = true;
|
||||
message.value = 'Uploading file...';
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('fileName', selectedFile.value.name);
|
||||
formData.append('file', selectedFile.value);
|
||||
|
||||
const response = await $fetch('/api/test/test-response', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
message.value = JSON.stringify(response, null, 2);
|
||||
} catch (error) {
|
||||
message.value = 'Error: ' + error.message;
|
||||
} finally {
|
||||
isUploading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleFileSelect(event) {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
// Check file size (10MB limit)
|
||||
if (file.size > 10 * 1024 * 1024) {
|
||||
alert('File size must be less than 10MB');
|
||||
event.target.value = ''; // Clear the input
|
||||
selectedFile.value = null;
|
||||
return;
|
||||
}
|
||||
selectedFile.value = file;
|
||||
message.value = `Selected file: ${file.name} (${formatFileSize(file.size)})`;
|
||||
}
|
||||
}
|
||||
|
||||
function formatFileSize(bytes) {
|
||||
if (bytes === 0) return '0 B';
|
||||
const k = 1024;
|
||||
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const multipartFormData = new FormData();
|
||||
multipartFormData.append('name', formData.value.name);
|
||||
multipartFormData.append('animal', formData.value.animal);
|
||||
|
||||
// Only send file metadata if a file is selected
|
||||
if (selectedFile.value) {
|
||||
multipartFormData.append('fileName', selectedFile.value.name);
|
||||
multipartFormData.append('fileType', selectedFile.value.type);
|
||||
}
|
||||
|
||||
const response = await fetch('/api/test/test-response', {
|
||||
method: 'POST',
|
||||
body: multipartFormData
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
message.value = data.message;
|
||||
|
||||
// If we have a file and received a signed URL, upload to S3
|
||||
if (selectedFile.value && data.signedUrl) {
|
||||
try {
|
||||
const s3Response = await fetch(data.signedUrl, {
|
||||
method: 'PUT',
|
||||
body: selectedFile.value,
|
||||
headers: {
|
||||
'Content-Type': selectedFile.value.type
|
||||
}
|
||||
});
|
||||
|
||||
if (!s3Response.ok) {
|
||||
throw new Error('Failed to upload file to S3');
|
||||
}
|
||||
|
||||
message.value += '\nFile successfully uploaded to S3!';
|
||||
} catch (s3Error) {
|
||||
message.value += '\nError uploading file to S3: ' + s3Error.message;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
message.value = 'Error: ' + error.message;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col items-center justify-center min-h-screen p-4 space-y-6">
|
||||
<!-- File Upload Section -->
|
||||
<!-- Form Section -->
|
||||
<div class="w-full max-w-md space-y-4">
|
||||
<div class="flex flex-col items-center space-y-4">
|
||||
<input
|
||||
type="file"
|
||||
@change="handleFileSelect"
|
||||
class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100"
|
||||
/>
|
||||
<button
|
||||
@click="handleFileUpload"
|
||||
:disabled="!selectedFile || isUploading"
|
||||
class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors disabled:bg-blue-300 disabled:cursor-not-allowed w-full"
|
||||
>
|
||||
{{ isUploading ? 'Uploading...' : 'Upload File' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<form @submit.prevent="handleSubmit" class="space-y-4">
|
||||
<div>
|
||||
<label for="name" class="block text-sm font-medium text-gray-700 mb-1">Your Name</label>
|
||||
<input
|
||||
id="name"
|
||||
v-model="formData.name"
|
||||
type="text"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="Enter your name"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="animal" class="block text-sm font-medium text-gray-700 mb-1">Favorite Animal</label>
|
||||
<input
|
||||
id="animal"
|
||||
v-model="formData.animal"
|
||||
type="text"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="Enter your favorite animal"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Test API Button -->
|
||||
<button
|
||||
@click="testApi"
|
||||
class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors"
|
||||
>
|
||||
Test API
|
||||
</button>
|
||||
<div>
|
||||
<label for="file" class="block text-sm font-medium text-gray-700 mb-1">Upload File (Max 10MB)</label>
|
||||
<input
|
||||
id="file"
|
||||
type="file"
|
||||
@change="handleFileSelect"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Response Message -->
|
||||
<pre v-if="message" class="mt-4 p-4 bg-gray-100 rounded w-full max-w-md overflow-x-auto">{{ message }}</pre>
|
||||
|
@ -1,8 +1,18 @@
|
||||
import { readMultipartFormData } from 'h3';
|
||||
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
|
||||
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const s3 = new S3Client({
|
||||
region: 'ap-southeast-1',
|
||||
credentials: {
|
||||
accessKeyId: process.env.NUXT_AWS_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.NUXT_AWS_SECRET_ACCESS_KEY,
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
const parts = await readMultipartFormData(event);
|
||||
const parts = await readMultipartFormData(event, { maxSize: 1024 * 1024 }); // 1MB limit since we're only handling metadata
|
||||
|
||||
if (!parts) {
|
||||
return {
|
||||
@ -11,29 +21,46 @@ export default defineEventHandler(async (event) => {
|
||||
};
|
||||
}
|
||||
|
||||
const fileNamePart = parts.find(p => p.name === "fileName");
|
||||
const filePart = parts.find(p => p.name === "file");
|
||||
// Extract form data
|
||||
const name = parts.find(p => p.name === 'name')?.data.toString() || '';
|
||||
const animal = parts.find(p => p.name === 'animal')?.data.toString() || '';
|
||||
const fileName = parts.find(p => p.name === 'fileName')?.data.toString();
|
||||
const fileType = parts.find(p => p.name === 'fileType')?.data.toString();
|
||||
|
||||
if (!fileNamePart || !filePart) {
|
||||
return {
|
||||
status: 400,
|
||||
message: "Missing required fields (fileName or file)"
|
||||
};
|
||||
// Log the received data
|
||||
console.log('Received name:', name);
|
||||
console.log('Received favorite animal:', animal);
|
||||
|
||||
// Prepare response message
|
||||
let message = `You are ${name} and your favorite animal is ${animal}`;
|
||||
|
||||
let signedUrl = null;
|
||||
if (fileName && fileType) {
|
||||
console.log('Received file metadata:', { fileName, fileType });
|
||||
message += `. You will upload the file: ${fileName}`;
|
||||
|
||||
// Generate a unique key using timestamp and filename
|
||||
const uploadKey = `uploads/${Date.now()}-${fileName}`;
|
||||
|
||||
const s3Command = new PutObjectCommand({
|
||||
Bucket: process.env.NUXT_AWS_BUCKET,
|
||||
Key: uploadKey,
|
||||
ContentType: fileType,
|
||||
});
|
||||
|
||||
signedUrl = await getSignedUrl(s3, s3Command, { expiresIn: 60 });
|
||||
}
|
||||
|
||||
const fileName = fileNamePart.data.toString();
|
||||
console.log("Received file:", fileName);
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
message: "File received successfully",
|
||||
fileName: fileName
|
||||
message,
|
||||
signedUrl
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error processing file upload:", error);
|
||||
console.error("Error processing request:", error);
|
||||
return {
|
||||
status: 500,
|
||||
message: "Error processing file upload",
|
||||
message: "Error processing request",
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
|
24
yarn.lock
24
yarn.lock
@ -504,6 +504,20 @@
|
||||
"@smithy/util-middleware" "^4.0.4"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/s3-request-presigner@^3.830.0":
|
||||
version "3.830.0"
|
||||
resolved "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.830.0.tgz"
|
||||
integrity sha512-IEiSJfuf/hcf9WvCmvV35ci1yGcV3IYKS0e6l5xPRLYDNBrAWao9j8mrp0N4WCD3Nr+3xZRY5JglEFAH6CN3OQ==
|
||||
dependencies:
|
||||
"@aws-sdk/signature-v4-multi-region" "3.826.0"
|
||||
"@aws-sdk/types" "3.821.0"
|
||||
"@aws-sdk/util-format-url" "3.821.0"
|
||||
"@smithy/middleware-endpoint" "^4.1.11"
|
||||
"@smithy/protocol-http" "^5.1.2"
|
||||
"@smithy/smithy-client" "^4.4.3"
|
||||
"@smithy/types" "^4.3.1"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/signature-v4-multi-region@3.826.0":
|
||||
version "3.826.0"
|
||||
resolved "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.826.0.tgz"
|
||||
@ -554,6 +568,16 @@
|
||||
"@smithy/util-endpoints" "^3.0.6"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/util-format-url@3.821.0":
|
||||
version "3.821.0"
|
||||
resolved "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.821.0.tgz"
|
||||
integrity sha512-h+xqmPToxDrZ0a7rxE1a8Oh4zpWfZe9oiQUphGtfiGFA6j75UiURH5J3MmGHa/G4t15I3iLLbYtUXxvb1i7evg==
|
||||
dependencies:
|
||||
"@aws-sdk/types" "3.821.0"
|
||||
"@smithy/querystring-builder" "^4.0.4"
|
||||
"@smithy/types" "^4.3.1"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/util-locate-window@^3.0.0":
|
||||
version "3.804.0"
|
||||
resolved "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz"
|
||||
|
Loading…
x
Reference in New Issue
Block a user