perf: add compression and caching for faster public access
Deploy Dashboard (Dev) / deploy-dev (push) Successful in 2m20s

Optimizations to reduce latency over Tailscale tunnel:
- Add GZip middleware for 70% bandwidth reduction (480KB → ~150KB)
- Cache static assets with immutable headers (1 year)
- Remove 500ms blocking CPU interval in system stats

These changes significantly improve dashboard load time over public network.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Gan, Jimmy
2026-03-01 22:21:51 +08:00
parent ae437db92e
commit 50e1d779de
2 changed files with 10 additions and 1 deletions
+9
View File
@@ -1,6 +1,7 @@
from fastapi import FastAPI, Depends, Request from fastapi import FastAPI, Depends, Request
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from slowapi import Limiter from slowapi import Limiter
from slowapi.util import get_remote_address from slowapi.util import get_remote_address
@@ -31,6 +32,9 @@ app.add_middleware(
allow_headers=["Authorization", "Content-Type"], allow_headers=["Authorization", "Content-Type"],
) )
# Compress responses (static files and API responses)
app.add_middleware(GZipMiddleware, minimum_size=1000)
@app.exception_handler(RateLimitExceeded) @app.exception_handler(RateLimitExceeded)
async def rate_limit_handler(request: Request, exc: RateLimitExceeded): async def rate_limit_handler(request: Request, exc: RateLimitExceeded):
return JSONResponse(status_code=429, content={"detail": "Too many requests, try again later"}) return JSONResponse(status_code=429, content={"detail": "Too many requests, try again later"})
@@ -43,6 +47,11 @@ async def security_headers(request: Request, call_next):
response.headers["X-XSS-Protection"] = "1; mode=block" response.headers["X-XSS-Protection"] = "1; mode=block"
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin" response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
response.headers["Content-Security-Policy"] = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' wss: ws:; frame-ancestors 'none'" response.headers["Content-Security-Policy"] = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' wss: ws:; frame-ancestors 'none'"
# Cache static assets with hashed filenames (Vite generates these)
if request.url.path.startswith("/assets/"):
response.headers["Cache-Control"] = "public, max-age=31536000, immutable"
return response return response
# Audit logger # Audit logger
+1 -1
View File
@@ -39,7 +39,7 @@ def system_stats():
except Exception: except Exception:
pass pass
mem = psutil.virtual_memory() mem = psutil.virtual_memory()
cpu_pct = psutil.cpu_percent(interval=0.5) cpu_pct = psutil.cpu_percent(interval=0) # Non-blocking, uses cached value
load_1, load_5, load_15 = psutil.getloadavg() load_1, load_5, load_15 = psutil.getloadavg()
uptime_s = time.time() - psutil.boot_time() uptime_s = time.time() - psutil.boot_time()