Prepare deployment and Forgejo CI
Some checks failed
CI / check (push) Failing after 8s

This commit is contained in:
kacper 2026-04-14 20:17:29 -04:00
parent 51706d2d11
commit 853e99ca5f
21 changed files with 1402 additions and 77 deletions

74
app.py
View file

@ -18,10 +18,14 @@ from auth import (
create_login_session,
create_oauth_state,
current_session_user,
resolve_forgejo_token,
resolve_forgejo_auth,
)
from forgejo_client import ForgejoClient, ForgejoClientError
from live_prototype import discussion_card_from_issue
from live_prototype import (
DISCUSSION_LABEL_NAME,
discussion_card_from_issue,
issue_has_discussion_label,
)
from prototype_cache import PrototypePayloadCache
from settings import Settings, get_settings
from update_events import UpdateBroker, stream_sse_events
@ -31,15 +35,22 @@ DIST_DIR = BASE_DIR / "frontend" / "dist"
def create_app() -> FastAPI:
app = FastAPI(title="Robot U Community Prototype")
settings = get_settings()
app = FastAPI(title="Robot U Community Site")
prototype_cache = PrototypePayloadCache()
update_broker = UpdateBroker()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
allow_origins=list(settings.cors_allow_origins),
allow_methods=["GET", "POST", "DELETE", "OPTIONS"],
allow_headers=[
"Authorization",
"Content-Type",
"X-Forgejo-Signature",
"X-Gitea-Signature",
"X-Hub-Signature-256",
],
)
@app.get("/health")
@ -50,12 +61,13 @@ def create_app() -> FastAPI:
async def prototype(request: Request) -> JSONResponse:
settings = get_settings()
session_user = current_session_user(request, settings)
forgejo_token, auth_source = resolve_forgejo_token(request, settings)
forgejo_token, auth_source, auth_scheme = resolve_forgejo_auth(request, settings)
payload = await prototype_cache.get(settings)
payload["auth"] = await _auth_payload_for_request(
settings,
forgejo_token=forgejo_token,
auth_source=auth_source,
auth_scheme=auth_scheme,
session_user=session_user,
)
return JSONResponse(payload)
@ -67,11 +79,13 @@ def create_app() -> FastAPI:
if session_user:
return JSONResponse(_auth_payload(session_user, "session"))
forgejo_token, auth_source = resolve_forgejo_token(request, settings)
forgejo_token, auth_source, auth_scheme = resolve_forgejo_auth(request, settings)
if not forgejo_token or auth_source == "server":
return JSONResponse(_auth_payload(None, "none"))
async with ForgejoClient(settings, forgejo_token=forgejo_token) as client:
async with ForgejoClient(
settings, forgejo_token=forgejo_token, auth_scheme=auth_scheme
) as client:
try:
user = await client.fetch_current_user()
except ForgejoClientError as error:
@ -183,6 +197,11 @@ def create_app() -> FastAPI:
detail="This site only reads public Forgejo repositories.",
)
issue = await client.fetch_issue(owner, repo, issue_number)
if not issue_has_discussion_label(issue):
raise HTTPException(
status_code=404,
detail="This issue is not labeled as a discussion.",
)
comments = [
_discussion_reply(comment)
for comment in await client.list_issue_comments(owner, repo, issue_number)
@ -203,14 +222,16 @@ def create_app() -> FastAPI:
body = _required_string(payload, "body")
issue_number = _required_positive_int(payload, "number")
settings = get_settings()
forgejo_token, auth_source = resolve_forgejo_token(request, settings)
forgejo_token, auth_source, auth_scheme = resolve_forgejo_auth(request, settings)
if not forgejo_token or auth_source == "server":
raise HTTPException(
status_code=401,
detail="Sign in or send an Authorization token before replying.",
)
async with ForgejoClient(settings, forgejo_token=forgejo_token) as client:
async with ForgejoClient(
settings, forgejo_token=forgejo_token, auth_scheme=auth_scheme
) as client:
try:
repo_payload = await client.fetch_repository(owner, repo)
if repo_payload.get("private"):
@ -248,14 +269,16 @@ def create_app() -> FastAPI:
)
settings = get_settings()
forgejo_token, auth_source = resolve_forgejo_token(request, settings)
forgejo_token, auth_source, auth_scheme = resolve_forgejo_auth(request, settings)
if not forgejo_token or auth_source == "server":
raise HTTPException(
status_code=401,
detail="Sign in or send an Authorization token before starting a discussion.",
)
async with ForgejoClient(settings, forgejo_token=forgejo_token) as client:
async with ForgejoClient(
settings, forgejo_token=forgejo_token, auth_scheme=auth_scheme
) as client:
try:
repo_payload = await client.fetch_repository(owner, repo)
if repo_payload.get("private"):
@ -263,11 +286,29 @@ def create_app() -> FastAPI:
status_code=403,
detail="This site only writes to public Forgejo repositories.",
)
issue = await client.create_issue(owner, repo, title, issue_body)
discussion_label_id = await client.ensure_repo_label(
owner,
repo,
DISCUSSION_LABEL_NAME,
color="#0f6f8f",
description="Shown on Robot U as a community discussion.",
)
issue = await client.create_issue(
owner,
repo,
title,
issue_body,
label_ids=[discussion_label_id],
)
except ForgejoClientError as error:
raise HTTPException(status_code=502, detail=str(error)) from error
issue["repository"] = _issue_repository_payload(repo_payload, owner, repo)
if not issue_has_discussion_label(issue):
issue["labels"] = [
*[label for label in issue.get("labels", []) if isinstance(label, dict)],
{"id": discussion_label_id, "name": DISCUSSION_LABEL_NAME},
]
prototype_cache.invalidate()
await update_broker.publish(
"content-updated",
@ -429,6 +470,7 @@ async def _auth_payload_for_request(
*,
forgejo_token: str | None,
auth_source: str,
auth_scheme: str,
session_user: dict[str, Any] | None,
) -> dict[str, object]:
if session_user:
@ -437,7 +479,9 @@ async def _auth_payload_for_request(
if not forgejo_token or auth_source == "server":
return _auth_payload(None, "none")
async with ForgejoClient(settings, forgejo_token=forgejo_token) as client:
async with ForgejoClient(
settings, forgejo_token=forgejo_token, auth_scheme=auth_scheme
) as client:
try:
user = await client.fetch_current_user()
except ForgejoClientError as error: