17ffd95a70
Upgrade both Nuxt apps to Nuxt 4.4.6 (vue-tsc 3, TypeScript 5.6, undici 7) and add a root tsconfig.json to each app. Fix the strict-null / noUncheckedIndexedAccess errors surfaced by Nuxt 4's stricter generated tsconfig and vue-tsc 3. Drop the nuxt-oidc-auth pnpm patch (Nuxt 4 fixes the prepare:types crash natively).
57 lines
2.0 KiB
TypeScript
57 lines
2.0 KiB
TypeScript
// Helper: forward a request to platform-api using the signed-in operator's
|
|
// access token. Every operator proxy route uses this — it's the only place
|
|
// we touch the encrypted session.
|
|
//
|
|
// Also propagates the originating client IP via X-Forwarded-For so the
|
|
// platform-api can record it in the audit log. Without this, the API would
|
|
// only see the operator container's IP.
|
|
|
|
import type { H3Event } from 'h3'
|
|
import { getUserSession } from 'nuxt-oidc-auth/runtime/server/utils/session.js'
|
|
|
|
const BASE = process.env.PLATFORM_API_INTERNAL_URL ?? 'http://platform-api:3001'
|
|
|
|
function originatingIp(event: H3Event): string | undefined {
|
|
// Traefik already injects X-Forwarded-For on the way in. Take the leftmost
|
|
// entry (the original client), trimming any whitespace.
|
|
const fwd = getHeader(event, 'x-forwarded-for')
|
|
if (fwd) {
|
|
const first = fwd.split(',')[0]?.trim()
|
|
if (first) return first
|
|
}
|
|
// Direct request (no proxy header) — fall back to the socket address.
|
|
return event.node.req.socket?.remoteAddress
|
|
}
|
|
|
|
export async function platformApi<T = unknown>(
|
|
event: H3Event,
|
|
path: string,
|
|
init: {
|
|
method?: string
|
|
body?: BodyInit | Record<string, unknown> | null
|
|
query?: Record<string, string | number | undefined>
|
|
} = {},
|
|
): Promise<T> {
|
|
const session = await getUserSession(event).catch(() => null)
|
|
const accessToken = (session as { accessToken?: string } | null)?.accessToken
|
|
if (!accessToken) {
|
|
throw createError({ statusCode: 401, statusMessage: 'Not signed in' })
|
|
}
|
|
|
|
const clientIp = originatingIp(event)
|
|
const headers: Record<string, string> = { Authorization: `Bearer ${accessToken}` }
|
|
if (clientIp) headers['x-forwarded-for'] = clientIp
|
|
|
|
try {
|
|
return (await $fetch(`${BASE}${path}`, {
|
|
method: (init.method as 'GET' | 'POST' | 'PATCH' | 'DELETE') ?? 'GET',
|
|
headers,
|
|
body: init.body,
|
|
query: init.query,
|
|
})) as T
|
|
} catch (err: unknown) {
|
|
const e = err as { statusCode?: number; data?: unknown }
|
|
throw createError({ statusCode: e.statusCode ?? 500, data: e.data })
|
|
}
|
|
}
|