GWM : server code rearranged

This commit is contained in:
Gowtham M 2025-12-19 09:15:14 +05:30
parent de5c505e01
commit 4f5ecedb8f

168
server.js
View File

@ -1,6 +1,6 @@
const express = require("express"); const express = require("express");
const cors = require("cors"); const cors = require("cors");
const helmet = require("helmet"); const helmet = require("helmet");
const morgan = require("morgan"); const morgan = require("morgan");
const swaggerUi = require("swagger-ui-express"); const swaggerUi = require("swagger-ui-express");
const swaggerJsdoc = require("swagger-jsdoc"); const swaggerJsdoc = require("swagger-jsdoc");
@ -17,30 +17,42 @@ require("dotenv").config();
const csrf = require("csurf"); const csrf = require("csurf");
const isProd = process.env.NODE_ENV === "production"; const isProd = process.env.NODE_ENV === "production";
const isLocal = process.env.NODE_ENV === "development";
/**
* =========================
* CSRF CONFIG (CORRECT)
* =========================
*/
const csrfProtection = csrf({ const csrfProtection = csrf({
cookie: { cookie: {
key: "_csrf", key: "_csrf",
httpOnly: true, // always true httpOnly: true,
secure: isProd, // true only in production (HTTPS) secure: isProd, // HTTPS in UAT/PROD
sameSite: isProd ? "none" : "lax", sameSite: isProd ? "none" : "lax",
// prod: cross-domain frontend → backend
// dev : localhost friendly
}, },
}); });
const app = express(); const app = express();
// REQUIRED for HSTS when behind proxy (AWS ALB / Nginx / Cloudflare) /**
* REQUIRED for HSTS when behind proxy
*/
app.set("trust proxy", 1); app.set("trust proxy", 1);
// GLOBAL MIDDLEWARE (body parsing) /**
* =========================
* GLOBAL MIDDLEWARE
* =========================
*/
app.use(express.json()); app.use(express.json());
app.use(cookieParser()); app.use(cookieParser());
const isLocal = process.env.NODE_ENV === "development"; /**
* =========================
* HELMET + SECURITY HEADERS
* =========================
*/
app.use( app.use(
helmet({ helmet({
contentSecurityPolicy: { contentSecurityPolicy: {
@ -76,54 +88,64 @@ if (!isLocal) {
app.use(morgan("dev")); app.use(morgan("dev"));
app.use(sanitizeInput); app.use(sanitizeInput);
/**
* =========================
* CORS
* =========================
*/
const allowedOrigins = process.env.ALLOWED_ORIGINS const allowedOrigins = process.env.ALLOWED_ORIGINS
? process.env.ALLOWED_ORIGINS.split(",").map(origin => origin.trim()) ? process.env.ALLOWED_ORIGINS.split(",").map(o => o.trim())
: []; : [];
app.use(cors({
origin: allowedOrigins,
credentials: true
}));
console.log("is production = " + process.env.NODE_ENV === "production"); app.use(
console.log("allowedOrigins = " + allowedOrigins); cors({
origin: allowedOrigins,
credentials: true,
})
);
/**
* Manual headers (kept as-is, but fixed OPTIONS flow)
*/
app.use((req, res, next) => { app.use((req, res, next) => {
console.log("Origin from header = " + req.headers.origin);
if (allowedOrigins.includes(req.headers.origin)) { if (allowedOrigins.includes(req.headers.origin)) {
console.log("Origin set = " + req.headers.origin);
res.header("Access-Control-Allow-Origin", req.headers.origin); res.header("Access-Control-Allow-Origin", req.headers.origin);
} }
res.header("Access-Control-Allow-Credentials", "true"); res.header("Access-Control-Allow-Credentials", "true");
res.header( res.header(
"Access-Control-Allow-Headers", "Access-Control-Allow-Headers",
"Content-Type, Authorization, APP_SIGNATURE, x-app-signature" "Content-Type, Authorization, APP_SIGNATURE, x-app-signature, X-CSRF-Token"
); );
res.header( res.header(
"Access-Control-Allow-Methods", "Access-Control-Allow-Methods",
"GET, POST, PUT, PATCH, DELETE, OPTIONS" "GET, POST, PUT, PATCH, DELETE, OPTIONS"
); );
// ⚠️ IMPORTANT: DO NOT TERMINATE REQUEST HERE
if (req.method === "OPTIONS") { if (req.method === "OPTIONS") {
return res.sendStatus(200); return next();
} }
next(); next();
}); });
/**
* =========================
* SWAGGER
// Swagger setup * =========================
*/
const swaggerOptions = { const swaggerOptions = {
definition: { definition: {
openapi: "3.0.0", openapi: "3.0.0",
info: { info: {
title: "FCSC IPI Survey", title: "FCSC IPI Survey",
version: "1.0.0", version: "1.0.0",
description: "Federal Competitiveness and Statistics Centre (FCSC) - Industrial Production Index (IPI)", description:
"Federal Competitiveness and Statistics Centre (FCSC) - Industrial Production Index (IPI)",
}, },
components: { components: {
securitySchemes: { securitySchemes: {
// Optional: Still support Bearer for Swagger-only testing
bearerAuth: { bearerAuth: {
type: "http", type: "http",
scheme: "bearer", scheme: "bearer",
@ -132,8 +154,13 @@ const swaggerOptions = {
appSignature: { appSignature: {
type: "apiKey", type: "apiKey",
in: "header", in: "header",
name: "x-app-signature" name: "x-app-signature",
} },
csrfToken: {
type: "apiKey",
in: "header",
name: "X-CSRF-Token",
},
}, },
}, },
}, },
@ -147,26 +174,32 @@ app.use(
swaggerUi.serve, swaggerUi.serve,
swaggerUi.setup(swaggerDocs, { swaggerUi.setup(swaggerDocs, {
swaggerOptions: { swaggerOptions: {
withCredentials: true, // 🔥 VERY IMPORTANT withCredentials: true,
}, },
}) })
); );
// Routes
/** /**
* -------------------- * =========================
* AUTH (NO CSRF) * AUTH (NO CSRF)
* -------------------- * =========================
*/ */
app.post("/api/auth/login",[verifySignature], authController.login); app.post("/api/auth/login", [verifySignature], authController.login);
app.post("/api/forgot-password/request-otp",[verifySignature], establishmentController.forgotPasswordRequestOTP); app.post(
app.post("/api/forgot-password/verify-otp",[verifySignature], establishmentController.forgotPasswordVerifyOTP); "/api/forgot-password/request-otp",
[verifySignature],
establishmentController.forgotPasswordRequestOTP
);
app.post(
"/api/forgot-password/verify-otp",
[verifySignature],
establishmentController.forgotPasswordVerifyOTP
);
/** /**
* -------------------- * =========================
* CSRF TOKEN ENDPOINT * CSRF TOKEN ENDPOINT
* -------------------- * =========================
*/ */
app.get("/api/csrf-token", csrfProtection, (req, res) => { app.get("/api/csrf-token", csrfProtection, (req, res) => {
res.status(200).json({ res.status(200).json({
@ -175,17 +208,28 @@ app.get("/api/csrf-token", csrfProtection, (req, res) => {
}); });
/** /**
* -------------------- * =========================
* PROTECTED ROUTES (CSRF REQUIRED) * ENFORCE CSRF FOR STATE-CHANGING REQUESTS
* -------------------- * =========================
*/ */
app.use("/api", csrfProtection, routes); app.use((req, res, next) => {
// app.use("/api", routes); if (["GET", "HEAD", "OPTIONS"].includes(req.method)) {
return next();
}
return csrfProtection(req, res, next);
});
/** /**
* -------------------- * =========================
* PROTECTED ROUTES
* =========================
*/
app.use("/api", routes);
/**
* =========================
* CSRF ERROR HANDLER * CSRF ERROR HANDLER
* -------------------- * =========================
*/ */
app.use((err, req, res, next) => { app.use((err, req, res, next) => {
if (err.code === "EBADCSRFTOKEN") { if (err.code === "EBADCSRFTOKEN") {
@ -197,28 +241,28 @@ app.use((err, req, res, next) => {
next(err); next(err);
}); });
/**
* =========================
* TEST ROUTE
apis: [path.join(__dirname, "app/routes/*.js")], * =========================
*/
app.get("/api/test", (req, res) => { app.get("/api/test", (req, res) => {
res.json({ status: "success", message: "Test API working fine 🚀" }); res.json({ status: "success", message: "Test API working fine 🚀" });
}); });
/**
* =========================
* DEPLOYMENT ROUTE
//deployment route * =========================
*/
const deploymentController = require("./app/controllers/deployment.controller"); const deploymentController = require("./app/controllers/deployment.controller");
app.post("/deploy", deploymentController.deployment); app.post("/deploy", deploymentController.deployment);
// Sync DB /**
// db.sequelize.sync({ alter: true }).then(() => { * =========================
// console.log("✅ Database connected & synced."); * START SERVER
// }); * =========================
*/
// Start server
const PORT = process.env.PORT || 5000; const PORT = process.env.PORT || 5000;
app.listen(PORT, () => { app.listen(PORT, () => {
console.log(`🚀 Server running on port ${PORT}`); console.log(`🚀 Server running on port ${PORT}`);