View file admin/logs.php

File size: 4.54Kb
<?php
/**
 * CMS: LaiCMS (v1.0 Edition 2026)
 * File: admin/logs.php
 * Оптимизация: Ultra-Compact Event Stream
 */

require_once '../system/db.php';
require_once '../system/functions.php';

if (!isAdmin()) { die("403 Forbidden"); }

$log_file = '../system/logs.json';

// Очистка логов (Safe)
if (isset($_POST['clear_logs']) && check_csrf($_POST['csrf_token'])) {
    file_put_contents($log_file, json_encode([]));
    header("Location: logs.php?status=cleared"); exit;
}

// Чтение логов
$logs = file_exists($log_file) ? (json_decode(file_get_contents($log_file), true) ?: []) : [];
$logs = array_reverse($logs);

$page_title = "Logs — LaiCMS";
include '../system/header.php';
?>

<style>
    .logs-wrap { padding: 10px; max-width: 100%; }
    
    /* Компактная лента событий */
    .log-item { 
        background: var(--pico-card-background-color); 
        border: 1px solid var(--pico-muted-border-color);
        border-radius: 12px;
        padding: 10px;
        margin-bottom: 8px;
        display: flex;
        flex-direction: column;
        gap: 5px;
        transition: 0.2s;
    }
    .log-item:hover { border-color: var(--pico-primary); }

    /* Мета-данные в одну строку */
    .log-meta { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid rgba(255,255,255,0.05); padding-bottom: 5px; }
    
    /* Типы событий */
    .type-tag { font-size: 0.6rem; font-weight: 800; padding: 2px 6px; border-radius: 4px; text-transform: uppercase; color: #fff; }
    .type-error { background: #e74c3c; box-shadow: 0 0 8px #e74c3c44; }
    .type-security { background: #9b59b6; }
    .type-success { background: #2ecc71; }
    .type-info { background: #3498db; }

    .log-msg { font-size: 0.8rem; line-height: 1.4; color: var(--pico-contrast); }
    .log-footer { display: flex; gap: 10px; font-size: 0.7rem; opacity: 0.6; }

    @media (min-width: 768px) {
        .log-item { flex-direction: row; align-items: center; gap: 15px; }
        .log-meta { border-bottom: none; padding-bottom: 0; min-width: 180px; }
        .log-msg { flex: 1; }
    }
</style>

<div class="logs-wrap">
    <div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 1.5rem;">
        <hgroup style="margin:0;">
            <h3 style="margin:0;"><i class="fa-solid fa-receipt"></i> Журнал</h3>
            <p style="font-size:0.7rem; opacity:0.7;">Всего записей: <?= count($logs) ?></p>
        </hgroup>
        
        <form method="POST" onsubmit="return confirm('Удалить всю историю?')">
            <input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
            <button type="submit" name="clear_logs" class="outline secondary btn-sm" style="margin:0;">
                <i class="fa-solid fa-eraser"></i> <span class="hide-mobile">Очистить</span>
            </button>
        </form>
    </div>

    <input type="text" id="logSearch" placeholder="Фильтр по IP, юзеру или тексту..." onkeyup="filterLogs()" style="margin-bottom: 1rem; font-size: 0.85rem;">

    <div id="logsContainer">
        <?php if (empty($logs)): ?>
            <article style="text-align:center;">Журнал пуст</article>
        <?php else: ?>
            <?php foreach ($logs as $entry): 
                $type = $entry['type'] ?? 'info';
            ?>
            <div class="log-item" data-text="<?= strtolower(_e($entry['user'].$entry['message'].$entry['ip'])) ?>">
                <div class="log-meta">
                    <span class="type-tag type-<?= $type ?>"><?= $type ?></span>
                    <small style="font-size: 0.65rem; opacity: 0.5;"><?= date('H:i:s d.m', strtotime($entry['date'])) ?></small>
                </div>
                
                <div class="log-msg">
                    <strong><?= _e($entry['user'] ?? 'Guest') ?>:</strong> <?= _e($entry['message']) ?>
                </div>

                <div class="log-footer">
                    <span><i class="fa-solid fa-network-wired"></i> <?= _e($entry['ip']) ?></span>
                </div>
            </div>
            <?php endforeach; ?>
        <?php endif; ?>
    </div>
</div>



<script>
function filterLogs() {
    const query = document.getElementById("logSearch").value.toLowerCase();
    document.querySelectorAll(".log-item").forEach(item => {
        item.style.display = item.getAttribute("data-text").includes(query) ? "flex" : "none";
    });
}
</script>

<?php include '../system/footer.php'; ?>