187 lines
4.8 KiB
HTML
187 lines
4.8 KiB
HTML
<!doctype html>
|
|
<html lang="ru">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Fission Console</title>
|
|
<style>
|
|
:root {
|
|
--bg-page: #001120;
|
|
--bg-surface: #001929;
|
|
--bg-navbar: #001c34;
|
|
--border: #0b2d50;
|
|
--accent: #1a7fd4;
|
|
--text-primary: #e2ecf6;
|
|
--text-secondary: #6b8eaa;
|
|
}
|
|
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
|
background: var(--bg-page);
|
|
color: var(--text-primary);
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.navbar {
|
|
height: 56px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 20px;
|
|
background: var(--bg-navbar);
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
|
|
.title {
|
|
font-weight: 700;
|
|
letter-spacing: .3px;
|
|
}
|
|
|
|
.btn {
|
|
background: var(--accent);
|
|
color: #fff;
|
|
border: 0;
|
|
border-radius: 6px;
|
|
padding: 8px 12px;
|
|
cursor: pointer;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.wrap {
|
|
max-width: 1100px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
}
|
|
|
|
.grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
gap: 12px;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.card {
|
|
background: var(--bg-surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 14px;
|
|
}
|
|
|
|
.k {
|
|
color: var(--text-secondary);
|
|
font-size: 12px;
|
|
text-transform: uppercase;
|
|
letter-spacing: .06em;
|
|
}
|
|
|
|
.v {
|
|
font-size: 24px;
|
|
font-weight: 700;
|
|
margin-top: 6px;
|
|
}
|
|
|
|
.box {
|
|
background: var(--bg-surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 14px;
|
|
margin-top: 12px;
|
|
}
|
|
|
|
table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
font-size: 13px;
|
|
margin-top: 8px;
|
|
}
|
|
|
|
th, td {
|
|
text-align: left;
|
|
padding: 8px;
|
|
border-bottom: 1px solid #0b2a48;
|
|
}
|
|
|
|
.hint {
|
|
color: var(--text-secondary);
|
|
font-size: 12px;
|
|
margin-top: 8px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="navbar">
|
|
<div class="title">sless / Fission Console</div>
|
|
<button class="btn" onclick="reloadAll()">Refresh</button>
|
|
</div>
|
|
|
|
<div class="wrap">
|
|
<div class="grid">
|
|
<div class="card"><div class="k">Environments</div><div id="env-count" class="v">-</div></div>
|
|
<div class="card"><div class="k">Packages</div><div id="pkg-count" class="v">-</div></div>
|
|
<div class="card"><div class="k">Functions</div><div id="fn-count" class="v">-</div></div>
|
|
<div class="card"><div class="k">HTTP Triggers</div><div id="http-count" class="v">-</div></div>
|
|
<div class="card"><div class="k">Time Triggers</div><div id="time-count" class="v">-</div></div>
|
|
</div>
|
|
|
|
<div class="box">
|
|
<div style="font-weight:600;">Functions (MVP list)</div>
|
|
<table>
|
|
<thead>
|
|
<tr><th>Name</th><th>Environment</th><th>Package</th></tr>
|
|
</thead>
|
|
<tbody id="fn-rows"></tbody>
|
|
</table>
|
|
<div class="hint">Initial MVP. Next step: create/edit/invoke/delete.</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
async function getJSON(url) {
|
|
const r = await fetch(url);
|
|
if (!r.ok) {
|
|
throw new Error(url + ': ' + r.status);
|
|
}
|
|
return r.json();
|
|
}
|
|
|
|
function setText(id, value) {
|
|
document.getElementById(id).textContent = String(value);
|
|
}
|
|
|
|
async function reloadAll() {
|
|
try {
|
|
const [envs, pkgs, fns, http, time] = await Promise.all([
|
|
getJSON('/api/environments'),
|
|
getJSON('/api/packages'),
|
|
getJSON('/api/functions'),
|
|
getJSON('/api/httptriggers'),
|
|
getJSON('/api/timetriggers')
|
|
]);
|
|
|
|
setText('env-count', envs.length || 0);
|
|
setText('pkg-count', pkgs.length || 0);
|
|
setText('fn-count', fns.length || 0);
|
|
setText('http-count', http.length || 0);
|
|
setText('time-count', time.length || 0);
|
|
|
|
const rows = (fns || []).map(function (f) {
|
|
const spec = f.spec || {};
|
|
const env = (spec.environment && spec.environment.name) || '-';
|
|
const pkg = (spec.package && spec.package.packageref && spec.package.packageref.name) || '-';
|
|
const name = (f.metadata && f.metadata.name) || '-';
|
|
return '<tr><td>' + name + '</td><td>' + env + '</td><td>' + pkg + '</td></tr>';
|
|
}).join('');
|
|
|
|
document.getElementById('fn-rows').innerHTML = rows || '<tr><td colspan="3">No functions</td></tr>';
|
|
} catch (e) {
|
|
document.getElementById('fn-rows').innerHTML = '<tr><td colspan="3">Load error: ' + e.message + '</td></tr>';
|
|
}
|
|
}
|
|
|
|
reloadAll();
|
|
</script>
|
|
</body>
|
|
</html>
|