From e01e410d4300039838431e9bc8343c265a78c720 Mon Sep 17 00:00:00 2001 From: bruno Date: Mon, 13 Jul 2026 13:26:07 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20CSRF=20token=20dans=20toutes=20les=20req?= =?UTF-8?q?u=C3=AAtes=20admin=20(adminFetch)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tous les appels /api/admin/* passent maintenant par adminFetch() qui ajoute X-CSRF-Token depuis le cookie csrf_token --- app/templates/settings.html | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) 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) {} },