Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,12 @@ This runs:
- SQLite DB path defaults to `backend/.data/edms.db`
- Plant storage defaults to `backend/storage/plant`
- Document upload storage defaults to `backend/storage/documents`


## Vercel deployment notes

- Set `VITE_API_URL` in the frontend environment to your deployed backend URL (for example `https://<your-backend>.vercel.app`).
- Backend CORS allows:
- `https://gitplant-oggy.vercel.app`
- any `https://*.vercel.app` origin (for Vercel preview deployments)
- CORS preflight `OPTIONS` requests are handled by FastAPI `CORSMiddleware`.
36 changes: 25 additions & 11 deletions backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,21 @@

app = FastAPI(title=settings.app_name)

if settings.app_env.lower() in {"dev", "development", "local", "test"}:
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:5173",
"http://127.0.0.1:5173",
],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
allowed_origins = [
"http://localhost:5173",
"http://127.0.0.1:5173",
"https://gitplant-oggy.vercel.app",
]
allowed_origin_regex = r"https://.*\.vercel\.app"

app.add_middleware(
CORSMiddleware,
allow_origins=allowed_origins,
allow_origin_regex=allowed_origin_regex,
allow_credentials=False,
allow_methods=["*"],
allow_headers=["*"],
)

app.include_router(health.router)
app.include_router(auth.router)
Expand Down Expand Up @@ -55,6 +59,16 @@ def read_root():
}


@app.get("/cors-debug", tags=["debug"])
def cors_debug(request: Request):
return {
"origin": request.headers.get("origin"),
"allowed_origins": allowed_origins,
"allowed_origin_regex": allowed_origin_regex,
"allow_credentials": False,
}


frontend_dist = (Path(__file__).resolve().parents[2] / "frontend" / "dist").resolve()
if frontend_dist.exists():
app.mount("/assets", StaticFiles(directory=str(frontend_dist / "assets")), name="assets")
Expand Down
28 changes: 25 additions & 3 deletions backend/tests/test_dev_and_cors.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,35 @@ def test_dev_endpoints_disabled_message() -> None:
settings.enable_demo_endpoints = original


def test_cors_preflight_allows_frontend_origin() -> None:
def test_cors_preflight_allows_vercel_production_origin() -> None:
response = client.options(
'/projects',
headers={
'Origin': 'http://localhost:5173',
'Origin': 'https://gitplant-oggy.vercel.app',
'Access-Control-Request-Method': 'POST',
},
)
assert response.status_code == 200
assert response.headers['access-control-allow-origin'] == 'http://localhost:5173'
assert response.headers['access-control-allow-origin'] == 'https://gitplant-oggy.vercel.app'


def test_cors_preflight_allows_vercel_preview_origin() -> None:
response = client.options(
'/projects',
headers={
'Origin': 'https://preview-123.vercel.app',
'Access-Control-Request-Method': 'POST',
},
)
assert response.status_code == 200
assert response.headers['access-control-allow-origin'] == 'https://preview-123.vercel.app'


def test_cors_debug_reports_origin_and_allowed_origins() -> None:
origin = 'https://gitplant-oggy.vercel.app'
response = client.get('/cors-debug', headers={'Origin': origin})
assert response.status_code == 200
body = response.json()
assert body['origin'] == origin
assert 'https://gitplant-oggy.vercel.app' in body['allowed_origins']
assert body['allowed_origin_regex'] == r'https://.*\.vercel\.app'