@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/jest.config.js
CHANGED
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
|
|
3
|
-
module.exports = {
|
|
4
|
-
// Test environment
|
|
5
|
-
testEnvironment: 'node',
|
|
6
|
-
|
|
7
|
-
// Test file patterns
|
|
8
|
-
testMatch: [
|
|
9
|
-
'<rootDir>/test/**/*.test.js',
|
|
10
|
-
'<rootDir>/**/*.test.js'
|
|
11
|
-
],
|
|
12
|
-
|
|
13
|
-
// Setup files
|
|
14
|
-
setupFilesAfterEnv: [
|
|
15
|
-
'<rootDir>/test/setup.js'
|
|
16
|
-
],
|
|
17
|
-
|
|
18
|
-
// Coverage configuration
|
|
19
|
-
collectCoverage: true,
|
|
20
|
-
coverageDirectory: '<rootDir>/coverage',
|
|
21
|
-
coverageReporters: ['text', 'lcov', 'html'],
|
|
22
|
-
coveragePathIgnorePatterns: [
|
|
23
|
-
'/node_modules/',
|
|
24
|
-
'/test/',
|
|
25
|
-
'/coverage/',
|
|
26
|
-
'jest.config.js',
|
|
27
|
-
'setup.js'
|
|
28
|
-
],
|
|
29
|
-
|
|
30
|
-
// Module resolution
|
|
31
|
-
moduleDirectories: ['node_modules', '<rootDir>'],
|
|
32
|
-
moduleNameMapper: {
|
|
33
|
-
'^@app-connect/core/(.*)$': '<rootDir>/$1'
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
// Test timeout
|
|
37
|
-
testTimeout: 30000,
|
|
38
|
-
|
|
39
|
-
// Reporters
|
|
40
|
-
reporters: ['default'],
|
|
41
|
-
|
|
42
|
-
// Ignore patterns
|
|
43
|
-
modulePathIgnorePatterns: [
|
|
44
|
-
'<rootDir>/node_modules/',
|
|
45
|
-
'<rootDir>/coverage/',
|
|
46
|
-
'<rootDir>/test-results/'
|
|
47
|
-
],
|
|
48
|
-
|
|
49
|
-
// Clear mocks between tests
|
|
50
|
-
clearMocks: true,
|
|
51
|
-
|
|
52
|
-
// Restore mocks between tests
|
|
53
|
-
restoreMocks: true,
|
|
54
|
-
|
|
55
|
-
// Verbose output
|
|
56
|
-
verbose: true
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
// Test environment
|
|
5
|
+
testEnvironment: 'node',
|
|
6
|
+
|
|
7
|
+
// Test file patterns
|
|
8
|
+
testMatch: [
|
|
9
|
+
'<rootDir>/test/**/*.test.js',
|
|
10
|
+
'<rootDir>/**/*.test.js'
|
|
11
|
+
],
|
|
12
|
+
|
|
13
|
+
// Setup files
|
|
14
|
+
setupFilesAfterEnv: [
|
|
15
|
+
'<rootDir>/test/setup.js'
|
|
16
|
+
],
|
|
17
|
+
|
|
18
|
+
// Coverage configuration
|
|
19
|
+
collectCoverage: true,
|
|
20
|
+
coverageDirectory: '<rootDir>/coverage',
|
|
21
|
+
coverageReporters: ['text', 'lcov', 'html'],
|
|
22
|
+
coveragePathIgnorePatterns: [
|
|
23
|
+
'/node_modules/',
|
|
24
|
+
'/test/',
|
|
25
|
+
'/coverage/',
|
|
26
|
+
'jest.config.js',
|
|
27
|
+
'setup.js'
|
|
28
|
+
],
|
|
29
|
+
|
|
30
|
+
// Module resolution
|
|
31
|
+
moduleDirectories: ['node_modules', '<rootDir>'],
|
|
32
|
+
moduleNameMapper: {
|
|
33
|
+
'^@app-connect/core/(.*)$': '<rootDir>/$1'
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
// Test timeout
|
|
37
|
+
testTimeout: 30000,
|
|
38
|
+
|
|
39
|
+
// Reporters
|
|
40
|
+
reporters: ['default'],
|
|
41
|
+
|
|
42
|
+
// Ignore patterns
|
|
43
|
+
modulePathIgnorePatterns: [
|
|
44
|
+
'<rootDir>/node_modules/',
|
|
45
|
+
'<rootDir>/coverage/',
|
|
46
|
+
'<rootDir>/test-results/'
|
|
47
|
+
],
|
|
48
|
+
|
|
49
|
+
// Clear mocks between tests
|
|
50
|
+
clearMocks: true,
|
|
51
|
+
|
|
52
|
+
// Restore mocks between tests
|
|
53
|
+
restoreMocks: true,
|
|
54
|
+
|
|
55
|
+
// Verbose output
|
|
56
|
+
verbose: true
|
|
57
57
|
};
|
package/lib/analytics.js
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
const Mixpanel = require('mixpanel');
|
|
2
|
-
const parser = require('ua-parser-js');
|
|
3
|
-
const logger = require('./logger');
|
|
4
|
-
let packageJson = null;
|
|
5
|
-
try {
|
|
6
|
-
packageJson = require('../package.json');
|
|
7
|
-
}
|
|
8
|
-
catch (e) {
|
|
9
|
-
logger.warn('Error loading package.json', { stack: e.stack });
|
|
10
|
-
packageJson = require('../../package.json');
|
|
11
|
-
}
|
|
12
|
-
const appName = 'App Connect';
|
|
13
|
-
const defaultEventAddedVia = 'server';
|
|
14
|
-
const version = packageJson.version;
|
|
15
|
-
let mixpanel = null;
|
|
16
|
-
|
|
17
|
-
exports.init = function init() {
|
|
18
|
-
if (!process.env.MIXPANEL_TOKEN) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
mixpanel = Mixpanel.init(process.env.MIXPANEL_TOKEN);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
exports.track = function track({ eventName, interfaceName, connectorName, accountId, extensionId, success, requestDuration, userAgent, ip, author, eventAddedVia, extras = null }) {
|
|
25
|
-
if (!mixpanel || !extensionId) {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
const inUseEventAddedVia = eventAddedVia || defaultEventAddedVia;
|
|
29
|
-
mixpanel.people.set_once(extensionId, {
|
|
30
|
-
version,
|
|
31
|
-
appName,
|
|
32
|
-
crmPlatform: connectorName
|
|
33
|
-
});
|
|
34
|
-
const ua = parser(userAgent);
|
|
35
|
-
mixpanel.track(eventName, {
|
|
36
|
-
distinct_id: extensionId,
|
|
37
|
-
interfaceName,
|
|
38
|
-
adapterName: connectorName,
|
|
39
|
-
rcAccountId: accountId,
|
|
40
|
-
extensionId,
|
|
41
|
-
success,
|
|
42
|
-
requestDuration,
|
|
43
|
-
collectedFrom: 'server',
|
|
44
|
-
version,
|
|
45
|
-
appName,
|
|
46
|
-
eventAddedVia: inUseEventAddedVia,
|
|
47
|
-
$browser: ua.browser.name,
|
|
48
|
-
$os: ua.os.name,
|
|
49
|
-
$device: ua.device.type,
|
|
50
|
-
ip,
|
|
51
|
-
author,
|
|
52
|
-
...extras
|
|
53
|
-
});
|
|
54
|
-
logger.info(`Event: ${eventName}`);
|
|
1
|
+
const Mixpanel = require('mixpanel');
|
|
2
|
+
const parser = require('ua-parser-js');
|
|
3
|
+
const logger = require('./logger');
|
|
4
|
+
let packageJson = null;
|
|
5
|
+
try {
|
|
6
|
+
packageJson = require('../package.json');
|
|
7
|
+
}
|
|
8
|
+
catch (e) {
|
|
9
|
+
logger.warn('Error loading package.json', { stack: e.stack });
|
|
10
|
+
packageJson = require('../../package.json');
|
|
11
|
+
}
|
|
12
|
+
const appName = 'App Connect';
|
|
13
|
+
const defaultEventAddedVia = 'server';
|
|
14
|
+
const version = packageJson.version;
|
|
15
|
+
let mixpanel = null;
|
|
16
|
+
|
|
17
|
+
exports.init = function init() {
|
|
18
|
+
if (!process.env.MIXPANEL_TOKEN) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
mixpanel = Mixpanel.init(process.env.MIXPANEL_TOKEN);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
exports.track = function track({ eventName, interfaceName, connectorName, accountId, extensionId, success, requestDuration, userAgent, ip, author, eventAddedVia, extras = null }) {
|
|
25
|
+
if (!mixpanel || !extensionId) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const inUseEventAddedVia = eventAddedVia || defaultEventAddedVia;
|
|
29
|
+
mixpanel.people.set_once(extensionId, {
|
|
30
|
+
version,
|
|
31
|
+
appName,
|
|
32
|
+
crmPlatform: connectorName
|
|
33
|
+
});
|
|
34
|
+
const ua = parser(userAgent);
|
|
35
|
+
mixpanel.track(eventName, {
|
|
36
|
+
distinct_id: extensionId,
|
|
37
|
+
interfaceName,
|
|
38
|
+
adapterName: connectorName,
|
|
39
|
+
rcAccountId: accountId,
|
|
40
|
+
extensionId,
|
|
41
|
+
success,
|
|
42
|
+
requestDuration,
|
|
43
|
+
collectedFrom: 'server',
|
|
44
|
+
version,
|
|
45
|
+
appName,
|
|
46
|
+
eventAddedVia: inUseEventAddedVia,
|
|
47
|
+
$browser: ua.browser.name,
|
|
48
|
+
$os: ua.os.name,
|
|
49
|
+
$device: ua.device.type,
|
|
50
|
+
ip,
|
|
51
|
+
author,
|
|
52
|
+
...extras
|
|
53
|
+
});
|
|
54
|
+
logger.info(`Event: ${eventName}`);
|
|
55
55
|
}
|
package/lib/authSession.js
CHANGED
|
@@ -1,109 +1,109 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auth Session Helper
|
|
3
|
-
*
|
|
4
|
-
* Helper functions for managing OAuth auth sessions using CacheModel
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { CacheModel } = require('../models/cacheModel');
|
|
8
|
-
|
|
9
|
-
const AUTH_SESSION_PREFIX = 'auth-session';
|
|
10
|
-
const PENDING_SESSION_EXPIRY_MINUTES = 5;
|
|
11
|
-
const SETTLED_SESSION_EXPIRY_MINUTES = 15;
|
|
12
|
-
|
|
13
|
-
function getSessionRecordId(sessionId) {
|
|
14
|
-
return `${AUTH_SESSION_PREFIX}-${sessionId}`;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function getExpiry(minutes) {
|
|
18
|
-
return new Date(Date.now() + minutes * 60 * 1000);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function isExpired(record) {
|
|
22
|
-
return Boolean(record?.expiry && record.expiry <= new Date());
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Create (or reset) an auth session.
|
|
27
|
-
* If a record already exists for the sessionId (e.g., user retries auth within
|
|
28
|
-
* the same ChatGPT conversation), it is reset to 'pending' so polling works
|
|
29
|
-
* correctly for the new attempt.
|
|
30
|
-
*/
|
|
31
|
-
async function createAuthSession(sessionId, data) {
|
|
32
|
-
const id = getSessionRecordId(sessionId);
|
|
33
|
-
const expiry = getExpiry(PENDING_SESSION_EXPIRY_MINUTES);
|
|
34
|
-
const sessionData = {
|
|
35
|
-
platform: data.platform,
|
|
36
|
-
hostname: data.hostname || '',
|
|
37
|
-
createdAt: new Date().toISOString(),
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const existing = await CacheModel.findByPk(id);
|
|
41
|
-
if (existing) {
|
|
42
|
-
await existing.update({ status: 'pending', data: sessionData, expiry });
|
|
43
|
-
} else {
|
|
44
|
-
await CacheModel.create({
|
|
45
|
-
id,
|
|
46
|
-
cacheKey: AUTH_SESSION_PREFIX,
|
|
47
|
-
userId: sessionId,
|
|
48
|
-
status: 'pending',
|
|
49
|
-
data: sessionData,
|
|
50
|
-
expiry,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Get an auth session by ID
|
|
57
|
-
*/
|
|
58
|
-
async function getAuthSession(sessionId) {
|
|
59
|
-
const record = await CacheModel.findByPk(getSessionRecordId(sessionId));
|
|
60
|
-
|
|
61
|
-
if (!record) return null;
|
|
62
|
-
|
|
63
|
-
if (isExpired(record)) {
|
|
64
|
-
if (record.status !== 'expired') {
|
|
65
|
-
await record.update({ status: 'expired' });
|
|
66
|
-
}
|
|
67
|
-
return {
|
|
68
|
-
sessionId: record.userId,
|
|
69
|
-
status: 'expired',
|
|
70
|
-
...record.data
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return {
|
|
75
|
-
sessionId: record.userId,
|
|
76
|
-
status: record.status,
|
|
77
|
-
...record.data
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Update an auth session
|
|
83
|
-
*/
|
|
84
|
-
async function updateAuthSession(sessionId, data) {
|
|
85
|
-
const record = await CacheModel.findByPk(getSessionRecordId(sessionId));
|
|
86
|
-
|
|
87
|
-
if (!record) return;
|
|
88
|
-
|
|
89
|
-
const existingData = record.data || {};
|
|
90
|
-
const nextStatus = data.status || record.status;
|
|
91
|
-
await record.update({
|
|
92
|
-
status: nextStatus,
|
|
93
|
-
data: {
|
|
94
|
-
...existingData,
|
|
95
|
-
...data,
|
|
96
|
-
updatedAt: new Date().toISOString()
|
|
97
|
-
},
|
|
98
|
-
expiry: nextStatus === 'pending'
|
|
99
|
-
? getExpiry(PENDING_SESSION_EXPIRY_MINUTES)
|
|
100
|
-
: getExpiry(SETTLED_SESSION_EXPIRY_MINUTES)
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
module.exports = {
|
|
105
|
-
createAuthSession,
|
|
106
|
-
getAuthSession,
|
|
107
|
-
updateAuthSession
|
|
108
|
-
};
|
|
109
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Auth Session Helper
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for managing OAuth auth sessions using CacheModel
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { CacheModel } = require('../models/cacheModel');
|
|
8
|
+
|
|
9
|
+
const AUTH_SESSION_PREFIX = 'auth-session';
|
|
10
|
+
const PENDING_SESSION_EXPIRY_MINUTES = 5;
|
|
11
|
+
const SETTLED_SESSION_EXPIRY_MINUTES = 15;
|
|
12
|
+
|
|
13
|
+
function getSessionRecordId(sessionId) {
|
|
14
|
+
return `${AUTH_SESSION_PREFIX}-${sessionId}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getExpiry(minutes) {
|
|
18
|
+
return new Date(Date.now() + minutes * 60 * 1000);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isExpired(record) {
|
|
22
|
+
return Boolean(record?.expiry && record.expiry <= new Date());
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Create (or reset) an auth session.
|
|
27
|
+
* If a record already exists for the sessionId (e.g., user retries auth within
|
|
28
|
+
* the same ChatGPT conversation), it is reset to 'pending' so polling works
|
|
29
|
+
* correctly for the new attempt.
|
|
30
|
+
*/
|
|
31
|
+
async function createAuthSession(sessionId, data) {
|
|
32
|
+
const id = getSessionRecordId(sessionId);
|
|
33
|
+
const expiry = getExpiry(PENDING_SESSION_EXPIRY_MINUTES);
|
|
34
|
+
const sessionData = {
|
|
35
|
+
platform: data.platform,
|
|
36
|
+
hostname: data.hostname || '',
|
|
37
|
+
createdAt: new Date().toISOString(),
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const existing = await CacheModel.findByPk(id);
|
|
41
|
+
if (existing) {
|
|
42
|
+
await existing.update({ status: 'pending', data: sessionData, expiry });
|
|
43
|
+
} else {
|
|
44
|
+
await CacheModel.create({
|
|
45
|
+
id,
|
|
46
|
+
cacheKey: AUTH_SESSION_PREFIX,
|
|
47
|
+
userId: sessionId,
|
|
48
|
+
status: 'pending',
|
|
49
|
+
data: sessionData,
|
|
50
|
+
expiry,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get an auth session by ID
|
|
57
|
+
*/
|
|
58
|
+
async function getAuthSession(sessionId) {
|
|
59
|
+
const record = await CacheModel.findByPk(getSessionRecordId(sessionId));
|
|
60
|
+
|
|
61
|
+
if (!record) return null;
|
|
62
|
+
|
|
63
|
+
if (isExpired(record)) {
|
|
64
|
+
if (record.status !== 'expired') {
|
|
65
|
+
await record.update({ status: 'expired' });
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
sessionId: record.userId,
|
|
69
|
+
status: 'expired',
|
|
70
|
+
...record.data
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
sessionId: record.userId,
|
|
76
|
+
status: record.status,
|
|
77
|
+
...record.data
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Update an auth session
|
|
83
|
+
*/
|
|
84
|
+
async function updateAuthSession(sessionId, data) {
|
|
85
|
+
const record = await CacheModel.findByPk(getSessionRecordId(sessionId));
|
|
86
|
+
|
|
87
|
+
if (!record) return;
|
|
88
|
+
|
|
89
|
+
const existingData = record.data || {};
|
|
90
|
+
const nextStatus = data.status || record.status;
|
|
91
|
+
await record.update({
|
|
92
|
+
status: nextStatus,
|
|
93
|
+
data: {
|
|
94
|
+
...existingData,
|
|
95
|
+
...data,
|
|
96
|
+
updatedAt: new Date().toISOString()
|
|
97
|
+
},
|
|
98
|
+
expiry: nextStatus === 'pending'
|
|
99
|
+
? getExpiry(PENDING_SESSION_EXPIRY_MINUTES)
|
|
100
|
+
: getExpiry(SETTLED_SESSION_EXPIRY_MINUTES)
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = {
|
|
105
|
+
createAuthSession,
|
|
106
|
+
getAuthSession,
|
|
107
|
+
updateAuthSession
|
|
108
|
+
};
|
|
109
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const { Op } = require('sequelize');
|
|
2
|
+
const { CacheModel } = require('../models/cacheModel');
|
|
3
|
+
const logger = require('./logger');
|
|
4
|
+
|
|
5
|
+
async function clearExpiredCache({ now = new Date() } = {}) {
|
|
6
|
+
const deletedCount = await CacheModel.destroy({
|
|
7
|
+
where: {
|
|
8
|
+
expiry: {
|
|
9
|
+
[Op.lte]: now
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
logger.info('Expired cache cleanup completed', { deletedCount });
|
|
15
|
+
|
|
16
|
+
return deletedCount;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
clearExpiredCache
|
|
21
|
+
};
|