@lazycatcloud/lzc-cli 1.1.5 → 1.1.8
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/cmds/app.js +1 -5
- package/cmds/dev.js +11 -5
- package/lib/api.js +24 -7
- package/lib/app/index.js +93 -0
- package/lib/app/lpk_build.js +241 -0
- package/lib/app/lpk_create.js +201 -0
- package/lib/app/lpk_devshell.js +621 -0
- package/lib/app/lpk_installer.js +67 -0
- package/lib/appstore/login.js +134 -0
- package/lib/archiver.js +0 -42
- package/lib/autologin.js +82 -0
- package/lib/box/check_qemu.js +39 -16
- package/lib/box/hportal.js +7 -1
- package/lib/box/index.js +8 -7
- package/lib/box/qemu_vm_mgr.js +12 -11
- package/lib/dev.js +0 -4
- package/lib/env.js +16 -49
- package/lib/generator.js +2 -2
- package/lib/sdk.js +5 -6
- package/lib/utils.js +66 -144
- package/package.json +8 -3
- package/scripts/cli.js +91 -12
- package/template/_lazycat/debug/shell/Dockerfile +5 -3
- package/template/_lazycat/debug/shell/docker-compose.override.yml.in +2 -12
- package/template/_lazycat/debug/shell/entrypoint.sh +3 -1
- package/template/_lpk/Dockerfile.in +8 -0
- package/template/_lpk/devshell/Dockerfile +18 -0
- package/template/_lpk/devshell/build.sh +5 -0
- package/template/_lpk/devshell/entrypoint.sh +8 -0
- package/template/_lpk/devshell/sshd_config +117 -0
- package/template/_lpk/manifest.yml.in +17 -0
- package/template/_lpk/sync/Dockerfile +16 -0
- package/template/_lpk/sync/build.sh +5 -0
- package/template/_lpk/sync/entrypoint.sh +8 -0
- package/template/_lpk/sync/sshd_config +117 -0
- package/template/_lpk/sync.manifest.yml.in +3 -0
- package/template/golang/build.sh +6 -0
- package/template/golang/lzc-build.yml +52 -0
- package/template/ionic_vue3/lzc-build.yml +53 -0
- package/template/ionic_vue3/vite.config.ts +1 -1
- package/template/release/golang/build.sh +1 -1
- package/template/release/ionic_vue3/Dockerfile +1 -1
- package/template/release/ionic_vue3/build.sh +1 -3
- package/template/release/ionic_vue3/docker-compose.yml.in +0 -5
- package/template/release/vue/Dockerfile +1 -1
- package/template/release/vue/build.sh +2 -3
- package/template/release/vue/docker-compose.yml.in +0 -5
- package/template/vue/lzc-build.yml +53 -0
- package/template/vue/src/main.js +3 -14
- package/template/vue/vue.config.js +2 -1
- package/template/_lazycat/debug/shell/nginx.conf.template +0 -64
- package/template/vue/src/lzc.js +0 -110
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import fetch from "node-fetch";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import FormData from "form-data";
|
|
4
|
+
import process from "process";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import { APP_FOLDER, APP_CONFIG_FILE } from "../utils.js"
|
|
7
|
+
|
|
8
|
+
const server = "https://account.lazycat.cloud"
|
|
9
|
+
|
|
10
|
+
function genFormData(data) {
|
|
11
|
+
const form = new FormData();
|
|
12
|
+
for (const name in data) {
|
|
13
|
+
form.append(name, data[name]);
|
|
14
|
+
}
|
|
15
|
+
return form
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function join(...params) {
|
|
19
|
+
var x = path.join(...params);
|
|
20
|
+
return x;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function Login(username, password) {
|
|
24
|
+
let form = genFormData({ username, password })
|
|
25
|
+
return fetch(join(server, "/api/login/signin"), {
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers: {
|
|
28
|
+
"content-type": `multipart/form-data; boundary=${form.getBoundary()}`
|
|
29
|
+
},
|
|
30
|
+
body: form
|
|
31
|
+
}).then(async res => {
|
|
32
|
+
let bodyText = await res.text();
|
|
33
|
+
let body = JSON.parse(bodyText)
|
|
34
|
+
if (body.success) {
|
|
35
|
+
return Promise.resolve(body.data)
|
|
36
|
+
}
|
|
37
|
+
return Promise.reject(body.message)
|
|
38
|
+
}).then(data => {
|
|
39
|
+
setToken(username, password, data.token)
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function IsLogin() {
|
|
44
|
+
try {
|
|
45
|
+
let token = getToken()
|
|
46
|
+
if (!token) {
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
return fetch(join(server, "/api/user/current"), {
|
|
50
|
+
method: "GET",
|
|
51
|
+
headers: {
|
|
52
|
+
"X-User-Token": token
|
|
53
|
+
},
|
|
54
|
+
}).then(async res => {
|
|
55
|
+
let bodyText = await res.text();
|
|
56
|
+
let body = JSON.parse(bodyText)
|
|
57
|
+
if (body.success) {
|
|
58
|
+
return token
|
|
59
|
+
}
|
|
60
|
+
console.log(body.message);
|
|
61
|
+
return false
|
|
62
|
+
})
|
|
63
|
+
} catch (e) {
|
|
64
|
+
console.log(e);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return false
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function AutoLogin() {
|
|
71
|
+
try {
|
|
72
|
+
let token = await IsLogin()
|
|
73
|
+
if (!!token) {
|
|
74
|
+
return true
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log("token错误,尝试自动登录");
|
|
78
|
+
let obj = getLocalObj()
|
|
79
|
+
if (!!obj && !!obj.username && !!obj.password) {
|
|
80
|
+
let l = await Login(obj.username, obj.password)
|
|
81
|
+
return true
|
|
82
|
+
}
|
|
83
|
+
} catch (e) {
|
|
84
|
+
console.log(e);
|
|
85
|
+
}
|
|
86
|
+
removeToken()
|
|
87
|
+
throw "自动登录失败"
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const TokenFile = "user.json"
|
|
91
|
+
const encoding = "utf8"
|
|
92
|
+
function setToken(username, password, token) {
|
|
93
|
+
let configHome = join(process.cwd(), APP_FOLDER, APP_CONFIG_FILE)
|
|
94
|
+
if (!fs.existsSync(configHome)) {
|
|
95
|
+
fs.mkdirSync(configHome, { recursive: true })
|
|
96
|
+
}
|
|
97
|
+
let tokenFile = join(configHome, TokenFile)
|
|
98
|
+
let content = JSON.stringify({ username, password, token })
|
|
99
|
+
fs.writeFileSync(tokenFile, content, {
|
|
100
|
+
encoding,
|
|
101
|
+
flag: "w",
|
|
102
|
+
mode: 0o666
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function getToken() {
|
|
107
|
+
let tokenFile = join(process.cwd(), APP_FOLDER, APP_CONFIG_FILE, TokenFile)
|
|
108
|
+
let buf = fs.readFileSync(tokenFile, { encoding, flag: 'r' })
|
|
109
|
+
let obj = JSON.parse(buf)
|
|
110
|
+
if (!obj || !obj.token) {
|
|
111
|
+
throw "读取本地文件失败"
|
|
112
|
+
}
|
|
113
|
+
return obj.token
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getLocalObj() {
|
|
117
|
+
let tokenFile = join(process.cwd(), APP_FOLDER, APP_CONFIG_FILE, TokenFile)
|
|
118
|
+
let buf = fs.readFileSync(tokenFile, { encoding, flag: 'r' })
|
|
119
|
+
let obj = JSON.parse(buf)
|
|
120
|
+
if (!obj || !obj.token) {
|
|
121
|
+
throw "读取本地文件失败"
|
|
122
|
+
}
|
|
123
|
+
return obj
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function removeToken() {
|
|
127
|
+
let tokenFile = join(process.cwd(), APP_FOLDER, APP_CONFIG_FILE, TokenFile)
|
|
128
|
+
return fs.rmSync(tokenFile)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export default {
|
|
132
|
+
Login,
|
|
133
|
+
AutoLogin
|
|
134
|
+
}
|
package/lib/archiver.js
CHANGED
|
@@ -94,54 +94,12 @@ class Archiver {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
async finalize(zip = true) {
|
|
97
|
-
// await this._changeContent();
|
|
98
97
|
if (zip) {
|
|
99
98
|
return await archiveFolder(this.tmpDir);
|
|
100
99
|
} else {
|
|
101
100
|
return this.tmpDir;
|
|
102
101
|
}
|
|
103
102
|
}
|
|
104
|
-
//
|
|
105
|
-
// permissions:
|
|
106
|
-
// - lzcapis
|
|
107
|
-
// need REMOVE deprecated
|
|
108
|
-
async _changeContent() {
|
|
109
|
-
let base = new DockerCompose(path.join(this.tmpDir, "docker-compose.yml"));
|
|
110
|
-
base
|
|
111
|
-
.pipe((template) => {
|
|
112
|
-
const meta = template[META_MARK];
|
|
113
|
-
if (
|
|
114
|
-
meta &&
|
|
115
|
-
Array.isArray(meta["permissions"]) &&
|
|
116
|
-
meta["permissions"].includes("lzcapis")
|
|
117
|
-
) {
|
|
118
|
-
template[META_MARK]["ingress"].push({
|
|
119
|
-
service: "lazycat-apis-sidecar",
|
|
120
|
-
port: 8888,
|
|
121
|
-
subdomain: env.get("APP_ID"),
|
|
122
|
-
path: "/lzcapis/",
|
|
123
|
-
auth: "oidc",
|
|
124
|
-
authcallback: "/lzcapis/oidc-callback",
|
|
125
|
-
});
|
|
126
|
-
template["services"]["lazycat-apis-sidecar"] = {
|
|
127
|
-
image: "registry.lazycat.cloud/lazycat-apis-sidecar",
|
|
128
|
-
pull_policy: "always",
|
|
129
|
-
// volumes_from: ["${APP_NAME}:rw"],
|
|
130
|
-
volumes: ["lzcapis-lzcapp:/lzcapp"],
|
|
131
|
-
command: [
|
|
132
|
-
"--client-id=${LAZYCAT_AUTH_OIDC_CLIENT_ID}",
|
|
133
|
-
"--client-secret=${LAZYCAT_AUTH_OIDC_CLIENT_SECRET}",
|
|
134
|
-
"--client-url=https://${LAZYCAT_APP_ORIGIN}/lzcapis/",
|
|
135
|
-
"--issuer=${LAZYCAT_AUTH_OIDC_ISSUER_URL}",
|
|
136
|
-
"--prefix=lzcapis",
|
|
137
|
-
"--fs-root=/lzcapp/documents",
|
|
138
|
-
],
|
|
139
|
-
};
|
|
140
|
-
template["volumes"]["lzcapis-lzcapp"] = null;
|
|
141
|
-
}
|
|
142
|
-
})
|
|
143
|
-
.save();
|
|
144
|
-
}
|
|
145
103
|
}
|
|
146
104
|
|
|
147
105
|
export default Archiver;
|
package/lib/autologin.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import fetch from "node-fetch";
|
|
2
|
+
|
|
3
|
+
// autologin.js 提供一个封装好的 fetch request 请求方法,通过这个中间件,可以不用考虑
|
|
4
|
+
// auth:auto 所做的授权验证。
|
|
5
|
+
let AutoLoginToken = "";
|
|
6
|
+
let AutoLoginFailed = false;
|
|
7
|
+
|
|
8
|
+
function fetchTokenAction(url) {
|
|
9
|
+
return fetch(url).then(async (res) => {
|
|
10
|
+
const body = await res.text();
|
|
11
|
+
const token = body.match(/name=\"token\" value=\"(.*?)\"/i)[1];
|
|
12
|
+
const action = body.match(/action=\"(.*?)\"/i)[1];
|
|
13
|
+
|
|
14
|
+
return { token, action };
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function fetchTokenActionAndRedirect(dataToken, dataAction) {
|
|
19
|
+
return fetch(dataAction, {
|
|
20
|
+
method: "POST",
|
|
21
|
+
body: `token=${dataToken}`,
|
|
22
|
+
redirect: "follow",
|
|
23
|
+
headers: {
|
|
24
|
+
accept:
|
|
25
|
+
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
|
|
26
|
+
"accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
|
|
27
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
28
|
+
},
|
|
29
|
+
}).then(async (res) => {
|
|
30
|
+
let body = await res.text();
|
|
31
|
+
const token = body.match(/name=\"token\" value=\"(.*?)\"/i)[1];
|
|
32
|
+
const action = body.match(/action=\"(.*?)\"/i)[1];
|
|
33
|
+
const redirect = body.match(/name=\"redirect\" value=\"(.*?)\"/i)[1];
|
|
34
|
+
|
|
35
|
+
return { token, action, redirect };
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function request(url, options = {}) {
|
|
40
|
+
if (AutoLoginFailed) {
|
|
41
|
+
return fetch(url, options);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
await tryAuthLogin(url);
|
|
45
|
+
|
|
46
|
+
if (!AutoLoginToken) {
|
|
47
|
+
return fetch(url, options);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let headers = { cookie: `HC-Auth-Token=${AutoLoginToken}` };
|
|
51
|
+
if (options.headers) {
|
|
52
|
+
headers = Object.assign({}, options.headers, headers);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
options = Object.assign({}, options, { headers });
|
|
56
|
+
return fetch(url, options);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function tryAuthLogin(url) {
|
|
60
|
+
if (!url.startsWith("http")) {
|
|
61
|
+
url = "https://" + url;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
if (!AutoLoginToken) {
|
|
66
|
+
let _url = new URL(url);
|
|
67
|
+
let u = "sdk."+_url.host.split(".").reverse().slice(0, 3).reverse().join(".");
|
|
68
|
+
|
|
69
|
+
console.log(`登陆到 ${u}`);
|
|
70
|
+
|
|
71
|
+
let data = await fetchTokenAction(`${_url.protocol}//${u}`);
|
|
72
|
+
let data2 = await fetchTokenActionAndRedirect(data.token, data.action);
|
|
73
|
+
|
|
74
|
+
AutoLoginToken = data2.token;
|
|
75
|
+
return AutoLoginToken;
|
|
76
|
+
}
|
|
77
|
+
} catch (e) {
|
|
78
|
+
console.log(`无法登陆`);
|
|
79
|
+
AutoLoginFailed = true;
|
|
80
|
+
return "";
|
|
81
|
+
}
|
|
82
|
+
}
|
package/lib/box/check_qemu.js
CHANGED
|
@@ -1,27 +1,50 @@
|
|
|
1
1
|
// 检测当前系统是否安装 qemu 软件
|
|
2
2
|
import commandExist from "command-exists";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
|
|
6
|
+
function linuxPlatform(pkg) {
|
|
7
|
+
let r = os.release();
|
|
8
|
+
if (r.search(/arch/gi) > -1) {
|
|
9
|
+
return `sudo pacman -S ${pkg}`;
|
|
10
|
+
} else if (r.search(/debian/gi) > -1) {
|
|
11
|
+
return `sudo apt install ${pkg}`;
|
|
12
|
+
} else {
|
|
13
|
+
return ``;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function platformPackage(pkg) {
|
|
18
|
+
let cmd = "";
|
|
19
|
+
switch (os.platform()) {
|
|
20
|
+
case "darwin":
|
|
21
|
+
cmd = `brew install ${pkg}`;
|
|
22
|
+
break;
|
|
23
|
+
case "linux":
|
|
24
|
+
cmd = linuxPlatform(pkg);
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
return `${pkg}包没有发现,请先通过系统包管理器安装。\n${cmd}`;
|
|
28
|
+
}
|
|
4
29
|
|
|
5
30
|
export class CheckQemu {
|
|
6
31
|
constructor() {
|
|
7
|
-
this.commands = ["qemu-img", "qemu-system-x86_64"
|
|
32
|
+
this.commands = ["qemu-img", "qemu-system-x86_64"];
|
|
8
33
|
}
|
|
9
34
|
|
|
10
35
|
async init() {
|
|
11
|
-
let
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
});
|
|
25
|
-
return Promise.all(ps);
|
|
36
|
+
for (let cmd of this.commands) {
|
|
37
|
+
if (!commandExist.sync(cmd)) {
|
|
38
|
+
let cmdErr = chalk.red(`${cmd} 命令没有发现`);
|
|
39
|
+
let pkgTips = chalk.blue(platformPackage("qemu"));
|
|
40
|
+
let tips = `${cmdErr}
|
|
41
|
+
|
|
42
|
+
${pkgTips}
|
|
43
|
+
|
|
44
|
+
查看更多信息 https://www.qemu.org/download/
|
|
45
|
+
`;
|
|
46
|
+
throw tips;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
26
49
|
}
|
|
27
50
|
}
|
package/lib/box/hportal.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fetch from "node-fetch";
|
|
2
|
+
import logger from "loglevel";
|
|
2
3
|
|
|
3
4
|
export class HportalManager {
|
|
4
5
|
constructor(apiHost) {
|
|
@@ -58,11 +59,16 @@ export class HportalManager {
|
|
|
58
59
|
async boxs(boxName, defaultBoxName = "") {
|
|
59
60
|
let url = this.apiHost + "/admin/boxes";
|
|
60
61
|
|
|
61
|
-
let origin;
|
|
62
|
+
let origin = [];
|
|
62
63
|
try {
|
|
63
64
|
const resp = await fetch(url);
|
|
64
65
|
if (resp.status == 200) {
|
|
65
66
|
origin = await resp.json();
|
|
67
|
+
} else {
|
|
68
|
+
logger.debug(
|
|
69
|
+
`status: ${resp.status}, url: ${resp.url}, text: ${resp.statusText}`
|
|
70
|
+
);
|
|
71
|
+
return [];
|
|
66
72
|
}
|
|
67
73
|
} catch {
|
|
68
74
|
return [];
|
package/lib/box/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { contextDirname } from "../utils.js";
|
|
|
7
7
|
import env, { sdkEnv } from "../env.js";
|
|
8
8
|
import { HportalManager, showBoxInfo } from "./hportal.js";
|
|
9
9
|
import { CheckQemu } from "./check_qemu.js";
|
|
10
|
+
import logger from "loglevel";
|
|
10
11
|
|
|
11
12
|
async function initQemuVM(ensureResources = false) {
|
|
12
13
|
let defaultSchemeFile = path.join(
|
|
@@ -60,7 +61,7 @@ export function boxCommand(box) {
|
|
|
60
61
|
// 创建盒子阶段,如果出错,将直接删除所有的资源。
|
|
61
62
|
boxId = await m.createVM(answer);
|
|
62
63
|
} catch (error) {
|
|
63
|
-
|
|
64
|
+
logger.error(error);
|
|
64
65
|
await m.cleanVM(answer.boxName);
|
|
65
66
|
return;
|
|
66
67
|
}
|
|
@@ -70,15 +71,15 @@ export function boxCommand(box) {
|
|
|
70
71
|
if (!defaultBoxName) {
|
|
71
72
|
// 新创建的盒子在初始化的时候,盒子里面需要安装hc的组件,需要一点时间,
|
|
72
73
|
// 所以这里并不对sdk url进行校验是否有效
|
|
73
|
-
await sdkEnv.setDefaultBoxName(answer.boxName
|
|
74
|
+
await sdkEnv.setDefaultBoxName(answer.boxName);
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
let h = await initHportal();
|
|
77
|
-
|
|
78
|
+
logger.debug("添加盒子到 Hportal ......");
|
|
78
79
|
await h.addBox(answer.boxName);
|
|
79
|
-
|
|
80
|
+
logger.debug("正在使用管理员帐号密码登录中......");
|
|
80
81
|
await h.loginBox(boxId, answer.adminName, answer.adminPass);
|
|
81
|
-
|
|
82
|
+
logger.debug("登录成功!");
|
|
82
83
|
},
|
|
83
84
|
},
|
|
84
85
|
{
|
|
@@ -88,7 +89,7 @@ export function boxCommand(box) {
|
|
|
88
89
|
let m = await initQemuVM();
|
|
89
90
|
let boxid = await m.runVM(boxName);
|
|
90
91
|
if (boxid) {
|
|
91
|
-
|
|
92
|
+
logger.info("盒子ID: ", boxid);
|
|
92
93
|
}
|
|
93
94
|
},
|
|
94
95
|
},
|
|
@@ -127,7 +128,7 @@ export function boxCommand(box) {
|
|
|
127
128
|
|
|
128
129
|
// 过滤条件不满足
|
|
129
130
|
if (vmInfos.length == 0 && rmInfos == 0 && boxName) {
|
|
130
|
-
|
|
131
|
+
logger.info(`${boxName} 盒子不存在`);
|
|
131
132
|
let allVmInfos = await m.infoVM("", defaultBoxName);
|
|
132
133
|
let allRmInfos = await rm.boxs("", defaultBoxName);
|
|
133
134
|
showBoxInfo(allVmInfos, allRmInfos);
|
package/lib/box/qemu_vm_mgr.js
CHANGED
|
@@ -10,6 +10,7 @@ import net from "node:net";
|
|
|
10
10
|
import fetch from "node-fetch";
|
|
11
11
|
import zlib from "node:zlib";
|
|
12
12
|
import os from "node:os";
|
|
13
|
+
import logger from "loglevel";
|
|
13
14
|
|
|
14
15
|
async function getFreePort() {
|
|
15
16
|
return new Promise((resolve, reject) => {
|
|
@@ -215,7 +216,7 @@ export class QemuVM {
|
|
|
215
216
|
p.on("error", (e) => {
|
|
216
217
|
throw e;
|
|
217
218
|
});
|
|
218
|
-
|
|
219
|
+
logger.debug("启动中...");
|
|
219
220
|
|
|
220
221
|
// 需要等待 boxid 的出现
|
|
221
222
|
return new Promise((resolve, reject) => {
|
|
@@ -239,7 +240,7 @@ export class QemuVM {
|
|
|
239
240
|
clearInterval(id);
|
|
240
241
|
p.unref();
|
|
241
242
|
resolve(boxId);
|
|
242
|
-
|
|
243
|
+
logger.debug("启动成功!");
|
|
243
244
|
}
|
|
244
245
|
}, 1000);
|
|
245
246
|
|
|
@@ -260,17 +261,17 @@ export class QemuVM {
|
|
|
260
261
|
async runVM(name) {
|
|
261
262
|
let boxid = this.readBoxid(name);
|
|
262
263
|
if (!boxid) {
|
|
263
|
-
|
|
264
|
+
logger.info(`${name} 盒子不存在,请通过 lzc-cli box create 创建`);
|
|
264
265
|
return;
|
|
265
266
|
}
|
|
266
267
|
let vmDir = path.join(this.scheme.path, `vm-${name}`);
|
|
267
268
|
let pid = parseVmPID(vmDir);
|
|
268
269
|
if (pid) {
|
|
269
|
-
|
|
270
|
+
logger.info(`${name} 盒子已经启动`);
|
|
270
271
|
return boxid;
|
|
271
272
|
}
|
|
272
273
|
await this.startVM(name, vmDir);
|
|
273
|
-
|
|
274
|
+
logger.info(`${name} 盒子启动成功!`);
|
|
274
275
|
}
|
|
275
276
|
|
|
276
277
|
/**
|
|
@@ -281,7 +282,7 @@ export class QemuVM {
|
|
|
281
282
|
this.ensureVolumeDir(vmDir);
|
|
282
283
|
await this.buildDisks(vmDir);
|
|
283
284
|
let boxId = await this.startVM(boxName, vmDir);
|
|
284
|
-
|
|
285
|
+
logger.info("盒子ID: ", boxId);
|
|
285
286
|
|
|
286
287
|
await this.registerVM(boxId, {
|
|
287
288
|
boxName,
|
|
@@ -372,7 +373,7 @@ export class QemuVM {
|
|
|
372
373
|
}
|
|
373
374
|
|
|
374
375
|
async buildSystemDisk(name, diskPath, diskInfo) {
|
|
375
|
-
|
|
376
|
+
logger.debug(`构建系统盘快照:${diskPath}`);
|
|
376
377
|
let baseImage = path.join(this.scheme.path, name);
|
|
377
378
|
return spawnSync(
|
|
378
379
|
"qemu-img",
|
|
@@ -382,7 +383,7 @@ export class QemuVM {
|
|
|
382
383
|
}
|
|
383
384
|
|
|
384
385
|
async buildDataDisk(name, diskPath, diskInfo) {
|
|
385
|
-
|
|
386
|
+
logger.debug(`构建数据盘:${diskPath}`);
|
|
386
387
|
return spawnSync(
|
|
387
388
|
"qemu-img",
|
|
388
389
|
["create", "-f", "qcow2", diskPath, diskInfo.size],
|
|
@@ -459,7 +460,7 @@ export class QemuVM {
|
|
|
459
460
|
if (pid) {
|
|
460
461
|
process.kill(pid);
|
|
461
462
|
}
|
|
462
|
-
|
|
463
|
+
logger.info(`${name} 盒子已停止`);
|
|
463
464
|
}
|
|
464
465
|
|
|
465
466
|
/**
|
|
@@ -487,7 +488,7 @@ export class QemuVM {
|
|
|
487
488
|
fs.accessSync(vmDir);
|
|
488
489
|
} catch {
|
|
489
490
|
if (!silence) {
|
|
490
|
-
|
|
491
|
+
logger.warn(`${name} 盒子不存在或者该盒子为一个真实盒子`);
|
|
491
492
|
}
|
|
492
493
|
return;
|
|
493
494
|
}
|
|
@@ -508,7 +509,7 @@ export class QemuVM {
|
|
|
508
509
|
return;
|
|
509
510
|
}
|
|
510
511
|
process.kill(pid);
|
|
511
|
-
|
|
512
|
+
logger.info(`${name} 盒子已停止`);
|
|
512
513
|
});
|
|
513
514
|
}
|
|
514
515
|
|
package/lib/dev.js
CHANGED
|
@@ -13,7 +13,6 @@ import path from "path";
|
|
|
13
13
|
import env, { sdkEnv } from "./env.js";
|
|
14
14
|
import _get from "lodash.get";
|
|
15
15
|
import { execPreBuild } from "./builder.js";
|
|
16
|
-
import Key from "./key.js";
|
|
17
16
|
import chokidar from "chokidar";
|
|
18
17
|
import commandExists from "command-exists";
|
|
19
18
|
|
|
@@ -262,9 +261,6 @@ class DevModule {
|
|
|
262
261
|
let appAddr;
|
|
263
262
|
|
|
264
263
|
try {
|
|
265
|
-
// 确保 sdk 能通过公钥正常访问
|
|
266
|
-
await new Key().ensure(sdkEnv.sdkUrl);
|
|
267
|
-
|
|
268
264
|
keyFile = path.join(this.tempDir, "debug/ssh/id_ed25519");
|
|
269
265
|
appAddr = this.getAddress();
|
|
270
266
|
|
package/lib/env.js
CHANGED
|
@@ -5,14 +5,11 @@ import {
|
|
|
5
5
|
APP_CONFIG_FILE,
|
|
6
6
|
GLOBAL_CONFIG_DIR,
|
|
7
7
|
APP_FOLDER,
|
|
8
|
-
findAppIsInstalled,
|
|
9
|
-
checkSDKInstallStatus,
|
|
10
|
-
InstallSDK,
|
|
11
8
|
} from "./utils.js";
|
|
12
9
|
import { debuglog } from "util";
|
|
13
10
|
import inquirer from "inquirer";
|
|
14
11
|
import chalk from "chalk";
|
|
15
|
-
import
|
|
12
|
+
import { request } from "./autologin.js";
|
|
16
13
|
const debug = debuglog("lib/env");
|
|
17
14
|
|
|
18
15
|
const GLOBAL_CONFIG_NAME = "box-config.json";
|
|
@@ -83,7 +80,7 @@ async function checkURL(url, suffix = "") {
|
|
|
83
80
|
}, 5000);
|
|
84
81
|
|
|
85
82
|
try {
|
|
86
|
-
const resp = await
|
|
83
|
+
const resp = await request(url + suffix, { signal: controller.signal });
|
|
87
84
|
if (resp.status != 200) {
|
|
88
85
|
// 设备可以访问 (client 有正常连接), 但是sdk 这个服务无法访问
|
|
89
86
|
throw new Error(
|
|
@@ -99,9 +96,6 @@ async function checkURL(url, suffix = "") {
|
|
|
99
96
|
case "FetchError":
|
|
100
97
|
console.log(chalk.red("盒子入口地址有误,请核对后再试"));
|
|
101
98
|
process.exit();
|
|
102
|
-
case "AbortError":
|
|
103
|
-
console.log(chalk.red(`请求 ${url} 超时, 请确保懒猫云客户端已连接`));
|
|
104
|
-
process.exit();
|
|
105
99
|
default:
|
|
106
100
|
throw error;
|
|
107
101
|
}
|
|
@@ -246,30 +240,28 @@ class SDKEnv {
|
|
|
246
240
|
return "";
|
|
247
241
|
}
|
|
248
242
|
|
|
243
|
+
get sdkHostName() {
|
|
244
|
+
if (env.has("DEFAULT_BOXNAME")) {
|
|
245
|
+
return `sdk.${env.get("DEFAULT_BOXNAME")}.heiyu.space`;
|
|
246
|
+
}
|
|
247
|
+
return "";
|
|
248
|
+
}
|
|
249
|
+
|
|
249
250
|
async ensure() {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
await this.installSDK(this.sdkUrl);
|
|
257
|
-
}
|
|
258
|
-
} else {
|
|
259
|
-
await this.setDefaultBoxName();
|
|
251
|
+
try {
|
|
252
|
+
const resp = await request(`${this.sdkUrl}/api/v1/ping`);
|
|
253
|
+
if (resp.status != 200) throw new Error(chalk.red("debug.bridge服务未正常运行"));
|
|
254
|
+
} catch (e) {
|
|
255
|
+
console.log(chalk.yellow("检测到SDK尚未安装,请将盒子开启开发者模式"));
|
|
256
|
+
process.exit(-1);
|
|
260
257
|
}
|
|
261
258
|
}
|
|
262
259
|
|
|
263
|
-
async setDefaultBoxName(boxname = ""
|
|
260
|
+
async setDefaultBoxName(boxname = "") {
|
|
264
261
|
if (!boxname) {
|
|
265
262
|
boxname = await this._promptAnswers();
|
|
266
263
|
}
|
|
267
264
|
let url = this.buildSdkUrl(boxname);
|
|
268
|
-
|
|
269
|
-
if (ensureSDK) {
|
|
270
|
-
await this.installSDK(url);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
265
|
env.set({ DEFAULT_BOXNAME: boxname }, true);
|
|
274
266
|
}
|
|
275
267
|
|
|
@@ -281,31 +273,6 @@ class SDKEnv {
|
|
|
281
273
|
return await checkURL(host, "/api/v1/ping");
|
|
282
274
|
}
|
|
283
275
|
|
|
284
|
-
/**
|
|
285
|
-
* 确保sdk已安装
|
|
286
|
-
* @param need_answer {boolean} 是否需要询问用户地址
|
|
287
|
-
*/
|
|
288
|
-
async installSDK(sdkURL) {
|
|
289
|
-
// 无法直接从环境变量拿到SDK_URL,就让用户输入
|
|
290
|
-
debug(sdkURL);
|
|
291
|
-
|
|
292
|
-
let boxURL = sdkURL.replace("sdk.", "");
|
|
293
|
-
|
|
294
|
-
try {
|
|
295
|
-
if (await findAppIsInstalled(boxURL, "sdk")) {
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
console.log(chalk.yellow("检测到SDK尚未安装,开始安装SDK"));
|
|
300
|
-
await InstallSDK(boxURL);
|
|
301
|
-
console.log(chalk.green("SDK安装完成,开始部署SDK"));
|
|
302
|
-
|
|
303
|
-
await checkSDKInstallStatus(boxURL);
|
|
304
|
-
} catch (e) {
|
|
305
|
-
throw e;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
276
|
/**
|
|
310
277
|
* 提示用户回答问题
|
|
311
278
|
*
|
package/lib/generator.js
CHANGED
|
@@ -12,7 +12,7 @@ export const TemplateConfig = {
|
|
|
12
12
|
ionic_vue3: {
|
|
13
13
|
template: "ionic_vue3",
|
|
14
14
|
defaultEnvs: {
|
|
15
|
-
HTTP_SERVICE_PORT: "
|
|
15
|
+
HTTP_SERVICE_PORT: "80",
|
|
16
16
|
BUILD_CONTEXT: ".lazycat/release",
|
|
17
17
|
},
|
|
18
18
|
after: async function (name) {
|
|
@@ -43,7 +43,7 @@ export const TemplateConfig = {
|
|
|
43
43
|
vue: {
|
|
44
44
|
template: "vue",
|
|
45
45
|
defaultEnvs: {
|
|
46
|
-
HTTP_SERVICE_PORT: "
|
|
46
|
+
HTTP_SERVICE_PORT: "80",
|
|
47
47
|
BUILD_CONTEXT: ".lazycat/release",
|
|
48
48
|
},
|
|
49
49
|
after: async function (name) {
|