import html
from fastapi import APIRouter, Depends, Form, HTTPException, Request
from fastapi.responses import HTMLResponse, RedirectResponse
from app.auth import require_user
from app.db.audit import log_audit_event
from app.db.jobs import create_job
from app.db.migration_readiness import (
BOOTSTRAP_SCRIPT,
PREFLIGHT_SCRIPT,
get_latest_bootstrap_job,
get_latest_preflight_job,
summarize_readiness,
summarize_script_run,
)
from app.routes.jobs import render_job_status
from app.templates.layout import page
router = APIRouter()
BOOTSTRAP_MODES = {"check", "repair", "deploy"}
def render_readiness_pill(ready: bool) -> str:
if ready:
return 'READY'
return 'NOT READY'
def render_script_result(title: str, script_name: str, summary: dict) -> str:
job = summary.get("job")
job_rows = f'
| Zatím není uložený žádný výsledek skriptu {html.escape(script_name)}. |
'
if job:
job_id = html.escape(str(job.get("id", "")))
return_code = summary.get("return_code")
return_code_label = "" if return_code is None else html.escape(str(return_code))
job_rows = f"""
| Úloha | #{job_id} |
| Status | {render_job_status(job.get("status"))} |
| Return code | {return_code_label} |
| Started at | {html.escape(job.get("started_at", "") or "")} |
| Finished at | {html.escape(job.get("finished_at", "") or "")} |
"""
stdout = html.escape(summary.get("stdout") or "Zatím není uložený žádný stdout log.")
stderr = html.escape(summary.get("stderr") or "Zatím není uložený žádný stderr log.")
return f"""
{html.escape(title)}
Poslední výsledek maintenance skriptu {html.escape(script_name)} uložený v databázi.
"""
@router.get("/migration-readiness", response_class=HTMLResponse)
def migration_readiness_page(request: Request, user=Depends(require_user)):
latest_job = get_latest_preflight_job()
summary = summarize_readiness(latest_job)
bootstrap_summary = summarize_script_run(get_latest_bootstrap_job())
job = summary["job"]
log_audit_event(
user,
action="migration_readiness.viewed",
target_type="migration_readiness",
target_id=PREFLIGHT_SCRIPT,
metadata={"job_id": job.get("id") if job else None},
)
job_rows = '| Zatím není uložený žádný výsledek preflight kontroly. |
'
if job:
job_id = html.escape(str(job.get("id", "")))
return_code = summary.get("return_code")
return_code_label = "" if return_code is None else html.escape(str(return_code))
job_rows = f"""
| Úloha | #{job_id} |
| Stav úlohy | {render_job_status(job.get("status"))} |
| Return code | {return_code_label} |
| Vytvořeno | {html.escape(job.get("created_at", "") or "")} |
| Spuštěno | {html.escape(job.get("started_at", "") or "")} |
| Dokončeno | {html.escape(job.get("finished_at", "") or "")} |
| Worker | {html.escape(job.get("worker_id", "") or "")} |
"""
stdout = html.escape(summary.get("stdout") or "Zatím není uložený žádný stdout log.")
stderr = html.escape(summary.get("stderr") or "Zatím není uložený žádný stderr log.")
return page(
"Migration Readiness",
f"""
Migration Readiness
Poslední výsledek maintenance skriptu {html.escape(PREFLIGHT_SCRIPT)} uložený v databázi. Stav READY / NOT READY vychází pouze z return code jobu.
Bootstrap v2 má explicitní režimy.
- check bezpečně ověřuje stav bez oprav.
- repair opravuje ownership /opt/appfactory a nastavuje executable práva pro maintenance a alert skripty.
- deploy provádí plný bootstrap včetně deploy core services a spuštění preflight-check.sh.
Portál bootstrap nespouští přímo přes shell. Pouze založí maintenance job pro {html.escape(BOOTSTRAP_SCRIPT)}.
Plný Bootstrap Deploy není výchozí akce.
Režim deploy deployuje core services a může změnit stav systému. Spouštějte ho jen po kontrole dopadu.
Stav{render_readiness_pill(summary["ready"])}
OK{html.escape(str(summary["ok_count"]))}
WARN{html.escape(str(summary["warn_count"]))}
FAIL{html.escape(str(summary["fail_count"]))}
Raw preflight stdout
{stdout}
Raw preflight stderr
{stderr}
{render_script_result("Poslední výsledek Bootstrap v2", BOOTSTRAP_SCRIPT, bootstrap_summary)}
""",
user=user,
)
@router.post("/migration-readiness/run")
def run_migration_readiness(user=Depends(require_user)):
job_id = create_job(
job_type="run_script",
target_type="maintenance_script",
target_id=PREFLIGHT_SCRIPT,
payload={"script_name": PREFLIGHT_SCRIPT},
user=user,
source="portal_manual",
)
return RedirectResponse(url=f"/portal/jobs/{job_id}", status_code=303)
@router.post("/migration-readiness/bootstrap")
def run_bootstrap_v2(mode: str = Form(...), user=Depends(require_user)):
mode = (mode or "").strip().lower()
if mode not in BOOTSTRAP_MODES:
raise HTTPException(status_code=400, detail="Neplatný režim bootstrapu")
job_id = create_job(
job_type="run_script",
target_type="maintenance_script",
target_id=BOOTSTRAP_SCRIPT,
payload={
"script_name": BOOTSTRAP_SCRIPT,
"args": [mode],
"arguments": [mode],
"mode": mode,
},
user=user,
source="portal_manual",
)
return RedirectResponse(url=f"/portal/jobs/{job_id}", status_code=303)