aiot-toolkit 2.0.2-beta.1 → 2.0.2-beta.11
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/lib/bin.js +27 -28
- package/lib/builder/UxBuilder.d.ts +19 -4
- package/lib/builder/UxBuilder.js +55 -28
- package/lib/builder/XtsBuilder.js +10 -13
- package/lib/interface/VelaEmulatorInterface.d.ts +7 -6
- package/lib/starter/AndroidUxStart.d.ts +39 -0
- package/lib/starter/AndroidUxStart.js +170 -0
- package/lib/starter/IStarter.d.ts +2 -1
- package/lib/starter/IStarter.js +5 -1
- package/lib/starter/UxStarter.d.ts +14 -2
- package/lib/starter/UxStarter.js +95 -44
- package/lib/starter/XtsStarter.d.ts +2 -2
- package/lib/starter/androidRouter/LinkMode.d.ts +9 -0
- package/lib/starter/androidRouter/LinkMode.js +12 -0
- package/lib/starter/androidRouter/PackageRouter.d.ts +55 -0
- package/lib/starter/androidRouter/PackageRouter.js +152 -0
- package/lib/starter/androidRouter/h5/index.css +167 -0
- package/lib/starter/androidRouter/h5/index.html +58 -0
- package/lib/starter/androidRouter/h5/index.js +66 -0
- package/lib/utils/AdbUtils.js +3 -3
- package/lib/utils/DeviceUtil.js +37 -24
- package/lib/utils/RequestUtils.js +5 -5
- package/lib/utils/VelaAvdUtils.d.ts +25 -9
- package/lib/utils/VelaAvdUtils.js +262 -100
- package/package.json +18 -9
- package/lib/utils/UxBuilderUtils.d.ts +0 -8
- package/lib/utils/UxBuilderUtils.js +0 -51
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { IParam } from '@aiot-toolkit/commander';
|
|
2
2
|
import { IStartOptions } from '@aiot-toolkit/emulator';
|
|
3
3
|
import UxBuilder from '../builder/UxBuilder';
|
|
4
4
|
import IStarter from './IStarter';
|
|
5
|
+
/**
|
|
6
|
+
* UxStarter
|
|
7
|
+
* ux快应用启动器
|
|
8
|
+
*/
|
|
5
9
|
declare class UxStarter extends IStarter<IStartOptions> {
|
|
10
|
+
private ws;
|
|
6
11
|
builder: UxBuilder;
|
|
7
|
-
params:
|
|
12
|
+
params: IParam[];
|
|
8
13
|
start(projectPath: string, options: any): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* 检查选择的模拟器在当前环境下是否可以使用
|
|
16
|
+
* 下面的几种情况会导致模拟器不可用:1. 模拟器绑定的Vela镜像不存在;2. Vela4.0缺少coredump.core或者vela_data.bin文件
|
|
17
|
+
* @param avdName 模拟器名称
|
|
18
|
+
* @returns {boolean}
|
|
19
|
+
*/
|
|
20
|
+
isAvailableEmulator(avdName: string): boolean;
|
|
9
21
|
}
|
|
10
22
|
export default UxStarter;
|
package/lib/starter/UxStarter.js
CHANGED
|
@@ -1,27 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
2
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
3
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
4
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -35,10 +12,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
35
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
13
|
};
|
|
37
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
-
const
|
|
15
|
+
const aiotpack_1 = require("@aiot-toolkit/aiotpack");
|
|
39
16
|
const emulator_1 = require("@aiot-toolkit/emulator");
|
|
40
17
|
const constants_1 = require("@aiot-toolkit/emulator/lib/static/constants");
|
|
41
|
-
const
|
|
18
|
+
const index_1 = require("@aiot-toolkit/emulator/lib/utils/index");
|
|
19
|
+
const shared_utils_1 = require("@aiot-toolkit/shared-utils");
|
|
42
20
|
const prompts_1 = require("@inquirer/prompts");
|
|
43
21
|
const os_1 = __importDefault(require("os"));
|
|
44
22
|
const path_1 = __importDefault(require("path"));
|
|
@@ -46,10 +24,23 @@ const portfinder_1 = __importDefault(require("portfinder"));
|
|
|
46
24
|
const UxBuilder_1 = __importDefault(require("../builder/UxBuilder"));
|
|
47
25
|
const VelaAvdUtils_1 = __importDefault(require("../utils/VelaAvdUtils"));
|
|
48
26
|
const IStarter_1 = __importDefault(require("./IStarter"));
|
|
27
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
28
|
+
const ws_1 = __importDefault(require("ws"));
|
|
29
|
+
/**
|
|
30
|
+
* UxStarter
|
|
31
|
+
* ux快应用启动器
|
|
32
|
+
*/
|
|
49
33
|
class UxStarter extends IStarter_1.default {
|
|
50
34
|
constructor() {
|
|
51
35
|
super(...arguments);
|
|
52
|
-
this.builder = new UxBuilder_1.default(
|
|
36
|
+
this.builder = new UxBuilder_1.default({
|
|
37
|
+
onBuildSuccess: () => {
|
|
38
|
+
var _a;
|
|
39
|
+
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify({
|
|
40
|
+
type: 'restart'
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
});
|
|
53
44
|
this.params = [
|
|
54
45
|
{
|
|
55
46
|
name: 'disableNSH',
|
|
@@ -74,49 +65,89 @@ class UxStarter extends IStarter_1.default {
|
|
|
74
65
|
}
|
|
75
66
|
start(projectPath, options) {
|
|
76
67
|
return __awaiter(this, void 0, void 0, function* () {
|
|
68
|
+
// 获取已经创建的模拟器列表
|
|
77
69
|
const avdList = VelaAvdUtils_1.default.velaAvdCls.getVelaAvdList();
|
|
70
|
+
let avdName;
|
|
78
71
|
if (avdList.length === 0) {
|
|
79
|
-
|
|
72
|
+
shared_utils_1.ColorConsole.error('no vela emulator available, please create it first.');
|
|
80
73
|
VelaAvdUtils_1.default.createVelaAvdByInquire();
|
|
81
74
|
return;
|
|
82
75
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
})
|
|
88
|
-
});
|
|
89
|
-
let serverPort;
|
|
90
|
-
// watch模型下开启server
|
|
91
|
-
if (options.watch) {
|
|
92
|
-
serverPort = yield portfinder_1.default.getPortPromise({
|
|
93
|
-
port: JavascriptDefaultCompileOption_1.default.serverPort
|
|
76
|
+
else if (avdList.length === 1) {
|
|
77
|
+
const needtoRun = yield (0, prompts_1.confirm)({
|
|
78
|
+
message: 'there is only one emulator, need to Run?',
|
|
79
|
+
default: true
|
|
94
80
|
});
|
|
95
|
-
|
|
81
|
+
if (!needtoRun) {
|
|
82
|
+
VelaAvdUtils_1.default.createVelaAvdByInquire();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
avdName = avdList[0].avdName;
|
|
86
|
+
shared_utils_1.ColorConsole.warn(`Start run by :${avdName} `);
|
|
96
87
|
}
|
|
88
|
+
else {
|
|
89
|
+
avdName = yield (0, prompts_1.select)({
|
|
90
|
+
message: 'name of the avd to start',
|
|
91
|
+
choices: avdList.map((item) => {
|
|
92
|
+
return { value: item.avdName };
|
|
93
|
+
})
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
// 检查选择的模拟器是否符合要求
|
|
97
|
+
if (avdName && !this.isAvailableEmulator(avdName)) {
|
|
98
|
+
shared_utils_1.ColorConsole.throw(`this emulator is unavailable, please create a new emulator.`);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
let serverPort;
|
|
102
|
+
// watch模型下开启server
|
|
103
|
+
serverPort = yield portfinder_1.default.getPortPromise({
|
|
104
|
+
port: aiotpack_1.JavascriptDefaultCompileOption.serverPort
|
|
105
|
+
});
|
|
106
|
+
(0, aiotpack_1.setServerPort)(serverPort);
|
|
97
107
|
// build
|
|
108
|
+
const compilerOption = this.builder.getCompilerOption(projectPath, options);
|
|
98
109
|
yield this.builder.build(projectPath, options);
|
|
99
110
|
// start
|
|
100
111
|
const params = {
|
|
101
112
|
sdkHome: path_1.default.resolve(os_1.default.homedir(), '.export'),
|
|
102
113
|
avdHome: path_1.default.resolve(os_1.default.homedir(), '.android/avd'),
|
|
103
|
-
projectPath
|
|
114
|
+
projectPath,
|
|
115
|
+
compilerOption
|
|
104
116
|
};
|
|
117
|
+
// 寻找对应的模拟器instance
|
|
105
118
|
const goldfishInstance = (0, emulator_1.findInstance)(avdName, params);
|
|
106
119
|
if (!goldfishInstance)
|
|
107
120
|
return;
|
|
108
|
-
let
|
|
121
|
+
let grpcPort;
|
|
122
|
+
// 设置vncPort
|
|
109
123
|
if (options.openVNC) {
|
|
110
|
-
|
|
124
|
+
grpcPort = yield portfinder_1.default.getPortPromise({
|
|
125
|
+
port: constants_1.defaultVncPort,
|
|
126
|
+
stopPort: constants_1.defaultVncPort + 100
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
// 设置adbPort
|
|
130
|
+
const adbPort = yield (0, index_1.getEvenPort)();
|
|
131
|
+
if (!adbPort) {
|
|
132
|
+
shared_utils_1.ColorConsole.throw(`${adbPort},5555和5585之间的端口号已全部不占用,请解决端口冲突,再启动模拟器`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
// 设置debugPort
|
|
136
|
+
let debugPort;
|
|
137
|
+
if (options.devtool) {
|
|
138
|
+
debugPort = yield portfinder_1.default.getPortPromise({ port: constants_1.defaultDebugPort });
|
|
111
139
|
}
|
|
112
140
|
const startOptions = {
|
|
113
141
|
avdName,
|
|
114
142
|
devtool: options.devtool,
|
|
115
143
|
disableNSH: options.disableNSH,
|
|
116
144
|
serverPort,
|
|
117
|
-
|
|
145
|
+
grpcPort,
|
|
146
|
+
adbPort,
|
|
147
|
+
debugPort
|
|
118
148
|
};
|
|
119
|
-
goldfishInstance.start(startOptions);
|
|
149
|
+
yield goldfishInstance.start(startOptions);
|
|
150
|
+
this.ws = new ws_1.default(`ws://localhost:${serverPort}`);
|
|
120
151
|
// waiter
|
|
121
152
|
// const startWaiter: PersistentCommand = new PersistentCommand({
|
|
122
153
|
// description: 'aiot-toolkit start 的常驻命令',
|
|
@@ -135,5 +166,25 @@ class UxStarter extends IStarter_1.default {
|
|
|
135
166
|
// startWaiter.start()
|
|
136
167
|
});
|
|
137
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* 检查选择的模拟器在当前环境下是否可以使用
|
|
171
|
+
* 下面的几种情况会导致模拟器不可用:1. 模拟器绑定的Vela镜像不存在;2. Vela4.0缺少coredump.core或者vela_data.bin文件
|
|
172
|
+
* @param avdName 模拟器名称
|
|
173
|
+
* @returns {boolean}
|
|
174
|
+
*/
|
|
175
|
+
isAvailableEmulator(avdName) {
|
|
176
|
+
const { avdImagePath } = VelaAvdUtils_1.default.velaAvdCls.getVelaAvdInfo(avdName);
|
|
177
|
+
// 没有avdImagePath,即对应的configIni里没有 image.sysdir.1 字段
|
|
178
|
+
// 有avdImagePath,但是这个目录下没有nuttx
|
|
179
|
+
if (!avdImagePath || !fs_extra_1.default.existsSync(path_1.default.resolve(avdImagePath, 'nuttx'))) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
if (avdImagePath.includes('vela-release') &&
|
|
183
|
+
(!fs_extra_1.default.existsSync(path_1.default.resolve(avdImagePath, 'coredump.core')) ||
|
|
184
|
+
!fs_extra_1.default.existsSync(path_1.default.resolve(avdImagePath, 'vela_data.bin')))) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
138
189
|
}
|
|
139
190
|
exports.default = UxStarter;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { IParam } from '@aiot-toolkit/commander';
|
|
2
2
|
import XtsBuilder from '../builder/XtsBuilder';
|
|
3
3
|
import IStarter from './IStarter';
|
|
4
4
|
declare class XtsStarter extends IStarter {
|
|
5
5
|
builder: XtsBuilder;
|
|
6
|
-
params:
|
|
6
|
+
params: IParam[];
|
|
7
7
|
start(projectPath: string, options: any): void;
|
|
8
8
|
}
|
|
9
9
|
export default XtsStarter;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* 连接类型
|
|
5
|
+
*/
|
|
6
|
+
var LinkMode;
|
|
7
|
+
(function (LinkMode) {
|
|
8
|
+
LinkMode[LinkMode["NULL"] = 0] = "NULL";
|
|
9
|
+
LinkMode[LinkMode["WIFI"] = 1] = "WIFI";
|
|
10
|
+
LinkMode[LinkMode["ADB"] = 2] = "ADB";
|
|
11
|
+
})(LinkMode || (LinkMode = {}));
|
|
12
|
+
exports.default = LinkMode;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { IJavascriptCompileOption } from '@aiot-toolkit/aiotpack';
|
|
2
|
+
import { IRouteItem, IRouter } from '@aiot-toolkit/shared-utils';
|
|
3
|
+
interface IPackageRouter {
|
|
4
|
+
projectPath: string;
|
|
5
|
+
options: IJavascriptCompileOption;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* 打包相关的路由配置
|
|
9
|
+
* 用于开发机的httpServer
|
|
10
|
+
*/
|
|
11
|
+
declare class PackageRouter implements IRouter<IPackageRouter> {
|
|
12
|
+
readonly param: IPackageRouter;
|
|
13
|
+
constructor(param: IPackageRouter);
|
|
14
|
+
routeList: IRouteItem<any, {}>[];
|
|
15
|
+
/**
|
|
16
|
+
* 下载 rpk 包
|
|
17
|
+
* @param ctx
|
|
18
|
+
* @param next
|
|
19
|
+
*/
|
|
20
|
+
private handlerDownLoadRpk;
|
|
21
|
+
private handlerQrCode;
|
|
22
|
+
/**
|
|
23
|
+
* 记录设备信息
|
|
24
|
+
*
|
|
25
|
+
* 获取请求的 ip 和端口,记录到本机中,最多记录5个
|
|
26
|
+
*
|
|
27
|
+
* 文件内容的格式为:
|
|
28
|
+
* ```
|
|
29
|
+
* records: {
|
|
30
|
+
* "快应用项目路径":[
|
|
31
|
+
* {ip:"", port:""},
|
|
32
|
+
* {ip:"", port:""},
|
|
33
|
+
* {ip:"", port:""}
|
|
34
|
+
* ]
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
*
|
|
39
|
+
* 此记录的作用是:当重新打包后,获取已连接的设备列表
|
|
40
|
+
* @param ctx
|
|
41
|
+
* @param next
|
|
42
|
+
*/
|
|
43
|
+
private recordDevice;
|
|
44
|
+
private getClientFromRequest;
|
|
45
|
+
/**
|
|
46
|
+
* http 请求中,用于返回下载文件
|
|
47
|
+
*
|
|
48
|
+
* 调用此方法后,仍需调用 next()
|
|
49
|
+
*
|
|
50
|
+
* @param filePath 文件绝对路径
|
|
51
|
+
* @param ctx 请求上下文
|
|
52
|
+
*/
|
|
53
|
+
private download;
|
|
54
|
+
}
|
|
55
|
+
export default PackageRouter;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const shared_utils_1 = require("@aiot-toolkit/shared-utils");
|
|
16
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
17
|
+
const path_1 = __importDefault(require("path"));
|
|
18
|
+
const qr_image_1 = __importDefault(require("qr-image"));
|
|
19
|
+
const LinkMode_1 = __importDefault(require("./LinkMode"));
|
|
20
|
+
const CLIENT_PORT = 39517;
|
|
21
|
+
/**
|
|
22
|
+
* 打包相关的路由配置
|
|
23
|
+
* 用于开发机的httpServer
|
|
24
|
+
*/
|
|
25
|
+
class PackageRouter {
|
|
26
|
+
constructor(param) {
|
|
27
|
+
this.param = param;
|
|
28
|
+
this.routeList = [
|
|
29
|
+
{
|
|
30
|
+
path: '/qrcode',
|
|
31
|
+
handler: (ctx, next) => {
|
|
32
|
+
return this.handlerQrCode(ctx, next);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
path: '/bundle',
|
|
37
|
+
handler: (ctx, next) => {
|
|
38
|
+
this.recordDevice(ctx);
|
|
39
|
+
return this.handlerDownLoadRpk(ctx, next);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
];
|
|
43
|
+
/**
|
|
44
|
+
* 下载 rpk 包
|
|
45
|
+
* @param ctx
|
|
46
|
+
* @param next
|
|
47
|
+
*/
|
|
48
|
+
this.handlerDownLoadRpk = (ctx, next) => {
|
|
49
|
+
const { projectPath, options } = this.param;
|
|
50
|
+
const dist = path_1.default.join(projectPath, options.releasePath);
|
|
51
|
+
const files = fs_extra_1.default.readdirSync(dist).filter((item) => path_1.default.extname(item) === '.rpk');
|
|
52
|
+
const filePath = files.length ? path_1.default.join(dist, files[0]) : '';
|
|
53
|
+
this.download(filePath, ctx);
|
|
54
|
+
return next();
|
|
55
|
+
};
|
|
56
|
+
this.handlerQrCode = (ctx, next) => {
|
|
57
|
+
const qrText = ctx.origin;
|
|
58
|
+
const image = qr_image_1.default.image(qrText, {
|
|
59
|
+
size: 9
|
|
60
|
+
});
|
|
61
|
+
ctx.type = 'image/png';
|
|
62
|
+
ctx.body = image;
|
|
63
|
+
return next();
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* 记录设备信息
|
|
67
|
+
*
|
|
68
|
+
* 获取请求的 ip 和端口,记录到本机中,最多记录5个
|
|
69
|
+
*
|
|
70
|
+
* 文件内容的格式为:
|
|
71
|
+
* ```
|
|
72
|
+
* records: {
|
|
73
|
+
* "快应用项目路径":[
|
|
74
|
+
* {ip:"", port:""},
|
|
75
|
+
* {ip:"", port:""},
|
|
76
|
+
* {ip:"", port:""}
|
|
77
|
+
* ]
|
|
78
|
+
* }
|
|
79
|
+
* ```
|
|
80
|
+
*
|
|
81
|
+
*
|
|
82
|
+
* 此记录的作用是:当重新打包后,获取已连接的设备列表
|
|
83
|
+
* @param ctx
|
|
84
|
+
* @param next
|
|
85
|
+
*/
|
|
86
|
+
this.recordDevice = (ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
87
|
+
const clientInfo = this.getClientFromRequest(ctx);
|
|
88
|
+
if (!(clientInfo === null || clientInfo === void 0 ? void 0 : clientInfo.clientIp)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (clientInfo.linkMode === LinkMode_1.default.WIFI) {
|
|
92
|
+
const max = 5;
|
|
93
|
+
const filePath = this.param.options.clientRecordPath;
|
|
94
|
+
const projectPath = this.param.projectPath;
|
|
95
|
+
const data = {
|
|
96
|
+
sn: clientInfo.sn,
|
|
97
|
+
ip: clientInfo.clientIp,
|
|
98
|
+
port: CLIENT_PORT
|
|
99
|
+
};
|
|
100
|
+
const json = fs_extra_1.default.readJSONSync(filePath, { throws: false }) || {};
|
|
101
|
+
if (!json.records) {
|
|
102
|
+
json.records = {};
|
|
103
|
+
}
|
|
104
|
+
if (!json.records[projectPath]) {
|
|
105
|
+
json.records[projectPath] = [];
|
|
106
|
+
}
|
|
107
|
+
const deviceList = json.records[projectPath];
|
|
108
|
+
if (deviceList.findIndex((item) => item.ip === data.ip) < 0) {
|
|
109
|
+
deviceList.push(data);
|
|
110
|
+
if (deviceList.length > max) {
|
|
111
|
+
deviceList.splice(0, deviceList.length - max);
|
|
112
|
+
}
|
|
113
|
+
fs_extra_1.default.ensureDirSync(path_1.default.dirname(filePath));
|
|
114
|
+
fs_extra_1.default.writeJSONSync(filePath, json, { spaces: 2 });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
getClientFromRequest(ctx) {
|
|
120
|
+
const clientIp = shared_utils_1.NetworkUtil.getClientIp(ctx);
|
|
121
|
+
const serverIp = shared_utils_1.NetworkUtil.getIPv4IPAddress();
|
|
122
|
+
const sn = ctx.request.header['device-serial-number'];
|
|
123
|
+
let linkMode = LinkMode_1.default.NULL;
|
|
124
|
+
if (clientIp === '127.0.0.1' && sn) {
|
|
125
|
+
linkMode = LinkMode_1.default.ADB;
|
|
126
|
+
}
|
|
127
|
+
else if (clientIp !== '127.0.0.1' && clientIp !== serverIp) {
|
|
128
|
+
linkMode = LinkMode_1.default.WIFI;
|
|
129
|
+
}
|
|
130
|
+
return { clientIp, sn, linkMode };
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* http 请求中,用于返回下载文件
|
|
134
|
+
*
|
|
135
|
+
* 调用此方法后,仍需调用 next()
|
|
136
|
+
*
|
|
137
|
+
* @param filePath 文件绝对路径
|
|
138
|
+
* @param ctx 请求上下文
|
|
139
|
+
*/
|
|
140
|
+
download(filePath, ctx) {
|
|
141
|
+
if (filePath && fs_extra_1.default.existsSync(filePath)) {
|
|
142
|
+
ctx.set('Content-Type', 'application/octet-stream');
|
|
143
|
+
ctx.set('Content-Transfer-Encoding', 'binary');
|
|
144
|
+
ctx.set('Content-Disposition', `attachment; filename=${path_1.default.basename(filePath)}`);
|
|
145
|
+
ctx.body = fs_extra_1.default.createReadStream(filePath);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
ctx.throw('404', 404);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
exports.default = PackageRouter;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
.alert-warning {
|
|
2
|
+
color: #8a6d3b;
|
|
3
|
+
background-color: #fcf8e3;
|
|
4
|
+
border-color: #faebcc;
|
|
5
|
+
text-align: left;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.alert {
|
|
9
|
+
padding: 8px;
|
|
10
|
+
margin: 10px auto;
|
|
11
|
+
width: 400px;
|
|
12
|
+
min-width: 350px;
|
|
13
|
+
border: 1px solid transparent;
|
|
14
|
+
border-radius: 6px;
|
|
15
|
+
box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.alert p {
|
|
19
|
+
margin: 6px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.panel-default {
|
|
23
|
+
border-color: #ddd;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.panel {
|
|
27
|
+
margin-bottom: 20px;
|
|
28
|
+
background-color: #fff;
|
|
29
|
+
border-radius: 4px;
|
|
30
|
+
-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
|
|
31
|
+
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
|
|
32
|
+
margin: 10px auto;
|
|
33
|
+
box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15);
|
|
34
|
+
width: 400px;
|
|
35
|
+
min-width: 350px;
|
|
36
|
+
padding: 8px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.panel-default > .panel-heading {
|
|
40
|
+
color: #333;
|
|
41
|
+
border-color: #ddd;
|
|
42
|
+
text-align: left;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.panel-heading {
|
|
46
|
+
padding: 8px 15px;
|
|
47
|
+
border-bottom: 1px solid transparent;
|
|
48
|
+
border-top-left-radius: 3px;
|
|
49
|
+
border-top-right-radius: 3px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.table {
|
|
53
|
+
width: 100%;
|
|
54
|
+
max-width: 100%;
|
|
55
|
+
margin: 6px;
|
|
56
|
+
background-color: transparent;
|
|
57
|
+
border-spacing: 0;
|
|
58
|
+
border-collapse: collapse;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.table td {
|
|
62
|
+
color: black;
|
|
63
|
+
text-shadow: none;
|
|
64
|
+
text-align: left;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.table td.tname {
|
|
68
|
+
width: 70%;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
body {
|
|
72
|
+
margin: 0;
|
|
73
|
+
display: block;
|
|
74
|
+
position: absolute;
|
|
75
|
+
top: 0;
|
|
76
|
+
bottom: 0;
|
|
77
|
+
left: 0;
|
|
78
|
+
right: 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.hide {
|
|
82
|
+
display: none !important;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.qr-code-panel {
|
|
86
|
+
text-align: center;
|
|
87
|
+
display: flex;
|
|
88
|
+
flex-direction: column;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.qr-code-panel .qr-img {
|
|
92
|
+
margin: 10px 200px;
|
|
93
|
+
min-width: 350px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.bd-bg {
|
|
97
|
+
position: relative;
|
|
98
|
+
padding: 40px 0;
|
|
99
|
+
color: black;
|
|
100
|
+
text-align: center;
|
|
101
|
+
background: #eee;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.updatetip-link {
|
|
105
|
+
padding: 0 5px;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
table.tunning tr {
|
|
109
|
+
padding: 8px 15px;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.btn {
|
|
113
|
+
display: inline-block;
|
|
114
|
+
padding: 6px 12px;
|
|
115
|
+
margin-bottom: 0;
|
|
116
|
+
font-size: 14px;
|
|
117
|
+
font-weight: 400;
|
|
118
|
+
line-height: 1.42857143;
|
|
119
|
+
text-align: center;
|
|
120
|
+
white-space: nowrap;
|
|
121
|
+
vertical-align: middle;
|
|
122
|
+
-ms-touch-action: manipulation;
|
|
123
|
+
touch-action: manipulation;
|
|
124
|
+
cursor: pointer;
|
|
125
|
+
-webkit-user-select: none;
|
|
126
|
+
-moz-user-select: none;
|
|
127
|
+
-ms-user-select: none;
|
|
128
|
+
user-select: none;
|
|
129
|
+
background-image: none;
|
|
130
|
+
border: 1px solid transparent;
|
|
131
|
+
border-radius: 4px;
|
|
132
|
+
margin-left: 5px;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.btn-default {
|
|
136
|
+
color: #333;
|
|
137
|
+
background-color: #fff;
|
|
138
|
+
border-color: #ccc;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.label {
|
|
142
|
+
display: inline;
|
|
143
|
+
padding: 0.7em;
|
|
144
|
+
font-size: 75%;
|
|
145
|
+
font-weight: 700;
|
|
146
|
+
line-height: 1;
|
|
147
|
+
color: #fff;
|
|
148
|
+
text-align: center;
|
|
149
|
+
text-decoration: none;
|
|
150
|
+
white-space: nowrap;
|
|
151
|
+
vertical-align: baseline;
|
|
152
|
+
border-radius: 0.25em;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.label-info {
|
|
156
|
+
background-color: #5bc0de;
|
|
157
|
+
}
|
|
158
|
+
.block {
|
|
159
|
+
width: 420px;
|
|
160
|
+
}
|
|
161
|
+
.block .block-title {
|
|
162
|
+
font-size: 16px;
|
|
163
|
+
margin: 0.5em 0;
|
|
164
|
+
}
|
|
165
|
+
.block a {
|
|
166
|
+
color: #0055ff;
|
|
167
|
+
}
|