corvix.web.app¶
Litestar app serving Corvix dashboard data and UI.
Attributes¶
Classes¶
Mutable container for the module-level AppConfig cache. |
|
Mutable container for the module-level storage backend. |
Functions¶
|
|
|
Serve the dashboard single-page UI. |
|
Serve the dashboard SPA for bookmarkable dashboard URLs. |
|
Return the configured secret, delegating to middleware._get_secret(). |
|
Serve the login form, or redirect to / when auth is not configured. |
|
Validate the submitted token and issue a session cookie on success. |
|
Clear the session cookie and redirect to the login page. |
|
|
|
|
|
|
Resolve the poller status for the health check, or a failure payload. |
|
|
Compute and return the health check response. |
|
Compute and return the typed snapshot payload. |
Compute and return the typed rule-snippets payload. |
|
|
Health endpoint for container checks. |
|
Expose Prometheus metrics in text exposition format for scraping. |
|
Return available theme presets. |
|
List configured dashboard names. |
|
Return the selected dashboard data from storage. |
|
Return the server-side SSE poll interval in seconds. |
|
Build the snapshot payload and serialize it to a compact JSON string. |
|
Return the snapshot body for dashboard, reusing a build newer than ttl. |
|
Serialize an SSE |
Yield SSE messages for dashboard, pushing only on change. |
|
|
Stream dashboard snapshots as Server-Sent Events. |
Return prefilled ignore-rule snippets for a notification. |
|
|
Dismiss a notification thread (removes it from the GitHub inbox). |
|
Mark a notification thread as read in GitHub and local storage. |
|
Deprecated: use /api/v1/health. |
|
Deprecated: use /api/v1/themes. |
|
Deprecated: use /api/v1/dashboards. |
|
Deprecated: use /api/v1/snapshot. |
Deprecated: use /api/v1/notifications/{account_id}/{thread_id}/rule-snippets. |
|
|
Deprecated: use /api/v1/notifications/{account_id}/{thread_id}/dismiss. |
Deprecated: use /api/v1/notifications/{account_id}/{thread_id}/dismiss. |
|
Deprecated: use /api/v1/notifications/{account_id}/{thread_id}/mark-read. |
|
Deprecated: use /api/v1/notifications/{account_id}/{thread_id}/mark-read. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Inject the storage backend used by route handlers. |
|
Return the injected backend, or lazily build PostgreSQL storage from config. |
|
Discard the cached AppConfig so the next request reloads from disk. |
|
Return the cached AppConfig, re-parsing from disk only when the file changes. |
|
Register a SIGHUP handler that clears the config cache. |
|
|
|
|
|
Configure structured logging and optional tracing at app startup. |
|
Run app with uvicorn. |
Module Contents¶
- corvix.web.app._LOGIN_HTML = Multiline-String[source][source]¶
Show Value
"""<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Corvix — Sign in</title> <style> *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: system-ui, sans-serif; display: flex; height: 100vh; align-items: center; justify-content: center; background: #07111f; color: #edf3ff; } form { display: flex; flex-direction: column; gap: 1rem; min-width: 300px; padding: 2rem; background: #0e1a2b; border-radius: 8px; } h2 { font-size: 1.4rem; color: #74c0fc; } input[type=password] { padding: .65rem .9rem; border-radius: 6px; border: 1px solid #223753; background: #132238; color: #edf3ff; font-size: 1rem; } input[type=password]:focus { outline: 2px solid #74c0fc; border-color: transparent; } button { padding: .65rem .9rem; border-radius: 6px; border: none; background: #74c0fc; color: #07111f; font-size: 1rem; font-weight: 600; cursor: pointer; } button:hover { background: #a5d4ff; } </style> </head> <body> <form method="post" action="/login"> <h2>Corvix</h2> <input type="password" name="token" placeholder="Secret token" required autofocus> <button type="submit">Sign in</button> </form> </body> </html>"""
- corvix.web.app.dashboard_index(dashboard_name: str) litestar.Response[str][source][source]¶
Serve the dashboard SPA for bookmarkable dashboard URLs.
- corvix.web.app._get_auth_secret() str[source][source]¶
Return the configured secret, delegating to middleware._get_secret().
Using the shared implementation ensures consistent TTL caching, memoized misconfiguration logging, and
_FILEsupport in one place.
- corvix.web.app.login_page() litestar.Response[Any][source][source]¶
Serve the login form, or redirect to / when auth is not configured.
- async corvix.web.app.login(request: litestar.Request) litestar.Response[None][source][source]¶
Validate the submitted token and issue a session cookie on success.
The
Secureattribute is set when the request arrived over HTTPS. The scheme is read fromrequest.url.schemewhich reflects the real protocol when uvicorn is started with--proxy-headers(trustingX-Forwarded-Protofrom a reverse proxy). This avoids raw header inspection from application code, which can be spoofed by untrusted clients not behind a proxy.
- corvix.web.app.logout() litestar.Response[None][source][source]¶
Clear the session cookie and redirect to the login page.
- corvix.web.app._health_error(poller_status: corvix.domain.PollerStatus) dict[str, object][source][source]¶
- corvix.web.app._health_response(payload: dict[str, object]) litestar.Response[dict[str, object]][source][source]¶
- corvix.web.app._read_health_poller_status() corvix.domain.PollerStatus | dict[str, object][source][source]¶
Resolve the poller status for the health check, or a failure payload.
- corvix.web.app._health_impl(extra_headers: dict[str, str] | None = None) litestar.Response[dict[str, object]][source][source]¶
Compute and return the health check response.
- corvix.web.app._snapshot_impl(dashboard: str | None = None) corvix.web.schemas.SnapshotResponse[source][source]¶
Compute and return the typed snapshot payload.
- corvix.web.app._notification_rule_snippets_impl(account_id: str, thread_id: str, dashboard: str | None = None) corvix.web.schemas.RuleSnippetsResponse[source][source]¶
Compute and return the typed rule-snippets payload.
- corvix.web.app.health() litestar.Response[dict[str, object]][source][source]¶
Health endpoint for container checks.
Returns 200 with {“status”: “ok”} when config and storage are readable, the poller is running, and the poller’s last poll time is not stale.
Returns 503 with {“status”: “unhealthy”} and one of these reasons: “config_unavailable”, “storage_unavailable”, “invalid_cache”, “poller_not_running”, “poller_error”, “invalid_poll_time”, or “stale”.
- corvix.web.app.metrics_endpoint() litestar.Response[bytes][source][source]¶
Expose Prometheus metrics in text exposition format for scraping.
- corvix.web.app.snapshot(dashboard: str | None = None) corvix.web.schemas.SnapshotResponse[source][source]¶
Return the selected dashboard data from storage.
- corvix.web.app._sse_poll_interval() float[source][source]¶
Return the server-side SSE poll interval in seconds.
Read from
CORVIX_SSE_POLL_INTERVAL_SECONDS; falls back to the default when unset, non-numeric, or non-positive.
- corvix.web.app._snapshot_event_body(dashboard: str | None) str[source][source]¶
Build the snapshot payload and serialize it to a compact JSON string.
Uses msgspec (the encoder Litestar uses for the equivalent HTTP route) so the SSE body and the
GET /api/v1/snapshotresponse share one serialization path and stay byte-for-byte consistent.
- corvix.web.app._cached_snapshot_event_body(dashboard: str | None, ttl: float) str[source][source]¶
Return the snapshot body for dashboard, reusing a build newer than ttl.
Within a
ttl-second window (one poll interval) concurrent SSE connections watching the same dashboard share a single storage read and serialization. A strictage < ttlcomparison means a lone connection, whose ticks are spaced one interval apart, still rebuilds every tick — so the cache adds no latency in the common single-client case.
- corvix.web.app._snapshot_error_payload(error: Exception) str[source][source]¶
Serialize an SSE
snapshot-errorpayload for error.HTTPExceptioncarries a client-safe detail and status code; any other exception is reported generically (its message is not leaked to the client).
- async corvix.web.app._snapshot_event_generator(dashboard: str | None) collections.abc.AsyncIterator[litestar.response.ServerSentEventMessage][source][source]¶
Yield SSE messages for dashboard, pushing only on change.
Emits a
snapshotevent whenever the serialized payload differs from the last one sent, asnapshot-errorevent when the payload cannot be produced, and a comment-only keep-alive when nothing has changed for a while (so proxies do not drop an idle connection). The blocking storage read runs in a worker thread to avoid stalling the event loop.Any error building the snapshot is reported to the client and the stream keeps running, recovering on a later tick; this avoids tearing down the connection (and triggering a client reconnection storm) on a transient storage or serialization failure.
- async corvix.web.app.events(dashboard: str | None = None) litestar.response.ServerSentEvent[source][source]¶
Stream dashboard snapshots as Server-Sent Events.
Replaces fixed-interval client polling: the connection stays open and the server pushes a
snapshotevent only when the data changes, cutting both latency and per-cycle overhead when nothing has happened.
- corvix.web.app.notification_rule_snippets(account_id: str, thread_id: str, dashboard: str | None = None) corvix.web.schemas.RuleSnippetsResponse[source][source]¶
Return prefilled ignore-rule snippets for a notification.
- corvix.web.app.dismiss_notification(account_id: str, thread_id: str) litestar.Response[None][source][source]¶
Dismiss a notification thread (removes it from the GitHub inbox).
Calls DELETE /notifications/threads/{id} on GitHub, then marks the record as dismissed in local storage. Returns 204 No Content on success.
- corvix.web.app.mark_notification_read(account_id: str, thread_id: str) litestar.Response[None][source][source]¶
Mark a notification thread as read in GitHub and local storage.
- corvix.web.app.health_deprecated() litestar.Response[dict[str, object]][source][source]¶
Deprecated: use /api/v1/health.
- corvix.web.app.api_themes_deprecated() litestar.Response[dict[str, object]][source][source]¶
Deprecated: use /api/v1/themes.
- corvix.web.app.dashboards_deprecated() litestar.Response[dict[str, object]][source][source]¶
Deprecated: use /api/v1/dashboards.
- corvix.web.app.snapshot_deprecated(dashboard: str | None = None) litestar.Response[corvix.web.schemas.SnapshotResponse][source][source]¶
Deprecated: use /api/v1/snapshot.
- corvix.web.app.notification_rule_snippets_deprecated(account_id: str, thread_id: str, dashboard: str | None = None) litestar.Response[corvix.web.schemas.RuleSnippetsResponse][source][source]¶
Deprecated: use /api/v1/notifications/{account_id}/{thread_id}/rule-snippets.
- corvix.web.app.dismiss_notification_deprecated(account_id: str, thread_id: str) litestar.Response[None][source][source]¶
Deprecated: use /api/v1/notifications/{account_id}/{thread_id}/dismiss.
- corvix.web.app.dismiss_notification_default_account(thread_id: str) litestar.Response[None][source][source]¶
Deprecated: use /api/v1/notifications/{account_id}/{thread_id}/dismiss.
- corvix.web.app.mark_notification_read_deprecated(account_id: str, thread_id: str) litestar.Response[None][source][source]¶
Deprecated: use /api/v1/notifications/{account_id}/{thread_id}/mark-read.
- corvix.web.app.mark_notification_read_default_account(thread_id: str) litestar.Response[None][source][source]¶
Deprecated: use /api/v1/notifications/{account_id}/{thread_id}/mark-read.
- corvix.web.app._dismiss_notification_impl(account_id: str, thread_id: str) litestar.Response[None][source][source]¶
- corvix.web.app._mark_notification_read_impl(account_id: str, thread_id: str) litestar.Response[None][source][source]¶
- corvix.web.app._require_account(config: corvix.config.AppConfig, account_id: str) corvix.config.GitHubAccountConfig[source][source]¶
- corvix.web.app._build_github_client(config: corvix.config.AppConfig, account: corvix.config.GitHubAccountConfig, token: str) corvix.ingestion.GitHubNotificationsClient[source][source]¶
- corvix.web.app._default_account_id(config: corvix.config.AppConfig) str[source][source]¶
- corvix.web.app._find_record(*, records: list[corvix.domain.NotificationRecord], account_id: str, thread_id: str) corvix.domain.NotificationRecord | None[source][source]¶
- corvix.web.app._rule_name_for_record(record: corvix.domain.NotificationRecord) str[source][source]¶
- corvix.web.app._rule_match_lines(*, record: corvix.domain.NotificationRecord, include_context: Literal[False]) list[str][source][source]¶
- corvix.web.app._rule_match_lines(*, record: corvix.domain.NotificationRecord, include_context: Literal[True]) list[str] | None
- corvix.web.app._context_predicate_lines(*, record: corvix.domain.NotificationRecord) list[str][source][source]¶
- corvix.web.app._context_path_value(*, context: collections.abc.Mapping[str, object], path: str) tuple[bool, object | None][source][source]¶
- corvix.web.app._global_exclude_rule_snippet(*, record: corvix.domain.NotificationRecord, match_lines: list[str]) str[source][source]¶
- class corvix.web.app._ConfigCache[source][source]¶
Mutable container for the module-level AppConfig cache.
- config: corvix.config.AppConfig | None = None[source][source]¶
- class corvix.web.app._StorageState[source][source]¶
Mutable container for the module-level storage backend.
- backend: corvix.storage.StorageBackend | None = None[source][source]¶
- corvix.web.app.set_storage_backend(backend: corvix.storage.StorageBackend | None) None[source][source]¶
Inject the storage backend used by route handlers.
Production wiring leaves this unset and the backend is built lazily from config (PostgreSQL is required). Tests inject a backend directly and reset it to
Noneafterwards.
- corvix.web.app._get_storage() corvix.storage.StorageBackend[source][source]¶
Return the injected backend, or lazily build PostgreSQL storage from config.
The built backend is cached so its connection pool is reused across requests. Building is guarded by a lock so concurrent first requests don’t each create (and leak) a connection pool. Raises
HTTPException(500) when no database is configured.
- corvix.web.app._clear_config_cache() None[source][source]¶
Discard the cached AppConfig so the next request reloads from disk.
- corvix.web.app._load_runtime_config() corvix.config.AppConfig[source][source]¶
Return the cached AppConfig, re-parsing from disk only when the file changes.
Config is read from the path in the
CORVIX_CONFIGenvironment variable (default:corvix.yaml). The file’s mtime is checked on every call; the YAML is only re-parsed when either the path or the mtime differs from the last successful load, eliminating redundant I/O on every request.
- corvix.web.app._install_sighup_handler() None[source][source]¶
Register a SIGHUP handler that clears the config cache.
Sending
SIGHUPto the server process forces the config to be reloaded from disk on the next request without restarting the process:kill -HUP <pid>
The handler is a no-op on platforms that do not support SIGHUP (e.g. Windows).
- corvix.web.app._select_dashboard(dashboards: list[corvix.config.DashboardSpec], selected_name: str | None) corvix.config.DashboardSpec[source][source]¶
- corvix.web.app._dashboard_names(dashboards: list[corvix.config.DashboardSpec]) list[str][source][source]¶