feat(welcome): página de bienvenida en / del Golden Path (shape 1.2.0)
All checks were successful
__APP_NAME__ — build & deploy / ¿Credenciales de build listas? (push) Successful in 0s
__APP_NAME__ — build & deploy / Calidad + build + push (push) Has been skipped
__APP_NAME__ — build & deploy / ¿Cluster del tenant listo? (push) Has been skipped
__APP_NAME__ — build & deploy / Helm upgrade --install (push) Has been skipped

This commit is contained in:
2026-06-10 23:07:02 +00:00
parent 56aa5f78a4
commit 6037476842

137
code/app/welcome.py Normal file
View File

@@ -0,0 +1,137 @@
# ruff: noqa: E501 (HTML embebido: líneas largas intencionales, no se parten para no romper el render)
"""
Página de bienvenida por defecto del Golden Path `web-backend/python`.
Servida en la ruta raíz `/`. Es lo primero que ve el tenant cuando su
plataforma queda lista: explica cómo usar su entorno y deja claro que, en
cuanto haga su primer commit + push, su desarrollo reemplazará esta página.
Los placeholders `__APP_NAME__`, `__REPO_OWNER__` y `__REPO_NAME__` los
sustituye `repo-provisioner` server-side al scaffoldear el repo del tenant.
"""
WELCOME_HTML = """<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>__APP_NAME__ — Tu plataforma Cove</title>
<style>
:root {
--copper: #b5651d;
--copper-soft: #b5651d18;
--ink: #1c1917;
--muted: #6b6660;
--surface: #ffffff;
--bg: #faf8f5;
--border: #ece7e0;
--ok: #15803d;
--ok-bg: #dcfce7;
}
* { box-sizing: border-box; }
body {
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
background: var(--bg); color: var(--ink);
margin: 0; padding: 3rem 1rem; line-height: 1.6;
}
.card {
max-width: 760px; margin: 0 auto; background: var(--surface);
border: 1px solid var(--border); border-radius: 16px;
padding: 2.5rem 2.75rem; box-shadow: 0 8px 30px #1c191708;
}
.brand { display: flex; align-items: center; gap: .6rem; font-weight: 700;
letter-spacing: .02em; color: var(--copper); font-size: 1.05rem; }
.brand .dot { width: 12px; height: 12px; border-radius: 50%; background: var(--copper); }
h1 { font-size: 1.9rem; margin: 1.2rem 0 .3rem; letter-spacing: -.02em; }
.sub { color: var(--muted); margin: 0 0 1.5rem; }
.pill { display: inline-flex; align-items: center; gap: .5rem; font-size: .82rem;
font-weight: 600; padding: .35rem .8rem; border-radius: 999px;
background: var(--copper-soft); color: var(--copper); }
.status { display: inline-flex; align-items: center; gap: .5rem; font-size: .82rem;
font-weight: 600; padding: .35rem .8rem; border-radius: 999px;
background: #f4f4f4; color: var(--muted); margin-left: .5rem; }
.status.ok { background: var(--ok-bg); color: var(--ok); }
.status .led { width: 8px; height: 8px; border-radius: 50%; background: currentColor; }
.callout {
border-left: 3px solid var(--copper); background: var(--copper-soft);
padding: 1rem 1.2rem; border-radius: 0 10px 10px 0; margin: 1.75rem 0;
font-weight: 500;
}
h2 { font-size: 1.05rem; margin: 2rem 0 .8rem; }
ol { padding-left: 0; counter-reset: step; list-style: none; margin: 0; }
ol li { counter-increment: step; position: relative; padding: .55rem 0 .55rem 2.6rem; }
ol li::before {
content: counter(step); position: absolute; left: 0; top: .5rem;
width: 1.7rem; height: 1.7rem; border-radius: 50%;
background: var(--copper); color: #fff; font-size: .82rem; font-weight: 700;
display: flex; align-items: center; justify-content: center;
}
code, pre { font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; }
code { background: #f4f1ec; padding: .15rem .45rem; border-radius: 5px; font-size: .88em; }
pre { background: var(--ink); color: #f5f0e9; padding: .9rem 1.1rem; border-radius: 10px;
overflow-x: auto; font-size: .85rem; margin: .4rem 0 0; }
.links { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: 2rem;
padding-top: 1.5rem; border-top: 1px solid var(--border); }
.links a { text-decoration: none; font-size: .85rem; font-weight: 600;
color: var(--ink); border: 1px solid var(--border); padding: .45rem .9rem;
border-radius: 8px; transition: border-color .15s, color .15s; }
.links a:hover { border-color: var(--copper); color: var(--copper); }
footer { text-align: center; color: var(--muted); font-size: .78rem; margin-top: 2rem; }
</style>
</head>
<body>
<div class="card">
<div class="brand"><span class="dot"></span> CORALWARE · COVE</div>
<h1>Tu plataforma está lista 🎉</h1>
<p class="sub">Bienvenido. Este es tu entorno <strong>__APP_NAME__</strong>, corriendo en tu propio cluster.</p>
<span class="pill">__APP_NAME__</span>
<span id="health" class="status"><span class="led"></span> comprobando…</span>
<div class="callout">
📦 <strong>En cuanto hagas tu primer <code>commit</code> + <code>push</code>, tu desarrollo
reemplazará esta página.</strong> Esto es solo la bienvenida por defecto del Golden Path.
</div>
<h2>Cómo empezar</h2>
<ol>
<li>
Clona tu repositorio:
<pre>git clone https://gitea.coralsafety.com/__REPO_OWNER__/__REPO_NAME__.git</pre>
</li>
<li>Edita tu código en <code>code/</code> — la app es FastAPI (Python). Tu ruta <code>/</code> reemplaza esta página.</li>
<li>
Haz commit y push a <code>main</code>:
<pre>git add .
git commit -m "Mi primer cambio"
git push origin main</pre>
</li>
<li>El CI construye la imagen y la despliega automáticamente en tu cluster (~35 min).</li>
<li>Recarga esta misma URL — <strong>aquí verás tu desarrollo</strong>.</li>
</ol>
<div class="links">
<a href="https://gitea.coralsafety.com/__REPO_OWNER__/__REPO_NAME__" target="_blank">📂 Tu repositorio</a>
<a href="/docs">📘 API Docs (Swagger)</a>
<a href="/health">💚 Health</a>
<a href="/demo">🧩 Demo</a>
</div>
<footer>Coralware IDP · Golden Path <code>web-backend/python</code></footer>
</div>
<script>
fetch('/health').then(function (r) { return r.json(); }).then(function (d) {
var el = document.getElementById('health');
var ok = d.status === 'ok';
el.className = 'status' + (ok ? ' ok' : '');
el.innerHTML = '<span class="led"></span> ' + (ok ? 'En línea · DB: ' + d.db : 'Estado: ' + d.status);
}).catch(function () {
var el = document.getElementById('health');
el.innerHTML = '<span class="led"></span> /health no responde';
});
</script>
</body>
</html>
"""