SESSION_LIFETIME, 'path' => '/', 'secure' => false, 'httponly' => true, 'samesite' => 'Lax' ]); session_start(); } } public static function attempt(string $username, string $password): bool { $db = Database::getInstance(); $user = $db->fetchOne( "SELECT u.*, r.slug as role_slug, r.nama as role_nama FROM users u JOIN roles r ON u.id_role = r.id WHERE (u.username = ? OR u.email = ?) AND u.is_active = 1", [$username, $username] ); if ($user && password_verify($password, $user['password'])) { // Update last login $db->update('users', ['last_login' => date('Y-m-d H:i:s')], 'id = ?', [$user['id']]); // Store in session $_SESSION['user_id'] = $user['id']; $_SESSION['user_name'] = $user['nama']; $_SESSION['user_role'] = $user['role_slug']; $_SESSION['user_email'] = $user['email']; $_SESSION['id_kelurahan'] = $user['id_kelurahan']; $_SESSION['login_time'] = time(); // Log activity self::logActivity($user['id'], $user['nama'], $user['role_slug'], 'Login', 'auth', 'Login ke sistem'); return true; } return false; } public static function check(): bool { return isset($_SESSION['user_id']) && !empty($_SESSION['user_id']); } public static function user(): ?array { if (!self::check()) return null; if (self::$user !== null) return self::$user; $db = Database::getInstance(); self::$user = $db->fetchOne( "SELECT u.*, r.slug as role_slug, r.nama as role_nama, k.nama as kelurahan_nama, kec.nama as kecamatan_nama, ri.id as rumah_ibadah_id, ri.nama as rumah_ibadah_nama, ri.lat as ri_lat, ri.lng as ri_lng, ri.radius_meter as ri_radius FROM users u JOIN roles r ON u.id_role = r.id LEFT JOIN kelurahan k ON u.id_kelurahan = k.id LEFT JOIN kecamatan kec ON k.id_kecamatan = kec.id LEFT JOIN rumah_ibadah ri ON ri.id_user = u.id WHERE u.id = ?", [$_SESSION['user_id']] ); return self::$user; } public static function id(): ?int { return self::check() ? (int)$_SESSION['user_id'] : null; } public static function role(): ?string { return $_SESSION['user_role'] ?? null; } public static function is(string ...$roles): bool { return in_array(self::role(), $roles); } public static function logout(): void { if (self::check()) { self::logActivity( $_SESSION['user_id'], $_SESSION['user_name'], $_SESSION['user_role'], 'Logout', 'auth', 'Logout dari sistem' ); } $_SESSION = []; session_destroy(); self::$user = null; } public static function require(string ...$roles): void { self::init(); if (!self::check()) { header('Location: ' . APP_URL . '/login'); exit; } if (!empty($roles) && !self::is(...$roles)) { header('Location: ' . APP_URL . '/forbidden'); exit; } } public static function redirectIfLoggedIn(): void { if (self::check()) { header('Location: ' . self::getDashboardUrl()); exit; } } public static function getDashboardUrl(): string { return match(self::role()) { 'superadmin' => APP_URL . '/admin/dashboard', 'verifikator' => APP_URL . '/verifikator/dashboard', 'rumah_ibadah' => APP_URL . '/rumah-ibadah/dashboard', 'dinsos' => APP_URL . '/dinsos/dashboard', 'walikota' => APP_URL . '/walikota/dashboard', default => APP_URL . '/' }; } public static function logActivity(int $userId, string $namaUser, string $role, string $aksi, string $modul, string $deskripsi = ''): void { try { $db = Database::getInstance(); $db->insert('aktivitas_sistem', [ 'id_user' => $userId, 'nama_user' => $namaUser, 'role' => $role, 'aksi' => $aksi, 'modul' => $modul, 'deskripsi' => $deskripsi, 'ip_address' => $_SERVER['REMOTE_ADDR'] ?? '', 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '' ]); } catch (Exception $e) { // Silent fail for logging } } }