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,146 @@
<?php
declare(strict_types=1);
namespace App\Framework\Svg\Charts;
use App\Framework\Core\ValueObjects\Dimensions;
use App\Framework\Svg\Builder\SvgBuilder;
use App\Framework\Svg\Charts\ValueObjects\ChartConfig;
use App\Framework\Svg\Charts\ValueObjects\ChartData;
use App\Framework\Svg\ValueObjects\Geometry\Position;
use App\Framework\Svg\ValueObjects\Geometry\Radius;
use App\Framework\Svg\ValueObjects\Styling\Fill;
use App\Framework\Svg\ValueObjects\Styling\Stroke;
use App\Framework\Svg\ValueObjects\Styling\StrokeWidth;
use App\Framework\Svg\ValueObjects\Text\TextStyle;
/**
* Bar Chart Generator
*/
final readonly class BarChart
{
public function __construct(
public ChartData $data,
public ChartConfig $config = new ChartConfig(new Dimensions(600, 400))
) {
}
/**
* Create from simple array
*/
public static function fromArray(array $data, ?ChartConfig $config = null): self
{
return new self(
ChartData::fromArray($data),
$config ?? ChartConfig::default()
);
}
/**
* Render bar chart to SVG string
*/
public function render(): string
{
$canvas = SvgBuilder::canvas($this->config->dimensions);
// Background
$canvas->rect(
Position::zero(),
$this->config->dimensions,
Fill::solid($this->config->backgroundColor)
);
$chartArea = $this->config->getChartArea();
$barCount = $this->data->count();
$maxValue = $this->data->getMaxValue();
// Calculate bar dimensions
$barSpacing = 10;
$totalSpacing = $barSpacing * ($barCount + 1);
$availableWidth = $chartArea->width - $totalSpacing;
$barWidth = $availableWidth / $barCount;
// Draw bars
$x = $this->config->padding + $barSpacing;
foreach ($this->data->points as $point) {
// Calculate bar height (normalized to chart area)
$barHeight = ($point->value / $maxValue) * $chartArea->height;
$barY = $this->config->padding + ($chartArea->height - $barHeight);
// Draw bar with rounded corners
$canvas->rect(
new Position($x, $barY),
new Dimensions((int) $barWidth, (int) $barHeight),
Fill::solid($this->config->primaryColor),
Stroke::solid($this->config->primaryColor, 1),
new Radius(4)
);
// Draw label if enabled
if ($this->config->showLabels) {
$labelY = $this->config->dimensions->height - $this->config->padding + 20;
$canvas->text(
$point->label,
new Position($x + ($barWidth / 2), $labelY),
TextStyle::default()
->centered()
->withFill(Fill::solid($this->config->textColor))
);
}
// Draw value if enabled
if ($this->config->showValues) {
$canvas->text(
number_format($point->value, 1),
new Position($x + ($barWidth / 2), $barY - 5),
TextStyle::default()
->centered()
->withFill(Fill::solid($this->config->textColor))
);
}
$x += $barWidth + $barSpacing;
}
// Draw grid if enabled
if ($this->config->showGrid) {
$this->drawGrid($canvas, $chartArea, $maxValue);
}
return $canvas->toSvg();
}
private function drawGrid($canvas, Dimensions $chartArea, float $maxValue): void
{
$gridColor = $this->config->textColor;
$gridStroke = new Stroke(
$gridColor,
new StrokeWidth(1),
new \App\Framework\Svg\ValueObjects\Styling\Opacity(0.1)
);
// Horizontal grid lines (5 lines)
$gridLines = 5;
for ($i = 0; $i <= $gridLines; $i++) {
$y = $this->config->padding + ($chartArea->height * ($i / $gridLines));
$canvas->line(
new Position($this->config->padding, $y),
new Position($this->config->padding + $chartArea->width, $y),
$gridStroke
);
// Grid value label
$value = $maxValue * (1 - ($i / $gridLines));
$canvas->text(
number_format($value, 0),
new Position($this->config->padding - 10, $y + 4),
TextStyle::default()
->withTextAnchor(\App\Framework\Svg\ValueObjects\Text\TextAnchor::END)
->withFill(new Fill($gridColor, new \App\Framework\Svg\ValueObjects\Styling\Opacity(0.6)))
);
}
}
}

View File

@@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
namespace App\Framework\Svg\Charts\ValueObjects;
use App\Framework\Core\ValueObjects\Dimensions;
use App\Framework\Svg\ValueObjects\Styling\SvgColor;
use App\Framework\Svg\ValueObjects\Text\FontSize;
/**
* Value Object representing chart configuration
*/
final readonly class ChartConfig
{
public function __construct(
public Dimensions $dimensions,
public SvgColor $primaryColor = new SvgColor(new \App\Framework\Core\ValueObjects\RGBColor(59, 130, 246)), // Blue
public SvgColor $backgroundColor = new SvgColor(new \App\Framework\Core\ValueObjects\RGBColor(255, 255, 255)),
public SvgColor $textColor = new SvgColor(new \App\Framework\Core\ValueObjects\RGBColor(0, 0, 0)),
public FontSize $fontSize = new FontSize(12.0),
public int $padding = 40,
public bool $showGrid = true,
public bool $showLabels = true,
public bool $showValues = false
) {
}
/**
* Create default configuration
*/
public static function default(int $width = 600, int $height = 400): self
{
return new self(new Dimensions($width, $height));
}
/**
* Get chart area dimensions (excluding padding)
*/
public function getChartArea(): Dimensions
{
return new Dimensions(
$this->dimensions->width - ($this->padding * 2),
$this->dimensions->height - ($this->padding * 2)
);
}
/**
* Create variant with different color
*/
public function withColor(SvgColor $color): self
{
return new self(
$this->dimensions,
$color,
$this->backgroundColor,
$this->textColor,
$this->fontSize,
$this->padding,
$this->showGrid,
$this->showLabels,
$this->showValues
);
}
/**
* Create dark theme variant
*/
public function darkTheme(): self
{
return new self(
$this->dimensions,
new SvgColor(new \App\Framework\Core\ValueObjects\RGBColor(96, 165, 250)), // Lighter blue
new SvgColor(new \App\Framework\Core\ValueObjects\RGBColor(17, 24, 39)), // Dark gray
new SvgColor(new \App\Framework\Core\ValueObjects\RGBColor(229, 231, 235)), // Light gray
$this->fontSize,
$this->padding,
$this->showGrid,
$this->showLabels,
$this->showValues
);
}
public function toArray(): array
{
return [
'dimensions' => $this->dimensions->toArray(),
'primaryColor' => $this->primaryColor->toSvgValue(),
'backgroundColor' => $this->backgroundColor->toSvgValue(),
'textColor' => $this->textColor->toSvgValue(),
'fontSize' => $this->fontSize->toSvgValue(),
'padding' => $this->padding,
'showGrid' => $this->showGrid,
'showLabels' => $this->showLabels,
'showValues' => $this->showValues,
];
}
}

View File

@@ -0,0 +1,120 @@
<?php
declare(strict_types=1);
namespace App\Framework\Svg\Charts\ValueObjects;
use App\Framework\Exception\ErrorCode;
use App\Framework\Exception\FrameworkException;
/**
* Value Object representing chart data
*/
final readonly class ChartData
{
/**
* @param array<DataPoint> $points
*/
public function __construct(
public array $points
) {
if (empty($points)) {
throw FrameworkException::create(
ErrorCode::VAL_INVALID_FORMAT,
'Chart data cannot be empty'
);
}
// Validate all points are DataPoint instances
foreach ($points as $point) {
if (!$point instanceof DataPoint) {
throw FrameworkException::create(
ErrorCode::VAL_INVALID_FORMAT,
'All chart data points must be DataPoint instances'
);
}
}
}
/**
* Create from array of values with labels
*/
public static function fromArray(array $data): self
{
$points = [];
foreach ($data as $label => $value) {
$points[] = new DataPoint((string) $label, (float) $value);
}
return new self($points);
}
/**
* Get point count
*/
public function count(): int
{
return count($this->points);
}
/**
* Get minimum value
*/
public function getMinValue(): float
{
return min(array_map(fn (DataPoint $p) => $p->value, $this->points));
}
/**
* Get maximum value
*/
public function getMaxValue(): float
{
return max(array_map(fn (DataPoint $p) => $p->value, $this->points));
}
/**
* Get sum of all values
*/
public function getSum(): float
{
return array_sum(array_map(fn (DataPoint $p) => $p->value, $this->points));
}
/**
* Get average value
*/
public function getAverage(): float
{
return $this->getSum() / $this->count();
}
/**
* Normalize values to 0-1 range
*/
public function normalize(): self
{
$min = $this->getMinValue();
$max = $this->getMaxValue();
$range = $max - $min;
if ($range === 0.0) {
return $this;
}
$normalized = array_map(
fn (DataPoint $p) => new DataPoint(
$p->label,
($p->value - $min) / $range
),
$this->points
);
return new self($normalized);
}
public function toArray(): array
{
return array_map(fn (DataPoint $p) => $p->toArray(), $this->points);
}
}

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace App\Framework\Svg\Charts\ValueObjects;
/**
* Value Object representing a single data point in a chart
*/
final readonly class DataPoint
{
public function __construct(
public string $label,
public float $value
) {
}
/**
* Check if value is positive
*/
public function isPositive(): bool
{
return $this->value > 0;
}
/**
* Check if value is negative
*/
public function isNegative(): bool
{
return $this->value < 0;
}
/**
* Check if value is zero
*/
public function isZero(): bool
{
return $this->value === 0.0;
}
/**
* Scale value by factor
*/
public function scale(float $factor): self
{
return new self($this->label, $this->value * $factor);
}
public function toArray(): array
{
return [
'label' => $this->label,
'value' => $this->value,
];
}
}