stackchain-dashboard/src/main.py
timmy 610b659fd4
All checks were successful
CI / lint (pull_request) Successful in 9s
CI / build-frontend (pull_request) Successful in 5s
feat: add Gitea readiness probe
2026-08-05 10:02:54 +00:00

109 lines
3.8 KiB
Python

import asyncio
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
from src.gitea_proxy import activity_events, current_user, repos, issues, pull_requests
from src.models import User, Repo, Issue, PullRequest
from src.suggestion_engine import compute
from src.views import router as frontend_router
app = FastAPI(title="Stackchain Dashboard")
CONTEXT_TIMEOUT_SECONDS = 5.0
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=False,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(frontend_router)
@app.get("/healthz")
def health() -> dict[str, str]:
"""Return process liveness without depending on Gitea."""
return {"status": "ok", "service": "stackchain-dashboard"}
@app.get("/readyz")
async def readiness():
"""Return readiness after verifying the configured Gitea connection."""
try:
user = await current_user()
except Exception as exc:
return JSONResponse(
{
"status": "not_ready",
"service": "stackchain-dashboard",
"error": str(exc),
},
status_code=503,
)
return {
"status": "ready",
"service": "stackchain-dashboard",
"gitea_user": user["login"],
}
@app.get("/api/v1/context")
async def context() -> JSONResponse:
try:
user_data, repo_data, issues_data, prs_data = await asyncio.wait_for(
asyncio.gather(current_user(), repos(), issues(), pull_requests()),
timeout=CONTEXT_TIMEOUT_SECONDS,
)
except Exception as e:
error_message = (
f"Gitea context request timed out after {CONTEXT_TIMEOUT_SECONDS:g}s"
if isinstance(e, TimeoutError)
else str(e)
)
return JSONResponse({
"user": {"id": None, "login": "timmy", "full_name": "Timmy Jr", "email": ""},
"repos": [],
"issues": [],
"pull_requests": [],
"view": "dashboard",
"deltas": [
{"panel": "auth", "action": "connect", "target": "stackchain-dashboard backend", "priority": "high"},
{"panel": "gitea", "action": "set GITEA_URL + GITEA_TOKEN", "target": "/root/stackchain-dashboard/.env or systemd env", "priority": "high"},
],
"error": error_message,
})
user_model = User(id=user_data["id"], login=user_data["login"], full_name=user_data.get("full_name", ""), email=user_data.get("email", ""))
repo_models = [
Repo(id=r["id"], name=r["name"], full_name=r["full_name"], description=r.get("description", ""), url=r["html_url"], updated_at=r.get("updated_at", ""))
for r in repo_data[:50]
if isinstance(r, dict)
]
issue_models = [
Issue(id=i["id"], number=i["number"], title=i["title"], state=i["state"], labels=[l.get("name", "") for l in i.get("labels", [])], assignees=[a.get("login", "") for a in i.get("assignees", [])], url=i["html_url"])
for i in issues_data[:50]
if isinstance(i, dict)
]
pr_models = [
PullRequest(id=p["id"], number=p["number"], title=p["title"], state=p["state"], user=p.get("user", {}).get("login", ""), url=p["html_url"])
for p in prs_data[:50]
if isinstance(p, dict)
]
ctx = compute(user_model, repo_models, issue_models, pr_models)
return JSONResponse({
"user": ctx.user.dict(),
"repos": [r.dict() for r in ctx.repos],
"issues": [i.dict() for i in ctx.issues],
"pull_requests": [p.dict() for p in ctx.pull_requests],
"view": ctx.view,
"deltas": [d.dict() for d in ctx.deltas],
})
@app.get("/api/v1/events")
async def event_stream() -> list[dict]:
return await activity_events()