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

@@ -4,6 +4,9 @@ declare(strict_types=1);
namespace App\Framework\View\Components;
use App\Application\Admin\ValueObjects\NavigationMenu;
use App\Application\Admin\ValueObjects\NavigationSection;
use App\Application\Admin\ValueObjects\NavigationItem;
use App\Framework\View\Attributes\ComponentName;
use App\Framework\View\Contracts\StaticComponent;
use App\Framework\View\Dom\ElementNode;
@@ -16,6 +19,7 @@ final readonly class AdminSidebar implements StaticComponent
private string $title;
private string $logoUrl;
private string $currentPath;
private ?NavigationMenu $navigationMenu;
public function __construct(
string $content = '',
@@ -24,7 +28,10 @@ final readonly class AdminSidebar implements StaticComponent
// Extract attributes with defaults
$this->title = $attributes['title'] ?? 'Admin Panel';
$this->logoUrl = $attributes['logo-url'] ?? '/admin';
$this->currentPath = $attributes['current-path'] ?? '/admin';
$this->currentPath = $attributes['current-path'] ?? $attributes['currentPath'] ?? '/admin';
// Parse navigation menu from attributes
$this->navigationMenu = $this->parseNavigationMenu($attributes);
}
public function getRootNode(): Node
@@ -42,6 +49,15 @@ final readonly class AdminSidebar implements StaticComponent
}
private function buildSidebarContent(): string
{
$header = $this->buildHeader();
$navigation = $this->buildNavigation();
$footer = $this->buildFooter();
return $header . $navigation . $footer;
}
private function buildHeader(): string
{
return <<<HTML
<div class="admin-sidebar__header">
@@ -49,10 +65,128 @@ final readonly class AdminSidebar implements StaticComponent
<span class="admin-sidebar__title">{$this->title}</span>
</a>
</div>
HTML;
}
private function buildNavigation(): string
{
if ($this->navigationMenu !== null) {
return $this->buildDynamicNavigation();
}
return $this->buildFallbackNavigation();
}
private function buildDynamicNavigation(): string
{
$html = '<nav class="admin-nav" aria-label="Primary">';
foreach ($this->navigationMenu->sections as $section) {
$html .= $this->renderSection($section);
}
$html .= '</nav>';
return $html;
}
private function renderSection(NavigationSection $section): string
{
$html = '<div class="admin-nav__section">';
if ($section->name !== '') {
$html .= '<h2 class="admin-nav__section-title">' . htmlspecialchars($section->name) . '</h2>';
}
$html .= '<ul class="admin-nav__list" role="list">';
foreach ($section->items as $item) {
$html .= $this->renderItem($item);
}
$html .= '</ul></div>';
return $html;
}
private function renderItem(NavigationItem $item): string
{
$activeState = $item->isActive($this->currentPath)
? 'aria-current="page"'
: '';
$iconHtml = $this->renderIcon($item->icon);
return <<<HTML
<li class="admin-nav__item">
<a href="{$this->escape($item->url)}" class="admin-nav__link" {$activeState}>
{$iconHtml}
<span>{$this->escape($item->name)}</span>
</a>
</li>
HTML;
}
private function renderIcon(?string $icon): string
{
if ($icon === null || $icon === '') {
return '';
}
// Try to match icon name to SVG or use emoji fallback
$svgIcon = $this->getSvgIcon($icon);
if ($svgIcon !== null) {
return '<svg class="admin-nav__icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">' . $svgIcon . '</svg>';
}
// Fallback to emoji or text
$emojiIcon = $this->getEmojiIcon($icon);
return '<span class="admin-nav__icon">' . $emojiIcon . '</span>';
}
private function getSvgIcon(string $iconName): ?string
{
// Common icon mappings
$icons = [
'dashboard' => '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>',
'server' => '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"/>',
'database' => '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"/>',
'photo' => '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"/>',
'chart-bar' => '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>',
'code' => '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"/>',
'bell' => '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/>',
'brain' => '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"/>',
'file' => '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>',
];
return $icons[strtolower($iconName)] ?? null;
}
private function getEmojiIcon(string $iconName): string
{
$emoji = [
'dashboard' => '📊',
'server' => '🖥️',
'database' => '💾',
'photo' => '🖼️',
'chart-bar' => '📈',
'code' => '💻',
'bell' => '🔔',
'brain' => '🧠',
'file' => '📄',
];
return $emoji[strtolower($iconName)] ?? '📄';
}
private function buildFallbackNavigation(): string
{
// Fallback to old hardcoded navigation for backward compatibility
return <<<HTML
<nav class="admin-nav">
<div class="admin-nav__section">
<h3 class="admin-nav__section-title">Dashboard</h3>
<h2 class="admin-nav__section-title">Dashboard</h2>
<ul class="admin-nav__list" role="list">
<li class="admin-nav__item">
<a href="/admin" class="admin-nav__link" {$this->getActiveState('/admin')}>
@@ -62,50 +196,13 @@ final readonly class AdminSidebar implements StaticComponent
</li>
</ul>
</div>
<div class="admin-nav__section">
<h3 class="admin-nav__section-title">System</h3>
<ul class="admin-nav__list" role="list">
<li class="admin-nav__item">
<a href="/admin/infrastructure/cache" class="admin-nav__link" {$this->getActiveState('/admin/infrastructure/cache')}>
<span class="admin-nav__icon">💾</span>
<span>Cache</span>
</a>
</li>
<li class="admin-nav__item">
<a href="/admin/infrastructure/logs" class="admin-nav__link" {$this->getActiveState('/admin/infrastructure/logs')}>
<span class="admin-nav__icon">📝</span>
<span>Logs</span>
</a>
</li>
<li class="admin-nav__item">
<a href="/admin/infrastructure/migrations" class="admin-nav__link" {$this->getActiveState('/admin/infrastructure/migrations')}>
<span class="admin-nav__icon">🔄</span>
<span>Migrations</span>
</a>
</li>
</ul>
</div>
<div class="admin-nav__section">
<h3 class="admin-nav__section-title">Content</h3>
<ul class="admin-nav__list" role="list">
<li class="admin-nav__item">
<a href="/admin/images" class="admin-nav__link" {$this->getActiveState('/admin/images')}>
<span class="admin-nav__icon">🖼️</span>
<span>Images</span>
</a>
</li>
<li class="admin-nav__item">
<a href="/admin/users" class="admin-nav__link" {$this->getActiveState('/admin/users')}>
<span class="admin-nav__icon">👥</span>
<span>Users</span>
</a>
</li>
</ul>
</div>
</nav>
HTML;
}
private function buildFooter(): string
{
return <<<HTML
<div class="admin-sidebar__footer">
<div class="admin-sidebar__user">
<div class="admin-sidebar__avatar" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); width: 32px; height: 32px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-weight: 600; font-size: 0.875rem;">A</div>
@@ -122,4 +219,51 @@ final readonly class AdminSidebar implements StaticComponent
{
return $path === $this->currentPath ? 'aria-current="page"' : '';
}
/**
* Parse navigation menu from attributes
*/
private function parseNavigationMenu(array $attributes): ?NavigationMenu
{
// Try different attribute names
$menuData = $attributes['navigation-menu']
?? $attributes['navigation_menu']
?? $attributes['navigationMenu']
?? null;
if ($menuData === null) {
return null;
}
// Handle array directly
if (is_array($menuData)) {
try {
return NavigationMenu::fromArray($menuData);
} catch (\Exception) {
return null;
}
}
// Handle JSON string
if (is_string($menuData)) {
$decoded = json_decode($menuData, true);
if (is_array($decoded)) {
try {
return NavigationMenu::fromArray($decoded);
} catch (\Exception) {
return null;
}
}
}
return null;
}
/**
* Escape HTML special characters
*/
private function escape(string $string): string
{
return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
}
}