feat: stabilize API startup and lifecycle management

- 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.
This commit is contained in:
Sathira Sri Sathara
2026-09-03 13:33:26 +05:30
parent 624fce31c5
commit 267e80e2ec
40 changed files with 1682 additions and 261 deletions
+10 -6
View File
@@ -12,19 +12,23 @@
const jwt = require("jsonwebtoken");
require("dotenv").config();
const JWT_SECRET = process.env.JWT_SECRET || "your_jwt_secret_key";
const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || "15m"; // token validity
const REFRESH_TOKEN_SECRET = process.env.REFRESH_TOKEN_SECRET || "your_refresh_token_secret_key";
const REFRESH_TOKEN_DAYS = process.env.REFRESH_TOKEN_DAYS || "7d"; // refresh token validity
const getSecret = (name) => {
const value = process.env[name];
if (!value || value.length < 32) throw new Error(`${name} is not configured securely`);
return value;
};
/**
* Generate JWT token
* @param {Object} payload - usually { id, email, role }
* @returns string
*/
const generateToken = (payload) => {
return jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
return jwt.sign(payload, getSecret("JWT_SECRET"), { expiresIn: JWT_EXPIRES_IN });
};
/**
@@ -33,15 +37,15 @@ const generateToken = (payload) => {
* @returns payload or throws error
*/
const verifyToken = (token) => {
return jwt.verify(token, JWT_SECRET);
return jwt.verify(token, getSecret("JWT_SECRET"));
};
const generateRefreshToken = (payload) => {
return jwt.sign(payload, REFRESH_TOKEN_SECRET, { expiresIn: REFRESH_TOKEN_DAYS });
return jwt.sign(payload, getSecret("REFRESH_TOKEN_SECRET"), { expiresIn: REFRESH_TOKEN_DAYS });
}
const verifyRefreshToken = (token) => {
return jwt.verify(token, REFRESH_TOKEN_SECRET);
return jwt.verify(token, getSecret("REFRESH_TOKEN_SECRET"));
}
module.exports = { generateToken, verifyToken, generateRefreshToken, verifyRefreshToken };
+9 -4
View File
@@ -23,10 +23,12 @@ const transporter = nodemailer.createTransport({
auth: mailConfig.auth,
});
transporter.verify((err) => {
if (err) console.error("Mail server connection failed", err);
else console.log("Mail server ready");
});
if (process.env.NODE_ENV !== "test" && process.env.ENABLE_MAIL === "true") {
transporter.verify((err) => {
if (err) console.error("Mail server connection failed", { message: err.message });
else console.log("Mail server ready");
});
}
// Utility to load template and replace placeholders
const loadTemplate = (templateName, variables = {}) => {
@@ -42,6 +44,9 @@ const loadTemplate = (templateName, variables = {}) => {
};
const send = async ({ to, subject, templateName, templateVars = {}, text }) => {
if (process.env.ENABLE_MAIL !== "true") {
throw new Error("Email functionality is not enabled");
}
const html = templateName ? loadTemplate(templateName, templateVars) : undefined;
const mailOptions = {
+1 -2
View File
@@ -15,8 +15,7 @@ const { getSignedUrl } = require("@aws-sdk/s3-request-presigner");
const s3 = require("../config/s3.config");
const { v4: uuidv4 } = require("uuid");
const path = require("path");
const createRedisConnection = require("../config/redis.config");
const redis = createRedisConnection();
const redis = require("../config/redisClient");
const { log } = require("./consoleLog.utill");
// Upload + return key (BEST PRACTICE)