Fix some issues

This commit is contained in:
2026-03-26 13:18:09 -04:00
parent ae6a44199f
commit 169820240a
25 changed files with 319 additions and 38 deletions
+25 -6
View File
@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1.0"/>
<meta name="csrf-token" content="{{ csrf_token() }}"/>
<title>{% block title %}IT Helpdesk{% endblock %} — TechDesk</title>
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
@@ -438,6 +439,27 @@
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script>
<script>
// ── CSRF helper ───────────────────────────────────────────────────────────────
// All state-changing fetch() calls must include the CSRF token header.
// Use csrfPost(url, body) instead of raw fetch(..., {method:'POST'}) to ensure
// the token from the <meta> tag is sent automatically.
const _csrfToken = () => document.querySelector('meta[name="csrf-token"]')?.content || '';
async function csrfPost(url, body = null) {
const opts = {
method : 'POST',
headers: { 'X-CSRFToken': _csrfToken() },
};
if (body !== null) {
if (typeof body === 'object' && !(body instanceof FormData)) {
opts.headers['Content-Type'] = 'application/json';
opts.body = JSON.stringify(body);
} else {
opts.body = body;
}
}
return fetch(url, opts);
}
// ── WebSocket ────────────────────────────────────────────────────────────────
{% if current_user.is_authenticated %}
const socket = io({
@@ -506,7 +528,7 @@ function _bindNotifClick(el) {
const link = el.dataset.link;
// Mark as read in DB
if(el.classList.contains('unread')){
try { await fetch(`/api/notifications/${id}/read`, { method: 'POST' }); } catch(_){}
try { await csrfPost(`/api/notifications/${id}/read`); } catch(_){}
el.classList.remove('unread');
const dot = el.querySelector('.notif-dot');
if(dot) dot.remove();
@@ -567,7 +589,7 @@ function prependNotif(n){
}
async function markAllRead(){
try { await fetch('/api/notifications/mark-all-read', { method: 'POST' }); } catch(_){}
try { await csrfPost('/api/notifications/mark-all-read'); } catch(_){}
updateBadge(0);
document.querySelectorAll('.notif-item.unread').forEach(el => {
el.classList.remove('unread');
@@ -601,10 +623,7 @@ async function sendChat(){
chatHistory.push({role:'user',content:msg});
const typing = appendTyping();
try{
const r = await fetch('/chatbot/message',{
method:'POST',headers:{'Content-Type':'application/json'},
body:JSON.stringify({message:msg,history:chatHistory})
});
const r = await csrfPost('/chatbot/message', {message:msg,history:chatHistory});
const d = await r.json();
typing.remove();
const reply = d.reply || 'Sorry, I encountered an error.';