Files
ftb-project-management/apps/server/src/common/auth/auth-context.service.ts

71 lines
1.9 KiB
TypeScript

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<string, string | string[] | undefined>;
currentUser?: CurrentUser | null;
}
@Injectable()
export class AuthContextService {
constructor(private readonly prisma: PrismaService) {}
async resolveCurrentUser(request: AuthenticatedRequest): Promise<CurrentUser | null> {
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;
}
}