267e80e2ec
- Refactor server initialization to separate concerns and improve error handling. - Implement centralized environment validation using Zod. - Introduce database, Redis, and queue lifecycle management. - Add health check endpoints for liveness and readiness. - Enhance error handling middleware for better response structure. - Implement rate limiting for API endpoints. - Add request ID middleware for traceability. - Create Sequelize CLI configuration and baseline migration for schema management. - Establish CI workflow with Gitea for testing and syntax checks. - Document foundational changes and migration strategy in PHASE_0_FOUNDATION_STABILIZATION.md. - Add Docker Compose configuration for local development and testing. - Implement unit and integration tests for critical functionality.
90 lines
2.2 KiB
JavaScript
90 lines
2.2 KiB
JavaScript
/**
|
|
* Copyright (c) 2026 Niolla
|
|
* All rights reserved.
|
|
*
|
|
* This source code is proprietary and confidential.
|
|
* Unauthorized copying, modification, distribution, or use
|
|
* of this file, via any medium, is strictly prohibited.
|
|
*/
|
|
|
|
// app/utils/s3Upload.util.js
|
|
|
|
const { PutObjectCommand, GetObjectCommand } = require("@aws-sdk/client-s3");
|
|
|
|
const { getSignedUrl } = require("@aws-sdk/s3-request-presigner");
|
|
const s3 = require("../config/s3.config");
|
|
const { v4: uuidv4 } = require("uuid");
|
|
const path = require("path");
|
|
const redis = require("../config/redisClient");
|
|
const { log } = require("./consoleLog.utill");
|
|
|
|
// Upload + return key (BEST PRACTICE)
|
|
const uploadToS3 = async (file, usedFor) => {
|
|
try {
|
|
const fileExtension = path.extname(file.originalname);
|
|
const fileName = `${uuidv4()}${fileExtension}`;
|
|
let key = `uploads/${fileName}`;
|
|
|
|
if (usedFor === "asset") {
|
|
key = `assets/${fileName}`;
|
|
}
|
|
|
|
log("Uploading file to S3 with key:", key);
|
|
|
|
const params = {
|
|
Bucket: process.env.AWS_S3_BUCKET_NAME,
|
|
Key: key,
|
|
Body: file.buffer,
|
|
ContentType: file.mimetype,
|
|
};
|
|
|
|
await s3.send(new PutObjectCommand(params));
|
|
|
|
// Return KEY (not URL)
|
|
return key;
|
|
} catch (error) {
|
|
log("S3 Upload Error:", error);
|
|
throw new Error("File upload failed");
|
|
}
|
|
};
|
|
|
|
// Generate signed URL with Redis caching
|
|
const getSignedFileUrl = async (key) => {
|
|
try {
|
|
const cacheKey = `s3:url:${key}`;
|
|
|
|
// 1. Check cache
|
|
const cachedUrl = await redis.get(cacheKey);
|
|
|
|
if (cachedUrl) {
|
|
log("⚡ Cache HIT - returning signed URL from Redis");
|
|
return cachedUrl;
|
|
}
|
|
|
|
log("🐢 Cache MISS - generating new signed URL");
|
|
|
|
// 2. Generate signed URL
|
|
const command = new GetObjectCommand({
|
|
Bucket: process.env.AWS_S3_BUCKET_NAME,
|
|
Key: key,
|
|
});
|
|
|
|
const signedUrl = await getSignedUrl(s3, command, {
|
|
expiresIn: 3600, // 1 hour
|
|
});
|
|
|
|
// 3. Store in Redis (TTL: 55 minutes)
|
|
await redis.set(cacheKey, signedUrl, "EX", 3300);
|
|
|
|
return signedUrl;
|
|
} catch (error) {
|
|
log("Signed URL Error:", error);
|
|
throw new Error("Failed to generate file URL");
|
|
}
|
|
};
|
|
|
|
module.exports = {
|
|
uploadToS3,
|
|
getSignedFileUrl,
|
|
};
|