@app-connect/core 1.7.24 → 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 -42
- package/connector/mock.js +84 -77
- package/connector/proxy/engine.js +164 -163
- 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 -117
- 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 +72 -72
- package/releaseNotes.json +1093 -1073
- package/test/connector/proxy/engine.test.js +126 -93
- 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 -1015
- package/test/handlers/contact.test.js +1014 -1014
- package/test/handlers/log.test.js +1298 -1160
- package/test/handlers/managedAuth.test.js +458 -458
- 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 -82
- 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 -129
- package/test/setup.js +178 -178
package/lib/logger.js
CHANGED
|
@@ -1,190 +1,190 @@
|
|
|
1
|
-
const util = require('util');
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Logger levels
|
|
5
|
-
*/
|
|
6
|
-
const LOG_LEVELS = {
|
|
7
|
-
ERROR: 0,
|
|
8
|
-
WARN: 1,
|
|
9
|
-
INFO: 2,
|
|
10
|
-
DEBUG: 3,
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Color codes for terminal output
|
|
15
|
-
*/
|
|
16
|
-
const COLORS = {
|
|
17
|
-
ERROR: '\x1b[31m', // Red
|
|
18
|
-
WARN: '\x1b[33m', // Yellow
|
|
19
|
-
INFO: '\x1b[36m', // Cyan
|
|
20
|
-
DEBUG: '\x1b[90m', // Gray
|
|
21
|
-
RESET: '\x1b[0m',
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
class Logger {
|
|
25
|
-
constructor(options = {}) {
|
|
26
|
-
this.level = this._getLogLevel(options.level || process.env.LOG_LEVEL || 'INFO');
|
|
27
|
-
this.isProd = process.env.NODE_ENV === 'production';
|
|
28
|
-
this.enableColors = !this.isProd && process.stdout.isTTY;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
_getLogLevel(levelName) {
|
|
32
|
-
const upperLevel = levelName.toUpperCase();
|
|
33
|
-
return LOG_LEVELS[upperLevel] !== undefined ? LOG_LEVELS[upperLevel] : LOG_LEVELS.INFO;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
_shouldLog(level) {
|
|
37
|
-
return LOG_LEVELS[level] <= this.level;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
_formatMessage(level, message, context = {}) {
|
|
41
|
-
const timestamp = new Date().toISOString();
|
|
42
|
-
|
|
43
|
-
if (this.isProd) {
|
|
44
|
-
// Production: JSON format for log aggregation tools
|
|
45
|
-
return JSON.stringify({
|
|
46
|
-
timestamp,
|
|
47
|
-
level,
|
|
48
|
-
message,
|
|
49
|
-
...context,
|
|
50
|
-
});
|
|
51
|
-
} else {
|
|
52
|
-
// Development: Human-readable format with colors
|
|
53
|
-
const color = this.enableColors ? COLORS[level] : '';
|
|
54
|
-
const reset = this.enableColors ? COLORS.RESET : '';
|
|
55
|
-
const contextStr = Object.keys(context).length > 0
|
|
56
|
-
? '\n' + util.inspect(context, { depth: 4, colors: this.enableColors })
|
|
57
|
-
: '';
|
|
58
|
-
|
|
59
|
-
return `${color}[${timestamp}] [${level}]${reset} ${message}${contextStr}`;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
_log(level, message, context = {}) {
|
|
64
|
-
if (!this._shouldLog(level)) {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const formattedMessage = this._formatMessage(level, message, context);
|
|
69
|
-
|
|
70
|
-
if (level === 'ERROR' || level === 'WARN') {
|
|
71
|
-
console.error(formattedMessage);
|
|
72
|
-
} else {
|
|
73
|
-
console.log(formattedMessage);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Log error level messages
|
|
79
|
-
* @param {string} message - Log message
|
|
80
|
-
* @param {Object} context - Additional context (error, stack, platform, etc.)
|
|
81
|
-
*/
|
|
82
|
-
error(message, context = {}) {
|
|
83
|
-
// If context has an error object, extract useful information
|
|
84
|
-
let enrichedContext = context;
|
|
85
|
-
if (context.error instanceof Error) {
|
|
86
|
-
const { error, ...rest } = context;
|
|
87
|
-
enrichedContext = {
|
|
88
|
-
...rest,
|
|
89
|
-
errorMessage: error.message,
|
|
90
|
-
errorStack: error.stack,
|
|
91
|
-
errorResponse: error.response?.data,
|
|
92
|
-
errorStatus: error.response?.status,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
this._log('ERROR', message, enrichedContext);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Log warning level messages
|
|
101
|
-
* @param {string} message - Log message
|
|
102
|
-
* @param {Object} context - Additional context
|
|
103
|
-
*/
|
|
104
|
-
warn(message, context = {}) {
|
|
105
|
-
this._log('WARN', message, context);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Log info level messages
|
|
110
|
-
* @param {string} message - Log message
|
|
111
|
-
* @param {Object} context - Additional context
|
|
112
|
-
*/
|
|
113
|
-
info(message, context = {}) {
|
|
114
|
-
this._log('INFO', message, context);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Log debug level messages
|
|
119
|
-
* @param {string} message - Log message
|
|
120
|
-
* @param {Object} context - Additional context
|
|
121
|
-
*/
|
|
122
|
-
debug(message, context = {}) {
|
|
123
|
-
this._log('DEBUG', message, context);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Create a child logger with default context
|
|
128
|
-
* Useful for adding request-specific or module-specific context
|
|
129
|
-
* @param {Object} defaultContext - Context to include in all logs
|
|
130
|
-
* @returns {Object} Child logger with bound context
|
|
131
|
-
*/
|
|
132
|
-
child(defaultContext = {}) {
|
|
133
|
-
return {
|
|
134
|
-
error: (message, context = {}) => this.error(message, { ...defaultContext, ...context }),
|
|
135
|
-
warn: (message, context = {}) => this.warn(message, { ...defaultContext, ...context }),
|
|
136
|
-
info: (message, context = {}) => this.info(message, { ...defaultContext, ...context }),
|
|
137
|
-
debug: (message, context = {}) => this.debug(message, { ...defaultContext, ...context }),
|
|
138
|
-
child: (additionalContext) => this.child({ ...defaultContext, ...additionalContext }),
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Log API request/response for debugging
|
|
144
|
-
* @param {Object} options - Request details
|
|
145
|
-
*/
|
|
146
|
-
logApiRequest({ method, url, status, duration, platform, error }) {
|
|
147
|
-
const context = {
|
|
148
|
-
method,
|
|
149
|
-
url,
|
|
150
|
-
status,
|
|
151
|
-
duration: duration ? `${duration}ms` : undefined,
|
|
152
|
-
platform,
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
if (error) {
|
|
156
|
-
this.error('API request failed', { ...context, error });
|
|
157
|
-
} else if (status >= 400) {
|
|
158
|
-
this.warn('API request returned error status', context);
|
|
159
|
-
} else {
|
|
160
|
-
this.debug('API request completed', context);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Log database query for debugging
|
|
166
|
-
* @param {Object} options - Query details
|
|
167
|
-
*/
|
|
168
|
-
logDatabaseQuery({ operation, table, duration, error }) {
|
|
169
|
-
const context = {
|
|
170
|
-
operation,
|
|
171
|
-
table,
|
|
172
|
-
duration: duration ? `${duration}ms` : undefined,
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
if (error) {
|
|
176
|
-
this.error('Database query failed', { ...context, error });
|
|
177
|
-
} else {
|
|
178
|
-
this.debug('Database query completed', context);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Create singleton instance
|
|
184
|
-
const logger = new Logger();
|
|
185
|
-
|
|
186
|
-
// Export both the class and instance
|
|
187
|
-
module.exports = logger;
|
|
188
|
-
module.exports.Logger = Logger;
|
|
189
|
-
module.exports.LOG_LEVELS = LOG_LEVELS;
|
|
190
|
-
|
|
1
|
+
const util = require('util');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Logger levels
|
|
5
|
+
*/
|
|
6
|
+
const LOG_LEVELS = {
|
|
7
|
+
ERROR: 0,
|
|
8
|
+
WARN: 1,
|
|
9
|
+
INFO: 2,
|
|
10
|
+
DEBUG: 3,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Color codes for terminal output
|
|
15
|
+
*/
|
|
16
|
+
const COLORS = {
|
|
17
|
+
ERROR: '\x1b[31m', // Red
|
|
18
|
+
WARN: '\x1b[33m', // Yellow
|
|
19
|
+
INFO: '\x1b[36m', // Cyan
|
|
20
|
+
DEBUG: '\x1b[90m', // Gray
|
|
21
|
+
RESET: '\x1b[0m',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
class Logger {
|
|
25
|
+
constructor(options = {}) {
|
|
26
|
+
this.level = this._getLogLevel(options.level || process.env.LOG_LEVEL || 'INFO');
|
|
27
|
+
this.isProd = process.env.NODE_ENV === 'production';
|
|
28
|
+
this.enableColors = !this.isProd && process.stdout.isTTY;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
_getLogLevel(levelName) {
|
|
32
|
+
const upperLevel = levelName.toUpperCase();
|
|
33
|
+
return LOG_LEVELS[upperLevel] !== undefined ? LOG_LEVELS[upperLevel] : LOG_LEVELS.INFO;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_shouldLog(level) {
|
|
37
|
+
return LOG_LEVELS[level] <= this.level;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
_formatMessage(level, message, context = {}) {
|
|
41
|
+
const timestamp = new Date().toISOString();
|
|
42
|
+
|
|
43
|
+
if (this.isProd) {
|
|
44
|
+
// Production: JSON format for log aggregation tools
|
|
45
|
+
return JSON.stringify({
|
|
46
|
+
timestamp,
|
|
47
|
+
level,
|
|
48
|
+
message,
|
|
49
|
+
...context,
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
// Development: Human-readable format with colors
|
|
53
|
+
const color = this.enableColors ? COLORS[level] : '';
|
|
54
|
+
const reset = this.enableColors ? COLORS.RESET : '';
|
|
55
|
+
const contextStr = Object.keys(context).length > 0
|
|
56
|
+
? '\n' + util.inspect(context, { depth: 4, colors: this.enableColors })
|
|
57
|
+
: '';
|
|
58
|
+
|
|
59
|
+
return `${color}[${timestamp}] [${level}]${reset} ${message}${contextStr}`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
_log(level, message, context = {}) {
|
|
64
|
+
if (!this._shouldLog(level)) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const formattedMessage = this._formatMessage(level, message, context);
|
|
69
|
+
|
|
70
|
+
if (level === 'ERROR' || level === 'WARN') {
|
|
71
|
+
console.error(formattedMessage);
|
|
72
|
+
} else {
|
|
73
|
+
console.log(formattedMessage);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Log error level messages
|
|
79
|
+
* @param {string} message - Log message
|
|
80
|
+
* @param {Object} context - Additional context (error, stack, platform, etc.)
|
|
81
|
+
*/
|
|
82
|
+
error(message, context = {}) {
|
|
83
|
+
// If context has an error object, extract useful information
|
|
84
|
+
let enrichedContext = context;
|
|
85
|
+
if (context.error instanceof Error) {
|
|
86
|
+
const { error, ...rest } = context;
|
|
87
|
+
enrichedContext = {
|
|
88
|
+
...rest,
|
|
89
|
+
errorMessage: error.message,
|
|
90
|
+
errorStack: error.stack,
|
|
91
|
+
errorResponse: error.response?.data,
|
|
92
|
+
errorStatus: error.response?.status,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this._log('ERROR', message, enrichedContext);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Log warning level messages
|
|
101
|
+
* @param {string} message - Log message
|
|
102
|
+
* @param {Object} context - Additional context
|
|
103
|
+
*/
|
|
104
|
+
warn(message, context = {}) {
|
|
105
|
+
this._log('WARN', message, context);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Log info level messages
|
|
110
|
+
* @param {string} message - Log message
|
|
111
|
+
* @param {Object} context - Additional context
|
|
112
|
+
*/
|
|
113
|
+
info(message, context = {}) {
|
|
114
|
+
this._log('INFO', message, context);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Log debug level messages
|
|
119
|
+
* @param {string} message - Log message
|
|
120
|
+
* @param {Object} context - Additional context
|
|
121
|
+
*/
|
|
122
|
+
debug(message, context = {}) {
|
|
123
|
+
this._log('DEBUG', message, context);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Create a child logger with default context
|
|
128
|
+
* Useful for adding request-specific or module-specific context
|
|
129
|
+
* @param {Object} defaultContext - Context to include in all logs
|
|
130
|
+
* @returns {Object} Child logger with bound context
|
|
131
|
+
*/
|
|
132
|
+
child(defaultContext = {}) {
|
|
133
|
+
return {
|
|
134
|
+
error: (message, context = {}) => this.error(message, { ...defaultContext, ...context }),
|
|
135
|
+
warn: (message, context = {}) => this.warn(message, { ...defaultContext, ...context }),
|
|
136
|
+
info: (message, context = {}) => this.info(message, { ...defaultContext, ...context }),
|
|
137
|
+
debug: (message, context = {}) => this.debug(message, { ...defaultContext, ...context }),
|
|
138
|
+
child: (additionalContext) => this.child({ ...defaultContext, ...additionalContext }),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Log API request/response for debugging
|
|
144
|
+
* @param {Object} options - Request details
|
|
145
|
+
*/
|
|
146
|
+
logApiRequest({ method, url, status, duration, platform, error }) {
|
|
147
|
+
const context = {
|
|
148
|
+
method,
|
|
149
|
+
url,
|
|
150
|
+
status,
|
|
151
|
+
duration: duration ? `${duration}ms` : undefined,
|
|
152
|
+
platform,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
if (error) {
|
|
156
|
+
this.error('API request failed', { ...context, error });
|
|
157
|
+
} else if (status >= 400) {
|
|
158
|
+
this.warn('API request returned error status', context);
|
|
159
|
+
} else {
|
|
160
|
+
this.debug('API request completed', context);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Log database query for debugging
|
|
166
|
+
* @param {Object} options - Query details
|
|
167
|
+
*/
|
|
168
|
+
logDatabaseQuery({ operation, table, duration, error }) {
|
|
169
|
+
const context = {
|
|
170
|
+
operation,
|
|
171
|
+
table,
|
|
172
|
+
duration: duration ? `${duration}ms` : undefined,
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
if (error) {
|
|
176
|
+
this.error('Database query failed', { ...context, error });
|
|
177
|
+
} else {
|
|
178
|
+
this.debug('Database query completed', context);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Create singleton instance
|
|
184
|
+
const logger = new Logger();
|
|
185
|
+
|
|
186
|
+
// Export both the class and instance
|
|
187
|
+
module.exports = logger;
|
|
188
|
+
module.exports.Logger = Logger;
|
|
189
|
+
module.exports.LOG_LEVELS = LOG_LEVELS;
|
|
190
|
+
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
const Sequelize = require('sequelize');
|
|
2
|
+
|
|
3
|
+
function findColumnKey(tableDescription, name) {
|
|
4
|
+
const lower = name.toLowerCase();
|
|
5
|
+
return Object.keys(tableDescription).find((k) => k.toLowerCase() === lower);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
async function fetchCallLogsCreateSqlSqlite(sequelize, options = {}) {
|
|
9
|
+
const rows = await sequelize.query(
|
|
10
|
+
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'callLogs'",
|
|
11
|
+
{ type: Sequelize.QueryTypes.SELECT, ...options },
|
|
12
|
+
);
|
|
13
|
+
return rows[0]?.sql ?? null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function callLogsCreateSqlClaimsExtensionPk(createSql) {
|
|
17
|
+
if (!createSql || typeof createSql !== 'string') {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const pkRegex = /PRIMARY\s+KEY\s*\(([^)]+)\)/gi;
|
|
21
|
+
let m;
|
|
22
|
+
while ((m = pkRegex.exec(createSql)) !== null) {
|
|
23
|
+
if (/\bextensionNumber\b/i.test(m[1])) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function sqliteCallLogsPkIncludesExtension(sequelize, options = {}) {
|
|
31
|
+
const sql = await fetchCallLogsCreateSqlSqlite(sequelize, options);
|
|
32
|
+
return callLogsCreateSqlClaimsExtensionPk(sql);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* SQLite cannot change composite PK via Sequelize addConstraint (it appends a second PRIMARY KEY).
|
|
37
|
+
* Rebuild the table with the correct 3-column primary key and copy rows.
|
|
38
|
+
*/
|
|
39
|
+
async function migrateCallLogsExtensionNumberSqlite(sequelize, transaction) {
|
|
40
|
+
const qg = sequelize.getQueryInterface().queryGenerator;
|
|
41
|
+
const qi = sequelize.getQueryInterface();
|
|
42
|
+
const q = (id) => qg.quoteIdentifier(id);
|
|
43
|
+
const tmpName = `callLogs_mig_legacy_${Date.now()}`;
|
|
44
|
+
const mainTableSql = qg.quoteTable({ tableName: 'callLogs' });
|
|
45
|
+
const tmpTableSql = qg.quoteTable({ tableName: tmpName });
|
|
46
|
+
|
|
47
|
+
await sequelize.query(
|
|
48
|
+
`ALTER TABLE ${mainTableSql} RENAME TO ${qg.quoteIdentifier(tmpName)};`,
|
|
49
|
+
{ transaction },
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
await sequelize.query(
|
|
53
|
+
`
|
|
54
|
+
CREATE TABLE ${mainTableSql} (
|
|
55
|
+
${q('id')} VARCHAR(255) NOT NULL,
|
|
56
|
+
${q('sessionId')} VARCHAR(255) NOT NULL,
|
|
57
|
+
${q('extensionNumber')} VARCHAR(255) NOT NULL DEFAULT '',
|
|
58
|
+
${q('platform')} VARCHAR(255),
|
|
59
|
+
${q('thirdPartyLogId')} VARCHAR(255),
|
|
60
|
+
${q('userId')} VARCHAR(255),
|
|
61
|
+
${q('contactId')} VARCHAR(255),
|
|
62
|
+
${q('createdAt')} DATETIME NOT NULL,
|
|
63
|
+
${q('updatedAt')} DATETIME NOT NULL,
|
|
64
|
+
PRIMARY KEY (${q('id')}, ${q('sessionId')}, ${q('extensionNumber')})
|
|
65
|
+
);
|
|
66
|
+
`,
|
|
67
|
+
{ transaction },
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const oldDesc = await qi.describeTable(tmpName, { transaction });
|
|
71
|
+
|
|
72
|
+
const targetCols = [
|
|
73
|
+
'id',
|
|
74
|
+
'sessionId',
|
|
75
|
+
'extensionNumber',
|
|
76
|
+
'platform',
|
|
77
|
+
'thirdPartyLogId',
|
|
78
|
+
'userId',
|
|
79
|
+
'contactId',
|
|
80
|
+
'createdAt',
|
|
81
|
+
'updatedAt',
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
const missingRequired = targetCols.filter(
|
|
85
|
+
(c) => findColumnKey(oldDesc, c) == null && c !== 'extensionNumber',
|
|
86
|
+
);
|
|
87
|
+
if (missingRequired.length) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`unexpected callLogs schema before migration (missing columns: ${missingRequired.join(', ')})`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const insertCols = targetCols.map(q).join(', ');
|
|
94
|
+
const selectExprs = targetCols
|
|
95
|
+
.map((col) => {
|
|
96
|
+
const key = findColumnKey(oldDesc, col);
|
|
97
|
+
if (key != null) {
|
|
98
|
+
return q(key);
|
|
99
|
+
}
|
|
100
|
+
return `CAST('' AS VARCHAR(255))`;
|
|
101
|
+
})
|
|
102
|
+
.join(', ');
|
|
103
|
+
|
|
104
|
+
await sequelize.query(
|
|
105
|
+
`INSERT INTO ${mainTableSql} (${insertCols}) SELECT ${selectExprs} FROM ${tmpTableSql};`,
|
|
106
|
+
{ transaction },
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
await sequelize.query(`DROP TABLE ${tmpTableSql};`, { transaction });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
module.exports = {
|
|
113
|
+
findColumnKey,
|
|
114
|
+
migrateCallLogsExtensionNumberSqlite,
|
|
115
|
+
sqliteCallLogsPkIncludesExtension,
|
|
116
|
+
};
|