@app-connect/core 1.7.25 → 1.7.26
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/.env.test +5 -5
- package/README.md +441 -441
- package/connector/developerPortal.js +31 -31
- package/connector/mock.js +84 -77
- package/connector/proxy/engine.js +164 -164
- package/connector/proxy/index.js +500 -500
- package/connector/registry.js +252 -252
- package/docs/README.md +50 -50
- package/docs/architecture.md +93 -93
- package/docs/connectors.md +116 -116
- package/docs/handlers.md +125 -125
- package/docs/libraries.md +101 -101
- package/docs/models.md +144 -144
- package/docs/routes.md +115 -115
- package/docs/tests.md +73 -73
- package/handlers/admin.js +523 -523
- package/handlers/appointment.js +193 -0
- package/handlers/auth.js +296 -296
- package/handlers/calldown.js +99 -99
- package/handlers/contact.js +280 -280
- package/handlers/disposition.js +82 -80
- package/handlers/log.js +984 -973
- package/handlers/managedAuth.js +446 -446
- package/handlers/plugin.js +208 -208
- package/handlers/user.js +142 -142
- package/index.js +3140 -2652
- package/jest.config.js +56 -56
- package/lib/analytics.js +54 -54
- package/lib/authSession.js +109 -109
- package/lib/cacheCleanup.js +21 -0
- package/lib/callLogComposer.js +898 -898
- package/lib/callLogLookup.js +34 -0
- package/lib/constants.js +8 -8
- package/lib/debugTracer.js +177 -177
- package/lib/encode.js +30 -30
- package/lib/errorHandler.js +218 -206
- package/lib/generalErrorMessage.js +41 -41
- package/lib/jwt.js +18 -18
- package/lib/logger.js +190 -190
- package/lib/migrateCallLogsSchema.js +116 -0
- package/lib/ringcentral.js +266 -266
- package/lib/s3ErrorLogReport.js +65 -65
- package/lib/sharedSMSComposer.js +471 -471
- package/lib/util.js +67 -67
- package/mcp/README.md +412 -395
- package/mcp/lib/validator.js +91 -91
- package/mcp/mcpHandler.js +425 -425
- package/mcp/tools/cancelAppointment.js +101 -0
- package/mcp/tools/checkAuthStatus.js +105 -105
- package/mcp/tools/confirmAppointment.js +101 -0
- package/mcp/tools/createAppointment.js +157 -0
- package/mcp/tools/createCallLog.js +327 -316
- package/mcp/tools/createContact.js +117 -117
- package/mcp/tools/createMessageLog.js +287 -287
- package/mcp/tools/doAuth.js +60 -60
- package/mcp/tools/findContactByName.js +93 -93
- package/mcp/tools/findContactByPhone.js +101 -101
- package/mcp/tools/getCallLog.js +111 -102
- package/mcp/tools/getGoogleFilePicker.js +99 -99
- package/mcp/tools/getHelp.js +43 -43
- package/mcp/tools/getPublicConnectors.js +94 -94
- package/mcp/tools/getSessionInfo.js +90 -90
- package/mcp/tools/index.js +51 -41
- package/mcp/tools/listAppointments.js +163 -0
- package/mcp/tools/logout.js +96 -96
- package/mcp/tools/rcGetCallLogs.js +65 -65
- package/mcp/tools/updateAppointment.js +154 -0
- package/mcp/tools/updateCallLog.js +130 -126
- package/mcp/ui/App/App.tsx +358 -358
- package/mcp/ui/App/components/AuthInfoForm.tsx +113 -113
- package/mcp/ui/App/components/AuthSuccess.tsx +22 -22
- package/mcp/ui/App/components/ConnectorList.tsx +82 -82
- package/mcp/ui/App/components/DebugPanel.tsx +43 -43
- package/mcp/ui/App/components/OAuthConnect.tsx +270 -270
- package/mcp/ui/App/lib/callTool.ts +130 -130
- package/mcp/ui/App/lib/debugLog.ts +41 -41
- package/mcp/ui/App/lib/developerPortal.ts +111 -111
- package/mcp/ui/App/main.css +5 -5
- package/mcp/ui/App/root.tsx +13 -13
- package/mcp/ui/index.html +13 -13
- package/mcp/ui/package-lock.json +6356 -6356
- package/mcp/ui/package.json +25 -25
- package/mcp/ui/tsconfig.json +26 -26
- package/mcp/ui/vite.config.ts +16 -16
- package/models/accountDataModel.js +33 -33
- package/models/adminConfigModel.js +35 -35
- package/models/cacheModel.js +30 -26
- package/models/callDownListModel.js +34 -34
- package/models/callLogModel.js +33 -27
- package/models/dynamo/connectorSchema.js +146 -146
- package/models/dynamo/lockSchema.js +24 -24
- package/models/dynamo/noteCacheSchema.js +29 -29
- package/models/llmSessionModel.js +17 -17
- package/models/messageLogModel.js +25 -25
- package/models/sequelize.js +16 -16
- package/models/userModel.js +45 -45
- package/package.json +1 -1
- package/releaseNotes.json +1093 -1081
- package/test/connector/proxy/engine.test.js +126 -126
- package/test/connector/proxy/index.test.js +279 -279
- package/test/connector/proxy/sample.json +161 -161
- package/test/connector/registry.test.js +415 -415
- package/test/handlers/admin.test.js +616 -616
- package/test/handlers/auth.test.js +1018 -1018
- package/test/handlers/contact.test.js +1014 -1014
- package/test/handlers/log.test.js +1298 -1160
- package/test/handlers/managedAuth.test.js +457 -457
- package/test/handlers/plugin.test.js +380 -380
- package/test/index.test.js +105 -105
- package/test/lib/cacheCleanup.test.js +42 -0
- package/test/lib/callLogComposer.test.js +1231 -1231
- package/test/lib/debugTracer.test.js +328 -328
- package/test/lib/jwt.test.js +176 -176
- package/test/lib/logger.test.js +206 -206
- package/test/lib/oauth.test.js +359 -359
- package/test/lib/ringcentral.test.js +467 -467
- package/test/lib/sharedSMSComposer.test.js +1084 -1084
- package/test/lib/util.test.js +329 -329
- package/test/mcp/tools/checkAuthStatus.test.js +83 -83
- package/test/mcp/tools/createCallLog.test.js +436 -436
- package/test/mcp/tools/createContact.test.js +58 -58
- package/test/mcp/tools/createMessageLog.test.js +595 -595
- package/test/mcp/tools/doAuth.test.js +113 -113
- package/test/mcp/tools/findContactByName.test.js +275 -275
- package/test/mcp/tools/findContactByPhone.test.js +296 -296
- package/test/mcp/tools/getCallLog.test.js +298 -298
- package/test/mcp/tools/getGoogleFilePicker.test.js +281 -281
- package/test/mcp/tools/getPublicConnectors.test.js +107 -107
- package/test/mcp/tools/getSessionInfo.test.js +127 -127
- package/test/mcp/tools/logout.test.js +233 -233
- package/test/mcp/tools/rcGetCallLogs.test.js +56 -56
- package/test/mcp/tools/updateCallLog.test.js +360 -360
- package/test/models/accountDataModel.test.js +98 -98
- package/test/models/dynamo/connectorSchema.test.js +189 -189
- package/test/models/models.test.js +568 -539
- package/test/routes/managedAuthRoutes.test.js +104 -104
- package/test/setup.js +178 -178
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const Op = require('sequelize').Op;
|
|
2
|
+
|
|
3
|
+
function getCallLogExtensionNumber(incomingData) {
|
|
4
|
+
return (incomingData?.extensionNumber ?? incomingData?.logInfo?.extensionNumber)?.toString() ?? '';
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function buildCallLogSessionWhere({ sessionId, sessionIds, extensionNumber }) {
|
|
8
|
+
const where = {};
|
|
9
|
+
if (sessionIds) {
|
|
10
|
+
where.sessionId = {
|
|
11
|
+
[Op.in]: sessionIds,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
where.sessionId = sessionId;
|
|
16
|
+
}
|
|
17
|
+
const extensionNumberValue = extensionNumber?.toString() ?? '';
|
|
18
|
+
if (extensionNumberValue) {
|
|
19
|
+
where.extensionNumber = extensionNumberValue;
|
|
20
|
+
}
|
|
21
|
+
return where;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function findMatchingCallLog(callLogs, sessionId, extensionNumber) {
|
|
25
|
+
const extensionNumberValue = extensionNumber?.toString() ?? '';
|
|
26
|
+
return callLogs.find(callLog => (
|
|
27
|
+
callLog.sessionId === sessionId &&
|
|
28
|
+
(!extensionNumberValue || (callLog.extensionNumber?.toString() ?? '') === extensionNumberValue)
|
|
29
|
+
));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
exports.getCallLogExtensionNumber = getCallLogExtensionNumber;
|
|
33
|
+
exports.buildCallLogSessionWhere = buildCallLogSessionWhere;
|
|
34
|
+
exports.findMatchingCallLog = findMatchingCallLog;
|
package/lib/constants.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
const LOG_DETAILS_FORMAT_TYPE = {
|
|
2
|
-
PLAIN_TEXT: 'text/plain',
|
|
3
|
-
HTML: 'text/html',
|
|
4
|
-
MARKDOWN: 'text/markdown'
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
module.exports = {
|
|
8
|
-
LOG_DETAILS_FORMAT_TYPE
|
|
1
|
+
const LOG_DETAILS_FORMAT_TYPE = {
|
|
2
|
+
PLAIN_TEXT: 'text/plain',
|
|
3
|
+
HTML: 'text/html',
|
|
4
|
+
MARKDOWN: 'text/markdown'
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
module.exports = {
|
|
8
|
+
LOG_DETAILS_FORMAT_TYPE
|
|
9
9
|
};
|
package/lib/debugTracer.js
CHANGED
|
@@ -1,177 +1,177 @@
|
|
|
1
|
-
class DebugTracer {
|
|
2
|
-
/**
|
|
3
|
-
* Creates a new DebugTracer instance
|
|
4
|
-
* @param {Object} headers - Request headers object
|
|
5
|
-
*/
|
|
6
|
-
constructor(headers = {}) {
|
|
7
|
-
this.traces = [];
|
|
8
|
-
this.startTime = Date.now();
|
|
9
|
-
this.requestId = this._generateRequestId();
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Generates a unique request ID for tracking
|
|
14
|
-
* @returns {string} Unique request ID
|
|
15
|
-
*/
|
|
16
|
-
_generateRequestId() {
|
|
17
|
-
return `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Captures the current stack trace
|
|
22
|
-
* @param {number} skipFrames - Number of frames to skip from the top (default: 2)
|
|
23
|
-
* @returns {string[]} Array of stack trace lines
|
|
24
|
-
*/
|
|
25
|
-
_captureStackTrace(skipFrames = 2) {
|
|
26
|
-
const err = new Error();
|
|
27
|
-
const stack = err.stack || '';
|
|
28
|
-
const lines = stack.split('\n').slice(skipFrames);
|
|
29
|
-
return lines.map(line => line.trim()).filter(line => line.startsWith('at '));
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Records a trace entry with method name, data, and stack trace
|
|
34
|
-
* @param {string} methodName - Name of the method/function being traced
|
|
35
|
-
* @param {Object} data - Data to record (will be sanitized to remove sensitive info)
|
|
36
|
-
* @param {Object} options - Additional options
|
|
37
|
-
* @param {boolean} options.includeStack - Whether to include stack trace (default: true)
|
|
38
|
-
* @param {string} options.level - Log level: 'info', 'warn', 'error' (default: 'info')
|
|
39
|
-
* @returns {DebugTracer} Returns this for chaining
|
|
40
|
-
*/
|
|
41
|
-
trace(methodName, data = {}, options = {}) {
|
|
42
|
-
const { includeStack = true, level = 'info' } = options;
|
|
43
|
-
|
|
44
|
-
const traceEntry = {
|
|
45
|
-
timestamp: new Date().toISOString(),
|
|
46
|
-
elapsed: Date.now() - this.startTime,
|
|
47
|
-
methodName,
|
|
48
|
-
level,
|
|
49
|
-
data: this._sanitizeData(data)
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
if (includeStack) {
|
|
53
|
-
traceEntry.stackTrace = this._captureStackTrace(3);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
this.traces.push(traceEntry);
|
|
57
|
-
return this;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Records an error trace with full stack information
|
|
62
|
-
* @param {string} methodName - Name of the method where error occurred
|
|
63
|
-
* @param {Error|string} error - Error object or message
|
|
64
|
-
* @param {Object} additionalData - Additional context data
|
|
65
|
-
* @returns {DebugTracer} Returns this for chaining
|
|
66
|
-
*/
|
|
67
|
-
traceError(methodName, error, additionalData = {}) {
|
|
68
|
-
const errorData = {
|
|
69
|
-
message: error instanceof Error ? error.message : String(error),
|
|
70
|
-
errorStack: error instanceof Error ? error.stack : null,
|
|
71
|
-
...additionalData
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
return this.trace(methodName, errorData, { level: 'error' });
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Sanitizes data by removing sensitive fields
|
|
79
|
-
* @param {Object} data - Data to sanitize
|
|
80
|
-
* @returns {Object} Sanitized data
|
|
81
|
-
*/
|
|
82
|
-
_sanitizeData(data) {
|
|
83
|
-
if (!data || typeof data !== 'object') {
|
|
84
|
-
return data;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const sensitiveFields = [
|
|
88
|
-
'accessToken', 'refreshToken', 'apiKey', 'password',
|
|
89
|
-
'secret', 'token', 'authorization', 'auth', 'key',
|
|
90
|
-
'credential', 'credentials', 'privateKey', 'clientSecret'
|
|
91
|
-
];
|
|
92
|
-
|
|
93
|
-
const sanitized = Array.isArray(data) ? [...data] : { ...data };
|
|
94
|
-
|
|
95
|
-
const sanitizeRecursive = (obj) => {
|
|
96
|
-
if (!obj || typeof obj !== 'object') {
|
|
97
|
-
return obj;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
for (const key of Object.keys(obj)) {
|
|
101
|
-
const lowerKey = key.toLowerCase();
|
|
102
|
-
if (sensitiveFields.some(field => lowerKey.includes(field.toLowerCase()))) {
|
|
103
|
-
// eslint-disable-next-line no-param-reassign
|
|
104
|
-
obj[key] = '[REDACTED]';
|
|
105
|
-
} else if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
106
|
-
// eslint-disable-next-line no-param-reassign
|
|
107
|
-
obj[key] = sanitizeRecursive(
|
|
108
|
-
Array.isArray(obj[key]) ? [...obj[key]] : { ...obj[key] }
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return obj;
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
return sanitizeRecursive(sanitized);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Builds a compact summary of all recorded actions, one entry per trace.
|
|
120
|
-
* Each entry contains the method name, log level, and elapsed time at the
|
|
121
|
-
* point the trace was recorded, making it easy to skim what happened without
|
|
122
|
-
* reading the full trace list.
|
|
123
|
-
* @returns {string[]} Array of human-readable action summary strings
|
|
124
|
-
*/
|
|
125
|
-
_buildActionSummary() {
|
|
126
|
-
return this.traces.map((t, i) => ({
|
|
127
|
-
index: i + 1,
|
|
128
|
-
timestamp: t.timestamp,
|
|
129
|
-
level: t.level.toUpperCase(),
|
|
130
|
-
method: t.methodName,
|
|
131
|
-
elapsedMs: t.elapsed
|
|
132
|
-
}));
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Gets the complete trace data for inclusion in response
|
|
137
|
-
* @returns {Object} Trace data object
|
|
138
|
-
*/
|
|
139
|
-
getTraceData() {
|
|
140
|
-
return {
|
|
141
|
-
sum: this._buildActionSummary(),
|
|
142
|
-
requestId: this.requestId,
|
|
143
|
-
totalDuration: `${Date.now() - this.startTime}ms`,
|
|
144
|
-
traceCount: this.traces.length,
|
|
145
|
-
traces: this.traces
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Gets trace data and merges it into a response object
|
|
151
|
-
* @param {Object} response - Original response object
|
|
152
|
-
* @returns {Object} Response with debug trace data appended (if debug mode enabled)
|
|
153
|
-
*/
|
|
154
|
-
wrapResponse(response) {
|
|
155
|
-
const traceData = this.getTraceData();
|
|
156
|
-
if (!traceData) {
|
|
157
|
-
return response;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return {
|
|
161
|
-
...response,
|
|
162
|
-
_debug: traceData
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Static helper to create tracer from Express request
|
|
168
|
-
* @param {Object} req - Express request object
|
|
169
|
-
* @returns {DebugTracer} New tracer instance
|
|
170
|
-
*/
|
|
171
|
-
static fromRequest(req) {
|
|
172
|
-
return new DebugTracer(req.headers || {});
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
module.exports = { DebugTracer };
|
|
177
|
-
|
|
1
|
+
class DebugTracer {
|
|
2
|
+
/**
|
|
3
|
+
* Creates a new DebugTracer instance
|
|
4
|
+
* @param {Object} headers - Request headers object
|
|
5
|
+
*/
|
|
6
|
+
constructor(headers = {}) {
|
|
7
|
+
this.traces = [];
|
|
8
|
+
this.startTime = Date.now();
|
|
9
|
+
this.requestId = this._generateRequestId();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Generates a unique request ID for tracking
|
|
14
|
+
* @returns {string} Unique request ID
|
|
15
|
+
*/
|
|
16
|
+
_generateRequestId() {
|
|
17
|
+
return `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Captures the current stack trace
|
|
22
|
+
* @param {number} skipFrames - Number of frames to skip from the top (default: 2)
|
|
23
|
+
* @returns {string[]} Array of stack trace lines
|
|
24
|
+
*/
|
|
25
|
+
_captureStackTrace(skipFrames = 2) {
|
|
26
|
+
const err = new Error();
|
|
27
|
+
const stack = err.stack || '';
|
|
28
|
+
const lines = stack.split('\n').slice(skipFrames);
|
|
29
|
+
return lines.map(line => line.trim()).filter(line => line.startsWith('at '));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Records a trace entry with method name, data, and stack trace
|
|
34
|
+
* @param {string} methodName - Name of the method/function being traced
|
|
35
|
+
* @param {Object} data - Data to record (will be sanitized to remove sensitive info)
|
|
36
|
+
* @param {Object} options - Additional options
|
|
37
|
+
* @param {boolean} options.includeStack - Whether to include stack trace (default: true)
|
|
38
|
+
* @param {string} options.level - Log level: 'info', 'warn', 'error' (default: 'info')
|
|
39
|
+
* @returns {DebugTracer} Returns this for chaining
|
|
40
|
+
*/
|
|
41
|
+
trace(methodName, data = {}, options = {}) {
|
|
42
|
+
const { includeStack = true, level = 'info' } = options;
|
|
43
|
+
|
|
44
|
+
const traceEntry = {
|
|
45
|
+
timestamp: new Date().toISOString(),
|
|
46
|
+
elapsed: Date.now() - this.startTime,
|
|
47
|
+
methodName,
|
|
48
|
+
level,
|
|
49
|
+
data: this._sanitizeData(data)
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
if (includeStack) {
|
|
53
|
+
traceEntry.stackTrace = this._captureStackTrace(3);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.traces.push(traceEntry);
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Records an error trace with full stack information
|
|
62
|
+
* @param {string} methodName - Name of the method where error occurred
|
|
63
|
+
* @param {Error|string} error - Error object or message
|
|
64
|
+
* @param {Object} additionalData - Additional context data
|
|
65
|
+
* @returns {DebugTracer} Returns this for chaining
|
|
66
|
+
*/
|
|
67
|
+
traceError(methodName, error, additionalData = {}) {
|
|
68
|
+
const errorData = {
|
|
69
|
+
message: error instanceof Error ? error.message : String(error),
|
|
70
|
+
errorStack: error instanceof Error ? error.stack : null,
|
|
71
|
+
...additionalData
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return this.trace(methodName, errorData, { level: 'error' });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Sanitizes data by removing sensitive fields
|
|
79
|
+
* @param {Object} data - Data to sanitize
|
|
80
|
+
* @returns {Object} Sanitized data
|
|
81
|
+
*/
|
|
82
|
+
_sanitizeData(data) {
|
|
83
|
+
if (!data || typeof data !== 'object') {
|
|
84
|
+
return data;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const sensitiveFields = [
|
|
88
|
+
'accessToken', 'refreshToken', 'apiKey', 'password',
|
|
89
|
+
'secret', 'token', 'authorization', 'auth', 'key',
|
|
90
|
+
'credential', 'credentials', 'privateKey', 'clientSecret'
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
const sanitized = Array.isArray(data) ? [...data] : { ...data };
|
|
94
|
+
|
|
95
|
+
const sanitizeRecursive = (obj) => {
|
|
96
|
+
if (!obj || typeof obj !== 'object') {
|
|
97
|
+
return obj;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (const key of Object.keys(obj)) {
|
|
101
|
+
const lowerKey = key.toLowerCase();
|
|
102
|
+
if (sensitiveFields.some(field => lowerKey.includes(field.toLowerCase()))) {
|
|
103
|
+
// eslint-disable-next-line no-param-reassign
|
|
104
|
+
obj[key] = '[REDACTED]';
|
|
105
|
+
} else if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
106
|
+
// eslint-disable-next-line no-param-reassign
|
|
107
|
+
obj[key] = sanitizeRecursive(
|
|
108
|
+
Array.isArray(obj[key]) ? [...obj[key]] : { ...obj[key] }
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return obj;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return sanitizeRecursive(sanitized);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Builds a compact summary of all recorded actions, one entry per trace.
|
|
120
|
+
* Each entry contains the method name, log level, and elapsed time at the
|
|
121
|
+
* point the trace was recorded, making it easy to skim what happened without
|
|
122
|
+
* reading the full trace list.
|
|
123
|
+
* @returns {string[]} Array of human-readable action summary strings
|
|
124
|
+
*/
|
|
125
|
+
_buildActionSummary() {
|
|
126
|
+
return this.traces.map((t, i) => ({
|
|
127
|
+
index: i + 1,
|
|
128
|
+
timestamp: t.timestamp,
|
|
129
|
+
level: t.level.toUpperCase(),
|
|
130
|
+
method: t.methodName,
|
|
131
|
+
elapsedMs: t.elapsed
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Gets the complete trace data for inclusion in response
|
|
137
|
+
* @returns {Object} Trace data object
|
|
138
|
+
*/
|
|
139
|
+
getTraceData() {
|
|
140
|
+
return {
|
|
141
|
+
sum: this._buildActionSummary(),
|
|
142
|
+
requestId: this.requestId,
|
|
143
|
+
totalDuration: `${Date.now() - this.startTime}ms`,
|
|
144
|
+
traceCount: this.traces.length,
|
|
145
|
+
traces: this.traces
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Gets trace data and merges it into a response object
|
|
151
|
+
* @param {Object} response - Original response object
|
|
152
|
+
* @returns {Object} Response with debug trace data appended (if debug mode enabled)
|
|
153
|
+
*/
|
|
154
|
+
wrapResponse(response) {
|
|
155
|
+
const traceData = this.getTraceData();
|
|
156
|
+
if (!traceData) {
|
|
157
|
+
return response;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
...response,
|
|
162
|
+
_debug: traceData
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Static helper to create tracer from Express request
|
|
168
|
+
* @param {Object} req - Express request object
|
|
169
|
+
* @returns {DebugTracer} New tracer instance
|
|
170
|
+
*/
|
|
171
|
+
static fromRequest(req) {
|
|
172
|
+
return new DebugTracer(req.headers || {});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
module.exports = { DebugTracer };
|
|
177
|
+
|
package/lib/encode.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
const crypto = require('crypto');
|
|
2
|
-
|
|
3
|
-
function getCipherKey() {
|
|
4
|
-
if (!process.env.APP_SERVER_SECRET_KEY) {
|
|
5
|
-
throw new Error('APP_SERVER_SECRET_KEY is not defined');
|
|
6
|
-
}
|
|
7
|
-
if (process.env.APP_SERVER_SECRET_KEY.length < 32) {
|
|
8
|
-
// pad secret key with spaces if it is less than 32 bytes
|
|
9
|
-
return process.env.APP_SERVER_SECRET_KEY.padEnd(32, ' ');
|
|
10
|
-
}
|
|
11
|
-
if (process.env.APP_SERVER_SECRET_KEY.length > 32) {
|
|
12
|
-
// truncate secret key if it is more than 32 bytes
|
|
13
|
-
return process.env.APP_SERVER_SECRET_KEY.slice(0, 32);
|
|
14
|
-
}
|
|
15
|
-
return process.env.APP_SERVER_SECRET_KEY;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function encode(data) {
|
|
19
|
-
const cipher = crypto.createCipheriv('aes-256-cbc', getCipherKey(), Buffer.alloc(16, 0));
|
|
20
|
-
return cipher.update(data, 'utf8', 'hex') + cipher.final('hex');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function decoded(encryptedData) {
|
|
24
|
-
const decipher = crypto.createDecipheriv('aes-256-cbc', getCipherKey(), Buffer.alloc(16, 0));
|
|
25
|
-
return decipher.update(encryptedData, 'hex', 'utf8') + decipher.final('utf8');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
exports.encode = encode;
|
|
30
|
-
exports.decoded = decoded;
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
|
|
3
|
+
function getCipherKey() {
|
|
4
|
+
if (!process.env.APP_SERVER_SECRET_KEY) {
|
|
5
|
+
throw new Error('APP_SERVER_SECRET_KEY is not defined');
|
|
6
|
+
}
|
|
7
|
+
if (process.env.APP_SERVER_SECRET_KEY.length < 32) {
|
|
8
|
+
// pad secret key with spaces if it is less than 32 bytes
|
|
9
|
+
return process.env.APP_SERVER_SECRET_KEY.padEnd(32, ' ');
|
|
10
|
+
}
|
|
11
|
+
if (process.env.APP_SERVER_SECRET_KEY.length > 32) {
|
|
12
|
+
// truncate secret key if it is more than 32 bytes
|
|
13
|
+
return process.env.APP_SERVER_SECRET_KEY.slice(0, 32);
|
|
14
|
+
}
|
|
15
|
+
return process.env.APP_SERVER_SECRET_KEY;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function encode(data) {
|
|
19
|
+
const cipher = crypto.createCipheriv('aes-256-cbc', getCipherKey(), Buffer.alloc(16, 0));
|
|
20
|
+
return cipher.update(data, 'utf8', 'hex') + cipher.final('hex');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function decoded(encryptedData) {
|
|
24
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', getCipherKey(), Buffer.alloc(16, 0));
|
|
25
|
+
return decipher.update(encryptedData, 'hex', 'utf8') + decipher.final('utf8');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
exports.encode = encode;
|
|
30
|
+
exports.decoded = decoded;
|