fix: make backend/.env optional in compose.yml to fix first-run error on clean servers

This commit is contained in:
markmorado
2026-03-08 12:22:38 +01:00
parent 85aa59e7b6
commit 65a71a5742
3 changed files with 21 additions and 19 deletions

View File

@@ -480,7 +480,8 @@ function onServerChange() {
// ===== Nodes =====
async function fetchNodes(serverId) {
try {
const data = await fetchWithAuth(`/proxmox/api/servers/${serverId}/cluster-info`);
const resp = await fetchWithAuth(`/proxmox/api/servers/${serverId}/cluster-info`);
const data = await resp.json();
return (data.nodes || []).filter(Boolean);
} catch { return []; }
}
@@ -495,7 +496,7 @@ function fillNodeSelect(selectId, nodes) {
async function populateStorageDropdown(targetId = 'storageSelectBackups') {
if (!currentServerId) return;
try {
const r = await fetchWithAuth(`/proxmox/api/backups/storages/${currentServerId}`);
const r = await (await fetchWithAuth(`/proxmox/api/backups/storages/${currentServerId}`)).json();
const backupStorages = (r.storages || []).filter(s => (s.content || '').includes('backup'));
const sel = document.getElementById(targetId);
sel.innerHTML = '<option value="">{{ t("select_storage") }}</option>';
@@ -521,7 +522,7 @@ async function loadBackups() {
let url = `/proxmox/api/backups/list/${currentServerId}?node=${node}&storage=${storage}`;
if (vmid) url += `&vmid=${vmid}`;
try {
const r = await fetchWithAuth(url);
const r = await (await fetchWithAuth(url)).json();
const backups = r.backups || [];
const tbody = document.getElementById('backupsTbody');
if (!backups.length) { tbody.innerHTML = '<tr><td colspan="7" class="text-center py-3 text-muted">{{ t("backup_empty") }}</td></tr>'; return; }
@@ -562,10 +563,10 @@ function formatSize(bytes) {
async function deleteBackup(volid, node, storage) {
if (!confirm('{{ t("backup_delete_confirm") }}')) return;
try {
const r = await fetchWithAuth(`/proxmox/api/backups/${currentServerId}/backup`, {
const r = await (await fetchWithAuth(`/proxmox/api/backups/${currentServerId}/backup`, {
method: 'DELETE',
body: JSON.stringify({ node, storage, volid }),
});
})).json();
if (r.success) { showToast('{{ t("success") }}', '{{ t("backup_deleted") }}', 'success'); loadBackups(); }
} catch(e) { showToast('{{ t("error") }}', e.message, 'danger'); }
}
@@ -591,7 +592,7 @@ async function submitCreateBackup() {
notes: document.getElementById('cbNotes').value || null,
};
try {
const r = await fetchWithAuth('/proxmox/api/backups/create', { method: 'POST', body: JSON.stringify(payload) });
const r = await (await fetchWithAuth('/proxmox/api/backups/create', { method: 'POST', body: JSON.stringify(payload) })).json();
if (r.success) {
bootstrap.Modal.getInstance(document.getElementById('createBackupModal'))?.hide();
showToast('{{ t("success") }}', '{{ t("backup_started") }}', 'success');
@@ -628,7 +629,7 @@ async function submitRestore() {
unique: document.getElementById('restoreUnique').checked,
};
try {
const r = await fetchWithAuth('/proxmox/api/backups/restore', { method: 'POST', body: JSON.stringify(payload) });
const r = await (await fetchWithAuth('/proxmox/api/backups/restore', { method: 'POST', body: JSON.stringify(payload) })).json();
if (r.success) {
bootstrap.Modal.getInstance(document.getElementById('restoreModal'))?.hide();
showToast('{{ t("success") }}', '{{ t("backup_restore_started") }}', 'success');
@@ -646,7 +647,7 @@ async function openTaskLog(serverId, node, upid) {
let done = false;
while (!done) {
try {
const r = await fetchWithAuth(`/proxmox/api/backups/task/${serverId}/${node}/${encodeURIComponent(upid)}`);
const r = await (await fetchWithAuth(`/proxmox/api/backups/task/${serverId}/${node}/${encodeURIComponent(upid)}`)).json();
const status = r.status || {};
const lines = (r.log || []).map(l => l.t || '').join('\n');
document.getElementById('taskLogContent').textContent = lines;
@@ -670,7 +671,7 @@ async function openTaskLog(serverId, node, upid) {
async function loadStorages() {
if (!currentServerId) { document.getElementById('storagesTbody').innerHTML = '<tr><td colspan="7" class="text-center py-3 text-muted">{{ t("select_server") }}</td></tr>'; return; }
try {
const r = await fetchWithAuth(`/proxmox/api/backups/storages/${currentServerId}`);
const r = await (await fetchWithAuth(`/proxmox/api/backups/storages/${currentServerId}`)).json();
const storages = r.storages || [];
const tbody = document.getElementById('storagesTbody');
if (!storages.length) { tbody.innerHTML = '<tr><td colspan="7" class="text-center py-3 text-muted">{{ t("backup_no_storages") }}</td></tr>'; return; }
@@ -697,7 +698,7 @@ async function loadStorages() {
async function deleteStorage(storageId) {
if (!confirm('{{ t("backup_storage_delete_confirm") }}')) return;
try {
const r = await fetchWithAuth(`/proxmox/api/backups/storages/${currentServerId}/${storageId}`, { method: 'DELETE' });
const r = await (await fetchWithAuth(`/proxmox/api/backups/storages/${currentServerId}/${storageId}`, { method: 'DELETE' })).json();
if (r.success) { showToast('{{ t("success") }}', '{{ t("backup_storage_deleted") }}', 'success'); loadStorages(); }
} catch(e) { showToast('{{ t("error") }}', e.message, 'danger'); }
}
@@ -731,7 +732,7 @@ async function submitAddStorage() {
if (type === 'cifs') { payload.server = document.getElementById('asCifsServer').value; payload.share = document.getElementById('asCifsShare').value; payload.username = document.getElementById('asCifsUser').value; payload.password = document.getElementById('asCifsPass').value; }
if (type === 'pbs') { payload.server = document.getElementById('asPbsServer').value; payload.datastore = document.getElementById('asPbsDatastore').value; payload.username = document.getElementById('asPbsUser').value; payload.password = document.getElementById('asPbsPass').value; if (document.getElementById('asPbsFingerprint').value) payload.fingerprint = document.getElementById('asPbsFingerprint').value; }
try {
const r = await fetchWithAuth(`/proxmox/api/backups/storages/${currentServerId}`, { method: 'POST', body: JSON.stringify(payload) });
const r = await (await fetchWithAuth(`/proxmox/api/backups/storages/${currentServerId}`, { method: 'POST', body: JSON.stringify(payload) })).json();
if (r.success) { bootstrap.Modal.getInstance(document.getElementById('addStorageModal'))?.hide(); showToast('{{ t("success") }}', '{{ t("backup_storage_added") }}', 'success'); loadStorages(); }
} catch(e) { showToast('{{ t("error") }}', e.message, 'danger'); }
}
@@ -739,7 +740,7 @@ async function submitAddStorage() {
// ===== JOBS Tab =====
async function loadJobs() {
try {
const r = await fetchWithAuth('/proxmox/api/backups/jobs');
const r = await (await fetchWithAuth('/proxmox/api/backups/jobs')).json();
const jobs = r.jobs || [];
const tbody = document.getElementById('jobsTbody');
if (!jobs.length) { tbody.innerHTML = '<tr><td colspan="8" class="text-center py-3 text-muted">{{ t("backup_no_jobs") }}</td></tr>'; return; }
@@ -784,7 +785,7 @@ async function onJobServerChange(serverId) {
async function populateJobStorages(serverId) {
try {
const r = await fetchWithAuth(`/proxmox/api/backups/storages/${serverId}`);
const r = await (await fetchWithAuth(`/proxmox/api/backups/storages/${serverId}`)).json();
const backupStorages = (r.storages || []).filter(s => (s.content || '').includes('backup'));
const sel = document.getElementById('ajStorage');
sel.innerHTML = '';
@@ -854,7 +855,7 @@ async function submitSaveJob() {
try {
const url = jobId ? `/proxmox/api/backups/jobs/${jobId}` : '/proxmox/api/backups/jobs';
const method = jobId ? 'PUT' : 'POST';
const r = await fetchWithAuth(url, { method, body: JSON.stringify(payload) });
const r = await (await fetchWithAuth(url, { method, body: JSON.stringify(payload) })).json();
if (r.success) {
bootstrap.Modal.getInstance(document.getElementById('addJobModal'))?.hide();
showToast('{{ t("success") }}', '{{ t("saved") }}', 'success');
@@ -865,14 +866,14 @@ async function submitSaveJob() {
async function toggleJob(jobId) {
try {
const r = await fetchWithAuth(`/proxmox/api/backups/jobs/${jobId}/toggle`, { method: 'PATCH' });
const r = await (await fetchWithAuth(`/proxmox/api/backups/jobs/${jobId}/toggle`, { method: 'PATCH' })).json();
if (r.success) loadJobs();
} catch(e) { showToast('{{ t("error") }}', e.message, 'danger'); }
}
async function runJobNow(jobId) {
try {
const r = await fetchWithAuth(`/proxmox/api/backups/jobs/${jobId}/run-now`, { method: 'POST' });
const r = await (await fetchWithAuth(`/proxmox/api/backups/jobs/${jobId}/run-now`, { method: 'POST' })).json();
if (r.success) { showToast('{{ t("success") }}', '{{ t("backup_job_triggered") }}', 'success'); setTimeout(loadJobs, 2000); }
} catch(e) { showToast('{{ t("error") }}', e.message, 'danger'); }
}
@@ -880,7 +881,7 @@ async function runJobNow(jobId) {
async function deleteJob(jobId) {
if (!confirm('{{ t("backup_job_delete_confirm") }}')) return;
try {
const r = await fetchWithAuth(`/proxmox/api/backups/jobs/${jobId}`, { method: 'DELETE' });
const r = await (await fetchWithAuth(`/proxmox/api/backups/jobs/${jobId}`, { method: 'DELETE' })).json();
if (r.success) { showToast('{{ t("success") }}', '{{ t("deleted") }}', 'success'); loadJobs(); }
} catch(e) { showToast('{{ t("error") }}', e.message, 'danger'); }
}