lightshortcuts 1.0.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.md ADDED
@@ -0,0 +1,59 @@
1
+ # Light快捷指令
2
+
3
+ 仅适用于Light管控台的快捷指令,用于快速处理离线包的一些处理,如发布等。请向相关平台管理员获取地址和token。
4
+
5
+ #### 使用说明
6
+
7
+ 1. 安装
8
+
9
+ ```bash
10
+ npm install -g lightshortcuts
11
+ ```
12
+
13
+ 2. 初始化发布配置
14
+
15
+ ```bash
16
+ lsc init
17
+ ```
18
+
19
+ 之后将在当前路径下创建发布配置文件lsc.config.json,请打开文件,根据需要做相应修改:
20
+
21
+ ```json
22
+ {
23
+ "publishInfo": {
24
+ "token": "XXXXX", //登录token
25
+ "pkgid": "XXXXX", //离线包ID
26
+ "set_pkg_version": "1.0.1", //指定发布版本
27
+ "publish_app_arr": [3577, 3592], //发布到指定APP,默认开发版
28
+ "apps_name": {
29
+ "3592": "开发版_iOS",
30
+ "3577": "开发版_安卓"
31
+ },
32
+ "android_version_scope": "7.0.6.0", //Android端离线包兼容版本
33
+ "ios_version_scope": "7.0.6", //iOS端离线包兼容版本
34
+ "pkg_zip_name": "dist.zip",//离线包压缩包文件名
35
+ "pkg_dir": "./dist/", //离线包相对所在路径
36
+ "release_desc": "功能更新", //发布日志
37
+ "task_status": "0" //发布当前版本后,对上一版本的处理:0:发布(不处理),1:暂停,2:结束(下架)
38
+ },
39
+ "lightBaseURL": "https://xxx.xxx.xxx/lightadmin/pas-api"//Light管控台地址
40
+ }
41
+ ```
42
+
43
+
44
+
45
+ 3. 离线包发布
46
+
47
+ - 单项目发布:
48
+
49
+ ```bash
50
+ lsc publish #或简写为 lsc p
51
+ ```
52
+
53
+ - 批量发布。配置项(除pkgid,pkg_zip_name外)与单项目发布一样,批量发布将以离线包名称作为ID,以`ID.zip`作为压缩包文件名,请务必确保ID的准确性:
54
+
55
+ ```bash
56
+ lsc batch #或简写为 lsc b
57
+ ```
58
+
59
+
package/bin/lsc.js ADDED
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { access, statSync, constants, writeFile, readdirSync } = require("fs");
4
+ const path = require("path");
5
+ const { Command } = require("commander");
6
+ const inquirer = require("inquirer");
7
+ const program = new Command();
8
+ const { LSC } = require("../index");
9
+ const { merge, getZipFileList } = require("../lib/utils");
10
+ const loadModSync = require("../lib/helpers/loadModSync");
11
+
12
+ // 读取发布配置文件
13
+ const defaultConfig = require("../lib/defaults");
14
+ const { checkSession } = require("../lib/core/API");
15
+ const { config } = require("process");
16
+ let confPath = path.join(process.cwd(), "./lsc.config.json");
17
+
18
+ program.version("0.0.1");
19
+
20
+ program.command("config").action(function () {
21
+ inquirer
22
+ .prompt([
23
+ {
24
+ type: "input",
25
+ message: "请输入token:",
26
+ name: "token",
27
+ },
28
+ {
29
+ type: "input",
30
+ message: "请输入离线包id:",
31
+ name: "h5pkgid",
32
+ },
33
+ {
34
+ type: "input",
35
+ message: "请输入离线包名称:",
36
+ name: "name",
37
+ },
38
+ {
39
+ type: "input",
40
+ message: "请指定发布版本号:",
41
+ name: "version",
42
+ default: "1.0.1",
43
+ },
44
+ {
45
+ type: "checkbox",
46
+ message: "请选择发布的APP:",
47
+ name: "apps",
48
+ choices: [
49
+ {
50
+ name: "开发版_iOS",
51
+ checked: true,
52
+ },
53
+ {
54
+ name: "开发版_安卓",
55
+ checked: true,
56
+ },
57
+ ],
58
+ validate(answer) {
59
+ if (answer.length < 1) {
60
+ return "请至少选择一项";
61
+ }
62
+ return true;
63
+ },
64
+ },
65
+ {
66
+ type: "input",
67
+ message: "离线包压缩包存放位置:",
68
+ name: "pkg_dir",
69
+ default: "./dist/",
70
+ },
71
+ {
72
+ type: "input",
73
+ message: "离线包名称:",
74
+ name: "pkg_zip_name",
75
+ default: "dist.zip",
76
+ },
77
+ {
78
+ type: "rawlist",
79
+ message: "是否处理上一版本离线包?",
80
+ name: "task_status",
81
+ default: "保留",
82
+ choices: ["保留", "暂停", "下架"],
83
+ },
84
+ ])
85
+ .then((answer) => {
86
+ // 用户输入的结果最终会在这里输出
87
+ console.log(answer);
88
+ });
89
+ });
90
+
91
+ program.command("init").action(function () {
92
+ let confPath = path.join(process.cwd(), "./lsc.config.json");
93
+ access(confPath, constants.F_OK, (err) => {
94
+ if (!err) {
95
+ // 存在配置文件,唤起交互输入
96
+ inquirer
97
+ .prompt([
98
+ {
99
+ type: "confirm",
100
+ message: "注意!!!配置文件已经存在,是否重新初始化?",
101
+ name: "initConfig",
102
+ default: false,
103
+ },
104
+ ])
105
+ .then((answer) => {
106
+ if (answer.initConfig) {
107
+ writeConfig();
108
+ } else {
109
+ console.log("配置无变化");
110
+ }
111
+ });
112
+ } else {
113
+ writeConfig();
114
+ }
115
+ var writeConfig = function () {
116
+ // 写入文件内容(如果文件不存在会创建一个文件)
117
+ writeFile(
118
+ "./lsc.config.json",
119
+ JSON.stringify(defaultConfig),
120
+ { flag: "w" },
121
+ function (err) {
122
+ if (err) {
123
+ throw err;
124
+ }
125
+ console.log("创建配置模板成功,请打开文件进行相应修改");
126
+ }
127
+ );
128
+ };
129
+ });
130
+ });
131
+
132
+ // 单项目发布
133
+ program
134
+ .command("publish")
135
+ .alias("p")
136
+ .description("发布离线包")
137
+ .action(() => {
138
+ access(confPath, constants.F_OK, (err) => {
139
+ if (!err) {
140
+ try {
141
+ let lscConfig = require(confPath);
142
+ let totalConfig = merge(defaultConfig, lscConfig);
143
+ LSC(totalConfig);
144
+ } catch (error) {
145
+ console.log("主程序异常", error);
146
+ }
147
+ } else {
148
+ // 不存在配置文件,唤起交互输入
149
+ console.log(
150
+ "不存在配置文件,请先进行配置或者使用如下命令创建:lsc init"
151
+ );
152
+ }
153
+ });
154
+ });
155
+
156
+ // 批量离线包发布.已经包名作为pkgid
157
+ program
158
+ .command("batch")
159
+ .alias("b")
160
+ .action(async function () {
161
+ let currPath = path.join(process.cwd(), "./dist/");
162
+ let zipLists = await getZipFileList(currPath);
163
+ for (let i = 0; i < zipLists.length; i++) {
164
+ const e = zipLists[i],
165
+ s = e.split("."),
166
+ pkgid = s[0];
167
+ let lscConfig = require(confPath);
168
+ let totalConfig = merge(defaultConfig, lscConfig);
169
+ let batchConfig = merge(totalConfig, {
170
+ publishInfo: { pkgid, pkg_zip_name: e },
171
+ });
172
+ // console.log("batchConfig", batchConfig);
173
+ await LSC(batchConfig);
174
+ }
175
+ });
176
+
177
+ program.parse(process.argv);
package/index.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./lib/LSC');
package/lib/LSC.js ADDED
@@ -0,0 +1,151 @@
1
+ const { statSync } = require("fs");
2
+ const path = require("path");
3
+ const { getPkgInfo, setPkgVersion, merge } = require("./utils");
4
+ const axios = require("axios").default;
5
+
6
+ let totalConfig = {};
7
+ var token,
8
+ pkgid = "ffff",
9
+ publish_app_arr,
10
+ set_pkg_version,
11
+ task_status,
12
+ release_desc,
13
+ android_version_scope,
14
+ ios_version_scope,
15
+ pkg_zip_name,
16
+ pkg_dir,
17
+ apps_name;
18
+
19
+
20
+ //引入API
21
+ const {
22
+ allOfflinePkgLists,
23
+ checkSession,
24
+ fileUpload,
25
+ addOfflineVersion,
26
+ addOfflineTask,
27
+ queryOfflineTask,
28
+ updateOfflineTask,
29
+ queryOfflinePkgList,
30
+ queryOfflineVersionlist,
31
+ syncPkgList,
32
+ } = require("./core/API");
33
+
34
+ //在APP上发布指定离线包主逻辑
35
+ async function publish(h5pkgid, app_id) {
36
+ try {
37
+ const __filePath = path.join(process.cwd(), `${pkg_dir}${pkg_zip_name}`);
38
+ let package_id = await fileUpload(__filePath);
39
+
40
+ let mvRes = await queryOfflineVersionlist(app_id, h5pkgid),
41
+ { max_version } = mvRes.data;
42
+
43
+ let { id } = await getPkgInfo(allOfflinePkgLists, app_id, h5pkgid);
44
+
45
+ let file_size = await statSync(__filePath).size;
46
+ let pkg_id = id;
47
+ let relVersion = await setPkgVersion(set_pkg_version, max_version);
48
+ console.log("即将发布的版本号:", relVersion);
49
+ //创建离线发布版本
50
+ let aovRes = await addOfflineVersion({
51
+ app_id,
52
+ pkg_id,
53
+ version: relVersion,
54
+ android_version_scope,
55
+ ios_version_scope,
56
+ is_wifi: "0",
57
+ package_id,
58
+ file_name: pkg_zip_name,
59
+ file_size,
60
+ });
61
+ if (aovRes) {
62
+ //更新列表
63
+ let m = await queryOfflineVersionlist(app_id, h5pkgid),
64
+ { id } = m.data.list[0];
65
+ let aotRes = await addOfflineTask({
66
+ app_id,
67
+ version_id: id,
68
+ pkg_id,
69
+ release_type: 0,
70
+ update_strategy: 0,
71
+ release_strategy: "",
72
+ release_desc,
73
+ });
74
+ console.info(`版本${relVersion}发布${aotRes ? "成功" : "失败"}`);
75
+ let ls = m.data.list.slice(1),
76
+ fs = ls.length > 0 ? ls.filter((val) => val.status === 1) : [],
77
+ prevVerInfo = fs[0];
78
+ if (fs.length > 0) {
79
+ const qotRes = await queryOfflineTask({
80
+ app_id,
81
+ version_id: prevVerInfo.id,
82
+ }),
83
+ e = qotRes.filter((val) => val.status == 0),
84
+ f = e[0];
85
+ // 处理上一版本的问题
86
+ if (task_status != 0) {
87
+ let sm = { 0: "发布", 1: "暂停", 2: "下架" };
88
+ const uotRes = await updateOfflineTask({
89
+ id: f.id,
90
+ status: task_status, //0发布,1暂停,2下架
91
+ app_id,
92
+ });
93
+ console.log(
94
+ `版本${prevVerInfo.version}${sm[task_status]}${
95
+ uotRes ? "成功" : "失败"
96
+ }`
97
+ );
98
+ }
99
+ }
100
+ }
101
+ } catch (error) {
102
+ console.log("发布失败:", error);
103
+ }
104
+ }
105
+
106
+ async function Main(h5pkgid, ids) {
107
+ let tokenIsReady = await checkSession();
108
+ if (tokenIsReady) {
109
+ // console.info("会话有效,准备处理!");
110
+ let chkPkg = await syncPkgList(ids, h5pkgid);
111
+ if (chkPkg) {
112
+ await queryOfflinePkgList(ids);
113
+ for (let i = 0; i < ids.length; i++) {
114
+ console.log(`*************${apps_name[ids[i]]==undefined?"":apps_name[ids[i]]}:::${h5pkgid}*************`);
115
+ // console.log(apps_name[ids[i]]==undefined,ids[i])
116
+ await publish(h5pkgid, ids[i]);
117
+ }
118
+ } else {
119
+ console.warn("请联系Light管理员先在创建离线包!");
120
+ }
121
+ } else {
122
+ console.info("token已过期");
123
+ }
124
+ }
125
+
126
+ async function LSC(config = {}) {
127
+ let { publishInfo, lightBaseURL } = config;
128
+ totalConfig = publishInfo;
129
+
130
+ token = publishInfo.token;
131
+ pkgid = publishInfo.pkgid;
132
+ publish_app_arr = publishInfo.publish_app_arr;
133
+ set_pkg_version = publishInfo.set_pkg_version;
134
+ task_status = publishInfo.task_status;
135
+ release_desc = publishInfo.release_desc;
136
+ android_version_scope = publishInfo.android_version_scope;
137
+ ios_version_scope = publishInfo.ios_version_scope;
138
+ pkg_zip_name = publishInfo.pkg_zip_name;
139
+ pkg_dir = publishInfo.pkg_dir;
140
+ apps_name= publishInfo.apps_name;
141
+
142
+ //全局配置
143
+ axios.defaults.baseURL = lightBaseURL;
144
+ axios.defaults.headers["cookie"] = `token=${token}`;
145
+ console.log(pkgid, publish_app_arr)
146
+ await Main(pkgid, publish_app_arr);
147
+ }
148
+
149
+ module.exports = {
150
+ LSC,
151
+ };
@@ -0,0 +1,314 @@
1
+ "use strict";
2
+ const axios = require("axios").default;
3
+ const qs = require("qs");
4
+ const { createReadStream, access, statSync, constants } = require("fs");
5
+ const FormData = require("form-data");
6
+ const { getPkgInfo } = require("../utils");
7
+ const { ApiList } = require("./apiUrl");
8
+
9
+ // 存储APP的离线包
10
+ let allOfflinePkgLists = {};
11
+
12
+ // 添加响应拦截器
13
+ axios.interceptors.response.use(
14
+ function (response) {
15
+ // 2xx 范围内的状态码都会触发该函数。
16
+ return response;
17
+ },
18
+ function (error) {
19
+ // 超出 2xx 范围的状态码都会触发该函数。
20
+ return Promise.reject(error);
21
+ }
22
+ );
23
+
24
+ //校验会话有效性
25
+ const checkSession = async function (token) {
26
+ if(token){
27
+
28
+ }
29
+ return new Promise((resolve, reject) => {
30
+ axios
31
+ .get(ApiList.checkSession)
32
+ .then((res) => {
33
+ let { data } = res;
34
+ if (data.err_no === 0) {
35
+ resolve(true);
36
+ } else {
37
+ resolve(false);
38
+ }
39
+ })
40
+ .catch((err) => {
41
+ console.warn("检查会话报错!", err);
42
+ resolve(false);
43
+ });
44
+ });
45
+ };
46
+
47
+ // 上传文件,__filePath:绝对路径
48
+ const fileUpload = async function (__filePath) {
49
+ return new Promise((resolve, reject) => {
50
+ const formData = new FormData();
51
+ formData.append("file", createReadStream(__filePath));
52
+ access(__filePath, constants.F_OK, (err) => {
53
+ if (err) {
54
+ console.log(`文件${__filePath}不存在`);
55
+ reject(`文件${__filePath}不存在`);
56
+ } else {
57
+ axios
58
+ .post(ApiList.fileUpload, formData, {
59
+ headers: formData.getHeaders(),
60
+ })
61
+ .then((res) => {
62
+ console.log("上传文件:", res.data);
63
+ let { err_no, data, err_msg } = res.data;
64
+ if (err_no == 0) {
65
+ resolve(data);
66
+ } else {
67
+ reject(res.data);
68
+ }
69
+ resolve(res);
70
+ })
71
+ .catch((e) => reject(e));
72
+ }
73
+ });
74
+ });
75
+ };
76
+
77
+ // 查询APP下的离线包列表 √
78
+ const queryOfflinePkgList = async function (ids = ["3592", "3577"]) {
79
+ const getPkgListOf = function (appid) {
80
+ return new Promise((resolve, reject) => {
81
+ axios
82
+ .get(`${ApiList.queryOfflinePkgList}?app_id=${appid}`)
83
+ .then((res) => {
84
+ let { err_no, err_info, data } = res.data;
85
+ if (err_no == 0) {
86
+ allOfflinePkgLists[appid] = data;
87
+ } else {
88
+ console.log(`${appid}报错:${err_info}`);
89
+ }
90
+ resolve(true);
91
+ })
92
+ .catch((e) => {
93
+ let { err_no, err_info } = e.response.data;
94
+ console.log(`${appid}采集报错:${err_info}`);
95
+ resolve(false);
96
+ });
97
+ });
98
+ };
99
+
100
+ // 批量查询
101
+ return new Promise((resolve, reject) => {
102
+ Promise.all(
103
+ ids.reduce((prev, next) => {
104
+ return prev.concat(getPkgListOf(next));
105
+ }, [])
106
+ ).then((rs) => {
107
+ // console.log("批量查询查询情况:", rs);
108
+ resolve(allOfflinePkgLists);
109
+ });
110
+ });
111
+ };
112
+
113
+ // 同步APP的离线包列表,如果没有创建,则进行创建,以拉平两端离线包
114
+ async function syncPkgList(ids = [], h5app_id) {
115
+ await queryOfflinePkgList(ids);
116
+
117
+ let keys = Object.keys(allOfflinePkgLists);
118
+ let newAppPkgList = keys.reduce(
119
+ (acc, cur) => {
120
+ let a = allOfflinePkgLists[cur];
121
+ let b = a.filter((val) => val.h5app_id == h5app_id);
122
+ // acc[cur] = b;
123
+ if (b.length > 0) {
124
+ acc.name = b[0].name;
125
+ } else {
126
+ acc.none.push(cur);
127
+ }
128
+ return acc;
129
+ },
130
+ { none: [], name: "" }
131
+ );
132
+
133
+ return new Promise((resolve, reject) => {
134
+ if (newAppPkgList["none"].length > 0 && newAppPkgList["name"] != "") {
135
+ Promise.all(
136
+ newAppPkgList["none"].reduce((prev, next) => {
137
+ return prev.concat(
138
+ addOfflineH5pkg(next, h5app_id, newAppPkgList["name"])
139
+ );
140
+ }, [])
141
+ ).then((rs) => {
142
+ console.log("同步离线包信息:", rs);
143
+ resolve(true);
144
+ });
145
+ } else {
146
+ resolve(true);
147
+ }
148
+ });
149
+ }
150
+
151
+ //查询版本信息√
152
+ const queryOfflineVersionlist = async function (app_id, h5app_id) {
153
+ let t = await getPkgInfo(allOfflinePkgLists, app_id, h5app_id),
154
+ { id } = t;
155
+ //pkg_id
156
+ let qovRes = await axios.get(
157
+ `${ApiList.queryOfflineVersionlist}?app_id=${app_id}&pkg_id=${id}&page_no=1&page_size=10`
158
+ );
159
+ return qovRes.data;
160
+ };
161
+
162
+ // 如果未创建离线包,则创建之√
163
+ const addOfflineH5pkg = async function (app_id, h5app_id, name) {
164
+ // return new Promise((resolve, reject) => {
165
+ let copRes = await axios.get(
166
+ `${ApiList.checkOfflinePkgid}?app_id=${app_id}&h5app_id=${h5app_id}`
167
+ ),
168
+ copFlag = copRes.data.data;
169
+ let conRes = await axios.get(
170
+ `${ApiList.checkOfflinePkgname}?app_id=${app_id}&name=${encodeURI(name)}`
171
+ ),
172
+ conFlag = conRes.data.data;
173
+ if (copFlag || conFlag) {
174
+ console.log("已存在");
175
+ } else {
176
+ let aofRes = await axios({
177
+ method: "post",
178
+ url: ApiList.addOfflineH5pkg,
179
+ data: qs.stringify({
180
+ app_id,
181
+ h5app_id,
182
+ name,
183
+ pkg_type: 1,
184
+ }),
185
+ });
186
+ let { err_no, data } = aofRes.data;
187
+ console.log(`添加离线包:${h5app_id}-${name}`, aofRes.data);
188
+ if (err_no == 0 && data == 1) {
189
+ await queryOfflinePkgList([app_id]);
190
+ return true;
191
+ } else {
192
+ return false;
193
+ }
194
+ }
195
+ // });
196
+ };
197
+
198
+ //创建离线发布版本 √
199
+ const addOfflineVersion = async function (args) {
200
+ return new Promise((resolve, reject) => {
201
+ // 创建版本
202
+ axios({
203
+ method: "post",
204
+ url: ApiList.addOfflineVersion,
205
+ data: qs.stringify(args),
206
+ })
207
+ .then(function (res) {
208
+ // {"err_no":0,"err_info":"成功执行","data":1}
209
+ console.log("创建离线发布版本:", res.data);
210
+ const { err_no } = res.data;
211
+ if (err_no == 0) {
212
+ resolve(true);
213
+ } else {
214
+ resolve(false);
215
+ }
216
+ })
217
+ .catch(function (error) {
218
+ console.log("创建离线发布版本报错:", error);
219
+ resolve(false);
220
+ });
221
+ });
222
+ };
223
+
224
+ // 添加发布任务 √
225
+ const addOfflineTask = async function (args) {
226
+ return new Promise((resolve, reject) => {
227
+ // 创建版本
228
+ axios({
229
+ method: "post",
230
+ url: ApiList.addOfflineTask,
231
+ data: qs.stringify(args),
232
+ })
233
+ .then((res) => {
234
+ // {"err_no":0,"err_info":"成功执行","data":24303}
235
+ console.log("添加发布任务:", res.data);
236
+ const { err_no } = res.data;
237
+ if (err_no == 0) {
238
+ resolve(true);
239
+ } else {
240
+ resolve(false);
241
+ }
242
+ })
243
+ .catch((error) => {
244
+ console.log("添加发布任务报错:", error);
245
+ resolve(false);
246
+ });
247
+ });
248
+ };
249
+
250
+ //查询某个版本的发布任务 √
251
+ const queryOfflineTask = async function (args) {
252
+ return new Promise((resolve, reject) => {
253
+ // 创建版本
254
+ axios
255
+ .get(
256
+ `${ApiList.queryOfflineTask}?${qs.stringify(
257
+ args
258
+ )}&page_no=1&page_size=5`
259
+ )
260
+ .then(function (res) {
261
+ // console.log("获取指定版本下的发布任务列表:", JSON.stringify(res.data));
262
+ const { err_no, data } = res.data;
263
+ if (err_no == 0) {
264
+ resolve(data.list);
265
+ } else {
266
+ resolve([]);
267
+ }
268
+ })
269
+ .catch(function (error) {
270
+ console.log("获取指定版本下的发布任务列表报错:", error);
271
+ reject(error);
272
+ });
273
+ });
274
+ };
275
+
276
+ //更新发布任务状态√
277
+ const updateOfflineTask = async function (uotArgs = {}) {
278
+ return new Promise((resolve, reject) => {
279
+ // 创建版本
280
+ axios({
281
+ method: "post",
282
+ url: ApiList.updateOfflineTask,
283
+ data: qs.stringify(uotArgs),
284
+ })
285
+ .then(function (res) {
286
+ console.log("更新发布任务状态:", res.data);
287
+ const { err_no } = res.data;
288
+ if (err_no == 0) {
289
+ resolve(true);
290
+ } else {
291
+ resolve(false);
292
+ }
293
+ })
294
+ .catch(function (error) {
295
+ console.log("更新发布任务状态报错:", error);
296
+ resolve(false);
297
+ });
298
+ });
299
+ };
300
+
301
+ module.exports = {
302
+ allOfflinePkgLists,
303
+ checkSession,
304
+ fileUpload,
305
+ queryOfflinePkgList,
306
+ getPkgInfo,
307
+ queryOfflineVersionlist,
308
+ addOfflineH5pkg,
309
+ addOfflineVersion,
310
+ addOfflineTask,
311
+ queryOfflineTask,
312
+ updateOfflineTask,
313
+ syncPkgList,
314
+ };
@@ -0,0 +1,19 @@
1
+ "use strict"
2
+
3
+ // API列表
4
+ const offlineURlPrefix = `/light/light-platform-client/v/appdiy/offline/`;
5
+ const ApiList = {
6
+ fileUpload: "/ltcommon/file/v/upload?bizType=h5_offline_src",
7
+ checkSession: "/light/light-platform-client/v/auth/check_session",
8
+ queryOfflinePkgList: offlineURlPrefix + "query_offline_pkglist1",
9
+ queryOfflineVersionlist: offlineURlPrefix + "query_offline_versionlist",
10
+ addOfflineH5pkg: offlineURlPrefix + "add_offline_h5pkg",
11
+ checkOfflinePkgid: offlineURlPrefix + "check_offline_pkgid",
12
+ checkOfflinePkgname: offlineURlPrefix + "check_offline_pkgname",
13
+ addOfflineVersion: offlineURlPrefix + "add_offline_version",
14
+ addOfflineTask: offlineURlPrefix + "add_offline_task",
15
+ queryOfflineTask: offlineURlPrefix + "query_offline_task",
16
+ updateOfflineTask: offlineURlPrefix + "update_offline_task",
17
+ };
18
+
19
+ module.exports={ApiList}
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var defaults = {
3
+ publishInfo: {
4
+ token: "XXXXX", //登录token
5
+ pkgid: "XXXXX", //离线包ID
6
+ set_pkg_version: "1.0.1", //指定发布版本
7
+ publish_app_arr: [3577, 3592], //发布到指定APP,默认开发版
8
+ apps_name: {
9
+ 3592: "开发版_iOS",
10
+ 3577: "开发版_安卓",
11
+ 3559: "测试",
12
+ },
13
+ android_version_scope: "7.0.6.0", //Android端离线包兼容版本
14
+ ios_version_scope: "7.0.6", //iOS端离线包兼容版本
15
+ pkg_zip_name: "dist.zip",
16
+ pkg_dir: "./dist/", //离线包所在路径
17
+ release_desc: "功能更新", //发布日志
18
+ task_status: "0", //对上一版本的处理:0:发布,1:暂停,2:下架
19
+ },
20
+ lightBaseURL: "https://xxx.xxx.xxx/lightadmin/pas-api",
21
+ };
22
+
23
+ module.exports = defaults;
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ module.exports = function bind(fn, thisArg) {
4
+ return function wrap() {
5
+ var args = new Array(arguments.length);
6
+ for (var i = 0; i < args.length; i++) {
7
+ args[i] = arguments[i];
8
+ }
9
+ return fn.apply(thisArg, args);
10
+ };
11
+ };
@@ -0,0 +1,31 @@
1
+ "user strict"
2
+ var fs = require("fs");
3
+ var path = require("path");
4
+
5
+ var load = function (path, name) {
6
+ if (name) {
7
+ return require(path + name);
8
+ }
9
+ return require(path);
10
+ };
11
+
12
+ module.exports = function (dir) {
13
+ patcher = {};
14
+ if (!/\.js$/.test(filename)) {
15
+ return;
16
+ }
17
+ var name = path.basename(filename, ".js");
18
+ var _load = load.bind(null, "./" + dir + "/", name);
19
+ patcher.__defineGetter__(name, _load);
20
+
21
+ // fs.readdirSync(__dirname + "/" + dir).forEach(function (filename) {
22
+ // if (!/\.js$/.test(filename)) {
23
+ // return;
24
+ // }
25
+ // var name = path.basename(filename, ".js");
26
+ // var _load = load.bind(null, "./" + dir + "/", name);
27
+ // patcher.__defineGetter__(name, _load);
28
+ // });
29
+
30
+ return patcher;
31
+ };
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+ // const axios = require('axios').default;
3
+ // module.exports = robot;
package/lib/utils.js ADDED
@@ -0,0 +1,435 @@
1
+ "use strict";
2
+ const { access, statSync, constants, writeFile, readdirSync } = require("fs");
3
+
4
+ var bind = require("./helpers/bind");
5
+
6
+ // utils is a library of generic helper functions non-specific to this project;
7
+
8
+ var toString = Object.prototype.toString;
9
+
10
+ /**
11
+ * Determine if a value is an Array
12
+ *
13
+ * @param {Object} val The value to test
14
+ * @returns {boolean} True if value is an Array, otherwise false
15
+ */
16
+ function isArray(val) {
17
+ return Array.isArray(val);
18
+ }
19
+
20
+ /**
21
+ * Determine if a value is undefined
22
+ *
23
+ * @param {Object} val The value to test
24
+ * @returns {boolean} True if the value is undefined, otherwise false
25
+ */
26
+ function isUndefined(val) {
27
+ return typeof val === "undefined";
28
+ }
29
+
30
+ /**
31
+ * Determine if a value is a Buffer
32
+ *
33
+ * @param {Object} val The value to test
34
+ * @returns {boolean} True if value is a Buffer, otherwise false
35
+ */
36
+ function isBuffer(val) {
37
+ return (
38
+ val !== null &&
39
+ !isUndefined(val) &&
40
+ val.constructor !== null &&
41
+ !isUndefined(val.constructor) &&
42
+ typeof val.constructor.isBuffer === "function" &&
43
+ val.constructor.isBuffer(val)
44
+ );
45
+ }
46
+
47
+ /**
48
+ * Determine if a value is an ArrayBuffer
49
+ *
50
+ * @param {Object} val The value to test
51
+ * @returns {boolean} True if value is an ArrayBuffer, otherwise false
52
+ */
53
+ function isArrayBuffer(val) {
54
+ return toString.call(val) === "[object ArrayBuffer]";
55
+ }
56
+
57
+ /**
58
+ * Determine if a value is a FormData
59
+ *
60
+ * @param {Object} val The value to test
61
+ * @returns {boolean} True if value is an FormData, otherwise false
62
+ */
63
+ function isFormData(val) {
64
+ return toString.call(val) === "[object FormData]";
65
+ }
66
+
67
+ /**
68
+ * Determine if a value is a view on an ArrayBuffer
69
+ *
70
+ * @param {Object} val The value to test
71
+ * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false
72
+ */
73
+ function isArrayBufferView(val) {
74
+ var result;
75
+ if (typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView) {
76
+ result = ArrayBuffer.isView(val);
77
+ } else {
78
+ result = val && val.buffer && isArrayBuffer(val.buffer);
79
+ }
80
+ return result;
81
+ }
82
+
83
+ /**
84
+ * Determine if a value is a String
85
+ *
86
+ * @param {Object} val The value to test
87
+ * @returns {boolean} True if value is a String, otherwise false
88
+ */
89
+ function isString(val) {
90
+ return typeof val === "string";
91
+ }
92
+
93
+ /**
94
+ * Determine if a value is a Number
95
+ *
96
+ * @param {Object} val The value to test
97
+ * @returns {boolean} True if value is a Number, otherwise false
98
+ */
99
+ function isNumber(val) {
100
+ return typeof val === "number";
101
+ }
102
+
103
+ /**
104
+ * Determine if a value is an Object
105
+ *
106
+ * @param {Object} val The value to test
107
+ * @returns {boolean} True if value is an Object, otherwise false
108
+ */
109
+ function isObject(val) {
110
+ return val !== null && typeof val === "object";
111
+ }
112
+
113
+ /**
114
+ * Determine if a value is a plain Object
115
+ *
116
+ * @param {Object} val The value to test
117
+ * @return {boolean} True if value is a plain Object, otherwise false
118
+ */
119
+ function isPlainObject(val) {
120
+ if (toString.call(val) !== "[object Object]") {
121
+ return false;
122
+ }
123
+
124
+ var prototype = Object.getPrototypeOf(val);
125
+ return prototype === null || prototype === Object.prototype;
126
+ }
127
+
128
+ /**
129
+ * Determine if a value is a Date
130
+ *
131
+ * @param {Object} val The value to test
132
+ * @returns {boolean} True if value is a Date, otherwise false
133
+ */
134
+ function isDate(val) {
135
+ return toString.call(val) === "[object Date]";
136
+ }
137
+
138
+ /**
139
+ * Determine if a value is a File
140
+ *
141
+ * @param {Object} val The value to test
142
+ * @returns {boolean} True if value is a File, otherwise false
143
+ */
144
+ function isFile(val) {
145
+ return toString.call(val) === "[object File]";
146
+ }
147
+
148
+ /**
149
+ * Determine if a value is a Blob
150
+ *
151
+ * @param {Object} val The value to test
152
+ * @returns {boolean} True if value is a Blob, otherwise false
153
+ */
154
+ function isBlob(val) {
155
+ return toString.call(val) === "[object Blob]";
156
+ }
157
+
158
+ /**
159
+ * Determine if a value is a Function
160
+ *
161
+ * @param {Object} val The value to test
162
+ * @returns {boolean} True if value is a Function, otherwise false
163
+ */
164
+ function isFunction(val) {
165
+ return toString.call(val) === "[object Function]";
166
+ }
167
+
168
+ /**
169
+ * Determine if a value is a Stream
170
+ *
171
+ * @param {Object} val The value to test
172
+ * @returns {boolean} True if value is a Stream, otherwise false
173
+ */
174
+ function isStream(val) {
175
+ return isObject(val) && isFunction(val.pipe);
176
+ }
177
+
178
+ /**
179
+ * Determine if a value is a URLSearchParams object
180
+ *
181
+ * @param {Object} val The value to test
182
+ * @returns {boolean} True if value is a URLSearchParams object, otherwise false
183
+ */
184
+ function isURLSearchParams(val) {
185
+ return toString.call(val) === "[object URLSearchParams]";
186
+ }
187
+
188
+ /**
189
+ * Trim excess whitespace off the beginning and end of a string
190
+ *
191
+ * @param {String} str The String to trim
192
+ * @returns {String} The String freed of excess whitespace
193
+ */
194
+ function trim(str) {
195
+ return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, "");
196
+ }
197
+
198
+ /**
199
+ * Determine if we're running in a standard browser environment
200
+ *
201
+ * This allows axios to run in a web worker, and react-native.
202
+ * Both environments support XMLHttpRequest, but not fully standard globals.
203
+ *
204
+ * web workers:
205
+ * typeof window -> undefined
206
+ * typeof document -> undefined
207
+ *
208
+ * react-native:
209
+ * navigator.product -> 'ReactNative'
210
+ * nativescript
211
+ * navigator.product -> 'NativeScript' or 'NS'
212
+ */
213
+ function isStandardBrowserEnv() {
214
+ if (
215
+ typeof navigator !== "undefined" &&
216
+ (navigator.product === "ReactNative" ||
217
+ navigator.product === "NativeScript" ||
218
+ navigator.product === "NS")
219
+ ) {
220
+ return false;
221
+ }
222
+ return typeof window !== "undefined" && typeof document !== "undefined";
223
+ }
224
+
225
+ /**
226
+ * Iterate over an Array or an Object invoking a function for each item.
227
+ *
228
+ * If `obj` is an Array callback will be called passing
229
+ * the value, index, and complete array for each item.
230
+ *
231
+ * If 'obj' is an Object callback will be called passing
232
+ * the value, key, and complete object for each property.
233
+ *
234
+ * @param {Object|Array} obj The object to iterate
235
+ * @param {Function} fn The callback to invoke for each item
236
+ */
237
+ function forEach(obj, fn) {
238
+ // Don't bother if no value provided
239
+ if (obj === null || typeof obj === "undefined") {
240
+ return;
241
+ }
242
+
243
+ // Force an array if not already something iterable
244
+ if (typeof obj !== "object") {
245
+ /*eslint no-param-reassign:0*/
246
+ obj = [obj];
247
+ }
248
+
249
+ if (isArray(obj)) {
250
+ // Iterate over array values
251
+ for (var i = 0, l = obj.length; i < l; i++) {
252
+ fn.call(null, obj[i], i, obj);
253
+ }
254
+ } else {
255
+ // Iterate over object keys
256
+ for (var key in obj) {
257
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
258
+ fn.call(null, obj[key], key, obj);
259
+ }
260
+ }
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Accepts varargs expecting each argument to be an object, then
266
+ * immutably merges the properties of each object and returns result.
267
+ *
268
+ * When multiple objects contain the same key the later object in
269
+ * the arguments list will take precedence.
270
+ *
271
+ * Example:
272
+ *
273
+ * ```js
274
+ * var result = merge({foo: 123}, {foo: 456});
275
+ * console.log(result.foo); // outputs 456
276
+ * ```
277
+ *
278
+ * @param {Object} obj1 Object to merge
279
+ * @returns {Object} Result of all merge properties
280
+ */
281
+ function merge(/* obj1, obj2, obj3, ... */) {
282
+ var result = {};
283
+ function assignValue(val, key) {
284
+ if (isPlainObject(result[key]) && isPlainObject(val)) {
285
+ result[key] = merge(result[key], val);
286
+ } else if (isPlainObject(val)) {
287
+ result[key] = merge({}, val);
288
+ } else if (isArray(val)) {
289
+ result[key] = val.slice();
290
+ } else {
291
+ result[key] = val;
292
+ }
293
+ }
294
+
295
+ for (var i = 0, l = arguments.length; i < l; i++) {
296
+ forEach(arguments[i], assignValue);
297
+ }
298
+ return result;
299
+ }
300
+
301
+ /**
302
+ * Extends object a by mutably adding to it the properties of object b.
303
+ *
304
+ * @param {Object} a The object to be extended
305
+ * @param {Object} b The object to copy properties from
306
+ * @param {Object} thisArg The object to bind function to
307
+ * @return {Object} The resulting value of object a
308
+ */
309
+ function extend(a, b, thisArg) {
310
+ forEach(b, function assignValue(val, key) {
311
+ if (thisArg && typeof val === "function") {
312
+ a[key] = bind(val, thisArg);
313
+ } else {
314
+ a[key] = val;
315
+ }
316
+ });
317
+ return a;
318
+ }
319
+
320
+ /**
321
+ * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
322
+ *
323
+ * @param {string} content with BOM
324
+ * @return {string} content value without BOM
325
+ */
326
+ function stripBOM(content) {
327
+ if (content.charCodeAt(0) === 0xfeff) {
328
+ content = content.slice(1);
329
+ }
330
+ return content;
331
+ }
332
+
333
+ const fmtTime = function () {
334
+ var date = new Date();
335
+ hh = `${date.getHours() < 10 ? "0" : ""}${date.getHours()}`;
336
+ min = `${date.getMinutes() < 10 ? "0" : ""}${date.getMinutes()}`;
337
+ sec = `${date.getSeconds() < 10 ? "0" : ""}${date.getSeconds()}`;
338
+ mymilliseconds = date.getMilliseconds();
339
+ return hh + ":" + min + ":" + sec + ":" + mymilliseconds;
340
+ };
341
+
342
+ //获取离线包信息√
343
+ async function getPkgInfo(pkgLists, appid, h5pkgid) {
344
+ let f = pkgLists[appid].filter((val) => val.h5app_id == h5pkgid);
345
+ return f[0];
346
+ }
347
+
348
+ //设置要发布版本号
349
+ const setPkgVersion = function (set_pkg_version, max_version) {
350
+ const compare = (a, b) => {
351
+ if (a === b) {
352
+ return 0;
353
+ }
354
+ const aArr = a.split("."),
355
+ bArr = b.split(".");
356
+ for (let i = 0; i < Math.min(aArr.length, bArr.length); i++) {
357
+ if (parseInt(aArr[i]) < parseInt(bArr[i])) {
358
+ return -1;
359
+ }
360
+ if (parseInt(aArr[i]) > parseInt(bArr[i])) {
361
+ return 1;
362
+ }
363
+ }
364
+ if (aArr.length < bArr.length) {
365
+ return -1;
366
+ }
367
+ if (aArr.length > bArr.length) {
368
+ return 1;
369
+ }
370
+ return 0;
371
+ };
372
+
373
+ let e = max_version.split("."),
374
+ p = e.slice(-1),
375
+ f = e.pop(),
376
+ n = parseInt(p) + 1,
377
+ newVersion = `${[...e, n].join(".")}`;
378
+ if (set_pkg_version && compare(set_pkg_version, max_version) == 1) {
379
+ // 如果指定发布版本号
380
+ newVersion = set_pkg_version;
381
+ }
382
+ return newVersion;
383
+ };
384
+
385
+ //获取指定文件夹下的离线包
386
+ //获取项目工程里的图片
387
+ const readFileList = function (path, filesList) {
388
+ var files = readdirSync(path);
389
+ files.forEach(function (item, index) {
390
+ var stat = statSync(path + item);
391
+ if (stat.isDirectory()) {
392
+ //递归读取文件
393
+ readFileList(path + item + "/", filesList);
394
+ } else {
395
+ if (item.includes(".zip")) {
396
+ filesList.push(item);
397
+ }
398
+ }
399
+ });
400
+ };
401
+ //获取文件夹下的所有文件
402
+ const getZipFileList = async function (path) {
403
+ var filesList = [];
404
+ readFileList(path, filesList);
405
+ return filesList;
406
+ };
407
+
408
+ module.exports = {
409
+ fmtTime,
410
+ getPkgInfo,
411
+ setPkgVersion,
412
+ getZipFileList,
413
+ isArray: isArray,
414
+ isArrayBuffer: isArrayBuffer,
415
+ isBuffer: isBuffer,
416
+ isFormData: isFormData,
417
+ isArrayBufferView: isArrayBufferView,
418
+ isString: isString,
419
+ isNumber: isNumber,
420
+ isObject: isObject,
421
+ isPlainObject: isPlainObject,
422
+ isUndefined: isUndefined,
423
+ isDate: isDate,
424
+ isFile: isFile,
425
+ isBlob: isBlob,
426
+ isFunction: isFunction,
427
+ isStream: isStream,
428
+ isURLSearchParams: isURLSearchParams,
429
+ isStandardBrowserEnv: isStandardBrowserEnv,
430
+ forEach: forEach,
431
+ merge: merge,
432
+ extend: extend,
433
+ trim: trim,
434
+ stripBOM: stripBOM,
435
+ };
@@ -0,0 +1,14 @@
1
+ {
2
+ "publishInfo": {
3
+ "token": "xxx.xxx.xxx",
4
+ "pkgid": "xxx.xxx.xxx",
5
+ "set_pkg_version": "1.2.9",
6
+ "publish_app_arr": [3592, 3577],
7
+ "android_version_scope": "7.0.7.0",
8
+ "ios_version_scope": "7.0.7",
9
+ "pkg_zip_name": "xxx.xxx.xxx.zip",
10
+ "pkg_dir": "./dist/",
11
+ "task_status": "2"
12
+ },
13
+ "lightBaseURL": "https://xxx.xxx.xxx/lightadmin/pas-api"
14
+ }
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "lightshortcuts",
3
+ "dependencies": {
4
+ "axios": "^0.24.0",
5
+ "chalk": "^5.0.0",
6
+ "commander": "^8.3.0",
7
+ "filesize": "^8.0.6",
8
+ "form-data": "^4.0.0",
9
+ "inquirer": "^8.2.0",
10
+ "qs": "^6.10.3"
11
+ },
12
+ "version": "1.0.0",
13
+ "description": "Light离线包自动发布工具",
14
+ "main": "index.js",
15
+ "devDependencies": {},
16
+ "scripts": {
17
+ "start": "lsc publish",
18
+ "test": "echo \"Error: no test specified\" && exit 1"
19
+ },
20
+ "bin": {
21
+ "lsc": "./bin/lsc.js"
22
+ },
23
+ "keywords": [
24
+ "offline",
25
+ "tools"
26
+ ],
27
+ "author": "weizhen@idoseek.com",
28
+ "license": "ISC"
29
+ }