GWM : server code rearranged
This commit is contained in:
parent
de5c505e01
commit
4f5ecedb8f
168
server.js
168
server.js
@ -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}`);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user