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,69 @@
<?php
declare(strict_types=1);
namespace App\Framework\LiveComponents\Attributes;
use Attribute;
/**
* Action Attribute - Marks a method as a LiveComponent action
*
* Only methods with this attribute can be called as actions from the client.
* This provides explicit action allow-list security.
*
* Usage:
* ```php
* final readonly class CounterComponent implements LiveComponentContract
* {
* #[Action]
* public function increment(): ComponentData
* {
* // Action logic
* }
*
* #[Action(description: 'Decrement counter by specified amount')]
* public function decrement(int $by = 1): ComponentData
* {
* // Action logic
* }
*
* // ❌ NOT an action - cannot be called from client
* public function internalMethod(): void
* {
* // Internal logic
* }
* }
* ```
*
* Security Benefits:
* - Explicit allow-list: Only marked methods are callable
* - Prevents calling framework methods (getId, getData, getRenderData)
* - Prevents calling lifecycle hooks (onMount, onUpdate, onDestroy)
* - Makes action surface area visible
* - Integrates with error messages (list available actions)
*/
#[Attribute(Attribute::TARGET_METHOD)]
final readonly class Action
{
public function __construct(
/**
* Optional description of what this action does
* Used for documentation and error messages
*/
public ?string $description = null,
/**
* Optional rate limit for this specific action (requests per minute)
* Overrides component-level rate limit
*/
public ?int $rateLimit = null,
/**
* Optional cache TTL for idempotent actions
* Enables automatic deduplication based on idempotency key
*/
public ?int $idempotencyTTL = null
) {
}
}

View File

@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace App\Framework\LiveComponents\Attributes;
use Attribute;
/**
* DataProvider Attribute - Marks a class as a data provider implementation
*
* Enables automatic discovery and injection of data providers based on interface and name.
* The interface is automatically detected from the class's implemented interfaces.
* The framework will resolve the correct provider instance when reconstructing components
* from state (e.g., after LiveComponent actions).
*
* Example:
* ```php
* #[DataProvider(name: 'demo')]
* final readonly class DemoChartDataProvider implements ChartDataProvider
* {
* // Interface ChartDataProvider is automatically detected
* }
*
* #[DataProvider(name: 'database')]
* final readonly class DatabaseChartDataProvider implements ChartDataProvider
* {
* // Interface ChartDataProvider is automatically detected
* }
* ```
*
* Discovery Process:
* - Framework scans for classes with #[DataProvider] attribute
* - Automatically detects implemented interfaces via reflection
* - Builds registry: Interface + Name → Provider Class
* - Example: ChartDataProvider + 'demo' → DemoChartDataProvider
*
* Usage in Component:
* ```php
* final readonly class ChartComponent
* {
* public function __construct(
* ComponentId $id,
* ?ComponentData $initialData = null,
* ?ChartDataProvider $dataProvider = null, // Injected by Controller
* // ... other parameters
* ) {
* if ($initialData !== null) {
* // Restore state and resolve provider from stored name
* $this->state = ChartState::fromComponentData($initialData);
* $this->dataProvider = $this->resolveProvider(
* ChartDataProvider::class,
* $this->state->dataProviderName
* );
* }
* }
* }
* ```
*/
#[Attribute(Attribute::TARGET_CLASS)]
final readonly class DataProvider
{
/**
* @param string $name Provider name/identifier (e.g., 'demo', 'database', 'api')
*/
public function __construct(
public string $name = 'default'
) {
}
}

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace App\Framework\LiveComponents\Attributes;
use Attribute;
/**
* Marks a class as a LiveComponent and defines its name
*
* The name will be used in URLs and client-side instead of the full class name
* for better security and cleaner URLs.
*
* @example #[LiveComponent('counter')]
*/
#[Attribute(Attribute::TARGET_CLASS)]
final readonly class LiveComponent
{
public function __construct(
public string $name
) {
}
}

View File

@@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
namespace App\Framework\LiveComponents\Attributes;
use Attribute;
/**
* Marks a LiveComponent action as requiring specific permission
*
* Usage:
* ```php
* #[RequiresPermission('posts.delete')]
* public function deletePost(string $postId): ComponentData
* {
* // Only executed if user has 'posts.delete' permission
* }
* ```
*
* Multiple permissions (OR logic):
* ```php
* #[RequiresPermission('posts.edit', 'posts.admin')]
* public function editPost(string $postId): ComponentData
* {
* // User needs EITHER 'posts.edit' OR 'posts.admin'
* }
* ```
*
* Framework Integration:
* - LiveComponentHandler checks this attribute before executing action
* - Throws UnauthorizedActionException if permission check fails
* - Integrates with AuthorizationChecker interface for flexible implementations
*/
#[Attribute(Attribute::TARGET_METHOD)]
final readonly class RequiresPermission
{
/**
* @var array<string> Required permissions (OR logic - user needs at least one)
*/
public readonly array $permissions;
/**
* @param string ...$permissions Required permissions (OR logic - user needs at least one)
*/
public function __construct(
string ...$permissions
) {
if (empty($permissions)) {
throw new \InvalidArgumentException(
'RequiresPermission attribute requires at least one permission'
);
}
$this->permissions = $permissions;
}
/**
* Check if user has any of the required permissions
*
* @param array<string> $userPermissions User's permissions
*/
public function isAuthorized(array $userPermissions): bool
{
foreach ($this->permissions as $requiredPermission) {
if (in_array($requiredPermission, $userPermissions, true)) {
return true;
}
}
return false;
}
/**
* Get all required permissions
*
* @return array<string>
*/
public function getPermissions(): array
{
return $this->permissions;
}
/**
* Get first permission (useful for error messages)
*/
public function getPrimaryPermission(): string
{
return $this->permissions[0];
}
/**
* Check if multiple permissions are required
*/
public function hasMultiplePermissions(): bool
{
return count($this->permissions) > 1;
}
}

View File

@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace App\Framework\LiveComponents\Attributes;
use Attribute;
/**
* Explicitly disables state encryption for a LiveComponent
*
* By default, ALL LiveComponent state data is encrypted for security.
* This attribute allows opt-out for performance-critical components with non-sensitive data.
*
* Framework Principles:
* - Security by default (encryption is standard)
* - Explicit opt-out (must consciously disable encryption)
* - Readonly attribute class
* - No inheritance
*
* ⚠️ WARNING: Only use this for components with:
* - No personal identifiable information (PII)
* - No payment information
* - No health records
* - No confidential business data
* - Public or non-sensitive data only
*
* Valid Use Cases:
* - Simple counters (e.g., click counters)
* - UI state (e.g., accordion open/closed)
* - Non-sensitive filters (e.g., search filters)
* - Public data displays
*
* Performance Impact:
* - Saves ~5-10ms per state serialize/deserialize operation
* - Only use if encryption overhead is measurably problematic
*
* Example:
* ```php
* #[UnencryptedState(reason: 'Simple counter with no sensitive data')]
* final readonly class ClickCounterComponent implements LiveComponentContract
* {
* public function __construct(
* public CounterState $state
* ) {}
* }
* ```
*
* Security Notes:
* - This attribute will be logged for security auditing
* - Requires explicit reason for disabling encryption
* - Should be reviewed during security audits
*
* @see EncryptedStateSerializer For encryption implementation
*/
#[Attribute(Attribute::TARGET_CLASS)]
final readonly class UnencryptedState
{
/**
* @param string $reason Required justification for disabling encryption (for security audit trail)
*/
public function __construct(
public string $reason
) {
if (empty(trim($reason))) {
throw new \InvalidArgumentException(
'#[UnencryptedState] requires explicit reason for disabling encryption. ' .
'Example: #[UnencryptedState(reason: "Simple counter with no sensitive data")]'
);
}
}
}