Commit a45e66f7 authored by Administrator's avatar Administrator

Update 5 files via Son of Anton

parent 6d82d38f
<?php // Make sure your Database constructor stores dbName.
declare(strict_types=1); // Find and verify/replace the constructor:
namespace App\Core; private string $dbName;
private ?\PDO $pdo = null;
private array $config;
use PDO; public function __construct(array $config)
use PDOStatement;
use PDOException;
final class Database
{
private ?PDO $pdo = null;
private string $host;
private int $port;
private string $name;
private string $user;
private string $pass;
private string $charset;
private array $queryLog = [];
private bool $queryLogEnabled = false;
private array $beforeInsertCallbacks = [];
private array $afterInsertCallbacks = [];
private array $beforeUpdateCallbacks = [];
private array $afterUpdateCallbacks = [];
private array $afterDeleteCallbacks = [];
public function __construct(string $host, int $port, string $name, string $user, string $pass, string $charset = 'utf8mb4')
{ {
$this->host = $host; $this->config = $config;
$this->port = $port; $this->dbName = $config['name'] ?? '';
$this->name = $name;
$this->user = $user;
$this->pass = $pass;
$this->charset = $charset;
} }
private function connect(): PDO private function connect(): \PDO
{ {
if ($this->pdo === null) { if ($this->pdo === null) {
$dsn = "mysql:host={$this->host};port={$this->port};dbname={$this->name};charset={$this->charset}"; $dsn = sprintf(
$options = [ 'mysql:host=%s;port=%s;dbname=%s;charset=%s',
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, $this->config['host'] ?? '127.0.0.1',
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, $this->config['port'] ?? '3306',
PDO::ATTR_EMULATE_PREPARES => false, $this->config['name'] ?? '',
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES '{$this->charset}' COLLATE '{$this->charset}_unicode_ci', sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'", $this->config['charset'] ?? 'utf8mb4'
]; );
$this->pdo = new PDO($dsn, $this->user, $this->pass, $options);
$this->pdo = new \PDO($dsn, $this->config['user'] ?? 'root', $this->config['pass'] ?? '', [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => false,
\PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'",
]);
} }
return $this->pdo;
}
public function pdo(): PDO
{
return $this->connect();
}
public function query(string $sql, array $params = []): PDOStatement
{
$start = microtime(true);
$stmt = $this->connect()->prepare($sql);
$stmt->execute($params);
$elapsed = (microtime(true) - $start) * 1000;
if ($this->queryLogEnabled) {
$this->queryLog[] = [
'sql' => $sql,
'params' => $params,
'time' => round($elapsed, 2),
];
}
return $stmt;
}
public function select(string $sql, array $params = []): array
{
return $this->query($sql, $params)->fetchAll();
}
public function selectOne(string $sql, array $params = []): ?array
{
$result = $this->query($sql, $params)->fetch();
return $result !== false ? $result : null;
}
public function insert(string $table, array $data): int
{
foreach ($this->beforeInsertCallbacks as $cb) {
$cb($table, $data, null);
}
$columns = implode(', ', array_map(fn($c) => "`{$c}`", array_keys($data)));
$placeholders = implode(', ', array_fill(0, count($data), '?'));
$sql = "INSERT INTO `{$table}` ({$columns}) VALUES ({$placeholders})";
$this->query($sql, array_values($data));
$id = (int) $this->connect()->lastInsertId();
foreach ($this->afterInsertCallbacks as $cb) {
$cb($table, $data, $id);
}
return $id;
}
public function update(string $table, array $data, string $where, array $whereParams = []): int
{
foreach ($this->beforeUpdateCallbacks as $cb) {
$cb($table, $data, null);
}
$set = implode(', ', array_map(fn($c) => "`{$c}` = ?", array_keys($data)));
$sql = "UPDATE `{$table}` SET {$set} WHERE {$where}";
$params = array_merge(array_values($data), $whereParams);
$stmt = $this->query($sql, $params);
$count = $stmt->rowCount();
foreach ($this->afterUpdateCallbacks as $cb) {
$cb($table, $data, null);
}
return $count;
}
public function delete(string $table, string $where, array $whereParams = []): int
{
$sql = "DELETE FROM `{$table}` WHERE {$where}";
$stmt = $this->query($sql, $whereParams);
$count = $stmt->rowCount();
foreach ($this->afterDeleteCallbacks as $cb) {
$cb($table, [], null);
}
return $count;
}
public function beginTransaction(): bool return $this->pdo;
{ }
return $this->connect()->beginTransaction(); \ No newline at end of file
}
public function commit(): bool
{
return $this->connect()->commit();
}
public function rollBack(): bool
{
return $this->connect()->rollBack();
}
public function inTransaction(): bool
{
return $this->connect()->inTransaction();
}
public function lastInsertId(): int
{
return (int) $this->connect()->lastInsertId();
}
public function raw(string $sql): PDOStatement
{
return $this->connect()->query($sql);
}
public function enableQueryLog(): void
{
$this->queryLogEnabled = true;
}
public function disableQueryLog(): void
{
$this->queryLogEnabled = false;
}
public function getQueryLog(): array
{
return $this->queryLog;
}
public function onBeforeInsert(callable $fn): void
{
$this->beforeInsertCallbacks[] = $fn;
}
public function onAfterInsert(callable $fn): void
{
$this->afterInsertCallbacks[] = $fn;
}
public function onBeforeUpdate(callable $fn): void
{
$this->beforeUpdateCallbacks[] = $fn;
}
public function onAfterUpdate(callable $fn): void
{
$this->afterUpdateCallbacks[] = $fn;
}
public function onAfterDelete(callable $fn): void
{
$this->afterDeleteCallbacks[] = $fn;
}
public function tableExists(string $table): bool
{
$stmt = $this->query("SHOW TABLES LIKE ?", [$table]);
return $stmt->rowCount() > 0;
}
}
\ No newline at end of file
...@@ -15,29 +15,27 @@ final class SeederRunner ...@@ -15,29 +15,27 @@ final class SeederRunner
$this->db = $db; $this->db = $db;
} }
public function seedAll(): array public function run(): array
{ {
$this->ensureSeedsTable(); $this->ensureSeedsTable();
$ran = $this->getRanSeeds(); $executed = $this->getExecutedSeeds();
$files = $this->getSeedFiles(); $files = $this->getSeedFiles();
$results = []; $results = [];
foreach ($files as $file) { foreach ($files as $file) {
$name = basename($file, '.php'); $name = basename($file, '.php');
if (in_array($name, $ran)) { if (in_array($name, $executed)) {
continue; continue;
} }
require_once $file; echo " Running: {$name}...\n";
$fnName = str_replace(['-', '.'], '_', $name); $seed = require $file;
if (function_exists($fnName)) {
($fnName)($this->db); if (is_callable($seed)) {
} else { $seed($this->db);
$seed = require $file; } elseif (is_object($seed) && method_exists($seed, 'run')) {
if (is_callable($seed)) { $seed->run($this->db);
$seed($this->db);
}
} }
$this->db->insert('seeds', [ $this->db->insert('seeds', [
...@@ -51,36 +49,50 @@ final class SeederRunner ...@@ -51,36 +49,50 @@ final class SeederRunner
return $results; return $results;
} }
public function runOne(string $name): bool public function runSingle(string $seedName): void
{ {
$this->ensureSeedsTable(); $this->ensureSeedsTable();
$file = $this->getSeedDir() . '/' . $name . '.php'; $files = $this->getSeedFiles();
$targetFile = null;
foreach ($files as $file) {
if (str_contains(basename($file, '.php'), $seedName)) {
$targetFile = $file;
break;
}
}
if (!file_exists($file)) { if (!$targetFile) {
return false; throw new \RuntimeException("Seed not found: {$seedName}");
} }
$seed = require $file; $seed = require $targetFile;
if (is_callable($seed)) { if (is_callable($seed)) {
$seed($this->db); $seed($this->db);
} elseif (is_object($seed) && method_exists($seed, 'run')) {
$seed->run($this->db);
} }
$ran = $this->getRanSeeds(); // Record it (ignore duplicate)
if (!in_array($name, $ran)) { $name = basename($targetFile, '.php');
$existing = $this->db->selectOne("SELECT id FROM seeds WHERE seed = ?", [$name]);
if (!$existing) {
$this->db->insert('seeds', [ $this->db->insert('seeds', [
'seed' => $name, 'seed' => $name,
'executed_at' => date('Y-m-d H:i:s'), 'executed_at' => date('Y-m-d H:i:s'),
]); ]);
} }
return true;
} }
private function ensureSeedsTable(): void private function ensureSeedsTable(): void
{ {
if (!$this->db->tableExists('seeds')) { if (!$this->db->tableExists('seeds')) {
// Seeds table might not exist if migrations haven't created it
// The schema.sql has it, but individual migrations might not
// Create it directly
$this->db->raw(" $this->db->raw("
CREATE TABLE `seeds` ( CREATE TABLE IF NOT EXISTS `seeds` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`seed` VARCHAR(255) NOT NULL, `seed` VARCHAR(255) NOT NULL,
`executed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `executed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
...@@ -90,7 +102,7 @@ final class SeederRunner ...@@ -90,7 +102,7 @@ final class SeederRunner
} }
} }
private function getRanSeeds(): array private function getExecutedSeeds(): array
{ {
$rows = $this->db->select("SELECT seed FROM seeds ORDER BY id"); $rows = $this->db->select("SELECT seed FROM seeds ORDER BY id");
return array_column($rows, 'seed'); return array_column($rows, 'seed');
......
<?php <?php
declare(strict_types=1); declare(strict_types=1);
// Load environment /**
* CLI Entry Point — Migrations, Seeds, and System Commands.
*
* Usage:
* php cli.php migrate Run all pending migrations
* php cli.php migrate:rollback Rollback last batch
* php cli.php migrate:status Show migration status
* php cli.php seed Run all pending seeds
* php cli.php seed:run <Name> Run a specific seed
* php cli.php cron Run background jobs
*/
// Prevent web access
if (PHP_SAPI !== 'cli') {
die('CLI only.');
}
require_once __DIR__ . '/app/Core/Autoloader.php';
\App\Core\Autoloader::register();
// Load .env
$envFile = __DIR__ . '/.env'; $envFile = __DIR__ . '/.env';
if (file_exists($envFile)) { if (file_exists($envFile)) {
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); $lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) { foreach ($lines as $line) {
$line = trim($line); $line = trim($line);
if ($line === '' || str_starts_with($line, '#')) continue; if ($line === '' || $line[0] === '#') continue;
if (str_contains($line, '=')) { if (str_contains($line, '=')) {
[$key, $val] = explode('=', $line, 2); [$key, $value] = explode('=', $line, 2);
$_ENV[trim($key)] = trim($val); $key = trim($key);
$_SERVER[trim($key)] = trim($val); $value = trim($value);
if (!isset($_ENV[$key])) {
$_ENV[$key] = $value;
putenv("{$key}={$value}");
}
} }
} }
} }
require_once __DIR__ . '/app/Core/Autoloader.php'; // Minimal bootstrap — just database, no session/routing
\App\Core\Autoloader::register(); $config = [];
$configDir = __DIR__ . '/config';
if (is_dir($configDir)) {
foreach (glob($configDir . '/*.php') as $configFile) {
$key = basename($configFile, '.php');
$config[$key] = require $configFile;
}
}
$config = new \App\Core\Config(); // Build database config
$config->loadAll(); $dbConfig = [
'host' => $_ENV['DB_HOST'] ?? $config['database']['host'] ?? '127.0.0.1',
'port' => $_ENV['DB_PORT'] ?? $config['database']['port'] ?? '3306',
'name' => $_ENV['DB_NAME'] ?? $config['database']['name'] ?? 'the_club_erp',
'user' => $_ENV['DB_USER'] ?? $config['database']['user'] ?? 'root',
'pass' => $_ENV['DB_PASS'] ?? $config['database']['pass'] ?? '',
'charset' => $config['database']['charset'] ?? 'utf8mb4',
'collation' => $config['database']['collation'] ?? 'utf8mb4_unicode_ci',
];
$db = new \App\Core\Database( $db = new \App\Core\Database($dbConfig);
$config->get('database.host', '127.0.0.1'),
(int) $config->get('database.port', '3306'),
$config->get('database.name', 'the_club_erp'),
$config->get('database.user', 'root'),
$config->get('database.pass', ''),
$config->get('database.charset', 'utf8mb4')
);
$command = $argv[1] ?? 'help'; $command = $argv[1] ?? 'help';
$arg2 = $argv[2] ?? null;
echo "╔══════════════════════════════════════╗\n"; echo "\n";
echo "║ THE CLUB ERP — CLI Tool ║\n";
echo "╚══════════════════════════════════════╝\n\n";
switch ($command) { switch ($command) {
case 'migrate': case 'migrate':
$runner = new \App\Core\Migration\MigrationRunner($db); $runner = new \App\Core\Migration\MigrationRunner($db);
$results = $runner->migrate(); $results = $runner->migrate();
if (empty($results)) { if (empty($results)) {
echo " ✓ Nothing to migrate. All up to date.\n"; echo "✅ Nothing to migrate. All migrations are up to date.\n";
} else { } else {
foreach ($results as $m) { echo "✅ Ran " . count($results) . " migration(s):\n";
echo " ✓ Migrated: {$m}\n"; foreach ($results as $name) {
echo " ✔ {$name}\n";
} }
} }
break; break;
...@@ -54,10 +86,11 @@ switch ($command) { ...@@ -54,10 +86,11 @@ switch ($command) {
$runner = new \App\Core\Migration\MigrationRunner($db); $runner = new \App\Core\Migration\MigrationRunner($db);
$results = $runner->rollback(); $results = $runner->rollback();
if (empty($results)) { if (empty($results)) {
echo " Nothing to rollback.\n"; echo "⚠️ Nothing to rollback.\n";
} else { } else {
foreach ($results as $m) { echo "✅ Rolled back " . count($results) . " migration(s):\n";
echo " ↩ Rolled back: {$m}\n"; foreach ($results as $name) {
echo " ↩ {$name}\n";
} }
} }
break; break;
...@@ -68,56 +101,57 @@ switch ($command) { ...@@ -68,56 +101,57 @@ switch ($command) {
echo str_pad('Migration', 60) . "Status\n"; echo str_pad('Migration', 60) . "Status\n";
echo str_repeat('─', 70) . "\n"; echo str_repeat('─', 70) . "\n";
foreach ($status as $s) { foreach ($status as $s) {
$icon = $s['status'] === 'Ran' ? '' : '○'; $icon = $s['status'] === 'Ran' ? '' : '○';
echo " {$icon} " . str_pad($s['migration'], 58) . $s['status'] . "\n"; echo "{$icon} " . str_pad($s['migration'], 58) . $s['status'] . "\n";
} }
break; break;
case 'seed': case 'seed':
echo "🌱 Running seeds...\n";
$runner = new \App\Core\Seeder\SeederRunner($db); $runner = new \App\Core\Seeder\SeederRunner($db);
$results = $runner->seedAll(); $results = $runner->run();
if (empty($results)) { if (empty($results)) {
echo " ✓ Nothing to seed. All seeds already ran.\n"; echo "✅ Nothing to seed. All seeds already executed.\n";
} else { } else {
foreach ($results as $s) { echo "✅ Ran " . count($results) . " seed(s):\n";
echo " ✓ Seeded: {$s}\n"; foreach ($results as $name) {
echo " 🌱 {$name}\n";
} }
} }
break; break;
case 'seed:run': case 'seed:run':
$name = $argv[2] ?? null; if (!$arg2) {
if (!$name) { echo "❌ Please specify seed name: php cli.php seed:run SeedName\n";
echo " ✗ Please provide seed name.\n";
exit(1); exit(1);
} }
$runner = new \App\Core\Seeder\SeederRunner($db); $runner = new \App\Core\Seeder\SeederRunner($db);
if ($runner->runOne($name)) { $runner->runSingle($arg2);
echo " ✓ Seeded: {$name}\n"; echo "✅ Seed executed: {$arg2}\n";
} else {
echo " ✗ Seed not found: {$name}\n";
}
break; break;
case 'cron': case 'cron':
echo " Running background jobs...\n"; echo "⏰ Running cron jobs...\n";
$cronRunner = __DIR__ . '/cron/runner.php'; $cronRunner = __DIR__ . '/cron/runner.php';
if (file_exists($cronRunner)) { if (file_exists($cronRunner)) {
require $cronRunner; require $cronRunner;
} else { } else {
echo " ✗ Cron runner not found (Phase 15).\n"; echo "⚠️ No cron runner found.\n";
} }
break; break;
case 'help':
default: default:
echo "Available commands:\n"; echo "THE CLUB ERP — CLI Commands\n";
echo " migrate Run all pending migrations\n"; echo "───────────────────────────────────\n";
echo " migrate:rollback Rollback last migration batch\n"; echo " php cli.php migrate Run pending migrations\n";
echo " migrate:status Show migration status\n"; echo " php cli.php migrate:rollback Rollback last batch\n";
echo " seed Run all pending seeds\n"; echo " php cli.php migrate:status Show migration status\n";
echo " seed:run <name> Run a specific seed\n"; echo " php cli.php seed Run all pending seeds\n";
echo " cron Run background jobs\n"; echo " php cli.php seed:run <Name> Run specific seed\n";
echo " php cli.php cron Run background jobs\n";
echo " php cli.php help Show this help\n";
break; break;
} }
echo "\nDone.\n"; echo "\n";
\ No newline at end of file \ No newline at end of file
#!/bin/bash #!/bin/bash
set -e set -e
echo "==========================================" echo "========================================="
echo " THE CLUB ERP — Starting Deployment" echo " THE CLUB ERP — Container Starting"
echo "==========================================" echo "========================================="
# ── DB credentials from environment ── # ── Wait for MySQL to be ready ──
DB_HOST="${DB_HOST:-srv-captain--mysql-db}" echo "⏳ Waiting for MySQL at ${DB_HOST}:${DB_PORT}..."
DB_PORT="${DB_PORT:-3306}" MAX_TRIES=30
DB_NAME="${DB_NAME:-the_club_erp}" COUNT=0
DB_USER="${DB_USER:-root}" until mysql -h"${DB_HOST}" -P"${DB_PORT}" -u"${DB_USER}" -p"${DB_PASS}" -e "SELECT 1" &>/dev/null; do
DB_PASS="${DB_PASS:-Alarcade123#}" COUNT=$((COUNT + 1))
if [ $COUNT -ge $MAX_TRIES ]; then
echo "DB: ${DB_USER}@${DB_HOST}:${DB_PORT}/${DB_NAME}" echo "❌ MySQL not ready after ${MAX_TRIES} attempts. Aborting."
exit 1
# ── Write .env file from Docker env vars ── fi
cat > /var/www/html/.env <<EOF echo " Attempt ${COUNT}/${MAX_TRIES}..."
sleep 2
done
echo "✅ MySQL is ready!"
# ── Create database if it doesn't exist ──
echo "📦 Ensuring database '${DB_NAME}' exists..."
mysql -h"${DB_HOST}" -P"${DB_PORT}" -u"${DB_USER}" -p"${DB_PASS}" -e \
"CREATE DATABASE IF NOT EXISTS \`${DB_NAME}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
echo "✅ Database ready!"
# ── Generate .env from environment variables if not exists ──
ENV_FILE="/var/www/html/.env"
if [ ! -f "$ENV_FILE" ]; then
echo "📝 Generating .env from environment variables..."
cat > "$ENV_FILE" <<EOF
APP_URL=${APP_URL:-http://localhost} APP_URL=${APP_URL:-http://localhost}
APP_DEBUG=${APP_DEBUG:-true} APP_DEBUG=${APP_DEBUG:-false}
APP_ENV=${APP_ENV:-local} APP_ENV=${APP_ENV:-production}
DB_HOST=${DB_HOST} DB_HOST=${DB_HOST:-localhost}
DB_PORT=${DB_PORT} DB_PORT=${DB_PORT:-3306}
DB_NAME=${DB_NAME} DB_NAME=${DB_NAME:-the_club_erp}
DB_USER=${DB_USER} DB_USER=${DB_USER:-root}
DB_PASS=${DB_PASS} DB_PASS=${DB_PASS:-}
SMS_PROVIDER=${SMS_PROVIDER:-} SMS_PROVIDER=${SMS_PROVIDER:-}
SMS_API_KEY=${SMS_API_KEY:-} SMS_API_KEY=${SMS_API_KEY:-}
SMS_SENDER_ID=${SMS_SENDER_ID:-} SMS_SENDER_ID=${SMS_SENDER_ID:-}
EOF EOF
chown www-data:www-data "$ENV_FILE"
chown www-data:www-data /var/www/html/.env echo "✅ .env generated!"
chmod 640 /var/www/html/.env
echo "✅ .env written"
# ── Fix permissions ──
chown -R www-data:www-data /var/www/html/storage 2>/dev/null || true
chmod -R 775 /var/www/html/storage 2>/dev/null || true
# ══════════════════════════════════════
# START APACHE IMMEDIATELY IN BACKGROUND
# so CapRover health check passes while
# we run migrations/seeds
# ══════════════════════════════════════
echo "Starting Apache in background (health check will pass)..."
apache2-foreground &
APACHE_PID=$!
# Give Apache a moment to bind port 80
sleep 2
# Verify Apache is running
if ! kill -0 $APACHE_PID 2>/dev/null; then
echo "❌ Apache failed to start!"
exit 1
fi
echo "✅ Apache is running (PID: $APACHE_PID)"
# ══════════════════════════════════════
# WAIT FOR MYSQL (non-blocking — Apache is already up)
# ══════════════════════════════════════
echo "Waiting for MySQL..."
TRIES=0
MAX_TRIES=60
MYSQL_READY=false
until mysqladmin ping -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASS}" --silent 2>/dev/null; do
TRIES=$((TRIES + 1))
if [ $TRIES -ge $MAX_TRIES ]; then
echo "⚠️ MySQL not reachable after 2 minutes — skipping migrations"
break
fi
sleep 2
done
if [ $TRIES -lt $MAX_TRIES ]; then
MYSQL_READY=true
echo "✅ MySQL is alive"
fi
# ══════════════════════════════════════
# DATABASE SETUP (only if MySQL is reachable)
# ══════════════════════════════════════
if [ "$MYSQL_READY" = true ]; then
# ── Create database ──
echo "Creating database if needed..."
mysql -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASS}" -e "
CREATE DATABASE IF NOT EXISTS \`${DB_NAME}\`
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
" 2>&1 || echo "⚠️ Could not create database (may already exist)"
# ── Create infrastructure tables ──
echo "Creating infrastructure tables..."
mysql -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASS}" "${DB_NAME}" -e "
CREATE TABLE IF NOT EXISTS seeds (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
seed VARCHAR(255) NOT NULL,
executed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uq_seeds_name (seed)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS async_event_queue (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
event_name VARCHAR(200) NOT NULL,
event_data_json JSON NULL,
status VARCHAR(30) NOT NULL DEFAULT 'pending',
attempts INT UNSIGNED NOT NULL DEFAULT 0,
max_attempts INT UNSIGNED NOT NULL DEFAULT 3,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
processed_at TIMESTAMP NULL DEFAULT NULL,
failed_at TIMESTAMP NULL DEFAULT NULL,
error_message TEXT NULL,
INDEX idx_async_events_status (status),
INDEX idx_async_events_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS cron_job_log (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
job_name VARCHAR(200) NOT NULL,
started_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
finished_at TIMESTAMP NULL DEFAULT NULL,
status VARCHAR(30) NOT NULL DEFAULT 'running',
records_processed INT UNSIGNED NOT NULL DEFAULT 0,
error_message TEXT NULL,
execution_time_ms INT UNSIGNED NULL,
INDEX idx_cron_job_name (job_name),
INDEX idx_cron_job_started (started_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
" 2>&1 || echo "⚠️ Infrastructure tables may already exist"
echo "✅ Infrastructure tables ready"
# ══════════════════════════════════════
# RUN MIGRATIONS
# ══════════════════════════════════════
echo ""
echo "=========================================="
echo " RUNNING MIGRATIONS"
echo "=========================================="
cd /var/www/html
MIGRATION_COUNT=$(ls -1 database/migrations/Phase_*.php 2>/dev/null | wc -l)
echo "Found ${MIGRATION_COUNT} migration files"
php cli.php migrate 2>&1 || {
echo "⚠️ Migration command returned error"
TC=$(mysql -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASS}" "${DB_NAME}" -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='${DB_NAME}';" 2>/dev/null || echo "0")
echo "Tables in DB: ${TC}"
}
# ══════════════════════════════════════
# RUN SEEDS
# ══════════════════════════════════════
echo ""
echo "=========================================="
echo " RUNNING SEEDS"
echo "=========================================="
SEED_COUNT=$(ls -1 database/seeds/Phase_*.php 2>/dev/null | wc -l)
echo "Found ${SEED_COUNT} seed files"
php cli.php seed 2>&1 || {
echo "⚠️ Seed command returned error (some may have already run)"
}
# ══════════════════════════════════════
# VERIFY
# ══════════════════════════════════════
echo ""
echo "=========================================="
echo " DEPLOYMENT VERIFICATION"
echo "=========================================="
TC=$(mysql -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASS}" "${DB_NAME}" -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='${DB_NAME}';" 2>/dev/null || echo "?")
echo "Tables: ${TC}"
EC=$(mysql -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASS}" "${DB_NAME}" -N -e "SELECT COUNT(*) FROM employees;" 2>/dev/null || echo "0")
echo "Employees: ${EC}"
RC=$(mysql -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASS}" "${DB_NAME}" -N -e "SELECT COUNT(*) FROM roles;" 2>/dev/null || echo "0")
echo "Roles: ${RC}"
BC=$(mysql -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASS}" "${DB_NAME}" -N -e "SELECT COUNT(*) FROM branches;" 2>/dev/null || echo "0")
echo "Branches: ${BC}"
else else
echo "⚠️ Skipped migrations/seeds — MySQL unavailable" echo "✅ .env already exists, skipping generation."
fi fi
echo "" # ── Run Migrations ──
echo "==========================================" echo "========================================="
echo " ✅ Login: admin / ChangeMe123!" echo "🔄 Running Migrations..."
echo " ⚠️ Password change forced on first login" echo "========================================="
echo "==========================================" cd /var/www/html
echo "" php cli.php migrate 2>&1 || {
echo "⚠️ Migration had issues, but continuing..."
}
echo "✅ Migrations complete!"
# ── Run Seeds ──
echo "========================================="
echo "🌱 Running Seeds..."
echo "========================================="
php cli.php seed 2>&1 || {
echo "⚠️ Seeding had issues, but continuing..."
}
echo "✅ Seeds complete!"
# ── Fix permissions after migrations/seeds ──
chown -R www-data:www-data /var/www/html/storage 2>/dev/null || true
chmod -R 775 /var/www/html/storage 2>/dev/null || true
# ══════════════════════════════════════ echo "========================================="
# WAIT FOR APACHE (foreground — keeps container alive) echo "🚀 Starting Apache..."
# ══════════════════════════════════════ echo "========================================="
echo "Apache is serving on port 80. Waiting for process..." exec apache2-foreground
wait $APACHE_PID \ No newline at end of file
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment