ework-web 0.10.1 → 0.10.3
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/package.json +1 -1
- package/src/daemon-deploy.ts +13 -7
- package/src/index.ts +22 -13
- package/src/static/daemon-mgr.js +9 -0
package/package.json
CHANGED
package/src/daemon-deploy.ts
CHANGED
|
@@ -85,7 +85,7 @@ function buildEnvBlock(env: Map<string, string>, target: DeployTarget): string {
|
|
|
85
85
|
return lines.join("\n");
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
function buildSetupScript(envBlock: string, daemonPort: number): string {
|
|
88
|
+
function buildSetupScript(envBlock: string, daemonPort: number, mysqlHost: string, mysqlPort: string): string {
|
|
89
89
|
return [
|
|
90
90
|
"set -e",
|
|
91
91
|
`command -v npm >/dev/null 2>&1 || { curl -fsSL https://deb.nodesource.com/setup_22.x | sudo bash -; sudo apt-get install -y nodejs; }`,
|
|
@@ -95,16 +95,22 @@ function buildSetupScript(envBlock: string, daemonPort: number): string {
|
|
|
95
95
|
`mkdir -p "$HOME/.local/lib"`,
|
|
96
96
|
`npm config set prefix "$HOME/.local/lib"`,
|
|
97
97
|
`grep -q '.local/lib/bin' "$HOME/.bashrc" 2>/dev/null || echo 'export PATH="$HOME/.local/lib/bin:$HOME/.bun/bin:$PATH"' >> "$HOME/.bashrc"`,
|
|
98
|
-
`echo "[1/
|
|
98
|
+
`echo "[1/5] checking MySQL connectivity to ${mysqlHost}:${mysqlPort}..."`,
|
|
99
|
+
`timeout 5 bash -c "echo > /dev/tcp/${mysqlHost}/${mysqlPort}" 2>/dev/null && echo "MySQL port reachable" || { echo "MySQL UNREACHABLE at ${mysqlHost}:${mysqlPort} — daemon will fail to connect"; exit 1; }`,
|
|
100
|
+
`echo "[2/5] installing ework-aio..."`,
|
|
99
101
|
`npm install -g ework-aio`,
|
|
100
|
-
`echo "[
|
|
102
|
+
`echo "[3/5] writing daemon config..."`,
|
|
101
103
|
`mkdir -p ~/.local/share/ework-aio/ework-daemon`,
|
|
102
|
-
`
|
|
104
|
+
`REMOTE_IP=$(hostname -I 2>/dev/null | awk '{print $1}')`,
|
|
105
|
+
`DAEMON_ENDPOINT_LINE=""`,
|
|
106
|
+
`if [ -n "$REMOTE_IP" ]; then DAEMON_ENDPOINT_LINE="DAEMON_ENDPOINT=$REMOTE_IP:${String(daemonPort)}"; fi`,
|
|
107
|
+
`{ cat <<'EWORK_DAEMON_ENV_EOF'`,
|
|
103
108
|
envBlock,
|
|
104
109
|
`EWORK_DAEMON_ENV_EOF`,
|
|
105
|
-
`echo "
|
|
110
|
+
`echo "$DAEMON_ENDPOINT_LINE"; } > ~/.local/share/ework-aio/ework-daemon/.env`,
|
|
111
|
+
`echo "[4/5] starting daemon..."`,
|
|
106
112
|
`ework-aio start daemon`,
|
|
107
|
-
`echo "[
|
|
113
|
+
`echo "[5/5] verifying daemon is alive..."`,
|
|
108
114
|
`sleep 2`,
|
|
109
115
|
`curl -sf --max-time 3 http://127.0.0.1:${String(daemonPort)}/api/status >/dev/null 2>&1 && echo "DAEMON_ALIVE" || echo "DAEMON_WARNING: status check failed (may need a moment to boot)"`,
|
|
110
116
|
`echo DAEMON_STARTED`,
|
|
@@ -128,7 +134,7 @@ export async function deployRemoteDaemon(opts: DeployOpts): Promise<DeployResult
|
|
|
128
134
|
}
|
|
129
135
|
const env = parseEnvFile(envContent);
|
|
130
136
|
const envBlock = buildEnvBlock(env, opts);
|
|
131
|
-
const script = buildSetupScript(envBlock, opts.daemonPort ?? 3101);
|
|
137
|
+
const script = buildSetupScript(envBlock, opts.daemonPort ?? 3101, opts.mysqlHost, String(env.get("WORK_DB_PORT") ?? "3306"));
|
|
132
138
|
|
|
133
139
|
const args = [
|
|
134
140
|
"ssh",
|
package/src/index.ts
CHANGED
|
@@ -479,6 +479,9 @@ async function handle(req: Request, url: URL, ip: string, ctx: { authed: boolean
|
|
|
479
479
|
|
|
480
480
|
const auth = await checkAuth(req, cfg, ip);
|
|
481
481
|
if (!auth.ok) {
|
|
482
|
+
if (url.pathname.startsWith("/api/")) {
|
|
483
|
+
return json({ error: "authentication required" }, 401);
|
|
484
|
+
}
|
|
482
485
|
const next = sanitizeNext(url.pathname + url.search);
|
|
483
486
|
return Response.redirect(`${url.origin}/login?next=${encodeURIComponent(next)}`, 302);
|
|
484
487
|
}
|
|
@@ -970,25 +973,31 @@ async function handle(req: Request, url: URL, ip: string, ctx: { authed: boolean
|
|
|
970
973
|
if (targets.length === 0 || !mysqlHost) {
|
|
971
974
|
return json({ ok: false, error: "至少需要一个目标 + mysqlHost" }, 400);
|
|
972
975
|
}
|
|
973
|
-
if (targets.length === 1) {
|
|
974
|
-
const [single] = targets;
|
|
975
|
-
if (!single) return json({ ok: false, error: "no target" }, 400);
|
|
976
|
-
const result = await deployRemoteDaemon({ ...single, timeoutMs });
|
|
977
|
-
return json(result, result.ok ? 200 : 422);
|
|
978
|
-
}
|
|
979
976
|
|
|
980
977
|
const { readable, writable } = new TransformStream();
|
|
981
978
|
const writer = writable.getWriter();
|
|
982
979
|
const enc = new TextEncoder();
|
|
983
980
|
const send = (s: string) => writer.write(enc.encode(`data: ${JSON.stringify(s)}\n\n`));
|
|
984
981
|
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
982
|
+
if (targets.length === 1) {
|
|
983
|
+
const [single] = targets;
|
|
984
|
+
send(`▶ 部署到 ${single!.sshHost}:${single!.daemonPort ?? 3101}(超时 ${timeoutMs / 1000}s)...\n\n`);
|
|
985
|
+
deployRemoteDaemon({ ...single!, timeoutMs, onOutput: (chunk) => send(chunk) })
|
|
986
|
+
.then((result) => {
|
|
987
|
+
send(`\n${result.ok ? "✓ 成功" : "✗ 失败"}: ${result.ok ? "daemon 已部署" : (result.error || "未知错误")}\n`);
|
|
988
|
+
if (result.output) send(result.output);
|
|
989
|
+
})
|
|
990
|
+
.catch((e) => send(`\n✗ 异常: ${e instanceof Error ? e.message : String(e)}\n`))
|
|
991
|
+
.finally(() => writer.close());
|
|
992
|
+
} else {
|
|
993
|
+
deployBatch(targets, timeoutMs, (label, chunk) => send(`[${label}] ${chunk}`))
|
|
994
|
+
.then((results) => {
|
|
995
|
+
const ok = [...results.values()].every((r) => r.ok);
|
|
996
|
+
send(`\n${ok ? "ALL OK" : "SOME FAILED"}: ${[...results.entries()].map(([k, v]) => `${k}=${v.ok ? "✓" : "✗"}`).join(", ")}`);
|
|
997
|
+
})
|
|
998
|
+
.catch((e) => send(`ERROR: ${e instanceof Error ? e.message : String(e)}`))
|
|
999
|
+
.finally(() => writer.close());
|
|
1000
|
+
}
|
|
992
1001
|
|
|
993
1002
|
return new Response(readable, {
|
|
994
1003
|
headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" },
|
package/src/static/daemon-mgr.js
CHANGED
|
@@ -159,6 +159,10 @@
|
|
|
159
159
|
setResult('正在重启 daemon #' + id + '…', 'loading');
|
|
160
160
|
try {
|
|
161
161
|
var res = await fetch('/api/daemons/' + id + '/restart', { method: 'POST' });
|
|
162
|
+
if (res.status === 401) {
|
|
163
|
+
setResult('✗ 登录已过期,请刷新页面重新登录', 'err');
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
162
166
|
var data = await res.json();
|
|
163
167
|
if (data.ok) {
|
|
164
168
|
setResult('✓ daemon #' + id + ' 已重新激活', 'ok');
|
|
@@ -243,6 +247,11 @@
|
|
|
243
247
|
if (deployBtn) deployBtn.disabled = false;
|
|
244
248
|
return;
|
|
245
249
|
}
|
|
250
|
+
if (/^(127\.|localhost$|0\.0\.0\.0$)/.test(mysqlHost)) {
|
|
251
|
+
setDeployResult('✗ MySQL 主机必须是远程机器可达的地址(不能是 ' + mysqlHost + ')。请使用 LAN IP,如 192.168.x.x', 'err');
|
|
252
|
+
if (deployBtn) deployBtn.disabled = false;
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
246
255
|
|
|
247
256
|
deployResultEl.textContent = '';
|
|
248
257
|
appendDeployLog('▶ 部署 ' + targets.length + ' 个目标(超时 ' + (timeoutMs / 1000) + 's/个)...\n\n');
|