1- import { Inject } from '@nestjs/common' ;
1+ import { Inject , NotFoundException } from '@nestjs/common' ;
2+ import {
3+ GetDecryptedSecretKey ,
4+ GetDecryptedSecretKeyCommand ,
5+ InMemoryLRUCacheService ,
6+ InMemoryLRUCacheStore ,
7+ PinoLogger ,
8+ } from '@novu/application-generic' ;
29import { PostActionEnum , type Workflow } from '@novu/framework/internal' ;
310import { Client , NovuHandler , NovuRequestHandler } from '@novu/framework/nest' ;
411import { EnvironmentTypeEnum } from '@novu/shared' ;
@@ -18,10 +25,23 @@ const frameworkName = 'novu-nest';
1825export class NovuBridgeClient {
1926 constructor (
2027 @Inject ( NovuHandler ) private novuHandler : NovuHandler ,
21- private constructFrameworkWorkflow : ConstructFrameworkWorkflow
28+ private constructFrameworkWorkflow : ConstructFrameworkWorkflow ,
29+ private getDecryptedSecretKey : GetDecryptedSecretKey ,
30+ private inMemoryLRUCacheService : InMemoryLRUCacheService ,
31+ private logger : PinoLogger
2232 ) { }
2333
2434 public async handleRequest ( req : Request , res : Response ) {
35+ const environmentId = req . params . environmentId ;
36+ if ( ! environmentId || ! String ( environmentId ) . trim ( ) ) {
37+ res . status ( 400 ) . json ( {
38+ error : 'Missing or invalid environmentId' ,
39+ details : 'The bridge route requires a non-empty environmentId path parameter.' ,
40+ } ) ;
41+
42+ return ;
43+ }
44+
2545 const workflows : Workflow [ ] = [ ] ;
2646
2747 /*
@@ -32,7 +52,7 @@ export class NovuBridgeClient {
3252 if ( Object . values ( PostActionEnum ) . includes ( req . query . action as PostActionEnum ) ) {
3353 const programmaticallyConstructedWorkflow = await this . constructFrameworkWorkflow . execute (
3454 ConstructFrameworkWorkflowCommand . create ( {
35- environmentId : req . params . environmentId ,
55+ environmentId,
3656 workflowId : req . query . workflowId as string ,
3757 layoutId : req . query . layoutId as string ,
3858 controlValues : req . body . controls ,
@@ -46,13 +66,76 @@ export class NovuBridgeClient {
4666 workflows . push ( programmaticallyConstructedWorkflow ) ;
4767 }
4868
69+ const cacheKey = `bridge-secret-key:${ environmentId } ` ;
70+ const storeName = InMemoryLRUCacheStore . VALIDATOR ;
71+
72+ let secretKey : string ;
73+ try {
74+ const resolved = await this . inMemoryLRUCacheService . get (
75+ storeName ,
76+ cacheKey ,
77+ ( ) =>
78+ this . getDecryptedSecretKey . execute (
79+ GetDecryptedSecretKeyCommand . create ( {
80+ environmentId,
81+ } )
82+ ) ,
83+ {
84+ environmentId,
85+ cacheVariant : 'bridge-secret-key' ,
86+ }
87+ ) ;
88+
89+ if ( typeof resolved !== 'string' || ! resolved . trim ( ) ) {
90+ this . logger . error (
91+ `Bridge secret key missing or invalid after cache lookup (store=${ storeName } , cacheKey=${ cacheKey } , environmentId=${ environmentId } )`
92+ ) ;
93+ res . status ( 500 ) . json ( {
94+ error : 'Failed to resolve environment secret key' ,
95+ details : `Empty or invalid secret from ${ storeName } for cache key ${ cacheKey } .` ,
96+ } ) ;
97+
98+ return ;
99+ }
100+
101+ secretKey = resolved ;
102+ } catch ( error ) {
103+ if ( error instanceof NotFoundException ) {
104+ this . logger . warn (
105+ `Environment not found for bridge secret (store=${ storeName } , cacheKey=${ cacheKey } ): ${ error . message } `
106+ ) ;
107+ res . status ( 404 ) . json ( {
108+ error : 'Environment not found' ,
109+ details : `No environment for cache key ${ cacheKey } (${ storeName } ).` ,
110+ } ) ;
111+
112+ return ;
113+ }
114+
115+ this . logger . error (
116+ { err : error } ,
117+ `Failed to resolve bridge secret key (store=${ storeName } , cacheKey=${ cacheKey } , environmentId=${ environmentId } )`
118+ ) ;
119+ res . status ( 500 ) . json ( {
120+ error : 'Failed to resolve environment secret key' ,
121+ details : `Unexpected error while loading secret via ${ storeName } for cache key ${ cacheKey } .` ,
122+ } ) ;
123+
124+ return ;
125+ }
126+
49127 const novuRequestHandler = new NovuRequestHandler ( {
50128 frameworkName,
51129 workflows,
52- client : new Client ( { secretKey : 'INTERNAL_KEY' , strictAuthentication : false , verbose : false } ) ,
130+ client : new Client ( { secretKey, strictAuthentication : true , verbose : false } ) ,
53131 handler : this . novuHandler . handler ,
54132 } ) ;
55133
56- await novuRequestHandler . createHandler ( ) ( req as any , res as any ) ;
134+ const bridgeHandler = novuRequestHandler . createHandler ( ) as (
135+ request : Request ,
136+ response : Response
137+ ) => void | Promise < void > ;
138+
139+ await bridgeHandler ( req , res ) ;
57140 }
58141}
0 commit comments