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}`
}
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'> & {
body?: unknown
skipAuth?: boolean
@ -14,16 +29,9 @@ export async function apiFetch<T>(path: string, options: RequestOptions = {}): P
let authHeader: Record<string, string> = {}
if (!skipAuth) {
const storedUser = localStorage.getItem('user')
if (storedUser) {
try {
const userData = JSON.parse(storedUser)
if (userData?.jwt) {
authHeader = { Authorization: `Bearer ${userData.jwt}` }
}
} catch {
// Ignora erro de parsing
}
const token = getAuthToken()
if (token) {
authHeader = { Authorization: `Bearer ${token}` }
}
}

View file

@ -1,4 +1,4 @@
import { apiFetch } from '../lib/api'
import { apiFetch, getAuthToken, buildUrl } from '../lib/api'
export interface ReportSectionOutput {
id: number
@ -69,23 +69,10 @@ export const reportService = {
},
async createTemplate(orgId: number, formData: FormData): Promise<ReportTemplateOutput> {
const token = localStorage.getItem('token')
const apiUrl = import.meta.env.VITE_API_URL || 'https://api-task.beetech.dev'
const res = await fetch(`${apiUrl}/organizations/${orgId}/reports/templates`, {
return apiFetch<ReportTemplateOutput>(`/organizations/${orgId}/reports/templates`, {
method: 'POST',
headers: {
...(token ? { Authorization: `Bearer ${token}` } : {}),
},
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> {
@ -95,10 +82,9 @@ export const reportService = {
},
async generateReport(orgId: number, payload: GenerateReportPayload): Promise<{ blob: Blob; fileName: string }> {
const token = localStorage.getItem('token')
const apiUrl = import.meta.env.VITE_API_URL || 'https://api-task.beetech.dev'
const token = getAuthToken()
const res = await fetch(`${apiUrl}/organizations/${orgId}/reports/generate`, {
const res = await fetch(buildUrl(`/organizations/${orgId}/reports/generate`), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@ -108,8 +94,16 @@ export const reportService = {
})
if (!res.ok) {
const err = await res.json().catch(() => ({ message: 'Erro ao gerar relatório' }))
throw new Error(err.message || 'Erro ao gerar relatório')
const text = await res.text()
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