From 99d1ee0a1ebf3c91aecb0c2676bd97500def9129 Mon Sep 17 00:00:00 2001 From: JamesFlare1212 Date: Fri, 9 May 2025 10:06:04 -0400 Subject: [PATCH] add CORS setting --- README.md | 2 +- example.env | 3 ++- main.js | 47 +++++++++++++++++++++++++++++++++++------------ package.json | 1 + pnpm-lock.yaml | 18 ++++++++++++++++++ 5 files changed, 57 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index e64a682..6233def 100644 --- a/README.md +++ b/README.md @@ -8,5 +8,5 @@ cp example.env .env edit `.env` -`API_USERNAME` is your engage username in URL-encode. +`API_USERNAME` is your engage username in URL-encode. `API_PASSWORD` is your engage password in URL-encode. diff --git a/example.env b/example.env index 2d77d80..e253c99 100644 --- a/example.env +++ b/example.env @@ -1,4 +1,5 @@ API_USERNAME= API_PASSWORD= PORT=3000 -FIXED_STAFF_ACTIVITY_ID=7095 \ No newline at end of file +FIXED_STAFF_ACTIVITY_ID=7095 +ALLOWED_ORIGINS=* \ No newline at end of file diff --git a/main.js b/main.js index e5457ab..f840fbf 100644 --- a/main.js +++ b/main.js @@ -1,6 +1,7 @@ // main.js import express from 'express'; import dotenv from 'dotenv'; +import cors from 'cors'; // Import the cors middleware import { fetchActivityData } from './engage-api/get-activity.mjs'; import { structActivityData } from './engage-api/struct-activity.mjs'; import { structStaffData } from './engage-api/struct-staff.mjs'; @@ -11,13 +12,38 @@ dotenv.config(); // --- Configuration --- const USERNAME = process.env.API_USERNAME; const PASSWORD = process.env.API_PASSWORD; -const PORT = process.env.PORT || 3000; // Default to port 3000 if not specified +const PORT = process.env.PORT || 3000; const FIXED_STAFF_ACTIVITY_ID = process.env.FIXED_STAFF_ACTIVITY_ID || '3350'; +const allowedOriginsEnv = process.env.ALLOWED_ORIGINS || '*'; + +let corsOptions; + +if (allowedOriginsEnv === '*') { + corsOptions = { origin: '*' }; +} else { + // If ALLOWED_ORIGINS is a comma-separated list, split it into an array + const originsArray = allowedOriginsEnv.split(',').map(origin => origin.trim()); + corsOptions = { + origin: function (origin, callback) { + // Allow requests with no origin (like mobile apps or curl requests) + if (!origin) return callback(null, true); + if (originsArray.indexOf(origin) !== -1 || originsArray.includes('*')) { + callback(null, true); + } else { + callback(new Error('Not allowed by CORS')); + } + } + }; +} // --- Initialize Express App --- const app = express(); -// Middleware to parse JSON request bodies (useful if you add POST/PUT later) +// Apply CORS middleware globally FIRST +// This will add the 'Access-Control-Allow-Origin' header to all responses +app.use(cors(corsOptions)); + +// Middleware to parse JSON request bodies app.use(express.json()); // --- API Endpoints --- @@ -32,9 +58,8 @@ app.get('/', (req, res) => { // GET Endpoint: Fetch structured activity data by ID app.get('/v1/activity/:activityId', async (req, res) => { - let { activityId } = req.params; // Extract activityId from URL parameter + let { activityId } = req.params; - // Validate activityId: should be 1 to 4 digits if (!/^\d{1,4}$/.test(activityId)) { return res.status(400).json({ error: 'Invalid Activity ID format. Activity ID must be 1 to 4 digits (e.g., 1, 0001, 9999).' @@ -51,7 +76,7 @@ app.get('/v1/activity/:activityId', async (req, res) => { const activityJson = await fetchActivityData(activityId, USERNAME, PASSWORD); if (activityJson) { const structuredActivity = await structActivityData(activityJson); - res.json(structuredActivity); // Assuming structActivityData returns a JSON-friendly object + res.json(structuredActivity); } else { res.status(404).json({ error: `Could not retrieve details for activity ${activityId}.` }); } @@ -72,9 +97,7 @@ app.get('/v1/staffs', async (req, res) => { try { const activityJson = await fetchActivityData(FIXED_STAFF_ACTIVITY_ID, USERNAME, PASSWORD); if (activityJson) { - const staffMap = await structStaffData(activityJson); // This returns a Map - - // Convert Map to a plain object for JSON serialization + const staffMap = await structStaffData(activityJson); const staffObject = Object.fromEntries(staffMap); res.json(staffObject); } else { @@ -89,17 +112,17 @@ app.get('/v1/staffs', async (req, res) => { // --- Start the Server --- app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); + console.log(`Allowed CORS origins: ${allowedOriginsEnv === '*' ? 'All (*)' : allowedOriginsEnv}`); console.log('API Endpoints:'); - console.log(` GET /v1/activity/:activityId (ID must be 1-4 digits)`); - console.log(` GET /v1/staffs`); + console.log(` GET /v1/activity/:activityId (ID must be 1-4 digits)`); + console.log(` GET /v1/staffs`); if (!USERNAME || !PASSWORD) { console.warn('Warning: API_USERNAME or API_PASSWORD is not set. Please configure them in your .env file or environment variables.'); } }); -// Graceful shutdown (optional but good practice) +// Graceful shutdown process.on('SIGINT', () => { console.log('Server shutting down...'); - // Perform any cleanup here process.exit(0); }); \ No newline at end of file diff --git a/package.json b/package.json index ad4a506..5d11f16 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "axios": "^1.9.0", + "cors": "^2.8.5", "dotenv": "^16.5.0", "express": "^5.1.0", "node-fetch": "^3.3.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 069b19c..f39ee75 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: axios: specifier: ^1.9.0 version: 1.9.0 + cors: + specifier: ^2.8.5 + version: 2.8.5 dotenv: specifier: ^16.5.0 version: 16.5.0 @@ -69,6 +72,10 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + data-uri-to-buffer@4.0.1: resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} @@ -254,6 +261,10 @@ packages: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} @@ -413,6 +424,11 @@ snapshots: cookie@0.7.2: {} + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + data-uri-to-buffer@4.0.1: {} debug@4.4.0: @@ -599,6 +615,8 @@ snapshots: fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 + object-assign@4.1.1: {} + object-inspect@1.13.4: {} on-finished@2.4.1: