/home/devscfvi/AblePro/auth_check.php
<?php
// ============================================================
// AUTH MIDDLEWARE
// ============================================================

if (session_status() === PHP_SESSION_NONE) {
    session_set_cookie_params([
        'lifetime' => 0,
        'path'     => '/',
        'secure'   => false,   // set true when on HTTPS
        'httponly' => true,
        'samesite' => 'Lax',
    ]);
    session_start();
}

// ── Cache the raw request body once so it can be read repeatedly ──
if (!isset($GLOBALS['_request_body'])) {
    $GLOBALS['_request_body'] = file_get_contents('php://input');
}

// ── Public actions — no login required ──────────────────────
$PUBLIC_ACTIONS = [
    'login', 'register', 'requestPasswordReset',
    'resetPassword', 'checkSession'
];

function getCurrentUserId(): ?int {
    return isset($_SESSION['user_id']) && $_SESSION['user_id'] > 0
        ? (int)$_SESSION['user_id']
        : null;
}

function requireAuth(): int {
    global $PUBLIC_ACTIONS;

    // Read action from GET param or cached request body
    $action = $_GET['action'] ?? '';
    if (empty($action)) {
        $body   = json_decode($GLOBALS['_request_body'] ?? '', true);
        $action = $body['action'] ?? '';
    }

    if (in_array($action, $PUBLIC_ACTIONS)) {
        return 0; // public — skip auth check
    }

    $uid = getCurrentUserId();
    if (!$uid) {
        header('Content-Type: application/json');
        echo json_encode([
            'success'  => false,
            'error'    => 'Unauthenticated',
            'redirect' => 'pages/login-v1.html'
        ]);
        exit;
    }
    return $uid;
}

// ── Self-healing: create auth tables if missing ──────────────
function ensureAuthTables(\PDO $pdo): void {
    $pdo->exec("
        CREATE TABLE IF NOT EXISTS `users` (
          `id`            INT(11)      NOT NULL AUTO_INCREMENT,
          `first_name`    VARCHAR(100) NOT NULL DEFAULT '',
          `last_name`     VARCHAR(100) NOT NULL DEFAULT '',
          `email`         VARCHAR(255) NOT NULL,
          `password_hash` VARCHAR(255) NOT NULL,
          `trade_mode`    ENUM('crypto','stocks') NOT NULL DEFAULT 'crypto',
          `trial_ends_at` DATETIME DEFAULT NULL,
          `subscribed`    TINYINT(1) NOT NULL DEFAULT 0,
          `created_at`    TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
          `updated_at`    TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
          PRIMARY KEY (`id`),
          UNIQUE KEY `idx_email` (`email`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
    ");
    $pdo->exec("
        CREATE TABLE IF NOT EXISTS `password_resets` (
          `id`         INT(11)      NOT NULL AUTO_INCREMENT,
          `user_id`    INT(11)      NOT NULL,
          `token_hash` VARCHAR(255) NOT NULL,
          `expires_at` DATETIME     NOT NULL,
          `used`       TINYINT(1)   NOT NULL DEFAULT 0,
          `created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
          PRIMARY KEY (`id`),
          KEY `idx_token` (`token_hash`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
    ");
    // Add user_id columns silently (ignore if already exist)
    $tables = [
        'crypto_trades', 'trading_goals', 'trading_journal_entries',
        'budget_transactions', 'user_budget', 'favorite_coins'
    ];
    foreach ($tables as $tbl) {
        try {
            $pdo->exec("ALTER TABLE `{$tbl}` ADD COLUMN `user_id` INT(11) DEFAULT NULL");
        } catch (\PDOException $e) { /* already exists — ignore */ }
    }
}