From 5fae92efe85b93b05cfff72a1aed2aa55eb01896 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sun, 1 Mar 2026 00:58:25 +0000 Subject: [PATCH] Autosave: 20260301-005825 --- .gitignore | 1 + admin_dashboard.php | 112 ++++++++- api/run_migrations.php | 69 ++++++ db/config.php | 45 ++-- .../06_create_migration_history_table.sql | 5 + install.php | 216 ++++++++++++++++++ test_mail.php | 17 -- 7 files changed, 426 insertions(+), 39 deletions(-) create mode 100644 api/run_migrations.php create mode 100644 db/migrations/06_create_migration_history_table.sql create mode 100644 install.php delete mode 100644 test_mail.php diff --git a/.gitignore b/.gitignore index e427ff3..0ba1b12 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ */node_modules/ */build/ +db/.install_lock diff --git a/admin_dashboard.php b/admin_dashboard.php index 0b2f350..862c0b0 100644 --- a/admin_dashboard.php +++ b/admin_dashboard.php @@ -11,17 +11,27 @@ $users_count = 0; $lpas_count = 0; $users = []; $lpas = []; +$migration_history = []; try { + $db = db(); // Get stats - $users_count = db()->query("SELECT COUNT(*) FROM users")->fetchColumn(); - $lpas_count = db()->query("SELECT COUNT(*) FROM lpa_applications")->fetchColumn(); + $users_count = $db->query("SELECT COUNT(*) FROM users")->fetchColumn(); + $lpas_count = $db->query("SELECT COUNT(*) FROM lpa_applications")->fetchColumn(); // Get all users - $users = db()->query("SELECT * FROM users ORDER BY created_at DESC LIMIT 50")->fetchAll(); + $users = $db->query("SELECT * FROM users ORDER BY created_at DESC LIMIT 50")->fetchAll(); // Get all LPAs - $lpas = db()->query("SELECT * FROM lpa_applications ORDER BY created_at DESC LIMIT 50")->fetchAll(); + $lpas = $db->query("SELECT * FROM lpa_applications ORDER BY created_at DESC LIMIT 50")->fetchAll(); + + // Get migration history + try { + $migration_history = $db->query("SELECT * FROM migration_history ORDER BY executed_at DESC")->fetchAll(); + } catch (PDOException $e) { + // migration_history might not exist yet if installer hasn't run or table not created + $migration_history = []; + } } catch (PDOException $e) { error_log($e->getMessage()); } @@ -49,11 +59,35 @@ try {
-
-
+
+

System Administration

Overview of all users and applications in the system.

+
+ +
+
+ +
@@ -108,8 +142,8 @@ try { - - + + @@ -182,6 +216,36 @@ try {
+ +
+
+
+
Migration History
+
+
+ + + + + + + + + 0): ?> + + + + + + + + + + +
File NameExecuted At
No migration history found. Run migrations to initialize.
+
+
+
@@ -202,7 +266,6 @@ try { const row = document.getElementById('lpa-row-' + id); if (row) { row.remove(); - // Update count display if needed or refresh location.reload(); } } else { @@ -215,6 +278,37 @@ try { }); } } + + function runMigrations() { + if (!confirm('Run database migrations? This will execute all SQL files in db/migrations/ and may take a moment.')) return; + + const alertBox = document.getElementById('migration-alert'); + const messageEl = document.getElementById('migration-message'); + + fetch('api/run_migrations.php', { + method: 'POST' + }) + .then(response => response.json()) + .then(data => { + alertBox.style.display = 'block'; + if (data.success) { + messageEl.textContent = data.message + ' Page will reload in 2 seconds.'; + messageEl.parentElement.className = 'alert alert-success alert-dismissible fade show rounded-3 shadow-sm border-0 mb-4'; + setTimeout(() => location.reload(), 2000); + } else { + messageEl.textContent = 'Error: ' + data.error; + messageEl.parentElement.className = 'alert alert-danger alert-dismissible fade show rounded-3 shadow-sm border-0 mb-4'; + } + // Scroll to alert + alertBox.scrollIntoView({ behavior: 'smooth' }); + }) + .catch(error => { + alertBox.style.display = 'block'; + messageEl.textContent = 'An unexpected error occurred.'; + messageEl.parentElement.className = 'alert alert-danger alert-dismissible fade show rounded-3 shadow-sm border-0 mb-4'; + console.error('Error:', error); + }); + } \ No newline at end of file diff --git a/api/run_migrations.php b/api/run_migrations.php new file mode 100644 index 0000000..cc4fb23 --- /dev/null +++ b/api/run_migrations.php @@ -0,0 +1,69 @@ + false, 'error' => 'Unauthorized']); + exit; +} + +require_once '../db/config.php'; + +try { + $db = db(); + + // 1. Ensure migration_history table exists + $db->exec("CREATE TABLE IF NOT EXISTS migration_history ( + id INT AUTO_INCREMENT PRIMARY KEY, + filename VARCHAR(255) NOT NULL UNIQUE, + executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"); + + // 2. Scan migrations directory + $migrations_dir = __DIR__ . '/../db/migrations/'; + $migration_files = glob($migrations_dir . '*.sql'); + sort($migration_files); + + // 3. Get list of already executed migrations + $stmt = $db->query("SELECT filename FROM migration_history"); + $executed_migrations = $stmt->fetchAll(PDO::FETCH_COLUMN); + + $executed_count = 0; + $skipped_count = 0; + $new_migrations = []; + + foreach ($migration_files as $file_path) { + $filename = basename($file_path); + + if (in_array($filename, $executed_migrations)) { + $skipped_count++; + continue; + } + + $sql = file_get_contents($file_path); + if ($sql) { + // Execute migration + $db->exec($sql); + + // Record migration in history + $stmt = $db->prepare("INSERT INTO migration_history (filename) VALUES (?)"); + $stmt->execute([$filename]); + + $executed_count++; + $new_migrations[] = $filename; + } + } + + echo json_encode([ + 'success' => true, + 'message' => "Migration process complete.", + 'executed' => $executed_count, + 'skipped' => $skipped_count, + 'new_migrations' => $new_migrations + ]); +} catch (Exception $e) { + echo json_encode([ + 'success' => false, + 'error' => 'Migration failed: ' . $e->getMessage() + ]); +} \ No newline at end of file diff --git a/db/config.php b/db/config.php index 8207edd..893d556 100644 --- a/db/config.php +++ b/db/config.php @@ -1,17 +1,36 @@ PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - ]); - } - return $pdo; + static $pdo; + if (!$pdo) { + try { + // First, check if configuration is the default/placeholder which likely won't work elsewhere + if (DB_NAME === 'app_37684' && basename($_SERVER['PHP_SELF']) !== 'install.php' && !file_exists(__DIR__ . '/.install_lock')) { + // Potentially redirect if it's a new environment and we haven't locked it yet + // But safer to just try connection first. + } + + $pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + ]); + } catch (PDOException $e) { + // If connection fails and we are not already on the install page, redirect to install.php + if (basename($_SERVER['PHP_SELF']) !== 'install.php' && !headers_sent()) { + header('Location: install.php'); + exit; + } + throw $e; + } + } + return $pdo; } diff --git a/db/migrations/06_create_migration_history_table.sql b/db/migrations/06_create_migration_history_table.sql new file mode 100644 index 0000000..6b06118 --- /dev/null +++ b/db/migrations/06_create_migration_history_table.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS migration_history ( + id INT AUTO_INCREMENT PRIMARY KEY, + filename VARCHAR(255) NOT NULL UNIQUE, + executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/install.php b/install.php new file mode 100644 index 0000000..4009e75 --- /dev/null +++ b/install.php @@ -0,0 +1,216 @@ + PDO::ERRMODE_EXCEPTION, + ]); + + // 2. Create database if not exists + $pdo->exec("CREATE DATABASE IF NOT EXISTS `$db_name` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"); + $pdo->exec("USE `$db_name` "); + + // 3. Update db/config.php + $config_content = ' PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + ]); + } catch (PDOException $e) { + // If connection fails and we are not already on the install page, redirect to install.php + if (basename($_SERVER[\'PHP_SELF\']) !== \'install.php\' && !headers_sent()) { + header(\'Location: install.php\'); + exit; + } + throw $e; + } + } + return $pdo; +}'; + + if (file_put_contents(__DIR__ . '/db/config.php', $config_content) === false) { + throw new Exception("Could not write to db/config.php. Please check file permissions."); + } + + // 4. Ensure migration_history table exists + $pdo->exec("CREATE TABLE IF NOT EXISTS migration_history ( + id INT AUTO_INCREMENT PRIMARY KEY, + filename VARCHAR(255) NOT NULL UNIQUE, + executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"); + + // 5. Run migrations + $migrations_dir = __DIR__ . '/db/migrations/'; + $migration_files = glob($migrations_dir . '*.sql'); + sort($migration_files); + + // Get list of already executed migrations + $stmt = $pdo->query("SELECT filename FROM migration_history"); + $executed_migrations = $stmt->fetchAll(PDO::FETCH_COLUMN); + + foreach ($migration_files as $file_path) { + $filename = basename($file_path); + + if (in_array($filename, $executed_migrations)) { + continue; + } + + $sql = file_get_contents($file_path); + if ($sql) { + // Split SQL by ; to handle multiple statements if any + $pdo->exec($sql); + + // Record migration in history + $stmt = $pdo->prepare("INSERT INTO migration_history (filename) VALUES (?)"); + $stmt->execute([$filename]); + } + } + + // 6. Create Super User + $hashed_pass = password_hash($admin_pass, PASSWORD_DEFAULT); + $stmt = $pdo->prepare("INSERT INTO users (name, email, password, is_verified, role) VALUES (?, ?, ?, 1, 'Super User') ON DUPLICATE KEY UPDATE role = 'Super User'"); + $stmt->execute([$admin_name, $admin_email, $hashed_pass]); + + // 7. Create lock file + file_put_contents($lock_file, date('Y-m-d H:i:s')); + + $success = 'Installation successful! Admin account created. Redirecting to login...'; + header('Refresh: 3; url=login.php'); + } catch (Exception $e) { + $error = 'Setup failed: ' . $e->getMessage(); + } + } +} +?> + + + + + + Install — <?php echo htmlspecialchars($project_name); ?> + + + + + + +
+
+
+
+
+

System Setup

+

Configure your database and administrator account.

+
+ + +
+ + + +
+ +
+
+
+
Database
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
Super User
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ +
+
+ + +
+

The installer will create the database, tables, and your admin account.

+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/test_mail.php b/test_mail.php deleted file mode 100644 index 2139a14..0000000 --- a/test_mail.php +++ /dev/null @@ -1,17 +0,0 @@ -Test

This is a test email to verify mail functionality.

'; -$text = 'Test: This is a test email to verify mail functionality.'; - -echo "Sending email to $to...\n"; -$res = MailService::sendMail($to, $subject, $html, $text); - -if ($res['success']) { - echo "SUCCESS: Email sent successfully.\n"; -} else { - echo "ERROR: " . ($res['error'] ?? 'Unknown error') . "\n"; -} -