diff --git a/app/templates/settings.html b/app/templates/settings.html index bb5d066..376115d 100644 --- a/app/templates/settings.html +++ b/app/templates/settings.html @@ -551,9 +551,18 @@ document.addEventListener('alpine:init', function() { }, // ── Admin ── + getCsrfToken() { + return document.cookie.split('; ').find(r => r.startsWith('csrf_token='))?.split('=')[1] || ''; + }, + async adminFetch(url, opts) { + opts = opts || {}; + opts.headers = opts.headers || {}; + opts.headers['X-CSRF-Token'] = this.getCsrfToken(); + return await fetch(url, opts); + }, async loadAdminUsers() { try { - var r = await fetch('/api/admin/users'); + var r = await this.adminFetch('/api/admin/users'); var d = await r.json(); this.adminUsers = d.users || []; } catch(e) { this.adminUsers = []; } @@ -562,14 +571,14 @@ document.addEventListener('alpine:init', function() { async loadAdminStats() { try { - var r = await fetch('/api/admin/stats'); + var r = await this.adminFetch('/api/admin/stats'); this.adminStats = await r.json(); } catch(e) { this.adminStats = {}; } }, async loadAdminAudit() { try { - var r = await fetch('/api/admin/audit?limit=200'); + var r = await this.adminFetch('/api/admin/audit?limit=200'); var d = await r.json(); this.auditEntries = d.entries || []; } catch(e) { this.auditEntries = []; } @@ -579,7 +588,7 @@ document.addEventListener('alpine:init', function() { var u = this.newUser; if (!u.login || !u.password) return; try { - var r = await fetch('/api/admin/users', { + var r = await this.adminFetch('/api/admin/users', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({login:u.login, name:u.name, email:u.email, password:u.password, is_admin:u.is_admin?1:0}) }); @@ -600,7 +609,7 @@ document.addEventListener('alpine:init', function() { var body = {name: f.name, email: f.email, is_admin: f.is_admin?1:0, is_active: f.is_active?1:0}; if (f.password) body.password = f.password; try { - var r = await fetch('/api/admin/users/' + this.editingUser.id, { + var r = await this.adminFetch('/api/admin/users/' + this.editingUser.id, { method: 'PUT', headers: {'Content-Type':'application/json'}, body: JSON.stringify(body) }); @@ -614,7 +623,7 @@ document.addEventListener('alpine:init', function() { async adminDeleteUser(id) { if (!confirm('Permanently delete this user and all their data?')) return; try { - await fetch('/api/admin/users/' + id, { method: 'DELETE' }); + await this.adminFetch('/api/admin/users/' + id, { method: 'DELETE' }); await this.loadAdminUsers(); } catch(e) {} },