112 lines
4.7 KiB
HTML
112 lines
4.7 KiB
HTML
{{template "head" .}}
|
|
{{template "topbar" .}}
|
|
<div class="page">
|
|
<div class="float">
|
|
<div class="settings-head">
|
|
<h1>Admin</h1>
|
|
</div>
|
|
<div class="settings-body">
|
|
<p>Enter the admin key to manage server settings. The key is kept in
|
|
sessionStorage for this tab only and is sent as a request header — it is
|
|
never stored in a cookie, so it will not accompany normal paste requests.</p>
|
|
<form id="admin-key-form">
|
|
<label for="admin-key">Admin key</label><br>
|
|
<input type="password" id="admin-key" autocomplete="off" style="width:100%">
|
|
<button type="submit">Unlock</button>
|
|
<span id="admin-key-status"></span>
|
|
</form>
|
|
<div id="admin-panel" style="display:none">
|
|
<h2>Settings</h2>
|
|
<form id="admin-settings-form">
|
|
<table>
|
|
<tr><td>Rate-limit burst</td><td><input type="number" id="rl-burst" min="1" step="1"></td></tr>
|
|
<tr><td>Rate-limit refill per minute</td><td><input type="number" id="rl-refill" min="0.1" step="0.1"></td></tr>
|
|
<tr><td>Max content bytes</td><td><input type="number" id="max-content" min="1" step="1"></td></tr>
|
|
<tr><td>Default expiry</td><td><input type="text" id="default-expiry" placeholder="e.g. 168h, 30m, 0 = never"></td></tr>
|
|
<tr><td>Custom URL reservation days</td><td><input type="number" id="slug-days" min="1" step="1"></td></tr>
|
|
<tr><td>Burn viewer window (minutes)</td><td><input type="number" id="burn-window" min="1" step="1"></td></tr>
|
|
</table>
|
|
<button type="submit">Save</button>
|
|
<span id="admin-save-status"></span>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
(function () {
|
|
var KEY = 'palette_admin_key';
|
|
var keyInput = document.getElementById('admin-key');
|
|
var status = document.getElementById('admin-key-status');
|
|
var panel = document.getElementById('admin-panel');
|
|
|
|
function key() { return sessionStorage.getItem(KEY) || ''; }
|
|
|
|
function api(path, opts) {
|
|
opts = opts || {};
|
|
// allow callers to override the key header (e.g. validating a typed key, #99)
|
|
opts.headers = Object.assign({ 'X-Admin-Key': key() }, opts.headers || {});
|
|
if (opts.body) opts.headers['Content-Type'] = 'application/json';
|
|
return fetch(path, opts);
|
|
}
|
|
|
|
function loadSettings() {
|
|
api('/admin/api/settings').then(function (r) {
|
|
if (r.status !== 200) { showLock(); return; }
|
|
return r.json();
|
|
}).then(function (s) {
|
|
if (!s) return;
|
|
document.getElementById('rl-burst').value = s.rate_limit_burst;
|
|
document.getElementById('rl-refill').value = s.rate_limit_per_minute;
|
|
document.getElementById('max-content').value = s.max_content_bytes;
|
|
document.getElementById('default-expiry').value = s.default_expiry;
|
|
document.getElementById('slug-days').value = s.custom_slug_reservation_days;
|
|
document.getElementById('burn-window').value = s.burn_viewer_window_minutes;
|
|
panel.style.display = '';
|
|
});
|
|
}
|
|
|
|
function showLock() {
|
|
panel.style.display = 'none';
|
|
sessionStorage.removeItem(KEY);
|
|
}
|
|
|
|
document.getElementById('admin-key-form').addEventListener('submit', function (e) {
|
|
e.preventDefault();
|
|
// #99: don't persist the key until the server accepts it
|
|
api('/admin/api/settings', { headers: { 'X-Admin-Key': keyInput.value } }).then(function (r) {
|
|
if (r.status === 200) {
|
|
sessionStorage.setItem(KEY, keyInput.value);
|
|
status.textContent = '✓';
|
|
keyInput.value = '';
|
|
loadSettings();
|
|
} else {
|
|
status.textContent = 'invalid key';
|
|
showLock();
|
|
}
|
|
});
|
|
});
|
|
|
|
document.getElementById('admin-settings-form').addEventListener('submit', function (e) {
|
|
e.preventDefault();
|
|
var body = {
|
|
rate_limit_burst: parseFloat(document.getElementById('rl-burst').value),
|
|
rate_limit_per_minute: parseFloat(document.getElementById('rl-refill').value),
|
|
max_content_bytes: parseInt(document.getElementById('max-content').value, 10),
|
|
default_expiry: document.getElementById('default-expiry').value,
|
|
custom_slug_reservation_days: parseInt(document.getElementById('slug-days').value, 10),
|
|
burn_viewer_window_minutes: parseInt(document.getElementById('burn-window').value, 10)
|
|
};
|
|
api('/admin/api/settings', { method: 'POST', body: JSON.stringify(body) }).then(function (r) {
|
|
document.getElementById('admin-save-status').textContent = r.status === 200 ? 'saved' : 'error';
|
|
if (r.status !== 200) showLock();
|
|
});
|
|
});
|
|
|
|
// #112: always show the lock on fresh load — do not auto-restore the
|
|
// panel from a stale sessionStorage key. The key is only written after a
|
|
// successful unlock (above) so in-page actions still work within this visit.
|
|
})();
|
|
</script>
|
|
{{template "foot" .}}
|