Fix 405 when saving a drawn layer in the deployed build
Browse files- backend/api/endpoints/chat.py +2 -1
- backend/api/endpoints/user_layers.py +6 -1
- backend/main.py +7 -1
backend/api/endpoints/chat.py
CHANGED
|
@@ -47,7 +47,8 @@ class ChatResponse(BaseModel):
|
|
| 47 |
raw_data: list[dict[str, Any]] = []
|
| 48 |
|
| 49 |
|
| 50 |
-
@router.post("
|
|
|
|
| 51 |
async def chat(request: ChatRequest):
|
| 52 |
"""
|
| 53 |
Non-streaming chat endpoint. Routes to the appropriate handler based on
|
|
|
|
| 47 |
raw_data: list[dict[str, Any]] = []
|
| 48 |
|
| 49 |
|
| 50 |
+
@router.post("", response_model=ChatResponse)
|
| 51 |
+
@router.post("/", response_model=ChatResponse, include_in_schema=False)
|
| 52 |
async def chat(request: ChatRequest):
|
| 53 |
"""
|
| 54 |
Non-streaming chat endpoint. Routes to the appropriate handler based on
|
backend/api/endpoints/user_layers.py
CHANGED
|
@@ -22,7 +22,12 @@ class UserLayerResponse(BaseModel):
|
|
| 22 |
name: str # The table name in DuckDB
|
| 23 |
display_name: str
|
| 24 |
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
async def register_user_layer(request: UserLayerRequest):
|
| 27 |
"""
|
| 28 |
Registers a new user-drawn layer.
|
|
|
|
| 22 |
name: str # The table name in DuckDB
|
| 23 |
display_name: str
|
| 24 |
|
| 25 |
+
# Registered with and without the trailing slash. Starlette only redirects
|
| 26 |
+
# "/layers/user" -> "/layers/user/" when nothing else matches, and the
|
| 27 |
+
# catch-all that serves the frontend matches everything — so in the deployed
|
| 28 |
+
# build the redirect never happened and a POST came back 405.
|
| 29 |
+
@router.post("", response_model=UserLayerResponse)
|
| 30 |
+
@router.post("/", response_model=UserLayerResponse, include_in_schema=False)
|
| 31 |
async def register_user_layer(request: UserLayerRequest):
|
| 32 |
"""
|
| 33 |
Registers a new user-drawn layer.
|
backend/main.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
from fastapi import FastAPI
|
| 2 |
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
from fastapi.staticfiles import StaticFiles
|
| 4 |
from fastapi.responses import FileResponse
|
|
@@ -31,6 +31,12 @@ if static_dir.exists():
|
|
| 31 |
|
| 32 |
@app.get("/{full_path:path}")
|
| 33 |
async def serve_frontend(full_path: str):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
file_path = static_dir / full_path
|
| 35 |
if file_path.exists() and file_path.is_file():
|
| 36 |
return FileResponse(file_path)
|
|
|
|
| 1 |
+
from fastapi import FastAPI, HTTPException
|
| 2 |
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
from fastapi.staticfiles import StaticFiles
|
| 4 |
from fastapi.responses import FileResponse
|
|
|
|
| 31 |
|
| 32 |
@app.get("/{full_path:path}")
|
| 33 |
async def serve_frontend(full_path: str):
|
| 34 |
+
# An unknown API path is an error, not a page. Falling through to
|
| 35 |
+
# index.html here would answer a bad API call with HTML and a 200,
|
| 36 |
+
# and it makes this route shadow the API for non-GET methods.
|
| 37 |
+
if full_path.startswith("api/"):
|
| 38 |
+
raise HTTPException(status_code=404, detail=f"No such API route: /{full_path}")
|
| 39 |
+
|
| 40 |
file_path = static_dir / full_path
|
| 41 |
if file_path.exists() and file_path.is_file():
|
| 42 |
return FileResponse(file_path)
|