import express from 'express'; import * as http from 'http'; import * as dotenv from 'dotenv'; import swaggerUi from 'swagger-ui-express'; import swaggerJSDoc from 'swagger-jsdoc'; import logger from './src/middleware/logger'; // Router imports import {calendarRouter} from './src/models/calendar/Calendar.router'; import {feedbackRouter} from './src/models/feedback/Feedback.router'; let cors = require('cors'); dotenv.config(); if (!process.env.PORT) { logger.error('No port'); process.exit(1); } const port: number = parseInt(process.env.PORT, 10); const app: express.Application = express(); const server: http.Server = http.createServer(app); // Behind Plesk's nginx, req.ip is the proxy unless we trust the forwarded header. // Verify the resolved client IP is correct in staging before relying on it // (used by the feedback rate limiter). app.set('trust proxy', 1); // here we are adding middleware to parse all incoming requests as JSON app.use(express.json()); // Configure CORS let allowedHosts = [ 'https://www.nachklang.art', 'https://calendar.nachklang.art', 'https://feedback.nachklang.art' ]; const isDev = process.env.NODE_ENV !== 'production'; const localhostRegex = /^http:\/\/localhost:\d+$/; // Matches http://: - needed so the feedback form can // be reached from a real phone over WiFi during dev (the phone's Origin is // the dev machine's LAN IP, never "localhost"). Dev-only, same as above. const lanIpRegex = /^http:\/\/(192\.168\.\d{1,3}\.\d{1,3}|10\.\d{1,3}\.\d{1,3}\.\d{1,3}|172\.(1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}):\d+$/; app.use(cors({ allowedHeaders: ['Content-Type', 'X-Session-Id', 'X-Session-Key'], origin: function (origin: any, callback: any) { // Allow requests with no origin if (!origin) return callback(null, true); // Any localhost port, or a private-LAN IP, is fine outside production - // dev servers pick whatever port is free (Next.js falls back from 3000 // if it's taken), and real-device testing hits the dev machine by IP. if (isDev && (localhostRegex.test(origin) || lanIpRegex.test(origin))) { return callback(null, true); } // Block requests with wrong origin if (allowedHosts.indexOf(origin) === -1) { return callback(new Error('The CORS policy doesn\'t allow access for your origin.'), false); } // Allow all other requests return callback(null, true); } })); // Swagger documentation const swaggerDefinition = { openapi: '3.0.0', info: { title: 'Nachklang e.V. REST API', version: '0.1.0', license: { name: 'Licensed Under MIT', url: 'https://spdx.org/licenses/MIT.html' }, contact: { name: 'Nachklang e.V.', url: 'https://www.nachklang.art' } } }; const options = { swaggerDefinition, // Paths to files containing OpenAPI definitions apis: [ './src/models/**/*.interface.ts', './src/models/**/*.router.ts' ] }; const swaggerSpec = swaggerJSDoc(options); app.use( '/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec) ); // Add routers app.use('/calendar', calendarRouter); app.use('/feedback', feedbackRouter); // this is a simple route to make sure everything is working properly app.get('/', (req: express.Request, res: express.Response) => { res.status(200).send('Welcome to the Nachklang e.V. REST API!'); }); server.listen(port, () => { logger.info('Server listening on Port ' + port); });