fix: restore NSReconciler, archive upload create/edit, fix version label both places; v1.3.45

- tenant.go: restore StartNSReconciler (sync FISSION_RESOURCE_NAMESPACES every 30s)
- main.go: call nsm.StartNSReconciler on startup
- handlers.go: handleCreateFunctionFromArchive (multipart), handleUpdateFunctionArchive (PUT /archive)
  source-type annotation, source_type in GET response
- functions.js: submitCreate/submitEdit archive mode, openEdit uses source_type
- index.html: version label updated in BOTH places (line 103 and 403) to v1.3.45
- console.yaml: image v1.3.45
- doc: archive-and-edit-modal-flow.md
This commit is contained in:
“Naeel”
2026-05-04 06:16:11 +04:00
parent db3815d03d
commit d342b649a8
8 changed files with 784 additions and 19 deletions
+2 -2
View File
@@ -100,7 +100,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.40</div>
<div style="font-size:0.65rem; color:var(--text-secondary); margin-left:10px; align-self:center; opacity:0.7;">v1.3.45</div>
</div>
<div class="row" style="margin:0;">
<button class="btn ghost" onclick="reloadAll()">Refresh</button>
@@ -400,7 +400,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.40</span>
<span style="font-size:0.75rem; color:var(--text-secondary);">v1.3.45</span>
<button class="btn ghost" onclick="closeHelp()">Закрыть</button>
</div>
</div>
+58 -13
View File
@@ -167,15 +167,34 @@ async function submitCreate() {
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
});
// Определяем режим: archiveArea видна → archive mode
var archiveArea = document.getElementById('c-archive-area');
var isArchiveMode = archiveArea && archiveArea.style.display !== 'none';
var archiveFile = isArchiveMode ? document.getElementById('c-archive-file').files[0] : null;
if (isArchiveMode && archiveFile) {
// Отправляем multipart/form-data с архивом
var fd = new FormData();
fd.append('name', name);
fd.append('language', lang);
fd.append('entrypoint', document.getElementById('c-entry').value.trim());
fd.append('route', document.getElementById('c-route').value.trim());
fd.append('methods', document.getElementById('c-methods').value.trim());
fd.append('timeout', String(parseTimeout(document.getElementById('c-timeout').value)));
fd.append('archive', archiveFile);
var resp = await fetch(API_BASE + '/functions', { method: 'POST', headers: authHeaders(), body: fd });
if (!resp.ok) { var e = await resp.json().catch(() => ({})); throw new Error(e.error || resp.statusText); }
} else {
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');
@@ -208,6 +227,16 @@ async function openEdit(name) {
document.getElementById('e-entry').value = fn.entrypoint || '';
document.getElementById('e-timeout').value = String(fn.timeout || 60);
document.getElementById('e-code').value = fn.code || '';
// Сбрасываем режим: source_type из API (code / archive)
if (fn.source_type === 'archive') {
setCodeMode('e', 'archive');
} else {
setCodeMode('e', 'code');
}
var archiveInfo = document.getElementById('e-archive-info');
if (archiveInfo) archiveInfo.textContent = '';
var schedule = timeTriggerByFn(name);
document.getElementById('e-schedule-enabled').checked = !!schedule;
document.getElementById('e-cron').value = (schedule && schedule.spec && schedule.spec.cron) || '';
@@ -244,10 +273,26 @@ async function submitEdit() {
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)
});
// Определяем режим
var archiveArea = document.getElementById('e-archive-area');
var isArchiveMode = archiveArea && archiveArea.style.display !== 'none';
var archiveFile = isArchiveMode ? document.getElementById('e-archive-file').files[0] : null;
if (isArchiveMode && archiveFile) {
// Обновляем через архив
var fd = new FormData();
fd.append('timeout', String(parseTimeout(document.getElementById('e-timeout').value)));
fd.append('archive', archiveFile);
var resp = await fetch(API_BASE + '/functions/' + encodeURIComponent(name) + '/archive',
{ method: 'PUT', headers: authHeaders(), body: fd });
if (!resp.ok) { var e = await resp.json().catch(() => ({})); throw new Error(e.error || resp.statusText); }
} else {
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();