Notification updates
This commit is contained in:
@@ -123,6 +123,7 @@ const secretsSchema = z.object({
|
||||
postgres_password: z.string(),
|
||||
auth0_client_secret: z.string(),
|
||||
google_maps_api_key: z.string(),
|
||||
resend_api_key: z.string(),
|
||||
});
|
||||
|
||||
type Config = z.infer<typeof configSchema>;
|
||||
@@ -171,6 +172,7 @@ class ConfigurationLoader {
|
||||
'postgres-password',
|
||||
'auth0-client-secret',
|
||||
'google-maps-api-key',
|
||||
'resend-api-key',
|
||||
];
|
||||
|
||||
for (const secretFile of secretFiles) {
|
||||
@@ -227,6 +229,9 @@ class ConfigurationLoader {
|
||||
},
|
||||
};
|
||||
|
||||
// Set RESEND_API_KEY in environment for EmailService
|
||||
process.env['RESEND_API_KEY'] = secrets.resend_api_key;
|
||||
|
||||
logger.info('Configuration loaded successfully', {
|
||||
configSource: 'yaml',
|
||||
secretsSource: 'files',
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
/**
|
||||
* @ai-summary Fastify JWT authentication plugin using Auth0
|
||||
* @ai-context Validates JWT tokens against Auth0 JWKS endpoint
|
||||
* @ai-context Validates JWT tokens against Auth0 JWKS endpoint, hydrates userContext with profile
|
||||
*/
|
||||
import { FastifyPluginAsync, FastifyRequest, FastifyReply } from 'fastify';
|
||||
import fp from 'fastify-plugin';
|
||||
import buildGetJwks from 'get-jwks';
|
||||
import { appConfig } from '../config/config-loader';
|
||||
import { logger } from '../logging/logger';
|
||||
import { UserProfileRepository } from '../../features/user-profile/data/user-profile.repository';
|
||||
import { pool } from '../config/database';
|
||||
|
||||
declare module 'fastify' {
|
||||
interface FastifyInstance {
|
||||
@@ -18,6 +20,7 @@ declare module 'fastify' {
|
||||
userContext?: {
|
||||
userId: string;
|
||||
email?: string;
|
||||
displayName?: string;
|
||||
isAdmin: boolean;
|
||||
adminRecord?: any;
|
||||
};
|
||||
@@ -70,21 +73,51 @@ const authPlugin: FastifyPluginAsync = async (fastify) => {
|
||||
},
|
||||
});
|
||||
|
||||
// Initialize profile repository for user profile hydration
|
||||
const profileRepo = new UserProfileRepository(pool);
|
||||
|
||||
// Decorate with authenticate function that validates JWT
|
||||
fastify.decorate('authenticate', async function(request: FastifyRequest, reply: FastifyReply) {
|
||||
try {
|
||||
await request.jwtVerify();
|
||||
|
||||
// Hydrate userContext with basic auth info
|
||||
const userId = request.user?.sub;
|
||||
|
||||
// Get or create user profile from database
|
||||
// This ensures we have reliable email/displayName for notifications
|
||||
let email = request.user?.email;
|
||||
let displayName: string | undefined;
|
||||
|
||||
try {
|
||||
const profile = await profileRepo.getOrCreate(userId, {
|
||||
email: request.user?.email || `${userId}@unknown.local`,
|
||||
displayName: request.user?.name || request.user?.nickname,
|
||||
});
|
||||
|
||||
// Use notificationEmail if set, otherwise fall back to profile email
|
||||
email = profile.notificationEmail || profile.email;
|
||||
displayName = profile.displayName || undefined;
|
||||
} catch (profileError) {
|
||||
// Log but don't fail auth if profile fetch fails
|
||||
logger.warn('Failed to fetch user profile', {
|
||||
userId: userId?.substring(0, 8) + '...',
|
||||
error: profileError instanceof Error ? profileError.message : 'Unknown error',
|
||||
});
|
||||
// Fall back to JWT email if available
|
||||
email = request.user?.email;
|
||||
}
|
||||
|
||||
// Hydrate userContext with profile data
|
||||
request.userContext = {
|
||||
userId,
|
||||
email: request.user?.email,
|
||||
email,
|
||||
displayName,
|
||||
isAdmin: false, // Default to false; admin status checked by admin guard
|
||||
};
|
||||
|
||||
logger.info('JWT authentication successful', {
|
||||
userId: userId?.substring(0, 8) + '...',
|
||||
hasEmail: !!email,
|
||||
audience: auth0Config.audience
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user