Skip to content

Commit 16f5ba9

Browse files
Update main.py
1 parent 4dea1ac commit 16f5ba9

File tree

1 file changed

+59
-19
lines changed

1 file changed

+59
-19
lines changed

app/main.py

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
from fastapi import FastAPI, Request, Form, status, Depends
1+
import os
2+
import secrets
3+
from fastapi import FastAPI, Request, Form, status, Depends, HTTPException
24
from fastapi.responses import HTMLResponse, JSONResponse
35
from fastapi.templating import Jinja2Templates
46
from fastapi.staticfiles import StaticFiles
57
from fastapi.security import HTTPBasic, HTTPBasicCredentials
6-
import secrets
78

89
from . import dbtools
910

@@ -13,19 +14,24 @@
1314
templates = Jinja2Templates(directory="app/templates")
1415
app.mount("/static", StaticFiles(directory="app/static"), name="static")
1516

16-
USERNAME = "admin"
17-
PASSWORD = "change_this" # Set a strong secret in prod
17+
USERNAME = os.getenv("APP_ADMIN_USER", "admin")
18+
PASSWORD = os.getenv("APP_ADMIN_PASS", None)
19+
20+
def sanitize_error(error):
21+
# Prevent detailed DB error/trace from leaking in API response
22+
if not error:
23+
return None
24+
return "Backend error. See server logs for details." if "Traceback" in error or "Exception" in error else error
1825

1926
def get_current_user(credentials: HTTPBasicCredentials = Depends(security)):
2027
correct_username = secrets.compare_digest(credentials.username, USERNAME)
2128
correct_password = secrets.compare_digest(credentials.password, PASSWORD)
2229
if not (correct_username and correct_password):
23-
raise JSONResponse(status_code=status.HTTP_401_UNAUTHORIZED, content={"detail": "Invalid credentials"})
30+
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials")
2431
return credentials.username
2532

2633
@app.get("/", response_class=HTMLResponse)
2734
def form(request: Request, user: str = Depends(get_current_user)):
28-
# No results on initial GET
2935
return templates.TemplateResponse("index.html", {"request": request, "user": user, "result": None})
3036

3137
@app.post("/test-latency", response_class=HTMLResponse)
@@ -43,6 +49,27 @@ def test_latency(
4349
custom_sql: str = Form(""),
4450
user: str = Depends(get_current_user)
4551
):
52+
custom_sql = (custom_sql or "").strip()
53+
if custom_sql and len(custom_sql) > 5000:
54+
result = {
55+
"success": False,
56+
"error": "SQL query too long (limit 5000 chars)",
57+
"latency_stats": {}, "details": []
58+
}
59+
return templates.TemplateResponse("index.html", {
60+
"request": request,
61+
"user": user,
62+
"result": result,
63+
"dbtype": dbtype,
64+
"host": host,
65+
"port": port,
66+
"username": username,
67+
"database": database,
68+
"url": url,
69+
"interval": interval,
70+
"period": period,
71+
"custom_sql": custom_sql
72+
})
4673
result = dbtools.run_latency_test(
4774
dbtype=dbtype,
4875
host=host,
@@ -55,6 +82,9 @@ def test_latency(
5582
period=period,
5683
custom_sql=custom_sql
5784
)
85+
# Sanitize error for UI as well
86+
if not result.get("success") and result.get("error"):
87+
result["error"] = sanitize_error(result["error"])
5888
return templates.TemplateResponse("index.html", {
5989
"request": request,
6090
"user": user,
@@ -85,16 +115,26 @@ def api_test_latency(
85115
credentials: HTTPBasicCredentials = Depends(security)
86116
):
87117
get_current_user(credentials)
88-
result = dbtools.run_latency_test(
89-
dbtype=dbtype,
90-
host=host,
91-
port=port,
92-
username=username,
93-
password=password,
94-
database=database,
95-
url=url,
96-
interval=interval,
97-
period=period,
98-
custom_sql=custom_sql
99-
)
100-
return JSONResponse(result)
118+
custom_sql = (custom_sql or "").strip()
119+
if custom_sql and len(custom_sql) > 5000:
120+
return JSONResponse({"success": False, "error": "SQL query too long (limit 5000 chars)"})
121+
try:
122+
result = dbtools.run_latency_test(
123+
dbtype=dbtype,
124+
host=host,
125+
port=port,
126+
username=username,
127+
password=password,
128+
database=database,
129+
url=url,
130+
interval=interval,
131+
period=period,
132+
custom_sql=custom_sql
133+
)
134+
# Sanitize error
135+
if not result.get("success") and result.get("error"):
136+
result["error"] = sanitize_error(result["error"])
137+
return JSONResponse(result)
138+
except Exception as e:
139+
# Never leak stack trace
140+
return JSONResponse({"success": False, "error": "Internal error. See server logs."})

0 commit comments

Comments
 (0)