@app-connect/core 0.0.1

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/README.md ADDED
@@ -0,0 +1,266 @@
1
+ # App Connect Core
2
+
3
+ Core package for RingCentral App Connect project providing modular APIs for CRM integration, authentication, contact management, and call logging.
4
+
5
+ ## Features
6
+
7
+ - **Modular API Design**: Flexible Express app setup with customizable middleware and routes
8
+ - **CRM Adapter Registry**: Centralized adapter management for multiple CRM platforms
9
+ - **Authentication**: OAuth and API key authentication support
10
+ - **Contact Management**: Find, create, and manage contacts across CRM platforms
11
+ - **Call Logging**: Comprehensive call and message logging capabilities
12
+ - **Analytics**: Built-in analytics tracking for all operations
13
+ - **Database Integration**: DynamoDB integration with automatic table management
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @app-connect/core
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ### Basic Usage
24
+
25
+ ```javascript
26
+ const { createCoreApp, adapterRegistry } = require('@app-connect/core');
27
+
28
+ // Register your CRM adapters
29
+ adapterRegistry.registerAdapter('myCRM', myCRMAdapter);
30
+
31
+ // Create Express app with all core functionality
32
+ const app = createCoreApp();
33
+
34
+ // Add your custom routes
35
+ app.get('/my-custom-route', (req, res) => {
36
+ res.send('Hello from custom route!');
37
+ });
38
+
39
+ exports.getServer = () => app;
40
+ ```
41
+
42
+ ### Advanced Usage with Custom Middleware
43
+
44
+ ```javascript
45
+ const express = require('express');
46
+ const {
47
+ createCoreRouter,
48
+ createCoreMiddleware,
49
+ initializeCore,
50
+ adapterRegistry
51
+ } = require('@app-connect/core');
52
+
53
+ // Register adapters
54
+ adapterRegistry.registerAdapter('myCRM', myCRMAdapter);
55
+
56
+ // Initialize core services
57
+ initializeCore();
58
+
59
+ // Create your own Express app
60
+ const app = express();
61
+
62
+ // Add custom middleware first
63
+ app.use(express.static('public'));
64
+ app.use('/api/v2', customApiMiddleware);
65
+
66
+ // Apply core middleware
67
+ const coreMiddleware = createCoreMiddleware();
68
+ coreMiddleware.forEach(middleware => app.use(middleware));
69
+
70
+ // Add core routes
71
+ const coreRouter = createCoreRouter();
72
+ app.use('/', coreRouter);
73
+
74
+ // Add custom routes
75
+ app.get('/my-custom-route', (req, res) => {
76
+ res.send('Hello from custom route!');
77
+ });
78
+ ```
79
+
80
+ ## API Reference
81
+
82
+ ### Core Functions
83
+
84
+ #### `createCoreApp(options)`
85
+ Creates a complete Express app with all core functionality.
86
+
87
+ **Parameters:**
88
+ - `options` (Object, optional): Configuration options
89
+ - `skipDatabaseInit` (Boolean): Skip database initialization (default: false)
90
+ - `skipAnalyticsInit` (Boolean): Skip analytics initialization (default: false)
91
+
92
+ **Returns:** Express application instance
93
+
94
+ #### `createCoreRouter()`
95
+ Creates an Express router with all core routes.
96
+
97
+ **Returns:** Express router instance
98
+
99
+ #### `createCoreMiddleware()`
100
+ Returns an array of core middleware functions.
101
+
102
+ **Returns:** Array of middleware functions
103
+
104
+ #### `initializeCore(options)`
105
+ Initializes core services (database, analytics).
106
+
107
+ **Parameters:**
108
+ - `options` (Object, optional): Configuration options
109
+ - `skipDatabaseInit` (Boolean): Skip database initialization (default: false)
110
+ - `skipAnalyticsInit` (Boolean): Skip analytics initialization (default: false)
111
+
112
+ ### Adapter Registry
113
+
114
+ #### `adapterRegistry.setDefaultManifest(manifest)`
115
+ Sets the default manifest for adapters.
116
+
117
+ **Parameters:**
118
+ - `manifest` (Object): Default manifest
119
+
120
+ #### `adapterRegistry.registerAdapter(name, adapter, manifest)`
121
+ Registers a CRM adapter.
122
+
123
+ **Parameters:**
124
+ - `name` (String): Adapter name
125
+ - `adapter` (Object): Adapter implementation
126
+ - `manifest` (Object, optional): Adapter manifest
127
+
128
+ #### `adapterRegistry.getAdapter(name)`
129
+ Retrieves a registered adapter.
130
+
131
+ **Parameters:**
132
+ - `name` (String): Adapter name
133
+
134
+ **Returns:** Adapter instance
135
+
136
+ ### Exported Components
137
+
138
+ #### Handlers
139
+ ```javascript
140
+ const authHandler = require('@app-connect/core/handlers/auth');
141
+ const contactHandler = require('@app-connect/core/handlers/contact');
142
+ const logHandler = require('@app-connect/core/handlers/log');
143
+ const adminHandler = require('@app-connect/core/handlers/admin');
144
+ const userHandler = require('@app-connect/core/handlers/user');
145
+ const dispositionHandler = require('@app-connect/core/handlers/disposition');
146
+
147
+ // Available handlers:
148
+ // authHandler - Authentication operations
149
+ // contactHandler - Contact management
150
+ // logHandler - Call/message logging
151
+ // adminHandler - Admin operations
152
+ // userHandler - User management
153
+ // dispositionHandler - Call disposition
154
+ ```
155
+
156
+ #### Models
157
+ ```javascript
158
+ const { UserModel } = require('@app-connect/core/models/userModel');
159
+ const { CallLogModel } = require('@app-connect/core/models/callLogModel');
160
+ const { MessageLogModel } = require('@app-connect/core/models/messageLogModel');
161
+ const { AdminConfigModel } = require('@app-connect/core/models/adminConfigModel');
162
+ const { CacheModel } = require('@app-connect/core/models/cacheModel');
163
+
164
+ // Available models:
165
+ // UserModel - User data model
166
+ // CallLogModel - Call log data model
167
+ // MessageLogModel - Message log data model
168
+ // AdminConfigModel - Admin configuration model
169
+ // CacheModel - Cache data model
170
+ ```
171
+
172
+ #### Utilities
173
+ ```javascript
174
+ const jwt = require('@app-connect/core/lib/jwt');
175
+ const analytics = require('@app-connect/core/lib/analytics');
176
+ const util = require('@app-connect/core/lib/util');
177
+
178
+ // Available utilities:
179
+ // jwt - JWT token management
180
+ // analytics - Analytics tracking
181
+ // util - General utilities
182
+ ```
183
+
184
+ ## Core Routes
185
+
186
+ The core package provides the following API endpoints:
187
+
188
+ ### Authentication
189
+ - `GET /authValidation` - Validate user authentication
190
+ - `GET /oauth-callback` - OAuth callback handler
191
+ - `POST /apiKeyLogin` - API key authentication
192
+ - `POST /unAuthorize` - Logout user
193
+
194
+ ### Contact Management
195
+ - `GET /contact` - Find contacts by phone number
196
+ - `POST /contact` - Create new contact
197
+ - `GET /custom/contact/search` - Search contacts by name
198
+
199
+ ### Call Logging
200
+ - `GET /callLog` - Retrieve call logs
201
+ - `POST /callLog` - Create call log
202
+ - `PATCH /callLog` - Update call log
203
+ - `PUT /callDisposition` - Set call disposition
204
+ - `POST /messageLog` - Create message log
205
+
206
+ ### User Management
207
+ - `GET /user/settings` - Get user settings
208
+ - `POST /user/settings` - Update user settings
209
+ - `GET /user/preloadSettings` - Preload user settings
210
+
211
+ ### Admin Operations
212
+ - `GET /admin/settings` - Get admin settings
213
+ - `POST /admin/settings` - Update admin settings
214
+ - `GET /admin/serverLoggingSettings` - Get server logging settings
215
+ - `POST /admin/serverLoggingSettings` - Update server logging settings
216
+
217
+ ### System
218
+ - `GET /releaseNotes` - Get release notes
219
+ - `GET /crmManifest` - Get CRM manifest
220
+ - `GET /is-alive` - Health check
221
+ - `GET /serverVersionInfo` - Get server version
222
+ - `GET /hostname` - Get user hostname
223
+ - `GET /userInfoHash` - Get hashed user info
224
+
225
+ ## Environment Variables
226
+
227
+ The core package uses the following environment variables:
228
+
229
+ - `DYNAMODB_LOCALHOST` - Local DynamoDB endpoint for development
230
+ - `DISABLE_SYNC_DB_TABLE` - Skip database table synchronization
231
+ - `OVERRIDE_APP_SERVER` - Override app server URL in manifests
232
+ - `HASH_KEY` - Key for hashing user information
233
+ - `APP_SERVER_SECRET_KEY` - Server secret key
234
+ - `IS_PROD` - Production environment flag
235
+
236
+ ## Architecture
237
+
238
+ The core package follows a modular architecture:
239
+
240
+ ```
241
+ Core Package
242
+ ├── Handlers (Business Logic)
243
+ │ ├── auth.js - Authentication logic
244
+ │ ├── contact.js - Contact management
245
+ │ ├── log.js - Call/message logging
246
+ │ ├── admin.js - Admin operations
247
+ │ ├── user.js - User management
248
+ │ └── disposition.js - Call disposition
249
+ ├── Models (Data Layer)
250
+ │ ├── userModel.js
251
+ │ ├── callLogModel.js
252
+ │ ├── messageLogModel.js
253
+ │ ├── adminConfigModel.js
254
+ │ └── cacheModel.js
255
+ ├── Utils (Utilities)
256
+ │ ├── jwt.js - JWT operations
257
+ │ ├── analytics.js - Analytics tracking
258
+ │ └── util.js - General utilities
259
+ ├── Adapter Registry
260
+ │ └── registry.js - CRM adapter management
261
+ └── API Layer
262
+ ├── createCoreApp() - Complete app setup
263
+ ├── createCoreRouter() - Route management
264
+ ├── createCoreMiddleware() - Middleware management
265
+ └── initializeCore() - Service initialization
266
+ ```
@@ -0,0 +1,77 @@
1
+ // This mock is to run high traffic tests on the server
2
+
3
+ const { UserModel } = require("../models/userModel");
4
+ const { CallLogModel } = require("../models/callLogModel");
5
+ const shortid = require("shortid");
6
+ const Op = require('sequelize').Op;
7
+
8
+ async function createUser() {
9
+ let mockUser = await UserModel.findByPk('mockUser');
10
+ if (!mockUser) {
11
+ mockUser = await UserModel.create({
12
+ id: 'mockUser'
13
+ });
14
+ }
15
+ return mockUser;
16
+ }
17
+
18
+ async function deleteUser() {
19
+ let mockUser = await UserModel.findByPk('mockUser');
20
+ if (mockUser) {
21
+ await mockUser.destroy();
22
+ return true;
23
+ }
24
+ return false;
25
+ }
26
+
27
+ async function getCallLog({ sessionIds }) {
28
+ const sessionIdsArray = sessionIds.split(',');
29
+ const callLogs = await CallLogModel.findAll({
30
+ where: {
31
+ sessionId: {
32
+ [Op.in]: sessionIdsArray
33
+ }
34
+ }
35
+ });
36
+ const logs = [];
37
+ for (const sId of sessionIdsArray) {
38
+ const callLog = callLogs.find(c => c.sessionId === sId);
39
+ if (!callLog) {
40
+ logs.push({ sessionId: sId, matched: false });
41
+ }
42
+ else {
43
+ logs.push({ sessionId: callLog.sessionId, matched: true, logId: 'mockThirdPartyLogId' });
44
+ }
45
+ }
46
+
47
+ return logs;
48
+ }
49
+
50
+ async function createCallLog({ sessionId }) {
51
+ let callLog = await CallLogModel.findOne({
52
+ where: {
53
+ sessionId
54
+ }
55
+ });
56
+ if (!callLog) {
57
+ callLog = await CallLogModel.create({
58
+ id: shortid.generate(),
59
+ sessionId,
60
+ userId: 'mockUser'
61
+ });
62
+ }
63
+ }
64
+
65
+ async function cleanUpMockLogs() {
66
+ await CallLogModel.destroy({
67
+ where: {
68
+ userId: 'mockUser'
69
+ }
70
+ });
71
+ }
72
+
73
+ exports.createUser = createUser;
74
+ exports.deleteUser = deleteUser;
75
+ exports.getCallLog = getCallLog;
76
+ exports.createCallLog = createCallLog;
77
+ exports.cleanUpMockLogs = cleanUpMockLogs;
@@ -0,0 +1,115 @@
1
+ // core/src/adapter/registry.js
2
+ class AdapterRegistry {
3
+ constructor() {
4
+ this.adapters = new Map();
5
+ this.manifests = new Map();
6
+ this.releaseNotes = {};
7
+ }
8
+
9
+ setDefaultManifest(manifest) {
10
+ this.manifests.set('default', manifest);
11
+ }
12
+
13
+ /**
14
+ * Register an adapter with the core system
15
+ * @param {string} platform - Platform identifier (e.g., 'pipedrive', 'salesforce')
16
+ * @param {Object} adapter - Adapter implementation
17
+ * @param {Object} manifest - Adapter manifest configuration
18
+ */
19
+ registerAdapter(platform, adapter, manifest = null) {
20
+ // Validate adapter interface
21
+ this.validateAdapterInterface(platform, adapter);
22
+
23
+ this.adapters.set(platform, adapter);
24
+ if (manifest) {
25
+ this.manifests.set(platform, manifest);
26
+ }
27
+
28
+ console.log(`Registered adapter: ${platform}`);
29
+ }
30
+
31
+ /**
32
+ * Get adapter by platform name
33
+ * @param {string} platform - Platform identifier
34
+ * @returns {Object} Adapter implementation
35
+ */
36
+ getAdapter(platform) {
37
+ const adapter = this.adapters.get(platform);
38
+ if (!adapter) {
39
+ throw new Error(`Adapter not found for platform: ${platform}`);
40
+ }
41
+ return adapter;
42
+ }
43
+
44
+ /**
45
+ * Get manifest for a platform
46
+ * @param {string} platform - Platform identifier
47
+ * @returns {Object} Manifest configuration
48
+ */
49
+ getManifest(platform, fallbackToDefault = false) {
50
+ let manifest = this.manifests.get(platform);
51
+ if (!manifest && fallbackToDefault) {
52
+ manifest = this.manifests.get('default');
53
+ }
54
+ if (!manifest) {
55
+ throw new Error(`Manifest not found for platform: ${platform}`);
56
+ }
57
+ return manifest;
58
+ }
59
+
60
+ /**
61
+ * Get all registered platforms
62
+ * @returns {Array<string>} Array of platform names
63
+ */
64
+ getRegisteredPlatforms() {
65
+ return Array.from(this.adapters.keys());
66
+ }
67
+
68
+ /**
69
+ * Check if platform is registered
70
+ * @param {string} platform - Platform identifier
71
+ * @returns {boolean} True if platform is registered
72
+ */
73
+ isRegistered(platform) {
74
+ return this.adapters.has(platform);
75
+ }
76
+
77
+ /**
78
+ * Validate that adapter implements required interface
79
+ * @param {Object} adapter - Adapter to validate
80
+ */
81
+ validateAdapterInterface(platform, adapter) {
82
+ const requiredMethods = [
83
+ 'createCallLog',
84
+ 'updateCallLog',
85
+ ];
86
+
87
+ for (const method of requiredMethods) {
88
+ if (typeof adapter[method] !== 'function') {
89
+ throw new Error(`Adapter ${platform} missing required method: ${method}`);
90
+ }
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Unregister an adapter
96
+ * @param {string} platform - Platform identifier
97
+ */
98
+ unregisterAdapter(platform) {
99
+ this.adapters.delete(platform);
100
+ this.manifests.delete(platform);
101
+ console.log(`Unregistered adapter: ${platform}`);
102
+ }
103
+
104
+ setReleaseNotes(releaseNotes) {
105
+ this.releaseNotes = releaseNotes;
106
+ }
107
+
108
+ getReleaseNotes(platform) {
109
+ return this.releaseNotes;
110
+ }
111
+ }
112
+
113
+ // Export singleton instance
114
+ const adapterRegistry = new AdapterRegistry();
115
+ module.exports = adapterRegistry;
@@ -0,0 +1,60 @@
1
+ const axios = require('axios');
2
+ const { AdminConfigModel } = require('../models/adminConfigModel');
3
+ const adapterRegistry = require('../adapter/registry');
4
+
5
+ async function validateAdminRole({ rcAccessToken }) {
6
+ const rcExtensionResponse = await axios.get(
7
+ 'https://platform.ringcentral.com/restapi/v1.0/account/~/extension/~',
8
+ {
9
+ headers: {
10
+ Authorization: `Bearer ${rcAccessToken}`,
11
+ },
12
+ });
13
+ return {
14
+ isValidated: !!rcExtensionResponse.data?.permissions?.admin?.enabled || (!!process.env.ADMIN_EXTENSION_ID_DEV_PASS_LIST && process.env.ADMIN_EXTENSION_ID_DEV_PASS_LIST.split(',').includes(rcExtensionResponse.data.id.toString())),
15
+ rcAccountId: rcExtensionResponse.data.account.id
16
+ };
17
+ }
18
+
19
+ async function upsertAdminSettings({ hashedRcAccountId, adminSettings }) {
20
+ let existingAdminConfig = await AdminConfigModel.findByPk(hashedRcAccountId);
21
+ if (existingAdminConfig) {
22
+ await existingAdminConfig.update({
23
+ ...adminSettings
24
+ });
25
+ } else {
26
+ await AdminConfigModel.create({
27
+ id: hashedRcAccountId,
28
+ ...adminSettings
29
+ });
30
+ }
31
+ }
32
+
33
+ async function getAdminSettings({ hashedRcAccountId }) {
34
+ const existingAdminConfig = await AdminConfigModel.findByPk(hashedRcAccountId);
35
+ return existingAdminConfig;
36
+ }
37
+
38
+ async function getServerLoggingSettings({ user }) {
39
+ const platformModule = adapterRegistry.getAdapter(user.platform);
40
+ if (platformModule.getServerLoggingSettings) {
41
+ const serverLoggingSettings = await platformModule.getServerLoggingSettings({ user });
42
+ return serverLoggingSettings;
43
+ }
44
+ return {};
45
+ }
46
+
47
+ async function updateServerLoggingSettings({ user, additionalFieldValues }) {
48
+ const platformModule = adapterRegistry.getAdapter(user.platform);
49
+ if (platformModule.updateServerLoggingSettings) {
50
+ const serverLoggingSettings = await platformModule.updateServerLoggingSettings({ user, additionalFieldValues });
51
+ return serverLoggingSettings;
52
+ }
53
+ return {};
54
+ }
55
+
56
+ exports.validateAdminRole = validateAdminRole;
57
+ exports.upsertAdminSettings = upsertAdminSettings;
58
+ exports.getAdminSettings = getAdminSettings;
59
+ exports.getServerLoggingSettings = getServerLoggingSettings;
60
+ exports.updateServerLoggingSettings = updateServerLoggingSettings;
@@ -0,0 +1,156 @@
1
+ const oauth = require('../lib/oauth');
2
+ const { UserModel } = require('../models/userModel');
3
+ const adapterRegistry = require('../adapter/registry');
4
+ const Op = require('sequelize').Op;
5
+
6
+ async function onOAuthCallback({ platform, hostname, tokenUrl, callbackUri, apiUrl, username, query }) {
7
+ const platformModule = adapterRegistry.getAdapter(platform);
8
+ const oauthInfo = await platformModule.getOauthInfo({ tokenUrl, hostname, rcAccountId: query.rcAccountId });
9
+
10
+ if (oauthInfo.failMessage) {
11
+ return {
12
+ userInfo: null,
13
+ returnMessage: {
14
+ messageType: 'danger',
15
+ message: oauthInfo.failMessage
16
+ }
17
+ }
18
+ }
19
+
20
+ // Some platforms require different oauth queries, this won't affect normal OAuth process unless CRM module implements getOverridingOAuthOption() method
21
+ let overridingOAuthOption = null;
22
+ if (platformModule.getOverridingOAuthOption != null) {
23
+ overridingOAuthOption = platformModule.getOverridingOAuthOption({ code: callbackUri.split('code=')[1] });
24
+ }
25
+ const oauthApp = oauth.getOAuthApp(oauthInfo);
26
+ const { accessToken, refreshToken, expires } = await oauthApp.code.getToken(callbackUri, overridingOAuthOption);
27
+ const authHeader = `Bearer ${accessToken}`;
28
+ const { successful, platformUserInfo, returnMessage } = await platformModule.getUserInfo({ authHeader, tokenUrl, apiUrl, hostname, username, callbackUri, query });
29
+ if (successful) {
30
+ const userInfo = await saveUserInfo({
31
+ platformUserInfo,
32
+ platform,
33
+ tokenUrl,
34
+ apiUrl,
35
+ username,
36
+ hostname: platformUserInfo?.overridingHostname ? platformUserInfo.overridingHostname : hostname,
37
+ accessToken,
38
+ refreshToken,
39
+ tokenExpiry: expires
40
+ });
41
+ return {
42
+ userInfo,
43
+ returnMessage
44
+ };
45
+ }
46
+ else {
47
+ return {
48
+ userInfo: null,
49
+ returnMessage
50
+ }
51
+ }
52
+ }
53
+
54
+ async function onApiKeyLogin({ platform, hostname, apiKey, additionalInfo }) {
55
+ const platformModule = adapterRegistry.getAdapter(platform);
56
+ const basicAuth = platformModule.getBasicAuth({ apiKey });
57
+ const { successful, platformUserInfo, returnMessage } = await platformModule.getUserInfo({ authHeader: `Basic ${basicAuth}`, hostname, additionalInfo, apiKey });
58
+ if (successful) {
59
+ const userInfo = await saveUserInfo({
60
+ platformUserInfo,
61
+ platform,
62
+ hostname,
63
+ accessToken: platformUserInfo.overridingApiKey ?? apiKey
64
+ });
65
+ return {
66
+ userInfo,
67
+ returnMessage
68
+ };
69
+ }
70
+ else {
71
+ return {
72
+ userInfo: null,
73
+ returnMessage
74
+ }
75
+ }
76
+ }
77
+
78
+ async function saveUserInfo({ platformUserInfo, platform, hostname, accessToken, refreshToken, tokenExpiry }) {
79
+ const id = platformUserInfo.id;
80
+ const name = platformUserInfo.name;
81
+ const existingUser = await UserModel.findByPk(id);
82
+ const timezoneName = platformUserInfo.timezoneName;
83
+ const timezoneOffset = platformUserInfo.timezoneOffset;
84
+ const platformAdditionalInfo = platformUserInfo.platformAdditionalInfo;
85
+ if (existingUser) {
86
+ await existingUser.update(
87
+ {
88
+ hostname,
89
+ timezoneName,
90
+ timezoneOffset,
91
+ accessToken,
92
+ refreshToken,
93
+ tokenExpiry,
94
+ platformAdditionalInfo: {
95
+ ...existingUser.platformAdditionalInfo, // keep existing platformAdditionalInfo
96
+ ...platformAdditionalInfo,
97
+ }
98
+ }
99
+ );
100
+ }
101
+ else {
102
+ await UserModel.create({
103
+ id,
104
+ hostname,
105
+ timezoneName,
106
+ timezoneOffset,
107
+ platform,
108
+ accessToken,
109
+ refreshToken,
110
+ tokenExpiry,
111
+ platformAdditionalInfo,
112
+ userSettings: {}
113
+ });
114
+ }
115
+ return {
116
+ id,
117
+ name
118
+ };
119
+ }
120
+
121
+ // Just for oauth ATM
122
+ async function authValidation({ platform, userId }) {
123
+ let existingUser = await UserModel.findOne({
124
+ where: {
125
+ [Op.and]: [
126
+ {
127
+ id: userId,
128
+ platform
129
+ }
130
+ ]
131
+ }
132
+ });
133
+ if (existingUser) {
134
+ const platformModule = adapterRegistry.getAdapter(platform);
135
+ const oauthApp = oauth.getOAuthApp((await platformModule.getOauthInfo({ tokenUrl: existingUser?.platformAdditionalInfo?.tokenUrl, hostname: existingUser?.hostname })));
136
+ existingUser = await oauth.checkAndRefreshAccessToken(oauthApp, existingUser);
137
+ const { successful, returnMessage, status } = await platformModule.authValidation({ user: existingUser });
138
+ return {
139
+ successful,
140
+ returnMessage,
141
+ status,
142
+ failReason: successful ? '' : 'CRM. API failed'
143
+ }
144
+ }
145
+ else {
146
+ return {
147
+ successful: false,
148
+ status: 404,
149
+ failReason: 'App Connect. User not found in database'
150
+ }
151
+ }
152
+ }
153
+
154
+ exports.onOAuthCallback = onOAuthCallback;
155
+ exports.onApiKeyLogin = onApiKeyLogin;
156
+ exports.authValidation = authValidation;