@opentapd/tplugin-cli 0.19.0
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.cloud.md +13 -0
- package/Readme.ex.md +13 -0
- package/Readme.oa.md +23 -0
- package/cli.js +109 -0
- package/config.json +12 -0
- package/index.js +5 -0
- package/lib/create.js +314 -0
- package/lib/deploy.js +278 -0
- package/lib/encrypt.js +59 -0
- package/lib/init.js +132 -0
- package/lib/install.js +59 -0
- package/lib/lint.js +55 -0
- package/lib/login.js +194 -0
- package/lib/logout.js +21 -0
- package/lib/repo.js +52 -0
- package/lib/resources.js +256 -0
- package/lib/serve.js +455 -0
- package/lib/workspace.js +57 -0
- package/package.json +79 -0
- package/scf-bin/node.js +50 -0
- package/scf-bin/python.py +59 -0
- package/util/axios.js +52 -0
- package/util/checkNodeVersion.js +21 -0
- package/util/checkPluginYaml.js +19 -0
- package/util/createDevWorkspace.js +62 -0
- package/util/enhanceErrorMessages.js +23 -0
- package/util/errorHandler.js +131 -0
- package/util/getPluginCode.js +38 -0
- package/util/selectWorkspace.js +70 -0
- package/util/session.js +119 -0
- package/util/spinner.js +14 -0
- package/util/tapd.js +14 -0
- package/util/tools.js +46 -0
- package/util/trimObjectValue.js +10 -0
package/util/axios.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2021 Tencent Inc. All rights reserved.
|
|
3
|
+
* Author: gobichen@tencent.com
|
|
4
|
+
*
|
|
5
|
+
* Axios interceptors
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const axios = require('axios');
|
|
9
|
+
const { Base64 } = require('js-base64');
|
|
10
|
+
const { tapd } = require('../config');
|
|
11
|
+
const Session = require('../util/session');
|
|
12
|
+
const session = new Session();
|
|
13
|
+
const { get } = require('lodash');
|
|
14
|
+
|
|
15
|
+
// 创建一个独立的axios实例
|
|
16
|
+
const instance = axios.create({
|
|
17
|
+
baseURL: `${tapd.apiHost}`,
|
|
18
|
+
headers: {
|
|
19
|
+
'Content-Type': 'application/json',
|
|
20
|
+
// 登录统一使用tapd-personal账号
|
|
21
|
+
Authorization: `Basic ${Base64.encode('tapd-personal:tapd-personal')}`,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
instance.interceptors.request.use((config) => {
|
|
26
|
+
// 排除登录url
|
|
27
|
+
if (
|
|
28
|
+
// pin+token登录接口
|
|
29
|
+
!config.url.includes('oa_user_access_token')
|
|
30
|
+
// token换取接口
|
|
31
|
+
&& !(config.url.includes('tokens/request_token'))
|
|
32
|
+
// 工蜂IDE登录token接口
|
|
33
|
+
&& !(config.url.includes('token/oa_user_access_token_by_tgit_token'))
|
|
34
|
+
) {
|
|
35
|
+
config.headers.Authorization = `Bearer ${session.token()}`;
|
|
36
|
+
}
|
|
37
|
+
config.errorContext = new Error('Thrown at:');
|
|
38
|
+
return config;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
instance.interceptors.response.use(undefined, async (error) => {
|
|
42
|
+
// 获取原始调用栈
|
|
43
|
+
const originalStackTrace = get(error, 'config.errorContext.stack');
|
|
44
|
+
if (originalStackTrace) {
|
|
45
|
+
// 调用栈补充进error
|
|
46
|
+
error.stack = `${error.stack}\n${originalStackTrace}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
throw error;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
module.exports = instance;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2021 Tencent Inc. All rights reserved.
|
|
3
|
+
* Author: gobichen@tencent.com
|
|
4
|
+
*
|
|
5
|
+
* Node Version Check
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const semver = require('semver');
|
|
9
|
+
const { engines } = require('../package.json');
|
|
10
|
+
const requiredNodeVersion = engines.node;
|
|
11
|
+
|
|
12
|
+
// 判断Node版本范围
|
|
13
|
+
const isSatisfiedVersion = semver.satisfies(
|
|
14
|
+
process.version,
|
|
15
|
+
requiredNodeVersion,
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
if (!isSatisfiedVersion) {
|
|
19
|
+
console.log(`This project require node version: ${requiredNodeVersion}`);
|
|
20
|
+
process.exit(-1);
|
|
21
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2021 Tencent Inc. All rights reserved.
|
|
3
|
+
* Author: gobichen@tencent.com
|
|
4
|
+
*
|
|
5
|
+
* plugin.yaml is exist
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
const fs = require('fs-extra');
|
|
10
|
+
|
|
11
|
+
module.exports = () => {
|
|
12
|
+
const yamlPath = `${process.cwd()}/plugin.yaml`;
|
|
13
|
+
|
|
14
|
+
// 判断 plugin.yaml 文件是否缺失
|
|
15
|
+
if (!fs.pathExistsSync(yamlPath)) {
|
|
16
|
+
console.log(chalk.red('当前目录未找到plugin.yaml文件'));
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2021 Tencent Inc. All rights reserved.
|
|
3
|
+
* Author: gobichen@tencent.com
|
|
4
|
+
*
|
|
5
|
+
* 创建开发项目
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const inquirer = require('inquirer');
|
|
9
|
+
const tapdsdk = require('../util/tapd');
|
|
10
|
+
const getPluginCode = require('./getPluginCode');
|
|
11
|
+
const spinner = require('../util/spinner');
|
|
12
|
+
const { get } = require('lodash');
|
|
13
|
+
const Session = require('../util/session');
|
|
14
|
+
|
|
15
|
+
module.exports = async () => {
|
|
16
|
+
// 判断当前用户是否已经创建了 开发项目
|
|
17
|
+
const { data } = await tapdsdk.request({ url: '/open_user_app/check_user_dev_workspace_exist', method: 'GET' });
|
|
18
|
+
const hasDevWorkspace = data.data;
|
|
19
|
+
const neverCreateDevKey = 'never-create-dev-workspace';
|
|
20
|
+
const alreadyCreateDevKey = 'already-create-dev-workspace';
|
|
21
|
+
|
|
22
|
+
const session = new Session();
|
|
23
|
+
const neverCreateDev = session.getConfig(neverCreateDevKey);
|
|
24
|
+
const alreadyCreateDev = session.getConfig(alreadyCreateDevKey);
|
|
25
|
+
|
|
26
|
+
// 未创建开发项目
|
|
27
|
+
if (!neverCreateDev && !alreadyCreateDev && !hasDevWorkspace) {
|
|
28
|
+
const choices = [
|
|
29
|
+
{ name: '马上创建', value: 'create' },
|
|
30
|
+
{ name: '稍后创建', value: 'cancel' },
|
|
31
|
+
{ name: '不再提醒', value: 'never' },
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
// 选择是否创建
|
|
35
|
+
const createStatus = await inquirer.prompt([{
|
|
36
|
+
type: 'list',
|
|
37
|
+
name: 'isCreate',
|
|
38
|
+
message: '是否创建一个TAPD项目进行开发',
|
|
39
|
+
choices,
|
|
40
|
+
}]);
|
|
41
|
+
|
|
42
|
+
if (createStatus.isCreate === 'create') {
|
|
43
|
+
spinner.start('创建中, 请稍等...');
|
|
44
|
+
const appCode = getPluginCode();
|
|
45
|
+
|
|
46
|
+
// 发起开发项目创建
|
|
47
|
+
const { data } = await tapdsdk.request({ url: '/open_user_app/create_user_dev_workspace', params: { code: appCode }, method: 'POST' });
|
|
48
|
+
spinner.succeed('创建开发项目成功');
|
|
49
|
+
const workspaceId = get(data, 'data.workspace_id') || false;
|
|
50
|
+
|
|
51
|
+
// 保存状态“已创建”到Session
|
|
52
|
+
session.setConfig(alreadyCreateDevKey, true);
|
|
53
|
+
return workspaceId;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (createStatus.isCreate === 'never') {
|
|
57
|
+
// 保存状态“不再提醒”到Session
|
|
58
|
+
session.setConfig(neverCreateDevKey, true);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2021 Tencent Inc. All rights reserved.
|
|
3
|
+
* Author: gobichen@tencent.com
|
|
4
|
+
*
|
|
5
|
+
* 错误信息处理
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
const program = require('commander');
|
|
10
|
+
const chalk = require('chalk');
|
|
11
|
+
|
|
12
|
+
module.exports = (methodName, log) => {
|
|
13
|
+
program.Command.prototype[methodName] = function (...args) {
|
|
14
|
+
if (methodName === 'unknownOption' && this._allowUnknownOption) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
this.outputHelp();
|
|
18
|
+
// 标红输出 errorMessage
|
|
19
|
+
console.log(` ${chalk.red(log(...args))}`);
|
|
20
|
+
console.log();
|
|
21
|
+
process.exit(1);
|
|
22
|
+
};
|
|
23
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2021 Tencent Inc. All rights reserved.
|
|
3
|
+
* Author: gobichen@tencent.com
|
|
4
|
+
*
|
|
5
|
+
* 错误处理
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
const { get } = require('lodash');
|
|
10
|
+
const fs = require('fs-extra');
|
|
11
|
+
const { resolve } = require('path');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
const { tryFunc } = require('./tools');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* error 解析入口
|
|
17
|
+
* parseError(error): 根据axios error类型,选择解析方式
|
|
18
|
+
*/
|
|
19
|
+
function parseError(error) {
|
|
20
|
+
return error.isAxiosError
|
|
21
|
+
? parseAxiosError(error)
|
|
22
|
+
: parseProcessError(error);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function isTokenError(error) {
|
|
26
|
+
const responseCode = get(error, 'response.status', 500);
|
|
27
|
+
const responseMessage = get(error, 'response.data.info', error.message);
|
|
28
|
+
|
|
29
|
+
// tapd 的特定逻辑
|
|
30
|
+
return responseCode === 422 && [
|
|
31
|
+
'The access token provided is invalid',
|
|
32
|
+
'Malformed token (missing "expires")',
|
|
33
|
+
'The access token provided has expired',
|
|
34
|
+
].includes(responseMessage);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* axios错误时调用
|
|
39
|
+
* @description parseAxiosError(error) 主要解析代理 TADP 接口的错误
|
|
40
|
+
* @param error object
|
|
41
|
+
* @return object
|
|
42
|
+
*/
|
|
43
|
+
function parseAxiosError(error) {
|
|
44
|
+
// 当代理不成功的时候推送 500
|
|
45
|
+
const responseCode = get(error, 'response.status', 500);
|
|
46
|
+
// TAPD 的错误原因会放在 data.info 中。当代理失败的时候直接用 error.message
|
|
47
|
+
const responseMessage = get(error, 'response.data.info', error.message);
|
|
48
|
+
const responseStatusText = get(error, 'response.statusText', 'Proxy Error');
|
|
49
|
+
const responseMeta = get(error, 'response.data.meta', {});
|
|
50
|
+
|
|
51
|
+
// 使用tryFunc 包装 JSON 解析方法
|
|
52
|
+
const stringifier = tryFunc(
|
|
53
|
+
error => JSON.stringify({
|
|
54
|
+
...error.toJSON(),
|
|
55
|
+
responseHeader: error?.response?.headers,
|
|
56
|
+
responseData: error?.response?.data,
|
|
57
|
+
}, null, 2),
|
|
58
|
+
error.stack,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
code: responseCode,
|
|
63
|
+
status: responseStatusText,
|
|
64
|
+
message: isTokenError(error) ? `Token 失效,请重新登陆(${responseMessage})` : responseMessage,
|
|
65
|
+
// 解析json,return返回错误
|
|
66
|
+
detail: stringifier(error),
|
|
67
|
+
meta: responseMeta,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 解析进程错误
|
|
73
|
+
*/
|
|
74
|
+
function parseProcessError(error) {
|
|
75
|
+
return {
|
|
76
|
+
message: error.message,
|
|
77
|
+
detail: error.stack,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 错误处理入口
|
|
83
|
+
* @description ErrorHandle(error) 负责解析并格式化输出错误
|
|
84
|
+
*/
|
|
85
|
+
function ErrorHandle(error, { exit = true } = {}) {
|
|
86
|
+
const { title = 'TPLUGIN ERROR' } = error;
|
|
87
|
+
|
|
88
|
+
// 解析error报错信息 和 状态
|
|
89
|
+
const { message, detail, code = 500, status = '', meta = {} } = parseError(error);
|
|
90
|
+
const homedir = os.homedir();
|
|
91
|
+
const TAPD_PATH = resolve(homedir, '.tapd', 'logs');
|
|
92
|
+
|
|
93
|
+
if (!fs.pathExistsSync(TAPD_PATH)) {
|
|
94
|
+
fs.mkdirsSync(TAPD_PATH);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const date = new Date();
|
|
98
|
+
const logName = [
|
|
99
|
+
date.getFullYear(),
|
|
100
|
+
date.getMonth(),
|
|
101
|
+
date.getDate(),
|
|
102
|
+
date.getHours(),
|
|
103
|
+
date.getMinutes(),
|
|
104
|
+
date.getSeconds(),
|
|
105
|
+
].join('-');
|
|
106
|
+
const logFile = resolve(TAPD_PATH, `${logName}.log`);
|
|
107
|
+
|
|
108
|
+
const requestIdMsg = meta.request_id ? `\nOr Ask Help to TAPDHelper With RequestId: ${meta.request_id}` : '';
|
|
109
|
+
// 格式化error
|
|
110
|
+
const log = `
|
|
111
|
+
${chalk.red(title)}:
|
|
112
|
+
code: ${code} ${status}
|
|
113
|
+
message: ${message}
|
|
114
|
+
|
|
115
|
+
A complete log of this run can be found in:
|
|
116
|
+
${logFile}
|
|
117
|
+
${requestIdMsg}`;
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
// error msg 写log文件
|
|
121
|
+
fs.writeFileSync(logFile, detail);
|
|
122
|
+
// 控制台输出 error msg
|
|
123
|
+
console.log(log);
|
|
124
|
+
} catch (e) {
|
|
125
|
+
console.log(chalk.red(detail));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
exit && process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
module.exports = ErrorHandle;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2021 Tencent Inc. All rights reserved.
|
|
3
|
+
* Author: terrysxu@tencent.com
|
|
4
|
+
*
|
|
5
|
+
* 获取当前插件Code
|
|
6
|
+
*/
|
|
7
|
+
const chalk = require('chalk');
|
|
8
|
+
const fs = require('fs-extra');
|
|
9
|
+
const yaml = require('js-yaml');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {boolean} failBreak 错误时是否中断 指令 true: 中断当前CLI,用于单独的lint判断 false: 不中断进程,用于中间步骤判断
|
|
13
|
+
* @returns {boolean|string}
|
|
14
|
+
*/
|
|
15
|
+
module.exports = (failBreak = true) => {
|
|
16
|
+
const yamlPath = `${process.cwd()}/plugin.yaml`;
|
|
17
|
+
let docObj = {};
|
|
18
|
+
try {
|
|
19
|
+
// 读取并解析 plugin.yaml 文件
|
|
20
|
+
docObj = yaml.load(fs.readFileSync(yamlPath, 'utf8'));
|
|
21
|
+
} catch (e) {
|
|
22
|
+
console.log(chalk.red(`plugin.yaml格式错误.Err:${e}`));
|
|
23
|
+
if (failBreak) {
|
|
24
|
+
throw e;
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!docObj.app.code) {
|
|
30
|
+
console.log(chalk.red('plugin.yaml 格式错误app code not found!'));
|
|
31
|
+
if (failBreak) {
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return docObj.app.code;
|
|
38
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2021 Tencent Inc. All rights reserved.
|
|
3
|
+
* Author: terrysxu@tencent.com
|
|
4
|
+
*
|
|
5
|
+
* 获取当前插件Code
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
const inquirer = require('inquirer');
|
|
10
|
+
const tapdsdk = require('../util/tapd');
|
|
11
|
+
const tpluginConf = require('../config');
|
|
12
|
+
const getPluginCode = require('./getPluginCode');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 用于选择或者创建自己管理的项目
|
|
16
|
+
* @param {boolean} failBreak 错误时是否中断 指令 true: 中断当前CLI,用于单独的lint判断 false: 不中断进程,用于其他CLI的中间步骤,只关注结果
|
|
17
|
+
* @param {string} defaultItem 默认选择的项目
|
|
18
|
+
* @return object 返回选择的项目信息
|
|
19
|
+
*/
|
|
20
|
+
module.exports = async (failBreak = true, defaultItem = false) => {
|
|
21
|
+
const appCode = getPluginCode();
|
|
22
|
+
// 获取管理的项目列表
|
|
23
|
+
const { data } = await tapdsdk.request({ url: '/open_user_app/workspace_list', params: { code: appCode }, method: 'GET' });
|
|
24
|
+
const workspaces = data.data;
|
|
25
|
+
if (workspaces && workspaces.length === 0) {
|
|
26
|
+
// 空提示
|
|
27
|
+
console.log(chalk.red(`没有任何项目的管理权限。\n1.申请项目权限\n2.创建自己的调试项目:${tpluginConf.tapd.tapdHost}/workspaces/user_register_choose_template`));
|
|
28
|
+
if (failBreak) {
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let workspace;
|
|
35
|
+
const choices = [];
|
|
36
|
+
for (let i = 0;i < workspaces.length;i++) {
|
|
37
|
+
workspace = workspaces[i].Workspace;
|
|
38
|
+
|
|
39
|
+
let itemInfo = `${workspace.id}:${workspace.name}`;
|
|
40
|
+
// 标记版本号到已经安装插件的项目
|
|
41
|
+
if (workspace.installed_type) {
|
|
42
|
+
if (workspace.installed_type === 2) {
|
|
43
|
+
itemInfo += chalk.red('\t[Debugging]');
|
|
44
|
+
} else {
|
|
45
|
+
itemInfo += chalk.red(`\t[已安装: ${workspace.app_version}]`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const item = {
|
|
50
|
+
name: itemInfo,
|
|
51
|
+
value: i,
|
|
52
|
+
};
|
|
53
|
+
if (defaultItem && defaultItem === workspace.id) {
|
|
54
|
+
choices.unshift(item);
|
|
55
|
+
} else {
|
|
56
|
+
choices.push(item);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log('');
|
|
61
|
+
// 选择项目
|
|
62
|
+
const selectWorkspaceResult = await inquirer.prompt([{
|
|
63
|
+
type: 'list',
|
|
64
|
+
name: 'workspace',
|
|
65
|
+
message: '请选择一个调试项目',
|
|
66
|
+
choices,
|
|
67
|
+
}]);
|
|
68
|
+
const selectWorkspaceIdx = selectWorkspaceResult.workspace;
|
|
69
|
+
return workspaces[selectWorkspaceIdx].Workspace;
|
|
70
|
+
};
|
package/util/session.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2021 Tencent Inc. All rights reserved.
|
|
3
|
+
* Author: raferzeng@tencent.com
|
|
4
|
+
*
|
|
5
|
+
* local session
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('fs-extra');
|
|
10
|
+
const homedir = require('os').homedir();
|
|
11
|
+
const chalk = require('chalk');
|
|
12
|
+
|
|
13
|
+
class Session {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.tapdPath = path.resolve(homedir, '.tapd');
|
|
16
|
+
if (!fs.pathExistsSync(this.tapdPath)) {
|
|
17
|
+
fs.mkdirsSync(this.tapdPath);
|
|
18
|
+
}
|
|
19
|
+
this.configFile = path.resolve(this.tapdPath, 'config');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param token 登录用户token
|
|
24
|
+
* @param userId 登录用户ID
|
|
25
|
+
*/
|
|
26
|
+
persist(token, userId, companyId = '') {
|
|
27
|
+
const authInfo = {
|
|
28
|
+
token,
|
|
29
|
+
userId,
|
|
30
|
+
timestamp: Date.now(),
|
|
31
|
+
companyId,
|
|
32
|
+
};
|
|
33
|
+
try {
|
|
34
|
+
fs.writeFileSync(this.configFile, JSON.stringify(authInfo));
|
|
35
|
+
return true;
|
|
36
|
+
} catch (e) {
|
|
37
|
+
console.log(chalk.red('登陆信息写入失败'));
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* setConfig(key, val): 添加配置信息到配置文件中
|
|
44
|
+
* @param key 键
|
|
45
|
+
* @param val 值
|
|
46
|
+
* @return boolean
|
|
47
|
+
*/
|
|
48
|
+
setConfig(key, val) {
|
|
49
|
+
try {
|
|
50
|
+
// 锁的需求场景几乎没有,暂时不引入
|
|
51
|
+
const info = JSON.parse(fs.readFileSync(this.configFile));
|
|
52
|
+
info[key] = val;
|
|
53
|
+
fs.writeFileSync(this.configFile, JSON.stringify(info));
|
|
54
|
+
return true;
|
|
55
|
+
} catch (e) {
|
|
56
|
+
console.log(chalk.red(`配置写入错误,[${key}] ,Err: ${e}`));
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* getConfig(key): 从配置文件中获取配置项 key
|
|
63
|
+
* @param key 键
|
|
64
|
+
*/
|
|
65
|
+
getConfig(key) {
|
|
66
|
+
try {
|
|
67
|
+
const info = JSON.parse(fs.readFileSync(this.configFile));
|
|
68
|
+
return key in info ? info[key] : false;
|
|
69
|
+
} catch (e) {
|
|
70
|
+
console.log(chalk.red(`配置写入错误,[${key}] ,Err: ${e}`));
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* clear(): 清除登录和配置信息
|
|
77
|
+
*/
|
|
78
|
+
clear() {
|
|
79
|
+
try {
|
|
80
|
+
fs.truncate(this.configFile);
|
|
81
|
+
return true;
|
|
82
|
+
} catch (e) {
|
|
83
|
+
console.log(chalk.red('登陆信息清除失败'));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* token(): 获取登录用户token, 过期时,返回null
|
|
89
|
+
* @return null|string
|
|
90
|
+
*/
|
|
91
|
+
token() {
|
|
92
|
+
if (!fs.pathExistsSync(this.configFile)) return null;
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
const now = Date.now();
|
|
96
|
+
const session = fs.readJsonSync(this.configFile);
|
|
97
|
+
// 有效期两周
|
|
98
|
+
if (now - session.timestamp > 1000 * 3600 * 24 * 14) {
|
|
99
|
+
this.clear();
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
return session.token.trim();
|
|
103
|
+
} catch (e) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* checkAuth(): 校验当前登录态是否过期
|
|
110
|
+
*/
|
|
111
|
+
checkAuth() {
|
|
112
|
+
if (!this.token()) {
|
|
113
|
+
console.log(chalk.red('未登陆或登陆态过期,请重新登录'));
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module.exports = Session;
|
package/util/spinner.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2021 Tencent Inc. All rights reserved.
|
|
3
|
+
* Author: raferzeng@tencent.com
|
|
4
|
+
*
|
|
5
|
+
* spinner config
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const ora = require('ora');
|
|
9
|
+
const spinner = ora();
|
|
10
|
+
|
|
11
|
+
// 统一添加 spinner 前缀
|
|
12
|
+
spinner.prefixText = '\n';
|
|
13
|
+
|
|
14
|
+
module.exports = spinner;
|
package/util/tapd.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2021 Tencent Inc. All rights reserved.
|
|
3
|
+
* Author: gobichen@tencent.com
|
|
4
|
+
*
|
|
5
|
+
* tapd sdk import
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const Sdk = require('@tencent/tapd-node-sdk');
|
|
9
|
+
const Session = require('./session');
|
|
10
|
+
const session = new Session();
|
|
11
|
+
const accessToken = session.token();
|
|
12
|
+
const tpluginConf = require('../config');
|
|
13
|
+
const tapdsdk = new Sdk({ address: tpluginConf.tapd.apiHost, accessToken });
|
|
14
|
+
module.exports = tapdsdk;
|
package/util/tools.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tryFunc() 调用并包装异常为正常返回值
|
|
3
|
+
* @param {CallableFunction} fn - 调用的函数
|
|
4
|
+
* @param {Object} defaultRes - 默认的异常返回值
|
|
5
|
+
*/
|
|
6
|
+
function tryFunc(fn, defaultRes) {
|
|
7
|
+
return function (...args) {
|
|
8
|
+
try {
|
|
9
|
+
return fn(...args);
|
|
10
|
+
} catch (e) {
|
|
11
|
+
return defaultRes;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const unzipper = require('unzipper');
|
|
18
|
+
const axios = require('axios');
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
const downloadAndUnzip = async (url) => {
|
|
22
|
+
const response = await axios.get(url, { responseType: 'stream' });
|
|
23
|
+
const writer = fs.createWriteStream('temp.zip');
|
|
24
|
+
response.data.pipe(writer);
|
|
25
|
+
|
|
26
|
+
await new Promise((resolve, reject) => {
|
|
27
|
+
writer.on('finish', resolve);
|
|
28
|
+
writer.on('error', reject);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const unzip = fs.createReadStream('temp.zip').pipe(unzipper.Extract({ path: '.' }));
|
|
32
|
+
|
|
33
|
+
await new Promise((resolve, reject) => {
|
|
34
|
+
unzip.on('close', resolve);
|
|
35
|
+
unzip.on('error', reject);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
fs.unlinkSync('temp.zip');
|
|
39
|
+
console.log('Download and unzip complete!');
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
module.exports = {
|
|
44
|
+
tryFunc,
|
|
45
|
+
downloadAndUnzip,
|
|
46
|
+
};
|