<?php
declare(strict_types=1);
if (session_status() !== PHP_SESSION_ACTIVE) {
    ini_set('session.use_strict_mode', '1'); ini_set('session.cookie_httponly', '1'); ini_set('session.cookie_secure', (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? '1' : '0'); ini_set('session.cookie_samesite', 'Lax'); session_name('ABOXCONSOLESESSID'); session_start();
}
header('X-Frame-Options: SAMEORIGIN'); header('X-Content-Type-Options: nosniff'); header('Referrer-Policy: strict-origin-when-cross-origin'); header('Permissions-Policy: camera=(), microphone=(), geolocation=()'); header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://fonts.googleapis.com; font-src 'self' https://cdnjs.cloudflare.com https://fonts.gstatic.com data:; img-src 'self' data: https:; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self'");
$betaConfig = '/home/inv11/public_html/aboxbeta.inv11.in/configuration.php'; if (!file_exists($betaConfig)) { http_response_code(500); exit('Database configuration missing.'); } require_once $betaConfig; $cfg = new JConfig(); $con = new mysqli($cfg->host, $cfg->user, $cfg->password, $cfg->db); if ($con->connect_errno) { http_response_code(500); exit('Database connection failed.'); } $con->set_charset('utf8mb4');
define('APP_NAME', 'Attendance Box'); define('APP_ROOT', dirname(__DIR__));
function h($value): string { return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); }
function redirect(string $to): never { header('Location: '.$to); exit; }
function client_ip(): string { return substr($_SERVER['REMOTE_ADDR'] ?? '', 0, 64); }
function csrf_token(): string { if (empty($_SESSION['_csrf'])) $_SESSION['_csrf'] = bin2hex(random_bytes(32)); return $_SESSION['_csrf']; }
function verify_csrf(?string $token): bool { return is_string($token) && isset($_SESSION['_csrf']) && hash_equals($_SESSION['_csrf'], $token); }
function is_logged_in(): bool { return !empty($_SESSION['admin_user']['id']); }
function current_user(): ?array { return $_SESSION['admin_user'] ?? null; }
function audit_log(string $action, ?string $entity = null, ?string $entityId = null, ?string $detail = null): void { global $con; $uid = current_user()['id'] ?? null; $ip = client_ip(); $stmt = $con->prepare('INSERT INTO jos_admin_audit_logs (user_id, action, entity, entity_id, detail, ip_address) VALUES (?,?,?,?,?,?)'); if ($stmt) { $stmt->bind_param('isssss', $uid, $action, $entity, $entityId, $detail, $ip); $stmt->execute(); $stmt->close(); } }
function log_login_attempt(?int $uid, string $username, bool $success, string $reason): void { global $con; $ip = client_ip(); $ua = substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 255); $ok = $success ? 1 : 0; $stmt = $con->prepare('INSERT INTO jos_admin_login_logs (username, user_id, success, reason, ip_address, user_agent) VALUES (?,?,?,?,?,?)'); if ($stmt) { $stmt->bind_param('siisss', $username, $uid, $ok, $reason, $ip, $ua); $stmt->execute(); $stmt->close(); } }
function remember_session(int $uid): void { global $con; $hash = hash('sha256', session_id()); $ip = client_ip(); $ua = substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 255); $exp = date('Y-m-d H:i:s', time()+86400); $stmt = $con->prepare('INSERT INTO jos_admin_user_sessions (user_id, session_hash, ip_address, user_agent, last_seen_at, expires_at) VALUES (?,?,?,?,NOW(),?) ON DUPLICATE KEY UPDATE last_seen_at=NOW(), expires_at=VALUES(expires_at), revoked_at=NULL'); if ($stmt) { $stmt->bind_param('issss', $uid, $hash, $ip, $ua, $exp); $stmt->execute(); $stmt->close(); } }