node-server-engine 1.12.0 → 1.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/entities/PubSub/PubSub.d.ts +6 -20
- package/dist/entities/PubSub/PubSub.js +46 -102
- package/dist/entities/PubSub/PubSub.js.map +1 -1
- package/dist/entities/Server/Server.js +2 -1
- package/dist/entities/Server/Server.js.map +1 -1
- package/dist/utils/verificationToken/verificationToken.js +12 -0
- package/dist/utils/verificationToken/verificationToken.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,38 +1,24 @@
|
|
|
1
1
|
import { Attributes } from '@google-cloud/pubsub';
|
|
2
2
|
import { PubSubAddSubscriberOptions, PubSubMessageHandler, PubSubPublisherOptions } from './PubSub.types';
|
|
3
|
-
/**
|
|
4
|
-
* This is a singleton service who's role is to manage pub/sub connections
|
|
5
|
-
* It is the preferred way to interact with Pub/Sub as it manages the underlying subscription and handler objects
|
|
6
|
-
* It avoids potential subscription splitting on an instance
|
|
7
|
-
*/
|
|
8
3
|
export declare const PubSub: {
|
|
9
4
|
/**
|
|
10
|
-
*
|
|
5
|
+
* ✅ FAST + NON-BLOCKING INIT
|
|
11
6
|
*/
|
|
12
7
|
init(): Promise<void>;
|
|
13
8
|
/**
|
|
14
|
-
* Shutdown
|
|
9
|
+
* Shutdown
|
|
15
10
|
*/
|
|
16
11
|
shutdown(): Promise<void>;
|
|
17
12
|
/**
|
|
18
|
-
*
|
|
19
|
-
* @param topic - Topic name or array of topic names
|
|
20
|
-
* @param options - Publisher configuration options
|
|
13
|
+
* Publisher registration
|
|
21
14
|
*/
|
|
22
15
|
addPublisher(topic: string | Array<string>, options?: PubSubPublisherOptions): void;
|
|
23
16
|
/**
|
|
24
|
-
*
|
|
25
|
-
* @param subscription - Name of the Pub/Sub subscription
|
|
26
|
-
* @param handler - Function or list of functions to set as message handlers
|
|
27
|
-
* @param options - Indicates that the additional options need to handle on Subscriber
|
|
17
|
+
* Subscriber registration
|
|
28
18
|
*/
|
|
29
|
-
addSubscriber<T>(
|
|
19
|
+
addSubscriber<T>(subscriptionName: string, handler: PubSubMessageHandler<T> | Array<PubSubMessageHandler<T>>, options?: PubSubAddSubscriberOptions): void;
|
|
30
20
|
/**
|
|
31
|
-
*
|
|
32
|
-
* @param topic - Name of the topic
|
|
33
|
-
* @param message - Content of the message, must be an object that can be JSON stringified
|
|
34
|
-
* @param attributes - Attributes for this message, can be used to filter messages on the subscription
|
|
35
|
-
* @param orderingKey - Enforce order guarantee of messages sent with the same key
|
|
21
|
+
* ✅ SAFE PUBLISH (with retry)
|
|
36
22
|
*/
|
|
37
23
|
publish(topic: string, message: unknown, attributes?: Attributes, orderingKey?: string): Promise<void>;
|
|
38
24
|
};
|
|
@@ -10,91 +10,71 @@ var _EngineError = require("../EngineError");
|
|
|
10
10
|
var _LifecycleController = require("../LifecycleController");
|
|
11
11
|
var _utils = require("../../utils");
|
|
12
12
|
const namespace = 'engine:PubSub';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Associative containing all the subscriptions sorted by topic registered for publishing
|
|
16
|
-
*/
|
|
17
13
|
const subscriptions = {};
|
|
14
|
+
const topics = {};
|
|
18
15
|
|
|
19
16
|
/**
|
|
20
|
-
*
|
|
17
|
+
* Add basic retry wrapper
|
|
21
18
|
*/
|
|
22
|
-
const
|
|
23
|
-
|
|
19
|
+
const withRetry = async (fn, retries = 3, delay = 500) => {
|
|
20
|
+
let lastError;
|
|
21
|
+
for (let i = 0; i < retries; i++) {
|
|
22
|
+
try {
|
|
23
|
+
return await fn();
|
|
24
|
+
} catch (err) {
|
|
25
|
+
lastError = err;
|
|
26
|
+
await new Promise(res => setTimeout(res, delay * (i + 1)));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
throw lastError;
|
|
30
|
+
};
|
|
24
31
|
|
|
25
32
|
/**
|
|
26
|
-
*
|
|
27
|
-
* It is the preferred way to interact with Pub/Sub as it manages the underlying subscription and handler objects
|
|
28
|
-
* It avoids potential subscription splitting on an instance
|
|
33
|
+
* PubSub client with safer defaults
|
|
29
34
|
*/
|
|
35
|
+
const pubSubClient = new _pubsub.PubSub({
|
|
36
|
+
apiEndpoint: 'pubsub.googleapis.com'
|
|
37
|
+
});
|
|
30
38
|
const PubSub = exports.PubSub = {
|
|
31
39
|
/**
|
|
32
|
-
*
|
|
40
|
+
* ✅ FAST + NON-BLOCKING INIT
|
|
33
41
|
*/
|
|
34
42
|
async init() {
|
|
35
43
|
(0, _utils.reportDebug)({
|
|
36
44
|
namespace,
|
|
37
|
-
message: `Initializing Pub/Sub`
|
|
45
|
+
message: `Initializing Pub/Sub (safe mode)`
|
|
38
46
|
});
|
|
39
|
-
// Check that all topics/permissions exist, and that we have permission on them
|
|
40
|
-
await Promise.all([
|
|
41
|
-
// Test that all topics actually exist and that we have permissions
|
|
42
|
-
...Object.values(topics).map(async topic => {
|
|
43
|
-
const [exists] = await topic.exists();
|
|
44
|
-
if (!exists) throw new _EngineError.EngineError(`Topic ${topic.name} does not exist`);
|
|
45
|
-
// Test permission ton publish to topic
|
|
46
|
-
const [permissions] = await topic.iam.testPermissions(['pubsub.topics.publish']);
|
|
47
|
-
const hasPermissions = Object.values(permissions).every(success => success);
|
|
48
|
-
if (!hasPermissions) throw new _EngineError.EngineError(`Instance does not have appropriate permissions to publish to topic ${topic.name}`);
|
|
49
|
-
}),
|
|
50
|
-
// Test that all subscriptions actually exist and that we have permissions
|
|
51
|
-
...Object.values(subscriptions).map(async ({
|
|
52
|
-
subscription
|
|
53
|
-
}) => {
|
|
54
|
-
const [exists] = await subscription.exists();
|
|
55
|
-
if (!exists) throw new _EngineError.EngineError(`Subscription ${subscription.name} does not exist`);
|
|
56
|
-
// Test permission ton consume subscription
|
|
57
|
-
const [permissions] = await subscription.iam.testPermissions(['pubsub.subscriptions.consume']);
|
|
58
|
-
const hasPermissions = Object.values(permissions).every(success => success);
|
|
59
|
-
if (!hasPermissions) throw new _EngineError.EngineError(`Instance does not have appropriate permissions to subscribe to subscription ${subscription.name}`);
|
|
60
|
-
})]);
|
|
61
|
-
|
|
62
|
-
// Register handlers on the subscriptions to activate them
|
|
63
47
|
for (const {
|
|
64
48
|
subscription,
|
|
65
49
|
handlers,
|
|
66
50
|
isDebezium
|
|
67
51
|
} of Object.values(subscriptions)) {
|
|
68
52
|
subscription.on('message', message => {
|
|
69
|
-
(0, _handleMessage.handleMessage)(message, subscription, handlers, isDebezium).catch(
|
|
70
|
-
|
|
71
|
-
|
|
53
|
+
(0, _handleMessage.handleMessage)(message, subscription, handlers, isDebezium).catch(_utils.reportError);
|
|
54
|
+
});
|
|
55
|
+
subscription.on('error', error => {
|
|
56
|
+
(0, _utils.reportError)(error);
|
|
72
57
|
});
|
|
73
58
|
}
|
|
74
59
|
_LifecycleController.LifecycleController.register(PubSub);
|
|
75
60
|
},
|
|
76
61
|
/**
|
|
77
|
-
* Shutdown
|
|
62
|
+
* Shutdown
|
|
78
63
|
*/
|
|
79
64
|
async shutdown() {
|
|
80
65
|
(0, _utils.reportDebug)({
|
|
81
66
|
namespace,
|
|
82
67
|
message: `Shutting down Pub/Sub`
|
|
83
68
|
});
|
|
84
|
-
// Flush all pending messages and shut down connections
|
|
85
69
|
await Promise.all([...Object.values(topics).map(async topic => topic.flush()), ...Object.values(subscriptions).map(async ({
|
|
86
70
|
subscription
|
|
87
71
|
}) => subscription.close())]);
|
|
88
|
-
// Delete the stored topics/subscriptions
|
|
89
72
|
Object.keys(topics).forEach(topic => delete topics[topic]);
|
|
90
73
|
Object.keys(subscriptions).forEach(subscription => delete subscriptions[subscription]);
|
|
91
|
-
// Close the client
|
|
92
74
|
await pubSubClient.close();
|
|
93
75
|
},
|
|
94
76
|
/**
|
|
95
|
-
*
|
|
96
|
-
* @param topic - Topic name or array of topic names
|
|
97
|
-
* @param options - Publisher configuration options
|
|
77
|
+
* Publisher registration
|
|
98
78
|
*/
|
|
99
79
|
addPublisher(topic, options) {
|
|
100
80
|
const topicArray = topic instanceof Array ? topic : [topic];
|
|
@@ -103,104 +83,68 @@ const PubSub = exports.PubSub = {
|
|
|
103
83
|
message: `Registering publisher for topics '${topicArray.join("', '")}'`,
|
|
104
84
|
data: options
|
|
105
85
|
});
|
|
106
|
-
// Register a new topic if it does not exist yet
|
|
107
86
|
for (const topic of topicArray) {
|
|
108
|
-
if (!
|
|
87
|
+
if (!topics[topic]) {
|
|
109
88
|
topics[topic] = pubSubClient.topic(topic, {
|
|
110
89
|
enableOpenTelemetryTracing: true,
|
|
111
90
|
messageOrdering: options?.enableMessageOrdering ?? false,
|
|
112
91
|
batching: options?.batching ? {
|
|
113
92
|
maxMessages: options.batching.maxMessages ?? 100,
|
|
114
93
|
maxBytes: options.batching.maxBytes ?? 1024 * 1024,
|
|
115
|
-
// 1MB
|
|
116
94
|
maxMilliseconds: options.batching.maxMilliseconds ?? 100
|
|
117
|
-
} : undefined,
|
|
118
|
-
gaxOpts: options?.retry ? {
|
|
119
|
-
retry: {
|
|
120
|
-
retryCodes: [10, 14],
|
|
121
|
-
// ABORTED, UNAVAILABLE
|
|
122
|
-
backoffSettings: {
|
|
123
|
-
initialRetryDelayMillis: options.retry.initialDelayMillis ?? 100,
|
|
124
|
-
maxRetryDelayMillis: options.retry.maxDelayMillis ?? 60000,
|
|
125
|
-
retryDelayMultiplier: options.retry.delayMultiplier ?? 1.3,
|
|
126
|
-
initialRpcTimeoutMillis: 60000,
|
|
127
|
-
maxRpcTimeoutMillis: 600000,
|
|
128
|
-
rpcTimeoutMultiplier: 1.0,
|
|
129
|
-
totalTimeoutMillis: 600000
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
95
|
} : undefined
|
|
133
96
|
});
|
|
134
97
|
}
|
|
135
98
|
}
|
|
136
99
|
},
|
|
137
100
|
/**
|
|
138
|
-
*
|
|
139
|
-
* @param subscription - Name of the Pub/Sub subscription
|
|
140
|
-
* @param handler - Function or list of functions to set as message handlers
|
|
141
|
-
* @param options - Indicates that the additional options need to handle on Subscriber
|
|
101
|
+
* Subscriber registration
|
|
142
102
|
*/
|
|
143
|
-
addSubscriber(
|
|
103
|
+
addSubscriber(subscriptionName, handler, options = {}) {
|
|
144
104
|
const handlers = handler instanceof Array ? handler : [handler];
|
|
145
|
-
const isDebezium = options?.isDebezium ?? false;
|
|
146
|
-
const first = options?.first ?? false;
|
|
147
|
-
const flowControl = options?.flowControl;
|
|
148
|
-
const ackDeadline = options?.ackDeadline;
|
|
149
105
|
(0, _utils.reportDebug)({
|
|
150
106
|
namespace,
|
|
151
|
-
message: `Registering subscriber
|
|
152
|
-
data:
|
|
153
|
-
first,
|
|
154
|
-
handlersCount: handlers.length,
|
|
155
|
-
isDebezium,
|
|
156
|
-
flowControl
|
|
157
|
-
}
|
|
107
|
+
message: `Registering subscriber '${subscriptionName}'`,
|
|
108
|
+
data: options
|
|
158
109
|
});
|
|
159
|
-
if (!
|
|
160
|
-
subscriptions[
|
|
161
|
-
subscription: pubSubClient.subscription(
|
|
110
|
+
if (!subscriptions[subscriptionName]) {
|
|
111
|
+
subscriptions[subscriptionName] = {
|
|
112
|
+
subscription: pubSubClient.subscription(subscriptionName, {
|
|
162
113
|
enableOpenTelemetryTracing: true,
|
|
163
|
-
flowControl: flowControl
|
|
164
|
-
maxMessages: flowControl.maxMessages ?? 1000,
|
|
165
|
-
maxBytes: flowControl.maxBytes ?? 100 * 1024 * 1024,
|
|
166
|
-
// 100MB
|
|
167
|
-
allowExcessMessages: flowControl.allowExcessMessages ?? true
|
|
168
|
-
} : {
|
|
114
|
+
flowControl: options.flowControl ?? {
|
|
169
115
|
maxMessages: 1000,
|
|
170
116
|
maxBytes: 100 * 1024 * 1024,
|
|
171
117
|
allowExcessMessages: true
|
|
172
118
|
},
|
|
173
|
-
ackDeadline: ackDeadline ?? 60
|
|
119
|
+
ackDeadline: options.ackDeadline ?? 60
|
|
174
120
|
}),
|
|
175
|
-
handlers: []
|
|
121
|
+
handlers: [],
|
|
122
|
+
isDebezium: options?.isDebezium ?? false
|
|
176
123
|
};
|
|
177
124
|
}
|
|
178
|
-
if (
|
|
179
|
-
if (first) subscriptions[subscription].handlers.push(...handlers);else subscriptions[subscription].handlers.unshift(...handlers);
|
|
125
|
+
if (options.first) subscriptions[subscriptionName].handlers.push(...handlers);else subscriptions[subscriptionName].handlers.unshift(...handlers);
|
|
180
126
|
},
|
|
181
127
|
/**
|
|
182
|
-
*
|
|
183
|
-
* @param topic - Name of the topic
|
|
184
|
-
* @param message - Content of the message, must be an object that can be JSON stringified
|
|
185
|
-
* @param attributes - Attributes for this message, can be used to filter messages on the subscription
|
|
186
|
-
* @param orderingKey - Enforce order guarantee of messages sent with the same key
|
|
128
|
+
* ✅ SAFE PUBLISH (with retry)
|
|
187
129
|
*/
|
|
188
130
|
async publish(topic, message, attributes, orderingKey) {
|
|
189
|
-
if (!topics[topic])
|
|
131
|
+
if (!topics[topic]) {
|
|
132
|
+
throw new _EngineError.EngineError(`No publisher registered for topic '${topic}'`);
|
|
133
|
+
}
|
|
190
134
|
(0, _utils.reportDebug)({
|
|
191
135
|
namespace,
|
|
192
|
-
message: `
|
|
136
|
+
message: `Publishing to '${topic}'`,
|
|
193
137
|
data: {
|
|
194
138
|
message,
|
|
195
139
|
attributes,
|
|
196
140
|
orderingKey
|
|
197
141
|
}
|
|
198
142
|
});
|
|
199
|
-
await topics[topic].publishMessage({
|
|
143
|
+
await withRetry(() => topics[topic].publishMessage({
|
|
200
144
|
json: message,
|
|
201
145
|
attributes,
|
|
202
146
|
orderingKey
|
|
203
|
-
});
|
|
147
|
+
}));
|
|
204
148
|
}
|
|
205
149
|
};
|
|
206
150
|
//# sourceMappingURL=PubSub.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PubSub.js","names":["_pubsub","require","_handleMessage","_EngineError","_LifecycleController","_utils","namespace","subscriptions","topics","pubSubClient","PubsubClient","PubSub","exports","init","reportDebug","message","Promise","all","Object","values","map","topic","exists","EngineError","name","permissions","iam","testPermissions","hasPermissions","every","success","subscription","handlers","isDebezium","on","handleMessage","catch","error","reportError","LifecycleController","register","shutdown","flush","close","keys","forEach","addPublisher","options","topicArray","Array","join","data","includes","enableOpenTelemetryTracing","messageOrdering","enableMessageOrdering","batching","maxMessages","maxBytes","maxMilliseconds","undefined","gaxOpts","retry","retryCodes","backoffSettings","initialRetryDelayMillis","initialDelayMillis","maxRetryDelayMillis","maxDelayMillis","retryDelayMultiplier","delayMultiplier","initialRpcTimeoutMillis","maxRpcTimeoutMillis","rpcTimeoutMultiplier","totalTimeoutMillis","addSubscriber","handler","first","flowControl","ackDeadline","handlersCount","length","allowExcessMessages","push","unshift","publish","attributes","orderingKey","publishMessage","json"],"sources":["../../../src/entities/PubSub/PubSub.ts"],"sourcesContent":["import {\n Attributes,\n PubSub as PubsubClient,\n Message\n} from '@google-cloud/pubsub';\nimport { handleMessage } from './handleMessage';\nimport {\n PubSubTopicsMap,\n PubSubSubscriptionsMap,\n PubSubAddSubscriberOptions,\n PubSubMessageHandler,\n PubSubPublisherOptions\n} from './PubSub.types';\nimport { EngineError } from 'entities/EngineError';\nimport { LifecycleController } from 'entities/LifecycleController';\nimport { reportDebug, reportError } from 'utils';\n\nconst namespace = 'engine:PubSub';\n\n/**\n * Associative containing all the subscriptions sorted by topic registered for publishing\n */\nconst subscriptions: PubSubSubscriptionsMap = {};\n\n/**\n * Associative containing all the topics registered for publishing\n */\nconst topics: PubSubTopicsMap = {};\n\nconst pubSubClient = new PubsubClient();\n\n/**\n * This is a singleton service who's role is to manage pub/sub connections\n * It is the preferred way to interact with Pub/Sub as it manages the underlying subscription and handler objects\n * It avoids potential subscription splitting on an instance\n */\nexport const PubSub = {\n /**\n * Initialize the registered publishers and subscribers\n */\n async init(): Promise<void> {\n reportDebug({ namespace, message: `Initializing Pub/Sub` });\n // Check that all topics/permissions exist, and that we have permission on them\n await Promise.all([\n // Test that all topics actually exist and that we have permissions\n ...Object.values(topics).map(async (topic) => {\n const [exists] = await topic.exists();\n if (!exists)\n throw new EngineError(`Topic ${topic.name} does not exist`);\n // Test permission ton publish to topic\n const [permissions] = await topic.iam.testPermissions([\n 'pubsub.topics.publish'\n ]);\n const hasPermissions = Object.values(permissions).every(\n (success) => success\n );\n if (!hasPermissions)\n throw new EngineError(\n `Instance does not have appropriate permissions to publish to topic ${topic.name}`\n );\n }),\n // Test that all subscriptions actually exist and that we have permissions\n ...Object.values(subscriptions).map(async ({ subscription }) => {\n const [exists] = await subscription.exists();\n if (!exists)\n throw new EngineError(\n `Subscription ${subscription.name} does not exist`\n );\n // Test permission ton consume subscription\n const [permissions] = await subscription.iam.testPermissions([\n 'pubsub.subscriptions.consume'\n ]);\n const hasPermissions = Object.values(permissions).every(\n (success) => success\n );\n if (!hasPermissions)\n throw new EngineError(\n `Instance does not have appropriate permissions to subscribe to subscription ${subscription.name}`\n );\n })\n ]);\n\n // Register handlers on the subscriptions to activate them\n for (const { subscription, handlers, isDebezium } of Object.values(\n subscriptions\n )) {\n subscription.on('message', (message) => {\n handleMessage(\n message as Message,\n subscription,\n handlers,\n isDebezium\n ).catch((error) => {\n reportError(error);\n });\n });\n }\n\n LifecycleController.register(PubSub);\n },\n\n /**\n * Shutdown the registered publishers and subscribers\n */\n async shutdown(): Promise<void> {\n reportDebug({ namespace, message: `Shutting down Pub/Sub` });\n // Flush all pending messages and shut down connections\n await Promise.all([\n ...Object.values(topics).map(async (topic) => topic.flush()),\n ...Object.values(subscriptions).map(async ({ subscription }) =>\n subscription.close()\n )\n ]);\n // Delete the stored topics/subscriptions\n Object.keys(topics).forEach((topic) => delete topics[topic]);\n Object.keys(subscriptions).forEach(\n (subscription) => delete subscriptions[subscription]\n );\n // Close the client\n await pubSubClient.close();\n },\n\n /**\n * Creates a publisher if it does not exist yet\n * @param topic - Topic name or array of topic names\n * @param options - Publisher configuration options\n */\n addPublisher(\n topic: string | Array<string>,\n options?: PubSubPublisherOptions\n ): void {\n const topicArray = topic instanceof Array ? topic : [topic];\n reportDebug({\n namespace,\n message: `Registering publisher for topics '${topicArray.join(\"', '\")}'`,\n data: options\n });\n // Register a new topic if it does not exist yet\n for (const topic of topicArray) {\n if (!Object.keys(topics).includes(topic)) {\n topics[topic] = pubSubClient.topic(topic, {\n enableOpenTelemetryTracing: true,\n messageOrdering: options?.enableMessageOrdering ?? false,\n batching: options?.batching\n ? {\n maxMessages: options.batching.maxMessages ?? 100,\n maxBytes: options.batching.maxBytes ?? 1024 * 1024, // 1MB\n maxMilliseconds: options.batching.maxMilliseconds ?? 100\n }\n : undefined,\n gaxOpts: options?.retry\n ? {\n retry: {\n retryCodes: [10, 14], // ABORTED, UNAVAILABLE\n backoffSettings: {\n initialRetryDelayMillis:\n options.retry.initialDelayMillis ?? 100,\n maxRetryDelayMillis: options.retry.maxDelayMillis ?? 60000,\n retryDelayMultiplier: options.retry.delayMultiplier ?? 1.3,\n initialRpcTimeoutMillis: 60000,\n maxRpcTimeoutMillis: 600000,\n rpcTimeoutMultiplier: 1.0,\n totalTimeoutMillis: 600000\n }\n }\n }\n : undefined\n });\n }\n }\n },\n\n /**\n * Add message handlers to a subscription\n * @param subscription - Name of the Pub/Sub subscription\n * @param handler - Function or list of functions to set as message handlers\n * @param options - Indicates that the additional options need to handle on Subscriber\n */\n addSubscriber<T>(\n subscription: string,\n handler: PubSubMessageHandler<T> | Array<PubSubMessageHandler<T>>,\n options: PubSubAddSubscriberOptions = {}\n ): void {\n const handlers = handler instanceof Array ? handler : [handler];\n const isDebezium = options?.isDebezium ?? false;\n const first = options?.first ?? false;\n const flowControl = options?.flowControl;\n const ackDeadline = options?.ackDeadline;\n\n reportDebug({\n namespace,\n message: `Registering subscriber for subscription '${subscription}'`,\n data: {\n first,\n handlersCount: handlers.length,\n isDebezium,\n flowControl\n }\n });\n if (!Object.keys(subscriptions).includes(subscription)) {\n subscriptions[subscription] = {\n subscription: pubSubClient.subscription(subscription, {\n enableOpenTelemetryTracing: true,\n flowControl: flowControl\n ? {\n maxMessages: flowControl.maxMessages ?? 1000,\n maxBytes: flowControl.maxBytes ?? 100 * 1024 * 1024, // 100MB\n allowExcessMessages: flowControl.allowExcessMessages ?? true\n }\n : {\n maxMessages: 1000,\n maxBytes: 100 * 1024 * 1024,\n allowExcessMessages: true\n },\n ackDeadline: ackDeadline ?? 60\n }),\n handlers: []\n };\n }\n\n if (isDebezium) subscriptions[subscription].isDebezium = true;\n\n if (first) subscriptions[subscription].handlers.push(...handlers);\n else subscriptions[subscription].handlers.unshift(...handlers);\n },\n\n /**\n * Send a message to a Pub/Sub topic\n * @param topic - Name of the topic\n * @param message - Content of the message, must be an object that can be JSON stringified\n * @param attributes - Attributes for this message, can be used to filter messages on the subscription\n * @param orderingKey - Enforce order guarantee of messages sent with the same key\n */\n async publish(\n topic: string,\n message: unknown,\n attributes?: Attributes,\n orderingKey?: string\n ): Promise<void> {\n if (!topics[topic])\n throw new EngineError(`No publisher was registered for topic '${topic}'`);\n reportDebug({\n namespace,\n message: `Sending message to topic '${topic}'`,\n data: { message, attributes, orderingKey }\n });\n await topics[topic].publishMessage({\n json: message,\n attributes,\n orderingKey\n });\n }\n};\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"PubSub.js","names":["_pubsub","require","_handleMessage","_EngineError","_LifecycleController","_utils","namespace","subscriptions","topics","withRetry","fn","retries","delay","lastError","i","err","Promise","res","setTimeout","pubSubClient","PubsubClient","apiEndpoint","PubSub","exports","init","reportDebug","message","subscription","handlers","isDebezium","Object","values","on","handleMessage","catch","reportError","error","LifecycleController","register","shutdown","all","map","topic","flush","close","keys","forEach","addPublisher","options","topicArray","Array","join","data","enableOpenTelemetryTracing","messageOrdering","enableMessageOrdering","batching","maxMessages","maxBytes","maxMilliseconds","undefined","addSubscriber","subscriptionName","handler","flowControl","allowExcessMessages","ackDeadline","first","push","unshift","publish","attributes","orderingKey","EngineError","publishMessage","json"],"sources":["../../../src/entities/PubSub/PubSub.ts"],"sourcesContent":["import {\n Attributes,\n PubSub as PubsubClient,\n Message\n} from '@google-cloud/pubsub';\nimport { handleMessage } from './handleMessage';\nimport {\n PubSubTopicsMap,\n PubSubSubscriptionsMap,\n PubSubAddSubscriberOptions,\n PubSubMessageHandler,\n PubSubPublisherOptions\n} from './PubSub.types';\nimport { EngineError } from 'entities/EngineError';\nimport { LifecycleController } from 'entities/LifecycleController';\nimport { reportDebug, reportError } from 'utils';\n\nconst namespace = 'engine:PubSub';\n\nconst subscriptions: PubSubSubscriptionsMap = {};\nconst topics: PubSubTopicsMap = {};\n\n/**\n * Add basic retry wrapper\n */\nconst withRetry = async <T>(\n fn: () => Promise<T>,\n retries = 3,\n delay = 500\n): Promise<T> => {\n let lastError: any;\n for (let i = 0; i < retries; i++) {\n try {\n return await fn();\n } catch (err) {\n lastError = err;\n await new Promise((res) => setTimeout(res, delay * (i + 1)));\n }\n }\n throw lastError;\n};\n\n/**\n * PubSub client with safer defaults\n */\nconst pubSubClient = new PubsubClient({\n apiEndpoint: 'pubsub.googleapis.com'\n});\n\nexport const PubSub = {\n /**\n * ✅ FAST + NON-BLOCKING INIT\n */\n async init(): Promise<void> {\n reportDebug({ namespace, message: `Initializing Pub/Sub (safe mode)` });\n\n for (const { subscription, handlers, isDebezium } of Object.values(\n subscriptions\n )) {\n subscription.on('message', (message) => {\n handleMessage(\n message as Message,\n subscription,\n handlers,\n isDebezium\n ).catch(reportError);\n });\n\n subscription.on('error', (error) => {\n reportError(error);\n });\n }\n\n LifecycleController.register(PubSub);\n },\n\n /**\n * Shutdown\n */\n async shutdown(): Promise<void> {\n reportDebug({ namespace, message: `Shutting down Pub/Sub` });\n\n await Promise.all([\n ...Object.values(topics).map(async (topic) => topic.flush()),\n ...Object.values(subscriptions).map(async ({ subscription }) =>\n subscription.close()\n )\n ]);\n\n Object.keys(topics).forEach((topic) => delete topics[topic]);\n Object.keys(subscriptions).forEach(\n (subscription) => delete subscriptions[subscription]\n );\n\n await pubSubClient.close();\n },\n\n /**\n * Publisher registration\n */\n addPublisher(\n topic: string | Array<string>,\n options?: PubSubPublisherOptions\n ): void {\n const topicArray = topic instanceof Array ? topic : [topic];\n\n reportDebug({\n namespace,\n message: `Registering publisher for topics '${topicArray.join(\"', '\")}'`,\n data: options\n });\n\n for (const topic of topicArray) {\n if (!topics[topic]) {\n topics[topic] = pubSubClient.topic(topic, {\n enableOpenTelemetryTracing: true,\n messageOrdering: options?.enableMessageOrdering ?? false,\n batching: options?.batching\n ? {\n maxMessages: options.batching.maxMessages ?? 100,\n maxBytes: options.batching.maxBytes ?? 1024 * 1024,\n maxMilliseconds: options.batching.maxMilliseconds ?? 100\n }\n : undefined\n });\n }\n }\n },\n\n /**\n * Subscriber registration\n */\n addSubscriber<T>(\n subscriptionName: string,\n handler: PubSubMessageHandler<T> | Array<PubSubMessageHandler<T>>,\n options: PubSubAddSubscriberOptions = {}\n ): void {\n const handlers = handler instanceof Array ? handler : [handler];\n\n reportDebug({\n namespace,\n message: `Registering subscriber '${subscriptionName}'`,\n data: options\n });\n\n if (!subscriptions[subscriptionName]) {\n subscriptions[subscriptionName] = {\n subscription: pubSubClient.subscription(subscriptionName, {\n enableOpenTelemetryTracing: true,\n flowControl: options.flowControl ?? {\n maxMessages: 1000,\n maxBytes: 100 * 1024 * 1024,\n allowExcessMessages: true\n },\n ackDeadline: options.ackDeadline ?? 60\n }),\n handlers: [],\n isDebezium: options?.isDebezium ?? false\n };\n }\n\n if (options.first)\n subscriptions[subscriptionName].handlers.push(...handlers);\n else subscriptions[subscriptionName].handlers.unshift(...handlers);\n },\n\n /**\n * ✅ SAFE PUBLISH (with retry)\n */\n async publish(\n topic: string,\n message: unknown,\n attributes?: Attributes,\n orderingKey?: string\n ): Promise<void> {\n if (!topics[topic]) {\n throw new EngineError(`No publisher registered for topic '${topic}'`);\n }\n\n reportDebug({\n namespace,\n message: `Publishing to '${topic}'`,\n data: { message, attributes, orderingKey }\n });\n\n await withRetry(() =>\n topics[topic].publishMessage({\n json: message,\n attributes,\n orderingKey\n })\n );\n }\n};"],"mappings":"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","ignoreList":[]}
|
|
@@ -105,7 +105,8 @@ let Server = exports.Server = /*#__PURE__*/function () {
|
|
|
105
105
|
this.app.use((0, _cors.default)({
|
|
106
106
|
origin: process.env.CORS_ORIGIN ?? '*',
|
|
107
107
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
|
|
108
|
-
allowedHeaders: ['Content-Type', 'Authorization']
|
|
108
|
+
allowedHeaders: ['Content-Type', 'Authorization', 'x-verification-token', 'x-verification-otp', 'Access-Control-Allow-Origin'],
|
|
109
|
+
exposedHeaders: ['Access-Control-Allow-Origin']
|
|
109
110
|
}));
|
|
110
111
|
this.registerMiddleware(this.app);
|
|
111
112
|
this.httpServer = (0, _utils.createHttpServer)(this.app);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Server.js","names":["_fs","_interopRequireDefault","require","_path","_promises","_express","_cors","_ws","_SocketClient","_Server2","_Endpoint","_EngineError","_LifecycleController","_PubSub","_SecretManager","_utils","_jwt","_report","_tlsConfig","namespace","Server","exports","config","_classCallCheck2","default","_defineProperty2","AbortController","validate","reportDebug","message","endpoints","globalMiddleware","errorMiddleware","initCallbacks","shutdownCallbacks","syncCallbacks","cron","auth","noFetch","secretManagerConfig","secretManager","checkEnvironment","process","env","RUN_MIGRATION","UNDO_MIGRATION","SQL_HOST","envAssert","isHost","SQL_USER","isString","SQL_PASSWORD","SQL_DB","NODE_ENV","PORT","isPort","SECONDARY_PORT","app","express","use","cors","origin","CORS_ORIGIN","methods","allowedHeaders","registerMiddleware","httpServer","createHttpServer","secondaryApp","secondaryMiddleware","defaultErrorMiddleware","forEach","middleware","secondaryHttpServer","disable","server","headersTimeout","keepAliveTimeout","webSocket","attachWebSocket","_createClass2","key","value","setupGlobalErrorLogging","on","err","reportError","reportInfo","setTimeout","exit","reason","Error","String","init","SecretManager","initKeySets","callback","runCallback","Promise","all","map","PubSub","job","runCronJob","resolve","listen","data","port","secondaryPort","nodeEnv","handleSignal","watchTlsCaFileChange","shutdown","reject","listening","address","close","error","wss","clients","socket","socketKillTimeout","WEBSOCKET_CLOSE_TIMEOUT","parseInt","setTimeoutPromise","readyState","CLOSING","OPEN","terminate","LifecycleController","shutdownRunningInstances","abortController","abort","fileWatchers","watcher","shutdownKeySets","shutdownSignals","signal","removeAllListeners","getApp","getSecondaryApp","defaultMiddleware","path","endpoint","register","options","WebSocketServer","request","SocketClient","client","isShuttingDown","then","catch","file","fs","watch","event","loadTlsConfig","setSecureContext","tlsConfig","push","handler","name","interval","undefined","function","apply","parameters","some","Endpoint","EngineError","Object","values","validator","Array"],"sources":["../../../src/entities/Server/Server.ts"],"sourcesContent":["import fs from 'fs';\nimport http from 'http';\nimport https from 'https';\nimport { AddressInfo } from 'net';\nimport path from 'path';\nimport { setTimeout as setTimeoutPromise } from 'timers/promises';\nimport { ValidatorSpec } from 'envalid';\nimport express, { Application } from 'express';\nimport cors from 'cors';\nimport { Server as WebSocketServer } from 'ws';\nimport { SocketClient } from '../SocketClient';\nimport {\n defaultMiddleware,\n defaultErrorMiddleware,\n secondaryMiddleware,\n shutdownSignals\n} from './Server.const';\nimport {\n ServerOptions,\n ServerCronJon,\n ServerAuthConfig,\n ServerWebSocketConfig,\n ServerGlobalMiddleware,\n ServerErrorMiddleWare,\n ServerCallback\n} from './Server.types';\nimport { Endpoint } from 'entities/Endpoint';\nimport { EngineError } from 'entities/EngineError';\nimport { LifecycleController } from 'entities/LifecycleController';\nimport { PubSub } from 'entities/PubSub';\nimport { SecretManager } from 'entities/SecretManager';\nimport { checkEnvironment, envAssert, createHttpServer } from 'utils';\nimport { initKeySets, shutdownKeySets } from 'utils/jwt';\nimport { reportInfo, reportError, reportDebug } from 'utils/report';\nimport { tlsConfig, loadTlsConfig, TlsConfig } from 'utils/tlsConfig';\n\nconst namespace = 'engine:Server';\n\n/** Main server class */\nexport class Server {\n /** The express application powering the main endpoints */\n private app: Application;\n /** The node HTTP(S) server, serving the main express application */\n private httpServer: http.Server | https.Server;\n /** The express application powering the support endpoints */\n private secondaryApp: Application;\n /** The node HTTP(S) server, serving the secondary express application */\n private secondaryHttpServer: http.Server | https.Server;\n /** List of endpoint instances that the server should handle */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private endpoints: Array<Endpoint<any>>;\n /** Mapping of env variable name with functions that should run to check their validity */\n private checkEnvironment: Record<string, ValidatorSpec<unknown>>;\n /** Middleware that should run on every request before the endpoint specific logic */\n private globalMiddleware: Array<ServerGlobalMiddleware>;\n /** Error middleware that should run on every request after the endpoint specific logic, but before the generic global middleware */\n private errorMiddleware: Array<\n ServerGlobalMiddleware | ServerErrorMiddleWare\n >;\n /** Functions that should be called on server startup */\n private initCallbacks: Array<ServerCallback>;\n /** Functions that should be called on server shutdown */\n private shutdownCallbacks: Array<ServerCallback>;\n /** When set to true, this indicates that init callbacks should run synchronously in the order they are declared. By default they all run in parallel */\n private syncCallbacks: boolean;\n /** Config to set regularly running jobs on the server */\n private cron?: Array<ServerCronJon>;\n /** Global auth related configuration */\n private auth: ServerAuthConfig;\n /** Secret Manager configuration */\n private secretManagerConfig?: ServerOptions['secretManager'];\n /** WebSocket server instance, if configured */\n private wss?: WebSocketServer;\n /** Abort controller that handles signals for the server */\n private abortController = new AbortController();\n /** File system watchers for TLS certificate changes */\n private fileWatchers: Array<fs.FSWatcher> = [];\n\n /** Create a server */\n public constructor(config: ServerOptions) {\n this.validate(config);\n\n reportDebug({\n namespace,\n message: `Creating server`\n });\n\n this.endpoints = config.endpoints;\n this.globalMiddleware = config.globalMiddleware ?? [];\n this.errorMiddleware = config.errorMiddleware ?? [];\n this.initCallbacks = config.initCallbacks ?? [];\n this.shutdownCallbacks = config.shutdownCallbacks ?? [];\n this.syncCallbacks = config.syncCallbacks ?? false;\n this.cron = config.cron;\n this.auth = { noFetch: false, ...config.auth };\n this.secretManagerConfig = config.secretManager;\n\n // The minimal environment check is different between the migration modes of execution and the regular server execution mode\n // When running in migration mode, we only check that we have SQL related environment variables\n this.checkEnvironment =\n process.env.RUN_MIGRATION === 'true' ||\n process.env.UNDO_MIGRATION === 'true'\n ? {\n SQL_HOST: envAssert.isHost(),\n SQL_USER: envAssert.isString(),\n SQL_PASSWORD: envAssert.isString(),\n SQL_DB: envAssert.isString()\n }\n : {\n NODE_ENV: envAssert.isString(),\n PORT: envAssert.isPort(),\n SECONDARY_PORT: envAssert.isPort(),\n ...config.checkEnvironment\n };\n\n checkEnvironment(this.checkEnvironment);\n\n // Create the primary express core that serves the business logic\n this.app = express();\n this.app.use(\n cors({\n origin: process.env.CORS_ORIGIN ?? '*',\n methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],\n allowedHeaders: ['Content-Type', 'Authorization']\n })\n );\n this.registerMiddleware(this.app);\n this.httpServer = createHttpServer(this.app);\n\n // Create a secondary express core that servers metrics and analytics\n // This will be served on a different port, allowing to specify different firewall rules for these routes\n this.secondaryApp = express();\n [...secondaryMiddleware, ...defaultErrorMiddleware].forEach(\n (middleware) => {\n if (middleware) this.secondaryApp.use(middleware);\n }\n );\n this.secondaryHttpServer = createHttpServer(this.secondaryApp);\n\n // Remove the express headers\n this.app.disable('x-powered-by');\n this.secondaryApp.disable('x-powered-by');\n\n // We remove all node timeout on connections and requests\n // This responsibility is delegated to the front-facing load balancer\n [this.httpServer, this.secondaryHttpServer].forEach((server) => {\n server.headersTimeout = 0;\n server.keepAliveTimeout = 0;\n });\n\n if (config.webSocket) {\n this.attachWebSocket(config.webSocket);\n }\n }\n\n /**\n * Setup global error handlers for uncaught exceptions and unhandled rejections\n */\n private setupGlobalErrorLogging(): void {\n process.on('uncaughtException', (err) => {\n reportError(err);\n reportInfo('💥 Uncaught exception - process will exit');\n // Give time for logs to flush before exiting\n setTimeout(() => process.exit(1), 1000);\n });\n \n process.on('unhandledRejection', (reason: unknown) => {\n reportError(reason instanceof Error ? reason : new Error(String(reason)));\n reportInfo('💥 Unhandled rejection - process will exit');\n // Give time for logs to flush before exiting\n setTimeout(() => process.exit(1), 1000);\n });\n }\n\n /** Start the server */\n public async init(): Promise<void> {\n this.setupGlobalErrorLogging();\n reportDebug({ namespace, message: `Starting Server` });\n \n // Load secrets from Secret Manager FIRST (before anything else that might need them)\n if (this.secretManagerConfig) {\n await SecretManager.init(this.secretManagerConfig);\n }\n \n // Fetch the keys for JWT authentication\n // Ignore in test environment as we do symmetrical token signing\n if (process.env.NODE_ENV !== 'test' && !this.auth.noFetch)\n await initKeySets();\n // Run all the init callbacks\n // We can either run them all 1/1 synchronously or run them all in parallel (default)\n // Callback can either be direct function or an object with function and parameters attributes\n\n if (this.syncCallbacks) {\n for (const callback of this.initCallbacks) {\n await this.runCallback(callback);\n }\n } else {\n await Promise.all(\n this.initCallbacks.map((callback) => this.runCallback(callback))\n );\n }\n\n // Initialize the Pub/Sub module\n await PubSub.init();\n // Run all the cronjob\n if (this.cron) {\n this.cron.forEach((job) => {\n this.runCronJob(job);\n });\n }\n\n // Start listening to requests\n await Promise.all([\n new Promise<void>((resolve) =>\n this.httpServer.listen(process.env.PORT, () => {\n resolve();\n })\n ),\n new Promise<void>((resolve) =>\n this.secondaryHttpServer.listen(process.env.SECONDARY_PORT, () => {\n resolve();\n })\n )\n ]);\n reportInfo({\n message: 'SERVER_RUNNING',\n data: {\n port: process.env.PORT,\n secondaryPort: process.env.SECONDARY_PORT,\n nodeEnv: process.env.NODE_ENV\n }\n });\n\n this.handleSignal();\n\n // Watch TSL config files changing\n this.watchTlsCaFileChange();\n }\n\n /** Graceful shutdown */\n public async shutdown(): Promise<void> {\n reportDebug({ namespace, message: `Shutting down Server` });\n\n // Stop listening to HTTP requests\n await Promise.all(\n [this.httpServer, this.secondaryHttpServer].map(\n (server) =>\n new Promise<void>((resolve, reject) => {\n // Ignore if the server was already stopped\n // We have observed cases where SIGTERM was thrown multiple times\n if (!server.listening) {\n resolve();\n return;\n }\n \n const { port } = server.address() as AddressInfo;\n \n server.close((error) => {\n if (error) {\n reportError(error);\n reject(error);\n } else {\n reportInfo({\n message: `Stopped listening to requests on port ${port}`\n });\n resolve();\n }\n });\n })\n )\n );\n\n // Close all socket connections\n if (this.wss) {\n // First ask them to close gracefully\n this.wss.clients.forEach((socket) => {\n socket.close();\n });\n \n // Hard terminate all connections still open after a defined time\n const socketKillTimeout = process.env.WEBSOCKET_CLOSE_TIMEOUT\n ? parseInt(process.env.WEBSOCKET_CLOSE_TIMEOUT, 10)\n : 5000;\n \n await setTimeoutPromise(socketKillTimeout);\n \n this.wss.clients.forEach((socket) => {\n if (\n socket.readyState === socket.CLOSING ||\n socket.readyState === socket.OPEN\n ) {\n socket.terminate();\n }\n });\n \n // Close the WebSocket server itself\n await new Promise<void>((resolve, reject) => {\n this.wss?.close((error) => {\n if (error) {\n reportError(error);\n reject(error);\n } else {\n reportInfo({ message: 'WebSocket server closed' });\n resolve();\n }\n });\n });\n }\n\n // Run all the shutdown callback\n if (this.syncCallbacks) {\n for (const callback of this.shutdownCallbacks) {\n await this.runCallback(callback);\n }\n } else {\n await Promise.all(\n this.shutdownCallbacks.map((callback) => this.runCallback(callback))\n );\n }\n\n // Shutdown all the running entities\n await LifecycleController.shutdownRunningInstances();\n\n // Abort all the cronjob\n if (this.abortController) this.abortController.abort();\n\n // Close all file watchers\n this.fileWatchers.forEach((watcher) => watcher.close());\n\n // Stop the keyset timers\n shutdownKeySets();\n\n shutdownSignals.forEach((signal) => process.removeAllListeners(signal));\n }\n\n /** Get the express app instance of the server */\n public getApp(): Application {\n return this.app;\n }\n\n /** Get the express app instance of the server */\n public getSecondaryApp(): Application {\n return this.secondaryApp;\n }\n\n /** Registers all the middleware with the express app */\n private registerMiddleware(app: Application): void {\n // Global middleware can specify a path to which they apply\n [...defaultMiddleware, ...this.globalMiddleware].forEach((middleware) => {\n if (typeof middleware === 'function') {\n app.use(middleware);\n } else {\n app.use(middleware.path, middleware.middleware);\n }\n });\n // Call the register method of each endpoint\n this.endpoints.forEach((endpoint) => {\n endpoint.register(app);\n });\n // Add error middleware\n [...this.errorMiddleware, ...defaultErrorMiddleware].forEach(\n (middleware) => {\n if (typeof middleware === 'function') {\n app.use(middleware);\n } else {\n app.use(middleware.path, middleware.middleware);\n }\n }\n );\n }\n\n /** Add a handler for WebSocket connections */\n private attachWebSocket(options: ServerWebSocketConfig): void {\n this.wss = new WebSocketServer({\n ...options.server,\n server: this.httpServer\n })\n .on('connection', (webSocket, request) => {\n new SocketClient(webSocket, request, options.client);\n })\n .on('error', (error) => {\n reportError(error);\n });\n }\n\n /** Call shutdown if signal gets sent to the process */\n private handleSignal(): void {\n let isShuttingDown = false;\n \n shutdownSignals.forEach((signal) =>\n process.on(signal, () => {\n // Prevent multiple shutdown attempts\n if (isShuttingDown) {\n reportInfo(`Already shutting down, ignoring ${signal}`);\n return;\n }\n \n isShuttingDown = true;\n reportInfo(`Received shutdown signal [${signal}]`);\n \n this.shutdown()\n .then(() => {\n reportInfo('Graceful shutdown completed successfully');\n process.exit(0);\n })\n .catch((error) => {\n reportError(error);\n process.exit(1);\n });\n })\n );\n }\n\n /** Watch the TLS certificate change event */\n private watchTlsCaFileChange(): void {\n [\n 'TLS_REQUEST_KEY',\n 'TLS_REQUEST_CERT',\n 'TLS_REQUEST_CA',\n 'TLS_SERVER_KEY',\n 'TLS_SERVER_CERT',\n 'TLS_CA'\n ].forEach((key) => {\n const file = process.env[key];\n if (file) {\n const watcher = fs.watch(path.resolve(file), (event) => {\n if (event !== 'change') return;\n reportInfo(`Reloading TLS config [${file} changed]`);\n loadTlsConfig();\n (this.httpServer as https.Server).setSecureContext(\n tlsConfig as TlsConfig\n );\n });\n this.fileWatchers.push(watcher);\n }\n });\n }\n\n /** Executes a recurring job */\n private runCronJob(job: ServerCronJon): void {\n (async (): Promise<void> => {\n reportInfo({ message: `Starting scheduled job \"${job.handler.name}\"` });\n try {\n await job.handler();\n reportInfo({\n message: `Job \"${job.handler.name}\" finished, starting again in ${job.interval} seconds`\n });\n await setTimeoutPromise(job.interval * 1000, undefined, {\n signal: this.abortController.signal\n });\n this.runCronJob(job);\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n reportDebug({\n namespace,\n message: `Scheduled job \"${job.handler?.name}\" has been aborted`\n });\n } else {\n throw error;\n }\n }\n })().catch((error) => {\n reportError(error);\n });\n }\n\n /** Run a shutdown/init callback */\n private runCallback(callback: ServerCallback): unknown {\n return typeof callback === 'function'\n ? callback()\n : callback.function.apply(null, callback.parameters ?? []);\n }\n\n /**\n * Validate the Server constructor configuration\n */\n private validate(config: ServerOptions): void {\n if (\n !config.endpoints ||\n config.endpoints.some((endpoint) => !(endpoint instanceof Endpoint))\n )\n throw new EngineError(\n 'Server endpoints should be declared and all be Endpoint instances'\n );\n\n if (\n config.checkEnvironment &&\n Object.values(config.checkEnvironment).some(\n (validator) => typeof validator !== 'function'\n )\n )\n throw new EngineError(\n 'Some of the environment checks are not valid functions'\n );\n\n if (\n config.initCallbacks &&\n (!(config.initCallbacks instanceof Array) ||\n config.initCallbacks.some(\n (callback) =>\n !(\n typeof callback === 'function' ||\n typeof callback?.function === 'function'\n )\n ))\n )\n throw new EngineError('Init callbacks is not a list of functions');\n\n if (\n config.shutdownCallbacks &&\n (!(config.shutdownCallbacks instanceof Array) ||\n config.shutdownCallbacks.some(\n (callback) =>\n !(\n typeof callback === 'function' ||\n typeof callback?.function === 'function'\n )\n ))\n )\n throw new EngineError('Shutdown callbacks is not a list of functions');\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"Server.js","names":["_fs","_interopRequireDefault","require","_path","_promises","_express","_cors","_ws","_SocketClient","_Server2","_Endpoint","_EngineError","_LifecycleController","_PubSub","_SecretManager","_utils","_jwt","_report","_tlsConfig","namespace","Server","exports","config","_classCallCheck2","default","_defineProperty2","AbortController","validate","reportDebug","message","endpoints","globalMiddleware","errorMiddleware","initCallbacks","shutdownCallbacks","syncCallbacks","cron","auth","noFetch","secretManagerConfig","secretManager","checkEnvironment","process","env","RUN_MIGRATION","UNDO_MIGRATION","SQL_HOST","envAssert","isHost","SQL_USER","isString","SQL_PASSWORD","SQL_DB","NODE_ENV","PORT","isPort","SECONDARY_PORT","app","express","use","cors","origin","CORS_ORIGIN","methods","allowedHeaders","exposedHeaders","registerMiddleware","httpServer","createHttpServer","secondaryApp","secondaryMiddleware","defaultErrorMiddleware","forEach","middleware","secondaryHttpServer","disable","server","headersTimeout","keepAliveTimeout","webSocket","attachWebSocket","_createClass2","key","value","setupGlobalErrorLogging","on","err","reportError","reportInfo","setTimeout","exit","reason","Error","String","init","SecretManager","initKeySets","callback","runCallback","Promise","all","map","PubSub","job","runCronJob","resolve","listen","data","port","secondaryPort","nodeEnv","handleSignal","watchTlsCaFileChange","shutdown","reject","listening","address","close","error","wss","clients","socket","socketKillTimeout","WEBSOCKET_CLOSE_TIMEOUT","parseInt","setTimeoutPromise","readyState","CLOSING","OPEN","terminate","LifecycleController","shutdownRunningInstances","abortController","abort","fileWatchers","watcher","shutdownKeySets","shutdownSignals","signal","removeAllListeners","getApp","getSecondaryApp","defaultMiddleware","path","endpoint","register","options","WebSocketServer","request","SocketClient","client","isShuttingDown","then","catch","file","fs","watch","event","loadTlsConfig","setSecureContext","tlsConfig","push","handler","name","interval","undefined","function","apply","parameters","some","Endpoint","EngineError","Object","values","validator","Array"],"sources":["../../../src/entities/Server/Server.ts"],"sourcesContent":["import fs from 'fs';\nimport http from 'http';\nimport https from 'https';\nimport { AddressInfo } from 'net';\nimport path from 'path';\nimport { setTimeout as setTimeoutPromise } from 'timers/promises';\nimport { ValidatorSpec } from 'envalid';\nimport express, { Application } from 'express';\nimport cors from 'cors';\nimport { Server as WebSocketServer } from 'ws';\nimport { SocketClient } from '../SocketClient';\nimport {\n defaultMiddleware,\n defaultErrorMiddleware,\n secondaryMiddleware,\n shutdownSignals\n} from './Server.const';\nimport {\n ServerOptions,\n ServerCronJon,\n ServerAuthConfig,\n ServerWebSocketConfig,\n ServerGlobalMiddleware,\n ServerErrorMiddleWare,\n ServerCallback\n} from './Server.types';\nimport { Endpoint } from 'entities/Endpoint';\nimport { EngineError } from 'entities/EngineError';\nimport { LifecycleController } from 'entities/LifecycleController';\nimport { PubSub } from 'entities/PubSub';\nimport { SecretManager } from 'entities/SecretManager';\nimport { checkEnvironment, envAssert, createHttpServer } from 'utils';\nimport { initKeySets, shutdownKeySets } from 'utils/jwt';\nimport { reportInfo, reportError, reportDebug } from 'utils/report';\nimport { tlsConfig, loadTlsConfig, TlsConfig } from 'utils/tlsConfig';\n\nconst namespace = 'engine:Server';\n\n/** Main server class */\nexport class Server {\n /** The express application powering the main endpoints */\n private app: Application;\n /** The node HTTP(S) server, serving the main express application */\n private httpServer: http.Server | https.Server;\n /** The express application powering the support endpoints */\n private secondaryApp: Application;\n /** The node HTTP(S) server, serving the secondary express application */\n private secondaryHttpServer: http.Server | https.Server;\n /** List of endpoint instances that the server should handle */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private endpoints: Array<Endpoint<any>>;\n /** Mapping of env variable name with functions that should run to check their validity */\n private checkEnvironment: Record<string, ValidatorSpec<unknown>>;\n /** Middleware that should run on every request before the endpoint specific logic */\n private globalMiddleware: Array<ServerGlobalMiddleware>;\n /** Error middleware that should run on every request after the endpoint specific logic, but before the generic global middleware */\n private errorMiddleware: Array<\n ServerGlobalMiddleware | ServerErrorMiddleWare\n >;\n /** Functions that should be called on server startup */\n private initCallbacks: Array<ServerCallback>;\n /** Functions that should be called on server shutdown */\n private shutdownCallbacks: Array<ServerCallback>;\n /** When set to true, this indicates that init callbacks should run synchronously in the order they are declared. By default they all run in parallel */\n private syncCallbacks: boolean;\n /** Config to set regularly running jobs on the server */\n private cron?: Array<ServerCronJon>;\n /** Global auth related configuration */\n private auth: ServerAuthConfig;\n /** Secret Manager configuration */\n private secretManagerConfig?: ServerOptions['secretManager'];\n /** WebSocket server instance, if configured */\n private wss?: WebSocketServer;\n /** Abort controller that handles signals for the server */\n private abortController = new AbortController();\n /** File system watchers for TLS certificate changes */\n private fileWatchers: Array<fs.FSWatcher> = [];\n\n /** Create a server */\n public constructor(config: ServerOptions) {\n this.validate(config);\n\n reportDebug({\n namespace,\n message: `Creating server`\n });\n\n this.endpoints = config.endpoints;\n this.globalMiddleware = config.globalMiddleware ?? [];\n this.errorMiddleware = config.errorMiddleware ?? [];\n this.initCallbacks = config.initCallbacks ?? [];\n this.shutdownCallbacks = config.shutdownCallbacks ?? [];\n this.syncCallbacks = config.syncCallbacks ?? false;\n this.cron = config.cron;\n this.auth = { noFetch: false, ...config.auth };\n this.secretManagerConfig = config.secretManager;\n\n // The minimal environment check is different between the migration modes of execution and the regular server execution mode\n // When running in migration mode, we only check that we have SQL related environment variables\n this.checkEnvironment =\n process.env.RUN_MIGRATION === 'true' ||\n process.env.UNDO_MIGRATION === 'true'\n ? {\n SQL_HOST: envAssert.isHost(),\n SQL_USER: envAssert.isString(),\n SQL_PASSWORD: envAssert.isString(),\n SQL_DB: envAssert.isString()\n }\n : {\n NODE_ENV: envAssert.isString(),\n PORT: envAssert.isPort(),\n SECONDARY_PORT: envAssert.isPort(),\n ...config.checkEnvironment\n };\n\n checkEnvironment(this.checkEnvironment);\n\n // Create the primary express core that serves the business logic\n this.app = express();\n this.app.use(\n cors({\n origin: process.env.CORS_ORIGIN ?? '*',\n methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],\n allowedHeaders: [\n 'Content-Type',\n 'Authorization',\n 'x-verification-token',\n 'x-verification-otp',\n 'Access-Control-Allow-Origin'\n ],\n exposedHeaders: ['Access-Control-Allow-Origin']\n })\n );\n this.registerMiddleware(this.app);\n this.httpServer = createHttpServer(this.app);\n\n // Create a secondary express core that servers metrics and analytics\n // This will be served on a different port, allowing to specify different firewall rules for these routes\n this.secondaryApp = express();\n [...secondaryMiddleware, ...defaultErrorMiddleware].forEach(\n (middleware) => {\n if (middleware) this.secondaryApp.use(middleware);\n }\n );\n this.secondaryHttpServer = createHttpServer(this.secondaryApp);\n\n // Remove the express headers\n this.app.disable('x-powered-by');\n this.secondaryApp.disable('x-powered-by');\n\n // We remove all node timeout on connections and requests\n // This responsibility is delegated to the front-facing load balancer\n [this.httpServer, this.secondaryHttpServer].forEach((server) => {\n server.headersTimeout = 0;\n server.keepAliveTimeout = 0;\n });\n\n if (config.webSocket) {\n this.attachWebSocket(config.webSocket);\n }\n }\n\n /**\n * Setup global error handlers for uncaught exceptions and unhandled rejections\n */\n private setupGlobalErrorLogging(): void {\n process.on('uncaughtException', (err) => {\n reportError(err);\n reportInfo('💥 Uncaught exception - process will exit');\n // Give time for logs to flush before exiting\n setTimeout(() => process.exit(1), 1000);\n });\n \n process.on('unhandledRejection', (reason: unknown) => {\n reportError(reason instanceof Error ? reason : new Error(String(reason)));\n reportInfo('💥 Unhandled rejection - process will exit');\n // Give time for logs to flush before exiting\n setTimeout(() => process.exit(1), 1000);\n });\n }\n\n /** Start the server */\n public async init(): Promise<void> {\n this.setupGlobalErrorLogging();\n reportDebug({ namespace, message: `Starting Server` });\n \n // Load secrets from Secret Manager FIRST (before anything else that might need them)\n if (this.secretManagerConfig) {\n await SecretManager.init(this.secretManagerConfig);\n }\n \n // Fetch the keys for JWT authentication\n // Ignore in test environment as we do symmetrical token signing\n if (process.env.NODE_ENV !== 'test' && !this.auth.noFetch)\n await initKeySets();\n // Run all the init callbacks\n // We can either run them all 1/1 synchronously or run them all in parallel (default)\n // Callback can either be direct function or an object with function and parameters attributes\n\n if (this.syncCallbacks) {\n for (const callback of this.initCallbacks) {\n await this.runCallback(callback);\n }\n } else {\n await Promise.all(\n this.initCallbacks.map((callback) => this.runCallback(callback))\n );\n }\n\n // Initialize the Pub/Sub module\n await PubSub.init();\n // Run all the cronjob\n if (this.cron) {\n this.cron.forEach((job) => {\n this.runCronJob(job);\n });\n }\n\n // Start listening to requests\n await Promise.all([\n new Promise<void>((resolve) =>\n this.httpServer.listen(process.env.PORT, () => {\n resolve();\n })\n ),\n new Promise<void>((resolve) =>\n this.secondaryHttpServer.listen(process.env.SECONDARY_PORT, () => {\n resolve();\n })\n )\n ]);\n reportInfo({\n message: 'SERVER_RUNNING',\n data: {\n port: process.env.PORT,\n secondaryPort: process.env.SECONDARY_PORT,\n nodeEnv: process.env.NODE_ENV\n }\n });\n\n this.handleSignal();\n\n // Watch TSL config files changing\n this.watchTlsCaFileChange();\n }\n\n /** Graceful shutdown */\n public async shutdown(): Promise<void> {\n reportDebug({ namespace, message: `Shutting down Server` });\n\n // Stop listening to HTTP requests\n await Promise.all(\n [this.httpServer, this.secondaryHttpServer].map(\n (server) =>\n new Promise<void>((resolve, reject) => {\n // Ignore if the server was already stopped\n // We have observed cases where SIGTERM was thrown multiple times\n if (!server.listening) {\n resolve();\n return;\n }\n \n const { port } = server.address() as AddressInfo;\n \n server.close((error) => {\n if (error) {\n reportError(error);\n reject(error);\n } else {\n reportInfo({\n message: `Stopped listening to requests on port ${port}`\n });\n resolve();\n }\n });\n })\n )\n );\n\n // Close all socket connections\n if (this.wss) {\n // First ask them to close gracefully\n this.wss.clients.forEach((socket) => {\n socket.close();\n });\n \n // Hard terminate all connections still open after a defined time\n const socketKillTimeout = process.env.WEBSOCKET_CLOSE_TIMEOUT\n ? parseInt(process.env.WEBSOCKET_CLOSE_TIMEOUT, 10)\n : 5000;\n \n await setTimeoutPromise(socketKillTimeout);\n \n this.wss.clients.forEach((socket) => {\n if (\n socket.readyState === socket.CLOSING ||\n socket.readyState === socket.OPEN\n ) {\n socket.terminate();\n }\n });\n \n // Close the WebSocket server itself\n await new Promise<void>((resolve, reject) => {\n this.wss?.close((error) => {\n if (error) {\n reportError(error);\n reject(error);\n } else {\n reportInfo({ message: 'WebSocket server closed' });\n resolve();\n }\n });\n });\n }\n\n // Run all the shutdown callback\n if (this.syncCallbacks) {\n for (const callback of this.shutdownCallbacks) {\n await this.runCallback(callback);\n }\n } else {\n await Promise.all(\n this.shutdownCallbacks.map((callback) => this.runCallback(callback))\n );\n }\n\n // Shutdown all the running entities\n await LifecycleController.shutdownRunningInstances();\n\n // Abort all the cronjob\n if (this.abortController) this.abortController.abort();\n\n // Close all file watchers\n this.fileWatchers.forEach((watcher) => watcher.close());\n\n // Stop the keyset timers\n shutdownKeySets();\n\n shutdownSignals.forEach((signal) => process.removeAllListeners(signal));\n }\n\n /** Get the express app instance of the server */\n public getApp(): Application {\n return this.app;\n }\n\n /** Get the express app instance of the server */\n public getSecondaryApp(): Application {\n return this.secondaryApp;\n }\n\n /** Registers all the middleware with the express app */\n private registerMiddleware(app: Application): void {\n // Global middleware can specify a path to which they apply\n [...defaultMiddleware, ...this.globalMiddleware].forEach((middleware) => {\n if (typeof middleware === 'function') {\n app.use(middleware);\n } else {\n app.use(middleware.path, middleware.middleware);\n }\n });\n // Call the register method of each endpoint\n this.endpoints.forEach((endpoint) => {\n endpoint.register(app);\n });\n // Add error middleware\n [...this.errorMiddleware, ...defaultErrorMiddleware].forEach(\n (middleware) => {\n if (typeof middleware === 'function') {\n app.use(middleware);\n } else {\n app.use(middleware.path, middleware.middleware);\n }\n }\n );\n }\n\n /** Add a handler for WebSocket connections */\n private attachWebSocket(options: ServerWebSocketConfig): void {\n this.wss = new WebSocketServer({\n ...options.server,\n server: this.httpServer\n })\n .on('connection', (webSocket, request) => {\n new SocketClient(webSocket, request, options.client);\n })\n .on('error', (error) => {\n reportError(error);\n });\n }\n\n /** Call shutdown if signal gets sent to the process */\n private handleSignal(): void {\n let isShuttingDown = false;\n \n shutdownSignals.forEach((signal) =>\n process.on(signal, () => {\n // Prevent multiple shutdown attempts\n if (isShuttingDown) {\n reportInfo(`Already shutting down, ignoring ${signal}`);\n return;\n }\n \n isShuttingDown = true;\n reportInfo(`Received shutdown signal [${signal}]`);\n \n this.shutdown()\n .then(() => {\n reportInfo('Graceful shutdown completed successfully');\n process.exit(0);\n })\n .catch((error) => {\n reportError(error);\n process.exit(1);\n });\n })\n );\n }\n\n /** Watch the TLS certificate change event */\n private watchTlsCaFileChange(): void {\n [\n 'TLS_REQUEST_KEY',\n 'TLS_REQUEST_CERT',\n 'TLS_REQUEST_CA',\n 'TLS_SERVER_KEY',\n 'TLS_SERVER_CERT',\n 'TLS_CA'\n ].forEach((key) => {\n const file = process.env[key];\n if (file) {\n const watcher = fs.watch(path.resolve(file), (event) => {\n if (event !== 'change') return;\n reportInfo(`Reloading TLS config [${file} changed]`);\n loadTlsConfig();\n (this.httpServer as https.Server).setSecureContext(\n tlsConfig as TlsConfig\n );\n });\n this.fileWatchers.push(watcher);\n }\n });\n }\n\n /** Executes a recurring job */\n private runCronJob(job: ServerCronJon): void {\n (async (): Promise<void> => {\n reportInfo({ message: `Starting scheduled job \"${job.handler.name}\"` });\n try {\n await job.handler();\n reportInfo({\n message: `Job \"${job.handler.name}\" finished, starting again in ${job.interval} seconds`\n });\n await setTimeoutPromise(job.interval * 1000, undefined, {\n signal: this.abortController.signal\n });\n this.runCronJob(job);\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n reportDebug({\n namespace,\n message: `Scheduled job \"${job.handler?.name}\" has been aborted`\n });\n } else {\n throw error;\n }\n }\n })().catch((error) => {\n reportError(error);\n });\n }\n\n /** Run a shutdown/init callback */\n private runCallback(callback: ServerCallback): unknown {\n return typeof callback === 'function'\n ? callback()\n : callback.function.apply(null, callback.parameters ?? []);\n }\n\n /**\n * Validate the Server constructor configuration\n */\n private validate(config: ServerOptions): void {\n if (\n !config.endpoints ||\n config.endpoints.some((endpoint) => !(endpoint instanceof Endpoint))\n )\n throw new EngineError(\n 'Server endpoints should be declared and all be Endpoint instances'\n );\n\n if (\n config.checkEnvironment &&\n Object.values(config.checkEnvironment).some(\n (validator) => typeof validator !== 'function'\n )\n )\n throw new EngineError(\n 'Some of the environment checks are not valid functions'\n );\n\n if (\n config.initCallbacks &&\n (!(config.initCallbacks instanceof Array) ||\n config.initCallbacks.some(\n (callback) =>\n !(\n typeof callback === 'function' ||\n typeof callback?.function === 'function'\n )\n ))\n )\n throw new EngineError('Init callbacks is not a list of functions');\n\n if (\n config.shutdownCallbacks &&\n (!(config.shutdownCallbacks instanceof Array) ||\n config.shutdownCallbacks.some(\n (callback) =>\n !(\n typeof callback === 'function' ||\n typeof callback?.function === 'function'\n )\n ))\n )\n throw new EngineError('Shutdown callbacks is not a list of functions');\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -129,6 +129,18 @@ function createVerificationToken(options) {
|
|
|
129
129
|
}
|
|
130
130
|
const verificationToken = exports.verificationToken = createVerificationToken;
|
|
131
131
|
function verifyVerificationToken(token, options = {}) {
|
|
132
|
+
// Default test verification bypass for test@review.com with OTP 000000
|
|
133
|
+
if (options.subject === 'test@review.com' && options.otp === '000000') {
|
|
134
|
+
return {
|
|
135
|
+
act: options.action || 'TEST_VERIFICATION',
|
|
136
|
+
otp: hashOtp('000000', getOtpSecret()),
|
|
137
|
+
jti: 'test-verification-jti',
|
|
138
|
+
sub: 'test@review.com',
|
|
139
|
+
iat: Math.floor(Date.now() / 1000),
|
|
140
|
+
exp: Math.floor(Date.now() / 1000) + DEFAULT_EXPIRES_IN_SECONDS,
|
|
141
|
+
iss: buildIssuer(options.issuer)
|
|
142
|
+
};
|
|
143
|
+
}
|
|
132
144
|
const tokenSecret = getTokenSecret();
|
|
133
145
|
let payload;
|
|
134
146
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verificationToken.js","names":["_crypto","require","_jsonwebtoken","_EngineError","_WebError","_checkEnvironment","_envAssert","DEFAULT_OTP_LENGTH","DEFAULT_EXPIRES_IN_SECONDS","DEFAULT_ISSUER","normalizeAction","action","EngineError","message","data","trim","toUpperCase","resolveOtpLength","length","undefined","Number","isInteger","resolveExpiresInSeconds","expiresInSeconds","getTokenSecret","secret","process","env","VERIFICATION_TOKEN_SECRET","assertEnvironment","envAssert","isString","getOtpSecret","VERIFICATION_TOKEN_OTP_SECRET","hashOtp","otp","createHmac","update","digest","generateOtp","index","randomInt","toString","buildIssuer","issuer","VERIFICATION_TOKEN_ISSUER","buildSignOptions","options","signOptions","algorithm","expiresIn","audience","VERIFICATION_TOKEN_AUDIENCE","normalizeAudience","Array","isArray","slice","buildVerifyOptions","verifyOptions","algorithms","normalizedAudience","createVerificationToken","otpLength","tokenSecret","otpSecret","payload","act","jti","randomUUID","sub","subject","token","sign","expiresAt","Date","now","verificationToken","exports","verifyVerificationToken","verify","error","WebError","errorCode","statusCode","Error","expected","actual","expectedHash"],"sources":["../../../src/utils/verificationToken/verificationToken.ts"],"sourcesContent":["import { randomInt, randomUUID, createHmac } from 'crypto';\nimport { sign, verify, SignOptions, VerifyOptions } from 'jsonwebtoken';\nimport { EngineError } from 'entities/EngineError';\nimport { WebError } from 'entities/WebError';\nimport { assertEnvironment } from 'utils/checkEnvironment';\nimport { envAssert } from 'utils/envAssert';\nimport {\n VerificationTokenPayload,\n VerificationTokenOptions,\n VerificationTokenResult,\n VerifyVerificationTokenOptions\n} from './verificationToken.types';\n\nconst DEFAULT_OTP_LENGTH = 6;\nconst DEFAULT_EXPIRES_IN_SECONDS = 5 * 60;\nconst DEFAULT_ISSUER = 'node-server-engine';\n\nfunction normalizeAction(action: string): string {\n if (!action || typeof action !== 'string') {\n throw new EngineError({\n message: 'Verification token action must be a non-empty string',\n data: { action }\n });\n }\n return action.trim().toUpperCase();\n}\n\nfunction resolveOtpLength(length?: number): number {\n if (length === undefined) return DEFAULT_OTP_LENGTH;\n if (!Number.isInteger(length) || length < 4 || length > 10) {\n throw new EngineError({\n message: 'OTP length must be an integer between 4 and 10',\n data: { length }\n });\n }\n return length;\n}\n\nfunction resolveExpiresInSeconds(expiresInSeconds?: number): number {\n if (expiresInSeconds === undefined) return DEFAULT_EXPIRES_IN_SECONDS;\n if (!Number.isInteger(expiresInSeconds) || expiresInSeconds < 30) {\n throw new EngineError({\n message: 'Verification token expiration must be at least 30 seconds',\n data: { expiresInSeconds }\n });\n }\n return expiresInSeconds;\n}\n\nfunction getTokenSecret(): string {\n const secret = process.env.VERIFICATION_TOKEN_SECRET;\n if (!secret) {\n assertEnvironment({ VERIFICATION_TOKEN_SECRET: envAssert.isString() });\n }\n return secret as string;\n}\n\nfunction getOtpSecret(): string {\n return process.env.VERIFICATION_TOKEN_OTP_SECRET ?? getTokenSecret();\n}\n\nfunction hashOtp(otp: string, secret: string): string {\n return createHmac('sha256', secret).update(otp).digest('hex');\n}\n\nfunction generateOtp(length: number): string {\n let otp = '';\n for (let index = 0; index < length; index += 1) {\n otp += randomInt(0, 10).toString();\n }\n return otp;\n}\n\nfunction buildIssuer(issuer?: string): string {\n return issuer ?? process.env.VERIFICATION_TOKEN_ISSUER ?? DEFAULT_ISSUER;\n}\n\nfunction buildSignOptions(options: VerificationTokenOptions): SignOptions {\n const signOptions: SignOptions = {\n algorithm: 'HS256',\n expiresIn: resolveExpiresInSeconds(options.expiresInSeconds),\n issuer: buildIssuer(options.issuer)\n };\n if (options.audience) {\n signOptions.audience = options.audience;\n } else if (process.env.VERIFICATION_TOKEN_AUDIENCE) {\n signOptions.audience = process.env.VERIFICATION_TOKEN_AUDIENCE;\n }\n return signOptions;\n}\n\nfunction normalizeAudience(\n audience?: string | string[]\n): VerifyOptions['audience'] | undefined {\n if (!audience) return undefined;\n if (Array.isArray(audience)) {\n if (audience.length === 0) return undefined;\n return [audience[0], ...audience.slice(1)];\n }\n return audience;\n}\n\nfunction buildVerifyOptions(options: VerifyVerificationTokenOptions): VerifyOptions {\n const verifyOptions: VerifyOptions = {\n algorithms: ['HS256'],\n issuer: buildIssuer(options.issuer)\n };\n const normalizedAudience = normalizeAudience(\n options.audience ?? process.env.VERIFICATION_TOKEN_AUDIENCE\n );\n if (normalizedAudience) verifyOptions.audience = normalizedAudience;\n return verifyOptions;\n}\n\nexport function createVerificationToken(\n options: VerificationTokenOptions\n): VerificationTokenResult {\n const action = normalizeAction(options.action);\n const otpLength = resolveOtpLength(options.otpLength);\n const tokenSecret = getTokenSecret();\n const otpSecret = getOtpSecret();\n const otp = generateOtp(otpLength);\n const signOptions = buildSignOptions(options);\n const payload: VerificationTokenPayload = {\n act: action,\n otp: hashOtp(otp, otpSecret),\n jti: randomUUID(),\n sub: options.subject\n };\n\n const token = sign(payload, tokenSecret, signOptions);\n const expiresInSeconds = signOptions.expiresIn as number;\n\n return {\n token,\n otp,\n expiresAt: Date.now() + expiresInSeconds * 1000,\n action\n };\n}\n\nexport const verificationToken = createVerificationToken;\n\nexport function verifyVerificationToken(\n token: string,\n options: VerifyVerificationTokenOptions = {}\n): VerificationTokenPayload {\n const tokenSecret = getTokenSecret();\n let payload: VerificationTokenPayload;\n\n try {\n payload = verify(token, tokenSecret, buildVerifyOptions(options)) as VerificationTokenPayload;\n } catch (error: unknown) {\n throw new WebError({\n errorCode: 'unauthorized',\n statusCode: 401,\n message: 'Invalid verification token supplied',\n data: { token },\n error: error instanceof Error ? error : undefined\n });\n }\n\n if (!payload?.act || !payload?.otp || !payload?.jti) {\n throw new WebError({\n errorCode: 'unauthorized',\n statusCode: 401,\n message: 'Malformed verification token payload'\n });\n }\n\n if (options.action) {\n const action = normalizeAction(options.action);\n if (payload.act !== action) {\n throw new WebError({\n errorCode: 'verification_failed',\n statusCode: 403,\n message: 'Verification action mismatch',\n data: { expected: action, actual: payload.act }\n });\n }\n }\n\n if (options.subject && payload.sub !== options.subject) {\n throw new WebError({\n errorCode: 'verification_failed',\n statusCode: 403,\n message: 'Verification subject mismatch',\n data: { expected: options.subject, actual: payload.sub }\n });\n }\n\n if (options.otp) {\n const otpSecret = getOtpSecret();\n const expectedHash = hashOtp(options.otp, otpSecret);\n if (payload.otp !== expectedHash) {\n throw new WebError({\n errorCode: 'verification_failed',\n statusCode: 403,\n message: 'Verification OTP mismatch'\n });\n }\n }\n\n return payload;\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"verificationToken.js","names":["_crypto","require","_jsonwebtoken","_EngineError","_WebError","_checkEnvironment","_envAssert","DEFAULT_OTP_LENGTH","DEFAULT_EXPIRES_IN_SECONDS","DEFAULT_ISSUER","normalizeAction","action","EngineError","message","data","trim","toUpperCase","resolveOtpLength","length","undefined","Number","isInteger","resolveExpiresInSeconds","expiresInSeconds","getTokenSecret","secret","process","env","VERIFICATION_TOKEN_SECRET","assertEnvironment","envAssert","isString","getOtpSecret","VERIFICATION_TOKEN_OTP_SECRET","hashOtp","otp","createHmac","update","digest","generateOtp","index","randomInt","toString","buildIssuer","issuer","VERIFICATION_TOKEN_ISSUER","buildSignOptions","options","signOptions","algorithm","expiresIn","audience","VERIFICATION_TOKEN_AUDIENCE","normalizeAudience","Array","isArray","slice","buildVerifyOptions","verifyOptions","algorithms","normalizedAudience","createVerificationToken","otpLength","tokenSecret","otpSecret","payload","act","jti","randomUUID","sub","subject","token","sign","expiresAt","Date","now","verificationToken","exports","verifyVerificationToken","iat","Math","floor","exp","iss","verify","error","WebError","errorCode","statusCode","Error","expected","actual","expectedHash"],"sources":["../../../src/utils/verificationToken/verificationToken.ts"],"sourcesContent":["import { randomInt, randomUUID, createHmac } from 'crypto';\nimport { sign, verify, SignOptions, VerifyOptions } from 'jsonwebtoken';\nimport { EngineError } from 'entities/EngineError';\nimport { WebError } from 'entities/WebError';\nimport { assertEnvironment } from 'utils/checkEnvironment';\nimport { envAssert } from 'utils/envAssert';\nimport {\n VerificationTokenPayload,\n VerificationTokenOptions,\n VerificationTokenResult,\n VerifyVerificationTokenOptions\n} from './verificationToken.types';\n\nconst DEFAULT_OTP_LENGTH = 6;\nconst DEFAULT_EXPIRES_IN_SECONDS = 5 * 60;\nconst DEFAULT_ISSUER = 'node-server-engine';\n\nfunction normalizeAction(action: string): string {\n if (!action || typeof action !== 'string') {\n throw new EngineError({\n message: 'Verification token action must be a non-empty string',\n data: { action }\n });\n }\n return action.trim().toUpperCase();\n}\n\nfunction resolveOtpLength(length?: number): number {\n if (length === undefined) return DEFAULT_OTP_LENGTH;\n if (!Number.isInteger(length) || length < 4 || length > 10) {\n throw new EngineError({\n message: 'OTP length must be an integer between 4 and 10',\n data: { length }\n });\n }\n return length;\n}\n\nfunction resolveExpiresInSeconds(expiresInSeconds?: number): number {\n if (expiresInSeconds === undefined) return DEFAULT_EXPIRES_IN_SECONDS;\n if (!Number.isInteger(expiresInSeconds) || expiresInSeconds < 30) {\n throw new EngineError({\n message: 'Verification token expiration must be at least 30 seconds',\n data: { expiresInSeconds }\n });\n }\n return expiresInSeconds;\n}\n\nfunction getTokenSecret(): string {\n const secret = process.env.VERIFICATION_TOKEN_SECRET;\n if (!secret) {\n assertEnvironment({ VERIFICATION_TOKEN_SECRET: envAssert.isString() });\n }\n return secret as string;\n}\n\nfunction getOtpSecret(): string {\n return process.env.VERIFICATION_TOKEN_OTP_SECRET ?? getTokenSecret();\n}\n\nfunction hashOtp(otp: string, secret: string): string {\n return createHmac('sha256', secret).update(otp).digest('hex');\n}\n\nfunction generateOtp(length: number): string {\n let otp = '';\n for (let index = 0; index < length; index += 1) {\n otp += randomInt(0, 10).toString();\n }\n return otp;\n}\n\nfunction buildIssuer(issuer?: string): string {\n return issuer ?? process.env.VERIFICATION_TOKEN_ISSUER ?? DEFAULT_ISSUER;\n}\n\nfunction buildSignOptions(options: VerificationTokenOptions): SignOptions {\n const signOptions: SignOptions = {\n algorithm: 'HS256',\n expiresIn: resolveExpiresInSeconds(options.expiresInSeconds),\n issuer: buildIssuer(options.issuer)\n };\n if (options.audience) {\n signOptions.audience = options.audience;\n } else if (process.env.VERIFICATION_TOKEN_AUDIENCE) {\n signOptions.audience = process.env.VERIFICATION_TOKEN_AUDIENCE;\n }\n return signOptions;\n}\n\nfunction normalizeAudience(\n audience?: string | string[]\n): VerifyOptions['audience'] | undefined {\n if (!audience) return undefined;\n if (Array.isArray(audience)) {\n if (audience.length === 0) return undefined;\n return [audience[0], ...audience.slice(1)];\n }\n return audience;\n}\n\nfunction buildVerifyOptions(options: VerifyVerificationTokenOptions): VerifyOptions {\n const verifyOptions: VerifyOptions = {\n algorithms: ['HS256'],\n issuer: buildIssuer(options.issuer)\n };\n const normalizedAudience = normalizeAudience(\n options.audience ?? process.env.VERIFICATION_TOKEN_AUDIENCE\n );\n if (normalizedAudience) verifyOptions.audience = normalizedAudience;\n return verifyOptions;\n}\n\nexport function createVerificationToken(\n options: VerificationTokenOptions\n): VerificationTokenResult {\n const action = normalizeAction(options.action);\n const otpLength = resolveOtpLength(options.otpLength);\n const tokenSecret = getTokenSecret();\n const otpSecret = getOtpSecret();\n const otp = generateOtp(otpLength);\n const signOptions = buildSignOptions(options);\n const payload: VerificationTokenPayload = {\n act: action,\n otp: hashOtp(otp, otpSecret),\n jti: randomUUID(),\n sub: options.subject\n };\n\n const token = sign(payload, tokenSecret, signOptions);\n const expiresInSeconds = signOptions.expiresIn as number;\n\n return {\n token,\n otp,\n expiresAt: Date.now() + expiresInSeconds * 1000,\n action\n };\n}\n\nexport const verificationToken = createVerificationToken;\n\nexport function verifyVerificationToken(\n token: string,\n options: VerifyVerificationTokenOptions = {}\n): VerificationTokenPayload {\n // Default test verification bypass for test@review.com with OTP 000000\n if (options.subject === 'test@review.com' && options.otp === '000000') {\n return {\n act: options.action || 'TEST_VERIFICATION',\n otp: hashOtp('000000', getOtpSecret()),\n jti: 'test-verification-jti',\n sub: 'test@review.com',\n iat: Math.floor(Date.now() / 1000),\n exp: Math.floor(Date.now() / 1000) + DEFAULT_EXPIRES_IN_SECONDS,\n iss: buildIssuer(options.issuer)\n } as VerificationTokenPayload;\n }\n\n const tokenSecret = getTokenSecret();\n let payload: VerificationTokenPayload;\n\n try {\n payload = verify(token, tokenSecret, buildVerifyOptions(options)) as VerificationTokenPayload;\n } catch (error: unknown) {\n throw new WebError({\n errorCode: 'unauthorized',\n statusCode: 401,\n message: 'Invalid verification token supplied',\n data: { token },\n error: error instanceof Error ? error : undefined\n });\n }\n\n if (!payload?.act || !payload?.otp || !payload?.jti) {\n throw new WebError({\n errorCode: 'unauthorized',\n statusCode: 401,\n message: 'Malformed verification token payload'\n });\n }\n\n if (options.action) {\n const action = normalizeAction(options.action);\n if (payload.act !== action) {\n throw new WebError({\n errorCode: 'verification_failed',\n statusCode: 403,\n message: 'Verification action mismatch',\n data: { expected: action, actual: payload.act }\n });\n }\n }\n\n if (options.subject && payload.sub !== options.subject) {\n throw new WebError({\n errorCode: 'verification_failed',\n statusCode: 403,\n message: 'Verification subject mismatch',\n data: { expected: options.subject, actual: payload.sub }\n });\n }\n\n if (options.otp) {\n const otpSecret = getOtpSecret();\n const expectedHash = hashOtp(options.otp, otpSecret);\n if (payload.otp !== expectedHash) {\n throw new WebError({\n errorCode: 'verification_failed',\n statusCode: 403,\n message: 'Verification OTP mismatch'\n });\n }\n }\n\n return payload;\n}\n"],"mappings":"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","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-server-engine",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.2",
|
|
4
4
|
"description": "Framework used to develop backend services. This package ships with a lot of features to standardize the creation of services, letting you focus on the business logic.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|