- Backend: новый файл status.go с функцией определения статуса пода и Package buildStatus - API: GET /functions теперь возвращает функции с полем status в аннотациях - Frontend: добавлена колонка Status в таблицу функций с цветными кругами - Легенда статусов выше таблицы - Версия v1.3.70
124 lines
6.5 KiB
JavaScript
124 lines
6.5 KiB
JavaScript
/* app.js — основная логика: загрузка данных, таблица функций */
|
|
|
|
async function reloadAll() {
|
|
const progress = startTimedStatus('Загрузка...', 'Загрузка данных', null);
|
|
|
|
// Показываем таблицу сразу с placeholder'ом
|
|
document.getElementById('fn-rows').innerHTML =
|
|
'<tr><td colspan="8" style="color:var(--text-secondary); font-style:italic;">⏳ Загружаем функции...</td></tr>';
|
|
|
|
try {
|
|
const [envs, pkgs, fns, http, times] = await Promise.all([
|
|
getJSON(API_BASE + '/environments'),
|
|
getJSON(API_BASE + '/packages'),
|
|
getJSON(API_BASE + '/functions'),
|
|
getJSON(API_BASE + '/httptriggers'),
|
|
getJSON(API_BASE + '/timetriggers')
|
|
]);
|
|
|
|
S.envs = envs || [];
|
|
S.fns = fns || [];
|
|
S.httpTriggers = http || [];
|
|
S.timeTriggers = times || [];
|
|
|
|
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('cron-count', new Set((times || []).map(function (t) {
|
|
return t && t.spec && t.spec.functionref && t.spec.functionref.name;
|
|
}).filter(Boolean)).size);
|
|
|
|
function makeFnRow(f) {
|
|
const spec = f.spec || {};
|
|
const meta = f.metadata || {};
|
|
const ann = meta.annotations || {};
|
|
const env = (spec.environment && spec.environment.name) || '-';
|
|
const pkg = (spec.package && spec.package.packageref && spec.package.packageref.name) || '-';
|
|
const entrypoint = (spec.package && spec.package.functionName) || '-';
|
|
const timeout = spec.functionTimeout || 60;
|
|
const name = (f.metadata && f.metadata.name) || '-';
|
|
const trig = httpTriggerByFn(name) || {};
|
|
const timeTrig = timeTriggerByFn(name) || {};
|
|
const route = (trig.spec && trig.spec.relativeurl) || '-';
|
|
const methods = (trig.spec && trig.spec.methods) || [];
|
|
const chips = methods.map(function (m) { return '<span class="chip">' + h(m) + '</span>'; }).join('');
|
|
const cron = (timeTrig.spec && timeTrig.spec.cron) || '';
|
|
const cronCell = cron ? '<span class="chip">' + h(cron) + '</span>' : '<span class="mono" style="color:var(--text-secondary)">—</span>';
|
|
const createdAt = ann['fission-console/created-at'] || meta.creationTimestamp || '-';
|
|
const updatedAt = ann['fission-console/updated-at'] || createdAt;
|
|
const sourceType = ann['fission-console/source-type'] || 'code';
|
|
const status = ann['fission-console/status'] || 'Cold';
|
|
|
|
// Статус функции: иконка и цвет
|
|
const statusColor = {
|
|
'Ready': '#2a2', 'Cold': '#22a', 'Building': '#aa2', 'Error': '#a22'
|
|
}[status] || '#666';
|
|
const statusTitle = {
|
|
'Ready': 'Готова (есть pod)',
|
|
'Cold': 'Холодная (pod создаётся при первом вызове)',
|
|
'Building': 'Собирается / деплоится',
|
|
'Error': 'Ошибка (pod в CrashLoopBackOff или build failed)'
|
|
}[status] || 'Неизвестно';
|
|
const statusDot = '<span style="display:inline-block; width:12px; height:12px; border-radius:50%; background:' + statusColor + '; cursor:default;" title="' + h(statusTitle) + '"></span>';
|
|
|
|
const sourceIcon = sourceType === 'archive'
|
|
? '<span title="Из архива (.zip)" style="font-size:1.1em; cursor:default;">📦</span>'
|
|
: '<span title="Из кода (редактор)" style="font-size:1.1em; cursor:default;">📝</span>';
|
|
var isGo = /go[-_]env/.test(env);
|
|
var isTf = /^tf-/.test(name);
|
|
var tfBadge = (isGo || isTf) ? '<span class="chip" style="background:#555;color:#ffa" title="Управляется Terraform. Изменения могут быть перезаписаны при terraform apply.">TF</span> ' : '';
|
|
// Сохраняем все данные в data-атрибуте для openInfo (избегаем повторного запроса)
|
|
var infoData = h(JSON.stringify({
|
|
name: name, env: env, pkg: pkg, entrypoint: entrypoint, timeout: timeout,
|
|
route: route, methods: methods, sourceType: sourceType,
|
|
createdAt: createdAt, updatedAt: updatedAt, cron: cron, status: status
|
|
}));
|
|
var actions = tfBadge +
|
|
'<button class="btn ghost" onclick="openInfo(\'' + h(name) + '\', this)" data-info="' + infoData + '">Info</button> ' +
|
|
'<button class="btn ghost" onclick="openEdit(\'' + h(name) + '\')">Ред.</button> ' +
|
|
'<button class="btn ghost" onclick="openInvoke(\'' + h(name) + '\')">Вызов</button> ' +
|
|
'<button class="btn ghost" onclick="openLogs(\'' + h(name) + '\')">Логи</button> ' +
|
|
'<button class="btn danger" onclick="removeFn(\'' + h(name) + '\')">Удалить</button>';
|
|
return '<tr>' +
|
|
'<td class="mono">' + h(name) + '</td>' +
|
|
'<td style="text-align:center;">' + statusDot + '</td>' +
|
|
'<td style="text-align:center;">' + sourceIcon + '</td>' +
|
|
'<td>' + timestampCell(createdAt) + '</td>' +
|
|
'<td>' + timestampCell(updatedAt) + '</td>' +
|
|
'<td class="mono">' + h(route) + '</td>' +
|
|
'<td>' + chips + '</td>' +
|
|
'<td>' + cronCell + '</td>' +
|
|
'<td class="nowrap">' + actions + '</td>' +
|
|
'</tr>';
|
|
}
|
|
|
|
var fnList = fns || [];
|
|
if (fnList.length === 0) {
|
|
progress.stop('Загружено: нет функций', '');
|
|
document.getElementById('fn-rows').innerHTML = '<tr><td colspan="8">Нет функций</td></tr>';
|
|
} else {
|
|
// Рендерим первую строку сразу — убираем placeholder
|
|
var tbody = document.getElementById('fn-rows');
|
|
tbody.innerHTML = makeFnRow(fnList[0]);
|
|
// Остальные добавляем по одной с небольшой задержкой
|
|
var i = 1;
|
|
function appendNext() {
|
|
if (i >= fnList.length) {
|
|
progress.stop('Загружено ' + fnList.length + ' функций', 'ok');
|
|
return;
|
|
}
|
|
var tr = document.createElement('tbody');
|
|
tr.innerHTML = makeFnRow(fnList[i]);
|
|
tbody.appendChild(tr.firstChild);
|
|
i++;
|
|
setTimeout(appendNext, 40);
|
|
}
|
|
setTimeout(appendNext, 40);
|
|
}
|
|
} catch (e) {
|
|
document.getElementById('fn-rows').innerHTML = '<tr><td colspan="8">Load error: ' + e.message + '</td></tr>';
|
|
progress.stop('Ошибка загрузки: ' + e.message, 'err');
|
|
}
|
|
}
|