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,101 @@
<?php
declare(strict_types=1);
namespace App\Framework\Audit\ValueObjects;
use App\Framework\DateTime\Clock;
use App\Framework\Http\IpAddress;
use App\Framework\UserAgent\UserAgent;
use DateTimeImmutable;
/**
* Audit trail entry value object
*/
final readonly class AuditEntry
{
/**
* @param array<string, mixed> $metadata
*/
public function __construct(
public AuditId $id,
public AuditableAction $action,
public string $entityType,
public ?string $entityId,
public DateTimeImmutable $timestamp,
public ?string $userId = null,
public ?IpAddress $ipAddress = null,
public ?UserAgent $userAgent = null,
public array $metadata = [],
public bool $success = true,
public ?string $errorMessage = null
) {
}
public static function create(
Clock $clock,
AuditableAction $action,
string $entityType,
?string $entityId = null,
?string $userId = null,
?IpAddress $ipAddress = null,
?UserAgent $userAgent = null,
array $metadata = []
): self {
return new self(
id: AuditId::generate($clock),
action: $action,
entityType: $entityType,
entityId: $entityId,
timestamp: new DateTimeImmutable(),
userId: $userId,
ipAddress: $ipAddress,
userAgent: $userAgent,
metadata: $metadata,
success: true
);
}
public static function failed(
Clock $clock,
AuditableAction $action,
string $entityType,
?string $entityId = null,
string $errorMessage = '',
?string $userId = null,
?IpAddress $ipAddress = null,
?UserAgent $userAgent = null,
array $metadata = []
): self {
return new self(
id: AuditId::generate($clock),
action: $action,
entityType: $entityType,
entityId: $entityId,
timestamp: new DateTimeImmutable(),
userId: $userId,
ipAddress: $ipAddress,
userAgent: $userAgent,
metadata: $metadata,
success: false,
errorMessage: $errorMessage
);
}
public function toArray(): array
{
return [
'id' => $this->id->toString(),
'action' => $this->action->value,
'entity_type' => $this->entityType,
'entity_id' => $this->entityId,
'timestamp' => $this->timestamp->format('Y-m-d H:i:s'),
'user_id' => $this->userId,
'ip_address' => $this->ipAddress !== null ? (string) $this->ipAddress : null,
'user_agent' => $this->userAgent?->value,
'metadata' => $this->metadata,
'success' => $this->success,
'error_message' => $this->errorMessage,
];
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace App\Framework\Audit\ValueObjects;
use App\Framework\DateTime\Clock;
use App\Framework\Ulid\Ulid;
/**
* Audit Entry ID value object (ULID-based)
*/
final readonly class AuditId
{
private Ulid $ulid;
public function __construct(
Clock $clock,
?string $value = null
) {
$this->ulid = $value !== null
? Ulid::fromString($clock, $value)
: new Ulid($clock);
}
public static function generate(Clock $clock): self
{
return new self($clock);
}
public static function fromString(Clock $clock, string $value): self
{
return new self($clock, $value);
}
public function toString(): string
{
return (string) $this->ulid;
}
public function equals(self $other): bool
{
return $this->toString() === $other->toString();
}
public function __toString(): string
{
return $this->toString();
}
}

View File

@@ -0,0 +1,181 @@
<?php
declare(strict_types=1);
namespace App\Framework\Audit\ValueObjects;
use DateTimeImmutable;
/**
* Audit query filter value object
*/
final readonly class AuditQuery
{
public function __construct(
public ?AuditableAction $action = null,
public ?string $entityType = null,
public ?string $entityId = null,
public ?string $userId = null,
public ?bool $success = null,
public ?DateTimeImmutable $startDate = null,
public ?DateTimeImmutable $endDate = null,
public int $limit = 100,
public int $offset = 0
) {
}
public static function all(): self
{
return new self();
}
public static function forAction(AuditableAction $action): self
{
return new self(action: $action);
}
public static function forEntity(string $entityType, ?string $entityId = null): self
{
return new self(entityType: $entityType, entityId: $entityId);
}
public static function forUser(string $userId): self
{
return new self(userId: $userId);
}
public static function failedOnly(): self
{
return new self(success: false);
}
public static function successfulOnly(): self
{
return new self(success: true);
}
public static function inDateRange(DateTimeImmutable $start, DateTimeImmutable $end): self
{
return new self(startDate: $start, endDate: $end);
}
public function withAction(AuditableAction $action): self
{
return new self(
action: $action,
entityType: $this->entityType,
entityId: $this->entityId,
userId: $this->userId,
success: $this->success,
startDate: $this->startDate,
endDate: $this->endDate,
limit: $this->limit,
offset: $this->offset
);
}
public function withEntityType(string $entityType): self
{
return new self(
action: $this->action,
entityType: $entityType,
entityId: $this->entityId,
userId: $this->userId,
success: $this->success,
startDate: $this->startDate,
endDate: $this->endDate,
limit: $this->limit,
offset: $this->offset
);
}
public function withEntityId(string $entityId): self
{
return new self(
action: $this->action,
entityType: $this->entityType,
entityId: $entityId,
userId: $this->userId,
success: $this->success,
startDate: $this->startDate,
endDate: $this->endDate,
limit: $this->limit,
offset: $this->offset
);
}
public function withUserId(string $userId): self
{
return new self(
action: $this->action,
entityType: $this->entityType,
entityId: $this->entityId,
userId: $userId,
success: $this->success,
startDate: $this->startDate,
endDate: $this->endDate,
limit: $this->limit,
offset: $this->offset
);
}
public function withSuccess(bool $success): self
{
return new self(
action: $this->action,
entityType: $this->entityType,
entityId: $this->entityId,
userId: $this->userId,
success: $success,
startDate: $this->startDate,
endDate: $this->endDate,
limit: $this->limit,
offset: $this->offset
);
}
public function withDateRange(DateTimeImmutable $start, DateTimeImmutable $end): self
{
return new self(
action: $this->action,
entityType: $this->entityType,
entityId: $this->entityId,
userId: $this->userId,
success: $this->success,
startDate: $start,
endDate: $end,
limit: $this->limit,
offset: $this->offset
);
}
public function withLimit(int $limit): self
{
return new self(
action: $this->action,
entityType: $this->entityType,
entityId: $this->entityId,
userId: $this->userId,
success: $this->success,
startDate: $this->startDate,
endDate: $this->endDate,
limit: $limit,
offset: $this->offset
);
}
public function withOffset(int $offset): self
{
return new self(
action: $this->action,
entityType: $this->entityType,
entityId: $this->entityId,
userId: $this->userId,
success: $this->success,
startDate: $this->startDate,
endDate: $this->endDate,
limit: $this->limit,
offset: $offset
);
}
}

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace App\Framework\Audit\ValueObjects;
/**
* Auditable action types
*/
enum AuditableAction: string
{
// Authentication & Authorization
case LOGIN = 'auth.login';
case LOGOUT = 'auth.logout';
case LOGIN_FAILED = 'auth.login_failed';
case PASSWORD_CHANGED = 'auth.password_changed';
case PASSWORD_RESET = 'auth.password_reset';
case PERMISSION_GRANTED = 'authz.permission_granted';
case PERMISSION_DENIED = 'authz.permission_denied';
// CRUD Operations
case CREATE = 'crud.create';
case READ = 'crud.read';
case UPDATE = 'crud.update';
case DELETE = 'crud.delete';
// Data Operations
case EXPORT = 'data.export';
case IMPORT = 'data.import';
case DOWNLOAD = 'data.download';
// System Operations
case CONFIG_CHANGED = 'system.config_changed';
case FEATURE_ENABLED = 'system.feature_enabled';
case FEATURE_DISABLED = 'system.feature_disabled';
// Security Events
case SECURITY_VIOLATION = 'security.violation';
case SUSPICIOUS_ACTIVITY = 'security.suspicious_activity';
case ACCESS_DENIED = 'security.access_denied';
// Custom
case CUSTOM = 'custom';
}