Files
michaelschiemer/src/Framework/ErrorBoundaries/ErrorBoundaryFactory.php
Michael Schiemer 95147ff23e refactor(deployment): Remove WireGuard VPN dependency and restore public service access
Remove WireGuard integration from production deployment to simplify infrastructure:
- Remove docker-compose-direct-access.yml (VPN-bound services)
- Remove VPN-only middlewares from Grafana, Prometheus, Portainer
- Remove WireGuard middleware definitions from Traefik
- Remove WireGuard IPs (10.8.0.0/24) from Traefik forwarded headers

All monitoring services now publicly accessible via subdomains:
- grafana.michaelschiemer.de (with Grafana native auth)
- prometheus.michaelschiemer.de (with Basic Auth)
- portainer.michaelschiemer.de (with Portainer native auth)

All services use Let's Encrypt SSL certificates via Traefik.
2025-11-05 12:48:25 +01:00

183 lines
5.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\ErrorBoundaries;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\DateTime\SystemTimer;
use App\Framework\DateTime\Timer;
use App\Framework\ErrorAggregation\ErrorAggregatorInterface;
use App\Framework\ErrorBoundaries\CircuitBreaker\BoundaryCircuitBreakerManager;
use App\Framework\ErrorBoundaries\Events\BoundaryEventPublisher;
use App\Framework\EventBus\EventBus;
use App\Framework\ExceptionHandling\Context\ExceptionContextProvider;
use App\Framework\Logging\Logger;
use App\Framework\StateManagement\StateManagerFactory;
/**
* Factory for creating error boundaries with appropriate configurations
*/
final readonly class ErrorBoundaryFactory
{
private array $routeConfigs;
public function __construct(
private ?Timer $timer = null,
private ?Logger $logger = null,
private ?StateManagerFactory $stateManagerFactory = null,
private ?EventBus $eventBus = null,
private ?ErrorAggregatorInterface $errorAggregator = null,
private ?ExceptionContextProvider $contextProvider = null,
array $routeConfigs = []
) {
$this->routeConfigs = array_merge($this->getDefaultRouteConfigs(), $routeConfigs);
}
/**
* Create error boundary for specific route
*/
public function createForRoute(string $routeName): ErrorBoundary
{
$config = $this->getConfigForRoute($routeName);
return $this->create("route_{$routeName}", $config);
}
/**
* Create error boundary for database operations
*/
public function createForDatabase(string $operation = 'database'): ErrorBoundary
{
return $this->create($operation, BoundaryConfig::database());
}
/**
* Create error boundary for external service calls
*/
public function createForExternalService(string $serviceName): ErrorBoundary
{
return $this->create("external_{$serviceName}", BoundaryConfig::externalService());
}
/**
* Create error boundary for UI components
*/
public function createForUI(string $componentName): ErrorBoundary
{
return $this->create("ui_{$componentName}", BoundaryConfig::ui());
}
/**
* Create error boundary for background jobs
*/
public function createForBackgroundJob(string $jobName): ErrorBoundary
{
return $this->create("job_{$jobName}", BoundaryConfig::backgroundJob());
}
/**
* Create error boundary for critical operations
*/
public function createForCriticalOperation(string $operationName): ErrorBoundary
{
return $this->create("critical_{$operationName}", BoundaryConfig::critical());
}
/**
* Create custom error boundary
*/
public function create(string $name, BoundaryConfig $config): ErrorBoundary
{
$circuitBreakerManager = null;
if ($config->circuitBreakerEnabled && $this->stateManagerFactory) {
$stateManager = $this->stateManagerFactory->createForErrorBoundary();
$circuitBreakerManager = new BoundaryCircuitBreakerManager($stateManager, $this->logger);
}
$eventPublisher = new BoundaryEventPublisher($this->eventBus, $this->logger);
return new ErrorBoundary(
boundaryName: $name,
config: $config,
timer: $this->timer ?? new SystemTimer(),
logger: $this->logger,
circuitBreakerManager: $circuitBreakerManager,
eventPublisher: $eventPublisher,
errorAggregator: $this->errorAggregator,
contextProvider: $this->contextProvider,
);
}
private function getConfigForRoute(string $routeName): BoundaryConfig
{
// Check for exact route match
if (isset($this->routeConfigs[$routeName])) {
return $this->routeConfigs[$routeName];
}
// Check for pattern matches
foreach ($this->routeConfigs as $pattern => $config) {
if (str_contains($pattern, '*') && $this->matchesPattern($routeName, $pattern)) {
return $config;
}
}
// Default configuration
return $this->routeConfigs['default'];
}
private function matchesPattern(string $routeName, string $pattern): bool
{
$regex = str_replace('*', '.*', preg_quote($pattern, '/'));
return (bool) preg_match("/^{$regex}$/", $routeName);
}
private function getDefaultRouteConfigs(): array
{
return [
// API routes - more retries and circuit breaker
'api/*' => BoundaryConfig::externalService(),
// Admin routes - critical operations
'admin/*' => BoundaryConfig::critical(),
// Auth routes - fail fast for security
'auth/*' => BoundaryConfig::failFast(),
// Public routes - user-friendly defaults
'public/*' => BoundaryConfig::ui(),
// Background job routes
'job/*' => BoundaryConfig::backgroundJob(),
// Database operations
'db/*' => BoundaryConfig::database(),
// Default fallback
'default' => new BoundaryConfig(
maxRetries: 2,
retryStrategy: RetryStrategy::EXPONENTIAL_JITTER,
baseDelay: Duration::fromMilliseconds(100),
maxDelay: Duration::fromSeconds(2),
circuitBreakerEnabled: true,
circuitBreakerThreshold: 5,
circuitBreakerTimeout: Duration::fromMinutes(1),
enableMetrics: true
),
// HTTP request fallback
'http_request' => new BoundaryConfig(
maxRetries: 1,
retryStrategy: RetryStrategy::FIXED,
baseDelay: Duration::fromMilliseconds(50),
maxDelay: Duration::fromMilliseconds(200),
circuitBreakerEnabled: false,
enableMetrics: true
),
];
}
}