Compare commits

...
2 Commits
Author SHA1 Message Date
Gitea Actions d72a3ca27f Release 0.1.33 [auto-version-bump] 2026-10-15 11:51:29 +02:00
Ein Anderssono 910db15b61 Add admin controls for runtime debug logs
Build and publish frontend / publish (push) Successful in 44s
2026-10-15 11:51:04 +02:00
5 changed files with 34 additions and 4 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ During play the UI has no persistent location, task, inventory, economy, status,
If the player asks to find or approach someone and no NPC is present, the direct dialogue scene may propose one person and an opening line. Rust creates that NPC only for the explicit request and commits the first exchange idempotently; a later turn continues the saved conversation. Clear requests about the current location are answered from its saved facts without asking the player to restate what they want to inspect. If the player asks to find or approach someone and no NPC is present, the direct dialogue scene may propose one person and an opening line. Rust creates that NPC only for the explicit request and commits the first exchange idempotently; a later turn continues the saved conversation. Clear requests about the current location are answered from its saved facts without asking the player to restate what they want to inspect.
The separate `/admin` route provides an authenticated, read-only operations view for player search/profile/trace, YAML prompt assets, generated Luau rules with validation results, and the append-only audit log. All privileged data comes from the Rust `/api/admin/...` API; the browser never connects directly to PostgreSQL. Backend endpoints require `accounts.is_admin=true`. The separate `/admin` route provides an authenticated operations view for player search/profile/trace, YAML prompt assets, generated Luau rules with validation results, runtime debug logging, and the append-only audit log. All privileged data comes from the Rust `/api/admin/...` API; the browser never connects directly to PostgreSQL. Backend endpoints require `accounts.is_admin=true`. The Driftloggar view lets an administrator switch `debug=true` on or off without restarting pods; the setting is stored in PostgreSQL and propagated to running replicas. Verbose pod logs include request, AI-call, validation, and commit metadata but never raw player text, prompts, model responses, sessions, or secrets. Full protected prompt/provider exchanges remain in the audited player trace.
The game is text- and dialogue-based and uses no images. Place descriptions include sound, smell, air/temperature, touch/surface, and spatial-orientation cues. Visible details may also be described in words, but are never required to understand the scene or play. The transcript is screen-reader announced, and Enter submits the input while Shift+Enter inserts a newline; no separate send button is required. The game is text- and dialogue-based and uses no images. Place descriptions include sound, smell, air/temperature, touch/surface, and spatial-orientation cues. Visible details may also be described in words, but are never required to understand the scene or play. The transcript is screen-reader announced, and Enter submits the input while Shift+Enter inserts a newline; no separate send button is required.
+1 -1
View File
@@ -1 +1 @@
0.1.32 0.1.33
+2 -2
View File
@@ -2,5 +2,5 @@ apiVersion: v2
name: ai-spel-frontend name: ai-spel-frontend
description: Static web frontend for the Swedish life simulator description: Static web frontend for the Swedish life simulator
type: application type: application
version: 0.1.32 version: 0.1.33
appVersion: "0.1.32" appVersion: "0.1.33"
+28
View File
@@ -63,6 +63,7 @@
<button type="button" data-admin-view="players">Spelare</button> <button type="button" data-admin-view="players">Spelare</button>
<button type="button" data-admin-view="prompts">Prompter</button> <button type="button" data-admin-view="prompts">Prompter</button>
<button type="button" data-admin-view="rules">Luau-regler</button> <button type="button" data-admin-view="rules">Luau-regler</button>
<button type="button" data-admin-view="logging">Driftloggar</button>
<button type="button" data-admin-view="audit">Revisionslogg</button> <button type="button" data-admin-view="audit">Revisionslogg</button>
<button type="button" id="admin-logout">Logga ut</button> <button type="button" id="admin-logout">Logga ut</button>
</nav> </nav>
@@ -392,6 +393,32 @@
adminElement('h2', 'Revisionslogg'); adminElement('h2', 'Revisionslogg');
for (const entry of result.entries) adminJson(`${entry.created_at} · ${entry.action} · ${entry.admin_account_id || entry.operator_identity}`, entry); for (const entry of result.entries) adminJson(`${entry.created_at} · ${entry.action} · ${entry.admin_account_id || entry.operator_identity}`, entry);
} }
async function loadAdminLogging() {
adminContent.replaceChildren();
const result = await request('/api/admin/settings/logging');
adminElement('h2', 'Driftloggar');
adminElement('p', 'debug=true skriver detaljerade tekniska händelser för HTTP, AI-anrop, validering och commit. debug=false behåller korta tekniska åtkomstloggar och fel. Ändringen sparas i databasen och börjar gälla utan omstart. Spelartext, promptinnehåll, AI-svar, sessionsuppgifter och hemligheter skrivs aldrig till poddloggarna.');
const form = adminElement('form');
const label = adminElement('label', undefined, form);
const checkbox = document.createElement('input');
checkbox.type = 'checkbox'; checkbox.name = 'debug'; checkbox.checked = result.debug;
label.append(checkbox, document.createTextNode(' Aktivera verbose felsökningsloggar (debug=true)'));
const save = adminElement('button', 'Spara loggnivå', form); save.type = 'submit';
const status = adminElement('p', `Aktuell nivå: debug=${result.debug}`);
status.setAttribute('role', 'status');
form.addEventListener('submit', async event => {
event.preventDefault(); save.disabled = true;
try {
const updated = await request('/api/admin/settings/logging', {
method: 'PUT', headers: {'content-type': 'application/json'},
body: JSON.stringify({debug: checkbox.checked}),
});
status.textContent = `Aktuell nivå: debug=${updated.debug}. Ändringen gäller utan omstart.`;
} catch (error) {
status.textContent = `Loggnivån kunde inte ändras: ${error.message}`;
} finally { save.disabled = false; }
});
}
async function openAdmin() { async function openAdmin() {
showAdmin(); showAdmin();
adminContent.setAttribute('aria-busy', 'true'); adminContent.setAttribute('aria-busy', 'true');
@@ -632,6 +659,7 @@
if (button.dataset.adminView === 'players') await loadAdminPlayers(); if (button.dataset.adminView === 'players') await loadAdminPlayers();
else if (button.dataset.adminView === 'prompts') await loadAdminPrompts(); else if (button.dataset.adminView === 'prompts') await loadAdminPrompts();
else if (button.dataset.adminView === 'rules') await loadAdminRules(); else if (button.dataset.adminView === 'rules') await loadAdminRules();
else if (button.dataset.adminView === 'logging') await loadAdminLogging();
else await loadAdminAudit(); else await loadAdminAudit();
} catch (error) { adminContent.replaceChildren(); adminElement('p', `Vyn kunde inte läsas: ${error.message}`); } } catch (error) { adminContent.replaceChildren(); adminElement('p', `Vyn kunde inte läsas: ${error.message}`); }
finally { adminContent.setAttribute('aria-busy', 'false'); } finally { adminContent.setAttribute('aria-busy', 'false'); }
+2
View File
@@ -36,6 +36,8 @@ grep -q 'id="admin-view"' index.html
grep -q '/api/admin/players?' index.html grep -q '/api/admin/players?' index.html
grep -q "request('/api/admin/prompts'" index.html grep -q "request('/api/admin/prompts'" index.html
grep -q "request('/api/admin/rules'" index.html grep -q "request('/api/admin/rules'" index.html
grep -q "request('/api/admin/settings/logging'" index.html
grep -q 'data-admin-view="logging"' index.html
grep -q '/api/admin/audit?limit=200' index.html grep -q '/api/admin/audit?limit=200' index.html
grep -q 'entry.data.external_call' index.html grep -q 'entry.data.external_call' index.html
grep -q 'Externt tjänsteanrop' index.html grep -q 'Externt tjänsteanrop' index.html