aio-interface: offer system prune button (#7677)

This commit is contained in:
Simon L.
2026-04-18 12:08:01 +02:00
committed by GitHub
4 changed files with 96 additions and 0 deletions

View File

@@ -70,6 +70,7 @@ $app->post('/api/docker/backup-check-repair', AIO\Controller\DockerController::c
$app->post('/api/docker/backup-test', AIO\Controller\DockerController::class . ':StartBackupContainerTest');
$app->post('/api/docker/restore', AIO\Controller\DockerController::class . ':StartBackupContainerRestore');
$app->post('/api/docker/stop', AIO\Controller\DockerController::class . ':StopContainer');
$app->post('/api/docker/prune', AIO\Controller\DockerController::class . ':SystemPrune');
$app->get('/api/docker/logs', AIO\Controller\DockerController::class . ':GetLogs');
$app->post('/api/auth/login', AIO\Controller\LoginController::class . ':TryLogin');
$app->get('/api/auth/getlogin', AIO\Controller\LoginController::class . ':GetTryLogin');

View File

@@ -328,6 +328,22 @@ readonly class DockerController {
return $nonbufResp;
}
public function SystemPrune(Request $request, Response $response, array $args) : Response {
// Get streaming response start and closure
$nonbufResp = $this->startStreamingResponse($response);
$body = $nonbufResp->getBody();
$addToStreamingResponseBody = function (string $message) use ($body) : void {
$body->write("<div>$message</div>");
};
$this->dockerActionManager->SystemPrune($addToStreamingResponseBody);
// End streaming response
$this->finalizeStreamingResponse($nonbufResp);
return $nonbufResp;
}
public function stopTopContainer() : void {
$id = self::TOP_CONTAINER;
$this->PerformRecursiveContainerStop($id);

View File

@@ -997,4 +997,71 @@ readonly class DockerActionManager {
return $this->dockerHubManager->GetLatestDigestOfTag($imageName, $tag);
}
}
public function SystemPrune(?\Closure $addToStreamingResponseBody = null): void {
$endpoints = [
// Remove stopped containers
'containers/prune',
// Remove unused images
'images/prune',
// Remove unused volumes
'volumes/prune',
// Remove unused networks
'networks/prune',
// Prune build cache
'build/prune',
];
foreach ($endpoints as $endpoint) {
// Special-case images prune to include the dangling filter as requested
if ($endpoint === 'images/prune') {
$filters = json_encode(['dangling' => ['false']]);
$url = $this->BuildApiUrl($endpoint . '?filters=' . urlencode((string) $filters));
} else {
$url = $this->BuildApiUrl($endpoint);
}
if ($addToStreamingResponseBody !== null) {
$addToStreamingResponseBody("Running $endpoint...");
}
try {
$response = $this->guzzleClient->post($url);
if ($addToStreamingResponseBody !== null) {
$data = json_decode((string)$response->getBody(), true);
$deleted = 0;
foreach (['ContainersDeleted', 'ImagesDeleted', 'VolumesDeleted', 'NetworksDeleted', 'CachesDeleted'] as $key) {
if (isset($data[$key]) && is_array($data[$key])) {
$deleted += count($data[$key]);
}
}
$reclaimed = $data['SpaceReclaimed'] ?? 0;
$parts = [];
if ($deleted > 0) {
$parts[] = "$deleted item(s) deleted";
}
if ($reclaimed > 0) {
$i = (int)floor(log($reclaimed, 1024));
$parts[] = 'Space reclaimed: ' . (string)round($reclaimed / (1024 ** $i), 2) . ' ' . ['B','KB','MB','GB'][$i];
}
$addToStreamingResponseBody(!empty($parts) ? implode('. ', $parts) . '.' : 'Nothing to prune.');
}
} catch (RequestException $e) {
error_log(sprintf('Docker prune (%s) failed: %s', $endpoint, $e->getMessage()));
if ($addToStreamingResponseBody !== null) {
$addToStreamingResponseBody('Error: ' . $e->getMessage());
}
// continue with next prune step
}
}
if ($addToStreamingResponseBody !== null) {
$addToStreamingResponseBody("Docker system prune completed.");
sleep(1);
// We automatically reload after 10s so that the output can be read or copied if necessary
$addToStreamingResponseBody("Automatically reloading the page after 10s.");
sleep(10);
}
}
}

View File

@@ -582,6 +582,18 @@
{% if is_backup_container_running == false %}
{% if isApacheStarting == false %}
{% if isAnyRunning == true %}
<h2>Docker System Prune</h2>
<details>
<summary>Click here to reveal a button to prune the docker system.</summary>
<p>By clicking the button below you can run "docker system prune -a". This will remove unused images, containers, networks, volumes and build cache. It will not delete data of running containers.</p>
<form method="POST" action="api/docker/prune" target="overlay-log">
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
<input type="submit" value="Prune docker system" data-confirm="Run docker system prune -a? This will remove unused images, containers, networks, volumes and build cache. It will not delete data of running containers. Continue?" />
</form>
</details>
{% endif %}
<h2>AIO passphrase change</h2>
<details>
<summary>Click here to change your AIO passphrase</summary>