feat: Implement Phase 2 cross-cutting services with email and notification enhancements

- Refactor email verification and password reset utilities to use new email service.
- Introduce email delivery queue and notification delivery model for better tracking.
- Enhance file validation and storage services for improved security and ownership management.
- Add cron job for cleaning inactive notifications with retention policy.
- Update document worker to handle document generation and storage more efficiently.
- Implement logging improvements in activity and log workers.
- Create comprehensive documentation for new API endpoints and services.
- Add unit tests for file validation and notification policies to ensure robustness.
This commit is contained in:
Sathira Sri Sathara
2026-09-03 14:22:34 +05:30
parent 9d3d431416
commit b6b345f245
54 changed files with 593 additions and 1248 deletions
+6 -82
View File
@@ -1,89 +1,13 @@
/**
* 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.
*/
const storage = require("../services/storage/storage.service");
// 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");
}
const key = storage.createObjectKey({ ownerId: file.ownerId, mimeType: file.mimetype, purpose: usedFor });
await storage.uploadBuffer({ buffer: file.buffer, objectKey: key, mimeType: file.mimetype, checksum: file.checksum });
return key;
};
module.exports = {
uploadToS3,
getSignedFileUrl,
getSignedFileUrl: storage.createSignedDownloadUrl,
deleteFromS3: storage.deleteObject,
};