diff --git a/projects/ops-desk/api/app/auth.py b/projects/ops-desk/api/app/auth.py index 7543928..f6b5a1a 100644 --- a/projects/ops-desk/api/app/auth.py +++ b/projects/ops-desk/api/app/auth.py @@ -14,8 +14,8 @@ from typing import Any from fastapi import Depends, Header, HTTPException, Request from jose import JWTError, jwt import bcrypt -import time +from app.permissions import HUMAN_ROLES from app.totp_util import verify_code as verify_totp_code DB_PATH = Path(os.getenv("SQLITE_PATH", "/data/ops.db")) @@ -134,7 +134,7 @@ def decode_token(token: str) -> DeskUser: raise HTTPException(401, "invalid or expired token") from exc username = payload.get("sub") role = payload.get("role") - if not username or role not in {"super_admin", "ops_lead", "technician", "noc"}: + if not username or role not in HUMAN_ROLES: raise HTTPException(401, "invalid token claims") with db() as conn: row = conn.execute( diff --git a/projects/ops-desk/frontend/assets/agentic-ops.js b/projects/ops-desk/frontend/assets/agentic-ops.js index 7b69dd6..9fc708a 100644 --- a/projects/ops-desk/frontend/assets/agentic-ops.js +++ b/projects/ops-desk/frontend/assets/agentic-ops.js @@ -2,10 +2,13 @@ const esc = (s) => String(s ?? '').replace(/&/g,'&').replace(//g,'>'); let state = { threadId: null, selectedAgent: 'A6' }; - async function api(path, opts = {}) { + /** Usa o helper global do Desk (app.js) — garante JWT igual aos outros módulos. */ + async function agentsApi(path, opts = {}) { + const deskApi = typeof globalThis.api === 'function' ? globalThis.api : null; + if (deskApi) return deskApi(`/v1/agents${path}`, opts); const h = authHeaders({ ...(opts.headers || {}) }); if (!(opts.body instanceof FormData) && !h['Content-Type']) h['Content-Type'] = 'application/json'; - const r = await fetchWithTimeout(`/api/v1/agents${path}`, { ...opts, headers: h }); + const r = await fetchWithTimeout(`/api/v1/agents${path}`, { ...opts, headers: h }, 60000); if (r.status === 401) { logout(); throw new Error('sessão expirada — faça login novamente'); @@ -53,7 +56,7 @@ const box = el.querySelector('#agentic-thread-messages'); if (!box) return; box.innerHTML = '

Carregando thread…

'; - const data = await api(`/threads/${threadId}/messages`); + const data = await agentsApi(`/threads/${threadId}/messages`); box.innerHTML = data.messages.map(threadBubble).join('') || '

Sem mensagens.

'; box.scrollTop = box.scrollHeight; } @@ -62,13 +65,24 @@ const el = document.getElementById('agentic-ops-content'); if (!el) return; el.innerHTML = '

Carregando Agentic Ops…

'; + if (!getToken()) { + el.innerHTML = '

Sessão não encontrada neste endereço. Fazer login (use sempre o mesmo URL — ex. desk.ligbox.com.br).

'; + return; + } + if (typeof ensureValidSession === 'function') { + const ok = await ensureValidSession(); + if (!ok) { + el.innerHTML = '

Sessão expirada. Fazer login

'; + return; + } + } try { const [health, roster, inbox, threads, findings] = await Promise.all([ - api('/health'), - api('/roster'), - api('/inbox?limit=20'), - api('/threads?limit=15'), - api('/findings?limit=15'), + agentsApi('/health'), + agentsApi('/roster'), + agentsApi('/inbox?limit=20'), + agentsApi('/threads?limit=15'), + agentsApi('/findings?limit=15'), ]); const tier = health.tier === 't1' ? 'T1 LLM' : 'T0'; const ollama = health.ollama @@ -149,7 +163,7 @@ }); el.querySelectorAll('[data-ack-msg]').forEach(btn => { btn.addEventListener('click', async () => { - await api(`/messages/${btn.dataset.ackMsg}/ack`, { method: 'POST' }); + await agentsApi(`/messages/${btn.dataset.ackMsg}/ack`, { method: 'POST' }); await renderAgenticOps(); }); }); @@ -162,7 +176,7 @@ const tid = state.threadId || parseInt(el.querySelector('#agentic-thread-select')?.value, 10); const body = (input?.value || '').trim(); if (!tid || !body) return; - await api(`/threads/${tid}/reply`, { + await agentsApi(`/threads/${tid}/reply`, { method: 'POST', body: JSON.stringify({ body, target_agent: state.selectedAgent }), }); @@ -177,7 +191,7 @@ out.hidden = false; out.innerHTML = '

A pensar…

'; try { - const res = await api('/chat', { + const res = await agentsApi('/chat', { method: 'POST', body: JSON.stringify({ question: q, include_findings: true, target_agent: state.selectedAgent }), }); diff --git a/projects/ops-desk/frontend/index.html b/projects/ops-desk/frontend/index.html index 8f3b44d..5cba83e 100644 --- a/projects/ops-desk/frontend/index.html +++ b/projects/ops-desk/frontend/index.html @@ -443,7 +443,7 @@ - - + + diff --git a/projects/ops-desk/frontend/nginx.conf b/projects/ops-desk/frontend/nginx.conf index f6acf04..be5f3a9 100644 --- a/projects/ops-desk/frontend/nginx.conf +++ b/projects/ops-desk/frontend/nginx.conf @@ -4,7 +4,7 @@ server { resolver 127.0.0.11 valid=10s ipv6=off; - location ~* \.(html)$ { + location ~* \.(html|js)$ { add_header Cache-Control "no-cache, no-store, must-revalidate"; try_files $uri =404; } @@ -26,6 +26,7 @@ server { proxy_pass http://$upstream:8080$request_uri; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Authorization $http_authorization; proxy_connect_timeout 30s; proxy_send_timeout 600s; proxy_read_timeout 600s; diff --git a/projects/ops-desk/frontend/nginx.staging.conf b/projects/ops-desk/frontend/nginx.staging.conf index 5c267b5..0781a93 100644 --- a/projects/ops-desk/frontend/nginx.staging.conf +++ b/projects/ops-desk/frontend/nginx.staging.conf @@ -11,9 +11,15 @@ server { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Authorization $http_authorization; proxy_read_timeout 180s; } + location ~* \.(html|js)$ { + add_header Cache-Control "no-cache, no-store, must-revalidate"; + try_files $uri =404; + } + location / { try_files $uri $uri/ /index.html; }