fix: Gitea Traefik routing and connection pool optimization
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
- Remove middleware reference from Gitea Traefik labels (caused routing issues) - Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s) - Add explicit service reference in Traefik labels - Fix intermittent 504 timeouts by improving PostgreSQL connection handling Fixes Gitea unreachability via git.michaelschiemer.de
This commit is contained in:
@@ -0,0 +1,302 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\ExceptionHandling\Context;
|
||||
|
||||
use App\Framework\Cache\Cache;
|
||||
use App\Framework\ExceptionHandling\Scope\ErrorScope;
|
||||
use App\Framework\Http\IpAddress;
|
||||
use App\Framework\Http\Request;
|
||||
use App\Framework\Http\Session\SessionId;
|
||||
use App\Framework\UserAgent\UserAgent;
|
||||
|
||||
/**
|
||||
* Exception Context Builder
|
||||
*
|
||||
* Automatically collects context from various sources:
|
||||
* - ErrorScope (if available)
|
||||
* - HTTP Request (if in HTTP context)
|
||||
* - Session (if available)
|
||||
* - Authentication (User ID)
|
||||
* - System information (optional)
|
||||
*
|
||||
* Provides a simple API for automatic context collection and reduces boilerplate.
|
||||
*/
|
||||
final readonly class ExceptionContextBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private ?ErrorScope $errorScope = null,
|
||||
private ?ExceptionContextCache $contextCache = null
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Build context from current environment
|
||||
*
|
||||
* Automatically collects context from ErrorScope, HTTP Request, Session, etc.
|
||||
*/
|
||||
public function build(?ExceptionContextData $baseContext = null): ExceptionContextData
|
||||
{
|
||||
$context = $baseContext ?? ExceptionContextData::empty();
|
||||
|
||||
// First, enrich from ErrorScope if available
|
||||
if ($this->errorScope !== null) {
|
||||
$scopeContext = $this->errorScope->current();
|
||||
if ($scopeContext !== null) {
|
||||
$context = $this->enrichFromScope($context, $scopeContext);
|
||||
}
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build context from HTTP request
|
||||
*
|
||||
* Extracts context from HTTP request, session, and authentication.
|
||||
* Uses cache if available to improve performance.
|
||||
*/
|
||||
public function buildFromRequest(Request $request, ?ExceptionContextData $baseContext = null): ExceptionContextData
|
||||
{
|
||||
// Try to get from cache first
|
||||
if ($this->contextCache !== null) {
|
||||
$requestId = $request instanceof \App\Framework\Http\HttpRequest ? $request->id->toString() : null;
|
||||
$sessionId = property_exists($request, 'session') && $request->session !== null
|
||||
? $request->session->id->toString()
|
||||
: null;
|
||||
$userId = $this->extractUserId($request);
|
||||
|
||||
$cached = $this->contextCache->get($requestId, $sessionId, $userId);
|
||||
if ($cached !== null) {
|
||||
// Merge with base context if provided
|
||||
if ($baseContext !== null) {
|
||||
return $this->mergeContexts($cached, $baseContext);
|
||||
}
|
||||
return $cached;
|
||||
}
|
||||
}
|
||||
|
||||
$context = $baseContext ?? ExceptionContextData::empty();
|
||||
|
||||
// Extract request ID
|
||||
if ($request instanceof \App\Framework\Http\HttpRequest) {
|
||||
$context = $context->withRequestId($request->id->toString());
|
||||
}
|
||||
|
||||
// Extract IP address
|
||||
$ipAddress = $request->server->getRemoteAddr();
|
||||
if ($ipAddress !== null) {
|
||||
$ipValue = $ipAddress->value;
|
||||
if (is_string($ipValue) && IpAddress::isValid($ipValue)) {
|
||||
$context = $context->withClientIp(IpAddress::from($ipValue));
|
||||
} else {
|
||||
$context = $context->withClientIp($ipValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Extract user agent
|
||||
$userAgent = $request->server->getUserAgent();
|
||||
if ($userAgent !== null) {
|
||||
$userAgentValue = $userAgent->value;
|
||||
if (is_string($userAgentValue)) {
|
||||
$context = $context->withUserAgent(UserAgent::fromString($userAgentValue));
|
||||
} else {
|
||||
$context = $context->withUserAgent($userAgentValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Extract session ID
|
||||
if (property_exists($request, 'session') && $request->session !== null) {
|
||||
$sessionId = $request->session->id->toString();
|
||||
try {
|
||||
$context = $context->withSessionId(SessionId::fromString($sessionId));
|
||||
} catch (\InvalidArgumentException) {
|
||||
// If SessionId validation fails, keep as string for backward compatibility
|
||||
$context = $context->withSessionId($sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
// Extract user ID (if authenticated)
|
||||
$userId = $this->extractUserId($request);
|
||||
if ($userId !== null) {
|
||||
$context = $context->withUserId($userId);
|
||||
}
|
||||
|
||||
// Add HTTP-specific tags
|
||||
$context = $context->withTags('http', 'web');
|
||||
|
||||
// Enrich from ErrorScope if available (may override some values)
|
||||
if ($this->errorScope !== null) {
|
||||
$scopeContext = $this->errorScope->current();
|
||||
if ($scopeContext !== null) {
|
||||
$context = $this->enrichFromScope($context, $scopeContext);
|
||||
}
|
||||
}
|
||||
|
||||
// Cache context if cache is available
|
||||
if ($this->contextCache !== null) {
|
||||
$requestId = $request instanceof \App\Framework\Http\HttpRequest ? $request->id->toString() : null;
|
||||
$sessionId = property_exists($request, 'session') && $request->session !== null
|
||||
? $request->session->id->toString()
|
||||
: null;
|
||||
$userId = $this->extractUserId($request);
|
||||
|
||||
$this->contextCache->put($context, $requestId, $sessionId, $userId);
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two contexts (base takes precedence)
|
||||
*/
|
||||
private function mergeContexts(ExceptionContextData $cached, ExceptionContextData $base): ExceptionContextData
|
||||
{
|
||||
$merged = $cached;
|
||||
|
||||
// Override with base context values if present
|
||||
if ($base->operation !== null) {
|
||||
$merged = $merged->withOperation($base->operation, $base->component);
|
||||
}
|
||||
if ($base->component !== null && $merged->component === null) {
|
||||
$merged = $merged->withOperation($merged->operation ?? '', $base->component);
|
||||
}
|
||||
if (!empty($base->data)) {
|
||||
$merged = $merged->addData($base->data);
|
||||
}
|
||||
if (!empty($base->debug)) {
|
||||
$merged = $merged->addDebug($base->debug);
|
||||
}
|
||||
if (!empty($base->metadata)) {
|
||||
$merged = $merged->addMetadata($base->metadata);
|
||||
}
|
||||
if ($base->userId !== null) {
|
||||
$merged = $merged->withUserId($base->userId);
|
||||
}
|
||||
if ($base->requestId !== null) {
|
||||
$merged = $merged->withRequestId($base->requestId);
|
||||
}
|
||||
if ($base->sessionId !== null) {
|
||||
$merged = $merged->withSessionId($base->sessionId);
|
||||
}
|
||||
if ($base->clientIp !== null) {
|
||||
$merged = $merged->withClientIp($base->clientIp);
|
||||
}
|
||||
if ($base->userAgent !== null) {
|
||||
$merged = $merged->withUserAgent($base->userAgent);
|
||||
}
|
||||
if (!empty($base->tags)) {
|
||||
$merged = $merged->withTags(...array_merge($merged->tags, $base->tags));
|
||||
}
|
||||
|
||||
return $merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enrich context from ErrorScope
|
||||
*/
|
||||
private function enrichFromScope(
|
||||
ExceptionContextData $context,
|
||||
\App\Framework\ExceptionHandling\Scope\ErrorScopeContext $scopeContext
|
||||
): ExceptionContextData {
|
||||
// Add operation/component from scope if not already set
|
||||
if ($context->operation === null && $scopeContext->operation !== null) {
|
||||
$context = $context->withOperation(
|
||||
$scopeContext->operation,
|
||||
$scopeContext->component
|
||||
);
|
||||
}
|
||||
|
||||
// Add user ID from scope if not already set
|
||||
if ($context->userId === null && $scopeContext->userId !== null) {
|
||||
$context = $context->withUserId($scopeContext->userId);
|
||||
}
|
||||
|
||||
// Add request ID from scope if not already set
|
||||
if ($context->requestId === null && $scopeContext->requestId !== null) {
|
||||
$context = $context->withRequestId($scopeContext->requestId);
|
||||
}
|
||||
|
||||
// Add session ID from scope if not already set
|
||||
if ($context->sessionId === null && $scopeContext->sessionId !== null) {
|
||||
if (is_string($scopeContext->sessionId)) {
|
||||
try {
|
||||
$context = $context->withSessionId(SessionId::fromString($scopeContext->sessionId));
|
||||
} catch (\InvalidArgumentException) {
|
||||
$context = $context->withSessionId($scopeContext->sessionId);
|
||||
}
|
||||
} else {
|
||||
$context = $context->withSessionId($scopeContext->sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
// Extract HTTP fields from scope metadata (for HTTP scopes)
|
||||
if (isset($scopeContext->metadata['ip']) && $context->clientIp === null) {
|
||||
$ipValue = $scopeContext->metadata['ip'];
|
||||
if (is_string($ipValue) && IpAddress::isValid($ipValue)) {
|
||||
$context = $context->withClientIp(IpAddress::from($ipValue));
|
||||
} elseif ($ipValue instanceof IpAddress) {
|
||||
$context = $context->withClientIp($ipValue);
|
||||
} else {
|
||||
$context = $context->withClientIp($ipValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($scopeContext->metadata['user_agent']) && $context->userAgent === null) {
|
||||
$userAgentValue = $scopeContext->metadata['user_agent'];
|
||||
if (is_string($userAgentValue)) {
|
||||
$context = $context->withUserAgent(UserAgent::fromString($userAgentValue));
|
||||
} elseif ($userAgentValue instanceof UserAgent) {
|
||||
$context = $context->withUserAgent($userAgentValue);
|
||||
} else {
|
||||
$context = $context->withUserAgent($userAgentValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Add scope metadata
|
||||
$context = $context->addMetadata([
|
||||
'scope_type' => $scopeContext->type->value,
|
||||
'scope_id' => $scopeContext->scopeId,
|
||||
]);
|
||||
|
||||
// Add scope tags
|
||||
if (!empty($scopeContext->tags)) {
|
||||
$context = $context->withTags(...$scopeContext->tags);
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract user ID from request
|
||||
*/
|
||||
private function extractUserId(Request $request): ?string
|
||||
{
|
||||
// Try to get from request attribute (set by auth middleware)
|
||||
if (method_exists($request, 'getAttribute')) {
|
||||
$user = $request->getAttribute('user');
|
||||
if ($user !== null) {
|
||||
if (is_object($user) && property_exists($user, 'id')) {
|
||||
return (string) $user->id;
|
||||
}
|
||||
if (is_string($user)) {
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to get from request property (if available)
|
||||
if (property_exists($request, 'user') && $request->user !== null) {
|
||||
if (is_object($request->user) && property_exists($request->user, 'id')) {
|
||||
return (string) $request->user->id;
|
||||
}
|
||||
if (is_string($request->user)) {
|
||||
return $request->user;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user