neo-cmp-cli 1.8.7 → 1.8.9
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/bin/index.js +2 -2
- package/dist/_virtual/_commonjsHelpers.js +12 -0
- package/dist/_virtual/array-set.js +7 -0
- package/dist/_virtual/base64-vlq.js +7 -0
- package/dist/_virtual/base64.js +7 -0
- package/dist/_virtual/binary-search.js +7 -0
- package/dist/_virtual/mapping-list.js +7 -0
- package/dist/_virtual/quick-sort.js +7 -0
- package/dist/_virtual/source-map-consumer.js +7 -0
- package/dist/_virtual/source-map-generator.js +7 -0
- package/dist/_virtual/source-map-support.js +7 -0
- package/dist/_virtual/source-map.js +7 -0
- package/dist/_virtual/source-node.js +7 -0
- package/dist/_virtual/typescript.js +7 -0
- package/dist/_virtual/util.js +7 -0
- package/dist/config/auth.config.js +43 -0
- package/dist/config/default.config.js +192 -0
- package/dist/config/index.js +26 -0
- package/dist/main.js +9 -0
- package/dist/main2.js +259 -0
- package/dist/module/inspect.js +63 -0
- package/dist/module/neoInit.js +75 -0
- package/dist/module/neoInitByCopy.js +83 -0
- package/dist/neo/neoLogin.js +590 -0
- package/dist/neo/neoRequire.js +123 -0
- package/dist/neo/neoService.js +898 -0
- package/dist/node_modules/buffer-from/index.js +86 -0
- package/dist/node_modules/source-map/lib/array-set.js +138 -0
- package/dist/node_modules/source-map/lib/base64-vlq.js +157 -0
- package/dist/node_modules/source-map/lib/base64.js +83 -0
- package/dist/node_modules/source-map/lib/binary-search.js +129 -0
- package/dist/node_modules/source-map/lib/mapping-list.js +96 -0
- package/dist/node_modules/source-map/lib/quick-sort.js +130 -0
- package/dist/node_modules/source-map/lib/source-map-consumer.js +1166 -0
- package/dist/node_modules/source-map/lib/source-map-generator.js +445 -0
- package/dist/node_modules/source-map/lib/source-node.js +431 -0
- package/dist/node_modules/source-map/lib/util.js +506 -0
- package/dist/node_modules/source-map/source-map.js +27 -0
- package/dist/node_modules/source-map-support/source-map-support.js +646 -0
- package/dist/node_modules/typescript/lib/typescript.js +174130 -0
- package/dist/oss/publish2oss.js +331 -0
- package/dist/plugins/AddNeoRequirePlugin.js +195 -0
- package/dist/utils/autoEntryRootDir.js +103 -0
- package/dist/utils/cmpUtils/createCmpByTemplate.js +82 -0
- package/dist/utils/cmpUtils/createCmpByZip.js +433 -0
- package/dist/utils/cmpUtils/createCommonModulesCode.js +139 -0
- package/dist/utils/cmpUtils/deleteCmp.js +81 -0
- package/dist/utils/cmpUtils/getCmpModelRegisterCode.js +47 -0
- package/dist/utils/cmpUtils/getCmpPreviewCode.js +60 -0
- package/dist/utils/cmpUtils/getCmpRegisterCode.js +47 -0
- package/dist/utils/cmpUtils/getCmpTypeByDir.js +59 -0
- package/dist/utils/cmpUtils/hasCmpTypeByDir.js +27 -0
- package/dist/utils/cmpUtils/previewCmp.js +75 -0
- package/dist/utils/cmpUtils/pullCmp.js +126 -0
- package/dist/utils/cmpUtils/pushCmp.js +254 -0
- package/dist/utils/common.js +125 -0
- package/dist/utils/configureNeoBuild.js +129 -0
- package/dist/utils/generateEntries.js +80 -0
- package/dist/utils/neoConfigInit.js +30 -0
- package/dist/utils/neoParams.js +26 -0
- package/dist/utils/pathUtils.js +40 -0
- package/dist/utils/projectNameValidator.js +90 -0
- package/dist/utils/projectUtils/createCmpProjectByTemplate.js +81 -0
- package/dist/utils/projectUtils/createCmpProjectZip.js +141 -0
- package/dist/utils/projectUtils/getEntries.js +99 -0
- package/dist/utils/projectUtils/getEntriesWithAutoRegister.js +129 -0
- package/dist/utils/projectUtils/hasNeoProject.js +34 -0
- package/dist/utils/projectUtils/openProject.js +117 -0
- package/dist/utils/projectUtils/updatePublishLog.js +47 -0
- package/dist/utils/replaceInFilesByMap.js +71 -0
- package/dist/utils/replaceInPackage.js +151 -0
- package/dist/utils/resetPackageVersion.js +132 -0
- package/package.json +6 -8
- package/test/demo.js +0 -2
- package/test/deprecate-versions.js +1 -1
- package/src/config/auth.config.js +0 -27
- package/src/config/default.config.js +0 -176
- package/src/config/index.js +0 -9
- package/src/main.js +0 -221
- package/src/module/inspect.js +0 -41
- package/src/module/neoInit.js +0 -55
- package/src/module/neoInitByCopy.js +0 -61
- package/src/neo/NeoUMDContent.js +0 -30
- package/src/neo/neoLogin.js +0 -565
- package/src/neo/neoRequire.js +0 -125
- package/src/neo/neoService.js +0 -874
- package/src/neo/webpack.mf.js +0 -60
- package/src/neo/wrapperContent.js +0 -16
- package/src/oss/publish2oss.js +0 -348
- package/src/plugins/AddNeoRequirePlugin-v1.js +0 -47
- package/src/plugins/AddNeoRequirePlugin.js +0 -179
- package/src/plugins/README.md +0 -109
- package/src/utils/autoEntryRootDir.js +0 -85
- package/src/utils/cmpUtils/createCmpByTemplate.js +0 -60
- package/src/utils/cmpUtils/createCmpByZip.js +0 -408
- package/src/utils/cmpUtils/createCommonModulesCode.js +0 -121
- package/src/utils/cmpUtils/deleteCmp.js +0 -63
- package/src/utils/cmpUtils/getCmpModelRegisterCode.js +0 -31
- package/src/utils/cmpUtils/getCmpPreviewCode.js +0 -43
- package/src/utils/cmpUtils/getCmpRegisterCode.js +0 -31
- package/src/utils/cmpUtils/getCmpTypeByDir.js +0 -41
- package/src/utils/cmpUtils/hasCmpTypeByDir.js +0 -11
- package/src/utils/cmpUtils/previewCmp.js +0 -55
- package/src/utils/cmpUtils/pullCmp.js +0 -104
- package/src/utils/cmpUtils/pushCmp.js +0 -230
- package/src/utils/common.js +0 -107
- package/src/utils/configureNeoBuild.js +0 -109
- package/src/utils/generateEntries.js +0 -63
- package/src/utils/neoConfigInit.js +0 -13
- package/src/utils/neoParams.js +0 -12
- package/src/utils/pathUtils.js +0 -23
- package/src/utils/projectNameValidator.js +0 -76
- package/src/utils/projectUtils/createCmpProjectByTemplate.js +0 -59
- package/src/utils/projectUtils/createCmpProjectZip.js +0 -120
- package/src/utils/projectUtils/getEntries.js +0 -80
- package/src/utils/projectUtils/getEntriesWithAutoRegister.js +0 -108
- package/src/utils/projectUtils/hasNeoProject.js +0 -17
- package/src/utils/projectUtils/openProject.js +0 -96
- package/src/utils/projectUtils/updatePublishLog.js +0 -30
- package/src/utils/replaceInFiles.js +0 -47
- package/src/utils/replaceInFilesByMap.js +0 -54
- package/src/utils/replaceInPackage.js +0 -134
- package/src/utils/resetPackageVersion.js +0 -115
- /package/{src → template}/initData/defaultTemplate.html +0 -0
- /package/{src → template}/initData/neo.config.js +0 -0
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
4
|
+
|
|
5
|
+
const require$$0$1 = require('axios');
|
|
6
|
+
const require$$0 = require('node:fs');
|
|
7
|
+
const require$$1 = require('node:path');
|
|
8
|
+
const require$$3 = require('ora');
|
|
9
|
+
const require$$4 = require('node:http');
|
|
10
|
+
const require$$5 = require('node:url');
|
|
11
|
+
const require$$2 = require('open');
|
|
12
|
+
const require$$7 = require('node:net');
|
|
13
|
+
require('portfinder');
|
|
14
|
+
const common = require('../utils/common.js');
|
|
15
|
+
const auth_config = require('../config/auth.config.js');
|
|
16
|
+
|
|
17
|
+
var neoLogin;
|
|
18
|
+
var hasRequiredNeoLogin;
|
|
19
|
+
|
|
20
|
+
function requireNeoLogin () {
|
|
21
|
+
if (hasRequiredNeoLogin) return neoLogin;
|
|
22
|
+
hasRequiredNeoLogin = 1;
|
|
23
|
+
const axios = require$$0$1;
|
|
24
|
+
const fs = require$$0;
|
|
25
|
+
const path = require$$1;
|
|
26
|
+
const ora = require$$3;
|
|
27
|
+
const http = require$$4;
|
|
28
|
+
const url = require$$5;
|
|
29
|
+
const open = require$$2;
|
|
30
|
+
const net = require$$7;
|
|
31
|
+
const { errorLog, successLog } = common.__require();
|
|
32
|
+
const neoAuthConfig = auth_config.__require();
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Neo 登录授权服务类
|
|
36
|
+
* 实现 OAuth2 授权码模式的登录/登出功能
|
|
37
|
+
*/
|
|
38
|
+
class NeoLoginService {
|
|
39
|
+
/**
|
|
40
|
+
* 初始化登录服务
|
|
41
|
+
* @param {object} config 授权配置信息
|
|
42
|
+
* @param {string} config.loginURL 登录授权 URL
|
|
43
|
+
* @param {string} config.tokenAPI Token 获取接口地址
|
|
44
|
+
* @param {string} config.response_type 认证类型 (code)
|
|
45
|
+
* @param {string} config.client_id 客户端 ID
|
|
46
|
+
* @param {string} config.client_secret 客户端秘钥
|
|
47
|
+
* @param {string} config.scope 授权范围 (all)
|
|
48
|
+
* @param {string} config.oauthType OAuth 类型 (standard)
|
|
49
|
+
* @param {string} config.access_type 访问类型 (offline/online)
|
|
50
|
+
* @param {string} config.grant_type 授权类型 (authorization_code)
|
|
51
|
+
*/
|
|
52
|
+
constructor(config = {}) {
|
|
53
|
+
const { loginURL, tokenAPI } = config;
|
|
54
|
+
|
|
55
|
+
// 验证必需的配置项
|
|
56
|
+
if (!loginURL || !tokenAPI) {
|
|
57
|
+
throw new Error('auth.config.js 配置不完整,需要包含 loginURL、tokenAPI');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.loginURL = loginURL;
|
|
61
|
+
this.tokenAPI = tokenAPI;
|
|
62
|
+
this.response_type = neoAuthConfig.response_type || 'code';
|
|
63
|
+
this.client_id = neoAuthConfig.client_id;
|
|
64
|
+
this.client_secret = neoAuthConfig.client_secret;
|
|
65
|
+
this.scope = neoAuthConfig.scope || 'all';
|
|
66
|
+
this.oauthType = neoAuthConfig.oauthType || 'standard';
|
|
67
|
+
this.access_type = neoAuthConfig.access_type || 'offline';
|
|
68
|
+
this.grant_type = neoAuthConfig.grant_type || 'authorization_code';
|
|
69
|
+
|
|
70
|
+
// Token 存储路径(在当前项目根目录的 .neo-cli 文件夹下)
|
|
71
|
+
this.tokenDir = path.join(process.cwd(), '.neo-cli');
|
|
72
|
+
this.tokenFile = path.join(this.tokenDir, 'token.json');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 确保 token 存储目录存在
|
|
77
|
+
*/
|
|
78
|
+
ensureTokenDir() {
|
|
79
|
+
if (!fs.existsSync(this.tokenDir)) {
|
|
80
|
+
fs.mkdirSync(this.tokenDir, { recursive: true });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 保存 token 到本地文件
|
|
86
|
+
* @param {object} tokenData Token 数据
|
|
87
|
+
*/
|
|
88
|
+
saveToken(tokenData) {
|
|
89
|
+
this.ensureTokenDir();
|
|
90
|
+
const tokenInfo = {
|
|
91
|
+
...tokenData,
|
|
92
|
+
savedAt: Date.now(),
|
|
93
|
+
expiresAt: Date.now() + (tokenData.expires_in || 7200) * 1000
|
|
94
|
+
};
|
|
95
|
+
fs.writeFileSync(this.tokenFile, JSON.stringify(tokenInfo, null, 2), 'utf-8');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 读取本地保存的 token
|
|
100
|
+
* @returns {object|null} Token 数据,如果不存在或已过期则返回 null
|
|
101
|
+
*/
|
|
102
|
+
readToken() {
|
|
103
|
+
if (!fs.existsSync(this.tokenFile)) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
const tokenData = JSON.parse(fs.readFileSync(this.tokenFile, 'utf-8'));
|
|
109
|
+
return tokenData;
|
|
110
|
+
} catch (error) {
|
|
111
|
+
errorLog(`读取 token 文件失败: ${error.message}`);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 检查 token 是否过期
|
|
118
|
+
* @param {object} tokenData Token 数据
|
|
119
|
+
* @returns {boolean} true 表示已过期,false 表示未过期
|
|
120
|
+
*/
|
|
121
|
+
isTokenExpired(tokenData) {
|
|
122
|
+
if (!tokenData || !tokenData.expiresAt) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
// 提前 5 分钟判断为过期,避免边缘情况
|
|
126
|
+
return Date.now() >= tokenData.expiresAt - 5 * 60 * 1000;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 清除本地保存的 token
|
|
131
|
+
*/
|
|
132
|
+
clearToken() {
|
|
133
|
+
if (fs.existsSync(this.tokenFile)) {
|
|
134
|
+
fs.unlinkSync(this.tokenFile);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* 生成回调地址
|
|
140
|
+
* @param {number} port 端口号
|
|
141
|
+
* @returns {string} 回调地址
|
|
142
|
+
*/
|
|
143
|
+
getRedirectURI(port) {
|
|
144
|
+
return `http://localhost:${port}`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* 构建授权 URL
|
|
149
|
+
* @param {string} redirectUri 回调地址
|
|
150
|
+
* @returns {string} 授权 URL
|
|
151
|
+
*/
|
|
152
|
+
buildAuthUrl(redirectUri) {
|
|
153
|
+
const params = new URLSearchParams({
|
|
154
|
+
response_type: this.response_type,
|
|
155
|
+
client_id: this.client_id,
|
|
156
|
+
redirect_uri: redirectUri,
|
|
157
|
+
scope: this.scope,
|
|
158
|
+
oauthType: this.oauthType,
|
|
159
|
+
access_type: this.access_type
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
return `${this.loginURL}?${params.toString()}`;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* 打开浏览器访问授权 URL
|
|
167
|
+
* @param {string} authUrl 授权 URL
|
|
168
|
+
*/
|
|
169
|
+
async openBrowser(authUrl) {
|
|
170
|
+
try {
|
|
171
|
+
await open(authUrl);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
errorLog(`无法自动打开浏览器: ${error.message}`);
|
|
174
|
+
console.log(`\n请手动访问以下 URL 进行授权:\n${authUrl}\n`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 检测端口是否被占用
|
|
180
|
+
* @param {number} port 端口号
|
|
181
|
+
* @returns {Promise<boolean>} true 表示端口被占用,false 表示端口可用
|
|
182
|
+
*/
|
|
183
|
+
async isPortInUse(port) {
|
|
184
|
+
return new Promise((resolve) => {
|
|
185
|
+
const server = net.createServer();
|
|
186
|
+
|
|
187
|
+
server.once('error', (err) => {
|
|
188
|
+
if (err.code === 'EADDRINUSE') {
|
|
189
|
+
resolve(true); // 端口被占用
|
|
190
|
+
} else {
|
|
191
|
+
resolve(false); // 其他错误,假设端口可用
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
server.once('listening', () => {
|
|
196
|
+
// 端口可用,立即关闭服务器
|
|
197
|
+
server.once('close', () => {
|
|
198
|
+
resolve(false); // 端口可用
|
|
199
|
+
});
|
|
200
|
+
server.close();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
server.listen(port);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* 启动本地服务器接收授权码(code)
|
|
209
|
+
* @returns {Promise<{redirectUri: string, codePromise: Promise<string>}>} 回调地址和授权码Promise
|
|
210
|
+
*/
|
|
211
|
+
async startCallbackServer() {
|
|
212
|
+
let redirectUri = neoAuthConfig.redirectUri;
|
|
213
|
+
let redirectUrl = new URL(redirectUri);
|
|
214
|
+
let port = parseInt(redirectUrl.port, 10);
|
|
215
|
+
|
|
216
|
+
// 检测端口是否被占用
|
|
217
|
+
const portInUse = await this.isPortInUse(port);
|
|
218
|
+
|
|
219
|
+
if (portInUse) {
|
|
220
|
+
consoleError(
|
|
221
|
+
`\n警告: 端口 ${port} 已被占用,请调整 redirectUri 配置项,使其指向一个未被占用的端口。`
|
|
222
|
+
);
|
|
223
|
+
process.exit(1);
|
|
224
|
+
/*
|
|
225
|
+
// 使用 portfinder 查找可用端口
|
|
226
|
+
try {
|
|
227
|
+
port = await portfinder.getPortPromise({
|
|
228
|
+
port: port, // 从配置的端口开始查找
|
|
229
|
+
stopPort: 9999 // 结束端口
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// 更新 redirectUri 和 redirectUrl
|
|
233
|
+
redirectUri = this.getRedirectURI(port);
|
|
234
|
+
redirectUrl = new URL(redirectUri);
|
|
235
|
+
console.log(`已找到可用端口: ${port}`);
|
|
236
|
+
console.log(`回调地址已更新为: ${redirectUri}`);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
errorLog(`无法找到可用端口: ${error.message}`);
|
|
239
|
+
throw new Error(`端口 ${port} 已被占用,且无法找到可用端口`);
|
|
240
|
+
}
|
|
241
|
+
*/
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const codePromise = new Promise((resolve, reject) => {
|
|
245
|
+
const server = http.createServer((req, res) => {
|
|
246
|
+
const parsedUrl = url.parse(req.url, true);
|
|
247
|
+
|
|
248
|
+
if (parsedUrl.pathname === redirectUrl.pathname || parsedUrl.pathname === '/') {
|
|
249
|
+
const code = parsedUrl.query.code;
|
|
250
|
+
const error = parsedUrl.query.error;
|
|
251
|
+
|
|
252
|
+
if (error) {
|
|
253
|
+
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
254
|
+
res.end(`
|
|
255
|
+
<html>
|
|
256
|
+
<head>
|
|
257
|
+
<title>授权失败</title>
|
|
258
|
+
<style>
|
|
259
|
+
.container {
|
|
260
|
+
margin: 0 auto;
|
|
261
|
+
padding-top: 30px;
|
|
262
|
+
text-align: center;
|
|
263
|
+
|
|
264
|
+
.error-title {
|
|
265
|
+
color: red;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
h1, p {
|
|
269
|
+
text-align: center;
|
|
270
|
+
margin-top: 30px;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
</style>
|
|
274
|
+
</head>
|
|
275
|
+
<body>
|
|
276
|
+
<div class="container">
|
|
277
|
+
<h1 class="error-title">授权失败</h1>
|
|
278
|
+
<img src="https://custom-widgets.bj.bcebos.com/neocrmlogin.png" alt="授权失败" />
|
|
279
|
+
<p>错误信息: ${error}</p>
|
|
280
|
+
</div>
|
|
281
|
+
</body>
|
|
282
|
+
</html>
|
|
283
|
+
`);
|
|
284
|
+
server.close();
|
|
285
|
+
reject(new Error(`授权失败: ${error}`));
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (code) {
|
|
290
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
291
|
+
res.end(`
|
|
292
|
+
<html>
|
|
293
|
+
<head>
|
|
294
|
+
<title>授权成功</title>
|
|
295
|
+
<style>
|
|
296
|
+
.container {
|
|
297
|
+
margin: 0 auto;
|
|
298
|
+
padding-top: 30px;
|
|
299
|
+
text-align: center;
|
|
300
|
+
|
|
301
|
+
.success-title {
|
|
302
|
+
color: green;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
h1, p {
|
|
306
|
+
text-align: center;
|
|
307
|
+
margin-top: 30px;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
</style>
|
|
311
|
+
</head>
|
|
312
|
+
<body>
|
|
313
|
+
<div class="container">
|
|
314
|
+
<h1 class="success-title">授权成功!</h1>
|
|
315
|
+
<img src="https://custom-widgets.bj.bcebos.com/neocrmlogin.png" alt="授权成功" />
|
|
316
|
+
<p>您已成功授权开发账户,请关闭此页面。</p>
|
|
317
|
+
</div>
|
|
318
|
+
</body>
|
|
319
|
+
</html>
|
|
320
|
+
`);
|
|
321
|
+
server.close();
|
|
322
|
+
resolve(code);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
327
|
+
res.end(`
|
|
328
|
+
<html>
|
|
329
|
+
<head>
|
|
330
|
+
<title>授权失败</title>
|
|
331
|
+
<style>
|
|
332
|
+
.container {
|
|
333
|
+
margin: 0 auto;
|
|
334
|
+
padding-top: 30px;
|
|
335
|
+
text-align: center;
|
|
336
|
+
|
|
337
|
+
.error-title {
|
|
338
|
+
color: red;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
h1, p {
|
|
342
|
+
text-align: center;
|
|
343
|
+
margin-top: 30px;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
</style>
|
|
347
|
+
</head>
|
|
348
|
+
<body>
|
|
349
|
+
<div class="container">
|
|
350
|
+
<h1 class="error-title">授权失败</h1>
|
|
351
|
+
<img src="https://custom-widgets.bj.bcebos.com/neocrmlogin.png" alt="授权失败" />
|
|
352
|
+
<p>未获取到授权码。</p>
|
|
353
|
+
</div>
|
|
354
|
+
</body>
|
|
355
|
+
</html>
|
|
356
|
+
`);
|
|
357
|
+
server.close();
|
|
358
|
+
reject(new Error('未获取到授权码'));
|
|
359
|
+
} else {
|
|
360
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
361
|
+
res.end('Not Found');
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
server.on('error', (error) => {
|
|
366
|
+
if (error.code === 'EADDRINUSE') {
|
|
367
|
+
reject(new Error(`端口 ${port} 已被占用,无法启动回调服务器`));
|
|
368
|
+
} else {
|
|
369
|
+
reject(error);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
server.listen(port, () => {
|
|
374
|
+
console.log(`\n本地回调服务器已启动,监听端口: ${port}`);
|
|
375
|
+
console.log(`回调地址: ${redirectUri}`);
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
// 设置超时(5 分钟)
|
|
379
|
+
setTimeout(() => {
|
|
380
|
+
server.close();
|
|
381
|
+
reject(new Error('授权超时,请重试'));
|
|
382
|
+
}, 5 * 60 * 1000);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
return { redirectUri, codePromise };
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* 使用授权码获取 token
|
|
390
|
+
* @param {string} code 授权码
|
|
391
|
+
* @param {string} redirectUri 回调地址
|
|
392
|
+
* @returns {Promise<object>} Token 数据
|
|
393
|
+
*/
|
|
394
|
+
async getTokenByCode(code, redirectUri) {
|
|
395
|
+
const spinner = ora('正在获取 access token...').start();
|
|
396
|
+
|
|
397
|
+
try {
|
|
398
|
+
const formData = new URLSearchParams();
|
|
399
|
+
formData.append('grant_type', this.grant_type);
|
|
400
|
+
formData.append('client_id', this.client_id);
|
|
401
|
+
formData.append('client_secret', this.client_secret);
|
|
402
|
+
formData.append('code', code);
|
|
403
|
+
formData.append('redirect_uri', redirectUri);
|
|
404
|
+
|
|
405
|
+
const response = await axios.post(this.tokenAPI, formData.toString(), {
|
|
406
|
+
headers: {
|
|
407
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
const tokenData = response.data;
|
|
412
|
+
|
|
413
|
+
if (!tokenData || !tokenData.access_token) {
|
|
414
|
+
errorLog('获取 token 失败:响应中未包含 access_token', spinner);
|
|
415
|
+
errorLog(`响应数据: ${JSON.stringify(tokenData)}`);
|
|
416
|
+
process.exit(1);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
successLog('成功获取 access token', spinner);
|
|
420
|
+
return tokenData;
|
|
421
|
+
} catch (error) {
|
|
422
|
+
errorLog('获取 token 失败', spinner);
|
|
423
|
+
errorLog(`\n获取 token 失败: ${error.message}`);
|
|
424
|
+
if (error.response) {
|
|
425
|
+
errorLog(`响应数据: ${JSON.stringify(error.response.data)}`);
|
|
426
|
+
}
|
|
427
|
+
process.exit(1);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* 使用 refresh_token 刷新 token
|
|
433
|
+
* @param {string} refreshToken Refresh Token
|
|
434
|
+
* @returns {Promise<object>} 新的 Token 数据
|
|
435
|
+
*/
|
|
436
|
+
async refreshToken(refreshToken) {
|
|
437
|
+
const spinner = ora('正在刷新授权信息(token)...').start();
|
|
438
|
+
|
|
439
|
+
try {
|
|
440
|
+
const formData = new URLSearchParams();
|
|
441
|
+
formData.append('grant_type', 'refresh_token');
|
|
442
|
+
formData.append('client_id', this.client_id);
|
|
443
|
+
formData.append('client_secret', this.client_secret);
|
|
444
|
+
formData.append('refresh_token', refreshToken);
|
|
445
|
+
|
|
446
|
+
const response = await axios.post(this.tokenAPI, formData.toString(), {
|
|
447
|
+
headers: {
|
|
448
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
const tokenData = response.data;
|
|
453
|
+
|
|
454
|
+
if (!tokenData || !tokenData.access_token) {
|
|
455
|
+
errorLog('刷新授权信息失败:响应中未包含 access_token', spinner);
|
|
456
|
+
errorLog(`响应数据: ${JSON.stringify(tokenData)}`);
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
successLog('刷新授权信息成功(token)。', spinner);
|
|
461
|
+
return tokenData;
|
|
462
|
+
} catch (error) {
|
|
463
|
+
errorLog('刷新授权信息失败', spinner);
|
|
464
|
+
errorLog(`\n刷新授权信息失败: ${error.message}`);
|
|
465
|
+
if (error.response) {
|
|
466
|
+
errorLog(`响应数据: ${JSON.stringify(error.response.data)}`);
|
|
467
|
+
}
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* 执行登录流程
|
|
474
|
+
*/
|
|
475
|
+
async login() {
|
|
476
|
+
console.log('\n========== NeoCRM 登录授权 ==========\n');
|
|
477
|
+
|
|
478
|
+
try {
|
|
479
|
+
// 1. 启动本地回调服务器(自动获取可用端口)
|
|
480
|
+
const { redirectUri, codePromise } = await this.startCallbackServer();
|
|
481
|
+
|
|
482
|
+
// 2. 构建授权 URL
|
|
483
|
+
const authUrl = this.buildAuthUrl(redirectUri);
|
|
484
|
+
console.log('授权 URL:', authUrl);
|
|
485
|
+
|
|
486
|
+
// 3. 打开浏览器进行授权
|
|
487
|
+
console.log('\n正在打开浏览器进行授权...');
|
|
488
|
+
await this.openBrowser(authUrl);
|
|
489
|
+
|
|
490
|
+
// 4. 等待获取授权码
|
|
491
|
+
const code = await codePromise;
|
|
492
|
+
successLog('\n✓ 已获取授权码');
|
|
493
|
+
|
|
494
|
+
// 5. 使用授权码获取 token
|
|
495
|
+
const tokenData = await this.getTokenByCode(code, redirectUri);
|
|
496
|
+
|
|
497
|
+
// 6. 保存 token 到本地
|
|
498
|
+
this.saveToken(tokenData);
|
|
499
|
+
|
|
500
|
+
console.log('\n========== 登录成功 ==========\n');
|
|
501
|
+
console.log(`已缓存授权信息到: ${this.tokenFile}`);
|
|
502
|
+
console.log(`实例地址: ${tokenData.instance_uri || '未返回'}`);
|
|
503
|
+
console.log(`租户 ID: ${tokenData.tenant_id || '未返回'}`);
|
|
504
|
+
console.log(`授权信息有效期(access_token): ${tokenData.expires_in || 7200} 秒`);
|
|
505
|
+
console.log(
|
|
506
|
+
`自动刷新授权信息有效期(refresh_token): ${
|
|
507
|
+
tokenData.refresh_token_expires_in || 2592000
|
|
508
|
+
} 秒`
|
|
509
|
+
);
|
|
510
|
+
|
|
511
|
+
return tokenData;
|
|
512
|
+
} catch (error) {
|
|
513
|
+
errorLog(`\n登录失败: ${error.message}`);
|
|
514
|
+
process.exit(1);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* 执行登出流程
|
|
520
|
+
*/
|
|
521
|
+
async logout() {
|
|
522
|
+
console.log('\n========== NeoCRM 登出 ==========\n');
|
|
523
|
+
|
|
524
|
+
if (!fs.existsSync(this.tokenFile)) {
|
|
525
|
+
console.log('当前未登录,无需登出。');
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
try {
|
|
530
|
+
this.clearToken();
|
|
531
|
+
successLog(`已清除授权信息,下次登录需要重新授权。`);
|
|
532
|
+
console.log('\n登出成功!\n');
|
|
533
|
+
} catch (error) {
|
|
534
|
+
errorLog(`登出失败: ${error.message}`);
|
|
535
|
+
process.exit(1);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* 获取有效的 token(自动刷新)
|
|
541
|
+
* @returns {Promise<object>} Token 数据
|
|
542
|
+
*/
|
|
543
|
+
async getValidToken() {
|
|
544
|
+
const tokenData = this.readToken();
|
|
545
|
+
|
|
546
|
+
if (!tokenData) {
|
|
547
|
+
errorLog('未找到授权信息,请先执行 neo login 进行登录。');
|
|
548
|
+
process.exit(1);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// 检查 token 是否过期
|
|
552
|
+
if (this.isTokenExpired(tokenData)) {
|
|
553
|
+
console.log('授权信息已过期,正在尝试刷新...');
|
|
554
|
+
|
|
555
|
+
if (!tokenData.refresh_token) {
|
|
556
|
+
errorLog('自动刷新授权信息失败,请重新登录(neo login)。');
|
|
557
|
+
process.exit(1);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// 使用 refresh_token 刷新
|
|
561
|
+
const newTokenData = await this.refreshToken(tokenData.refresh_token);
|
|
562
|
+
|
|
563
|
+
if (!newTokenData) {
|
|
564
|
+
errorLog('刷新授权信息失败,请重新登录 (neo login)');
|
|
565
|
+
process.exit(1);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// 保存新的 token
|
|
569
|
+
this.saveToken(newTokenData);
|
|
570
|
+
return newTokenData;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return tokenData;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* 获取 access token 字符串
|
|
578
|
+
* @returns {Promise<string>} Access Token
|
|
579
|
+
*/
|
|
580
|
+
async getAccessToken() {
|
|
581
|
+
const tokenData = await this.getValidToken();
|
|
582
|
+
return tokenData.access_token;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
neoLogin = NeoLoginService;
|
|
587
|
+
return neoLogin;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
exports.__require = requireNeoLogin;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
4
|
+
|
|
5
|
+
const require$$2 = require('lodash');
|
|
6
|
+
|
|
7
|
+
var neoRequire_1;
|
|
8
|
+
var hasRequiredNeoRequire;
|
|
9
|
+
|
|
10
|
+
function requireNeoRequire () {
|
|
11
|
+
if (hasRequiredNeoRequire) return neoRequire_1;
|
|
12
|
+
hasRequiredNeoRequire = 1;
|
|
13
|
+
const _ = require$$2;
|
|
14
|
+
|
|
15
|
+
// Neo 共享出来的依赖模块
|
|
16
|
+
const NeoCommonModules = {
|
|
17
|
+
react: '^16.13.1',
|
|
18
|
+
'react-dom': '^16.13.1',
|
|
19
|
+
mobx: '^6.3.0',
|
|
20
|
+
'mobx-react': '^7.0.0',
|
|
21
|
+
'mobx-state-tree': '^5.4.0',
|
|
22
|
+
echarts: '5.4.2',
|
|
23
|
+
antd: '4.9.4',
|
|
24
|
+
'antd-mobile': '2.3.4',
|
|
25
|
+
'@ant-design/icons': '^4.8.0',
|
|
26
|
+
'video-react': '0.14.1',
|
|
27
|
+
axios: '^0.27.2',
|
|
28
|
+
classnames: '^2.3.2',
|
|
29
|
+
qs: '^6.11.0',
|
|
30
|
+
lodash: '^4.17.21',
|
|
31
|
+
'neo-ui-component-web': '^1.0.0',
|
|
32
|
+
'neo-ui-common': '^1.0.0',
|
|
33
|
+
'neo-open-api': '^1.0.11',
|
|
34
|
+
amis: '^1.1.5',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// 根据 Neo 共享出来的依赖模块,获取 externals 配置
|
|
38
|
+
const getExternalsByNeoCommonModules = (cmpNeoExternals) => {
|
|
39
|
+
const externals = {};
|
|
40
|
+
Object.keys(NeoCommonModules).forEach(moduleName => {
|
|
41
|
+
externals[moduleName] = `commonjs ${moduleName}`;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// 如果自定义组件有共享的依赖模块,则合并到 externals 中
|
|
45
|
+
if (cmpNeoExternals && cmpNeoExternals.length > 0) {
|
|
46
|
+
cmpNeoExternals.forEach(moduleName => {
|
|
47
|
+
externals[moduleName] = `commonjs ${moduleName}`;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return externals;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// 用于添加共享的依赖模块
|
|
55
|
+
const addNeoCommonModules = (modules) => {
|
|
56
|
+
if (!window.__NeoCommonModules) {
|
|
57
|
+
window.__NeoCommonModules = {};
|
|
58
|
+
}
|
|
59
|
+
if (isPlainObject(modules)) {
|
|
60
|
+
const moduleIds = Object.keys(modules);
|
|
61
|
+
moduleIds.forEach((moduleId) => {
|
|
62
|
+
const curModule = modules[moduleId];
|
|
63
|
+
const curCommonModule = window.__NeoCommonModules[moduleId];
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 如果 Neo 共享出来的依赖模块已经存在,则合并到 window.__NeoCommonModules 中
|
|
67
|
+
* 目的:解决依赖加载顺序问题,确保存在依赖的组件无论其加载顺序如何都能正常运行。
|
|
68
|
+
*
|
|
69
|
+
* 疑问1: 为什么 neoRequire 要返回一个 空的默认模块对象?
|
|
70
|
+
* 答:组件A 依赖 组件B 时,当 组件A 先加载(挂载对应的 asset 资源脚本)会报错,为了避免挂载时报错返回一个默认的空模块对象。
|
|
71
|
+
* 疑问2: 为什么不直接替换已有的模块,而是进行覆盖式合并(保持引用关联)?
|
|
72
|
+
* 答:为了避免依赖模块的引用关系被破坏,保持引用关联。确保动态注册进来的模块可以被提前挂载的组件所使用。
|
|
73
|
+
*/
|
|
74
|
+
if (curCommonModule && Object.keys(curCommonModule).length < 3) {
|
|
75
|
+
// 如果是默认模块对象,则覆盖式合并(保持引用关联)
|
|
76
|
+
window.__NeoCommonModules[moduleId] = Object.assign(window.__NeoCommonModules[moduleId], curModule);
|
|
77
|
+
|
|
78
|
+
// 处理模块中的特殊属性
|
|
79
|
+
if (curModule.__esModule !== undefined) {
|
|
80
|
+
window.__NeoCommonModules[moduleId].__esModule = curModule.__esModule;
|
|
81
|
+
}
|
|
82
|
+
if (curModule.default !== undefined) {
|
|
83
|
+
window.__NeoCommonModules[moduleId].default = curModule.default;
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
window.__NeoCommonModules[moduleId] = curModule;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// 用于添加自定义组件的远程依赖组件
|
|
93
|
+
const addNeoRemoteDeps = (remoteDeps) => {
|
|
94
|
+
if (!window.__NeoCommonModules) {
|
|
95
|
+
window.__NeoCommonModules = {};
|
|
96
|
+
}
|
|
97
|
+
if (!window.__NeoCommonModules.__neoRemoteDeps) {
|
|
98
|
+
window.__NeoCommonModules.__neoRemoteDeps = {};
|
|
99
|
+
}
|
|
100
|
+
if (_.isPlainObject(remoteDeps)) {
|
|
101
|
+
window.__NeoCommonModules.__neoRemoteDeps = Object.assign(window.__NeoCommonModules.__neoRemoteDeps, remoteDeps);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const initNeoRequire = () => {
|
|
106
|
+
if (!window.neoRequire) {
|
|
107
|
+
// 用于加载 Neo 共享出来的依赖模块
|
|
108
|
+
window.neoRequire = (moduleName) => {
|
|
109
|
+
return window.__NeoCommonModules[moduleName] || window[moduleName];
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
neoRequire_1 = {
|
|
115
|
+
initNeoRequire,
|
|
116
|
+
addNeoCommonModules,
|
|
117
|
+
addNeoRemoteDeps,
|
|
118
|
+
getExternalsByNeoCommonModules
|
|
119
|
+
};
|
|
120
|
+
return neoRequire_1;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
exports.__require = requireNeoRequire;
|