From 69c605602d1daf51d5607e9843974d07a6d8b053 Mon Sep 17 00:00:00 2001 From: LuisAPI Date: Wed, 19 Nov 2025 21:29:44 +0800 Subject: [PATCH 1/3] Make localhost/port values configurable via env vars - Use ALLOWED_ORIGINS env var or sensible default list for CORS - Make CLASSIFIER_URL configurable (default http://localhost:5001) - Make frontend REACT_APP_BACKEND_URL fallback dynamic (uses BACKEND_PORT) - Add README docs for new env vars (ALLOWED_ORIGINS, CLASSIFIER_URL, BACKEND_PORT, BACKEND_HOST) --- README.md | 15 ++++++++++ backend/routes/gmail.js | 5 ++-- backend/server.js | 61 ++++++++++++++++++++++------------------- frontend/src/App.js | 8 +++++- 4 files changed, 58 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 712888c..91cd80a 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,21 @@ This was proposed as the SiFri-Mail project for the S–CSSE321 and S–CSIS311 # FRONTEND_POSTAUTH_ROUTE sets the route to redirect to after authentication (default: /home) ``` +### Additional environment variables and defaults + +- `ALLOWED_ORIGINS` — Optional comma-separated list of origins allowed by CORS (e.g. `https://localhost:5003,http://localhost:3000`). + - Default: a sensible local-dev set including `localhost` and `127.0.0.1` on ports 3000, 5000, 5002, 5003, 5173. +- `CLASSIFIER_URL` — URL to the classifier service used by the Gmail route (POST /classify). + - Default: `http://localhost:5001` +- `BACKEND_PORT` — Port to run the backend on (used when `PORT` is not set). + - Default: `5002` +- `BACKEND_HOST` — Hostname used in local HTTPS startup logs (not required for operation). + - Default: `localhost` + +Notes: +- The backend will read `ALLOWED_ORIGINS` and split by comma if present; otherwise it uses the default local origins. This makes it easier to run the frontend on a different port or host in development without editing source files. +- `REACT_APP_BACKEND_URL` continues to control where the frontend sends auth/login requests. If unset, the frontend will default to `https://:` when running on `localhost`/`127.0.0.1`, or to the current page origin in non-local environments. + 5. Train the email classifier (optional): ```bash cd backend/classifier diff --git a/backend/routes/gmail.js b/backend/routes/gmail.js index 0de8abb..dc1215a 100644 --- a/backend/routes/gmail.js +++ b/backend/routes/gmail.js @@ -72,8 +72,9 @@ router.get("/callback", async (req, res) => { } } console.log("Email Bodies to Classify: ", emailBodies); - // Call classifier - const classificationResponse = await axios.post("http://localhost:5001/classify", { + // Call classifier (URL configurable via CLASSIFIER_URL env var) + const classifierBase = process.env.CLASSIFIER_URL || "http://localhost:5001"; + const classificationResponse = await axios.post(`${classifierBase.replace(/\/$/,"")}/classify`, { emails: emailBodies, }); const classifications = classificationResponse.data.predictions; diff --git a/backend/server.js b/backend/server.js index c0f00a4..1bffa6f 100644 --- a/backend/server.js +++ b/backend/server.js @@ -10,32 +10,36 @@ const app = express(); // Create an Express application instance app.use(bodyParser.json()); // Use body-parser to parse JSON request bodies -const allowedOrigins = [ - // Localhost (HTTPS) - "https://localhost:3000", - "https://localhost:5002", - "https://localhost:5000", - "https://localhost:5003", - "https://localhost:5173", - // Localhost (HTTP) - "http://localhost:3000", - "http://localhost:5002", - "http://localhost:5000", - "http://localhost:5003", - "http://localhost:5173", - // 127.0.0.1 loopback (HTTPS) - "https://127.0.0.1:3000", - "https://127.0.0.1:5002", - "https://127.0.0.1:5000", - "https://127.0.0.1:5003", - "https://127.0.0.1:5173", - // 127.0.0.1 loopback (HTTP) - "http://127.0.0.1:3000", - "http://127.0.0.1:5002", - "http://127.0.0.1:5000", - "http://127.0.0.1:5003", - "http://127.0.0.1:5173", -]; +// Allow configuring allowed CORS origins via environment variable `ALLOWED_ORIGINS` as a comma-separated list. +// If not provided, fall back to a safe default set used for local development. +const allowedOrigins = process.env.ALLOWED_ORIGINS + ? process.env.ALLOWED_ORIGINS.split(",").map((s) => s.trim()) + : [ + // Localhost (HTTPS) + "https://localhost:3000", + "https://localhost:5002", + "https://localhost:5000", + "https://localhost:5003", + "https://localhost:5173", + // Localhost (HTTP) + "http://localhost:3000", + "http://localhost:5002", + "http://localhost:5000", + "http://localhost:5003", + "http://localhost:5173", + // 127.0.0.1 loopback (HTTPS) + "https://127.0.0.1:3000", + "https://127.0.0.1:5002", + "https://127.0.0.1:5000", + "https://127.0.0.1:5003", + "https://127.0.0.1:5173", + // 127.0.0.1 loopback (HTTP) + "http://127.0.0.1:3000", + "http://127.0.0.1:5002", + "http://127.0.0.1:5000", + "http://127.0.0.1:5003", + "http://127.0.0.1:5173", + ]; app.use( cors({ @@ -120,7 +124,7 @@ app.use((req, res) => { }); // Start server in a way that's safe on Vercel (platform provides HTTPS) -const PORT = process.env.PORT || 5002; // Ensure backend uses port 5002 +const PORT = process.env.PORT || process.env.BACKEND_PORT || 5002; // Ensure backend uses BACKEND_PORT or default 5002 // Detect Vercel or similar serverless hosting environment. Vercel exposes // `VERCEL` or `VERCEL_ENV` / `NOW_REGION` environment variables at runtime. @@ -144,7 +148,8 @@ if (isVercel) { cert: fs.readFileSync(certPath), }; https.createServer(options, app).listen(PORT, () => { - console.log(`HTTPS server running at https://localhost:${PORT}`); + const host = process.env.BACKEND_HOST || "localhost"; + console.log(`HTTPS server running at https://${host}:${PORT}`); }); } catch (err) { console.error('Failed to start HTTPS server, falling back to HTTP:', err); diff --git a/frontend/src/App.js b/frontend/src/App.js index 03c12a3..0065ed0 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -36,7 +36,13 @@ function App() { }, []); // Function to handle Gmail login via OAuth - const backendUrl = process.env.REACT_APP_BACKEND_URL || "https://localhost:5002"; + // Backend URL is configurable via `REACT_APP_BACKEND_URL`. If not provided, in development we'll point + // to localhost on `BACKEND_PORT` (default 5002). In production default to the current origin. + const backendUrl = + process.env.REACT_APP_BACKEND_URL || + ((window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') + ? `https://${window.location.hostname}:${process.env.BACKEND_PORT || 5002}` + : `${window.location.protocol}//${window.location.host}`); const frontendUrl = window.location.origin; const handleGmailLogin = () => { window.location.href = `${backendUrl}/auth/gmail/login?redirect=${encodeURIComponent(frontendUrl)}`; From 0eae8ae67611d8aa8259f9e3d9119b6034c6e6c5 Mon Sep 17 00:00:00 2001 From: LuisAPI Date: Wed, 19 Nov 2025 21:35:15 +0800 Subject: [PATCH 2/3] Add missing env vars to .env.example and CI check for required env keys on PRs --- .env.example | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/.env.example b/.env.example index f3f1110..0c281f6 100644 --- a/.env.example +++ b/.env.example @@ -7,10 +7,38 @@ OUTLOOK_CLIENT_ID=your-outlook-client-id OUTLOOK_CLIENT_SECRET=your-outlook-client-secret OUTLOOK_TENANT_ID=your-outlook-tenant-id OUTLOOK_REDIRECT_URI=https://localhost:5002/auth/outlook/callback +# SSL paths: server looks for `SSL_KEY_FILE` / `SSL_CRT_FILE` by default. +# Older setups may use `SSL_KEY_PATH` / `SSL_CERT_PATH` — both are commonly used. +SSL_KEY_FILE=ssl/localhost-key.pem +SSL_CRT_FILE=ssl/localhost-cert.pem SSL_KEY_PATH=ssl/localhost-key.pem SSL_CERT_PATH=ssl/localhost-cert.pem + +# Frontend/backend URLs and redirect configuration REACT_APP_BACKEND_URL=https://localhost:5002 FRONTEND_REDIRECT_URL=http://localhost:5003/ FRONTEND_POSTAUTH_ROUTE=/home + +# Optional/advanced env vars added during development +# CORS: comma-separated list of allowed origins (overrides default local list) +ALLOWED_ORIGINS=https://localhost:5003,http://localhost:3000 +# Classifier service URL used by backend (POST /classify) +CLASSIFIER_URL=http://localhost:5001 +# Backend host/port used as fallbacks/logging +BACKEND_HOST=localhost +BACKEND_PORT=5002 + +# Frontend options +REACT_APP_USE_GROUP_LOGO=false +REACT_APP_TITLE=ImfrisivMail – Organize your emails +REACT_APP_DESCRIPTION="A brief description of the app" +REACT_APP_KEYWORDS=ImfrisivMail,email,productivity + +# Classification categories (comma-separated) +CLASSIFICATION_CATEGORIES=important,spam,newsletter,social,promotional,personal,business,automated + +# Optional API keys used by features (DO NOT COMMIT real secrets to the repo) +OPENAI_API_KEY=your-openai-key +PERPLEXITY_API_KEY=your-perplexity-key # FRONTEND_REDIRECT_URL is used as a fallback for dynamic OAuth2 redirects if not provided by the frontend. # FRONTEND_POSTAUTH_ROUTE sets the route to redirect to after authentication (default: /home) From 02c59b10c4c6fe7d81b4dd978d8a9c70a3983630 Mon Sep 17 00:00:00 2001 From: LuisAPI Date: Wed, 19 Nov 2025 21:35:36 +0800 Subject: [PATCH 3/3] Add GitHub Actions workflow to verify required env keys in .env.example on PRs --- .github/workflows/env-keys-check.yml | 55 ++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/env-keys-check.yml diff --git a/.github/workflows/env-keys-check.yml b/.github/workflows/env-keys-check.yml new file mode 100644 index 0000000..ef26662 --- /dev/null +++ b/.github/workflows/env-keys-check.yml @@ -0,0 +1,55 @@ +name: Env File Keys Check + +on: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + check-env-keys: + runs-on: ubuntu-latest + name: Verify required env keys are listed in .env.example + steps: + - uses: actions/checkout@v4 + + - name: Verify env keys + run: | + set -euo pipefail + REQUIRED_KEYS=( + "GMAIL_CLIENT_ID" + "GMAIL_CLIENT_SECRET" + "GMAIL_REDIRECT_URI" + "OUTLOOK_CLIENT_ID" + "OUTLOOK_CLIENT_SECRET" + "OUTLOOK_TENANT_ID" + "OUTLOOK_REDIRECT_URI" + "REACT_APP_BACKEND_URL" + "FRONTEND_REDIRECT_URL" + "FRONTEND_POSTAUTH_ROUTE" + "SSL_KEY_FILE" + "SSL_CRT_FILE" + "SSL_KEY_PATH" + "SSL_CERT_PATH" + "ALLOWED_ORIGINS" + "CLASSIFIER_URL" + "BACKEND_PORT" + "BACKEND_HOST" + "REACT_APP_USE_GROUP_LOGO" + "CLASSIFICATION_CATEGORIES" + ) + + MISSING=() + for key in "${REQUIRED_KEYS[@]}"; do + if ! grep -q "^${key}=" .env.example; then + MISSING+=("$key") + fi + done + + if [ ${#MISSING[@]} -ne 0 ]; then + echo "ERROR: The following required env keys are missing from .env.example:" + for k in "${MISSING[@]}"; do + echo " - $k" + done + echo "\nPlease add them to .env.example (use placeholders, do NOT commit secrets)." + exit 1 + fi + echo "All required env keys present in .env.example" \ No newline at end of file