function log_activity($action, $meta=''){
global $pdo;
try{
$uid = $_SESSION['admin_id'] ?? null;
$ip = $_SERVER['REMOTE_ADDR'] ?? '';
$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
$stmt = $pdo->prepare("INSERT INTO activity_log (user_id, action, meta, ip, user_agent) VALUES (?,?,?,?,?)");
$stmt->execute([$uid, $action, $meta, $ip, $ua]);
} catch(Exception $e){}
}
function has_role($role){
global $pdo;
$uid = $_SESSION['admin_id'] ?? 0;
if(!$uid) return false;
$stmt = $pdo->prepare("SELECT r.name FROM roles r JOIN user_roles ur ON ur.role_id=r.id WHERE ur.user_id=?");
$stmt->execute([$uid]);
$roles = array_map(fn($x)=>$x['name'], $stmt->fetchAll());
return in_array($role, $roles, true);
}
function require_role($roles){
if(!is_array($roles)) $roles = [$roles];
foreach($roles as $r){
if(has_role($r)) return;
}
http_response_code(403);
echo "
403 Forbidden
";
exit;
}
/* --------------------------------------------------------------------------
| CSRF Protection
|-------------------------------------------------------------------------- */
function csrf_token() {
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
function csrf_field() {
echo '';
}
function csrf_verify() {
$sent = $_POST['csrf_token'] ?? '';
$sess = $_SESSION['csrf_token'] ?? '';
if (!$sent || !$sess || !hash_equals($sess, $sent)) {
http_response_code(403);
echo '403 Forbidden (CSRF)';
exit;
}
}
/* --------------------------------------------------------------------------
| Simple Page Cache (optional)
|-------------------------------------------------------------------------- */
function cache_page($key, $ttl_seconds = 60) {
$dir = __DIR__ . '/../storage/cache_pages';
if (!is_dir($dir)) @mkdir($dir, 0755, true);
$file = $dir . '/' . preg_replace('~[^a-zA-Z0-9_\-]~','_', $key) . '.html';
if (file_exists($file) && (time() - filemtime($file)) < $ttl_seconds) {
readfile($file);
exit;
}
ob_start();
register_shutdown_function(function() use ($file) {
$out = ob_get_contents();
if ($out !== false) @file_put_contents($file, $out);
ob_end_flush();
});
}
/* --------------------------------------------------------------------------
| Brute-force protection helpers
|-------------------------------------------------------------------------- */
function login_attempts_bootstrap() {
global $pdo;
try {
$pdo->exec("CREATE TABLE IF NOT EXISTS login_attempts (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(190) NULL,
ip VARCHAR(64) NULL,
success TINYINT(1) NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
KEY idx_user_ip_time (username, ip, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
} catch (Throwable $e) {}
}
function login_is_blocked($username, $ip) {
global $pdo;
login_attempts_bootstrap();
$max = defined('LOGIN_MAX_ATTEMPTS') ? (int)LOGIN_MAX_ATTEMPTS : 5;
$mins = defined('LOGIN_BLOCK_MINUTES') ? (int)LOGIN_BLOCK_MINUTES : 15;
try {
$stmt = $pdo->prepare("SELECT COUNT(*) FROM login_attempts
WHERE success=0 AND created_at >= (NOW() - INTERVAL ? MINUTE)
AND (ip=? OR (username IS NOT NULL AND username=?))");
$stmt->execute([$mins, $ip, $username]);
return ((int)$stmt->fetchColumn()) >= $max;
} catch (Throwable $e) {
return false;
}
}
function login_record_attempt($username, $ip, $success) {
global $pdo;
login_attempts_bootstrap();
try {
$stmt = $pdo->prepare("INSERT INTO login_attempts (username, ip, success) VALUES (?,?,?)");
$stmt->execute([$username ?: null, $ip ?: null, $success ? 1 : 0]);
} catch (Throwable $e) {}
}
function require_any_role($roles) {
if (!is_array($roles)) $roles = [$roles];
foreach ($roles as $r) if (has_role($r)) return;
http_response_code(403);
echo "403 Forbidden";
exit;
}
/* --------------------------------------------------------------------------
| Settings (DB key/value)
|-------------------------------------------------------------------------- */
function settings_bootstrap() {
global $pdo;
try {
$pdo->exec("CREATE TABLE IF NOT EXISTS settings (
`key` VARCHAR(190) PRIMARY KEY,
`value` TEXT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
} catch (Throwable $e) {}
}
function setting($key, $default = '') {
global $pdo;
settings_bootstrap();
try {
$stmt = $pdo->prepare("SELECT value FROM settings WHERE `key`=? LIMIT 1");
$stmt->execute([$key]);
$v = $stmt->fetchColumn();
if ($v === false || $v === null || $v === '') return $default;
return $v;
} catch (Throwable $e) {
return $default;
}
}
function set_setting($key, $value) {
global $pdo;
settings_bootstrap();
try {
$stmt = $pdo->prepare("INSERT INTO settings (`key`,`value`) VALUES (?,?)
ON DUPLICATE KEY UPDATE `value`=VALUES(`value`)");
$stmt->execute([$key, $value]);
return true;
} catch (Throwable $e) {
return false;
}
}
روابي
روابي
حلول أمان متكاملة بمعايير احترافية
شركاؤنا في النجاح
لا توجد بيانات بعد.