Files
appfactory-portal/app/routes/developers.py
T
2026-06-17 11:39:52 +02:00

184 lines
8.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import html
from fastapi import APIRouter, Depends
from fastapi.responses import HTMLResponse
from ..auth import require_user
from ..config import DEFAULT_GITEA_ORG, get_gitea_public_url, read_env_value
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-file-lines", "Logy", "/portal/logs",
"Souhrnné čtení logů napříč všemi úlohami; filtr chyb a proklik na konkrétní řádek úlohy.", "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-sliders", "Environment", "/portal/admin/environment",
"Bezpečná úprava hlavního appfactory.env (backup, validace) + navazující redeploy core služeb (Portal, Worker, Webhook, Monitor, Gateway, Gitea, Registry) přes job frontu.", "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 = [
("guest", "Výchozí role nového účtu po prvním přihlášení. Bez přístupu vidí jen výzvu požádat administrátora o přidělení práv."),
("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", response_class=HTMLResponse)
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>
"""
gitea_url = get_gitea_public_url()
gitea_org = read_env_value("GITEA_ORG", DEFAULT_GITEA_ORG)
clone_base = gitea_url or "<gitea-url>"
clone_cmd = html.escape(f"git clone {clone_base}/{gitea_org}/<app-id>.git", quote=True)
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-cloud-arrow-up" aria-hidden="true"></i> Jak publikovat</h2>
<p class="muted">Postup od založení nové služby po nasazení změn.</p>
<ol class="publish-steps">
<li>
<strong>Vytvořte novou službu.</strong>
Otevřete <a href="/portal/new-app" target="_blank" rel="noopener">formulář Nová služba</a>;
vznikne Gitea repozitář, workspace a první nasazení.
</li>
<li>
<strong>Naklonujte repozitář.</strong>
<div class="cmd-row">
<input readonly value="{clone_cmd}" id="publish-clone">
<button type="button" onclick="return copyText(this)"><i class="fa-solid fa-copy" aria-hidden="true"></i> Kopírovat</button>
</div>
<span class="muted">Přesný příkaz pro konkrétní službu (HTTP i SSH) najdete také na stránce
<a href="/portal/apps">Služby</a>.</span>
</li>
<li>
<strong>Upravte kód.</strong>
Buď se řiďte souborem <code>AGENTS.md</code> v kořenové složce projektu, nebo úpravy
nechte provést AI (agent si <code>AGENTS.md</code> přečte sám).
</li>
<li>
<strong>Commit a push.</strong>
Zacommitujte a pushněte změny — build a nasazení se spustí automaticky
(worker je vždy spuštěný a změnu rovnou zpracuje).
</li>
<li>
<strong>Zkontrolujte výsledek.</strong>
Stav nasazení a běh služby ověříte na stránce
<a href="/portal/apps">Služby</a> (stav, health, dokumentace).
</li>
</ol>
</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,
)