From bb634b5326debf2e6f7c659c244fb46fc56f7e23 Mon Sep 17 00:00:00 2001 From: "j.stein" Date: Fri, 7 Oct 2022 11:05:08 -0400 Subject: [PATCH] Add tenancy maintenance mode drivers --- assets/config.php | 15 +++++ src/Database/Concerns/MaintenanceMode.php | 65 +++++++++++++++---- src/Maintenance/CacheBasedMaintenanceMode.php | 9 +++ .../DatabaseBasedMaintenanceMode.php | 37 +++++++++++ src/Maintenance/MaintenanceModeManager.php | 45 +++++++++++++ .../TenantMaintenanceModeContract.php | 10 +++ .../CheckTenantForMaintenanceMode.php | 4 +- src/TenancyServiceProvider.php | 20 ++++++ 8 files changed, 191 insertions(+), 14 deletions(-) create mode 100644 src/Maintenance/CacheBasedMaintenanceMode.php create mode 100644 src/Maintenance/DatabaseBasedMaintenanceMode.php create mode 100644 src/Maintenance/MaintenanceModeManager.php create mode 100644 src/Maintenance/TenantMaintenanceModeContract.php diff --git a/assets/config.php b/assets/config.php index 6130bade..0d07fd07 100644 --- a/assets/config.php +++ b/assets/config.php @@ -242,6 +242,21 @@ return [ // Stancl\Tenancy\Features\CrossDomainRedirect::class, // https://tenancyforlaravel.com/docs/v3/features/cross-domain-redirect ], + /** + * Maintenance mode driver + * + * These configuration options determine the driver used to determine and + * manage Tenancy's "maintenance mode" status. The "cache" driver will + * allow maintenance mode to be controlled across multiple machines. + * + * Supported drivers: "database", "cache" + * + */ + 'maintenance' => [ + 'driver' => env('TENANCY_DATABASE_DRIVER', 'database'), + // 'store' => 'redis', + ], + /** * Should tenancy routes be registered. * diff --git a/src/Database/Concerns/MaintenanceMode.php b/src/Database/Concerns/MaintenanceMode.php index cc4490f6..6602095f 100644 --- a/src/Database/Concerns/MaintenanceMode.php +++ b/src/Database/Concerns/MaintenanceMode.php @@ -4,27 +4,68 @@ declare(strict_types=1); namespace Stancl\Tenancy\Database\Concerns; +use Illuminate\Contracts\Container\BindingResolutionException; +use Stancl\Tenancy\Maintenance\TenantMaintenanceModeContract; + /** * @mixin \Illuminate\Database\Eloquent\Model */ trait MaintenanceMode { - public function putDownForMaintenance($data = []): void + /** + * Get an instance of the tenant maintenance mode manager implementation. + * + * @return TenantMaintenanceModeContract + * @throws BindingResolutionException + */ + public function maintenanceMode(): TenantMaintenanceModeContract { - $this->update([ - 'maintenance_mode' => [ - 'except' => $data['except'] ?? null, - 'redirect' => $data['redirect'] ?? null, - 'retry' => $data['retry'] ?? null, - 'refresh' => $data['refresh'] ?? null, - 'secret' => $data['secret'] ?? null, - 'status' => $data['status'] ?? 503, - ], - ]); + return app()->make(TenantMaintenanceModeContract::class); } + /** + * Put the tenant into maintenance + * + * @param array $payload + * @return void + * @throws BindingResolutionException + */ + public function putDownForMaintenance(array $payload = []): void + { + ray('down'); + $this->maintenanceMode()->activate($payload); + } + + /** + * Remove the tenant from maintenance + * + * @return void + * @throws BindingResolutionException + */ public function bringUpFromMaintenance(): void { - $this->update(['maintenance_mode' => null]); + $this->maintenanceMode()->deactivate(); + } + + /** + * Determine if the tenant is in maintenance + * + * @return bool + * @throws BindingResolutionException + */ + public function isDownForMaintenance(): bool + { + return $this->maintenanceMode()->active(); + } + + /** + * Get the data array which was provided when the tenant was placed into maintenance. + * + * @return array + * @throws BindingResolutionException + */ + public function getMaintenanceData(): array + { + return $this->maintenanceMode()->data(); } } diff --git a/src/Maintenance/CacheBasedMaintenanceMode.php b/src/Maintenance/CacheBasedMaintenanceMode.php new file mode 100644 index 00000000..729a170d --- /dev/null +++ b/src/Maintenance/CacheBasedMaintenanceMode.php @@ -0,0 +1,9 @@ +update([ + 'maintenance_mode' => [ + 'except' => $payload['except'] ?? null, + 'redirect' => $payload['redirect'] ?? null, + 'retry' => $payload['retry'] ?? null, + 'refresh' => $payload['refresh'] ?? null, + 'secret' => $payload['secret'] ?? null, + 'status' => $payload['status'] ?? 503, + ], + ]); + } + + public function deactivate(): void + { + tenant()->update(['maintenance_mode' => null]); + } + + public function active(): bool + { + return !is_null(tenant('maintenance_mode')); + } + + public function data(): array + { + return tenant('maintenance_mode'); + } +} \ No newline at end of file diff --git a/src/Maintenance/MaintenanceModeManager.php b/src/Maintenance/MaintenanceModeManager.php new file mode 100644 index 00000000..336a9b97 --- /dev/null +++ b/src/Maintenance/MaintenanceModeManager.php @@ -0,0 +1,45 @@ +container->make('cache'), + $this->config->get('tenancy.maintenance.store') ?: $this->config->get('cache.default'), + tenant()->getTenantKey() . ':down' + ); + } + + /** + * Get the default driver name. + * + * @return string + */ + public function getDefaultDriver(): string + { + return $this->config->get('tenancy.maintenance.driver', 'database'); + } +} diff --git a/src/Maintenance/TenantMaintenanceModeContract.php b/src/Maintenance/TenantMaintenanceModeContract.php new file mode 100644 index 00000000..7059f536 --- /dev/null +++ b/src/Maintenance/TenantMaintenanceModeContract.php @@ -0,0 +1,10 @@ +isDownForMaintenance()) { + $data = tenant()->getMaintenanceData(); if (isset($data['secret']) && $request->path() === $data['secret']) { return $this->bypassResponse($data['secret']); diff --git a/src/TenancyServiceProvider.php b/src/TenancyServiceProvider.php index 7e12a857..6d445955 100644 --- a/src/TenancyServiceProvider.php +++ b/src/TenancyServiceProvider.php @@ -12,6 +12,8 @@ use Stancl\Tenancy\Contracts\Domain; use Stancl\Tenancy\Contracts\Tenant; use Stancl\Tenancy\Enums\LogMode; use Stancl\Tenancy\Events\Contracts\TenancyEvent; +use Stancl\Tenancy\Maintenance\MaintenanceModeManager; +use Stancl\Tenancy\Maintenance\TenantMaintenanceModeContract; use Stancl\Tenancy\Resolvers\DomainTenantResolver; class TenancyServiceProvider extends ServiceProvider @@ -44,6 +46,8 @@ class TenancyServiceProvider extends ServiceProvider return DomainTenantResolver::$currentDomain; }); + $this->registerMaintenanceModeManager(); + // Make sure bootstrappers are stateful (singletons). foreach ($this->app['config']['tenancy.bootstrappers'] ?? [] as $bootstrapper) { if (method_exists($bootstrapper, '__constructStatic')) { @@ -137,4 +141,20 @@ class TenancyServiceProvider extends ServiceProvider return $instance; }); } + + + /** + * Register the maintenance mode manager service. + * + * @return void + */ + public function registerMaintenanceModeManager(): void + { + $this->app->singleton(MaintenanceModeManager::class); + + $this->app->bind( + TenantMaintenanceModeContract::class, + fn () => $this->app->make(MaintenanceModeManager::class)->driver() + ); + } }