diff --git a/db/migrate.php b/db/migrate.php new file mode 100644 index 0000000..4b9832d --- /dev/null +++ b/db/migrate.php @@ -0,0 +1,92 @@ +exec( + "\n CREATE TABLE IF NOT EXISTS migrations (\n id INT AUTO_INCREMENT PRIMARY KEY,\n migration_name VARCHAR(255) NOT NULL UNIQUE,\n executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n " + ); + } catch (PDOException $e) { + return ["Error creating migrations table: " . $e->getMessage()]; + } + + // 2. Get executed migrations + $executed = []; + try { + $stmt = $pdo->query("SELECT migration_name FROM migrations"); + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $executed[] = $row['migration_name']; + } + } catch (PDOException $e) { + return ["Error fetching executed migrations: " . $e->getMessage()]; + } + + // 3. Scan for migration files + $migrationFiles = glob(__DIR__ . '/migrations/*.php'); + if ($migrationFiles === false) { + return ["Error scanning migration directory."]; + } + + // Sort files to ensure order (by name usually works if named correctly) + sort($migrationFiles); + + $count = 0; + foreach ($migrationFiles as $file) { + $filename = basename($file); + + if (in_array($filename, $executed)) { + continue; + } + + // Run migration + try { + // We use output buffering to capture echo output from migration files + ob_start(); + // Include inside a closure/function scope to avoid variable collisions + // but we need to ensure $pdo is available if they use it. + // Most files do: require config.php; $pdo = db(); + // Since we already required config.php, require_once will skip it. + // So they will just get $pdo = db(); which works. + include $file; + $output = ob_get_clean(); + + // Log success + $messages[] = "Executed $filename: " . trim($output); + + // Record in DB + $stmt = $pdo->prepare("INSERT INTO migrations (migration_name) VALUES (?)"); + $stmt->execute([$filename]); + $count++; + + } catch (Throwable $e) { + ob_end_clean(); // Clean buffer if error + $messages[] = "Failed to execute $filename: " . $e->getMessage(); + return $messages; // Stop on error + } + } + + if ($count === 0) { + $messages[] = "No new migrations to run."; + } else { + $messages[] = "Successfully ran $count migrations."; + } + + return $messages; +} + +// If run directly from CLI +if (php_sapi_name() === 'cli' && basename(__FILE__) == basename($_SERVER['SCRIPT_FILENAME'])) { + $results = run_migrations(); + echo implode("\n", $results) . "\n"; +} diff --git a/db/migrations/initial_schema.php b/db/migrations/initial_schema.php index 2d4c91d..e48f9c3 100644 --- a/db/migrations/initial_schema.php +++ b/db/migrations/initial_schema.php @@ -1,5 +1,5 @@ getMessage(); -} +} \ No newline at end of file diff --git a/install.php b/install.php index d01da28..711fefb 100644 --- a/install.php +++ b/install.php @@ -2,7 +2,12 @@ // install.php - Simple Installer for Flatlogic LAMP Project session_start(); -$step = $_GET['step'] ?? 1; +// Ensure errors are displayed for debugging during installation +ini_set('display_errors', 1); +ini_set('display_startup_errors', 1); +error_reporting(E_ALL); + +$step = isset($_GET['step']) ? (int)$_GET['step'] : 1; $message = ''; $messageType = ''; @@ -37,7 +42,10 @@ function write_db_config($host, $name, $user, $pass) { // Handle Form Submissions if ($_SERVER['REQUEST_METHOD'] === 'POST') { - if ($step == 1) { + // Determine step from hidden field or GET, defaulting to 1 + $postStep = isset($_POST['step']) ? (int)$_POST['step'] : $step; + + if ($postStep == 1) { $host = $_POST['db_host'] ?? ''; $name = $_POST['db_name'] ?? ''; $user = $_POST['db_user'] ?? ''; @@ -63,36 +71,73 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $message = "Connection failed: " . $e->getMessage(); $messageType = "danger"; } - } elseif ($step == 2) { - require_once __DIR__ . '/includes/app.php'; + } elseif ($postStep == 2) { + // Step 2: Create Admin & Run Migrations + // Include config and migration script + if (file_exists(__DIR__ . '/db/config.php')) { + require_once __DIR__ . '/db/config.php'; + } else { + $message = "Configuration file missing. Please go back to Step 1."; + $messageType = "danger"; + $step = 1; + } + + if (file_exists(__DIR__ . '/db/migrate.php')) { + require_once __DIR__ . '/db/migrate.php'; + } + $email = $_POST['admin_email'] ?? ''; $password = $_POST['admin_pass'] ?? ''; $fullName = $_POST['admin_name'] ?? 'Administrator'; - if ($email && $password) { + if ($email && $password && defined('DB_HOST')) { try { - ensure_schema(); // Make sure tables exist + // 1. Run Migrations + $migrationResults = []; + if (function_exists('run_migrations')) { + $migrationResults = run_migrations(); + } + + // 2. Also ensure basic schema from app.php (just in case) + // We do this after migrations so migrations take precedence if they exist + if (file_exists(__DIR__ . '/includes/app.php')) { + // We catch output to prevent it from messing up headers/layout if app.php has echoes + ob_start(); + require_once __DIR__ . '/includes/app.php'; + if (function_exists('ensure_schema')) { + ensure_schema(); + } + ob_end_clean(); + } + + // 3. Create Admin User + $pdo = db(); // Check if admin exists - $stmt = db()->prepare("SELECT id FROM users WHERE email = ?"); + $stmt = $pdo->prepare("SELECT id FROM users WHERE email = ?"); $stmt->execute([$email]); if ($stmt->fetch()) { // Update existing - $stmt = db()->prepare("UPDATE users SET password = ?, full_name = ?, role = 'admin', status = 'active' WHERE email = ?"); + $stmt = $pdo->prepare("UPDATE users SET password = ?, full_name = ?, role = 'admin', status = 'active' WHERE email = ?"); $stmt->execute([password_hash($password, PASSWORD_DEFAULT), $fullName, $email]); } else { // Create new - $stmt = db()->prepare("INSERT INTO users (email, password, full_name, role, status) VALUES (?, ?, ?, 'admin', 'active')"); + $stmt = $pdo->prepare("INSERT INTO users (email, password, full_name, role, status) VALUES (?, ?, ?, 'admin', 'active')"); $stmt->execute([$email, password_hash($password, PASSWORD_DEFAULT), $fullName]); } - $message = "Admin account created successfully!"; + $migMsg = implode("
", $migrationResults); + $message = "Admin account created successfully!
$migMsg"; $messageType = "success"; $step = 3; // Success page + } catch (Exception $e) { - $message = "Error creating admin: " . $e->getMessage(); + $message = "Error: " . $e->getMessage(); + $messageType = "danger"; + } catch (Throwable $e) { + $message = "Fatal Error: " . $e->getMessage(); $messageType = "danger"; } } else { @@ -149,6 +194,7 @@ if (file_exists(__DIR__ . '/db/config.php')) {
+
@@ -170,7 +216,7 @@ if (file_exists(__DIR__ . '/db/config.php')) { +
@@ -195,7 +242,7 @@ if (file_exists(__DIR__ . '/db/config.php')) {
- + @@ -210,4 +257,4 @@ if (file_exists(__DIR__ . '/db/config.php')) {
- + \ No newline at end of file