ework-web 0.10.2 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ework-web",
3
- "version": "0.10.2",
3
+ "version": "0.10.3",
4
4
  "type": "module",
5
5
  "description": "ework-web — standalone multi-project issue tracker. Local SQLite-backed, no external API dependency. Bun + TypeScript + SSR HTML.",
6
6
  "license": "MIT",
@@ -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,9 +95,11 @@ 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/4] installing ework-aio..."`,
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 "[2/4] writing daemon config..."`,
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}')`,
103
105
  `DAEMON_ENDPOINT_LINE=""`,
@@ -106,9 +108,9 @@ function buildSetupScript(envBlock: string, daemonPort: number): string {
106
108
  envBlock,
107
109
  `EWORK_DAEMON_ENV_EOF`,
108
110
  `echo "$DAEMON_ENDPOINT_LINE"; } > ~/.local/share/ework-aio/ework-daemon/.env`,
109
- `echo "[3/4] starting daemon..."`,
111
+ `echo "[4/5] starting daemon..."`,
110
112
  `ework-aio start daemon`,
111
- `echo "[4/4] verifying daemon is alive..."`,
113
+ `echo "[5/5] verifying daemon is alive..."`,
112
114
  `sleep 2`,
113
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)"`,
114
116
  `echo DAEMON_STARTED`,
@@ -132,7 +134,7 @@ export async function deployRemoteDaemon(opts: DeployOpts): Promise<DeployResult
132
134
  }
133
135
  const env = parseEnvFile(envContent);
134
136
  const envBlock = buildEnvBlock(env, opts);
135
- const script = buildSetupScript(envBlock, opts.daemonPort ?? 3101);
137
+ const script = buildSetupScript(envBlock, opts.daemonPort ?? 3101, opts.mysqlHost, String(env.get("WORK_DB_PORT") ?? "3306"));
136
138
 
137
139
  const args = [
138
140
  "ssh",
package/src/index.ts CHANGED
@@ -973,25 +973,31 @@ async function handle(req: Request, url: URL, ip: string, ctx: { authed: boolean
973
973
  if (targets.length === 0 || !mysqlHost) {
974
974
  return json({ ok: false, error: "至少需要一个目标 + mysqlHost" }, 400);
975
975
  }
976
- if (targets.length === 1) {
977
- const [single] = targets;
978
- if (!single) return json({ ok: false, error: "no target" }, 400);
979
- const result = await deployRemoteDaemon({ ...single, timeoutMs });
980
- return json(result, result.ok ? 200 : 422);
981
- }
982
976
 
983
977
  const { readable, writable } = new TransformStream();
984
978
  const writer = writable.getWriter();
985
979
  const enc = new TextEncoder();
986
980
  const send = (s: string) => writer.write(enc.encode(`data: ${JSON.stringify(s)}\n\n`));
987
981
 
988
- deployBatch(targets, timeoutMs, (label, chunk) => send(`[${label}] ${chunk}`))
989
- .then((results) => {
990
- const ok = [...results.values()].every((r) => r.ok);
991
- send(`\n${ok ? "ALL OK" : "SOME FAILED"}: ${[...results.entries()].map(([k, v]) => `${k}=${v.ok ? "✓" : "✗"}`).join(", ")}`);
992
- })
993
- .catch((e) => send(`ERROR: ${e instanceof Error ? e.message : String(e)}`))
994
- .finally(() => writer.close());
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
+ }
995
1001
 
996
1002
  return new Response(readable, {
997
1003
  headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" },
@@ -247,6 +247,11 @@
247
247
  if (deployBtn) deployBtn.disabled = false;
248
248
  return;
249
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
+ }
250
255
 
251
256
  deployResultEl.textContent = '';
252
257
  appendDeployLog('▶ 部署 ' + targets.length + ' 个目标(超时 ' + (timeoutMs / 1000) + 's/个)...\n\n');