feat: переменные окружения функций (env vars в CRD)
- Backend: handleGetFunctionEnvVars / handlePutFunctionEnvVars в function_crud.go - API: GET/PUT /functions/:name/envvars - handleGetFunction теперь возвращает env_vars в ответе - Frontend: блок env vars в edit modal (key=value + [×] + [+ Добавить]) - TF-функции показывают env vars в режиме read-only - Документация: doc/descriptions/function-envvars-flow.md - Версия v1.3.72
This commit is contained in:
+13
-2
@@ -102,7 +102,7 @@
|
||||
<div class="nubes">NUBES</div>
|
||||
<div class="product">FISSION CONSOLE</div>
|
||||
</div>
|
||||
<div style="font-size:0.65rem; color:var(--text-secondary); margin-left:10px; align-self:center; opacity:0.7;">v1.3.71</div>
|
||||
<div style="font-size:0.65rem; color:var(--text-secondary); margin-left:10px; align-self:center; opacity:0.7;">v1.3.72</div>
|
||||
</div>
|
||||
<div class="row" style="margin:0;">
|
||||
<button class="btn ghost" onclick="reloadAll()">Refresh</button>
|
||||
@@ -389,6 +389,17 @@
|
||||
style="display:none;margin-top:8px;padding:10px 12px;border-radius:6px;font-size:13px;line-height:1.5;white-space:pre-wrap;font-family:monospace;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Переменные окружения -->
|
||||
<div style="margin-top:16px;">
|
||||
<div style="font-weight:600; margin-bottom:8px; font-size:13px;">Переменные окружения</div>
|
||||
<div id="e-tf-env-warn" style="display:none; margin-bottom:8px; padding:6px 10px; background:#553300; color:#ffa; border-radius:4px; font-size:12px;">
|
||||
⚠️ Функция управляется Terraform — переменные только для чтения
|
||||
</div>
|
||||
<div id="e-envvars-list" style="display:flex; flex-direction:column; gap:4px; margin-bottom:8px;"></div>
|
||||
<button id="e-add-envvar-btn" class="btn ghost" style="font-size:12px;" onclick="addEnvVarRow('e')">+ Добавить переменную</button>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="btn ghost" onclick="closeEdit()">Отмена</button>
|
||||
<button id="e-submit" class="btn" onclick="submitEdit()">Сохранить</button>
|
||||
@@ -498,7 +509,7 @@
|
||||
</div>
|
||||
|
||||
<div class="actions" style="justify-content:space-between; align-items:center;">
|
||||
<span style="font-size:0.75rem; color:var(--text-secondary);">v1.3.71</span>
|
||||
<span style="font-size:0.75rem; color:var(--text-secondary);">v1.3.72</span>
|
||||
<button class="btn ghost" onclick="closeHelp()">Закрыть</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -191,6 +191,11 @@ async function openEdit(name) {
|
||||
var isTf = /^tf-/.test(name) || /go[-_]env/.test(fn.environment || '');
|
||||
warnEl.style.display = isTf ? 'block' : 'none';
|
||||
}
|
||||
|
||||
// Env vars — отрисовать блок; TF-функции — только чтение
|
||||
var isTfFn = /^tf-/.test(name) || /go[-_]env/.test(fn.environment || '');
|
||||
renderEnvVars('e', fn.env_vars || [], isTfFn);
|
||||
|
||||
document.getElementById('edit-modal').classList.add('open');
|
||||
} catch (e) {
|
||||
showStatus('Ошибка загрузки функции: ' + e.message, 'err');
|
||||
@@ -238,6 +243,16 @@ async function submitEdit() {
|
||||
}
|
||||
|
||||
await syncScheduleForFunction(name, 'e');
|
||||
|
||||
// Сохраняем env vars (только если не TF-функция)
|
||||
var isTfFn = /^tf-/.test(name);
|
||||
if (!isTfFn) {
|
||||
var envVars = collectEnvVars('e');
|
||||
await requestJSON(API_BASE + '/functions/' + encodeURIComponent(name) + '/envvars', 'PUT', {
|
||||
env_vars: envVars
|
||||
});
|
||||
}
|
||||
|
||||
closeEdit();
|
||||
progress.stop('Код обновлён: ' + name, 'ok');
|
||||
await reloadAll();
|
||||
@@ -260,3 +275,91 @@ async function removeFn(name) {
|
||||
progress.stop('Ошибка удаления: ' + e.message, 'err');
|
||||
}
|
||||
}
|
||||
|
||||
// --- Env Vars UI ---
|
||||
|
||||
// renderEnvVars отрисовывает список переменных окружения в блоке prefix-envvars-list
|
||||
// vars: [{name: "KEY", value: "VAL"}, ...]
|
||||
// readOnly: true для TF-функций
|
||||
function renderEnvVars(prefix, vars, readOnly) {
|
||||
var list = document.getElementById(prefix + '-envvars-list');
|
||||
var addBtn = document.getElementById(prefix + '-add-envvar-btn');
|
||||
var tfWarn = document.getElementById(prefix + '-tf-env-warn');
|
||||
if (!list) return;
|
||||
list.innerHTML = '';
|
||||
if (readOnly) {
|
||||
if (tfWarn) tfWarn.style.display = '';
|
||||
if (addBtn) addBtn.style.display = 'none';
|
||||
} else {
|
||||
if (tfWarn) tfWarn.style.display = 'none';
|
||||
if (addBtn) addBtn.style.display = '';
|
||||
}
|
||||
(vars || []).forEach(function(ev, idx) {
|
||||
list.appendChild(makeEnvVarRow(prefix, ev.name || '', ev.value || '', readOnly, idx));
|
||||
});
|
||||
}
|
||||
|
||||
// makeEnvVarRow создаёт одну строку key=value с кнопкой удаления
|
||||
function makeEnvVarRow(prefix, key, val, readOnly, idx) {
|
||||
var row = document.createElement('div');
|
||||
row.style.cssText = 'display:flex; gap:6px; align-items:center;';
|
||||
row.dataset.envIdx = idx;
|
||||
|
||||
var kInput = document.createElement('input');
|
||||
kInput.placeholder = 'KEY';
|
||||
kInput.value = key;
|
||||
kInput.disabled = readOnly;
|
||||
kInput.style.cssText = 'flex:1; font-size:12px; font-family:monospace;';
|
||||
kInput.dataset.envKey = '1';
|
||||
|
||||
var vInput = document.createElement('input');
|
||||
vInput.placeholder = 'value';
|
||||
vInput.value = val;
|
||||
vInput.disabled = readOnly;
|
||||
vInput.style.cssText = 'flex:2; font-size:12px; font-family:monospace;';
|
||||
vInput.dataset.envVal = '1';
|
||||
|
||||
row.appendChild(kInput);
|
||||
|
||||
var eq = document.createElement('span');
|
||||
eq.textContent = '=';
|
||||
eq.style.cssText = 'color:var(--text-secondary); font-family:monospace;';
|
||||
row.appendChild(eq);
|
||||
row.appendChild(vInput);
|
||||
|
||||
if (!readOnly) {
|
||||
var delBtn = document.createElement('button');
|
||||
delBtn.textContent = '×';
|
||||
delBtn.className = 'btn ghost';
|
||||
delBtn.style.cssText = 'padding:2px 8px; font-size:14px; line-height:1;';
|
||||
delBtn.title = 'Удалить переменную';
|
||||
delBtn.onclick = function() { row.remove(); };
|
||||
row.appendChild(delBtn);
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
// addEnvVarRow добавляет пустую строку в список env vars
|
||||
function addEnvVarRow(prefix) {
|
||||
var list = document.getElementById(prefix + '-envvars-list');
|
||||
if (!list) return;
|
||||
var idx = list.children.length;
|
||||
list.appendChild(makeEnvVarRow(prefix, '', '', false, idx));
|
||||
}
|
||||
|
||||
// collectEnvVars читает текущие значения из DOM и возвращает [{name, value}, ...]
|
||||
function collectEnvVars(prefix) {
|
||||
var list = document.getElementById(prefix + '-envvars-list');
|
||||
if (!list) return [];
|
||||
var result = [];
|
||||
var rows = list.querySelectorAll('div[data-env-idx]');
|
||||
rows.forEach(function(row) {
|
||||
var k = row.querySelector('[data-env-key]');
|
||||
var v = row.querySelector('[data-env-val]');
|
||||
var key = k ? k.value.trim() : '';
|
||||
var val = v ? v.value : '';
|
||||
if (key) result.push({name: key, value: val});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user