@@ -5,21 +5,31 @@ import type {
55 INodeTypeDescription ,
66} from 'n8n-workflow' ;
77
8+ import { scanWorkflows } from '../../lib/scanner' ;
9+ import { generateDashboardHtml } from '../../lib/dashboardHtml' ;
10+
811export class TruseraWebhook implements INodeType {
912 description : INodeTypeDescription = {
1013 displayName : 'Trusera Webhook' ,
1114 name : 'truseraWebhook' ,
1215 icon : 'file:trusera.svg' ,
1316 group : [ 'trigger' ] ,
1417 version : 1 ,
15- subtitle : 'AI Security Trigger ' ,
18+ subtitle : 'AI Security Dashboard ' ,
1619 description :
17- 'Pre-configured webhook trigger for the Trusera AI-BOM dashboard. Connect to a Trusera Dashboard node for a one-click security dashboard .' ,
20+ 'One-node security dashboard — add n8n API credentials, activate, and visit /webhook/trusera to see your AI-BOM report .' ,
1821 defaults : {
1922 name : 'Trusera Webhook' ,
20- } ,
23+ webhookId : 'trusera-ai-bom' ,
24+ } as INodeTypeDescription [ 'defaults' ] ,
2125 inputs : [ ] ,
2226 outputs : [ 'main' ] ,
27+ credentials : [
28+ {
29+ name : 'truseraApi' ,
30+ required : true ,
31+ } ,
32+ ] ,
2333 webhooks : [
2434 {
2535 name : 'default' ,
@@ -29,22 +39,79 @@ export class TruseraWebhook implements INodeType {
2939 isFullPath : true ,
3040 } ,
3141 ] ,
32- properties : [ ] ,
42+ properties : [
43+ {
44+ displayName : 'Dashboard Password' ,
45+ name : 'password' ,
46+ type : 'string' ,
47+ typeOptions : { password : true } ,
48+ default : '' ,
49+ description :
50+ 'Optional. If set, the dashboard is AES-256-GCM encrypted and visitors must enter this password to view it.' ,
51+ } ,
52+ ] ,
3353 } ;
3454
3555 async webhook ( this : IWebhookFunctions ) : Promise < IWebhookResponseData > {
36- const req = this . getRequestObject ( ) ;
56+ const res = this . getResponseObject ( ) ;
57+
58+ try {
59+ const creds = await this . getCredentials ( 'truseraApi' ) ;
60+ const baseUrl = ( ( creds . baseUrl as string ) || 'http://localhost:5678' ) . replace ( / \/ $ / , '' ) ;
61+ const apiKey = creds . apiKey as string ;
62+ const password = this . getNodeParameter ( 'password' , '' ) as string ;
63+
64+ // Fetch all workflows via n8n REST API (paginated)
65+ const allWorkflows : Array < Record < string , unknown > > = [ ] ;
66+ let cursor : string | null = null ;
67+ do {
68+ const url =
69+ `${ baseUrl } /api/v1/workflows?limit=100` +
70+ ( cursor ? `&cursor=${ cursor } ` : '' ) ;
71+ const resp = await fetch ( url , {
72+ headers : {
73+ 'X-N8N-API-KEY' : apiKey ,
74+ 'Accept' : 'application/json' ,
75+ } ,
76+ } ) ;
77+ if ( ! resp . ok ) {
78+ throw new Error ( `n8n API error: ${ resp . status } ${ await resp . text ( ) } ` ) ;
79+ }
80+ const data = ( await resp . json ( ) ) as {
81+ data : Array < Record < string , unknown > > ;
82+ nextCursor ?: string ;
83+ } ;
84+ allWorkflows . push ( ...data . data ) ;
85+ cursor = data . nextCursor ?? null ;
86+ } while ( cursor ) ;
87+
88+ // Scan all workflows
89+ const workflows = allWorkflows . map ( ( wf ) => ( {
90+ data : wf ,
91+ filePath : ( wf . name as string ) || ( wf . id as string ) || 'unknown' ,
92+ } ) ) ;
93+ const scanResult = scanWorkflows ( workflows ) ;
94+
95+ // Generate HTML dashboard
96+ const html = generateDashboardHtml ( scanResult , password || undefined ) ;
97+
98+ // Serve HTML directly
99+ res . setHeader ( 'Content-Type' , 'text/html; charset=utf-8' ) ;
100+ res . status ( 200 ) . end ( html ) ;
101+ } catch ( err ) {
102+ res . setHeader ( 'Content-Type' , 'text/html; charset=utf-8' ) ;
103+ res . status ( 500 ) . end (
104+ `<!DOCTYPE html><html><body style="font-family:sans-serif;padding:40px">` +
105+ `<h1>Trusera Dashboard Error</h1>` +
106+ `<pre style="color:red">${ ( err as Error ) . message } </pre>` +
107+ `</body></html>` ,
108+ ) ;
109+ }
110+
37111 return {
112+ noWebhookResponse : true ,
38113 workflowData : [
39- [
40- {
41- json : {
42- headers : req . headers as Record < string , unknown > ,
43- params : req . query as Record < string , unknown > ,
44- timestamp : new Date ( ) . toISOString ( ) ,
45- } ,
46- } ,
47- ] ,
114+ [ { json : { served : true , timestamp : new Date ( ) . toISOString ( ) } } ] ,
48115 ] ,
49116 } ;
50117 }
0 commit comments