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

@@ -18,7 +18,8 @@ final readonly class AddLinkDestination
{
public function __construct(
private SmartLinkService $linkService
) {}
) {
}
#[Route(path: '/api/smart-links/{id}/destinations', method: Method::POST)]
public function __invoke(HttpRequest $request): JsonResult

View File

@@ -18,7 +18,8 @@ final readonly class AddLinkDestination
{
public function __construct(
private SmartLinkService $smartLinkService
) {}
) {
}
#[Route(path: '/api/smart-links/{id}/destinations', method: Method::POST)]
public function __invoke(HttpRequest $request, string $id): JsonResult
@@ -46,7 +47,7 @@ final readonly class AddLinkDestination
'url' => $destination->url->toString(),
'priority' => $destination->priority,
'is_default' => $destination->isDefault,
'created_at' => $destination->createdAt->format('Y-m-d H:i:s')
'created_at' => $destination->createdAt->format('Y-m-d H:i:s'),
], Status::CREATED);
}
}

View File

@@ -18,7 +18,8 @@ final readonly class CreateGeoRoutingRule
{
public function __construct(
private GeoRoutingRuleRepository $geoRoutingRuleRepository
) {}
) {
}
#[Route(path: '/api/smartlinks/{linkId}/geo-rules', method: Method::POST)]
public function __invoke(HttpRequest $request, string $linkId): JsonResult
@@ -40,7 +41,7 @@ final readonly class CreateGeoRoutingRule
'country_code' => $geoRule->countryCode->toString(),
'destination_id' => $geoRule->destinationId,
'priority' => $geoRule->priority,
'created_at' => $geoRule->createdAt->format('Y-m-d H:i:s')
'created_at' => $geoRule->createdAt->format('Y-m-d H:i:s'),
], Status::CREATED);
}
}

View File

@@ -17,7 +17,8 @@ final readonly class CreateSmartLink
{
public function __construct(
private SmartLinkService $smartLinkService
) {}
) {
}
#[Route(path: '/api/smart-links', method: Method::POST)]
public function __invoke(HttpRequest $request): JsonResult
@@ -30,9 +31,9 @@ final readonly class CreateSmartLink
$data = json_decode($rawBody, true) ?? [];
}
if (!isset($data['type']) || !isset($data['title'])) {
if (! isset($data['type']) || ! isset($data['title'])) {
return new JsonResult([
'error' => 'Missing required fields: type and title'
'error' => 'Missing required fields: type and title',
], Status::BAD_REQUEST);
}
@@ -52,7 +53,7 @@ final readonly class CreateSmartLink
'type' => $smartLink->type->value,
'title' => $smartLink->title->toString(),
'status' => $smartLink->status->value,
'created_at' => $smartLink->createdAt->format('Y-m-d H:i:s')
'created_at' => $smartLink->createdAt->format('Y-m-d H:i:s'),
], Status::CREATED);
}
}

View File

@@ -15,7 +15,8 @@ final readonly class DeleteGeoRoutingRule
{
public function __construct(
private GeoRoutingRuleRepository $geoRoutingRuleRepository
) {}
) {
}
#[Route(path: '/api/geo-rules/{ruleId}', method: Method::DELETE)]
public function __invoke(HttpRequest $request, string $ruleId): JsonResult

View File

@@ -15,7 +15,8 @@ final readonly class GetGeoRoutingRules
{
public function __construct(
private GeoRoutingRuleRepository $geoRoutingRuleRepository
) {}
) {
}
#[Route(path: '/api/smartlinks/{linkId}/geo-rules', method: Method::GET)]
public function __invoke(HttpRequest $request, string $linkId): JsonResult
@@ -31,7 +32,7 @@ final readonly class GetGeoRoutingRules
'country_code' => $rule->countryCode->toString(),
'destination_id' => $rule->destinationId,
'priority' => $rule->priority,
'created_at' => $rule->createdAt->format('Y-m-d H:i:s')
'created_at' => $rule->createdAt->format('Y-m-d H:i:s'),
];
}, $rules);

View File

@@ -15,7 +15,8 @@ final readonly class GetSmartLink
{
public function __construct(
private SmartLinkService $smartLinkService
) {}
) {
}
#[Route(path: '/api/smart-links/{id}', method: Method::GET)]
public function __invoke(HttpRequest $request, string $id): JsonResult
@@ -34,10 +35,10 @@ final readonly class GetSmartLink
'password_protected' => $smartLink->settings->isPasswordProtected(),
'click_limit' => $smartLink->settings->clickLimit,
'custom_domain' => $smartLink->settings->customDomain,
'show_branding' => $smartLink->settings->showBranding
'show_branding' => $smartLink->settings->showBranding,
],
'created_at' => $smartLink->createdAt->format('Y-m-d H:i:s'),
'updated_at' => $smartLink->updatedAt->format('Y-m-d H:i:s')
'updated_at' => $smartLink->updatedAt->format('Y-m-d H:i:s'),
]);
}
}

View File

@@ -15,7 +15,8 @@ final readonly class PublishSmartLink
{
public function __construct(
private SmartLinkService $smartLinkService
) {}
) {
}
#[Route(path: '/api/smart-links/{id}/publish', method: Method::POST)]
public function __invoke(HttpRequest $request, string $id): JsonResult
@@ -25,7 +26,7 @@ final readonly class PublishSmartLink
return new JsonResult([
'id' => $smartLink->id->toString(),
'status' => $smartLink->status->value,
'published_at' => $smartLink->updatedAt->format('Y-m-d H:i:s')
'published_at' => $smartLink->updatedAt->format('Y-m-d H:i:s'),
]);
}
}

View File

@@ -19,7 +19,8 @@ final readonly class RedirectToDestination
private SmartLinkService $smartLinkService,
private LinkRoutingEngine $routingEngine,
private ClickTrackingService $trackingService
) {}
) {
}
#[Route(path: '/l/{shortCode}', method: Method::GET)]
public function __invoke(HttpRequest $request, string $shortCode): Redirect

View File

@@ -18,9 +18,10 @@ final readonly class RedirectToDestination
public function __construct(
private LinkRoutingEngine $routingEngine,
private ClickTrackingService $trackingService
) {}
) {
}
#[Route(path: '/{shortCode}', method: Method::GET)]
#[Route(path: '/s/{shortCode}', method: Method::GET)]
public function __invoke(HttpRequest $request): Redirect
{
$shortCode = ShortCode::fromString($request->routeParameters->get('shortCode'));