neo-cmp-cli 1.7.3 → 1.7.5-beta.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.
@@ -0,0 +1,451 @@
1
+ const axios = require('axios');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const ora = require('ora');
5
+ const http = require('http');
6
+ const url = require('url');
7
+ const open = require('open');
8
+ const portfinder = require('portfinder');
9
+ const { errorLog, successLog } = require('../utils/common');
10
+ const neoAuthConfig = require('../config/auth.config');
11
+
12
+ /**
13
+ * Neo 登录授权服务类
14
+ * 实现 OAuth2 授权码模式的登录/登出功能
15
+ */
16
+ class NeoLoginService {
17
+ /**
18
+ * 初始化登录服务
19
+ * @param {object} config 授权配置信息
20
+ * @param {string} config.loginURL 登录授权 URL
21
+ * @param {string} config.tokenAPI Token 获取接口地址
22
+ * @param {string} config.response_type 认证类型 (code)
23
+ * @param {string} config.client_id 客户端 ID
24
+ * @param {string} config.client_secret 客户端秘钥
25
+ * @param {string} config.scope 授权范围 (all)
26
+ * @param {string} config.oauthType OAuth 类型 (standard)
27
+ * @param {string} config.access_type 访问类型 (offline/online)
28
+ * @param {string} config.grant_type 授权类型 (authorization_code)
29
+ */
30
+ constructor(config = {}) {
31
+ const { loginURL, tokenAPI } = config;
32
+
33
+ // 验证必需的配置项
34
+ if (!loginURL || !tokenAPI) {
35
+ throw new Error('auth.config.js 配置不完整,需要包含 loginURL、tokenAPI');
36
+ }
37
+
38
+ this.loginURL = loginURL;
39
+ this.tokenAPI = tokenAPI;
40
+ this.response_type = neoAuthConfig.response_type || 'code';
41
+ this.client_id = neoAuthConfig.client_id;
42
+ this.client_secret = neoAuthConfig.client_secret;
43
+ this.scope = neoAuthConfig.scope || 'all';
44
+ this.oauthType = neoAuthConfig.oauthType || 'standard';
45
+ this.access_type = neoAuthConfig.access_type || 'offline';
46
+ this.grant_type = neoAuthConfig.grant_type || 'authorization_code';
47
+
48
+ // Token 存储路径(在当前项目根目录的 .neo-cli 文件夹下)
49
+ this.tokenDir = path.join(process.cwd(), '.neo-cli');
50
+ this.tokenFile = path.join(this.tokenDir, 'token.json');
51
+ }
52
+
53
+ /**
54
+ * 确保 token 存储目录存在
55
+ */
56
+ ensureTokenDir() {
57
+ if (!fs.existsSync(this.tokenDir)) {
58
+ fs.mkdirSync(this.tokenDir, { recursive: true });
59
+ }
60
+ }
61
+
62
+ /**
63
+ * 保存 token 到本地文件
64
+ * @param {object} tokenData Token 数据
65
+ */
66
+ saveToken(tokenData) {
67
+ this.ensureTokenDir();
68
+ const tokenInfo = {
69
+ ...tokenData,
70
+ savedAt: Date.now(),
71
+ expiresAt: Date.now() + (tokenData.expires_in || 7200) * 1000
72
+ };
73
+ fs.writeFileSync(this.tokenFile, JSON.stringify(tokenInfo, null, 2), 'utf-8');
74
+ }
75
+
76
+ /**
77
+ * 读取本地保存的 token
78
+ * @returns {object|null} Token 数据,如果不存在或已过期则返回 null
79
+ */
80
+ readToken() {
81
+ if (!fs.existsSync(this.tokenFile)) {
82
+ return null;
83
+ }
84
+
85
+ try {
86
+ const tokenData = JSON.parse(fs.readFileSync(this.tokenFile, 'utf-8'));
87
+ return tokenData;
88
+ } catch (error) {
89
+ errorLog(`读取 token 文件失败: ${error.message}`);
90
+ return null;
91
+ }
92
+ }
93
+
94
+ /**
95
+ * 检查 token 是否过期
96
+ * @param {object} tokenData Token 数据
97
+ * @returns {boolean} true 表示已过期,false 表示未过期
98
+ */
99
+ isTokenExpired(tokenData) {
100
+ if (!tokenData || !tokenData.expiresAt) {
101
+ return true;
102
+ }
103
+ // 提前 5 分钟判断为过期,避免边缘情况
104
+ return Date.now() >= tokenData.expiresAt - 5 * 60 * 1000;
105
+ }
106
+
107
+ /**
108
+ * 清除本地保存的 token
109
+ */
110
+ clearToken() {
111
+ if (fs.existsSync(this.tokenFile)) {
112
+ fs.unlinkSync(this.tokenFile);
113
+ }
114
+ }
115
+
116
+ /**
117
+ * 生成回调地址
118
+ * @param {number} port 端口号
119
+ * @returns {string} 回调地址
120
+ */
121
+ getRedirectURI(port) {
122
+ return `http://localhost:${port}/callback`;
123
+ }
124
+
125
+ /**
126
+ * 构建授权 URL
127
+ * @param {string} redirectUri 回调地址
128
+ * @returns {string} 授权 URL
129
+ */
130
+ buildAuthUrl(redirectUri) {
131
+ const params = new URLSearchParams({
132
+ response_type: this.response_type,
133
+ client_id: this.client_id,
134
+ redirect_uri: redirectUri,
135
+ scope: this.scope,
136
+ oauthType: this.oauthType,
137
+ access_type: this.access_type
138
+ });
139
+
140
+ return `${this.loginURL}?${params.toString()}`;
141
+ }
142
+
143
+ /**
144
+ * 打开浏览器访问授权 URL
145
+ * @param {string} authUrl 授权 URL
146
+ */
147
+ async openBrowser(authUrl) {
148
+ try {
149
+ await open(authUrl);
150
+ } catch (error) {
151
+ errorLog(`无法自动打开浏览器: ${error.message}`);
152
+ console.log(`\n请手动访问以下 URL 进行授权:\n${authUrl}\n`);
153
+ }
154
+ }
155
+
156
+ /**
157
+ * 启动本地服务器接收授权码(code)
158
+ * @returns {Promise<{redirectUri: string, codePromise: Promise<string>}>} 回调地址和授权码Promise
159
+ */
160
+ async startCallbackServer() {
161
+ // 使用 portfinder 获取可用端口
162
+ const port = await portfinder.getPortPromise({
163
+ port: 8888, // 起始端口
164
+ stopPort: 9999 // 结束端口
165
+ });
166
+
167
+ // 使用获取到的端口生成 redirect_uri
168
+ const redirectUri = this.getRedirectURI(port);
169
+ const redirectUrl = new URL(redirectUri);
170
+
171
+ const codePromise = new Promise((resolve, reject) => {
172
+ const server = http.createServer((req, res) => {
173
+ const parsedUrl = url.parse(req.url, true);
174
+
175
+ if (parsedUrl.pathname === redirectUrl.pathname || parsedUrl.pathname === '/') {
176
+ const code = parsedUrl.query.code;
177
+ const error = parsedUrl.query.error;
178
+
179
+ if (error) {
180
+ res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
181
+ res.end(`
182
+ <html>
183
+ <head><title>授权失败</title></head>
184
+ <body>
185
+ <h1>授权失败</h1>
186
+ <p>错误信息: ${error}</p>
187
+ <p>您可以关闭此窗口</p>
188
+ </body>
189
+ </html>
190
+ `);
191
+ server.close();
192
+ reject(new Error(`授权失败: ${error}`));
193
+ return;
194
+ }
195
+
196
+ if (code) {
197
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
198
+ res.end(`
199
+ <html>
200
+ <head><title>授权成功</title></head>
201
+ <body>
202
+ <h1>授权成功!</h1>
203
+ <p>已获取授权码,正在获取 token...</p>
204
+ <p>您可以关闭此窗口</p>
205
+ <script>window.close();</script>
206
+ </body>
207
+ </html>
208
+ `);
209
+ server.close();
210
+ resolve(code);
211
+ return;
212
+ }
213
+
214
+ res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
215
+ res.end(`
216
+ <html>
217
+ <head><title>授权失败</title></head>
218
+ <body>
219
+ <h1>授权失败</h1>
220
+ <p>未获取到授权码</p>
221
+ <p>您可以关闭此窗口</p>
222
+ </body>
223
+ </html>
224
+ `);
225
+ server.close();
226
+ reject(new Error('未获取到授权码'));
227
+ } else {
228
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
229
+ res.end('Not Found');
230
+ }
231
+ });
232
+
233
+ server.on('error', (error) => {
234
+ if (error.code === 'EADDRINUSE') {
235
+ reject(new Error(`端口 ${port} 已被占用,无法启动回调服务器`));
236
+ } else {
237
+ reject(error);
238
+ }
239
+ });
240
+
241
+ server.listen(port, () => {
242
+ console.log(`\n本地回调服务器已启动,监听端口: ${port}`);
243
+ console.log(`回调地址: ${redirectUri}`);
244
+ });
245
+
246
+ // 设置超时(5 分钟)
247
+ setTimeout(() => {
248
+ server.close();
249
+ reject(new Error('授权超时,请重试'));
250
+ }, 5 * 60 * 1000);
251
+ });
252
+
253
+ return { redirectUri, codePromise };
254
+ }
255
+
256
+ /**
257
+ * 使用授权码获取 token
258
+ * @param {string} code 授权码
259
+ * @param {string} redirectUri 回调地址
260
+ * @returns {Promise<object>} Token 数据
261
+ */
262
+ async getTokenByCode(code, redirectUri) {
263
+ const spinner = ora('正在获取 access token...').start();
264
+
265
+ try {
266
+ const formData = new URLSearchParams();
267
+ formData.append('grant_type', this.grant_type);
268
+ formData.append('client_id', this.client_id);
269
+ formData.append('client_secret', this.client_secret);
270
+ formData.append('code', code);
271
+ formData.append('redirect_uri', redirectUri);
272
+
273
+ const response = await axios.post(this.tokenAPI, formData.toString(), {
274
+ headers: {
275
+ 'Content-Type': 'application/x-www-form-urlencoded'
276
+ }
277
+ });
278
+
279
+ const tokenData = response.data;
280
+
281
+ if (!tokenData || !tokenData.access_token) {
282
+ errorLog('获取 token 失败:响应中未包含 access_token', spinner);
283
+ errorLog(`响应数据: ${JSON.stringify(tokenData)}`);
284
+ process.exit(1);
285
+ }
286
+
287
+ successLog('成功获取 access token', spinner);
288
+ return tokenData;
289
+ } catch (error) {
290
+ errorLog('获取 token 失败', spinner);
291
+ errorLog(`\n获取 token 失败: ${error.message}`);
292
+ if (error.response) {
293
+ errorLog(`响应数据: ${JSON.stringify(error.response.data)}`);
294
+ }
295
+ process.exit(1);
296
+ }
297
+ }
298
+
299
+ /**
300
+ * 使用 refresh_token 刷新 token
301
+ * @param {string} refreshToken Refresh Token
302
+ * @returns {Promise<object>} 新的 Token 数据
303
+ */
304
+ async refreshToken(refreshToken) {
305
+ const spinner = ora('正在刷新 token...').start();
306
+
307
+ try {
308
+ const formData = new URLSearchParams();
309
+ formData.append('grant_type', 'refresh_token');
310
+ formData.append('client_id', this.client_id);
311
+ formData.append('client_secret', this.client_secret);
312
+ formData.append('refresh_token', refreshToken);
313
+
314
+ const response = await axios.post(this.tokenAPI, formData.toString(), {
315
+ headers: {
316
+ 'Content-Type': 'application/x-www-form-urlencoded'
317
+ }
318
+ });
319
+
320
+ const tokenData = response.data;
321
+
322
+ if (!tokenData || !tokenData.access_token) {
323
+ errorLog('刷新 token 失败:响应中未包含 access_token', spinner);
324
+ errorLog(`响应数据: ${JSON.stringify(tokenData)}`);
325
+ return null;
326
+ }
327
+
328
+ successLog('Token 刷新成功', spinner);
329
+ return tokenData;
330
+ } catch (error) {
331
+ errorLog('刷新 token 失败', spinner);
332
+ errorLog(`\n刷新 token 失败: ${error.message}`);
333
+ if (error.response) {
334
+ errorLog(`响应数据: ${JSON.stringify(error.response.data)}`);
335
+ }
336
+ return null;
337
+ }
338
+ }
339
+
340
+ /**
341
+ * 执行登录流程
342
+ */
343
+ async login() {
344
+ console.log('\n========== NeoCRM 登录授权 ==========\n');
345
+
346
+ try {
347
+ // 1. 启动本地回调服务器(自动获取可用端口)
348
+ const { redirectUri, codePromise } = await this.startCallbackServer();
349
+
350
+ // 2. 构建授权 URL
351
+ const authUrl = this.buildAuthUrl(redirectUri);
352
+ console.log('授权 URL:', authUrl);
353
+
354
+ // 3. 打开浏览器进行授权
355
+ console.log('\n正在打开浏览器进行授权...');
356
+ await this.openBrowser(authUrl);
357
+
358
+ // 4. 等待获取授权码
359
+ const code = await codePromise;
360
+ console.log('\n✓ 已获取授权码');
361
+
362
+ // 5. 使用授权码获取 token
363
+ const tokenData = await this.getTokenByCode(code, redirectUri);
364
+
365
+ // 6. 保存 token 到本地
366
+ this.saveToken(tokenData);
367
+
368
+ console.log('\n========== 登录成功 ==========\n');
369
+ console.log(`Token 已保存到: ${this.tokenFile}`);
370
+ console.log(`实例地址: ${tokenData.instance_uri || '未返回'}`);
371
+ console.log(`租户 ID: ${tokenData.tenant_id || '未返回'}`);
372
+ console.log(`Token 有效期: ${tokenData.expires_in || 7200} 秒`);
373
+ console.log(`Refresh Token 有效期: ${tokenData.refresh_token_expires_in || 2592000} 秒`);
374
+ console.log('');
375
+
376
+ return tokenData;
377
+ } catch (error) {
378
+ errorLog(`\n登录失败: ${error.message}`);
379
+ process.exit(1);
380
+ }
381
+ }
382
+
383
+ /**
384
+ * 执行登出流程
385
+ */
386
+ async logout() {
387
+ console.log('\n========== NeoCRM 登出 ==========\n');
388
+
389
+ if (!fs.existsSync(this.tokenFile)) {
390
+ console.log('当前未登录,无需登出。');
391
+ return;
392
+ }
393
+
394
+ try {
395
+ this.clearToken();
396
+ successLog(`已清除 token 文件: ${this.tokenFile}`);
397
+ console.log('\n登出成功!\n');
398
+ } catch (error) {
399
+ errorLog(`登出失败: ${error.message}`);
400
+ process.exit(1);
401
+ }
402
+ }
403
+
404
+ /**
405
+ * 获取有效的 token(自动刷新)
406
+ * @returns {Promise<object>} Token 数据
407
+ */
408
+ async getValidToken() {
409
+ const tokenData = this.readToken();
410
+
411
+ if (!tokenData) {
412
+ errorLog('未找到本地 token,请先执行 neo login 进行登录');
413
+ process.exit(1);
414
+ }
415
+
416
+ // 检查 token 是否过期
417
+ if (this.isTokenExpired(tokenData)) {
418
+ console.log('Token 已过期,正在尝试刷新...');
419
+
420
+ if (!tokenData.refresh_token) {
421
+ errorLog('Refresh token 不存在,请重新登录');
422
+ process.exit(1);
423
+ }
424
+
425
+ // 使用 refresh_token 刷新
426
+ const newTokenData = await this.refreshToken(tokenData.refresh_token);
427
+
428
+ if (!newTokenData) {
429
+ errorLog('Token 刷新失败,请重新登录 (neo login)');
430
+ process.exit(1);
431
+ }
432
+
433
+ // 保存新的 token
434
+ this.saveToken(newTokenData);
435
+ return newTokenData;
436
+ }
437
+
438
+ return tokenData;
439
+ }
440
+
441
+ /**
442
+ * 获取 access token 字符串
443
+ * @returns {Promise<string>} Access Token
444
+ */
445
+ async getAccessToken() {
446
+ const tokenData = await this.getValidToken();
447
+ return tokenData.access_token;
448
+ }
449
+ }
450
+
451
+ module.exports = NeoLoginService;
@@ -5,12 +5,14 @@ const path = require('path');
5
5
  const ora = require('ora');
6
6
  const _ = require('lodash');
7
7
  const { resolve } = require('akfun');
8
+ const NeoLoginService = require('./neoLogin');
8
9
  const updatePublishLog = require('../utils/projectUtils/updatePublishLog');
9
10
  const { getFramework, errorLog, successLog } = require('../utils/common');
10
11
 
11
12
  // NeoCRM 平台默认 API 配置
12
13
  const NeoCrmAPI = {
13
14
  neoBaseURL: 'https://crm.xiaoshouyi.com', // 平台根地址
15
+ loginAPI: 'https://login-cd.xiaoshouyi.com/auc/oauth2/auth', // code 获取接口地址
14
16
  tokenAPI: 'https://login.xiaoshouyi.com/auc/oauth2/token', // Token 获取接口地址
15
17
  uploadAPI: '/rest/metadata/v3.0/ui/customComponents/actions/upload', // 文件上传接口地址
16
18
  delete: '/rest/metadata/v3.0/ui/customComponents',
@@ -48,28 +50,47 @@ class NeoService {
48
50
  /**
49
51
  * 初始化 Neo 服务
50
52
  * @param {object} config 配置信息
53
+ * @param {string} config.authType 授权类型,可选值:oauth2(默认)、password
51
54
  * @param {string} config.neoBaseURL Neo 平台根地址
52
55
  * @param {string} config.tokenAPI Token 获取接口地址
56
+ * @param {string} config.loginURL OAuth2 登录授权 URL(OAuth2 模式需要)
53
57
  * @param {object} config.auth 授权信息
54
58
  * @param {string} config.auth.client_id 客户端 ID
55
59
  * @param {string} config.auth.client_secret 客户端密钥
56
- * @param {string} config.auth.username 用户名
57
- * @param {string} config.auth.password 密码
60
+ * @param {string} config.auth.username 用户名(password 模式需要)
61
+ * @param {string} config.auth.password 密码(password 模式需要)
58
62
  */
59
63
  constructor(config = {}) {
60
- const { assetsRoot, neoBaseURL, tokenAPI, auth } = config || {};
64
+ const { assetsRoot, neoBaseURL, tokenAPI, loginURL, auth, authType } = config || {};
65
+
66
+ // 设置授权类型,默认为 oauth2
67
+ this.authType = authType || 'oauth2';
68
+
61
69
  if (!auth) {
62
70
  throw new Error('auth 不能为空');
63
71
  }
64
- if (!auth.client_id || !auth.client_secret || !auth.username || !auth.password) {
65
- throw new Error(
66
- 'neoConfig / auth 配置不完整,需要包含 client_id、client_secret、username、password'
67
- );
72
+
73
+ // 根据授权类型验证必需的配置项
74
+ if (this.authType === 'password') {
75
+ if (!auth.client_id || !auth.client_secret || !auth.username || !auth.password) {
76
+ throw new Error(
77
+ 'neo.config.js / neoConfig / auth 配置不完整(password 模式),需要包含 client_id、client_secret、username、password'
78
+ );
79
+ }
80
+ } else if (this.authType === 'oauth2') {
81
+ if (!loginURL || !tokenAPI) {
82
+ throw new Error(
83
+ 'neo.config.js / neoConfig 配置不完整(oauth2 模式),需要包含 loginURL、tokenAPI 配置。'
84
+ );
85
+ }
86
+ } else {
87
+ throw new Error(`不支持的授权类型: ${this.authType},可选值:oauth2、password`);
68
88
  }
69
89
 
70
90
  this.assetsRoot = assetsRoot || resolve('dist');
71
91
  this.neoBaseURL = neoBaseURL || NeoCrmAPI.neoBaseURL;
72
92
  this.tokenAPI = tokenAPI || NeoCrmAPI.tokenAPI;
93
+ this.loginURL = loginURL || NeoCrmAPI.loginAPI;
73
94
  this.auth = auth;
74
95
  this.cmpList = [];
75
96
  this.cmpInfoMap = {};
@@ -117,10 +138,30 @@ class NeoService {
117
138
 
118
139
  /**
119
140
  * 获取 token(含授权信息、租户信息)
141
+ * 根据 authType 自动选择授权模式:oauth2 或 password
120
142
  * @returns {Promise<string>} token
121
143
  */
122
144
  async getToken() {
123
- const spinner = ora('获取 token...').start();
145
+ // 检查缓存是否有效
146
+ if (!this.isTokenExpired()) {
147
+ return this.tokenCache.token;
148
+ }
149
+
150
+ // 根据授权类型选择对应的获取方式
151
+ if (this.authType === 'oauth2') {
152
+ return await this.getTokenByOAuth2();
153
+ } else {
154
+ return await this.getTokenByPassword();
155
+ }
156
+ }
157
+
158
+ /**
159
+ * 使用密码模式获取 token
160
+ * @returns {Promise<string>} token
161
+ */
162
+ async getTokenByPassword() {
163
+ const spinner = ora('获取 token(密码模式)...').start();
164
+
124
165
  // 检查缓存是否有效
125
166
  if (!this.isTokenExpired()) {
126
167
  successLog('使用缓存的 token。', spinner);
@@ -176,6 +217,43 @@ class NeoService {
176
217
  }
177
218
  }
178
219
 
220
+ /**
221
+ * 获取 OAuth2 授权码模式的 token
222
+ * @returns {Promise<string>} token
223
+ */
224
+ async getTokenByOAuth2() {
225
+ const spinner = ora('获取 token(OAuth2 模式)...').start();
226
+
227
+ try {
228
+ // 构建 OAuth2 配置
229
+ const oauth2Config = {
230
+ loginURL: this.loginURL,
231
+ tokenAPI: this.tokenAPI
232
+ };
233
+
234
+ const loginService = new NeoLoginService(oauth2Config);
235
+ const accessToken = await loginService.getAccessToken();
236
+
237
+ // 缓存 token(OAuth2 模式下的 token 有效期由 NeoLoginService 管理)
238
+ // 这里我们设置一个较长的过期时间,实际过期检查由 NeoLoginService 内部处理
239
+ this.tokenCache = {
240
+ token: accessToken,
241
+ expiresAt: Date.now() + 7200 * 1000 // 默认 2 小时,实际由 NeoLoginService 管理
242
+ };
243
+
244
+ spinner.clear();
245
+ spinner.stop();
246
+ return accessToken;
247
+ } catch (error) {
248
+ errorLog('获取 token 失败(OAuth2 模式)', spinner);
249
+ errorLog(`\n获取 token 失败: ${error.message}`);
250
+ if (error.response) {
251
+ errorLog(`响应数据: ${JSON.stringify(error.response.data)}`);
252
+ }
253
+ process.exit(1);
254
+ }
255
+ }
256
+
179
257
  /**
180
258
  * 刷新 token
181
259
  * @returns {Promise<string>} 新的 token
@@ -103,22 +103,31 @@ module.exports = {
103
103
  }
104
104
  */
105
105
  },
106
- // NeoCRM 平台配置
106
+ // 授权码授权模式下的 NeoCRM 平台配置
107
107
  neoConfig: {
108
+ authType: 'oauth2', // 授权类型,可选值:oauth2(默认)、password
108
109
  neoBaseURL: 'https://crm-cd.xiaoshouyi.com', // 平台根地址(默认:https://crm.xiaoshouyi.com)
110
+ // 当 authType 为 oauth2 时,loginURL 配置项必填
111
+ loginURL: 'https://login-cd.xiaoshouyi.com/auc/oauth2/auth', // 登录授权 URL(默认:https://login.xiaoshouyi.com/auc/oauth2/auth)
109
112
  tokenAPI: 'https://login-cd.xiaoshouyi.com/auc/oauth2/token', // Token 获取接口地址(默认:https://login.xiaoshouyi.com/auc/oauth2/token)
110
- // NeoCRM 授权配置
113
+ },
114
+ /*
115
+ // 密码授权模式下的 NeoCRM 平台配置
116
+ neoConfig: {
117
+ authType: 'password', // 授权类型,可选值:oauth2(默认)、password
118
+ neoBaseURL: 'https://crm-cd.xiaoshouyi.com', // 平台根地址(默认:https://crm.xiaoshouyi.com)
119
+ tokenAPI: 'https://login-cd.xiaoshouyi.com/auc/oauth2/token', // Token 获取接口地址(默认:https://login.xiaoshouyi.com/auc/oauth2/token)
120
+ // 当 authType 为 password 时,auth 配置项必填
111
121
  auth: {
112
- client_id: 'xx', // 客户端 ID,从创建连接器的客户端信息中获取(Client_Id)
113
- client_secret: 'xxx', // 客户端秘钥,从创建连接器的客户端信息中获取(Client_Secret)
114
- username: 'xx', // 用户在销售易系统中的用户名
115
- /**
116
- * password 用户在销售易系统中的账号密码加上 8 位安全令牌。
117
- * 例如,用户密码为 123456,安全令牌为 ABCDEFGH,则 password 的值应为 123456ABCDEFGH。
118
- */
119
- password: 'xx xx', // 用户账户密码 + 8 位安全令牌
122
+ client_id: auth.client_id || 'xx', // 客户端 ID,从创建连接器的客户端信息中获取(Client_Id)
123
+ client_secret: auth.client_secret || 'xxx', // 客户端秘钥,从创建连接器的客户端信息中获取(Client_Secret)
124
+ username: auth.username || 'xx', // 用户在销售易系统中的用户名
125
+ // password 为 用户在销售易系统中的账号密码加上 8 位安全令牌。
126
+ // 例如,用户密码为 123456,安全令牌为 ABCDEFGH,则 password 的值应为 123456ABCDEFGH。
127
+ password: auth.password || 'xx xx', // 用户账户密码 + 8 位安全令牌
120
128
  },
121
129
  },
130
+ */
122
131
  pushCmp: {
123
132
  // 用于构建并发布至 NeoCRM 的相关配置
124
133
  /*
@@ -48,7 +48,7 @@
48
48
  "@commitlint/config-conventional": "^9.1.1",
49
49
  "@types/react": "^16.9.11",
50
50
  "@types/react-dom": "^16.9.15",
51
- "neo-cmp-cli": "^1.7.3",
51
+ "neo-cmp-cli": "^1.7.5",
52
52
  "husky": "^4.2.5",
53
53
  "lint-staged": "^10.2.9",
54
54
  "prettier": "^2.0.5"