Spaces:
Sleeping
Sleeping
File size: 8,257 Bytes
3cfc478 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | <!DOCTYPE html>
<html lang="et">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>ATOM · Command Center</title>
<style>
:root {
--green: #059669;
--green-dark: #047857;
--bg: #0b1220;
--panel: #111827;
--border: #1f2937;
--text: #f8fafc;
--muted: #94a3b8;
--card: #0f172a;
--ok: #34d399;
--warn: #fbbf24;
--err: #f87171;
}
* { box-sizing: border-box; }
body {
margin: 0; min-height: 100vh; font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
background: radial-gradient(1200px 600px at 10% -10%, #064e3b 0%, transparent 50%),
radial-gradient(900px 500px at 100% 0%, #1e3a5f 0%, transparent 45%),
var(--bg);
color: var(--text);
}
header {
display: flex; align-items: center; justify-content: space-between; gap: 1rem;
padding: 1rem 1.5rem; border-bottom: 1px solid var(--border);
background: rgba(15, 23, 42, 0.75); backdrop-filter: blur(10px); position: sticky; top: 0; z-index: 10;
}
.brand { display: flex; align-items: center; gap: .75rem; font-weight: 800; letter-spacing: .02em; }
.mark {
width: 36px; height: 36px; border-radius: 10px; display: grid; place-items: center;
background: linear-gradient(135deg, var(--green), #0ea5e9); color: white; font-size: .9rem;
}
.brand small { display: block; font-weight: 500; color: var(--muted); font-size: .75rem; }
nav { display: flex; flex-wrap: wrap; gap: .5rem; }
nav a, .btn {
color: var(--text); text-decoration: none; border: 1px solid var(--border); background: var(--panel);
padding: .55rem .85rem; border-radius: 10px; font-size: .9rem; font-weight: 600; cursor: pointer;
}
nav a:hover, .btn:hover { border-color: var(--green); }
.btn-primary { background: var(--green); border-color: var(--green); color: white; }
main { max-width: 1100px; margin: 0 auto; padding: 1.5rem; }
.hero {
display: grid; gap: 1rem; grid-template-columns: 1.4fr 1fr; margin-bottom: 1.25rem;
}
@media (max-width: 800px) { .hero { grid-template-columns: 1fr; } }
.card {
background: rgba(17, 24, 39, 0.9); border: 1px solid var(--border); border-radius: 16px; padding: 1.25rem;
}
h1 { margin: 0 0 .5rem; font-size: clamp(1.5rem, 3vw, 2rem); }
p { color: var(--muted); line-height: 1.55; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1rem; }
.tile h3 { margin: 0 0 .35rem; font-size: 1rem; }
.tile p { margin: 0 0 .85rem; font-size: .9rem; }
.status { display: flex; flex-wrap: wrap; gap: .5rem; margin: 1rem 0; }
.badge {
display: inline-flex; align-items: center; gap: .4rem; padding: .35rem .65rem; border-radius: 999px;
background: #0b1729; border: 1px solid var(--border); font-size: .8rem; color: var(--muted);
}
.badge.ok { color: var(--ok); border-color: rgba(52, 211, 153, .35); }
.badge.err { color: var(--err); border-color: rgba(248, 113, 113, .35); }
.badge.warn { color: var(--warn); border-color: rgba(251, 191, 36, .35); }
code, pre {
background: #020617; border: 1px solid var(--border); border-radius: 10px; color: #e2e8f0;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .8rem;
}
pre { padding: .85rem; overflow: auto; max-height: 220px; white-space: pre-wrap; word-break: break-word; }
.muted { color: var(--muted); font-size: .85rem; }
footer { margin-top: 1.5rem; color: var(--muted); font-size: .8rem; text-align: center; }
</style>
</head>
<body>
<header>
<div class="brand">
<div class="mark">AT</div>
<div>ATOM<span><small>Command Center · operator workspace</small></span></div>
</div>
<nav>
<a href="/docs">API Docs</a>
<a href="/api/hf/status">HF Status</a>
<a href="/api/healthz">Health</a>
<a href="/clients/">Kliendibaas</a>
<a href="/dashboard.html">AIMF dashboard</a>
<button class="btn" id="logoutBtn" type="button">Logi välja</button>
</nav>
</header>
<main>
<section class="hero">
<div class="card">
<h1>Tere tulemast ATOMisse</h1>
<p>
See on <strong>ATOM operaatori hub</strong> (mitte AI Money Flow kliendiportaal).
Pärast sisselogimist jõuad siia. All on live API staatus ja sinu konto.
</p>
<div class="status" id="badges">
<span class="badge warn">API: kontrollin…</span>
<span class="badge warn">Auth: kontrollin…</span>
</div>
<div style="display:flex; gap:.6rem; flex-wrap:wrap;">
<a class="btn btn-primary" href="/docs">Ava ATOM API</a>
<a class="btn" href="/login.html">Login leht</a>
<a class="btn" href="/">AIMF avaleht (klient)</a>
</div>
</div>
<div class="card">
<h3 style="margin-top:0">Sessioon</h3>
<p class="muted" id="sessionInfo">Tokenit loen…</p>
<pre id="meBox">/api/auth/me</pre>
</div>
</section>
<section class="grid">
<div class="card tile">
<h3>API / integratsioonid</h3>
<p>FastAPI backend, OpenAPI, health ja operaatori endpointid.</p>
<a class="btn" href="/docs">/docs</a>
</div>
<div class="card tile">
<h3>Kliendibaas</h3>
<p>Staatilised kliendi dossierid (HTML).</p>
<a class="btn" href="/clients/">/clients/</a>
</div>
<div class="card tile">
<h3>AI Money Flow</h3>
<p>Eraldi kliendiportaal (intake + demo dashboard).</p>
<a class="btn" href="/">Avaleht</a>
<a class="btn" href="/dashboard.html">Dashboard</a>
</div>
<div class="card tile">
<h3>Space health</h3>
<p>Hugging Face konteineri staatus.</p>
<a class="btn" href="/api/hf/status">/api/hf/status</a>
</div>
</section>
<footer>
ATOM Command Center · HF Docker Space · pärast login’it suunatakse siia (/atom.html)
</footer>
</main>
<script>
const badges = document.getElementById('badges');
const meBox = document.getElementById('meBox');
const sessionInfo = document.getElementById('sessionInfo');
const token = localStorage.getItem('token') || localStorage.getItem('auth_token') || '';
function setBadges(apiOk, authOk, apiText, authText) {
badges.innerHTML = `
<span class="badge ${apiOk ? 'ok' : 'err'}">API: ${apiText}</span>
<span class="badge ${authOk ? 'ok' : (token ? 'err' : 'warn')}">Auth: ${authText}</span>
`;
}
document.getElementById('logoutBtn').onclick = () => {
localStorage.removeItem('token');
localStorage.removeItem('auth_token');
location.href = '/login.html';
};
if (!token) {
sessionInfo.textContent = 'Token puudub — suunan loginile…';
setBadges(false, false, '—', 'puudub');
setTimeout(() => { location.href = '/login.html?next=/atom.html'; }, 700);
} else {
sessionInfo.textContent = 'Token olemas (localStorage).';
(async () => {
let apiOk = false, authOk = false, apiText = 'down', authText = 'fail';
try {
const h = await fetch('/api/hf/status');
const j = await h.json();
apiOk = h.ok;
apiText = apiOk ? (j.mode || 'ok') : ('HTTP ' + h.status);
} catch (e) {
apiText = 'error';
}
try {
const me = await fetch('/api/auth/me', {
headers: { Authorization: 'Bearer ' + token }
});
const body = await me.json().catch(() => ({}));
if (me.ok) {
authOk = true;
authText = body.email || 'ok';
meBox.textContent = JSON.stringify(body, null, 2);
} else {
authText = body.detail || ('HTTP ' + me.status);
meBox.textContent = JSON.stringify(body, null, 2);
}
} catch (e) {
authText = String(e);
meBox.textContent = String(e);
}
setBadges(apiOk, authOk, apiText, authText);
})();
}
</script>
</body>
</html>
|