diff --git a/src/config.ts b/src/config.ts index ccc64c4..4f498d1 100644 --- a/src/config.ts +++ b/src/config.ts @@ -3,6 +3,8 @@ import admin from "firebase-admin"; export const PORT = process.env.PORT || 8080; +export const CORS_ORIGIN = process.env.CORS_ORIGIN || "*"; + // Firebase Related if ( !process.env.FIREBASE_SERVICE_ACCOUNT_KEY || diff --git a/src/main.ts b/src/main.ts index 9a60ddb..b5223a8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,12 +1,35 @@ import express from "express"; import { PORT } from "./config"; +import { morganMiddleware, logger } from "./middleware/logging.middleware"; +import { + helmetMiddleware, + corsMiddleware, + rateLimitMiddleware, + compressionMiddleware, +} from "./middleware/security.middleware"; const app = express(); +// Security middlewares +app.use(helmetMiddleware); +app.use(corsMiddleware); +app.use(rateLimitMiddleware); +app.use(compressionMiddleware); + +// Log HTTP requests +app.use(morganMiddleware); + +// Parse JSON payloads +app.use(express.json({ limit: "10kb" })); // The request payload should not exceed 10kb + app.get("/", (_req, res) => { - res.json({ message: "Hello World" }); + res + .json({ + message: "Hello World", + }) + .status(200); }); app.listen(PORT, () => { - console.log(`Server is running on http://localhost:${PORT}`); + logger.info(`Server is running on http://localhost:${PORT}`); }); diff --git a/src/middleware/logging.middleware.ts b/src/middleware/logging.middleware.ts new file mode 100644 index 0000000..2873d21 --- /dev/null +++ b/src/middleware/logging.middleware.ts @@ -0,0 +1,22 @@ +import winston from "winston"; +import morgan from "morgan"; + +// Winston logger configuration +export const logger = winston.createLogger({ + level: process.env.NODE_ENV === "production" ? "info" : "debug", + format: winston.format.combine( + winston.format.colorize(), + winston.format.timestamp(), + winston.format.printf(({ timestamp, level, message }) => { + return `${timestamp} ${level}: ${message}`; + }), + ), + transports: [new winston.transports.Console()], +}); + +// Morgan (HTTP request logger middleware) configuration +export const morganMiddleware = morgan("tiny", { + stream: { + write: (message) => logger.http(message.trim()), + }, +}); diff --git a/src/middleware/security.middleware.ts b/src/middleware/security.middleware.ts new file mode 100644 index 0000000..11d4faa --- /dev/null +++ b/src/middleware/security.middleware.ts @@ -0,0 +1,47 @@ +import helmet from "helmet"; +import cors from "cors"; +import rateLimit from "express-rate-limit"; +import compression from "compression"; +import { CORS_ORIGIN } from "../config"; + +// Basic CORS configuration +export const corsMiddleware = cors({ + origin: CORS_ORIGIN, + methods: ["GET", "POST", "PUT", "DELETE", "PATCH"], + credentials: true, +}); + +// Rate limiting to prevent brute-force attacks +export const rateLimitMiddleware = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100, // Limit each IP to 100 requests per windowMs + message: "Too many requests from this IP, please try again later", +}); + +// Compression middleware +export const compressionMiddleware = compression(); + +// Helmet middleware with security configurations +export const helmetMiddleware = helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + scriptSrc: ["'self'", "'unsafe-inline'"], + styleSrc: ["'self'", "'unsafe-inline'"], + imgSrc: ["'self'", "data:", "https:"], + }, + }, + crossOriginEmbedderPolicy: true, + crossOriginOpenerPolicy: true, + crossOriginResourcePolicy: true, + dnsPrefetchControl: true, + frameguard: true, + hidePoweredBy: true, + hsts: true, + ieNoOpen: true, + noSniff: true, + originAgentCluster: true, + permittedCrossDomainPolicies: true, + referrerPolicy: true, + xssFilter: true, +});