containerDefinitionFetcher->GetContainerById($id); // Start all dependencies first and then itself foreach($container->dependsOn as $dependency) { $this->PerformRecursiveContainerStart($dependency, $pullImage, $addToStreamingResponseBody); } // Don't start if container is already running // This is expected to happen if a container is defined in depends_on of multiple containers if ($container->GetRunningState() === ContainerState::Running) { error_log('Not starting ' . $id . ' because it was already started.'); return; } $this->dockerActionManager->DeleteContainer($container); $this->dockerActionManager->CreateVolumes($container); $this->dockerActionManager->PullImage($container, $pullImage, $addToStreamingResponseBody); $this->dockerActionManager->CreateContainer($container); $this->dockerActionManager->StartContainer($container, $addToStreamingResponseBody); $this->dockerActionManager->ConnectContainerToNetwork($container); } private function PerformRecursiveImagePull(string $id) : void { $container = $this->containerDefinitionFetcher->GetContainerById($id); // Pull all dependencies first and then itself foreach($container->dependsOn as $dependency) { $this->PerformRecursiveImagePull($dependency); } $this->dockerActionManager->PullImage($container, true); } public function PullAllContainerImages(): void { $id = self::TOP_CONTAINER; $this->PerformRecursiveImagePull($id); } public function GetLogs(Request $request, Response $response, array $args) : Response { $requestParams = $request->getQueryParams(); $id = ''; if (isset($requestParams['id']) && is_string($requestParams['id'])) { $id = $requestParams['id']; } if (str_starts_with($id, 'nextcloud-aio-')) { $since = $this->getTimestampForDockerLogsApiSince($requestParams['since'] ?? ''); $logs = $this->dockerActionManager->GetLogs($id, $since); } else { $logs = 'Container not found.'; } $body = $response->getBody(); $body->write($logs); return $response ->withStatus(200) ->withHeader('Content-Type', 'text/plain; charset=utf-8') ->withHeader('Content-Disposition', 'inline'); } public function StartBackupContainerBackup(Request $request, Response $response, array $args) : Response { $forceStopNextcloud = true; $this->startBackup($forceStopNextcloud); return $response->withStatus(201)->withHeader('Location', '.'); } public function startBackup(bool $forceStopNextcloud = false) : void { $this->configurationManager->backupMode = 'backup'; $id = self::TOP_CONTAINER; $this->PerformRecursiveContainerStop($id, $forceStopNextcloud); $id = 'nextcloud-aio-borgbackup'; $this->PerformRecursiveContainerStart($id); } public function StartBackupContainerCheck(Request $request, Response $response, array $args) : Response { $this->checkBackup(); return $response->withStatus(201)->withHeader('Location', '.'); } public function StartBackupContainerList(Request $request, Response $response, array $args) : Response { $this->listBackup(); return $response->withStatus(201)->withHeader('Location', '.'); } public function checkBackup() : void { $this->configurationManager->backupMode = 'check'; $id = 'nextcloud-aio-borgbackup'; $this->PerformRecursiveContainerStart($id); } private function listBackup() : void { $this->configurationManager->backupMode = 'list'; $id = 'nextcloud-aio-borgbackup'; $this->PerformRecursiveContainerStart($id); } public function StartBackupContainerRestore(Request $request, Response $response, array $args) : Response { $this->configurationManager->startTransaction(); $this->configurationManager->backupMode = 'restore'; $this->configurationManager->selectedRestoreTime = $request->getParsedBody()['selected_restore_time'] ?? ''; $this->configurationManager->restoreExcludePreviews = isset($request->getParsedBody()['restore-exclude-previews']); $this->configurationManager->commitTransaction(); $id = self::TOP_CONTAINER; $forceStopNextcloud = true; $this->PerformRecursiveContainerStop($id, $forceStopNextcloud); $id = 'nextcloud-aio-borgbackup'; $this->PerformRecursiveContainerStart($id); return $response->withStatus(201)->withHeader('Location', '.'); } public function StartBackupContainerCheckRepair(Request $request, Response $response, array $args) : Response { $this->configurationManager->backupMode = 'check-repair'; $id = 'nextcloud-aio-borgbackup'; $this->PerformRecursiveContainerStart($id); // Restore to backup check which is needed to make the UI logic work correctly $this->configurationManager->backupMode = 'check'; return $response->withStatus(201)->withHeader('Location', '.'); } public function StartBackupContainerTest(Request $request, Response $response, array $args) : Response { $this->configurationManager->startTransaction(); $this->configurationManager->backupMode = 'test'; $this->configurationManager->instanceRestoreAttempt = false; $this->configurationManager->commitTransaction(); $id = self::TOP_CONTAINER; $this->PerformRecursiveContainerStop($id); $id = 'nextcloud-aio-borgbackup'; $this->PerformRecursiveContainerStart($id); return $response->withStatus(201)->withHeader('Location', '.'); } public function StartContainer(Request $request, Response $response, array $args) : Response { $uri = $request->getUri(); $host = $uri->getHost(); $port = $uri->getPort(); $path = $request->getParsedBody()['base_path'] ?? ''; if ($port === 8000) { error_log('The AIO_URL-port was discovered to be 8000 which is not expected. It is now set to 443.'); $port = 443; } if (isset($request->getParsedBody()['install_latest_major'])) { $installLatestMajor = '33'; } else { $installLatestMajor = ''; } $this->configurationManager->startTransaction(); $this->configurationManager->installLatestMajor = $installLatestMajor; // set AIO_URL $this->configurationManager->aioUrl = $host . ':' . (string)$port . $path; // set wasStartButtonClicked $this->configurationManager->wasStartButtonClicked = true; $this->configurationManager->commitTransaction(); // Do not pull container images in case 'bypass_container_update' is set via url params // Needed for local testing $pullImage = !isset($request->getParsedBody()['bypass_container_update']); if ($pullImage === false) { error_log('WARNING: Not pulling container images. Instead, using local ones.'); } $nonbufResp = $response ->withBody(new NonBufferedBody()) ->withHeader('Content-Type', 'text/html; charset=utf-8') ->withHeader('X-Accel-Buffering', 'no') ->withHeader('Cache-Control', 'no-cache'); // Text written into this body is immediately sent to the client, without waiting for later content. $streamingResponseBody = $nonbufResp->getBody(); $streamingResponseBody->write($this->getStreamingResponseHtmlStart()); // Create a closure to pass around to the code, which should to the logging (because it e.g. decides // if it'll actually pull an image), but which should not need to know anything about the // wanted markup or formatting. $addToStreamingResponseBody = function (Container $container, string $message) use ($streamingResponseBody) : void { $streamingResponseBody->write("