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

@@ -4,19 +4,19 @@ declare(strict_types=1);
namespace App\Application\Admin;
use App\Framework\Attributes\Route;
use App\Framework\Attributes\Auth;
use App\Framework\Core\Method;
use App\Framework\Http\HttpRequest;
use App\Framework\Http\ViewResult;
use App\Framework\Http\JsonResult;
use App\Framework\Http\Redirect;
use App\Domain\PreSave\PreSaveCampaign;
use App\Domain\PreSave\PreSaveCampaignRepository;
use App\Domain\PreSave\PreSaveRegistrationRepository;
use App\Domain\PreSave\PreSaveCampaign;
use App\Domain\PreSave\ValueObjects\CampaignStatus;
use App\Framework\Exception\FrameworkException;
use App\Framework\Attributes\Auth;
use App\Framework\Attributes\Route;
use App\Framework\Core\Method;
use App\Framework\Exception\ErrorCode;
use App\Framework\Exception\FrameworkException;
use App\Framework\Http\HttpRequest;
use App\Framework\Http\JsonResult;
use App\Framework\Http\Redirect;
use App\Framework\Http\ViewResult;
/**
* Admin Controller for Pre-Save Campaign Management
@@ -27,7 +27,8 @@ final readonly class PreSaveCampaignController
public function __construct(
private PreSaveCampaignRepository $campaignRepository,
private PreSaveRegistrationRepository $registrationRepository
) {}
) {
}
/**
* Show all campaigns overview
@@ -39,7 +40,7 @@ final readonly class PreSaveCampaignController
return new ViewResult('admin/presave/campaigns/index', [
'campaigns' => $campaigns,
'stats' => $this->getGlobalStats()
'stats' => $this->getGlobalStats(),
]);
}
@@ -65,13 +66,13 @@ final readonly class PreSaveCampaignController
// Parse track URLs from form
$trackUrls = [];
if (!empty($data['spotify_url'])) {
if (! empty($data['spotify_url'])) {
$trackUrls['spotify'] = $data['spotify_url'];
}
if (!empty($data['apple_music_url'])) {
if (! empty($data['apple_music_url'])) {
$trackUrls['apple_music'] = $data['apple_music_url'];
}
if (!empty($data['tidal_url'])) {
if (! empty($data['tidal_url'])) {
$trackUrls['tidal'] = $data['tidal_url'];
}
@@ -83,7 +84,7 @@ final readonly class PreSaveCampaignController
description: $data['description'] ?? null,
releaseDate: strtotime($data['release_date']),
trackUrls: $trackUrls,
startDate: !empty($data['start_date']) ? strtotime($data['start_date']) : null
startDate: ! empty($data['start_date']) ? strtotime($data['start_date']) : null
);
$this->campaignRepository->save($campaign);
@@ -114,7 +115,7 @@ final readonly class PreSaveCampaignController
return new ViewResult('admin/presave/campaigns/show', [
'campaign' => $campaign,
'registrations' => $registrations,
'stats' => $stats
'stats' => $stats,
]);
}
@@ -135,7 +136,7 @@ final readonly class PreSaveCampaignController
}
return new ViewResult('admin/presave/campaigns/edit', [
'campaign' => $campaign
'campaign' => $campaign,
]);
}
@@ -160,13 +161,13 @@ final readonly class PreSaveCampaignController
// Parse track URLs
$trackUrls = [];
if (!empty($data['spotify_url'])) {
if (! empty($data['spotify_url'])) {
$trackUrls['spotify'] = $data['spotify_url'];
}
if (!empty($data['apple_music_url'])) {
if (! empty($data['apple_music_url'])) {
$trackUrls['apple_music'] = $data['apple_music_url'];
}
if (!empty($data['tidal_url'])) {
if (! empty($data['tidal_url'])) {
$trackUrls['tidal'] = $data['tidal_url'];
}
@@ -198,7 +199,7 @@ final readonly class PreSaveCampaignController
if ($campaign === null) {
return new JsonResult([
'success' => false,
'message' => 'Campaign not found'
'message' => 'Campaign not found',
], 404);
}
@@ -206,7 +207,7 @@ final readonly class PreSaveCampaignController
return new JsonResult([
'success' => true,
'message' => 'Campaign deleted successfully'
'message' => 'Campaign deleted successfully',
]);
}
@@ -317,7 +318,7 @@ final readonly class PreSaveCampaignController
'active' => 0,
'paused' => 0,
'completed' => 0,
'total_registrations' => 0
'total_registrations' => 0,
];
foreach ($allCampaigns as $campaign) {
@@ -344,8 +345,8 @@ final readonly class PreSaveCampaignController
'by_platform' => [
'spotify' => 0,
'apple_music' => 0,
'tidal' => 0
]
'tidal' => 0,
],
];
foreach ($registrations as $registration) {