Files
smart-home-tablet/lib/voice-tools.ts
Cosmo 7fb05181e6
All checks were successful
Deploy / deploy (push) Successful in 3m10s
fix(voice/tools): use x-voice-internal header for loopback fetches
Tool endpoints (events, notes, transport, weather) call other /api/*
routes via loopback (http://localhost:3000). Those routes are
middleware-protected — cookie-less loopbacks were getting 401, which
surfaced to the voice agent as get_today_events → tool_http_502.

Add internal header bypass: middleware lets the request through when
x-voice-internal matches VOICE_API_KEY. Only our own tool endpoints
use this header, from inside the same container, so the blast radius
is limited to loopback traffic.

- middleware.ts: check x-voice-internal before cookie
- lib/voice-tools.ts: internalHeaders() helper
- app/api/voice/tools/{weather,transport,events,notes}: use it
2026-04-23 13:41:57 +00:00

29 lines
1.0 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Helper для /api/voice/tools/* — общий bearer-check и forwarding к внутренним endpoint'ам.
* Позволяет голосовому скрипту вызывать tools через один и тот же токен (VOICE_API_KEY).
*/
export function isBearerAuthorized(req: Request): boolean {
const expected = process.env.VOICE_API_KEY
if (!expected) return false
const auth = req.headers.get('authorization') || ''
const token = auth.replace(/^Bearer\s+/i, '').trim()
return token === expected
}
export function unauthorized() {
return new Response(JSON.stringify({ error: 'unauthorized' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
})
}
/**
* Headers для loopback-вызовов к другим /api/* роутам из tool endpoints.
* Middleware пропускает запросы с этим header'ом (см. middleware.ts).
*/
export function internalHeaders(): HeadersInit {
const key = process.env.VOICE_API_KEY || ''
return { 'x-voice-internal': key }
}