fix: make backend/.env optional in compose.yml to fix first-run error on clean servers
This commit is contained in:
@@ -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'); }
|
||||
}
|
||||
|
||||
@@ -554,7 +554,7 @@ body.update-in-progress .layout {
|
||||
<svg class="material-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect><line x1="8" y1="21" x2="16" y2="21"></line><line x1="12" y1="17" x2="12" y2="21"></line></svg>
|
||||
<span>{{ t('nav_vms') }}</span>
|
||||
</a></li>
|
||||
<li><a href="/vms" class="{{ 'active' if request.path == '/vms' or (request.path.startswith('/vms/') or (request.path.startswith('/proxmox/') and not request.path.startswith('/proxmox/server'))) else '' }}" title="{{ t('nav_proxmox') }}">
|
||||
<li><a href="/vms" class="{{ 'active' if request.path == '/vms' or (request.path.startswith('/vms/') or (request.path.startswith('/proxmox/') and not request.path.startswith('/proxmox/server') and not request.path.startswith('/proxmox/api/backups'))) else '' }}" title="{{ t('nav_proxmox') }}">
|
||||
<svg class="material-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2" ry="2"></rect><rect x="9" y="9" width="6" height="6"></rect><line x1="9" y1="1" x2="9" y2="4"></line><line x1="15" y1="1" x2="15" y2="4"></line><line x1="9" y1="20" x2="9" y2="23"></line><line x1="15" y1="20" x2="15" y2="23"></line><line x1="20" y1="9" x2="23" y2="9"></line><line x1="20" y1="14" x2="23" y2="14"></line><line x1="1" y1="9" x2="4" y2="9"></line><line x1="1" y1="14" x2="4" y2="14"></line></svg>
|
||||
<span>{{ t('nav_proxmox') }}</span>
|
||||
</a></li>
|
||||
|
||||
@@ -45,7 +45,8 @@ services:
|
||||
ports:
|
||||
- "8000:8000"
|
||||
env_file:
|
||||
- ./backend/.env
|
||||
- path: ./backend/.env
|
||||
required: false
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
Reference in New Issue
Block a user