+125
-79
@@ -265,34 +265,30 @@ pre.raw-line { background: var(--bs-tertiary-bg); padding: .75rem; border-radius
|
||||
<div class="page-section" id="page-settings">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h5 class="mb-0"><i class="bi bi-gear me-2"></i>Settings</h5>
|
||||
<button class="btn btn-outline-secondary btn-sm" onclick="loadSettings()"><i class="bi bi-arrow-clockwise"></i></button>
|
||||
</div>
|
||||
<div class="row g-3">
|
||||
<!-- Left column -->
|
||||
<div class="col-md-6">
|
||||
|
||||
<!-- Database status -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header"><i class="bi bi-info-circle me-1"></i>System Info</div>
|
||||
<div class="card-body">
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-4">Health</dt><dd class="col-sm-8"><span id="sysHealth" class="badge bg-secondary">checking...</span></dd>
|
||||
<dt class="col-sm-4">SQLite</dt><dd class="col-sm-8"><span id="sysSqlite" class="badge bg-secondary">checking...</span></dd>
|
||||
<dt class="col-sm-4">ClickHouse</dt><dd class="col-sm-8"><span id="sysClickhouse" class="badge bg-secondary">checking...</span></dd>
|
||||
<dt class="col-sm-4">DB Size</dt><dd class="col-sm-8"><span id="sysDbSize">—</span></dd>
|
||||
<dt class="col-sm-4">Auth Server</dt><dd class="col-sm-8"><a href="https://auth.jakach.ch" target="_blank">auth.jakach.ch</a></dd>
|
||||
<dt class="col-sm-4">Logged in as</dt><dd class="col-sm-8" id="settingsUser">—</dd>
|
||||
</dl>
|
||||
<div class="card-header"><i class="bi bi-database me-1"></i>Database</div>
|
||||
<div class="card-body py-2">
|
||||
<div class="row g-2">
|
||||
<div class="col-6"><small class="text-secondary d-block">SQLite</small><span id="sysSqlite" class="badge bg-secondary">checking...</span></div>
|
||||
<div class="col-6"><small class="text-secondary d-block">ClickHouse</small><span id="sysClickhouse" class="badge bg-secondary">checking...</span></div>
|
||||
<div class="col-6"><small class="text-secondary d-block">Health</small><span id="sysHealth" class="badge bg-secondary">checking...</span></div>
|
||||
<div class="col-6"><small class="text-secondary d-block">DB Size</small><span id="sysDbSize">—</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header"><i class="bi bi-book me-1"></i>Quick Reference</div>
|
||||
<div class="card-body">
|
||||
<p class="mb-1"><strong>File sources</strong> — path to a log file on the worker container</p>
|
||||
<p class="mb-1"><strong>TCP/UDP sources</strong> — <code>tcp://0.0.0.0:9514</code></p>
|
||||
<p class="mb-0"><strong>Rules</strong> — PHP regex patterns, e.g. <code>/error/i</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mt-3">
|
||||
|
||||
<!-- Data Retention -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header"><i class="bi bi-clock-history me-1"></i>Data Retention</div>
|
||||
<div class="card-body">
|
||||
<p class="small text-secondary">Auto-purge old data to keep the database small.</p>
|
||||
<p class="small text-secondary mb-2">ClickHouse TTL auto-deletes old data. Manual purge below removes rate-limiter state.</p>
|
||||
<div class="row g-2 mb-2">
|
||||
<div class="col">
|
||||
<label class="form-label">Keep logs (days)</label>
|
||||
@@ -307,52 +303,14 @@ pre.raw-line { background: var(--bs-tertiary-bg); padding: .75rem; border-radius
|
||||
<small id="retentionResult" class="ms-2"></small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mt-3">
|
||||
<div class="card-header"><i class="bi bi-journal-text me-1"></i>Audit Log</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive" style="max-height:300px;overflow-y:auto">
|
||||
<table class="table table-sm mb-0">
|
||||
<tbody id="auditLogBody"><tr><td class="text-secondary small text-center">Loading...</td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
|
||||
<!-- False Positives -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header"><i class="bi bi-shield-check me-1"></i>Security</div>
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span><i class="bi bi-x-circle me-1"></i>False Positives</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Allowed User Tokens</label>
|
||||
<p class="small text-secondary">Only these Jakach user tokens can access this system. Leave empty to allow any authenticated Jakach user.</p>
|
||||
<textarea class="form-control font-monospace" id="allowedTokensInput" rows="4" placeholder="One user_token per line"></textarea>
|
||||
</div>
|
||||
<button class="btn btn-primary btn-sm" id="saveTokensBtn"><i class="bi bi-floppy"></i> Save</button>
|
||||
<small id="tokenSaveStatus" class="ms-2"></small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<div class="card-header"><i class="bi bi-telegram me-1"></i>Telegram Notifications</div>
|
||||
<div class="card-body">
|
||||
<p class="small text-secondary">Send alerts to a Telegram chat via a bot.</p>
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Bot Token</label>
|
||||
<input type="text" class="form-control" id="telegramBotToken" placeholder="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11">
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Chat ID</label>
|
||||
<input type="text" class="form-control" id="telegramChatId" placeholder="-1001234567890">
|
||||
</div>
|
||||
<button class="btn btn-primary btn-sm" id="saveTelegramBtn"><i class="bi bi-floppy"></i> Save</button>
|
||||
<small id="telegramSaveStatus" class="ms-2"></small>
|
||||
<button class="btn btn-outline-secondary btn-sm ms-2" id="testTelegramBtn"><i class="bi bi-send"></i> Test</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<div class="card-header"><i class="bi bi-x-circle me-1"></i>False Positives</div>
|
||||
<div class="card-body">
|
||||
<p class="small text-secondary">Lines matching these patterns will be ignored and not trigger any alert.</p>
|
||||
<div class="table-responsive mb-2">
|
||||
<div class="table-responsive mb-2" style="max-height:240px;overflow-y:auto">
|
||||
<table class="table table-sm mb-0">
|
||||
<thead><tr><th>Pattern</th><th>Description</th><th style="width:50px"></th></tr></thead>
|
||||
<tbody id="falsePositivesBody"></tbody>
|
||||
@@ -360,22 +318,86 @@ pre.raw-line { background: var(--bs-tertiary-bg); padding: .75rem; border-radius
|
||||
</div>
|
||||
<div class="input-group input-group-sm">
|
||||
<input type="text" class="form-control font-monospace" id="fpPattern" placeholder="/healthcheck/i">
|
||||
<input type="text" class="form-control" id="fpDescription" placeholder="Description (optional)" style="max-width:200px">
|
||||
<button class="btn btn-primary" id="addFpBtn"><i class="bi bi-plus-lg"></i> Add</button>
|
||||
<input type="text" class="form-control" id="fpDescription" placeholder="Description" style="max-width:160px">
|
||||
<button class="btn btn-primary" id="addFpBtn"><i class="bi bi-plus-lg"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header"><i class="bi bi-info-circle me-1"></i>How to get your user token</div>
|
||||
<div class="card-body small">
|
||||
<ol class="mb-0 ps-3">
|
||||
<li>Log in at <a href="https://auth.jakach.ch" target="_blank">auth.jakach.ch</a></li>
|
||||
<li>Your <code>user_token</code> is shown in your profile</li>
|
||||
<li>Paste it above to grant access</li>
|
||||
</ol>
|
||||
|
||||
<!-- Audit Log -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header"><i class="bi bi-journal-text me-1"></i>Audit Log</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive" style="max-height:250px;overflow-y:auto">
|
||||
<table class="table table-sm mb-0">
|
||||
<tbody id="auditLogBody"><tr><td class="text-secondary small text-center">Loading...</td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Right column -->
|
||||
<div class="col-md-6">
|
||||
|
||||
<!-- Security -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header"><i class="bi bi-shield-check me-1"></i>Security</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Allowed User Tokens <i class="bi bi-question-circle text-secondary" title="Only these Jakach user tokens can access this system. Leave empty to allow any authenticated Jakach user."></i></label>
|
||||
<textarea class="form-control font-monospace" id="allowedTokensInput" rows="3" placeholder="One user_token per line"></textarea>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<button class="btn btn-primary btn-sm" id="saveTokensBtn"><i class="bi bi-floppy"></i> Save</button>
|
||||
<small id="tokenSaveStatus"></small>
|
||||
<span class="ms-auto text-secondary small">Your token: <code id="settingsUserToken" class="user-select-all">—</code></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Telegram -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span><i class="bi bi-telegram me-1"></i>Telegram Notifications</span>
|
||||
<span id="telegramStatusBadge"></span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Bot Token</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<input type="password" class="form-control" id="telegramBotToken" placeholder="Enter bot token">
|
||||
<button class="btn btn-outline-secondary" id="telegramTokenToggle" onclick="toggleTelegramToken()"><i class="bi bi-eye"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Chat ID</label>
|
||||
<input type="text" class="form-control form-control-sm" id="telegramChatId" placeholder="-1001234567890">
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<button class="btn btn-primary btn-sm" id="saveTelegramBtn"><i class="bi bi-floppy"></i> Save</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" id="testTelegramBtn"><i class="bi bi-send"></i> Test</button>
|
||||
<small id="telegramSaveStatus"></small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- System -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header"><i class="bi bi-info-circle me-1"></i>System</div>
|
||||
<div class="card-body">
|
||||
<dl class="row mb-0 small">
|
||||
<dt class="col-sm-4">Logged in as</dt><dd class="col-sm-8" id="settingsUser">—</dd>
|
||||
<dt class="col-sm-4">Auth Server</dt><dd class="col-sm-8"><a href="https://auth.jakach.ch" target="_blank">auth.jakach.ch</a></dd>
|
||||
<dt class="col-sm-4">File sources</dt><dd class="col-sm-8">path on worker container</dd>
|
||||
<dt class="col-sm-4">TCP/UDP</dt><dd class="col-sm-8"><code>tcp://0.0.0.0:9514</code></dd>
|
||||
<dt class="col-sm-4">Rules</dt><dd class="col-sm-8">PHP regex, e.g. <code>/error/i</code></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1037,27 +1059,39 @@ async function loadSettings() {
|
||||
document.getElementById('sysSqlite').className = 'badge bg-' + (health.sqlite === 'connected' ? 'success' : 'danger');
|
||||
document.getElementById('sysClickhouse').textContent = health.clickhouse === 'connected' ? 'Connected' : 'Error';
|
||||
document.getElementById('sysClickhouse').className = 'badge bg-' + (health.clickhouse === 'connected' ? 'success' : 'danger');
|
||||
if (health.db_size) {
|
||||
document.getElementById('sysDbSize').textContent = health.db_size;
|
||||
}
|
||||
document.getElementById('sysDbSize').textContent = health.db_size || '—';
|
||||
} catch {
|
||||
document.getElementById('sysHealth').textContent = 'Unreachable';
|
||||
document.getElementById('sysHealth').className = 'badge bg-danger';
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await api('/config/allowed_tokens');
|
||||
const tokens = res.tokens || [];
|
||||
const [tokensRes, meRes] = await Promise.all([
|
||||
api('/config/allowed_tokens'),
|
||||
api('/auth/me'),
|
||||
]);
|
||||
const tokens = tokensRes.tokens || [];
|
||||
document.getElementById('allowedTokensInput').value = tokens.join('\n');
|
||||
const userToken = meRes.user?.user_token || '';
|
||||
document.getElementById('settingsUserToken').textContent = userToken || '—';
|
||||
document.getElementById('settingsUser').textContent = meRes.user?.username || '—';
|
||||
} catch (e) { console.error('load tokens error', e); }
|
||||
|
||||
try {
|
||||
const res = await api('/config/telegram');
|
||||
document.getElementById('telegramBotToken').value = '';
|
||||
document.getElementById('telegramBotToken').placeholder = res.bot_token_configured
|
||||
? (res.bot_token_masked || 'Token configured')
|
||||
? 'Token configured (enter new to change)'
|
||||
: 'Enter bot token';
|
||||
document.getElementById('telegramChatId').value = res.chat_id || '';
|
||||
const badge = document.getElementById('telegramStatusBadge');
|
||||
if (res.bot_token_configured && res.chat_id) {
|
||||
badge.innerHTML = '<span class="badge bg-success">Configured</span>';
|
||||
} else if (res.bot_token_configured) {
|
||||
badge.innerHTML = '<span class="badge bg-warning text-dark">Missing Chat ID</span>';
|
||||
} else {
|
||||
badge.innerHTML = '<span class="badge bg-secondary">Not configured</span>';
|
||||
}
|
||||
} catch (e) { console.error('load telegram error', e); }
|
||||
|
||||
try {
|
||||
@@ -1095,6 +1129,18 @@ document.getElementById('saveTokensBtn').addEventListener('click', async () => {
|
||||
}
|
||||
});
|
||||
|
||||
function toggleTelegramToken() {
|
||||
const input = document.getElementById('telegramBotToken');
|
||||
const btn = document.getElementById('telegramTokenToggle');
|
||||
if (input.type === 'password') {
|
||||
input.type = 'text';
|
||||
btn.innerHTML = '<i class="bi bi-eye-slash"></i>';
|
||||
} else {
|
||||
input.type = 'password';
|
||||
btn.innerHTML = '<i class="bi bi-eye"></i>';
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('saveTelegramBtn').addEventListener('click', async () => {
|
||||
const botToken = document.getElementById('telegramBotToken').value.trim();
|
||||
const chatId = document.getElementById('telegramChatId').value.trim();
|
||||
|
||||
Reference in New Issue
Block a user