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
+30
View File
@@ -29,6 +29,36 @@ match ($action) {
Response::success(null, $msg);
})(),
'uninstall-account' => (function() use ($dm, $currentUser, $isAdmin, $_userAccountId, $body) {
// Stop and remove all containers and stacks belonging to one account.
// Users can only remove their own; admins can specify any account_id.
$accountId = $isAdmin ? (int)($body['account_id'] ?? $_userAccountId) : ($_userAccountId ?? 0);
if (!$accountId) Response::error('account_id required');
$db = DB::getInstance();
// Tear down compose stacks
$stacks = $db->fetchAll("SELECT * FROM docker_compose_stacks WHERE account_id = ?", [$accountId]);
foreach ($stacks as $stack) {
if (is_dir($stack['stack_dir']) && file_exists("{$stack['stack_dir']}/docker-compose.yml")) {
shell_exec("sudo docker compose -f " . escapeshellarg("{$stack['stack_dir']}/docker-compose.yml") . " down -v 2>/dev/null");
}
$db->execute("DELETE FROM docker_compose_stacks WHERE id = ?", [$stack['id']]);
}
// Stop and remove bare containers
$containers = $db->fetchAll("SELECT container_id FROM docker_containers WHERE account_id = ?", [$accountId]);
foreach ($containers as $c) {
if ($c['container_id']) {
shell_exec("sudo docker rm -f " . escapeshellarg($c['container_id']) . " 2>/dev/null");
}
}
$db->execute("DELETE FROM docker_containers WHERE account_id = ?", [$accountId]);
audit("docker.uninstall-account", "account:{$accountId}");
Response::success(null, 'All Docker apps and containers removed for this account');
})(),
'prune' => (function() use ($dm, $body, $isAdmin) {
if (!$isAdmin) Response::error('Admin only', 403);
$out = $dm->systemPrune((bool)($body['volumes'] ?? false));