feat: Docker catalog in admin panel + per-account app removal

- Admin Docker page: add App Catalog tab (60 apps, account-picker modal)
- Admin Docker page: add dockerAdminLaunchApp() for launching apps on behalf of any account
- User panel: add 'Remove All My Apps' button — stops/removes only that user's own containers and stacks
- API: add uninstall-account action (user-scoped; admin can specify account_id, users limited to own account)
- Admin panel: no global Docker uninstall (would affect all users on the server)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 12:58:42 +00:00
parent 2e96c2b0b9
commit 7a42be8d01
3 changed files with 104 additions and 3 deletions
+14 -1
View File
@@ -1076,9 +1076,10 @@ async function dockerPage(el) {
<div class="stat-card"><div class="stat-label">Max CPUs / App</div><div class="stat-value stat-green">${quota.max_cpus}</div></div>
</div>
<div style="display:flex;gap:.5rem;margin-bottom:1rem">
<div style="display:flex;gap:.5rem;margin-bottom:1rem;flex-wrap:wrap">
<button class="btn btn-sm ${_uDockerTab==='my-apps'?'btn-primary':'btn-ghost'}" onclick="uDockerTab('my-apps')">My Apps</button>
<button class="btn btn-sm ${_uDockerTab==='catalog'?'btn-primary':'btn-ghost'}" onclick="uDockerTab('catalog')">App Catalog</button>
<button class="btn btn-sm btn-danger" style="margin-left:auto" onclick="uDockerUninstallAll()">Remove All My Apps</button>
</div>
<div id="udocker-content"><div class="loading">Loading…</div></div>`;
@@ -1103,6 +1104,18 @@ async function dockerPage(el) {
window._uDockerTab = 'my-apps';
window.uDockerUninstallAll = () => Nova.confirm(
'Remove ALL your Docker apps? This will stop and delete every container and stack you own. Your hosting account and websites are not affected.',
async () => {
Nova.loading('Removing all your Docker apps…');
const r = await Nova.api('docker', 'uninstall-account', { method: 'POST', body: {} });
Nova.loadingDone();
Nova.toast(r?.success ? 'All Docker apps removed' : (r?.error || r?.message || 'Failed'), r?.success ? 'success' : 'error');
if (r?.success) await uDockerReloadStacks();
},
true
);
async function uDockerReloadStacks() {
const r = await Nova.api('docker', 'stacks');
window._uDockerStacks = r?.data?.stacks || [];