fosrl.pangolin/server/apiServer.ts

110 lines
3.5 KiB
TypeScript
Raw Normal View History

import express from "express";
2024-12-07 22:07:13 -05:00
import cors from "cors";
import cookieParser from "cookie-parser";
2025-01-01 21:41:31 -05:00
import config from "@server/lib/config";
2024-12-07 22:07:13 -05:00
import logger from "@server/logger";
import {
errorHandlerMiddleware,
notFoundMiddleware
} from "@server/middlewares";
2024-12-07 22:07:13 -05:00
import { authenticated, unauthenticated } from "@server/routers/external";
import { router as wsRouter, handleWSUpgrade } from "@server/routers/ws";
import { logIncomingMiddleware } from "./middlewares/logIncoming";
2024-12-25 22:04:20 -05:00
import { csrfProtectionMiddleware } from "./middlewares/csrfProtection";
import helmet from "helmet";
import rateLimit from "express-rate-limit";
import createHttpError from "http-errors";
import HttpCode from "./types/HttpCode";
import requestTimeoutMiddleware from "./middlewares/requestTimeout";
2025-07-16 15:50:03 -07:00
import { createStore } from "./lib/rateLimitStore";
2024-12-07 22:07:13 -05:00
const dev = config.isDev;
const externalPort = config.getRawConfig().server.external_port;
2024-12-07 22:07:13 -05:00
export function createApiServer() {
const apiServer = express();
2025-07-13 21:57:24 -07:00
const prefix = `/api/v1`;
const trustProxy = config.getRawConfig().server.trust_proxy;
if (trustProxy) {
apiServer.set("trust proxy", trustProxy);
}
const corsConfig = config.getRawConfig().server.cors;
const options = {
...(corsConfig?.origins
? { origin: corsConfig.origins }
: {
origin: (origin: any, callback: any) => {
callback(null, true);
}
}),
...(corsConfig?.methods && { methods: corsConfig.methods }),
...(corsConfig?.allowed_headers && {
allowedHeaders: corsConfig.allowed_headers
}),
credentials: !(corsConfig?.credentials === false)
};
logger.debug("Using CORS options", options);
apiServer.use(cors(options));
if (!dev) {
apiServer.use(helmet());
2024-12-25 22:04:20 -05:00
apiServer.use(csrfProtectionMiddleware);
}
2024-12-25 22:04:20 -05:00
apiServer.use(cookieParser());
apiServer.use(express.json());
2025-07-13 21:57:24 -07:00
// Add request timeout middleware
apiServer.use(requestTimeoutMiddleware(60000)); // 60 second timeout
if (!dev) {
apiServer.use(
rateLimit({
windowMs:
config.getRawConfig().rate_limits.global.window_minutes *
60 *
1000,
max: config.getRawConfig().rate_limits.global.max_requests,
keyGenerator: (req) => `apiServerGlobal:${req.ip}:${req.path}`,
handler: (req, res, next) => {
const message = `Rate limit exceeded. You can make ${config.getRawConfig().rate_limits.global.max_requests} requests every ${config.getRawConfig().rate_limits.global.window_minutes} minute(s).`;
return next(
createHttpError(HttpCode.TOO_MANY_REQUESTS, message)
);
2025-07-16 15:50:03 -07:00
},
store: createStore()
2024-12-25 22:04:20 -05:00
})
);
}
// API routes
apiServer.use(logIncomingMiddleware);
apiServer.use(prefix, unauthenticated);
apiServer.use(prefix, authenticated);
// WebSocket routes
apiServer.use(prefix, wsRouter);
// Error handling
apiServer.use(notFoundMiddleware);
apiServer.use(errorHandlerMiddleware);
// Create HTTP server
const httpServer = apiServer.listen(externalPort, (err?: any) => {
if (err) throw err;
logger.info(
2024-12-25 22:04:20 -05:00
`API server is running on http://localhost:${externalPort}`
);
});
// Handle WebSocket upgrades
handleWSUpgrade(httpServer);
return httpServer;
2024-12-07 22:07:13 -05:00
}