fix(reports): resolve 401 unauthorized by using central jwt token extraction and url builder

This commit is contained in:
Gabriel Menezes 2026-10-31 15:17:06 -04:00
parent 5fcb933399
commit 771fa61629
2 changed files with 34 additions and 32 deletions

View file

@ -1,9 +1,24 @@
const API_BASE_URL = (import.meta.env.VITE_API_URL as string | undefined) ?? 'http://localhost:8081' export const API_BASE_URL = (import.meta.env.VITE_API_URL as string | undefined) ?? 'http://localhost:8081'
function buildUrl(path: string) { export function buildUrl(path: string) {
return `${API_BASE_URL}${path}` return `${API_BASE_URL}${path}`
} }
export function getAuthToken(): string | null {
const storedUser = localStorage.getItem('user')
if (storedUser) {
try {
const userData = JSON.parse(storedUser)
if (userData?.jwt) {
return userData.jwt
}
} catch {
// Ignora erro de parsing
}
}
return null
}
type RequestOptions = Omit<RequestInit, 'body'> & { type RequestOptions = Omit<RequestInit, 'body'> & {
body?: unknown body?: unknown
skipAuth?: boolean skipAuth?: boolean
@ -14,16 +29,9 @@ export async function apiFetch<T>(path: string, options: RequestOptions = {}): P
let authHeader: Record<string, string> = {} let authHeader: Record<string, string> = {}
if (!skipAuth) { if (!skipAuth) {
const storedUser = localStorage.getItem('user') const token = getAuthToken()
if (storedUser) { if (token) {
try { authHeader = { Authorization: `Bearer ${token}` }
const userData = JSON.parse(storedUser)
if (userData?.jwt) {
authHeader = { Authorization: `Bearer ${userData.jwt}` }
}
} catch {
// Ignora erro de parsing
}
} }
} }

View file

@ -1,4 +1,4 @@
import { apiFetch } from '../lib/api' import { apiFetch, getAuthToken, buildUrl } from '../lib/api'
export interface ReportSectionOutput { export interface ReportSectionOutput {
id: number id: number
@ -69,23 +69,10 @@ export const reportService = {
}, },
async createTemplate(orgId: number, formData: FormData): Promise<ReportTemplateOutput> { async createTemplate(orgId: number, formData: FormData): Promise<ReportTemplateOutput> {
const token = localStorage.getItem('token') return apiFetch<ReportTemplateOutput>(`/organizations/${orgId}/reports/templates`, {
const apiUrl = import.meta.env.VITE_API_URL || 'https://api-task.beetech.dev'
const res = await fetch(`${apiUrl}/organizations/${orgId}/reports/templates`, {
method: 'POST', method: 'POST',
headers: {
...(token ? { Authorization: `Bearer ${token}` } : {}),
},
body: formData, body: formData,
}) })
if (!res.ok) {
const err = await res.json().catch(() => ({ message: 'Erro ao criar modelo de relatório' }))
throw new Error(err.message || 'Erro ao criar modelo de relatório')
}
return res.json()
}, },
async deleteTemplate(orgId: number, templateId: number): Promise<void> { async deleteTemplate(orgId: number, templateId: number): Promise<void> {
@ -95,10 +82,9 @@ export const reportService = {
}, },
async generateReport(orgId: number, payload: GenerateReportPayload): Promise<{ blob: Blob; fileName: string }> { async generateReport(orgId: number, payload: GenerateReportPayload): Promise<{ blob: Blob; fileName: string }> {
const token = localStorage.getItem('token') const token = getAuthToken()
const apiUrl = import.meta.env.VITE_API_URL || 'https://api-task.beetech.dev'
const res = await fetch(`${apiUrl}/organizations/${orgId}/reports/generate`, { const res = await fetch(buildUrl(`/organizations/${orgId}/reports/generate`), {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -108,8 +94,16 @@ export const reportService = {
}) })
if (!res.ok) { if (!res.ok) {
const err = await res.json().catch(() => ({ message: 'Erro ao gerar relatório' })) const text = await res.text()
throw new Error(err.message || 'Erro ao gerar relatório') let message = text || 'Erro ao gerar relatório'
try {
const err = JSON.parse(text)
if (err.message) message = err.message
if (err.error) message = err.error
} catch {
// Ignora erro de parsing
}
throw new Error(message)
} }
// Obter nome do arquivo do header Content-Disposition ou gerar fallback // Obter nome do arquivo do header Content-Disposition ou gerar fallback