@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 +266 -0
- package/adapter/mock.js +77 -0
- package/adapter/registry.js +115 -0
- package/handlers/admin.js +60 -0
- package/handlers/auth.js +156 -0
- package/handlers/contact.js +275 -0
- package/handlers/disposition.js +194 -0
- package/handlers/log.js +586 -0
- package/handlers/user.js +102 -0
- package/index.js +1202 -0
- package/lib/analytics.js +53 -0
- package/lib/callLogComposer.js +452 -0
- package/lib/encode.js +30 -0
- package/lib/generalErrorMessage.js +42 -0
- package/lib/jwt.js +16 -0
- package/lib/oauth.js +85 -0
- package/lib/util.js +40 -0
- package/models/adminConfigModel.js +17 -0
- package/models/cacheModel.js +23 -0
- package/models/callLogModel.js +27 -0
- package/models/dynamo/lockSchema.js +25 -0
- package/models/messageLogModel.js +25 -0
- package/models/sequelize.js +17 -0
- package/models/userModel.js +38 -0
- package/package.json +58 -0
- package/releaseNotes.json +578 -0
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
|
+
```
|
package/adapter/mock.js
ADDED
|
@@ -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;
|
package/handlers/auth.js
ADDED
|
@@ -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;
|