From 4f5ecedb8f21991666e21dba12318adb26e9effa Mon Sep 17 00:00:00 2001 From: Gowtham M Date: Fri, 19 Dec 2025 09:15:14 +0530 Subject: [PATCH] GWM : server code rearranged --- server.js | 168 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 106 insertions(+), 62 deletions(-) diff --git a/server.js b/server.js index 1ad9322..e4ef4d0 100644 --- a/server.js +++ b/server.js @@ -1,6 +1,6 @@ const express = require("express"); const cors = require("cors"); -const helmet = require("helmet"); +const helmet = require("helmet"); const morgan = require("morgan"); const swaggerUi = require("swagger-ui-express"); const swaggerJsdoc = require("swagger-jsdoc"); @@ -17,30 +17,42 @@ require("dotenv").config(); const csrf = require("csurf"); const isProd = process.env.NODE_ENV === "production"; +const isLocal = process.env.NODE_ENV === "development"; +/** + * ========================= + * CSRF CONFIG (CORRECT) + * ========================= + */ const csrfProtection = csrf({ cookie: { key: "_csrf", - httpOnly: true, // always true - secure: isProd, // true only in production (HTTPS) - sameSite: isProd ? "none" : "lax", - // prod: cross-domain frontend → backend - // dev : localhost friendly + httpOnly: true, + secure: isProd, // HTTPS in UAT/PROD + sameSite: isProd ? "none" : "lax", }, }); - const app = express(); -// REQUIRED for HSTS when behind proxy (AWS ALB / Nginx / Cloudflare) +/** + * REQUIRED for HSTS when behind proxy + */ app.set("trust proxy", 1); -// GLOBAL MIDDLEWARE (body parsing) +/** + * ========================= + * GLOBAL MIDDLEWARE + * ========================= + */ app.use(express.json()); app.use(cookieParser()); -const isLocal = process.env.NODE_ENV === "development"; - +/** + * ========================= + * HELMET + SECURITY HEADERS + * ========================= + */ app.use( helmet({ contentSecurityPolicy: { @@ -76,54 +88,64 @@ if (!isLocal) { app.use(morgan("dev")); app.use(sanitizeInput); +/** + * ========================= + * CORS + * ========================= + */ 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"); -console.log("allowedOrigins = " + allowedOrigins); +app.use( + cors({ + origin: allowedOrigins, + credentials: true, + }) +); +/** + * Manual headers (kept as-is, but fixed OPTIONS flow) + */ app.use((req, res, next) => { - console.log("Origin from header = " + 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-Credentials", "true"); res.header( "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( "Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS" ); + // ⚠️ IMPORTANT: DO NOT TERMINATE REQUEST HERE if (req.method === "OPTIONS") { - return res.sendStatus(200); + return next(); } + next(); }); - - - -// Swagger setup +/** + * ========================= + * SWAGGER + * ========================= + */ const swaggerOptions = { definition: { openapi: "3.0.0", info: { title: "FCSC IPI Survey", 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: { securitySchemes: { - // Optional: Still support Bearer for Swagger-only testing bearerAuth: { type: "http", scheme: "bearer", @@ -132,8 +154,13 @@ const swaggerOptions = { appSignature: { type: "apiKey", 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.setup(swaggerDocs, { swaggerOptions: { - withCredentials: true, // 🔥 VERY IMPORTANT + withCredentials: true, }, }) ); - -// Routes /** - * -------------------- + * ========================= * AUTH (NO CSRF) - * -------------------- + * ========================= */ -app.post("/api/auth/login",[verifySignature], authController.login); -app.post("/api/forgot-password/request-otp",[verifySignature], establishmentController.forgotPasswordRequestOTP); -app.post("/api/forgot-password/verify-otp",[verifySignature], establishmentController.forgotPasswordVerifyOTP); +app.post("/api/auth/login", [verifySignature], authController.login); +app.post( + "/api/forgot-password/request-otp", + [verifySignature], + establishmentController.forgotPasswordRequestOTP +); +app.post( + "/api/forgot-password/verify-otp", + [verifySignature], + establishmentController.forgotPasswordVerifyOTP +); /** - * -------------------- + * ========================= * CSRF TOKEN ENDPOINT - * -------------------- + * ========================= */ app.get("/api/csrf-token", csrfProtection, (req, res) => { 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("/api", routes); +app.use((req, res, next) => { + if (["GET", "HEAD", "OPTIONS"].includes(req.method)) { + return next(); + } + return csrfProtection(req, res, next); +}); /** - * -------------------- + * ========================= + * PROTECTED ROUTES + * ========================= + */ +app.use("/api", routes); + +/** + * ========================= * CSRF ERROR HANDLER - * -------------------- + * ========================= */ app.use((err, req, res, next) => { if (err.code === "EBADCSRFTOKEN") { @@ -197,28 +241,28 @@ app.use((err, req, res, next) => { next(err); }); - - - -apis: [path.join(__dirname, "app/routes/*.js")], - +/** + * ========================= + * TEST ROUTE + * ========================= + */ app.get("/api/test", (req, res) => { res.json({ status: "success", message: "Test API working fine 🚀" }); }); - - - -//deployment route +/** + * ========================= + * DEPLOYMENT ROUTE + * ========================= + */ const deploymentController = require("./app/controllers/deployment.controller"); 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; app.listen(PORT, () => { console.log(`🚀 Server running on port ${PORT}`);