2021-11-30 11:20:42 +01:00
< ? php
namespace AIO\Controller ;
2024-10-07 13:20:49 +02:00
use AIO\Container\ContainerState ;
2021-11-30 11:20:42 +01:00
use AIO\ContainerDefinitionFetcher ;
use AIO\Docker\DockerActionManager ;
use Psr\Http\Message\ResponseInterface as Response ;
use Psr\Http\Message\ServerRequestInterface as Request ;
use AIO\Data\ConfigurationManager ;
2024-10-07 10:12:43 +02:00
readonly class DockerController {
2024-05-13 18:07:40 +02:00
private const string TOP_CONTAINER = 'nextcloud-aio-apache' ;
2021-11-30 11:20:42 +01:00
public function __construct (
2024-10-07 10:12:43 +02:00
private DockerActionManager $dockerActionManager ,
private ContainerDefinitionFetcher $containerDefinitionFetcher ,
private ConfigurationManager $configurationManager
2021-11-30 11:20:42 +01:00
) {
}
2023-11-20 13:16:59 +01:00
private function PerformRecursiveContainerStart ( string $id , bool $pullImage = true ) : void {
2021-11-30 11:20:42 +01:00
$container = $this -> containerDefinitionFetcher -> GetContainerById ( $id );
foreach ( $container -> GetDependsOn () as $dependency ) {
2023-11-20 13:16:59 +01:00
$this -> PerformRecursiveContainerStart ( $dependency , $pullImage );
2021-11-30 11:20:42 +01:00
}
2023-11-05 20:06:09 +01:00
// Don't start if container is already running
// This is expected to happen if a container is defined in depends_on of multiple containers
2024-10-07 13:20:49 +02:00
if ( $container -> GetRunningState () === ContainerState :: Running ) {
2023-11-05 20:06:09 +01:00
error_log ( 'Not starting ' . $id . ' because it was already started.' );
return ;
}
// Skip database image pull if the last shutdown was not clean
2022-03-09 16:37:31 +01:00
if ( $id === 'nextcloud-aio-database' ) {
if ( $this -> dockerActionManager -> GetDatabasecontainerExitCode () > 0 ) {
2023-11-20 13:16:59 +01:00
$pullImage = false ;
2022-08-26 22:52:19 +02:00
error_log ( 'Not pulling the latest database image because the container was not correctly shut down.' );
2022-03-09 16:37:31 +01:00
}
}
2023-11-05 20:06:09 +01:00
2025-03-25 12:13:31 +01:00
// Check if registry is reachable in order to make sure that we do not try to pull an image if it is down
2024-02-20 15:20:43 +01:00
// and try to mitigate issues that are arising due to that
if ( $pullImage ) {
2025-03-25 12:13:31 +01:00
if ( ! $this -> dockerActionManager -> isRegistryReachable ( $container )) {
2024-02-20 15:20:43 +01:00
$pullImage = false ;
2025-03-25 12:13:31 +01:00
error_log ( 'Not pulling the ' . $container -> GetContainerName () . ' image for the ' . $container -> GetIdentifier () . ' container because the registry does not seem to be reachable.' );
2024-02-20 15:20:43 +01:00
}
}
2021-11-30 11:20:42 +01:00
$this -> dockerActionManager -> DeleteContainer ( $container );
$this -> dockerActionManager -> CreateVolumes ( $container );
2023-11-20 13:16:59 +01:00
if ( $pullImage ) {
$this -> dockerActionManager -> PullImage ( $container );
2022-03-09 16:37:31 +01:00
}
2021-11-30 11:20:42 +01:00
$this -> dockerActionManager -> CreateContainer ( $container );
$this -> dockerActionManager -> StartContainer ( $container );
$this -> dockerActionManager -> ConnectContainerToNetwork ( $container );
}
2023-01-02 15:46:58 +01:00
public function GetLogs ( Request $request , Response $response , array $args ) : Response
2021-11-30 11:20:42 +01:00
{
2025-06-10 17:27:32 +00:00
$requestParams = $request -> getQueryParams ();
$id = '' ;
if ( is_string ( $requestParams [ 'id' ])) {
$id = $requestParams [ 'id' ];
}
2022-03-09 16:50:53 +01:00
if ( str_starts_with ( $id , 'nextcloud-aio-' )) {
$logs = $this -> dockerActionManager -> GetLogs ( $id );
} else {
$logs = 'Container not found.' ;
}
2021-11-30 11:20:42 +01:00
$body = $response -> getBody ();
$body -> write ( $logs );
return $response
-> withStatus ( 200 )
-> withHeader ( 'Content-Type' , 'text/plain; charset=utf-8' )
-> withHeader ( 'Content-Disposition' , 'inline' );
}
2023-01-02 15:46:58 +01:00
public function StartBackupContainerBackup ( Request $request , Response $response , array $args ) : Response {
2022-04-04 19:12:07 +02:00
$this -> startBackup ();
return $response -> withStatus ( 201 ) -> withHeader ( 'Location' , '/' );
}
public function startBackup () : void {
2021-11-30 11:20:42 +01:00
$config = $this -> configurationManager -> GetConfig ();
$config [ 'backup-mode' ] = 'backup' ;
$this -> configurationManager -> WriteConfig ( $config );
$id = self :: TOP_CONTAINER ;
$this -> PerformRecursiveContainerStop ( $id );
$id = 'nextcloud-aio-borgbackup' ;
$this -> PerformRecursiveContainerStart ( $id );
}
2023-01-02 15:46:58 +01:00
public function StartBackupContainerCheck ( Request $request , Response $response , array $args ) : Response {
2022-08-31 14:00:37 +02:00
$this -> checkBackup ();
return $response -> withStatus ( 201 ) -> withHeader ( 'Location' , '/' );
}
public function checkBackup () : void {
2021-11-30 11:20:42 +01:00
$config = $this -> configurationManager -> GetConfig ();
$config [ 'backup-mode' ] = 'check' ;
$this -> configurationManager -> WriteConfig ( $config );
$id = 'nextcloud-aio-borgbackup' ;
$this -> PerformRecursiveContainerStart ( $id );
}
2023-01-02 15:46:58 +01:00
public function StartBackupContainerRestore ( Request $request , Response $response , array $args ) : Response {
2021-11-30 11:20:42 +01:00
$config = $this -> configurationManager -> GetConfig ();
$config [ 'backup-mode' ] = 'restore' ;
2022-10-16 17:37:13 +02:00
$config [ 'selected-restore-time' ] = $request -> getParsedBody ()[ 'selected_restore_time' ] ? ? '' ;
2024-11-08 11:10:01 +01:00
if ( isset ( $request -> getParsedBody ()[ 'restore-exclude-previews' ])) {
$config [ 'restore-exclude-previews' ] = 1 ;
} else {
$config [ 'restore-exclude-previews' ] = '' ;
}
2021-11-30 11:20:42 +01:00
$this -> configurationManager -> WriteConfig ( $config );
$id = self :: TOP_CONTAINER ;
$this -> PerformRecursiveContainerStop ( $id );
$id = 'nextcloud-aio-borgbackup' ;
$this -> PerformRecursiveContainerStart ( $id );
return $response -> withStatus ( 201 ) -> withHeader ( 'Location' , '/' );
}
2023-01-04 15:38:59 +01:00
public function StartBackupContainerCheckRepair ( Request $request , Response $response , array $args ) : Response {
$config = $this -> configurationManager -> GetConfig ();
$config [ 'backup-mode' ] = 'check-repair' ;
$this -> configurationManager -> WriteConfig ( $config );
$id = 'nextcloud-aio-borgbackup' ;
$this -> PerformRecursiveContainerStart ( $id );
// Restore to backup check which is needed to make the UI logic work correctly
$config = $this -> configurationManager -> GetConfig ();
$config [ 'backup-mode' ] = 'check' ;
$this -> configurationManager -> WriteConfig ( $config );
return $response -> withStatus ( 201 ) -> withHeader ( 'Location' , '/' );
}
2023-01-02 15:46:58 +01:00
public function StartBackupContainerTest ( Request $request , Response $response , array $args ) : Response {
2022-03-21 13:23:17 +01:00
$config = $this -> configurationManager -> GetConfig ();
$config [ 'backup-mode' ] = 'test' ;
2022-04-24 12:57:23 +02:00
$config [ 'instance_restore_attempt' ] = 0 ;
2022-03-21 13:23:17 +01:00
$this -> configurationManager -> WriteConfig ( $config );
$id = self :: TOP_CONTAINER ;
$this -> PerformRecursiveContainerStop ( $id );
$id = 'nextcloud-aio-borgbackup' ;
$this -> PerformRecursiveContainerStart ( $id );
return $response -> withStatus ( 201 ) -> withHeader ( 'Location' , '/' );
}
2023-01-02 15:46:58 +01:00
public function StartContainer ( Request $request , Response $response , array $args ) : Response
2021-11-30 11:20:42 +01:00
{
$uri = $request -> getUri ();
$host = $uri -> getHost ();
$port = $uri -> getPort ();
2022-05-20 12:36:16 +02:00
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 ;
}
2021-11-30 11:20:42 +01:00
2023-03-29 17:39:31 +02:00
if ( isset ( $request -> getParsedBody ()[ 'install_latest_major' ])) {
2025-02-17 11:50:52 +01:00
$installLatestMajor = 31 ;
2023-03-29 17:39:31 +02:00
} else {
$installLatestMajor = " " ;
}
2021-11-30 11:20:42 +01:00
$config = $this -> configurationManager -> GetConfig ();
// set AIO_URL
$config [ 'AIO_URL' ] = $host . ':' . $port ;
// set wasStartButtonClicked
$config [ 'wasStartButtonClicked' ] = 1 ;
2023-03-29 17:39:31 +02:00
// set install_latest_major
$config [ 'install_latest_major' ] = $installLatestMajor ;
2022-04-04 19:12:07 +02:00
$this -> configurationManager -> WriteConfig ( $config );
// Start container
2022-07-10 21:47:25 +02:00
$this -> startTopContainer ( true );
2022-04-04 19:12:07 +02:00
2023-05-20 19:03:42 +02:00
// Clear apcu cache in order to check if container updates are available
2023-07-14 09:22:36 +02:00
// Temporarily disabled as it leads much faster to docker rate limits
// apcu_clear_cache();
2023-05-20 19:03:42 +02:00
2022-04-04 19:12:07 +02:00
return $response -> withStatus ( 201 ) -> withHeader ( 'Location' , '/' );
}
2023-11-20 13:16:59 +01:00
public function startTopContainer ( bool $pullImage ) : void {
2022-04-04 19:12:07 +02:00
$config = $this -> configurationManager -> GetConfig ();
2021-11-30 11:20:42 +01:00
// set AIO_TOKEN
$config [ 'AIO_TOKEN' ] = bin2hex ( random_bytes ( 24 ));
$this -> configurationManager -> WriteConfig ( $config );
// Stop domaincheck since apache would not be able to start otherwise
$this -> StopDomaincheckContainer ();
$id = self :: TOP_CONTAINER ;
2023-11-20 13:16:59 +01:00
$this -> PerformRecursiveContainerStart ( $id , $pullImage );
2021-11-30 11:20:42 +01:00
}
2023-01-02 15:46:58 +01:00
public function StartWatchtowerContainer ( Request $request , Response $response , array $args ) : Response {
2022-04-04 19:12:07 +02:00
$this -> startWatchtower ();
return $response -> withStatus ( 201 ) -> withHeader ( 'Location' , '/' );
}
public function startWatchtower () : void {
2021-11-30 11:20:42 +01:00
$id = 'nextcloud-aio-watchtower' ;
$this -> PerformRecursiveContainerStart ( $id );
}
2022-03-01 11:44:59 +01:00
private function PerformRecursiveContainerStop ( string $id ) : void
2021-11-30 11:20:42 +01:00
{
$container = $this -> containerDefinitionFetcher -> GetContainerById ( $id );
foreach ( $container -> GetDependsOn () as $dependency ) {
$this -> PerformRecursiveContainerStop ( $dependency );
}
2022-02-21 14:01:27 +01:00
// Disconnecting is not needed. This also allows to start the containers manually via docker-cli
//$this->dockerActionManager->DisconnectContainerFromNetwork($container);
2021-11-30 11:20:42 +01:00
$this -> dockerActionManager -> StopContainer ( $container );
}
2023-01-02 15:46:58 +01:00
public function StopContainer ( Request $request , Response $response , array $args ) : Response
2021-11-30 11:20:42 +01:00
{
$id = self :: TOP_CONTAINER ;
$this -> PerformRecursiveContainerStop ( $id );
return $response -> withStatus ( 201 ) -> withHeader ( 'Location' , '/' );
}
2022-07-10 21:47:25 +02:00
public function stopTopContainer () : void {
$id = self :: TOP_CONTAINER ;
$this -> PerformRecursiveContainerStop ( $id );
}
2022-03-01 11:44:59 +01:00
public function StartDomaincheckContainer () : void
2021-11-30 11:20:42 +01:00
{
# Don't start if domain is already set
2022-03-11 22:12:31 +01:00
if ( $this -> configurationManager -> GetDomain () !== '' || $this -> configurationManager -> wasStartButtonClicked ()) {
2021-11-30 11:20:42 +01:00
return ;
}
$id = 'nextcloud-aio-domaincheck' ;
2022-03-11 22:12:31 +01:00
$cacheKey = 'domaincheckWasStarted' ;
$domaincheckContainer = $this -> containerDefinitionFetcher -> GetContainerById ( $id );
$apacheContainer = $this -> containerDefinitionFetcher -> GetContainerById ( self :: TOP_CONTAINER );
// Don't start if apache is already running
2024-10-07 13:20:49 +02:00
if ( $apacheContainer -> GetRunningState () === ContainerState :: Running ) {
2021-11-30 11:20:42 +01:00
return ;
2022-03-11 22:12:31 +01:00
// Don't start if domaincheck is already running
2024-10-07 13:20:49 +02:00
} elseif ( $domaincheckContainer -> GetRunningState () === ContainerState :: Running ) {
2022-03-11 22:12:31 +01:00
$domaincheckWasStarted = apcu_fetch ( $cacheKey );
// Start domaincheck again when 10 minutes are over by not returning here
if ( $domaincheckWasStarted !== false && is_string ( $domaincheckWasStarted )) {
return ;
}
2021-11-30 11:20:42 +01:00
}
2022-03-22 19:34:16 +01:00
$this -> StopDomaincheckContainer ();
2023-07-19 14:31:45 +02:00
try {
$this -> PerformRecursiveContainerStart ( $id );
} catch ( \Exception $e ) {
error_log ( 'Could not start domaincheck container: ' . $e -> getMessage ());
}
2022-03-11 22:12:31 +01:00
// Cache the start for 10 minutes
apcu_add ( $cacheKey , '1' , 600 );
2021-11-30 11:20:42 +01:00
}
2022-03-01 11:44:59 +01:00
private function StopDomaincheckContainer () : void
2021-11-30 11:20:42 +01:00
{
$id = 'nextcloud-aio-domaincheck' ;
$this -> PerformRecursiveContainerStop ( $id );
}
}