181 lines
7.1 KiB
JavaScript
181 lines
7.1 KiB
JavaScript
/* functions.js — CRUD операции с функциями */
|
|
|
|
// setCodeMode переключает режим формы между вводом кода и загрузкой архива.
|
|
// prefix: 'c' (create) или 'e' (edit)
|
|
// mode: 'code' | 'archive'
|
|
function setCodeMode(prefix, mode) {
|
|
var codeArea = document.getElementById(prefix + '-code-area');
|
|
var archiveArea = document.getElementById(prefix + '-archive-area');
|
|
var btnCode = document.getElementById(prefix + '-mode-code');
|
|
var btnArchive = document.getElementById(prefix + '-mode-archive');
|
|
if (!codeArea || !archiveArea) return;
|
|
|
|
var isCode = mode === 'code';
|
|
codeArea.style.display = isCode ? '' : 'none';
|
|
archiveArea.style.display = isCode ? 'none' : '';
|
|
if (btnCode) btnCode.style.opacity = isCode ? '1' : '0.5';
|
|
if (btnArchive) btnArchive.style.opacity = isCode ? '0.5' : '1';
|
|
}
|
|
|
|
// lintArchiveFile — заглушка линтера для архивов.
|
|
// Реализация (распаковка + пофайловая проверка) добавляется позже.
|
|
function lintArchiveFile(prefix) {
|
|
var input = document.getElementById(prefix + '-archive-file');
|
|
if (!input || !input.files || !input.files[0]) {
|
|
showStatus('Выберите .zip файл', 'err');
|
|
return;
|
|
}
|
|
showStatus('Линтинг архива — пока не реализован (скоро)', 'info');
|
|
}
|
|
|
|
function parseMethods(v) {
|
|
const items = String(v || '').split(',').map(s => s.trim().toUpperCase()).filter(Boolean);
|
|
return items.length ? Array.from(new Set(items)) : ['GET'];
|
|
}
|
|
|
|
function parseTimeout(v) {
|
|
var n = Number(String(v || '').trim());
|
|
if (!Number.isFinite(n) || n <= 0) return 60;
|
|
return Math.round(n);
|
|
}
|
|
|
|
function onLangChange() {
|
|
var lang = document.getElementById('c-lang').value;
|
|
var t = LANG_TEMPLATES[lang];
|
|
if (t) {
|
|
document.getElementById('c-entry').value = t.entrypoint;
|
|
document.getElementById('c-code').value = t.code;
|
|
}
|
|
}
|
|
|
|
function openCreate() {
|
|
document.getElementById('c-lang').value = 'python';
|
|
onLangChange();
|
|
document.getElementById('c-timeout').value = '60';
|
|
document.getElementById('c-schedule-enabled').checked = false;
|
|
document.getElementById('c-cron').value = '';
|
|
toggleScheduleFields('c');
|
|
document.getElementById('create-modal').classList.add('open');
|
|
document.getElementById('c-name').focus();
|
|
}
|
|
|
|
function closeCreate() {
|
|
document.getElementById('create-modal').classList.remove('open');
|
|
}
|
|
|
|
async function submitCreate() {
|
|
const btn = document.getElementById('c-submit');
|
|
btn.disabled = true;
|
|
const progress = startTimedStatus('Создаём функцию...', 'Создание функции...', explainDelay);
|
|
try {
|
|
const name = document.getElementById('c-name').value.trim();
|
|
if (!name) throw new Error('name is required');
|
|
const lang = document.getElementById('c-lang').value.trim();
|
|
if (!lang) throw new Error('language is required');
|
|
|
|
await requestJSON(API_BASE + '/functions', 'POST', {
|
|
name: name,
|
|
language: lang,
|
|
entrypoint: document.getElementById('c-entry').value.trim(),
|
|
route: document.getElementById('c-route').value.trim(),
|
|
methods: parseMethods(document.getElementById('c-methods').value),
|
|
timeout: parseTimeout(document.getElementById('c-timeout').value),
|
|
code: document.getElementById('c-code').value
|
|
});
|
|
|
|
try {
|
|
await syncScheduleForFunction(name, 'c');
|
|
} catch (scheduleErr) {
|
|
try {
|
|
await requestJSON(API_BASE + '/functions/' + encodeURIComponent(name), 'DELETE');
|
|
} catch (rollbackErr) {
|
|
scheduleErr.message += '; rollback failed: ' + rollbackErr.message;
|
|
}
|
|
throw scheduleErr;
|
|
}
|
|
|
|
closeCreate();
|
|
progress.stop('Функция создана: ' + name, 'ok');
|
|
await reloadAll();
|
|
} catch (e) {
|
|
progress.stop('Ошибка создания: ' + e.message, 'err');
|
|
} finally {
|
|
btn.disabled = false;
|
|
}
|
|
}
|
|
|
|
async function openEdit(name) {
|
|
try {
|
|
const fn = await getJSON(API_BASE + '/functions/' + encodeURIComponent(name));
|
|
S.currentEdit = fn;
|
|
document.getElementById('e-title').textContent = 'Редактирование: ' + name;
|
|
document.getElementById('e-name').value = name;
|
|
document.getElementById('e-env').value = fn.environment || '';
|
|
document.getElementById('e-entry').value = fn.entrypoint || '';
|
|
document.getElementById('e-timeout').value = String(fn.timeout || 60);
|
|
document.getElementById('e-code').value = fn.code || '';
|
|
var schedule = timeTriggerByFn(name);
|
|
document.getElementById('e-schedule-enabled').checked = !!schedule;
|
|
document.getElementById('e-cron').value = (schedule && schedule.spec && schedule.spec.cron) || '';
|
|
toggleScheduleFields('e');
|
|
var envName = (fn.environment || '').toLowerCase();
|
|
var lang = 'python';
|
|
if (envName.includes('node')) lang = 'nodejs';
|
|
else if (envName.includes('go')) lang = 'go';
|
|
else if (envName.includes('ruby')) lang = 'ruby';
|
|
else if (envName.includes('php')) lang = 'php';
|
|
document.getElementById('e-lang-hidden').value = lang;
|
|
var aiRes = document.getElementById('e-ai-result');
|
|
if (aiRes) { aiRes.style.display = 'none'; aiRes.textContent = ''; }
|
|
var warnEl = document.getElementById('e-tf-warn');
|
|
if (warnEl) {
|
|
var isTf = /^tf-/.test(name) || /go[-_]env/.test(fn.environment || '');
|
|
warnEl.style.display = isTf ? 'block' : 'none';
|
|
}
|
|
document.getElementById('edit-modal').classList.add('open');
|
|
} catch (e) {
|
|
showStatus('Ошибка загрузки функции: ' + e.message, 'err');
|
|
}
|
|
}
|
|
|
|
function closeEdit() {
|
|
document.getElementById('edit-modal').classList.remove('open');
|
|
S.currentEdit = null;
|
|
}
|
|
|
|
async function submitEdit() {
|
|
if (!S.currentEdit) return;
|
|
const btn = document.getElementById('e-submit');
|
|
btn.disabled = true;
|
|
const progress = startTimedStatus('Сохраняем код...', 'Сохранение кода...', explainDelay);
|
|
try {
|
|
const name = S.currentEdit.name;
|
|
await requestJSON(API_BASE + '/functions/' + encodeURIComponent(name) + '/code', 'PUT', {
|
|
code: document.getElementById('e-code').value,
|
|
timeout: parseTimeout(document.getElementById('e-timeout').value)
|
|
});
|
|
|
|
await syncScheduleForFunction(name, 'e');
|
|
closeEdit();
|
|
progress.stop('Код обновлён: ' + name, 'ok');
|
|
await reloadAll();
|
|
} catch (e) {
|
|
progress.stop('Ошибка обновления: ' + e.message, 'err');
|
|
} finally {
|
|
btn.disabled = false;
|
|
}
|
|
}
|
|
|
|
async function removeFn(name) {
|
|
var tfWarn = (/^tf-/.test(name)) ? '\n\n⚠️ Эта функция управляется Terraform. Удаление приведёт к рассинхронизации state!' : '';
|
|
if (!confirm('Удалить функцию ' + name + '?' + tfWarn)) return;
|
|
var progress = startTimedStatus('Удаляем функцию...', 'Удаление функции...', explainDelay);
|
|
try {
|
|
await requestJSON(API_BASE + '/functions/' + encodeURIComponent(name), 'DELETE');
|
|
progress.stop('Функция удалена: ' + name, 'ok');
|
|
await reloadAll();
|
|
} catch (e) {
|
|
progress.stop('Ошибка удаления: ' + e.message, 'err');
|
|
}
|
|
}
|