diff --git a/.env b/.env index bc65639..94a12f6 100644 --- a/.env +++ b/.env @@ -132,3 +132,8 @@ OAUTH_ENCRYPTION_KEY=580084fd179e67399467f59ee96658ac # postgresql+advisory://db_user:db_password@localhost/db_name LOCK_DSN=flock ###< symfony/lock ### + +MAINTENANCE_MODE_ENABLED=false +MAINTENANCE_MODE_IP_WHITELIST=188.245.62.21 +MAINTENANCE_MODE_ROUTE_WHITELIST= +MAINTENANCE_MODE_PATH_WHITELIST=/api,/token diff --git a/config/services.yaml b/config/services.yaml index c88251b..23dc26d 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=../vendor/symfony/dependency-injection/Loader/schema/services.schema.json # This file is the entry point to configure your own services. # Files in the packages/ subdirectory configure your dependencies. @@ -356,3 +357,11 @@ services: $apiBaseUrl: '%env(default::MAILJET_API_BASE_URL)%' $defaultListId: '%env(default::MAILJET_DEFAULT_LIST_ID)%' $contactMetadataFields: '%mailjet_contact_metadata_fields%' + + App\EventListener\MaintenanceModeListener: + arguments: + $options: + enabled: '%env(bool:MAINTENANCE_MODE_ENABLED)%' + whitelisted_ips: '%env(csv:MAINTENANCE_MODE_IP_WHITELIST)%' + whitelisted_routes: '%env(csv:MAINTENANCE_MODE_ROUTE_WHITELIST)%' + whitelisted_paths: '%env(csv:MAINTENANCE_MODE_PATH_WHITELIST)%' diff --git a/src/EventListener/MaintenanceModeListener.php b/src/EventListener/MaintenanceModeListener.php new file mode 100644 index 0000000..5be3ebd --- /dev/null +++ b/src/EventListener/MaintenanceModeListener.php @@ -0,0 +1,86 @@ + + */ + private array $config; + + /** + * @param array $options + */ + public function __construct(private readonly Environment $twig, array $options) + { + $this->config = $this->resolveConfig($options); + } + + public function onKernelRequest(RequestEvent $event): void + { + if (false === $event->isMainRequest()) { + return; + } + + if (false === $this->config['enabled']) { + return; + } + + $request = $event->getRequest(); + + // Check requested path against whitelist + foreach ($this->config['whitelisted_paths'] as $whitelistedPath) { + $prefix = rtrim((string) $whitelistedPath, '/'); + + if ($request->getPathInfo() === $prefix || str_starts_with($request->getPathInfo(), $prefix.'/')) { + return; + } + } + + // Check user's IP against whitelist + if (true === IpUtils::checkIp($request->getClientIp(), $this->config['whitelisted_ips'])) { + $request->attributes->set('_maintenance_mode_allowed', true); + + return; + } + + // Check requested route against whitelist + if (true === in_array($request->attributes->get('_route'), $this->config['whitelisted_routes'])) { + return; + } + + $content = $this->twig->render('maintenance/index.html.twig'); + + $event->setResponse(new Response($content, Response::HTTP_SERVICE_UNAVAILABLE)); + } + + /** + * @param array $options + * + * @return array + */ + private function resolveConfig(array $options): array + { + $optionsResolver = new OptionsResolver(); + $optionsResolver + ->setDefaults([ + 'enabled' => false, + 'whitelisted_ips' => [], + 'whitelisted_routes' => [], + 'whitelisted_paths' => [], + ]) + ; + + return $optionsResolver->resolve($options); + } +} diff --git a/templates/maintenance/index.html.twig b/templates/maintenance/index.html.twig new file mode 100644 index 0000000..b16c2ef --- /dev/null +++ b/templates/maintenance/index.html.twig @@ -0,0 +1,25 @@ +{% extends 'layout.html.twig' %} + +{% block content %} +
+
+

+ MyE&P +

+
+

+ ...ist außer Betrieb +

+

+ Wir müssen gerade leider unaufschiebbare Wartungsarbeiten durchführen, sind aber schnellstmöglich + wieder online. Versprochen. +

+

+ Vielen Dank für dein Verständnis! +

+
+
+
+
+
+{% endblock %}