refactor: reorganize project structure for better maintainability

- Move 45 debug/test files from root to organized scripts/ directories
- Secure public/ directory by removing debug files (security improvement)
- Create structured scripts organization:
  • scripts/debug/      (20 files) - Framework debugging tools
  • scripts/test/       (18 files) - Test and validation scripts
  • scripts/maintenance/ (5 files) - Maintenance utilities
  • scripts/dev/         (2 files) - Development tools

Security improvements:
- Removed all debug/test files from public/ directory
- Only production files remain: index.php, health.php

Root directory cleanup:
- Reduced from 47 to 2 PHP files in root
- Only essential production files: console.php, worker.php

This improves:
 Security (no debug code in public/)
 Organization (clear separation of concerns)
 Maintainability (easy to find and manage scripts)
 Professional structure (clean root directory)
This commit is contained in:
2025-10-05 10:59:15 +02:00
parent 03e5188644
commit 887847dde6
77 changed files with 3902 additions and 787 deletions

View File

@@ -0,0 +1,224 @@
<?php
declare(strict_types=1);
/**
* Script to populate the database with images found in the filesystem
*
* This script scans storage/uploads/ for image files and creates database records
* to match the existing files on disk.
*/
require_once 'vendor/autoload.php';
use App\Domain\Media\Image;
use App\Framework\Core\ValueObjects\FileSize;
use App\Framework\Core\ValueObjects\Hash;
use App\Framework\Database\DatabaseManager;
use App\Framework\Filesystem\FilePath;
use App\Framework\Http\MimeType;
use App\Framework\Ulid\Ulid;
use App\Framework\Ulid\UlidGenerator;
use App\Framework\DateTime\SystemClock;
class ImageMigrationScript
{
private PDO $db;
private SystemClock $clock;
private string $uploadsPath;
public function __construct()
{
$this->clock = new SystemClock();
$this->uploadsPath = __DIR__ . '/storage/uploads';
// Initialize database
$this->initializeDatabase();
}
private function initializeDatabase(): void
{
// Simple SQLite connection for this script
$pdo = new PDO('sqlite:' . __DIR__ . '/database.sqlite');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->db = $pdo;
}
public function run(): void
{
echo "🔍 Scanning for images in: {$this->uploadsPath}\n";
if (!is_dir($this->uploadsPath)) {
echo "❌ Uploads directory not found: {$this->uploadsPath}\n";
return;
}
$imageFiles = $this->findImageFiles();
echo "📁 Found " . count($imageFiles) . " image files\n";
if (empty($imageFiles)) {
echo " No images to migrate\n";
return;
}
$this->migrateImages($imageFiles);
echo "✅ Migration completed!\n";
}
private function findImageFiles(): array
{
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($this->uploadsPath)
);
$imageFiles = [];
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
foreach ($iterator as $file) {
if (!$file->isFile()) {
continue;
}
$extension = strtolower($file->getExtension());
if (!in_array($extension, $allowedExtensions)) {
continue;
}
$imageFiles[] = [
'path' => $file->getPathname(),
'filename' => $file->getFilename(),
'extension' => $extension,
'size' => $file->getSize(),
'mtime' => $file->getMTime()
];
}
return $imageFiles;
}
private function migrateImages(array $imageFiles): void
{
// Check current database schema
$this->checkDatabaseSchema();
$migrated = 0;
$errors = 0;
foreach ($imageFiles as $fileInfo) {
try {
$this->migrateImageFile($fileInfo);
$migrated++;
echo "✓ Migrated: {$fileInfo['filename']}\n";
} catch (Exception $e) {
$errors++;
echo "❌ Error migrating {$fileInfo['filename']}: " . $e->getMessage() . "\n";
}
}
echo "\n📊 Summary:\n";
echo " Migrated: $migrated\n";
echo " Errors: $errors\n";
}
private function checkDatabaseSchema(): void
{
// Check what columns exist in the images table
$stmt = $this->db->query("PRAGMA table_info(images)");
$columns = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo "📋 Database schema (images table):\n";
foreach ($columns as $column) {
echo " - {$column['name']} ({$column['type']})\n";
}
echo "\n";
}
private function migrateImageFile(array $fileInfo): void
{
$fullPath = $fileInfo['path'];
// Extract image dimensions if possible
$imageInfo = @getimagesize($fullPath);
$width = $imageInfo[0] ?? 0;
$height = $imageInfo[1] ?? 0;
// Generate ULID
$ulidGenerator = new UlidGenerator();
$ulidString = $ulidGenerator->generate($this->clock);
// Calculate hash
$hashValue = hash_file('sha256', $fullPath);
// Determine MIME type
$mimeTypeString = match (strtolower($fileInfo['extension'])) {
'jpg', 'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'webp' => 'image/webp',
default => 'image/jpeg'
};
// Extract original filename from the complex filename structure
$originalFilename = $this->extractOriginalFilename($fileInfo['filename']);
// Get relative path from storage root
$relativePath = str_replace($this->uploadsPath . '/', '', $fullPath);
$pathOnly = dirname($relativePath);
// Insert into database using the correct table structure
$sql = "INSERT INTO images (
ulid, filename, original_filename, mime_type, file_size,
width, height, hash, path, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
$stmt = $this->db->prepare($sql);
$now = date('Y-m-d H:i:s');
$stmt->execute([
$ulidString,
$fileInfo['filename'],
$originalFilename,
$mimeTypeString,
$fileInfo['size'],
$width,
$height,
$hashValue,
$pathOnly,
$now,
$now
]);
}
private function extractOriginalFilename(string $filename): string
{
// Pattern for files like: BFWCAKKEHTKF5SYR_6626fc6b...cd1_original.png
if (preg_match('/^[A-Z0-9]{16}_[a-f0-9]{64}_original\.(.+)$/', $filename, $matches)) {
// This is an original file, try to find the pattern in other files
$basePattern = substr($filename, 0, strpos($filename, '_original.'));
// For now, just return a cleaned version
return "original." . $matches[1];
}
// Pattern for simple files like: 00MF9VW9R36NJN3VCFSTS2CK6R.jpg
if (preg_match('/^[A-Z0-9]{26}\.(.+)$/', $filename, $matches)) {
return "image." . $matches[1];
}
// Fallback: return as-is
return $filename;
}
}
// Run the migration
echo "🚀 Starting image migration from filesystem to database...\n\n";
try {
$migration = new ImageMigrationScript();
$migration->run();
} catch (Exception $e) {
echo "💥 Migration failed: " . $e->getMessage() . "\n";
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
exit(1);
}
echo "\n🎉 Migration script completed!\n";