269 lines
13 KiB
PHP
269 lines
13 KiB
PHP
<?php
|
|
session_start();
|
|
|
|
$config_file = __DIR__ . '/db/config.php';
|
|
$step = isset($_GET['step']) ? (int)$_GET['step'] : 1;
|
|
|
|
// If config exists and we are at step 1, we might want to skip to step 2
|
|
if (file_exists($config_file) && $step === 1) {
|
|
$step = 2;
|
|
}
|
|
|
|
$error = '';
|
|
$success = '';
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
if ($step === 1) {
|
|
$host = $_POST['db_host'] ?? '';
|
|
$name = $_POST['db_name'] ?? '';
|
|
$user = $_POST['db_user'] ?? '';
|
|
$pass = $_POST['db_pass'] ?? '';
|
|
|
|
try {
|
|
$pdo = new PDO("mysql:host=$host", $user, $pass);
|
|
$pdo->exec("CREATE DATABASE IF NOT EXISTS `$name` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
|
|
$pdo = new PDO("mysql:host=$host;dbname=$name", $user, $pass);
|
|
|
|
$config_content = "<?php
|
|
define('DB_HOST', '$host');
|
|
define('DB_NAME', '$name');
|
|
define('DB_USER', '$user');
|
|
define('DB_PASS', '$pass');
|
|
|
|
function db() {
|
|
static \$pdo;
|
|
if (!\$pdo) {
|
|
\$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,
|
|
]);
|
|
}
|
|
return \$pdo;
|
|
}
|
|
|
|
require_once __DIR__ . '/../includes/functions.php';
|
|
";
|
|
file_put_contents($config_file, $config_content);
|
|
header("Location: install.php?step=2");
|
|
exit;
|
|
} catch (Exception $e) {
|
|
$error = "Database Connection Failed: " . $e->getMessage();
|
|
}
|
|
} elseif ($step === 2) {
|
|
require_once $config_file;
|
|
try {
|
|
$pdo = db();
|
|
|
|
// 1. Run Base Schema
|
|
$schema = file_get_contents(__DIR__ . '/db/schema.sql');
|
|
// Split schema by ; to execute one by one to avoid issues with some PDO drivers
|
|
// But usually PDO::exec handles multiple if allowed.
|
|
// Better to use a simpler approach for schema.sql
|
|
$pdo->exec($schema);
|
|
|
|
// 2. Create migrations table if not exists
|
|
$pdo->exec("CREATE TABLE IF NOT EXISTS migrations (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
migration VARCHAR(255) UNIQUE NOT NULL,
|
|
applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
)");
|
|
|
|
// 3. Run Migrations
|
|
$migrations_dir = __DIR__ . '/db/migrations/';
|
|
$migrations = glob($migrations_dir . '*.sql');
|
|
sort($migrations);
|
|
|
|
// Get applied migrations
|
|
$stmt = $pdo->query("SELECT migration FROM migrations");
|
|
$applied = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
|
|
|
foreach ($migrations as $migration_path) {
|
|
$migration_name = basename($migration_path);
|
|
if (!in_array($migration_name, $applied)) {
|
|
$sql = file_get_contents($migration_path);
|
|
if (!empty(trim($sql))) {
|
|
// Some migrations might fail because they were partially applied before we had tracking
|
|
// We will wrap each in a try-catch or just execute and catch specific duplicate column errors
|
|
try {
|
|
$pdo->exec($sql);
|
|
$stmt = $pdo->prepare("INSERT INTO migrations (migration) VALUES (?)");
|
|
$stmt->execute([$migration_name]);
|
|
} catch (PDOException $e) {
|
|
// If it's "Duplicate column name" or "Duplicate key name", we might want to skip and mark as applied
|
|
// to recover from a broken state
|
|
if (strpos($e->getMessage(), 'Duplicate column name') !== false ||
|
|
strpos($e->getMessage(), 'Duplicate key name') !== false ||
|
|
strpos($e->getMessage(), 'already exists') !== false) {
|
|
$stmt = $pdo->prepare("INSERT INTO migrations (migration) VALUES (?)");
|
|
$stmt->execute([$migration_name]);
|
|
} else {
|
|
throw $e;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 4. Seed initial data (from init.php logic)
|
|
$stmt = $pdo->query("SELECT COUNT(*) FROM outlets");
|
|
if ($stmt->fetchColumn() == 0) {
|
|
$pdo->exec("INSERT INTO outlets (name, address) VALUES ('Main Downtown', '123 Main St'), ('Westside Hub', '456 West Blvd')");
|
|
$pdo->exec("INSERT INTO categories (name, sort_order) VALUES ('Burgers', 1), ('Sides', 2), ('Drinks', 3)");
|
|
$pdo->exec("INSERT INTO products (category_id, name, description, price) VALUES
|
|
(1, 'Signature Burger', 'Juicy beef patty with special sauce', 12.99),
|
|
(1, 'Veggie Delight', 'Plant-based patty with fresh avocado', 11.50),
|
|
(2, 'Truffle Fries', 'Crispy fries with truffle oil and parmesan', 5.99),
|
|
(3, 'Craft Cola', 'House-made sparkling cola', 3.50)");
|
|
}
|
|
|
|
header("Location: install.php?step=3");
|
|
exit;
|
|
} catch (Exception $e) {
|
|
$error = "Initialization Failed: " . $e->getMessage();
|
|
}
|
|
} elseif ($step === 3) {
|
|
require_once $config_file;
|
|
$username = $_POST['admin_user'] ?? '';
|
|
$password = $_POST['admin_pass'] ?? '';
|
|
$email = $_POST['admin_email'] ?? '';
|
|
|
|
if (empty($username) || empty($password)) {
|
|
$error = "Username and Password are required.";
|
|
} else {
|
|
try {
|
|
$pdo = db();
|
|
$hashed_pass = password_hash($password, PASSWORD_DEFAULT);
|
|
|
|
// Ensure Admin group exists
|
|
$stmt = $pdo->prepare("SELECT id FROM user_groups WHERE name = 'Administrator' LIMIT 1");
|
|
$stmt->execute();
|
|
$group_id = $stmt->fetchColumn();
|
|
|
|
if (!$group_id) {
|
|
$pdo->exec("INSERT INTO user_groups (name, permissions) VALUES ('Administrator', 'all')");
|
|
$group_id = $pdo->lastInsertId();
|
|
}
|
|
|
|
$stmt = $pdo->prepare("SELECT id FROM users WHERE username = ?");
|
|
$stmt->execute([$username]);
|
|
if ($stmt->fetch()) {
|
|
// Admin already exists, just move to next step
|
|
header("Location: install.php?step=4");
|
|
exit;
|
|
}
|
|
|
|
$stmt = $pdo->prepare("INSERT INTO users (group_id, username, password, full_name, email, is_active) VALUES (?, ?, ?, ?, ?, 1)");
|
|
$stmt->execute([$group_id, $username, $hashed_pass, 'Super Admin', $email]);
|
|
|
|
header("Location: install.php?step=4");
|
|
exit;
|
|
} catch (Exception $e) {
|
|
$error = "Failed to create Admin: " . $e->getMessage();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>POS System Installation</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<style>
|
|
body { background-color: #f8f9fa; }
|
|
.install-card { max-width: 600px; margin: 50px auto; border-radius: 15px; box-shadow: 0 10px 30px rgba(0,0,0,0.1); }
|
|
.step-indicator { display: flex; justify-content: space-between; margin-bottom: 30px; }
|
|
.step { width: 30px; height: 30px; border-radius: 50%; background: #dee2e6; display: flex; align-items: center; justify-content: center; font-weight: bold; }
|
|
.step.active { background: #0d6efd; color: white; }
|
|
.step.completed { background: #198754; color: white; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="card install-card">
|
|
<div class="card-body p-5">
|
|
<h2 class="text-center mb-4">Installation</h2>
|
|
|
|
<div class="step-indicator">
|
|
<div class="step <?= $step >= 1 ? ($step > 1 ? 'completed' : 'active') : '' ?>">1</div>
|
|
<div class="step <?= $step >= 2 ? ($step > 2 ? 'completed' : 'active') : '' ?>">2</div>
|
|
<div class="step <?= $step >= 3 ? ($step > 3 ? 'completed' : 'active') : '' ?>">3</div>
|
|
<div class="step <?= $step >= 4 ? ($step > 4 ? 'completed' : 'active') : '' ?>">4</div>
|
|
</div>
|
|
|
|
<?php if ($error): ?>
|
|
<div class="alert alert-danger"><?= $error ?></div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($step === 1): ?>
|
|
<h4>Step 1: Database Configuration</h4>
|
|
<p class="text-muted">Enter your database connection details.</p>
|
|
<form method="POST">
|
|
<div class="mb-3">
|
|
<label class="form-label">DB Host</label>
|
|
<input type="text" name="db_host" class="form-control" value="localhost" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">DB Name</label>
|
|
<input type="text" name="db_name" class="form-control" placeholder="pos_system" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">DB User</label>
|
|
<input type="text" name="db_user" class="form-control" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">DB Password</label>
|
|
<input type="password" name="db_pass" class="form-control">
|
|
</div>
|
|
<button type="submit" class="btn btn-primary w-100">Test Connection & Save</button>
|
|
</form>
|
|
|
|
<?php elseif ($step === 2): ?>
|
|
<h4>Step 2: Initialize Database</h4>
|
|
<p>Ready to create tables and run migrations. This will set up the latest database structure.</p>
|
|
<form method="POST">
|
|
<button type="submit" class="btn btn-primary w-100">Run Database Setup</button>
|
|
</form>
|
|
|
|
<?php elseif ($step === 3): ?>
|
|
<h4>Step 3: Create Super Admin</h4>
|
|
<p class="text-muted">Create your first administrator account.</p>
|
|
<form method="POST">
|
|
<div class="mb-3">
|
|
<label class="form-label">Username</label>
|
|
<input type="text" name="admin_user" class="form-control" value="admin" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Email</label>
|
|
<input type="email" name="admin_email" class="form-control" placeholder="admin@example.com" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Password</label>
|
|
<input type="password" name="admin_pass" class="form-control" required>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary w-100">Create Admin Account</button>
|
|
</form>
|
|
|
|
<?php elseif ($step === 4): ?>
|
|
<div class="text-center">
|
|
<div class="text-success mb-4">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="currentColor" class="bi bi-check-circle-fill" viewBox="0 0 16 16">
|
|
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/>
|
|
</svg>
|
|
</div>
|
|
<h4>Installation Complete!</h4>
|
|
<p>The system has been successfully installed.</p>
|
|
<div class="alert alert-warning mt-3">
|
|
<strong>Security Warning:</strong> Please delete the <code>install.php</code> file from your server immediately!
|
|
</div>
|
|
<a href="login.php" class="btn btn-primary mt-3">Go to Login</a>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|