- Move index.html → templates/index.html (Jinja2, uses url_for) - Extract CSS → static/css/app.css - Split JS into static/js/: utils.js, dashboard.js, shares.js, quotas.js, users.js, main.js - Switch app.py from send_file to render_template Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
85 lines
3.6 KiB
JavaScript
85 lines
3.6 KiB
JavaScript
async function loadShares() {
|
|
const res = await apiFetch('/api/shares');
|
|
if (!res) return;
|
|
const shares = await res.json();
|
|
|
|
const sb = document.getElementById('smbTableBody');
|
|
if (!shares.smb?.length) {
|
|
sb.innerHTML = `<tr><td colspan="4" style="color:var(--muted)">No SMB shares.</td></tr>`;
|
|
} else {
|
|
sb.innerHTML = shares.smb.map(s => `
|
|
<tr>
|
|
<td>${esc(s.name)}</td>
|
|
<td><code>${esc(s.path)}</code></td>
|
|
<td style="color:var(--muted); font-size:.87em">${esc(s.valid_users || '—')}</td>
|
|
<td>
|
|
<button class="btn btn-sm me-1" style="border:1px solid var(--border); color:var(--muted)"
|
|
onclick="openQuotaModal('smb',${JSON.stringify(s.name)},${JSON.stringify(s.path)},${JSON.stringify(s.valid_users)})">
|
|
Quota
|
|
</button>
|
|
<button class="btn btn-sm btn-danger" disabled title="Coming in Phase 3">Delete</button>
|
|
</td>
|
|
</tr>`).join('');
|
|
}
|
|
|
|
const nb = document.getElementById('nfsTableBody');
|
|
if (!shares.nfs?.length) {
|
|
nb.innerHTML = `<tr><td colspan="3" style="color:var(--muted)">No NFS exports.</td></tr>`;
|
|
} else {
|
|
nb.innerHTML = shares.nfs.map(s => `
|
|
<tr>
|
|
<td><code>${esc(s.path)}</code></td>
|
|
<td style="color:var(--muted); font-size:.87em">${esc(s.clients)}</td>
|
|
<td>
|
|
<button class="btn btn-sm me-1" style="border:1px solid var(--border); color:var(--muted)"
|
|
onclick="openQuotaModal('nfs',null,${JSON.stringify(s.path)},null)">
|
|
Quota
|
|
</button>
|
|
<button class="btn btn-sm btn-danger" disabled title="Coming in Phase 3">Delete</button>
|
|
</td>
|
|
</tr>`).join('');
|
|
}
|
|
}
|
|
|
|
async function addSMBShare() {
|
|
const name = document.getElementById('smbName').value.trim();
|
|
const path = document.getElementById('smbPath').value.trim();
|
|
const validUsers = document.getElementById('smbUsers').value.trim();
|
|
if (!name || !path) return showToast('Name and path are required.', 'danger');
|
|
|
|
const res = await apiFetch('/api/share/smb', {
|
|
method: 'POST', headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({name, path, validUsers}),
|
|
});
|
|
if (!res) return;
|
|
const data = await res.json();
|
|
if (!res.ok) return showToast('Error: ' + (data.error || res.status), 'danger');
|
|
showToast('SMB share created.');
|
|
document.getElementById('smbName').value = '';
|
|
document.getElementById('smbPath').value = '';
|
|
document.getElementById('smbUsers').value = '';
|
|
loadShares();
|
|
loadDashboard();
|
|
}
|
|
|
|
async function addNFSShare() {
|
|
const path = document.getElementById('nfsPath').value.trim();
|
|
const clients = document.getElementById('nfsClients').value.trim() || '*';
|
|
const readOnly = document.getElementById('nfsReadOnly').checked;
|
|
if (!path) return showToast('Path is required.', 'danger');
|
|
|
|
const res = await apiFetch('/api/share/nfs', {
|
|
method: 'POST', headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({path, clients, readOnly}),
|
|
});
|
|
if (!res) return;
|
|
const data = await res.json();
|
|
if (!res.ok) return showToast('Error: ' + (data.error || res.status), 'danger');
|
|
showToast('NFS export created.');
|
|
document.getElementById('nfsPath').value = '';
|
|
document.getElementById('nfsClients').value = '';
|
|
document.getElementById('nfsReadOnly').checked = false;
|
|
loadShares();
|
|
loadDashboard();
|
|
}
|