Spaces:
Runtime error
Runtime error
Bansari Akhani commited on
Commit ·
637e633
1
Parent(s): 40f79e1
activity logs and change S3 piblic access to signed urls
Browse files
src/controllers/invoice/invoice.controller.ts
CHANGED
|
@@ -67,7 +67,7 @@ export const createInvoice = async (req: Request, res: Response) => {
|
|
| 67 |
term: aiServiceData.terms,
|
| 68 |
pw_work_order_id: aiServiceData.workOrderID,
|
| 69 |
filename: file.originalname,
|
| 70 |
-
pdf_url: (await uploadResults[index]).
|
| 71 |
uploaded_by: 1, // TODO : replace with logged in user when user module is implemented
|
| 72 |
};
|
| 73 |
|
|
@@ -364,7 +364,7 @@ export const updateInvoiceData = async (invoiceId: number, updatedInvoiceData: a
|
|
| 364 |
|
| 365 |
for (const key in updatedInvoiceData) {
|
| 366 |
if (originalInvoiceData[key] !== updatedInvoiceData[key]) {
|
| 367 |
-
await logInvoiceAction({ invoice_id: invoiceId, user_id: userId, activity_type: 'update', field_name: key, old_value: originalInvoiceData[key] as string, new_value: updatedInvoiceData[key] });
|
| 368 |
}
|
| 369 |
}
|
| 370 |
|
|
@@ -416,9 +416,6 @@ export const updateInvoice = async (req: Request, res: Response): Promise<Respon
|
|
| 416 |
}
|
| 417 |
};
|
| 418 |
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
// Delete an invoice
|
| 423 |
export const deleteInvoice = async (req: Request, res: Response): Promise<Response> => {
|
| 424 |
try {
|
|
|
|
| 67 |
term: aiServiceData.terms,
|
| 68 |
pw_work_order_id: aiServiceData.workOrderID,
|
| 69 |
filename: file.originalname,
|
| 70 |
+
pdf_url: (await uploadResults[index]).url,
|
| 71 |
uploaded_by: 1, // TODO : replace with logged in user when user module is implemented
|
| 72 |
};
|
| 73 |
|
|
|
|
| 364 |
|
| 365 |
for (const key in updatedInvoiceData) {
|
| 366 |
if (originalInvoiceData[key] !== updatedInvoiceData[key]) {
|
| 367 |
+
await logInvoiceAction({ invoice_id: invoiceId, user_id: userId, activity_type: 'update', field_name: key, old_value: originalInvoiceData[key] as string, new_value: updatedInvoiceData[key] as string });
|
| 368 |
}
|
| 369 |
}
|
| 370 |
|
|
|
|
| 416 |
}
|
| 417 |
};
|
| 418 |
|
|
|
|
|
|
|
|
|
|
| 419 |
// Delete an invoice
|
| 420 |
export const deleteInvoice = async (req: Request, res: Response): Promise<Response> => {
|
| 421 |
try {
|
src/middlewares/multer.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
import multer from 'multer';
|
| 2 |
import AWS from 'aws-sdk';
|
|
|
|
| 3 |
|
| 4 |
export const s3 = new AWS.S3({
|
| 5 |
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
@@ -12,3 +13,46 @@ const storage = multer.memoryStorage();
|
|
| 12 |
const upload = multer({ storage });
|
| 13 |
|
| 14 |
export const multipleUpload = upload.array('files');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import multer from 'multer';
|
| 2 |
import AWS from 'aws-sdk';
|
| 3 |
+
import { GenerateDownloadUrlParams, GenerateSignedUrlParams } from 'shared/interfaces/s3.interface';
|
| 4 |
|
| 5 |
export const s3 = new AWS.S3({
|
| 6 |
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
|
|
| 13 |
const upload = multer({ storage });
|
| 14 |
|
| 15 |
export const multipleUpload = upload.array('files');
|
| 16 |
+
|
| 17 |
+
export const generateSignedUrl = async ({
|
| 18 |
+
bucketName,
|
| 19 |
+
objectKey,
|
| 20 |
+
expiration = 3600,
|
| 21 |
+
contentType = 'application/octet-stream',
|
| 22 |
+
}: GenerateSignedUrlParams): Promise<string> => {
|
| 23 |
+
const params = {
|
| 24 |
+
Bucket: bucketName,
|
| 25 |
+
Key: objectKey,
|
| 26 |
+
Expires: expiration,
|
| 27 |
+
ContentType: contentType,
|
| 28 |
+
};
|
| 29 |
+
|
| 30 |
+
try {
|
| 31 |
+
const url = await s3.getSignedUrlPromise('putObject', params);
|
| 32 |
+
|
| 33 |
+
return url;
|
| 34 |
+
} catch (err) {
|
| 35 |
+
console.error('Error generating signed URL', err);
|
| 36 |
+
throw err;
|
| 37 |
+
}
|
| 38 |
+
};
|
| 39 |
+
|
| 40 |
+
export const generateDownloadUrl = async ({
|
| 41 |
+
bucketName,
|
| 42 |
+
objectKey,
|
| 43 |
+
expiration = 60 * 60 * 24 * 365 * 10 // URL EXPIRY is set to 10 years for now, TODO: change the logic in frontend to get file download url run time
|
| 44 |
+
}: GenerateDownloadUrlParams): Promise<string> => {
|
| 45 |
+
const params = {
|
| 46 |
+
Bucket: bucketName,
|
| 47 |
+
Key: objectKey,
|
| 48 |
+
Expires: expiration,
|
| 49 |
+
};
|
| 50 |
+
|
| 51 |
+
try {
|
| 52 |
+
const url = await s3.getSignedUrlPromise('getObject', params);
|
| 53 |
+
return url;
|
| 54 |
+
} catch (err) {
|
| 55 |
+
console.error('Error generating signed URL', err);
|
| 56 |
+
throw err;
|
| 57 |
+
}
|
| 58 |
+
};
|
src/shared/interfaces/s3.interface.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export interface GenerateSignedUrlParams {
|
| 2 |
+
bucketName: string;
|
| 3 |
+
objectKey: string;
|
| 4 |
+
expiration?: number;
|
| 5 |
+
contentType?: string;
|
| 6 |
+
}
|
| 7 |
+
export interface GenerateDownloadUrlParams {
|
| 8 |
+
bucketName: string;
|
| 9 |
+
objectKey: string;
|
| 10 |
+
expiration?: number;
|
| 11 |
+
}
|
src/shared/services/invoice.service.ts
CHANGED
|
@@ -1,17 +1,44 @@
|
|
| 1 |
-
import { s3 } from '../../middlewares/multer';
|
| 2 |
|
| 3 |
-
export const uploadInvoice = (files: Express.Multer.File[]) => {
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
Key: Date.now().toString() + '-' + file.originalname,
|
| 8 |
-
Body: file.buffer,
|
| 9 |
-
ContentType: 'application/pdf',
|
| 10 |
-
ContentDisposition: 'inline'
|
| 11 |
-
};
|
| 12 |
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
});
|
| 15 |
|
| 16 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
};
|
|
|
|
| 1 |
+
import { s3, generateSignedUrl, generateDownloadUrl } from '../../middlewares/multer';
|
| 2 |
|
| 3 |
+
export const uploadInvoice = async (files: Express.Multer.File[]) => {
|
| 4 |
+
const uploadPromises = files.map(async (file) => {
|
| 5 |
+
const objectKey = Date.now().toString() + '-' + file.originalname;
|
| 6 |
+
const bucketName = process.env.AWS_S3_BUCKET_NAME || '';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
+
try {
|
| 9 |
+
const signedUrl = await generateSignedUrl({
|
| 10 |
+
bucketName,
|
| 11 |
+
objectKey,
|
| 12 |
+
contentType: file.mimetype,
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
// Use fetch to upload the file using the signed URL
|
| 16 |
+
const response = await fetch(signedUrl, {
|
| 17 |
+
method: 'PUT',
|
| 18 |
+
headers: {
|
| 19 |
+
'Content-Type': file.mimetype,
|
| 20 |
+
},
|
| 21 |
+
body: file.buffer,
|
| 22 |
+
});
|
| 23 |
+
|
| 24 |
+
if (!response.ok) {
|
| 25 |
+
throw new Error('File upload failed');
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
// Generate download URL
|
| 29 |
+
const downloadUrl = await generateDownloadUrl({
|
| 30 |
+
bucketName: process.env.AWS_S3_BUCKET_NAME || '',
|
| 31 |
+
objectKey,
|
| 32 |
});
|
| 33 |
|
| 34 |
+
return { file: file.originalname, url: downloadUrl };
|
| 35 |
+
|
| 36 |
+
} catch (err) {
|
| 37 |
+
console.error('Error uploading file', err);
|
| 38 |
+
throw err;
|
| 39 |
+
}
|
| 40 |
+
});
|
| 41 |
+
|
| 42 |
+
return uploadPromises;
|
| 43 |
+
// return Promise.all(uploadPromises);
|
| 44 |
};
|