fix: CSRF token dans toutes les requêtes admin (adminFetch)
FlowDeck CI / test (push) Failing after 4s
FlowDeck CI / docker (push) Has been skipped

Tous les appels /api/admin/* passent maintenant par adminFetch()
qui ajoute X-CSRF-Token depuis le cookie csrf_token
This commit is contained in:
2026-07-13 13:26:07 -04:00
parent 9e9ea181e6
commit e01e410d43
+15 -6
View File
@@ -551,9 +551,18 @@ document.addEventListener('alpine:init', function() {
}, },
// ── Admin ── // ── 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() { async loadAdminUsers() {
try { try {
var r = await fetch('/api/admin/users'); var r = await this.adminFetch('/api/admin/users');
var d = await r.json(); var d = await r.json();
this.adminUsers = d.users || []; this.adminUsers = d.users || [];
} catch(e) { this.adminUsers = []; } } catch(e) { this.adminUsers = []; }
@@ -562,14 +571,14 @@ document.addEventListener('alpine:init', function() {
async loadAdminStats() { async loadAdminStats() {
try { try {
var r = await fetch('/api/admin/stats'); var r = await this.adminFetch('/api/admin/stats');
this.adminStats = await r.json(); this.adminStats = await r.json();
} catch(e) { this.adminStats = {}; } } catch(e) { this.adminStats = {}; }
}, },
async loadAdminAudit() { async loadAdminAudit() {
try { 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(); var d = await r.json();
this.auditEntries = d.entries || []; this.auditEntries = d.entries || [];
} catch(e) { this.auditEntries = []; } } catch(e) { this.auditEntries = []; }
@@ -579,7 +588,7 @@ document.addEventListener('alpine:init', function() {
var u = this.newUser; var u = this.newUser;
if (!u.login || !u.password) return; if (!u.login || !u.password) return;
try { try {
var r = await fetch('/api/admin/users', { var r = await this.adminFetch('/api/admin/users', {
method: 'POST', headers: {'Content-Type':'application/json'}, 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}) 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}; 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; if (f.password) body.password = f.password;
try { 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'}, method: 'PUT', headers: {'Content-Type':'application/json'},
body: JSON.stringify(body) body: JSON.stringify(body)
}); });
@@ -614,7 +623,7 @@ document.addEventListener('alpine:init', function() {
async adminDeleteUser(id) { async adminDeleteUser(id) {
if (!confirm('Permanently delete this user and all their data?')) return; if (!confirm('Permanently delete this user and all their data?')) return;
try { try {
await fetch('/api/admin/users/' + id, { method: 'DELETE' }); await this.adminFetch('/api/admin/users/' + id, { method: 'DELETE' });
await this.loadAdminUsers(); await this.loadAdminUsers();
} catch(e) {} } catch(e) {}
}, },