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,222 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Components\Parsers;
use App\Framework\Console\Components\EventBuffer;
use App\Framework\Console\Components\KeyEvent;
/**
* Isolated keyboard event parser with comprehensive sequence support.
* Handles all keyboard escape sequences and regular keys.
*/
final readonly class KeyboardEventParser
{
private const int TIMEOUT_MS = 10;
/**
* Comprehensive key mapping for escape sequences
*/
private const array KEY_MAP = [
// Arrow keys
"\033[A" => 'ArrowUp',
"\033[B" => 'ArrowDown',
"\033[C" => 'ArrowRight',
"\033[D" => 'ArrowLeft',
// Navigation
"\033[H" => 'Home',
"\033[F" => 'End',
"\033[1~" => 'Home',
"\033[4~" => 'End',
// Page navigation
"\033[5~" => 'PageUp',
"\033[6~" => 'PageDown',
// Function keys
"\033OP" => 'F1',
"\033OQ" => 'F2',
"\033OR" => 'F3',
"\033OS" => 'F4',
"\033[15~" => 'F5',
"\033[17~" => 'F6',
"\033[18~" => 'F7',
"\033[19~" => 'F8',
"\033[20~" => 'F9',
"\033[21~" => 'F10',
"\033[23~" => 'F11',
"\033[24~" => 'F12',
// Special keys
"\033[3~" => 'Delete',
"\033[2~" => 'Insert',
"\033[Z" => 'ShiftTab',
// Control sequences
"\033[1;5A" => 'Ctrl+ArrowUp',
"\033[1;5B" => 'Ctrl+ArrowDown',
"\033[1;5C" => 'Ctrl+ArrowRight',
"\033[1;5D" => 'Ctrl+ArrowLeft',
];
public function __construct(
private EventBuffer $eventBuffer
) {
}
/**
* Parse keyboard event from escape sequence
*/
public function parse(string $sequence): KeyEvent
{
// Check for Enter key
if ($sequence === "\n" || $sequence === "\r") {
return new KeyEvent(key: 'Enter', code: "\n");
}
// Check for Escape
if ($sequence === "\033") {
return new KeyEvent(key: 'Escape', code: "\033");
}
// Try to read more characters for multi-character sequences
$fullSequence = $this->readFullSequence($sequence);
// Check if we have a known mapping
if (isset(self::KEY_MAP[$fullSequence])) {
return new KeyEvent(key: self::KEY_MAP[$fullSequence], code: $fullSequence);
}
// Check for modifier sequences (Ctrl+Key, Alt+Key, etc.)
$modifierEvent = $this->parseModifierSequence($fullSequence);
if ($modifierEvent !== null) {
return $modifierEvent;
}
// Unknown sequence, return as-is
return new KeyEvent(key: $fullSequence, code: $fullSequence);
}
/**
* Read full escape sequence (handles multi-byte sequences)
*/
private function readFullSequence(string $initialSequence): string
{
$sequence = $initialSequence;
$timeout = self::TIMEOUT_MS;
$startTime = microtime(true) * 1000;
// Read additional characters if needed
while (true) {
$char = fgetc(STDIN);
if ($char === false) {
$elapsed = (microtime(true) * 1000) - $startTime;
if ($elapsed > $timeout) {
break;
}
usleep(1000); // 1ms
continue;
}
$sequence .= $char;
// Check if sequence is complete
if ($this->isSequenceComplete($sequence)) {
break;
}
// Safety limit
if (strlen($sequence) > 20) {
break;
}
}
return $sequence;
}
/**
* Check if escape sequence is complete
*/
private function isSequenceComplete(string $sequence): bool
{
// Simple sequences end with a letter
if (strlen($sequence) >= 3 && preg_match('/^\033\[[A-Za-z]$/', $sequence)) {
return true;
}
// Function keys end with ~
if (preg_match('/^\033\[\d+~$/', $sequence)) {
return true;
}
// Modifier sequences end with letter after numbers
if (preg_match('/^\033\[\d+;\d+[A-Za-z]$/', $sequence)) {
return true;
}
return false;
}
/**
* Parse modifier sequences (Ctrl+, Alt+, etc.)
*/
private function parseModifierSequence(string $sequence): ?KeyEvent
{
// Pattern: \e[number;number;key
if (!preg_match('/^\033\[(\d+);(\d+)([A-Za-z])$/', $sequence, $matches)) {
return null;
}
$modifierCode = (int) $matches[1];
$keyCode = (int) $matches[2];
$key = $matches[3];
$ctrl = false;
$shift = false;
$alt = false;
$meta = false;
// Decode modifiers (standard xterm codes)
if ($modifierCode === 5) {
$ctrl = true;
} elseif ($modifierCode === 3) {
$alt = true;
} elseif ($modifierCode === 2) {
$shift = true;
}
return new KeyEvent(
key: $key,
code: $sequence,
shift: $shift,
ctrl: $ctrl,
alt: $alt,
meta: $meta
);
}
/**
* Parse regular character (non-escape sequence)
*/
public function parseRegularChar(string $char): KeyEvent
{
// Check for Ctrl+C (ASCII 3)
if ($char === "\003") {
return new KeyEvent(key: 'C', ctrl: true, code: "\003");
}
// Check for other control characters
$ord = ord($char);
if ($ord < 32 && $ord !== 9 && $ord !== 10 && $ord !== 13) {
// Control character
$ctrlKey = chr($ord + 64);
return new KeyEvent(key: $ctrlKey, ctrl: true, code: $char);
}
// Regular character
return new KeyEvent(key: $char, code: $char);
}
}

View File

@@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Components\Parsers;
use App\Framework\Console\Components\EventBuffer;
use App\Framework\Console\Components\MouseEvent;
/**
* Isolated mouse event parser.
* Handles SGR mouse events (\e[<b;x;yM or \e[<b;x;ym)
*/
final readonly class MouseEventParser
{
private const int MAX_BUFFER_SIZE = 20;
private const int TIMEOUT_MS = 10;
private const int MAX_X = 1000;
private const int MAX_Y = 1000;
public function __construct(
private EventBuffer $eventBuffer
) {
}
/**
* Parse mouse event from sequence prefix \e[<
*/
public function parse(string $prefix): ?MouseEvent
{
$buffer = '';
$timeout = self::TIMEOUT_MS;
$startTime = microtime(true) * 1000;
// Read until we get 'M' or 'm'
while (true) {
$char = fgetc(STDIN);
if ($char === false) {
// Check timeout
$elapsed = (microtime(true) * 1000) - $startTime;
if ($elapsed > $timeout) {
// Timeout - store partial sequence for retry
if (strlen($prefix . $buffer) > 0) {
$this->eventBuffer->storePartialSequence($prefix . $buffer);
}
return null;
}
usleep(1000); // 1ms
continue;
}
$buffer .= $char;
// Mouse event ends with 'M' (press) or 'm' (release)
if ($char === 'M' || $char === 'm') {
break;
}
// Safety: limit buffer size
if (strlen($buffer) > self::MAX_BUFFER_SIZE) {
return null;
}
}
// Parse format: b;x;y where b is button code, x and y are coordinates
$data = substr($buffer, 0, -1);
$parts = explode(';', $data);
if (count($parts) < 3) {
// Invalid mouse event format
return null;
}
$buttonCode = (int) $parts[0];
$x = (int) $parts[1];
$y = (int) $parts[2];
// Validate coordinates
if ($x < 1 || $y < 1 || $x > self::MAX_X || $y > self::MAX_Y) {
// Invalid coordinates, likely corrupted
return null;
}
// Decode button and modifiers
$button = $buttonCode & 0x03;
$shift = ($buttonCode & 0x04) !== 0;
$alt = ($buttonCode & 0x08) !== 0;
$ctrl = ($buttonCode & 0x10) !== 0;
// Handle scroll events (button codes 64 and 65)
if ($buttonCode >= 64 && $buttonCode <= 65) {
$button = $buttonCode;
} elseif (($buttonCode & 0x20) !== 0) {
// Mouse move (button code 32 or bit 5 set)
$button = $buttonCode;
}
$pressed = $buffer[-1] === 'M';
return new MouseEvent(
x: $x,
y: $y,
button: $button,
pressed: $pressed,
shift: $shift,
ctrl: $ctrl,
alt: $alt
);
}
/**
* Check if sequence is a mouse event prefix
*/
public function isMouseEventPrefix(string $sequence): bool
{
return str_starts_with($sequence, "\033[<");
}
}