feat(Production): Complete production deployment infrastructure

- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
This commit is contained in:
2025-10-25 19:18:37 +02:00
parent caa85db796
commit fc3d7e6357
83016 changed files with 378904 additions and 20919 deletions

View File

@@ -0,0 +1,125 @@
<?php
declare(strict_types=1);
namespace App\Application\Admin\ValueObjects;
use App\Framework\Design\Theme;
use App\Framework\Design\ValueObjects\ThemeMode;
/**
* Admin Theme Value Object
*
* Represents the current theme setting for the admin interface.
* Supports light, dark, and automatic (system preference) modes.
*
* Implements the framework Theme interface for consistent theme management.
*/
final readonly class AdminTheme implements Theme
{
private function __construct(
public ThemeMode $mode
) {
}
public static function light(): self
{
return new self(ThemeMode::LIGHT);
}
public static function dark(): self
{
return new self(ThemeMode::DARK);
}
public static function auto(): self
{
return new self(ThemeMode::AUTO);
}
public function getMode(): ThemeMode
{
return $this->mode;
}
public static function fromString(string $mode): self
{
$normalizedMode = strtolower(trim($mode));
return match ($normalizedMode) {
'light' => self::light(),
'dark' => self::dark(),
'auto', 'system' => self::auto(),
default => throw new \InvalidArgumentException(
"Invalid theme mode: {$mode}. Valid options: light, dark, auto"
),
};
}
/**
* Get data-theme attribute value for HTML
*/
public function toDataAttribute(): string
{
return $this->mode->toDataAttribute();
}
/**
* Check if theme requires JavaScript preference detection
*/
public function requiresPreferenceDetection(): bool
{
return $this->mode->isAuto();
}
/**
* Get CSS class for theme
*/
public function toCssClass(): string
{
return $this->mode->toCssClass('admin-theme');
}
/**
* Get storage key for LocalStorage
*/
public function getStorageKey(): string
{
return 'admin-theme-preference';
}
/**
* Serialize for JSON storage
*/
public function toJson(): string
{
return json_encode([
'mode' => $this->mode->value,
'timestamp' => time(),
], JSON_THROW_ON_ERROR);
}
/**
* Deserialize from JSON
*/
public static function fromJson(string $json): self
{
$data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
if (!isset($data['mode'])) {
throw new \InvalidArgumentException('Invalid theme JSON: missing mode');
}
return self::fromString($data['mode']);
}
public function equals(self $other): bool
{
return $this->mode === $other->mode;
}
public function toString(): string
{
return $this->mode->value;
}
}

View File

@@ -0,0 +1,348 @@
<?php
declare(strict_types=1);
namespace App\Application\Admin\ValueObjects;
use App\Framework\Design\ValueObjects\CssColor;
use App\Framework\Design\ValueObjects\DesignToken;
use App\Framework\Design\ValueObjects\ColorFormat;
/**
* Admin Token Registry
*
* Central registry for all admin design tokens.
* Uses framework's DesignToken system for compatibility.
*/
final readonly class AdminTokenRegistry
{
/**
* @var array<string, DesignToken>
*/
private array $tokens;
public function __construct()
{
$this->tokens = $this->initializeTokens();
}
/**
* Get token by name
*/
public function get(string $name): ?DesignToken
{
return $this->tokens[$name] ?? null;
}
/**
* Get all tokens
* @return array<string, DesignToken>
*/
public function all(): array
{
return $this->tokens;
}
/**
* Get tokens by category
* @return array<string, DesignToken>
*/
public function byCategory(string $category): array
{
return array_filter(
$this->tokens,
fn(DesignToken $token) => str_starts_with($token->name, $category . '-')
);
}
/**
* Get all color tokens
* @return array<string, DesignToken>
*/
public function colors(): array
{
return array_filter(
$this->tokens,
fn(DesignToken $token) => $token->value instanceof CssColor
);
}
/**
* Get all spacing tokens
* @return array<string, DesignToken>
*/
public function spacing(): array
{
return $this->byCategory('spacing');
}
/**
* Generate CSS custom properties
*/
public function toCss(): string
{
$css = ":root {\n";
foreach ($this->tokens as $token) {
$css .= " " . $token->toCssCustomProperty() . "\n";
}
$css .= "}\n";
return $css;
}
/**
* Generate dark mode CSS overrides
*/
public function toDarkModeCss(): string
{
$css = "@media (prefers-color-scheme: dark) {\n";
$css .= " :root {\n";
foreach ($this->colors() as $token) {
$darkColor = $this->getDarkModeColor($token);
if ($darkColor) {
$css .= " --{$token->name}: {$darkColor->toString()};\n";
}
}
$css .= " }\n";
$css .= "}\n";
return $css;
}
/**
* Initialize all admin design tokens
* @return array<string, DesignToken>
*/
private function initializeTokens(): array
{
return [
// Background Colors
'admin-bg-primary' => DesignToken::color(
'admin-bg-primary',
new CssColor('oklch(98% 0.01 280)', ColorFormat::OKLCH),
'Primary background color'
),
'admin-bg-secondary' => DesignToken::color(
'admin-bg-secondary',
new CssColor('oklch(95% 0.01 280)', ColorFormat::OKLCH),
'Secondary background color'
),
'admin-bg-tertiary' => DesignToken::color(
'admin-bg-tertiary',
new CssColor('oklch(92% 0.01 280)', ColorFormat::OKLCH),
'Tertiary background color'
),
// Sidebar Colors
'admin-sidebar-bg' => DesignToken::color(
'admin-sidebar-bg',
new CssColor('oklch(25% 0.02 280)', ColorFormat::OKLCH),
'Sidebar background color'
),
'admin-sidebar-text' => DesignToken::color(
'admin-sidebar-text',
new CssColor('oklch(90% 0.01 280)', ColorFormat::OKLCH),
'Sidebar text color'
),
'admin-sidebar-text-hover' => DesignToken::color(
'admin-sidebar-text-hover',
new CssColor('oklch(100% 0 0)', ColorFormat::OKLCH),
'Sidebar text hover color'
),
'admin-sidebar-active' => DesignToken::color(
'admin-sidebar-active',
new CssColor('oklch(45% 0.15 280)', ColorFormat::OKLCH),
'Sidebar active item color'
),
'admin-sidebar-border' => DesignToken::color(
'admin-sidebar-border',
new CssColor('oklch(30% 0.02 280)', ColorFormat::OKLCH),
'Sidebar border color'
),
// Header Colors
'admin-header-bg' => DesignToken::color(
'admin-header-bg',
new CssColor('oklch(100% 0 0)', ColorFormat::OKLCH),
'Header background color'
),
'admin-header-border' => DesignToken::color(
'admin-header-border',
new CssColor('oklch(85% 0.01 280)', ColorFormat::OKLCH),
'Header border color'
),
'admin-header-text' => DesignToken::color(
'admin-header-text',
new CssColor('oklch(20% 0.02 280)', ColorFormat::OKLCH),
'Header text color'
),
// Content Colors
'admin-content-bg' => DesignToken::color(
'admin-content-bg',
new CssColor('oklch(100% 0 0)', ColorFormat::OKLCH),
'Content background color'
),
'admin-content-text' => DesignToken::color(
'admin-content-text',
new CssColor('oklch(20% 0.02 280)', ColorFormat::OKLCH),
'Content text color'
),
// Interactive Colors
'admin-link-color' => DesignToken::color(
'admin-link-color',
new CssColor('oklch(55% 0.2 260)', ColorFormat::OKLCH),
'Link color'
),
'admin-link-hover' => DesignToken::color(
'admin-link-hover',
new CssColor('oklch(45% 0.25 260)', ColorFormat::OKLCH),
'Link hover color'
),
'admin-link-active' => DesignToken::color(
'admin-link-active',
new CssColor('oklch(35% 0.3 260)', ColorFormat::OKLCH),
'Link active color'
),
// Accent Colors
'admin-accent-primary' => DesignToken::color(
'admin-accent-primary',
new CssColor('oklch(60% 0.2 280)', ColorFormat::OKLCH),
'Primary accent color'
),
'admin-accent-success' => DesignToken::color(
'admin-accent-success',
new CssColor('oklch(65% 0.2 145)', ColorFormat::OKLCH),
'Success accent color'
),
'admin-accent-warning' => DesignToken::color(
'admin-accent-warning',
new CssColor('oklch(70% 0.2 85)', ColorFormat::OKLCH),
'Warning accent color'
),
'admin-accent-error' => DesignToken::color(
'admin-accent-error',
new CssColor('oklch(60% 0.25 25)', ColorFormat::OKLCH),
'Error accent color'
),
'admin-accent-info' => DesignToken::color(
'admin-accent-info',
new CssColor('oklch(65% 0.2 240)', ColorFormat::OKLCH),
'Info accent color'
),
// Border Colors
'admin-border-light' => DesignToken::color(
'admin-border-light',
new CssColor('oklch(90% 0.01 280)', ColorFormat::OKLCH),
'Light border color'
),
'admin-border-medium' => DesignToken::color(
'admin-border-medium',
new CssColor('oklch(80% 0.02 280)', ColorFormat::OKLCH),
'Medium border color'
),
'admin-border-dark' => DesignToken::color(
'admin-border-dark',
new CssColor('oklch(70% 0.02 280)', ColorFormat::OKLCH),
'Dark border color'
),
// Focus/Hover States
'admin-focus-ring' => DesignToken::color(
'admin-focus-ring',
new CssColor('oklch(55% 0.2 260)', ColorFormat::OKLCH),
'Focus ring color'
),
'admin-hover-overlay' => DesignToken::color(
'admin-hover-overlay',
new CssColor('oklch(0% 0 0 / 0.05)', ColorFormat::OKLCH),
'Hover overlay color'
),
// Spacing Tokens
'spacing-xs' => DesignToken::spacing('spacing-xs', '0.25rem', 'Extra small spacing'),
'spacing-sm' => DesignToken::spacing('spacing-sm', '0.5rem', 'Small spacing'),
'spacing-md' => DesignToken::spacing('spacing-md', '1rem', 'Medium spacing'),
'spacing-lg' => DesignToken::spacing('spacing-lg', '1.5rem', 'Large spacing'),
'spacing-xl' => DesignToken::spacing('spacing-xl', '2rem', 'Extra large spacing'),
'spacing-2xl' => DesignToken::spacing('spacing-2xl', '3rem', 'Double extra large spacing'),
// Layout Spacing
'spacing-sidebar' => DesignToken::spacing('spacing-sidebar', '250px', 'Sidebar width'),
'spacing-sidebar-wide' => DesignToken::spacing('spacing-sidebar-wide', '280px', 'Sidebar width on wide screens'),
'spacing-header' => DesignToken::spacing('spacing-header', '4rem', 'Header height'),
'spacing-content-padding' => DesignToken::spacing('spacing-content-padding', '2rem', 'Content padding'),
'spacing-content-max-width' => DesignToken::spacing('spacing-content-max-width', '1400px', 'Content maximum width'),
// Typography Tokens
'font-size-xs' => DesignToken::typography('font-size-xs', '0.75rem', 'Extra small font size'),
'font-size-sm' => DesignToken::typography('font-size-sm', '0.875rem', 'Small font size'),
'font-size-base' => DesignToken::typography('font-size-base', '1rem', 'Base font size'),
'font-size-lg' => DesignToken::typography('font-size-lg', '1.125rem', 'Large font size'),
'font-size-xl' => DesignToken::typography('font-size-xl', '1.25rem', 'Extra large font size'),
'font-size-2xl' => DesignToken::typography('font-size-2xl', '1.5rem', 'Double extra large font size'),
'font-size-3xl' => DesignToken::typography('font-size-3xl', '1.875rem', 'Triple extra large font size'),
];
}
/**
* Get dark mode variant of a color
*/
private function getDarkModeColor(DesignToken $token): ?CssColor
{
if (!($token->value instanceof CssColor)) {
return null;
}
// Dark mode color mappings
$darkModeMap = [
// Backgrounds
'admin-bg-primary' => 'oklch(20% 0.02 280)',
'admin-bg-secondary' => 'oklch(23% 0.02 280)',
'admin-bg-tertiary' => 'oklch(26% 0.02 280)',
// Sidebar
'admin-sidebar-bg' => 'oklch(15% 0.02 280)',
'admin-sidebar-text' => 'oklch(75% 0.02 280)',
'admin-sidebar-text-hover' => 'oklch(95% 0.01 280)',
'admin-sidebar-active' => 'oklch(35% 0.2 280)',
'admin-sidebar-border' => 'oklch(25% 0.02 280)',
// Header
'admin-header-bg' => 'oklch(18% 0.02 280)',
'admin-header-border' => 'oklch(30% 0.02 280)',
'admin-header-text' => 'oklch(90% 0.01 280)',
// Content
'admin-content-bg' => 'oklch(20% 0.02 280)',
'admin-content-text' => 'oklch(90% 0.01 280)',
// Interactive
'admin-link-color' => 'oklch(70% 0.2 260)',
'admin-link-hover' => 'oklch(80% 0.22 260)',
'admin-link-active' => 'oklch(85% 0.25 260)',
// Borders
'admin-border-light' => 'oklch(30% 0.02 280)',
'admin-border-medium' => 'oklch(35% 0.02 280)',
'admin-border-dark' => 'oklch(40% 0.02 280)',
// Focus
'admin-focus-ring' => 'oklch(70% 0.2 260)',
'admin-hover-overlay' => 'oklch(100% 0 0 / 0.05)',
];
if (isset($darkModeMap[$token->name])) {
return new CssColor($darkModeMap[$token->name], ColorFormat::OKLCH);
}
return null;
}
}