Fix CSS variables, missing utility classes, API hardening, and audit log UX
- base.css: add --lt-border/--lt-surface aliases so dashboard.css respects
theme instead of using hardcoded fallback colors
- base.css: add lt-select-sm/lt-input-sm compact size variants (used in 15+
places), lt-msg-danger alias for lt-msg-error, lt-form-hint--warn,
lt-font-mono utility class
- audit_log.php: cap ?limit= at 500 to prevent DoS via oversized queries
- ApiKeysView.php: replace deprecated execCommand('copy') with lt.copy();
add integer casts on api_key_id in id attr and data-id
- AuditLogView.php: rebuild pagination with windowed prev/next/ellipsis
pattern matching DashboardView; integer cast on user_id select option
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+2
-2
@@ -71,8 +71,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
|||||||
// Normal JSON response for filtered logs
|
// Normal JSON response for filtered logs
|
||||||
try {
|
try {
|
||||||
// Get pagination parameters
|
// Get pagination parameters
|
||||||
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
$page = max(1, (int)($_GET['page'] ?? 1));
|
||||||
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 50;
|
$limit = min(500, max(1, (int)($_GET['limit'] ?? 50)));
|
||||||
$offset = ($page - 1) * $limit;
|
$offset = ($page - 1) * $limit;
|
||||||
|
|
||||||
// Build filters
|
// Build filters
|
||||||
|
|||||||
+21
-2
@@ -85,6 +85,8 @@
|
|||||||
--lt-cyan: var(--accent-cyan);
|
--lt-cyan: var(--accent-cyan);
|
||||||
--lt-success: var(--accent-green);
|
--lt-success: var(--accent-green);
|
||||||
--lt-text-primary: var(--accent-green);
|
--lt-text-primary: var(--accent-green);
|
||||||
|
--lt-border: var(--border-color);
|
||||||
|
--lt-surface: var(--bg-card);
|
||||||
|
|
||||||
/* Legacy aliases — keeps all existing component HTML working */
|
/* Legacy aliases — keeps all existing component HTML working */
|
||||||
--terminal-green: var(--accent-green);
|
--terminal-green: var(--accent-green);
|
||||||
@@ -966,11 +968,26 @@ select option:checked {
|
|||||||
clip-path: none;
|
clip-path: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Compact size variants for inline filter bars and tight layouts */
|
||||||
|
.lt-select-sm {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
padding: 0.25rem 1.6rem 0.25rem 0.5rem;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
.lt-input-sm {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.lt-form-hint {
|
.lt-form-hint {
|
||||||
font-size: 0.63rem;
|
font-size: 0.63rem;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
}
|
}
|
||||||
|
.lt-form-hint--warn { color: var(--accent-amber); }
|
||||||
|
|
||||||
|
.lt-font-mono { font-family: var(--font-mono); }
|
||||||
|
|
||||||
/* Search */
|
/* Search */
|
||||||
.lt-search { position: relative; }
|
.lt-search { position: relative; }
|
||||||
@@ -1586,12 +1603,14 @@ select option:checked {
|
|||||||
}
|
}
|
||||||
.lt-msg::before { flex-shrink: 0; font-weight: 700; }
|
.lt-msg::before { flex-shrink: 0; font-weight: 700; }
|
||||||
|
|
||||||
.lt-msg-error { color: var(--accent-red); background: var(--accent-red-dim); border-left-color: var(--accent-red); }
|
.lt-msg-error,
|
||||||
|
.lt-msg-danger { color: var(--accent-red); background: var(--accent-red-dim); border-left-color: var(--accent-red); }
|
||||||
.lt-msg-success { color: var(--accent-green); background: var(--accent-green-dim); border-left-color: var(--accent-green); }
|
.lt-msg-success { color: var(--accent-green); background: var(--accent-green-dim); border-left-color: var(--accent-green); }
|
||||||
.lt-msg-warning { color: var(--accent-amber); background: var(--accent-amber-dim); border-left-color: var(--accent-amber); }
|
.lt-msg-warning { color: var(--accent-amber); background: var(--accent-amber-dim); border-left-color: var(--accent-amber); }
|
||||||
.lt-msg-info { color: var(--accent-cyan); background: var(--accent-cyan-dim); border-left-color: var(--accent-cyan); }
|
.lt-msg-info { color: var(--accent-cyan); background: var(--accent-cyan-dim); border-left-color: var(--accent-cyan); }
|
||||||
|
|
||||||
.lt-msg-error::before { content: '✗'; }
|
.lt-msg-error::before,
|
||||||
|
.lt-msg-danger::before { content: '✗'; }
|
||||||
.lt-msg-success::before { content: '✓'; }
|
.lt-msg-success::before { content: '✓'; }
|
||||||
.lt-msg-warning::before { content: '!'; }
|
.lt-msg-warning::before { content: '!'; }
|
||||||
.lt-msg-info::before { content: 'i'; }
|
.lt-msg-info::before { content: 'i'; }
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ include __DIR__ . '/../../views/layout_header.php';
|
|||||||
<tr><td colspan="8" class="lt-empty">No API keys found. Generate one above.</td></tr>
|
<tr><td colspan="8" class="lt-empty">No API keys found. Generate one above.</td></tr>
|
||||||
<?php else: foreach ($apiKeys as $key): ?>
|
<?php else: foreach ($apiKeys as $key): ?>
|
||||||
<?php $expired = $key['expires_at'] && strtotime($key['expires_at']) < time(); ?>
|
<?php $expired = $key['expires_at'] && strtotime($key['expires_at']) < time(); ?>
|
||||||
<tr id="key-row-<?= $key['api_key_id'] ?>">
|
<tr id="key-row-<?= (int)$key['api_key_id'] ?>">
|
||||||
<td data-label="Name"><strong><?= htmlspecialchars($key['key_name']) ?></strong></td>
|
<td data-label="Name"><strong><?= htmlspecialchars($key['key_name']) ?></strong></td>
|
||||||
<td data-label="Prefix" class="lt-text-xs"><code><?= htmlspecialchars($key['key_prefix']) ?>…</code></td>
|
<td data-label="Prefix" class="lt-text-xs"><code><?= htmlspecialchars($key['key_prefix']) ?>…</code></td>
|
||||||
<td data-label="Created By" class="lt-text-xs"><?= htmlspecialchars($key['display_name'] ?? $key['username'] ?? 'Unknown') ?></td>
|
<td data-label="Created By" class="lt-text-xs"><?= htmlspecialchars($key['display_name'] ?? $key['username'] ?? 'Unknown') ?></td>
|
||||||
@@ -96,7 +96,7 @@ include __DIR__ . '/../../views/layout_header.php';
|
|||||||
<td data-label="Actions">
|
<td data-label="Actions">
|
||||||
<?php if ($key['is_active']): ?>
|
<?php if ($key['is_active']): ?>
|
||||||
<button type="button" class="lt-btn lt-btn-sm lt-btn-danger"
|
<button type="button" class="lt-btn lt-btn-sm lt-btn-danger"
|
||||||
data-action="revoke-key" data-id="<?= $key['api_key_id'] ?>">REVOKE</button>
|
data-action="revoke-key" data-id="<?= (int)$key['api_key_id'] ?>">REVOKE</button>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<span class="lt-text-muted lt-text-xs">—</span>
|
<span class="lt-text-muted lt-text-xs">—</span>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
@@ -173,10 +173,12 @@ document.getElementById('generateKeyForm').addEventListener('submit', function (
|
|||||||
});
|
});
|
||||||
|
|
||||||
function copyApiKey() {
|
function copyApiKey() {
|
||||||
var input = document.getElementById('newKeyValue');
|
var val = document.getElementById('newKeyValue').value;
|
||||||
input.select();
|
lt.copy(val).then(function () {
|
||||||
document.execCommand('copy');
|
|
||||||
lt.toast.success('Copied to clipboard!');
|
lt.toast.success('Copied to clipboard!');
|
||||||
|
}).catch(function () {
|
||||||
|
lt.toast.error('Copy failed — select the key manually');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function revokeKey(keyId) {
|
function revokeKey(keyId) {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ include __DIR__ . '/../../views/layout_header.php';
|
|||||||
<select name="user_id" id="user_id" class="lt-select lt-select-sm">
|
<select name="user_id" id="user_id" class="lt-select lt-select-sm">
|
||||||
<option value="">All Users</option>
|
<option value="">All Users</option>
|
||||||
<?php if (isset($users)): foreach ($users as $u): ?>
|
<?php if (isset($users)): foreach ($users as $u): ?>
|
||||||
<option value="<?= $u['user_id'] ?>" <?= ($filters['user_id'] ?? '') == $u['user_id'] ? 'selected' : '' ?>>
|
<option value="<?= (int)$u['user_id'] ?>" <?= ($filters['user_id'] ?? '') == $u['user_id'] ? 'selected' : '' ?>>
|
||||||
<?= htmlspecialchars($u['display_name'] ?? $u['username']) ?>
|
<?= htmlspecialchars($u['display_name'] ?? $u['username']) ?>
|
||||||
</option>
|
</option>
|
||||||
<?php endforeach; endif ?>
|
<?php endforeach; endif ?>
|
||||||
@@ -112,13 +112,35 @@ include __DIR__ . '/../../views/layout_header.php';
|
|||||||
<div class="lt-pagination" role="navigation">
|
<div class="lt-pagination" role="navigation">
|
||||||
<?php
|
<?php
|
||||||
$params = $_GET;
|
$params = $_GET;
|
||||||
for ($i = 1; $i <= min($totalPages, 10); $i++) {
|
$start = max(1, $page - 2);
|
||||||
|
$end = min($totalPages, $page + 2);
|
||||||
|
if ($page > 1) {
|
||||||
|
$params['page'] = $page - 1;
|
||||||
|
$pUrl = htmlspecialchars('?' . http_build_query($params), ENT_QUOTES, 'UTF-8');
|
||||||
|
echo '<a href="' . $pUrl . '" class="lt-btn lt-btn-sm" aria-label="Previous page">«</a> ';
|
||||||
|
}
|
||||||
|
if ($start > 1) {
|
||||||
|
$params['page'] = 1;
|
||||||
|
echo '<a href="' . htmlspecialchars('?' . http_build_query($params), ENT_QUOTES, 'UTF-8') . '" class="lt-btn lt-btn-sm">1</a> ';
|
||||||
|
if ($start > 2) echo '<span class="lt-text-muted lt-text-xs">…</span>';
|
||||||
|
}
|
||||||
|
for ($i = $start; $i <= $end; $i++) {
|
||||||
$params['page'] = $i;
|
$params['page'] = $i;
|
||||||
$url = htmlspecialchars('?' . http_build_query($params), ENT_QUOTES, 'UTF-8');
|
$url = htmlspecialchars('?' . http_build_query($params), ENT_QUOTES, 'UTF-8');
|
||||||
$class = ($i == $page) ? ' lt-btn-primary' : '';
|
$class = ($i == $page) ? ' lt-btn-primary' : '';
|
||||||
echo "<a href='$url' class='lt-btn lt-btn-sm$class'>$i</a> ";
|
$curr = ($i == $page) ? ' aria-current="page"' : '';
|
||||||
|
echo '<a href="' . $url . '" class="lt-btn lt-btn-sm' . $class . '"' . $curr . '>' . $i . '</a> ';
|
||||||
|
}
|
||||||
|
if ($end < $totalPages) {
|
||||||
|
if ($end < $totalPages - 1) echo '<span class="lt-text-muted lt-text-xs">…</span>';
|
||||||
|
$params['page'] = $totalPages;
|
||||||
|
echo '<a href="' . htmlspecialchars('?' . http_build_query($params), ENT_QUOTES, 'UTF-8') . '" class="lt-btn lt-btn-sm">' . $totalPages . '</a> ';
|
||||||
|
}
|
||||||
|
if ($page < $totalPages) {
|
||||||
|
$params['page'] = $page + 1;
|
||||||
|
$nUrl = htmlspecialchars('?' . http_build_query($params), ENT_QUOTES, 'UTF-8');
|
||||||
|
echo '<a href="' . $nUrl . '" class="lt-btn lt-btn-sm" aria-label="Next page">»</a>';
|
||||||
}
|
}
|
||||||
if ($totalPages > 10) echo '<span class="lt-text-muted lt-text-xs">…</span>';
|
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|||||||
Reference in New Issue
Block a user