feat: ADD health endpoint with simple testing #29
This commit is contained in:
parent
53263c8923
commit
e42e55c7e4
2 changed files with 38 additions and 1 deletions
|
|
@ -32,6 +32,7 @@ The goal is to keep it simple! For feature-rich solutions please check out [hedg
|
||||||
- `POST /` — create a pad from request body (curl-friendly, see *Usage*)
|
- `POST /` — create a pad from request body (curl-friendly, see *Usage*)
|
||||||
- `GET /{pad_id}/raw` — raw text (auth via `?pw=…` for protected pads)
|
- `GET /{pad_id}/raw` — raw text (auth via `?pw=…` for protected pads)
|
||||||
- `GET /system/info` — instance configuration page
|
- `GET /system/info` — instance configuration page
|
||||||
|
- `GET /health` — JSON health check (200 `ok` / 503 `degraded`)
|
||||||
- WebSocket `/ws/{pad_id}` — live collaboration
|
- WebSocket `/ws/{pad_id}` — live collaboration
|
||||||
|
|
||||||
**Deployment:**
|
**Deployment:**
|
||||||
|
|
|
||||||
38
app.py
38
app.py
|
|
@ -1,6 +1,6 @@
|
||||||
# aukpad.py
|
# aukpad.py
|
||||||
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Request, HTTPException
|
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Request, HTTPException
|
||||||
from fastapi.responses import HTMLResponse, RedirectResponse, PlainTextResponse, FileResponse
|
from fastapi.responses import HTMLResponse, RedirectResponse, PlainTextResponse, FileResponse, JSONResponse
|
||||||
import json, re, secrets, string, time, os, threading, asyncio, hashlib
|
import json, re, secrets, string, time, os, threading, asyncio, hashlib
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
@ -17,6 +17,7 @@ RETENTION_HOURS = int(os.getenv("RETENTION_HOURS", "48")) # Default 48 hours
|
||||||
MAX_ROOMS = int(os.getenv("MAX_ROOMS", "10000"))
|
MAX_ROOMS = int(os.getenv("MAX_ROOMS", "10000"))
|
||||||
TRUST_PROXY = os.getenv("TRUST_PROXY", "false").lower() == "true"
|
TRUST_PROXY = os.getenv("TRUST_PROXY", "false").lower() == "true"
|
||||||
DESCRIPTION = os.getenv("DESCRIPTION", "powered by aukpad.com")
|
DESCRIPTION = os.getenv("DESCRIPTION", "powered by aukpad.com")
|
||||||
|
START_TIME = time.time()
|
||||||
|
|
||||||
DOC_ID_RE = re.compile(r"^[a-zA-Z0-9_-]{1,64}$")
|
DOC_ID_RE = re.compile(r"^[a-zA-Z0-9_-]{1,64}$")
|
||||||
|
|
||||||
|
|
@ -540,6 +541,41 @@ ta.addEventListener("input", () => {
|
||||||
def favicon():
|
def favicon():
|
||||||
return FileResponse("favicon.ico")
|
return FileResponse("favicon.ico")
|
||||||
|
|
||||||
|
@app.get("/health", include_in_schema=False)
|
||||||
|
def health():
|
||||||
|
checks = {"app": "ok"}
|
||||||
|
healthy = True
|
||||||
|
|
||||||
|
# Writable = can we still take new pads, or are we at room capacity?
|
||||||
|
checks["write"] = "ok" if len(rooms) < MAX_ROOMS else "at capacity"
|
||||||
|
healthy = checks["write"] == "ok"
|
||||||
|
|
||||||
|
# Cache: real round-trip (write, read back, delete) so a half-dead
|
||||||
|
# connection shows up instead of just "configured".
|
||||||
|
if not USE_VALKEY:
|
||||||
|
checks["cache"] = "disabled"
|
||||||
|
elif redis_client is None:
|
||||||
|
checks["cache"] = "unavailable"
|
||||||
|
healthy = False
|
||||||
|
else:
|
||||||
|
key = f"health:{random_id()}"
|
||||||
|
try:
|
||||||
|
redis_client.setex(key, 10, "ok")
|
||||||
|
checks["cache"] = "ok" if redis_client.get(key) == "ok" else "readback failed"
|
||||||
|
redis_client.delete(key)
|
||||||
|
except Exception as e:
|
||||||
|
# Type only — the exception text can contain the connection URL.
|
||||||
|
checks["cache"] = f"error: {type(e).__name__}"
|
||||||
|
if checks["cache"] != "ok":
|
||||||
|
healthy = False
|
||||||
|
|
||||||
|
return JSONResponse({
|
||||||
|
"status": "ok" if healthy else "degraded",
|
||||||
|
"checks": checks,
|
||||||
|
"rooms": len(rooms),
|
||||||
|
"uptime_seconds": round(time.time() - START_TIME),
|
||||||
|
}, status_code=200 if healthy else 503)
|
||||||
|
|
||||||
@app.get("/system/info", response_class=HTMLResponse)
|
@app.get("/system/info", response_class=HTMLResponse)
|
||||||
def get_system_info():
|
def get_system_info():
|
||||||
max_text_size_mb = int(os.getenv("MAX_TEXT_SIZE", "5"))
|
max_text_size_mb = int(os.getenv("MAX_TEXT_SIZE", "5"))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue