chore: document and standardize hosts/ports across apps
This commit is contained in:
@@ -45,6 +45,9 @@ cd apps/SeleniumService
|
||||
python3 agent.py
|
||||
```
|
||||
|
||||
## 📖 Developer Documentation
|
||||
|
||||
- [Development Hosts & Ports](docs/ports.md) — which app runs on which host/port
|
||||
|
||||
|
||||
## This in a Turborepo. What's inside?
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
HOST=localhost
|
||||
NODE_ENV="development"
|
||||
HOST=0.0.0.0
|
||||
PORT=5000
|
||||
FRONTEND_URL=http://localhost:3000
|
||||
FRONTEND_URLS=http://localhost:3000,http://192.168.1.8:3000
|
||||
JWT_SECRET = 'dentalsecret'
|
||||
DB_HOST=localhost
|
||||
DB_USER=postgres
|
||||
|
||||
@@ -9,7 +9,11 @@ import dotenv from "dotenv";
|
||||
import { startBackupCron } from "./cron/backupCheck";
|
||||
|
||||
dotenv.config();
|
||||
const FRONTEND_URL = process.env.FRONTEND_URL;
|
||||
const NODE_ENV = (
|
||||
process.env.NODE_ENV ||
|
||||
process.env.ENV ||
|
||||
"development"
|
||||
).toLowerCase();
|
||||
|
||||
const app = express();
|
||||
|
||||
@@ -17,9 +21,48 @@ app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true })); // For form data
|
||||
app.use(apiLogger);
|
||||
|
||||
// --- CORS handling (flexible for dev and strict for prod) ---
|
||||
/**
|
||||
* FRONTEND_URLS env value: comma-separated allowed origins
|
||||
* Example: FRONTEND_URLS=http://localhost:3000,http://192.168.1.8:3000
|
||||
*/
|
||||
const rawFrontendUrls =
|
||||
process.env.FRONTEND_URLS || process.env.FRONTEND_URL || "";
|
||||
const FRONTEND_URLS = rawFrontendUrls
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
// helper to see if origin is allowed
|
||||
function isOriginAllowed(origin?: string | null) {
|
||||
if (!origin) return true; // allow non-browser clients (curl/postman)
|
||||
|
||||
if (NODE_ENV !== "production") {
|
||||
// Dev mode: allow localhost origins automatically
|
||||
if (
|
||||
origin.startsWith("http://localhost") ||
|
||||
origin.startsWith("http://127.0.0.1")
|
||||
)
|
||||
return true;
|
||||
// allow explicit FRONTEND_URLS if provided
|
||||
if (FRONTEND_URLS.includes(origin)) return true;
|
||||
// optionally allow the server's LAN IP if FRONTEND_LAN_IP is provided
|
||||
const lanIp = process.env.FRONTEND_LAN_IP;
|
||||
if (lanIp && origin.startsWith(`http://${lanIp}`)) return true;
|
||||
// fallback: deny if not matched
|
||||
return false;
|
||||
}
|
||||
|
||||
// production: strict whitelist — must match configured FRONTEND_URLS exactly
|
||||
return FRONTEND_URLS.includes(origin);
|
||||
}
|
||||
|
||||
app.use(
|
||||
cors({
|
||||
origin: FRONTEND_URL,
|
||||
origin: (origin, cb) => {
|
||||
if (isOriginAllowed(origin)) return cb(null, true);
|
||||
cb(new Error(`CORS: Origin ${origin} not allowed`));
|
||||
},
|
||||
methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
|
||||
allowedHeaders: ["Content-Type", "Authorization"],
|
||||
credentials: true,
|
||||
|
||||
@@ -3,11 +3,18 @@ import dotenv from "dotenv";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const HOST = process.env.HOST;
|
||||
const PORT = process.env.PORT;
|
||||
const NODE_ENV = (
|
||||
process.env.NODE_ENV ||
|
||||
process.env.ENV ||
|
||||
"development"
|
||||
).toLowerCase();
|
||||
const HOST = process.env.HOST || "0.0.0.0";
|
||||
const PORT = Number(process.env.PORT) || 5000;
|
||||
|
||||
const server = app.listen(PORT, () => {
|
||||
console.log(`✅ Server running at http://${HOST}:${PORT}`);
|
||||
const server = app.listen(PORT, HOST, () => {
|
||||
console.log(
|
||||
`✅ Server running in ${NODE_ENV} mode at http://${HOST}:${PORT}`
|
||||
);
|
||||
});
|
||||
|
||||
// Handle startup errors
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
NODE_ENV=development
|
||||
HOST=0.0.0.0
|
||||
PORT=3000
|
||||
VITE_API_BASE_URL_BACKEND=http://localhost:5000
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import path from 'path';
|
||||
import { defineConfig, loadEnv } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import path from "path";
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
const env = loadEnv(mode, process.cwd(), "");
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
react(),
|
||||
],
|
||||
return {
|
||||
plugins: [react()],
|
||||
server: {
|
||||
port: 3000,
|
||||
host: env.HOST,
|
||||
port: Number(env.PORT),
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src')
|
||||
}
|
||||
}
|
||||
"@": path.resolve(__dirname, "src"),
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
2
apps/PatientDataExtractorService/.env.example
Normal file
2
apps/PatientDataExtractorService/.env.example
Normal file
@@ -0,0 +1,2 @@
|
||||
HOST=localhost
|
||||
PORT=5001
|
||||
@@ -1,8 +1,22 @@
|
||||
from flask import Flask, request, jsonify
|
||||
from fastapi import FastAPI, UploadFile, File, HTTPException
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import uvicorn
|
||||
import fitz # PyMuPDF
|
||||
import re
|
||||
import os
|
||||
|
||||
app = Flask(__name__)
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
# Optional: allow CORS for development
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"], # change in production
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
DOB_RE = re.compile(r'(?<!\d)(\d{1,2})/(\d{1,2})/(\d{4})(?!\d)')
|
||||
ID_RE = re.compile(r'^\d{8,14}$') # 8–14 digits, whole line
|
||||
@@ -14,10 +28,18 @@ STOP_WORDS = {
|
||||
'provider', 'printed on', 'member id', 'name', 'date of birth'
|
||||
}
|
||||
|
||||
@app.route("/extract", methods=["POST"])
|
||||
def extract():
|
||||
file = request.files['pdf']
|
||||
doc = fitz.open(stream=file.read(), filetype="pdf")
|
||||
@app.post("/extract")
|
||||
async def extract(pdf: UploadFile = File(...)):
|
||||
if not pdf:
|
||||
raise HTTPException(status_code=400, detail="Missing 'pdf' file")
|
||||
|
||||
content = await pdf.read()
|
||||
try:
|
||||
doc = fitz.open(stream=content, filetype="pdf")
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=f"Unable to open PDF: {e}")
|
||||
|
||||
# Extract text from all pages
|
||||
text = "\n".join(page.get_text("text") for page in doc)
|
||||
lines = [line.strip() for line in text.splitlines() if line.strip()]
|
||||
|
||||
@@ -34,7 +56,7 @@ def extract():
|
||||
break
|
||||
|
||||
if id_idx == -1:
|
||||
return jsonify({"memberId": "", "name": "", "dob": ""})
|
||||
return {"memberId": "", "name": "", "dob": ""}
|
||||
|
||||
# 2) Scan forward to collect name + DOB; handle both same-line and next-line cases
|
||||
collected = []
|
||||
@@ -61,11 +83,13 @@ def extract():
|
||||
# fallback: if we didn't find a date, assume first collected line(s) are name
|
||||
name = blob
|
||||
|
||||
return jsonify({
|
||||
return {
|
||||
"memberId": member_id,
|
||||
"name": name,
|
||||
"dob": dob
|
||||
})
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(port=5001)
|
||||
host = os.getenv("HOST")
|
||||
port = int(os.getenv("PORT"))
|
||||
uvicorn.run(app, host=host, port=port)
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import List, Optional
|
||||
import io
|
||||
import os
|
||||
import asyncio
|
||||
|
||||
import uvicorn
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
|
||||
@@ -157,8 +157,6 @@ async def extract_csv(files: List[UploadFile] = File(...), filename: Optional[st
|
||||
# Entrypoint (same pattern as your selenium app)
|
||||
# -------------------------------------------------
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
host = os.getenv("HOST")
|
||||
port = int(os.getenv("PORT"))
|
||||
reload_flag = os.getenv("RELOAD", "false").lower() == "true"
|
||||
uvicorn.run(app, host=host, port=port, reload=reload_flag)
|
||||
uvicorn.run(app, host=host, port=port)
|
||||
|
||||
2
apps/SeleniumService/.env.example
Normal file
2
apps/SeleniumService/.env.example
Normal file
@@ -0,0 +1,2 @@
|
||||
HOST=localhost
|
||||
PORT=5002
|
||||
@@ -4,7 +4,10 @@ import uvicorn
|
||||
import asyncio
|
||||
from selenium_claimSubmitWorker import AutomationMassHealth
|
||||
from selenium_eligibilityCheckWorker import AutomationMassHealthEligibilityCheck
|
||||
import os
|
||||
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
|
||||
app = FastAPI()
|
||||
# Allow 1 selenium session at a time
|
||||
@@ -89,4 +92,6 @@ async def get_status():
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run(app, host="0.0.0.0", port=5002)
|
||||
host = os.getenv("HOST")
|
||||
port = int(os.getenv("PORT"))
|
||||
uvicorn.run(app, host=host, port=port)
|
||||
|
||||
88
docs/ports.md
Normal file
88
docs/ports.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# 🛰️ Development Hosts & Ports
|
||||
|
||||
This document defines the default **host** and **port** used by each app/service
|
||||
in this turborepo.
|
||||
Update this file whenever a new service is added or port is changed.
|
||||
|
||||
---
|
||||
|
||||
## 🌐 Frontend (React + Vite)
|
||||
- **Host:** `localhost` (default)
|
||||
- Use `0.0.0.0` if you need LAN access (phone/other device on same Wi-Fi).
|
||||
- **Port:** `3000`
|
||||
- **Access URLs:**
|
||||
- Local: [http://localhost:3000](http://localhost:3000)
|
||||
- LAN: `http://<your-ip>:3000` (only if HOST=0.0.0.0)
|
||||
|
||||
|
||||
**Current setup:**
|
||||
Frontend is running on `0.0.0.0` and is accessible via the device IP.
|
||||
|
||||
**`.env` file:**
|
||||
```env
|
||||
NODE_ENV=development
|
||||
HOST=0.0.0.0
|
||||
PORT=3000
|
||||
VITE_API_BASE_URL_BACKEND=http://192.168.1.8:5000
|
||||
```
|
||||
|
||||
Based on backend HOST and PORT. Currently Backend runs on 0.0.0.0 so its accessible all over the same network.
|
||||
Change the Backend url if needed,
|
||||
|
||||
And, VITE_API_BASE_URL_BACKEND shows the backend url of the network, make localhost if only own device to work with.
|
||||
Or change accordingly with real IP.
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Backend (FastAPI)
|
||||
- **Host:** `localhost`
|
||||
- **Port:** `5000`
|
||||
- **Access URL:** [http://localhost:5000](http://localhost:5000)
|
||||
|
||||
|
||||
**Current setup:**
|
||||
Currently runs for all network, and allow given frontend urls. Change accordingly.
|
||||
|
||||
|
||||
**`.env` file:**
|
||||
```env
|
||||
NODE_ENV="development"
|
||||
HOST=0.0.0.0
|
||||
PORT=5000
|
||||
FRONTEND_URLS=http://localhost:3000,http://192.168.1.8:3000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧾 Patient Data Extractor Service
|
||||
- **Host:** `localhost`
|
||||
- **Port:** `5001`
|
||||
- **Access URL:** [http://localhost:5001](http://localhost:5001)
|
||||
|
||||
|
||||
## 💳 Selenium Service
|
||||
- **Host:** `localhost`
|
||||
- **Port:** `5002`
|
||||
- **Access URL:** [http://localhost:5002](http://localhost:5002)
|
||||
|
||||
|
||||
## 💳 Payment OCR Service
|
||||
- **Host:** `localhost`
|
||||
- **Port:** `5003`
|
||||
- **Access URL:** [http://localhost:5003](http://localhost:5003)
|
||||
|
||||
---
|
||||
|
||||
## 📖 Notes
|
||||
- These values come from per-app `.env` files:
|
||||
- `HOST` controls binding (`localhost` = loopback only, `0.0.0.0` = all interfaces).
|
||||
- `PORT` controls the service’s port.
|
||||
- Frontend uses additional variables prefixed with `VITE_` for client-side access (e.g. `VITE_API_BASE_URL_BACKEND`).
|
||||
- In production, ports and hosts may differ (configured by deployment platform).
|
||||
|
||||
---
|
||||
|
||||
✅ **Action for developers:**
|
||||
1. Copy `.env.example` → `.env` inside each app folder.
|
||||
2. Adjust `HOST` / `PORT` if your ports are already taken.
|
||||
3. Run `npm run dev` from the repo root.
|
||||
@@ -11,7 +11,7 @@
|
||||
"db:generate": "prisma generate --schema=packages/db/prisma/schema.prisma && ts-node packages/db/scripts/patch-zod-buffer.ts",
|
||||
"db:migrate": "dotenv -e packages/db/.env -- prisma migrate dev --schema=packages/db/prisma/schema.prisma",
|
||||
"db:seed": "prisma db seed --schema=packages/db/prisma/schema.prisma",
|
||||
"setup:env": "shx cp packages/db/prisma/.env.example packages/db/prisma/.env && shx cp apps/Frontend/.env.example apps/Frontend/.env && shx cp apps/Backend/.env.example apps/Backend/.env && shx cp apps/PaymentOCRService/.env.example apps/PaymentOCRService/.env",
|
||||
"setup:env": "shx cp packages/db/prisma/.env.example packages/db/prisma/.env && shx cp apps/Frontend/.env.example apps/Frontend/.env && shx cp apps/Backend/.env.example apps/Backend/.env && shx cp apps/PatientDataExtractorService/.env.example apps/PatientDataExtractorService/.env && shx cp apps/SeleniumService/.env.example apps/SeleniumService/.env && shx cp apps/PaymentOCRService/.env.example apps/PaymentOCRService/.env",
|
||||
"postinstall": "npm --prefix apps/PatientDataExtractorService run postinstall && npm --prefix apps/PaymentOCRService run postinstall"
|
||||
},
|
||||
"prisma": {
|
||||
|
||||
Reference in New Issue
Block a user