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

- 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:
2025-11-09 14:46:15 +01:00
parent 85c369e846
commit 36ef2a1e2c
1366 changed files with 104925 additions and 28719 deletions

View File

@@ -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;
}
}