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

@@ -10,7 +10,7 @@ use App\Framework\Core\ValueObjects\Hash;
use App\Framework\Database\Attributes\Column;
use App\Framework\Database\Attributes\Entity;
use App\Framework\Database\Attributes\Type;
use App\Framework\Filesystem\FilePath;
use App\Framework\Filesystem\ValueObjects\FilePath;
use App\Framework\Http\MimeType;
use App\Framework\Ulid\Ulid;

View File

@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace App\Domain\Media;
use InvalidArgumentException;
enum ImageSize: string
{
case SMALL = 'small';
@@ -26,7 +28,7 @@ enum ImageSize: string
[ImageVariantType::HERO, self::LARGE] => 1920,
[ImageVariantType::HERO, self::XLARGE] => 2560,
default => throw new \InvalidArgumentException("Invalid combination: {$type->value} - {$this->value}"),
default => throw new InvalidArgumentException("Invalid combination: {$type->value} - {$this->value}"),
};
}

View File

@@ -7,6 +7,7 @@ namespace App\Domain\Media\Migrations;
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\Migration\Migration;
use App\Framework\Database\Migration\MigrationVersion;
use App\Framework\Database\Schema\Blueprint;
use App\Framework\Database\Schema\Schema;
final readonly class AddSizeToImageVariantsTable implements Migration
@@ -15,27 +16,27 @@ final readonly class AddSizeToImageVariantsTable implements Migration
{
$schema = new Schema($connection);
/*$schema->table('image_variants', function ($table) {
$schema->table('image_variants', function (Blueprint $table) {
$table->string('size', 25)->default('');
});*/
});
if (! $schema->hasColumn('image_variants', 'size')) {
$connection->execute("ALTER TABLE image_variants ADD COLUMN size VARCHAR(25) NOT NULL DEFAULT ''");
}
$schema->execute();
}
public function down(ConnectionInterface $connection): void
{
$schema = new Schema($connection);
if ($schema->hasColumn('image_variants', 'size')) {
$connection->execute("ALTER TABLE image_variants DROP COLUMN size");
}
$schema->table('image_variants', function (Blueprint $table) {
$table->dropColumn('size');
});
$schema->execute();
}
public function getVersion(): MigrationVersion
{
return MigrationVersion::fromTimestamp("2024_01_16_000005");
return MigrationVersion::fromTimestamp("2024_01_17_000004");
}
public function getDescription(): string

View File

@@ -7,53 +7,53 @@ namespace App\Domain\Media\Migrations;
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\Migration\Migration;
use App\Framework\Database\Migration\MigrationVersion;
use App\Framework\Database\Platform\SchemaBuilderFactory;
use App\Framework\Database\Platform\ValueObjects\TableOptions;
use App\Framework\Database\Schema\Blueprint;
use App\Framework\Database\Schema\ForeignKeyAction;
use App\Framework\Database\Schema\Schema;
final class CreateImageVariantsTable implements Migration
{
public function up(ConnectionInterface $connection): void
{
$schema = SchemaBuilderFactory::create($connection);
$schema = new Schema($connection);
$schema->createTable(
'image_variants',
[
$schema->id(),
$schema->string('image_id', 26)->notNull(),
$schema->string('variant_type', 50)->notNull(),
$schema->string('format', 25)->notNull(),
$schema->string('mime_type', 100)->notNull(),
$schema->integer('width')->notNull(),
$schema->integer('height')->notNull(),
$schema->bigInteger('file_size')->notNull(),
$schema->string('filename', 500)->notNull(),
$schema->string('path', 500)->notNull(),
$schema->timestamp('created_at')->notNull()->withDefault('CURRENT_TIMESTAMP'),
$schema->timestamp('updated_at')->withDefault(null),
],
TableOptions::default()
->withEngine('InnoDB')
->withCharset('utf8mb4')
->withCollation('utf8mb4_unicode_ci')
->withIfNotExists()
->withComment('Image variants table for different image sizes and formats')
);
$schema->createIfNotExists('image_variants', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('image_id', 26);
$table->string('variant_type', 50);
$table->string('format', 25);
$table->string('mime_type', 100);
$table->integer('width');
$table->integer('height');
$table->bigInteger('file_size');
$table->string('filename', 500);
$table->string('path', 500);
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->nullable();
// TODO: Add unique constraint, foreign key constraint, and index
// These will need to be implemented in a future enhancement of the SchemaBuilder
// For now, we'll add them manually:
$connection->query("ALTER TABLE image_variants ADD UNIQUE KEY uk_image_variants_combination (image_id, variant_type, format)");
$connection->query("ALTER TABLE image_variants ADD CONSTRAINT fk_image_variants_image_id FOREIGN KEY (image_id) REFERENCES images(ulid) ON DELETE CASCADE");
$connection->query("ALTER TABLE image_variants ADD INDEX idx_image_variants_lookup (image_id, variant_type)");
// Constraints and indexes
$table->unique(['image_id', 'variant_type', 'format'], 'uk_image_variants_combination');
$table->index(['image_id', 'variant_type'], 'idx_image_variants_lookup');
// Foreign key
$table->foreign('image_id')
->references('ulid')
->on('images')
->onDelete(ForeignKeyAction::CASCADE);
$table->engine('InnoDB');
$table->charset('utf8mb4');
$table->collation('utf8mb4_unicode_ci');
});
$schema->execute();
}
public function down(ConnectionInterface $connection): void
{
$schema = SchemaBuilderFactory::create($connection);
$this->dropExistingConstraints($connection);
$schema->dropTable('image_variants', true);
$schema = new Schema($connection);
$schema->dropIfExists('image_variants');
$schema->execute();
}
public function getVersion(): MigrationVersion

View File

@@ -7,38 +7,45 @@ namespace App\Domain\Media\Migrations;
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\Migration\Migration;
use App\Framework\Database\Migration\MigrationVersion;
use App\Framework\Database\Schema\Blueprint;
use App\Framework\Database\Schema\Schema;
final class CreateImagesTable implements Migration
{
public function up(ConnectionInterface $connection): void
{
$sql = <<<SQL
CREATE TABLE IF NOT EXISTS images (
ulid VARCHAR(26) NOT NULL,
filename VARCHAR(255) NOT NULL,
original_filename VARCHAR(255) NOT NULL,
mime_type VARCHAR(100) NOT NULL,
file_size BIGINT NOT NULL,
width INT UNSIGNED NOT NULL,
height INT UNSIGNED NOT NULL,
hash VARCHAR(255) NOT NULL,
path VARCHAR(500) NOT NULL,
alt_text TEXT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT NULL,
$schema = new Schema($connection);
PRIMARY KEY (ulid),
UNIQUE KEY uk_images_hash (hash),
UNIQUE KEY uk_images_ulid (ulid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
SQL;
$schema->createIfNotExists('images', function (Blueprint $table) {
$table->string('ulid', 26)->primary();
$table->string('filename', 255);
$table->string('original_filename', 255);
$table->string('mime_type', 100);
$table->bigInteger('file_size');
$table->integer('width');
$table->integer('height');
$table->string('hash', 255);
$table->string('path', 500);
$table->text('alt_text');
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->nullable();
$connection->query($sql);
$table->unique('hash', 'uk_images_hash');
$table->unique('ulid', 'uk_images_ulid');
$table->engine('InnoDB');
$table->charset('utf8mb4');
$table->collation('utf8mb4_unicode_ci');
});
$schema->execute();
}
public function down(ConnectionInterface $connection): void
{
$connection->execute("DROP TABLE IF EXISTS images");
$schema = new Schema($connection);
$schema->dropIfExists('images');
$schema->execute();
}
public function getVersion(): MigrationVersion

View File

@@ -7,23 +7,49 @@ namespace App\Domain\Media\Migrations;
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\Migration\Migration;
use App\Framework\Database\Migration\MigrationVersion;
use App\Framework\Database\Schema\Blueprint;
use App\Framework\Database\Schema\Schema;
final readonly class UpdateImageVariantsConstraint implements Migration
{
public function up(ConnectionInterface $connection): void
{
// Bestehenden Constraint entfernen
$connection->execute("ALTER TABLE image_variants DROP INDEX uk_image_variants_combination");
$schema = new Schema($connection);
// Neuen Constraint mit size-Spalte hinzufügen
$connection->execute("ALTER TABLE image_variants ADD UNIQUE KEY uk_image_variants_combination (image_id, variant_type, size, format)");
// Drop existing constraint
$schema->table('image_variants', function (Blueprint $table) {
$table->dropUnique('uk_image_variants_combination');
});
$schema->execute();
// Add new constraint with size column
$schema = new Schema($connection);
$schema->table('image_variants', function (Blueprint $table) {
$table->unique(['image_id', 'variant_type', 'size', 'format'], 'uk_image_variants_combination');
});
$schema->execute();
}
public function down(ConnectionInterface $connection): void
{
// Zurück zum ursprünglichen Constraint
$connection->execute("ALTER TABLE image_variants DROP INDEX uk_image_variants_combination");
$connection->execute("ALTER TABLE image_variants ADD UNIQUE KEY uk_image_variants_combination (image_id, variant_type, format)");
$schema = new Schema($connection);
// Drop current constraint
$schema->table('image_variants', function (Blueprint $table) {
$table->dropUnique('uk_image_variants_combination');
});
$schema->execute();
// Restore original constraint
$schema = new Schema($connection);
$schema->table('image_variants', function (Blueprint $table) {
$table->unique(['image_id', 'variant_type', 'format'], 'uk_image_variants_combination');
});
$schema->execute();
}
public function getVersion(): MigrationVersion

View File

@@ -16,6 +16,7 @@ final readonly class SaveImageFile
}
$fullPath = $image->path->join($image->filename)->toString();
return move_uploaded_file($tempFileName, $fullPath);
}
}