import { Injectable } from '@nestjs/common'; import { PrismaService } from '../../prisma/prisma.service'; export interface CurrentUser { id: string; name?: string; username?: string; roleId: string; email?: string; } export interface AuthenticatedRequest { headers?: Record; currentUser?: CurrentUser | null; } @Injectable() export class AuthContextService { constructor(private readonly prisma: PrismaService) {} async resolveCurrentUser(request: AuthenticatedRequest): Promise { if (request.currentUser !== undefined) return request.currentUser; const userId = headerValue(request, 'x-ftb-user-id') || headerValue(request, 'x-user-id'); if (!userId) { request.currentUser = null; return null; } const headerUser: CurrentUser = { id: userId, name: decodeHeader(headerValue(request, 'x-ftb-user-name')), username: headerValue(request, 'x-ftb-user-username'), roleId: headerValue(request, 'x-ftb-user-role-id') || '', email: headerValue(request, 'x-ftb-user-email'), }; if (headerUser.roleId) { request.currentUser = headerUser; return headerUser; } const row = await this.prisma.user.findUnique({ where: { id: userId }, select: { id: true, name: true, username: true, roleId: true, email: true }, }); request.currentUser = row ? { id: row.id, name: row.name, username: row.username ?? undefined, roleId: row.roleId || 'member', email: row.email, } : null; return request.currentUser; } } function headerValue(request: AuthenticatedRequest, name: string): string { const value = request.headers?.[name] ?? request.headers?.[name.toLowerCase()]; if (Array.isArray(value)) return value[0] ?? ''; return value ?? ''; } function decodeHeader(value: string): string { try { return decodeURIComponent(value); } catch { return value; } }