Files
appfactory-portal/app/routes/developers.py
T
2026-06-15 11:00:09 +02:00

140 lines
5.7 KiB
Python

import html
from fastapi import APIRouter, Depends
from ..auth import require_user
from ..templates.layout import page
router = APIRouter()
# Portal sections (the admin/agenda links shown in the navigation). role: minimum role that can
# actually use the section; viewers may see read-only data where a section allows it.
PORTAL_SECTIONS = [
("fa-server", "Služby", "/portal/apps",
"Přehled nasazených služeb: stav, dokumentace, prostředky (RAM/CPU), Git a akce.", "viewer (čtení) / developer / admin"),
("fa-list-check", "Úlohy", "/portal/jobs",
"Fronta a historie úloh (nasazení, skripty) včetně stavů a logů.", "developer / admin"),
("fa-calendar-days", "Plánované skripty", "/portal/scheduled-scripts",
"Cron-like skripty spouštěné na pozadí; úpravy jen pro administrátory.", "developer / admin"),
("fa-bell", "Alerting", "/portal/alerting/rules",
"Pravidla alertů a jejich skripty; úpravy jen pro administrátory.", "developer / admin"),
("fa-gauge-high", "Přehled", "/portal/operations",
"Operační přehled systému, běžící nasazení a stav služeb.", "admin"),
("fa-diagram-project", "Migration Readiness", "/portal/migration-readiness",
"Připravenost a deploy core služeb AppFactory.", "admin"),
("fa-rocket", "Nasazení", "/portal/deployments",
"Historie deploy běhů, stavů a výstupů z deploy procesu.", "developer / admin"),
("fa-triangle-exclamation", "Incidenty", "/portal/incidents",
"Evidované incidenty služeb.", "developer / admin"),
("fa-gears", "Workery", "/portal/workers",
"Stav workerů zpracovávajících úlohy.", "admin"),
("fa-users", "Users", "/portal/admin/users",
"Správa uživatelů a rolí (viewer / developer / admin).", "admin"),
("fa-clipboard-list", "Audit", "/portal/audit",
"Auditní log akcí provedených v portálu.", "admin"),
("fa-box-archive", "Zálohy", "/portal/backups",
"Vytváření, stažení a obnova záloh AppFactory. Obnova je jen pro administrátory.", "admin"),
]
# Public / API endpoints not gated by the portal login.
PUBLIC_ENDPOINTS = [
("GET", "/apps", "Veřejný JSON katalog služeb (název + odkaz na dokumentaci). Routuje se přes Caddy."),
("GET", "/portal/health", "Health endpoint portálu (HTTP 200, pokud portál běží)."),
("GET", "/apps/<app-id>/docs", "Swagger/OpenAPI dokumentace konkrétní služby."),
("GET", "/apps/<app-id>/health", "Health endpoint konkrétní služby."),
]
# Roles model.
ROLES = [
("viewer", "Pouze pro čtení: vidí jaké služby běží a jejich dokumentaci (Swagger). Bez Gitu, úprav, nasazení a mazání."),
("developer", "Vše co viewer + provozní workflow služeb: Git/klonování, nasazení, proměnné, prostředky, metadata. Nemůže mazat služby ani do Administrace."),
("admin", "Plný přístup: vše co developer + mazání služeb, správa záloh a jejich obnova, správa uživatelů, audit a admin-only operace."),
]
@router.get("/developers")
def developers_page(user=Depends(require_user)):
section_rows = ""
for icon, title, href, desc, roles in PORTAL_SECTIONS:
section_rows += f"""
<tr>
<td><a href="{href}"><i class="fa-solid {icon}" aria-hidden="true"></i> {html.escape(title)}</a></td>
<td><code>{html.escape(href)}</code></td>
<td>{html.escape(desc)}</td>
<td><span class="muted">{html.escape(roles)}</span></td>
</tr>
"""
endpoint_rows = ""
for method, path, desc in PUBLIC_ENDPOINTS:
endpoint_rows += f"""
<tr>
<td><span class="pill">{html.escape(method)}</span></td>
<td><code>{path}</code></td>
<td>{html.escape(desc)}</td>
</tr>
"""
role_rows = ""
for role, desc in ROLES:
role_rows += f"""
<tr>
<td><span class="pill pill-muted">{html.escape(role)}</span></td>
<td>{html.escape(desc)}</td>
</tr>
"""
return page(
"Pro vývojáře",
f"""
<div class="card">
<h2><i class="fa-solid fa-code" aria-hidden="true"></i> Pro vývojáře</h2>
<p class="muted">
Přehled všech sekcí portálu, veřejných endpointů a rolí. Portál běží za reverzní proxy
na cestě <code>/portal</code>; jednotlivé služby na <code>/apps/&lt;app-id&gt;</code>.
</p>
</div>
<div class="card">
<h2><i class="fa-solid fa-diagram-project" aria-hidden="true"></i> Sekce portálu (agendy)</h2>
<table>
<tr>
<th>Sekce</th>
<th>Odkaz</th>
<th>Popis</th>
<th>Role</th>
</tr>
{section_rows}
</table>
</div>
<div class="card">
<h2><i class="fa-solid fa-plug" aria-hidden="true"></i> Veřejné / API endpointy</h2>
<table>
<tr>
<th>Metoda</th>
<th>Cesta</th>
<th>Popis</th>
</tr>
{endpoint_rows}
</table>
<p class="muted">
Veřejný katalog <code>/apps</code> vrací JSON se seznamem služeb a odkazy na jejich dokumentaci.
</p>
</div>
<div class="card">
<h2><i class="fa-solid fa-user-shield" aria-hidden="true"></i> Role a oprávnění</h2>
<table>
<tr>
<th>Role</th>
<th>Co může</th>
</tr>
{role_rows}
</table>
</div>
""",
user=user,
)