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

@@ -5,6 +5,9 @@ declare(strict_types=1);
namespace App\Framework\Filesystem;
use App\Framework\Async\FiberManager;
use App\Framework\Core\PathProvider;
use App\Framework\DateTime\SystemClock;
use App\Framework\DateTime\SystemTimer;
use App\Framework\Filesystem\Exceptions\DirectoryCreateException;
use App\Framework\Filesystem\Exceptions\DirectoryListException;
use App\Framework\Filesystem\Exceptions\FileCopyException;
@@ -15,10 +18,16 @@ use App\Framework\Filesystem\Exceptions\FilePermissionException;
use App\Framework\Filesystem\Exceptions\FileReadException;
use App\Framework\Filesystem\Exceptions\FileWriteException;
use App\Framework\Filesystem\Traits\AppendableStorageTrait;
use App\Framework\Filesystem\ValueObjects\FileMetadata;
use App\Framework\Filesystem\Traits\AtomicStorageTrait;
use App\Framework\Filesystem\Traits\CompressibleStorageTrait;
use App\Framework\Filesystem\Traits\LockableStorageTrait;
use App\Framework\Filesystem\Traits\StreamableStorageTrait;
use App\Framework\Core\ValueObjects\FileSize;
use App\Framework\Filesystem\ValueObjects\FileOperation;
use App\Framework\Filesystem\ValueObjects\FileOperationContext;
use App\Framework\Logging\Logger;
use App\Framework\Logging\ValueObjects\LogContext;
final readonly class FileStorage implements Storage, AtomicStorage, AppendableStorage, StreamableStorage, LockableStorage, CompressibleStorage
{
@@ -29,23 +38,34 @@ final readonly class FileStorage implements Storage, AtomicStorage, AppendableSt
use LockableStorageTrait;
use CompressibleStorageTrait;
public readonly PermissionChecker $permissions;
public PermissionChecker $permissions;
public readonly FiberManager $fiberManager;
public FiberManager $fiberManager;
public ?FileValidator $validator;
public ?Logger $logger;
public function __construct(
private string $basePath = '/',
?FiberManager $fiberManager = null
?FiberManager $fiberManager = null,
?FileValidator $validator = null,
?Logger $logger = null,
?PathProvider $pathProvider = null
) {
$this->permissions = new PermissionChecker($this);
// PathProvider aus DI Container nutzen, Fallback auf basePath
$pathProvider = $pathProvider ?? new PathProvider($this->basePath);
$this->permissions = new PermissionChecker($pathProvider);
$this->fiberManager = $fiberManager ?? $this->createDefaultFiberManager();
$this->validator = $validator;
$this->logger = $logger;
}
private function createDefaultFiberManager(): FiberManager
{
// Create minimal dependencies for FiberManager
$clock = new \App\Framework\DateTime\SystemClock();
$timer = new \App\Framework\DateTime\SystemTimer($clock);
$clock = new SystemClock();
$timer = new SystemTimer($clock);
return new FiberManager($clock, $timer);
}
@@ -61,9 +81,35 @@ final readonly class FileStorage implements Storage, AtomicStorage, AppendableSt
return rtrim($this->basePath, '/') . '/' . ltrim($path, '/');
}
/**
* Log FileOperation context wenn Logger verfügbar
*/
private function logOperation(FileOperationContext $context): void
{
if ($this->logger === null) {
return;
}
$logContext = LogContext::fromArray($context->toArray());
if ($context->isHighSeverity()) {
$this->logger->framework->warning($context->toString(), $logContext);
} elseif ($context->isLargeOperation()) {
$this->logger->framework->info($context->toString(), $logContext);
} else {
$this->logger->framework->debug($context->toString(), $logContext);
}
}
public function get(string $path): string
{
$resolvedPath = $this->resolvePath($path);
// Validate with FileValidator if available
if ($this->validator !== null) {
$this->validator->validateRead($resolvedPath);
}
if (! is_file($resolvedPath)) {
throw new FileNotFoundException($path);
}
@@ -82,6 +128,12 @@ final readonly class FileStorage implements Storage, AtomicStorage, AppendableSt
throw new FileReadException($path);
}
// Log successful read operation
$this->logOperation(FileOperationContext::forRead(
path: $resolvedPath,
bytesRead: FileSize::fromBytes(strlen($content))
));
return $content;
}
@@ -90,6 +142,15 @@ final readonly class FileStorage implements Storage, AtomicStorage, AppendableSt
$resolvedPath = $this->resolvePath($path);
$dir = dirname($resolvedPath);
// Validate with FileValidator if available
if ($this->validator !== null) {
$fileSize = FileSize::fromBytes(strlen($content));
$this->validator->validateWrite($resolvedPath, $fileSize);
}
// Clear stat cache before directory operations to ensure fresh info
clearstatcache(true, $dir);
// Prüfe Directory-Permissions
if (! is_dir($dir)) {
if (! @mkdir($dir, 0777, true) && ! is_dir($dir)) {
@@ -117,6 +178,19 @@ final readonly class FileStorage implements Storage, AtomicStorage, AppendableSt
throw new FileWriteException($path);
}
// Set readable permissions for files (0644 = owner rw, group r, others r)
// This ensures cache files and other storage files are always readable
$this->permissions->setPermissions(
$path,
ValueObjects\FilePermissions::readWriteOwnerReadAll()
);
// Log successful write operation
$this->logOperation(FileOperationContext::forWrite(
path: $resolvedPath,
bytesWritten: FileSize::fromBytes(strlen($content))
));
}
public function exists(string $path): bool
@@ -127,6 +201,13 @@ final readonly class FileStorage implements Storage, AtomicStorage, AppendableSt
public function delete(string $path): void
{
$resolvedPath = $this->resolvePath($path);
// Validate with FileValidator if available
if ($this->validator !== null) {
$this->validator->validatePath($resolvedPath);
$this->validator->validateExists($resolvedPath);
}
if (! is_file($resolvedPath)) {
throw new FileNotFoundException($path);
}
@@ -150,6 +231,12 @@ final readonly class FileStorage implements Storage, AtomicStorage, AppendableSt
throw new FileDeleteException($path);
}
// Log successful delete operation
$this->logOperation(FileOperationContext::forOperation(
operation: FileOperation::DELETE,
path: $resolvedPath
));
}
public function copy(string $source, string $destination): void
@@ -157,6 +244,17 @@ final readonly class FileStorage implements Storage, AtomicStorage, AppendableSt
$resolvedSource = $this->resolvePath($source);
$resolvedDestination = $this->resolvePath($destination);
// Validate source with FileValidator if available
if ($this->validator !== null) {
$this->validator->validateRead($resolvedSource);
}
// Validate destination with FileValidator if available
if ($this->validator !== null) {
$this->validator->validatePath($resolvedDestination);
$this->validator->validateExtension($resolvedDestination);
}
if (! is_file($resolvedSource)) {
throw new FileNotFoundException($source);
}
@@ -170,6 +268,13 @@ final readonly class FileStorage implements Storage, AtomicStorage, AppendableSt
if (! @copy($resolvedSource, $resolvedDestination)) {
throw new FileCopyException($source, $destination);
}
// Log successful copy operation
$this->logOperation(FileOperationContext::forOperationWithDestination(
operation: FileOperation::COPY,
sourcePath: $resolvedSource,
destinationPath: $resolvedDestination
));
}
public function size(string $path): int