Split into a FastAPI backend and a React frontend, add case state and SSO
Some checks failed
build-and-deploy / test (push) Has been cancelled
build-and-deploy / image (push) Has been cancelled
build-and-deploy / deploy (push) Has been cancelled

The single-file stdlib server became the limit: no way to track what had been
done about an alert, no accounts, and a UI that had to be hand-rolled in
template strings. This restructures it into something deployable.

Backend (FastAPI)
- app/ holds config, database, auth, delivery and the routers; triagelib keeps
  the triage engine unchanged, so the validated screening and runbook logic is
  untouched.
- Cases persist per alert fingerprint with a status workflow (investigating,
  customer contacted, escalated to Infra, waiting, remediated, resolved, won't
  fix, false positive), an assignee, notes and an append-only history. An alert
  that stops and re-fires lands back on the same case and counts as a reopen.
- Suppression rules move from a JSON file into the database.

Auth
- Signed session cookies over PBKDF2 local accounts, plus an OIDC flow ready for
  Authentik: users are created on first login and admin follows a group claim.
  Local login can be switched off entirely once SSO is live.

Zendesk and Jira
- Delivery is now implemented, behind three gates: the integration must be
  configured, its feature flag on, and CX_FEATURE_SEND_ENABLED on. A demo
  instance leaves the last off and cannot mail anyone. Both search before
  creating, so re-diagnosing an alert updates one ticket rather than opening
  several, and a rolling daily cap stops a loop mailing everybody.

Deployment
- Multi-stage Dockerfile builds the bundle and serves it from the API origin.
- docker-compose for local and single-host use; Gitea Actions runs the tests,
  builds the image and renders deploy/k8s with envsubst.

Two fixes found while testing: assigning a case returned a null assignee, and
add_event could leave an already-loaded history collection stale.

Known gap: the engine reaches OpenStack via `docker exec <region>-osc`, which
does not work in a pod without the CX-Tools containers alongside it.
docs/DEPLOYMENT.md sets out the three ways to close that.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 07:11:28 +01:00
parent a039e0b5fd
commit 1262690276
68 changed files with 3839 additions and 2223 deletions

67
.env.example Normal file
View File

@@ -0,0 +1,67 @@
# ---------------------------------------------------------------------------
# Copy to .env and edit. Everything here is read at startup.
# ---------------------------------------------------------------------------
# --- app -------------------------------------------------------------------
CX_APP_NAME=CX Triage
CX_PORT=8080
# Must be the externally reachable URL: the SSO redirect is built from it.
CX_BASE_URL=http://localhost:8080
# openssl rand -hex 32
CX_SECRET_KEY=change-me
CX_SESSION_HOURS=12
# --- database --------------------------------------------------------------
# compose provides postgres; sqlite is fine for a single user.
# CX_DATABASE_URL=sqlite:////data/cx-triage.db
CX_DATABASE_URL=postgresql+psycopg://cx:cx@db:5432/cxtriage
# --- data sources ----------------------------------------------------------
CX_PROMETHEUS_BASE=http://10.11.254.250:9090
# Container to relay Prometheus queries through when the host has no route to
# the internal network. Leave blank to auto-detect (ca1-osc, us1-osc, ...).
CX_PROMETHEUS_RELAY=
CX_TOOLS_PATH=/opt/cx-tools
# --- authentication --------------------------------------------------------
CX_AUTH_LOCAL_ENABLED=true
# Only used to create the very first account, when the user table is empty.
CX_BOOTSTRAP_ADMIN_EMAIL=admin@nexgencloud.com
CX_BOOTSTRAP_ADMIN_PASSWORD=
# --- Authentik SSO ---------------------------------------------------------
# Redirect URI to register in Authentik:
# ${CX_BASE_URL}/api/auth/oidc/callback
CX_OIDC_ENABLED=false
CX_OIDC_ISSUER=https://sso.nexgencloud.com/application/o/cx-triage/
CX_OIDC_CLIENT_ID=
CX_OIDC_CLIENT_SECRET=
CX_OIDC_SCOPES=openid email profile
# Members of this group become administrators.
CX_OIDC_ADMIN_GROUP=cx-triage-admins
CX_OIDC_GROUPS_CLAIM=groups
# --- feature flags ---------------------------------------------------------
# The master switch. With this off nothing can reach a customer, whatever else
# is configured. Leave it off on demo and staging instances.
CX_FEATURE_SEND_ENABLED=false
CX_FEATURE_ZENDESK=false
CX_FEATURE_JIRA=false
CX_FEATURE_LINKAGE_SCAN=true
# Refuses to send more than this in a rolling 24h, so a loop cannot mail everyone.
CX_SEND_DAILY_CAP=25
# --- Zendesk ---------------------------------------------------------------
# Admin Center -> Apps and integrations -> APIs -> Zendesk API -> Add API token
CX_ZENDESK_SUBDOMAIN=
CX_ZENDESK_EMAIL=
CX_ZENDESK_TOKEN=
CX_ZENDESK_PUBLIC_REPLY=true
# --- Jira ------------------------------------------------------------------
# https://id.atlassian.com/manage-profile/security/api-tokens
CX_JIRA_BASE=https://nexgencloud.atlassian.net
CX_JIRA_EMAIL=
CX_JIRA_TOKEN=
CX_JIRA_PROJECT=INFRA
CX_JIRA_ISSUE_TYPE=Task