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.
This commit is contained in:
2025-11-05 12:48:25 +01:00
parent 7c52065aae
commit 95147ff23e
215 changed files with 29490 additions and 368 deletions

View File

@@ -0,0 +1,291 @@
<?php
declare(strict_types=1);
namespace App\Framework\Process\Console;
use App\Framework\Console\ConsoleCommand;
use App\Framework\Console\ConsoleInput;
use App\Framework\Console\ExitCode;
use App\Framework\Filesystem\ValueObjects\FilePath;
use App\Framework\Process\Services\LogAnalysisService;
/**
* Log Console Commands.
*/
final readonly class LogCommands
{
public function __construct(
private LogAnalysisService $logAnalysis
) {
}
#[ConsoleCommand('log:tail', 'Display last N lines of a log file')]
public function tail(ConsoleInput $input): int
{
$filePath = $input->getArgument('file');
if ($filePath === null) {
echo "❌ Please provide a log file path.\n";
echo "Usage: php console.php log:tail <file> [--lines=100]\n";
return ExitCode::FAILURE;
}
try {
$file = FilePath::create($filePath);
} catch (\InvalidArgumentException $e) {
echo "❌ Invalid file path: {$filePath}\n";
echo "Error: {$e->getMessage()}\n";
return ExitCode::FAILURE;
}
if (! $file->exists() || ! $file->isFile()) {
echo "❌ File does not exist or is not a file: {$filePath}\n";
return ExitCode::FAILURE;
}
$lines = (int) ($input->getOption('lines') ?? 100);
echo "Showing last {$lines} lines of: {$file->toString()}\n\n";
echo "--- LOG OUTPUT ---\n\n";
$output = $this->logAnalysis->tail($file, $lines);
echo $output;
if (empty($output)) {
echo "(No content)\n";
}
return ExitCode::SUCCESS;
}
#[ConsoleCommand('log:errors', 'Find errors in a log file')]
public function errors(ConsoleInput $input): int
{
$filePath = $input->getArgument('file');
if ($filePath === null) {
echo "❌ Please provide a log file path.\n";
echo "Usage: php console.php log:errors <file> [--lines=1000]\n";
return ExitCode::FAILURE;
}
try {
$file = FilePath::create($filePath);
} catch (\InvalidArgumentException $e) {
echo "❌ Invalid file path: {$filePath}\n";
echo "Error: {$e->getMessage()}\n";
return ExitCode::FAILURE;
}
if (! $file->exists() || ! $file->isFile()) {
echo "❌ File does not exist or is not a file: {$filePath}\n";
return ExitCode::FAILURE;
}
$lines = (int) ($input->getOption('lines') ?? 1000);
echo "Searching for errors in: {$file->toString()} (last {$lines} lines)...\n\n";
$result = $this->logAnalysis->findErrors($file, $lines);
if (empty($result->entries)) {
echo "✅ No errors found!\n";
return ExitCode::SUCCESS;
}
echo "┌─ ERRORS FOUND ──────────────────────────────────────────┐\n";
echo "│ Total Errors: {$result->getErrorCount()}\n";
echo "│ Total Lines: {$result->totalLines}\n";
echo "└─────────────────────────────────────────────────────────┘\n\n";
echo "Error Entries:\n";
foreach ($result->entries as $entry) {
$timestamp = $entry->timestamp?->format('Y-m-d H:i:s') ?? 'N/A';
echo " [{$timestamp}] {$entry->level}: {$entry->message}\n";
}
return ExitCode::SUCCESS;
}
#[ConsoleCommand('log:warnings', 'Find warnings in a log file')]
public function warnings(ConsoleInput $input): int
{
$filePath = $input->getArgument('file');
if ($filePath === null) {
echo "❌ Please provide a log file path.\n";
echo "Usage: php console.php log:warnings <file> [--lines=1000]\n";
return ExitCode::FAILURE;
}
try {
$file = FilePath::create($filePath);
} catch (\InvalidArgumentException $e) {
echo "❌ Invalid file path: {$filePath}\n";
echo "Error: {$e->getMessage()}\n";
return ExitCode::FAILURE;
}
if (! $file->exists() || ! $file->isFile()) {
echo "❌ File does not exist or is not a file: {$filePath}\n";
return ExitCode::FAILURE;
}
$lines = (int) ($input->getOption('lines') ?? 1000);
echo "Searching for warnings in: {$file->toString()} (last {$lines} lines)...\n\n";
$result = $this->logAnalysis->findWarnings($file, $lines);
if (empty($result->entries)) {
echo "✅ No warnings found!\n";
return ExitCode::SUCCESS;
}
echo "┌─ WARNINGS FOUND ────────────────────────────────────────┐\n";
echo "│ Total Warnings: {$result->getWarningCount()}\n";
echo "│ Total Lines: {$result->totalLines}\n";
echo "└─────────────────────────────────────────────────────────┘\n\n";
echo "Warning Entries:\n";
foreach ($result->entries as $entry) {
$timestamp = $entry->timestamp?->format('Y-m-d H:i:s') ?? 'N/A';
echo " [{$timestamp}] {$entry->level}: {$entry->message}\n";
}
return ExitCode::SUCCESS;
}
#[ConsoleCommand('log:search', 'Search for a pattern in a log file')]
public function search(ConsoleInput $input): int
{
$filePath = $input->getArgument('file');
$pattern = $input->getArgument('pattern');
if ($filePath === null || $pattern === null) {
echo "❌ Please provide a log file path and search pattern.\n";
echo "Usage: php console.php log:search <file> <pattern> [--lines=1000]\n";
return ExitCode::FAILURE;
}
try {
$file = FilePath::create($filePath);
} catch (\InvalidArgumentException $e) {
echo "❌ Invalid file path: {$filePath}\n";
echo "Error: {$e->getMessage()}\n";
return ExitCode::FAILURE;
}
if (! $file->exists() || ! $file->isFile()) {
echo "❌ File does not exist or is not a file: {$filePath}\n";
return ExitCode::FAILURE;
}
$lines = (int) ($input->getOption('lines') ?? 1000);
echo "Searching for '{$pattern}' in: {$file->toString()} (last {$lines} lines)...\n\n";
$result = $this->logAnalysis->search($file, $pattern, $lines);
if (empty($result->entries)) {
echo " No matches found for pattern: {$pattern}\n";
return ExitCode::SUCCESS;
}
echo "┌─ SEARCH RESULTS ────────────────────────────────────────┐\n";
echo "│ Pattern: {$pattern}\n";
echo "│ Matches: " . count($result->entries) . "\n";
echo "│ Total Lines: {$result->totalLines}\n";
echo "└─────────────────────────────────────────────────────────┘\n\n";
echo "Matching Entries:\n";
foreach ($result->entries as $entry) {
$timestamp = $entry->timestamp?->format('Y-m-d H:i:s') ?? 'N/A';
echo " [{$timestamp}] {$entry->level}: {$entry->message}\n";
}
return ExitCode::SUCCESS;
}
#[ConsoleCommand('log:stats', 'Show statistics for a log file')]
public function stats(ConsoleInput $input): int
{
$filePath = $input->getArgument('file');
if ($filePath === null) {
echo "❌ Please provide a log file path.\n";
echo "Usage: php console.php log:stats <file> [--lines=1000]\n";
return ExitCode::FAILURE;
}
try {
$file = FilePath::create($filePath);
} catch (\InvalidArgumentException $e) {
echo "❌ Invalid file path: {$filePath}\n";
echo "Error: {$e->getMessage()}\n";
return ExitCode::FAILURE;
}
if (! $file->exists() || ! $file->isFile()) {
echo "❌ File does not exist or is not a file: {$filePath}\n";
return ExitCode::FAILURE;
}
$lines = (int) ($input->getOption('lines') ?? 1000);
echo "Analyzing log file: {$file->toString()} (last {$lines} lines)...\n\n";
$stats = $this->logAnalysis->getStatistics($file, $lines);
echo "╔════════════════════════════════════════════════════════════╗\n";
echo "║ LOG STATISTICS ║\n";
echo "╚════════════════════════════════════════════════════════════╝\n\n";
echo "┌─ OVERVIEW ──────────────────────────────────────────────┐\n";
echo "│ Total Lines: {$stats['total_lines']}\n";
echo "│ Errors: {$stats['error_count']}\n";
echo "│ Warnings: {$stats['warning_count']}\n";
echo "└─────────────────────────────────────────────────────────┘\n\n";
if (! empty($stats['level_distribution'])) {
echo "┌─ LEVEL DISTRIBUTION ───────────────────────────────────┐\n";
foreach ($stats['level_distribution'] as $level => $count) {
echo "{$level}: {$count}\n";
}
echo "└─────────────────────────────────────────────────────────┘\n\n";
}
if (! empty($stats['top_errors'])) {
echo "┌─ TOP ERRORS ──────────────────────────────────────────┐\n";
$rank = 1;
foreach ($stats['top_errors'] as $message => $count) {
echo "{$rank}. ({$count}x) {$message}\n";
$rank++;
}
echo "└─────────────────────────────────────────────────────────┘\n";
}
return ExitCode::SUCCESS;
}
}