Use streaming overlay-log for system prune, move button to stopped-containers section

Co-authored-by: szaimen <42591237+szaimen@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-20 15:15:21 +00:00
parent 915a5a8321
commit 95f23defeb
3 changed files with 44 additions and 37 deletions

View File

@@ -286,12 +286,23 @@ readonly class DockerController {
}
public function SystemPrune(Request $request, Response $response, array $args) : Response {
$results = $this->dockerActionManager->SystemPrune();
$body = $response->getBody();
$body->write(json_encode($results));
return $response
->withStatus(200)
->withHeader('Content-Type', 'application/json; charset=utf-8');
$nonbufResp = $response
->withBody(new NonBufferedBody())
->withHeader('Content-Type', 'text/html; charset=utf-8')
->withHeader('X-Accel-Buffering', 'no')
->withHeader('Cache-Control', 'no-cache');
$streamingResponseBody = $nonbufResp->getBody();
$streamingResponseBody->write($this->getStreamingResponseHtmlStart());
$addToStreamingResponseBody = function (string $message) use ($streamingResponseBody) : void {
$streamingResponseBody->write("<div>{$message}</div>");
};
$this->dockerActionManager->SystemPrune($addToStreamingResponseBody);
$streamingResponseBody->write($this->getStreamingResponseHtmlEnd());
return $nonbufResp;
}
public function stopTopContainer() : void {

View File

@@ -984,22 +984,20 @@ readonly class DockerActionManager {
}
}
public function SystemPrune(): array {
$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',
public function SystemPrune(?\Closure $addToStreamingResponseBody = null): void {
$steps = [
'containers/prune' => 'Pruning stopped containers...',
'images/prune' => 'Pruning unused images...',
'volumes/prune' => 'Pruning unused volumes...',
'networks/prune' => 'Pruning unused networks...',
'build/prune' => 'Pruning build cache...',
];
$results = [];
foreach ($endpoints as $endpoint) {
foreach ($steps as $endpoint => $label) {
if ($addToStreamingResponseBody !== null) {
$addToStreamingResponseBody($label);
}
// Special-case images prune to include the dangling filter as requested
if ($endpoint === 'images/prune') {
$filters = json_encode(['dangling' => ['false']]);
@@ -1009,21 +1007,17 @@ readonly class DockerActionManager {
}
try {
$resp = $this->guzzleClient->post($url);
$body = (string)$resp->getBody();
$json = null;
try {
$json = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
} catch (\Throwable $e) {
// Non-JSON body, keep raw
$json = $body;
}
$results[$endpoint] = $json;
$this->guzzleClient->post($url);
} catch (RequestException $e) {
error_log(sprintf('Docker prune (%s) failed: %s', $endpoint, $e->getMessage()));
$results[$endpoint] = ['error' => $e->getMessage()];
if ($addToStreamingResponseBody !== null) {
$addToStreamingResponseBody(sprintf('Warning: %s failed: %s', $endpoint, $e->getMessage()));
}
// continue with next prune step
}
}
return $results;
if ($addToStreamingResponseBody !== null) {
$addToStreamingResponseBody('Docker system prune done.');
}
}

View File

@@ -322,11 +322,6 @@
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
<input type="submit" value="Stop containers" />
</form>
<form method="POST" action="api/docker/prune" class="xhr">
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
<input type="submit" value="Run docker system prune" onclick="return confirm('Run docker system prune? This will remove unused images, containers and volumes. Continue?')" />
</form>
{% endif %}
{% else %}
{% if isBackupOrRestoreRunning == true %}
@@ -372,6 +367,13 @@
</form>
{% endif %}
{% endif %}
{% if was_start_button_clicked == true %}
<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="Run docker system prune" onclick="return confirm('Run docker system prune? This will remove unused images, containers and volumes. Continue?')" />
</form>
{% endif %}
{% endif %}
{% endif %}