/** * Proxy genérico para o core-api. * * Fluxo: * 1. Lê cookie de sessão → busca tokens em Redis (refresh transparente se expirado) * 2. Resolve o tenant (dominio) a partir do hostname * 3. Busca codigoMunicipio via /publico/prefeitura/{dominio} (cacheado em Redis) * 4. Forward para core-api com Authorization + X-Municipio + X-Dominio * * Bypass: rotas /api/v1/publico/** podem ser acessadas sem sessão. */ export default defineEventHandler(async (event) => { const path = getRouterParam(event, 'path') ?? '' const isPublico = path.startsWith('publico/') let accessToken: string | null = null if (!isPublico) { const sid = readSessionCookie(event) if (!sid) { throw createError({ statusCode: 401, statusMessage: 'Sem sessão' }) } accessToken = await getValidAccessToken(sid) if (!accessToken) { clearSessionCookie(event) throw createError({ statusCode: 401, statusMessage: 'Sessão expirada' }) } } const dominio = tenantFromEvent(event) const prefeitura = await fetchPrefeituraInfo(dominio) if (!prefeitura) { throw createError({ statusCode: 400, statusMessage: `Tenant '${dominio}' não encontrado` }) } const cfg = useRuntimeConfig() const url = `${cfg.coreApiUrl}/api/v1/${path}` const query = getQuery(event) const method = event.method.toUpperCase() const body = ['GET', 'HEAD'].includes(method) ? undefined : await readRawBody(event) const headers: Record = { 'X-Municipio': String(prefeitura.codigoMunicipio), 'X-Dominio': prefeitura.dominio, } if (accessToken) headers.Authorization = `Bearer ${accessToken}` const contentType = getHeader(event, 'content-type') if (contentType) headers['Content-Type'] = contentType try { const res = await $fetch.raw(url, { method: method as 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', query, body, headers, responseType: 'stream', }) setResponseStatus(event, res.status) for (const [name, value] of res.headers.entries()) { if (name === 'transfer-encoding') continue setResponseHeader(event, name, value) } return res._data } catch (err: unknown) { const fetchErr = err as { response?: { status?: number; _data?: unknown } } if (fetchErr.response) { setResponseStatus(event, fetchErr.response.status ?? 500) return fetchErr.response._data } throw err } })