openship 0.6.0 → 0.6.5
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 +7 -2
- package/dist/{bare-FSUAM5V5.js → bare-XQIPDO4R.js} +10 -9
- package/dist/{chunk-PSZSKEPA.js → chunk-3ZAGC5NT.js} +64 -17
- package/dist/{chunk-6ODUBKUB.js → chunk-5OUWIPOB.js} +69 -7
- package/dist/chunk-6HOA5JT7.js +124 -0
- package/dist/{chunk-TWJCLD4W.js → chunk-6JELW75A.js} +4 -4
- package/dist/{chunk-GUQ3QCJ7.js → chunk-7334NSJL.js} +399 -369
- package/dist/chunk-7S4USQYF.js +1753 -0
- package/dist/{chunk-3LDWWY7U.js → chunk-A7EWM2KA.js} +41 -7
- package/dist/chunk-ELGRMZOW.js +25 -0
- package/dist/chunk-EZPPTWJQ.js +292 -0
- package/dist/{chunk-IXM5245F.js → chunk-GRTXMUHA.js} +553 -538
- package/dist/chunk-HPXP6LXZ.js +428 -0
- package/dist/{chunk-OTLSJYAC.js → chunk-LEYSL46B.js} +2135 -85
- package/dist/{chunk-EMXI4UCU.js → chunk-LFCSC4IU.js} +29 -10
- package/dist/{chunk-JIMUYEKN.js → chunk-MSZUCNPV.js} +1 -1
- package/dist/{chunk-OGY63DGD.js → chunk-PZRRL5PC.js} +71 -28
- package/dist/{chunk-VE2YOK4I.js → chunk-QLX7NBEA.js} +1218 -588
- package/dist/chunk-RFWJE7ZN.js +68 -0
- package/dist/{chunk-VDDMADOF.js → chunk-TM2IQPYU.js} +16 -2
- package/dist/{chunk-3FYIXVX5.js → chunk-VDESJSG6.js} +451 -106
- package/dist/{chunk-B3CE2OMR.js → chunk-VHMOQL5Y.js} +276 -50
- package/dist/{chunk-QEH4QUBX.js → chunk-VLQ3UIVO.js} +393 -53
- package/dist/{chunk-VZ2ZHPS2.js → chunk-WE7BXW6L.js} +3 -9
- package/dist/chunk-WFRXYWK2.js +444 -0
- package/dist/chunk-XRNRT37W.js +316 -0
- package/dist/{chunk-EP2TNALB.js → chunk-XVOBL7BC.js} +31 -8
- package/dist/cloud-3DODOWTR.js +36 -0
- package/dist/{detect-ALF4GXRX.js → detect-P4CIQBRH.js} +3 -3
- package/dist/{docker-QECQJ5P4.js → docker-3K2NEBD6.js} +15 -8
- package/dist/{docker-edge-executor-62N64MGS.js → docker-edge-executor-YZZ6P7IC.js} +5 -5
- package/dist/{edge-import-CQWHN2Q3.js → edge-import-MCJNVTZ2.js} +26 -23
- package/dist/edge-preflight-ES6FH4JK.js +42 -0
- package/dist/{ensure-container-edge-MJVXJQG5.js → ensure-container-edge-YPITNH45.js} +9 -8
- package/dist/{executor-JELARVKC.js → executor-DZGX55K2.js} +12 -5
- package/dist/host-channel-preflight-MUXE3KZU.js +49 -0
- package/dist/index.js +3668 -4063
- package/dist/{nginx-AXGNWRKZ.js → nginx-NSAHWGJV.js} +11 -5
- package/dist/node-bootstrap.js +241 -0
- package/dist/node-entry.js +52 -1
- package/dist/{openresty-lua-IE4P5YQX.js → openresty-lua-DR4D3EZ6.js} +6 -2
- package/dist/privilege-YTVI4SKF.js +19 -0
- package/dist/server/engine/samples/fail2ban/jail.d/sshd.local +4 -1
- package/dist/server/index.js +21501 -13137
- package/dist/server/lua/geo_country.lua +4 -3
- package/dist/server/lua/pipe_stream.lua +52 -39
- package/dist/server/lua/site_logger.lua +22 -24
- package/dist/server/migrations/0098_instance_product_mode.sql +12 -0
- package/dist/server/migrations/0099_mail_webmail_project.sql +24 -0
- package/dist/server/migrations/0100_server_ssh_private_key.sql +11 -0
- package/dist/server/migrations/0101_mail_resume_step.sql +12 -0
- package/dist/server/migrations/0102_project_workload_axes.sql +19 -0
- package/dist/server/migrations/0103_dns_credential.sql +41 -0
- package/dist/server/migrations/0104_instance_host_control.sql +15 -0
- package/dist/server/migrations/meta/_journal.json +49 -0
- package/dist/{setup-FA4PW4NU.js → setup-WUUZURHS.js} +10 -9
- package/dist/up-XEO3SR6Z.js +58 -0
- package/package.json +4 -2
- package/dist/chunk-IW2UKBVF.js +0 -144
- package/dist/chunk-ZCFVHA25.js +0 -89
- package/dist/cloud-E5M6VMCG.js +0 -31
- package/dist/server/lua/pipe_log.lua +0 -77
- /package/dist/{dist-es-JGBPABJI.js → dist-es-ZP5QDSJ3.js} +0 -0
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
":" //# ; exec "$(command -v node || command -v bun)" "$0" "$@"
|
|
3
|
+
import { createRequire as __ospCreateRequire } from "node:module";
|
|
4
|
+
const require = __ospCreateRequire(import.meta.url);
|
|
5
|
+
import {
|
|
6
|
+
OS_DIR
|
|
7
|
+
} from "./chunk-ELGRMZOW.js";
|
|
8
|
+
import {
|
|
9
|
+
beginEdgeTakeover,
|
|
10
|
+
collectProxyCerts,
|
|
11
|
+
completeEdgeTakeover,
|
|
12
|
+
copyStaticRootIntoEdge,
|
|
13
|
+
detectInstalledProxy,
|
|
14
|
+
edgeProxy,
|
|
15
|
+
edgeProxyFor,
|
|
16
|
+
foreignProxyOnEdge,
|
|
17
|
+
importSites,
|
|
18
|
+
recoverInterruptedTakeover,
|
|
19
|
+
rollbackEdgeTakeover,
|
|
20
|
+
scanImportableSites,
|
|
21
|
+
unreachableStaticRoots
|
|
22
|
+
} from "./chunk-7334NSJL.js";
|
|
23
|
+
import {
|
|
24
|
+
LocalExecutor
|
|
25
|
+
} from "./chunk-6FH4AXVY.js";
|
|
26
|
+
import {
|
|
27
|
+
ourEdgeContainerRunning
|
|
28
|
+
} from "./chunk-WE7BXW6L.js";
|
|
29
|
+
|
|
30
|
+
// src/lib/edge-preflight.ts
|
|
31
|
+
import { spawnSync } from "child_process";
|
|
32
|
+
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
33
|
+
import { join } from "path";
|
|
34
|
+
import chalk from "chalk";
|
|
35
|
+
import { isCancel, log, note, select } from "@clack/prompts";
|
|
36
|
+
function privilegeClause(privilegeDegraded) {
|
|
37
|
+
if (!privilegeDegraded) return "";
|
|
38
|
+
return " \u2014 and Openship could not run the stop as root on this host (see the privilege warning above), so stopping it was almost certainly refused rather than too slow. Retrying as this user will fail the same way: re-run as root, or as a user with passwordless sudo";
|
|
39
|
+
}
|
|
40
|
+
async function planAndApplyHostEdge(opts, overrides = {}) {
|
|
41
|
+
const deps = { ...defaultDeps(), ...overrides };
|
|
42
|
+
if (deps.platform !== "linux") return { proceed: true };
|
|
43
|
+
const executor = deps.makeExecutor();
|
|
44
|
+
await deps.recoverInterruptedTakeover(
|
|
45
|
+
executor,
|
|
46
|
+
(m, l) => deps.warn(l === "info" ? m : chalk.yellow(m)),
|
|
47
|
+
() => deps.ourEdgeContainerRunning(executor)
|
|
48
|
+
).catch(() => {
|
|
49
|
+
});
|
|
50
|
+
const { status, blocked, owner } = await deps.foreignProxyOnEdge(executor);
|
|
51
|
+
if (!blocked) {
|
|
52
|
+
return await offerStoppedProxyImport(executor, deps);
|
|
53
|
+
}
|
|
54
|
+
const { sites, warnings } = await deps.importSites(executor, status);
|
|
55
|
+
const action = await resolveAction(opts.edge, { status, owner, sites, warnings }, deps);
|
|
56
|
+
if (action === "cancel") return { proceed: false };
|
|
57
|
+
const certPems = action === "migrate" ? await deps.collectCerts(executor, sites, { status }) : void 0;
|
|
58
|
+
const staticRootOverrides = action === "migrate" ? await remediateUnreachableStaticRoots({
|
|
59
|
+
unreachable: unreachableStaticRoots(sites, { containerEdge: true }),
|
|
60
|
+
executor,
|
|
61
|
+
interactive: deps.interactive
|
|
62
|
+
}) : void 0;
|
|
63
|
+
const freed = await deps.beginEdgeTakeover(
|
|
64
|
+
executor,
|
|
65
|
+
status,
|
|
66
|
+
(m, l) => deps.warn(l === "info" ? m : chalk.yellow(m))
|
|
67
|
+
);
|
|
68
|
+
if (!freed.freed) {
|
|
69
|
+
const plural = freed.stillBound.length > 1;
|
|
70
|
+
const restored = await deps.rollbackHostEdge();
|
|
71
|
+
return {
|
|
72
|
+
proceed: false,
|
|
73
|
+
blockedBy: `port${plural ? "s" : ""} ${freed.stillBound.join(" and ")} ${plural ? "are" : "is"} still in use after stopping ${owner}` + (restored ? " (the previous proxy has been restored)" : "") + privilegeClause(freed.privilegeDegraded)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return action === "migrate" ? { proceed: true, action, sites, certPems, staticRootOverrides } : { proceed: true, action };
|
|
77
|
+
}
|
|
78
|
+
async function previewHostEdge(overrides = {}) {
|
|
79
|
+
const deps = { ...defaultDeps(), ...overrides };
|
|
80
|
+
const empty = { owner: null, blocked: false, sites: [], warnings: [] };
|
|
81
|
+
if (deps.platform !== "linux") return empty;
|
|
82
|
+
try {
|
|
83
|
+
const executor = deps.makeExecutor();
|
|
84
|
+
const { status, blocked, owner } = await deps.foreignProxyOnEdge(executor);
|
|
85
|
+
if (!blocked) return { ...empty, owner: owner || null };
|
|
86
|
+
const { sites, warnings } = await deps.importSites(executor, status);
|
|
87
|
+
return { owner: owner || null, blocked: true, sites, warnings };
|
|
88
|
+
} catch {
|
|
89
|
+
return empty;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async function rollbackHostEdge() {
|
|
93
|
+
if (process.platform !== "linux") return false;
|
|
94
|
+
try {
|
|
95
|
+
return await rollbackEdgeTakeover(
|
|
96
|
+
new LocalExecutor(),
|
|
97
|
+
(entry) => console.log(chalk.yellow(` ${entry.message}`))
|
|
98
|
+
);
|
|
99
|
+
} catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
var IMPORTED_MARKER = join(OS_DIR, "imported-proxy");
|
|
104
|
+
function edgeServedHostnames() {
|
|
105
|
+
const served = /* @__PURE__ */ new Set();
|
|
106
|
+
const r = spawnSync(
|
|
107
|
+
"docker",
|
|
108
|
+
[
|
|
109
|
+
"exec",
|
|
110
|
+
"openship-edge",
|
|
111
|
+
"sh",
|
|
112
|
+
"-c",
|
|
113
|
+
"cat /usr/local/openresty/nginx/conf/sites-enabled/*.conf 2>/dev/null || true"
|
|
114
|
+
],
|
|
115
|
+
{ encoding: "utf8" }
|
|
116
|
+
);
|
|
117
|
+
if (r.status !== 0 || !r.stdout) return served;
|
|
118
|
+
for (const m of r.stdout.matchAll(/server_name\s+([^;]+);/g)) {
|
|
119
|
+
for (const host of m[1].trim().split(/\s+/)) {
|
|
120
|
+
if (host && host !== "_") served.add(host.toLowerCase());
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return served;
|
|
124
|
+
}
|
|
125
|
+
function markStoppedProxyImported() {
|
|
126
|
+
try {
|
|
127
|
+
mkdirSync(OS_DIR, { recursive: true, mode: 448 });
|
|
128
|
+
writeFileSync(IMPORTED_MARKER, (/* @__PURE__ */ new Date()).toISOString(), { mode: 384 });
|
|
129
|
+
} catch {
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function offerStoppedProxyImport(executor, deps) {
|
|
133
|
+
if (!deps.interactive || existsSync(IMPORTED_MARKER)) return { proceed: true };
|
|
134
|
+
let proxy = null;
|
|
135
|
+
try {
|
|
136
|
+
proxy = await deps.detectInstalledProxy(executor);
|
|
137
|
+
} catch {
|
|
138
|
+
return { proceed: true };
|
|
139
|
+
}
|
|
140
|
+
if (!proxy) return { proceed: true };
|
|
141
|
+
let sites = [];
|
|
142
|
+
let warnings = [];
|
|
143
|
+
try {
|
|
144
|
+
const scan = await deps.scanProxySites(executor, proxy);
|
|
145
|
+
sites = scan.sites;
|
|
146
|
+
warnings = scan.warnings;
|
|
147
|
+
} catch {
|
|
148
|
+
return { proceed: true };
|
|
149
|
+
}
|
|
150
|
+
if (sites.length === 0) return { proceed: true };
|
|
151
|
+
const served = deps.edgeServedHostnames();
|
|
152
|
+
const pending = sites.filter(
|
|
153
|
+
(site) => !site.serverNames.every((h) => served.has(h.trim().toLowerCase()))
|
|
154
|
+
);
|
|
155
|
+
if (pending.length === 0) return { proceed: true };
|
|
156
|
+
sites = pending;
|
|
157
|
+
deps.render({ owner: `${proxy} (stopped)`, sites, warnings });
|
|
158
|
+
if (!await deps.confirmStoppedImport({ proxy, count: sites.length })) {
|
|
159
|
+
return { proceed: true };
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
proceed: true,
|
|
163
|
+
action: "migrate",
|
|
164
|
+
sites,
|
|
165
|
+
certPems: await deps.collectCerts(executor, sites, { proxy })
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function dockerState(name) {
|
|
169
|
+
const r = spawnSync(
|
|
170
|
+
"docker",
|
|
171
|
+
["ps", "-a", "--filter", `name=^${name}$`, "--format", "{{.State}}"],
|
|
172
|
+
{ encoding: "utf8" }
|
|
173
|
+
);
|
|
174
|
+
const state = (r.stdout ?? "").trim();
|
|
175
|
+
return { exists: state.length > 0, running: state === "running" };
|
|
176
|
+
}
|
|
177
|
+
async function diagnoseEdge() {
|
|
178
|
+
const empty = {
|
|
179
|
+
containerExists: false,
|
|
180
|
+
containerRunning: false,
|
|
181
|
+
occupant: null,
|
|
182
|
+
hostProxySquatting: false,
|
|
183
|
+
sites: [],
|
|
184
|
+
healthy: false
|
|
185
|
+
};
|
|
186
|
+
if (process.platform !== "linux") return empty;
|
|
187
|
+
const { exists, running } = dockerState("openship-edge");
|
|
188
|
+
const executor = new LocalExecutor();
|
|
189
|
+
let status;
|
|
190
|
+
let owner = null;
|
|
191
|
+
try {
|
|
192
|
+
const probe = await foreignProxyOnEdge(executor);
|
|
193
|
+
status = probe.status;
|
|
194
|
+
owner = probe.owner || null;
|
|
195
|
+
} catch {
|
|
196
|
+
return { ...empty, containerExists: exists, containerRunning: running };
|
|
197
|
+
}
|
|
198
|
+
const foreignOccupants = status.occupants.filter((o) => o.containerName !== "openship-edge");
|
|
199
|
+
const hostProxySquatting = !running && foreignOccupants.some((o) => !o.containerName);
|
|
200
|
+
let sites = [];
|
|
201
|
+
if (status.occupants.length > 0) {
|
|
202
|
+
try {
|
|
203
|
+
sites = (await importSites(executor, status)).sites;
|
|
204
|
+
} catch {
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
containerExists: exists,
|
|
209
|
+
containerRunning: running,
|
|
210
|
+
occupant: foreignOccupants.length > 0 ? owner : null,
|
|
211
|
+
hostProxySquatting,
|
|
212
|
+
sites,
|
|
213
|
+
healthy: running && foreignOccupants.length === 0
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
async function repairEdgeConflict(mode, apiPort, onLog) {
|
|
217
|
+
if (process.platform !== "linux") {
|
|
218
|
+
return { ok: false, registered: [], detail: "the containerized edge is Linux-only" };
|
|
219
|
+
}
|
|
220
|
+
const executor = new LocalExecutor();
|
|
221
|
+
const { status, owner } = await foreignProxyOnEdge(executor);
|
|
222
|
+
if (status.occupants.length === 0) {
|
|
223
|
+
spawnSync("docker", ["restart", "openship-edge"], { stdio: "ignore" });
|
|
224
|
+
return { ok: true, registered: [], detail: "ports were already free \u2014 restarted the edge" };
|
|
225
|
+
}
|
|
226
|
+
const scan = mode === "migrate" ? await importSites(executor, status) : { sites: [], warnings: [] };
|
|
227
|
+
const certPems = mode === "migrate" ? await collectCertsFromProxy(executor, scan.sites, { status }) : void 0;
|
|
228
|
+
const freed = await beginEdgeTakeover(
|
|
229
|
+
executor,
|
|
230
|
+
status,
|
|
231
|
+
(entry) => onLog(entry.message, entry.level)
|
|
232
|
+
);
|
|
233
|
+
if (!freed.freed) {
|
|
234
|
+
const restored = await rollbackHostEdge();
|
|
235
|
+
return {
|
|
236
|
+
ok: false,
|
|
237
|
+
registered: [],
|
|
238
|
+
detail: `port${freed.stillBound.length > 1 ? "s" : ""} ${freed.stillBound.join(" and ")} still in use after stopping ${owner || "the existing proxy"}` + (restored ? " \u2014 the previous proxy has been restored" : " \u2014 and the restore did NOT bring it back") + privilegeClause(freed.privilegeDegraded)
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
spawnSync("docker", ["restart", "openship-edge"], { stdio: "ignore" });
|
|
242
|
+
if (mode === "migrate" && scan.sites.length > 0) {
|
|
243
|
+
const { importMigratedSites } = await import("./edge-import-MCJNVTZ2.js");
|
|
244
|
+
const outcome = await importMigratedSites(apiPort, scan.sites, certPems);
|
|
245
|
+
if (outcome.registered.length === 0) {
|
|
246
|
+
return {
|
|
247
|
+
ok: false,
|
|
248
|
+
registered: [],
|
|
249
|
+
detail: `freed the ports but registered none of the ${scan.sites.length} site(s) \u2014 journal kept so the old proxy can be restored`
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
await completeHostEdge();
|
|
253
|
+
return {
|
|
254
|
+
ok: true,
|
|
255
|
+
registered: outcome.registered,
|
|
256
|
+
detail: `migrated ${outcome.registered.length}/${scan.sites.length} site(s) and restarted the edge`
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
await completeHostEdge();
|
|
260
|
+
return { ok: true, registered: [], detail: "freed :80/:443 and restarted the edge" };
|
|
261
|
+
}
|
|
262
|
+
async function completeHostEdge() {
|
|
263
|
+
if (process.platform !== "linux") return;
|
|
264
|
+
try {
|
|
265
|
+
await completeEdgeTakeover(new LocalExecutor());
|
|
266
|
+
} catch {
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
async function resolveAction(flag, ctx, deps) {
|
|
270
|
+
if (flag) return flag;
|
|
271
|
+
if (!deps.interactive) {
|
|
272
|
+
deps.warn(
|
|
273
|
+
`An existing proxy (${ctx.owner}) holds :80/:443 and this is a non-interactive run. Re-run with --edge=migrate (import its sites), --edge=takeover (stop it, sites drop), or --edge=cancel. Leaving it running.`
|
|
274
|
+
);
|
|
275
|
+
return "cancel";
|
|
276
|
+
}
|
|
277
|
+
deps.render({ owner: ctx.owner, sites: ctx.sites, warnings: ctx.warnings });
|
|
278
|
+
return deps.confirm({
|
|
279
|
+
owner: ctx.owner,
|
|
280
|
+
known: ctx.status.classification === "known",
|
|
281
|
+
importable: ctx.sites.length
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
async function collectCertsFromProxy(executor, sites, source) {
|
|
285
|
+
const api = source.proxy ? edgeProxyFor(executor, source.proxy) : await edgeProxy(executor, source.status ? { status: source.status } : {});
|
|
286
|
+
if (!api) return {};
|
|
287
|
+
const { certPems, warnings } = await collectProxyCerts(api, sites);
|
|
288
|
+
for (const w of warnings) console.log(chalk.yellow(` ${w}`));
|
|
289
|
+
return certPems;
|
|
290
|
+
}
|
|
291
|
+
function renderEdgeConflict(info) {
|
|
292
|
+
const { owner, sites, warnings } = info;
|
|
293
|
+
if (sites.length > 0) {
|
|
294
|
+
const lines = sites.map((st) => {
|
|
295
|
+
const host = (st.serverNames ?? []).join(", ") || "(no server_name)";
|
|
296
|
+
const dest = st.target.kind === "static" ? `static: ${st.target.root}` : st.target.url;
|
|
297
|
+
return `${chalk.bold(host)} \u2192 ${chalk.dim(dest)}${st.ssl ? chalk.green(" [TLS]") : ""}`;
|
|
298
|
+
});
|
|
299
|
+
note(lines.join("\n"), `Detected ${sites.length} site${sites.length === 1 ? "" : "s"} on ${owner}`);
|
|
300
|
+
}
|
|
301
|
+
if (warnings.length > 0) {
|
|
302
|
+
log.warn(`${warnings.length} config item${warnings.length === 1 ? "" : "s"} won't migrate automatically:`);
|
|
303
|
+
for (const w of warnings.slice(0, 8)) log.message(chalk.dim(`\u2022 ${w}`));
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
async function confirmEdgeAction(info) {
|
|
307
|
+
const { owner, known, importable } = info;
|
|
308
|
+
const choice = await select({
|
|
309
|
+
message: known ? `An existing reverse proxy (${owner}) is serving ports 80/443.` : `Ports 80/443 are in use by ${owner}, which we couldn't identify.`,
|
|
310
|
+
options: [
|
|
311
|
+
...importable > 0 ? [
|
|
312
|
+
{
|
|
313
|
+
value: "migrate",
|
|
314
|
+
label: `Migrate ${importable} site${importable === 1 ? "" : "s"} & take over`,
|
|
315
|
+
hint: "import the existing sites into Openship's edge, then take 80/443"
|
|
316
|
+
}
|
|
317
|
+
] : [],
|
|
318
|
+
{
|
|
319
|
+
value: "takeover",
|
|
320
|
+
label: "Stop it & take over 80/443",
|
|
321
|
+
hint: known ? "the existing sites stop being served" : "may interrupt a running service"
|
|
322
|
+
},
|
|
323
|
+
{ value: "cancel", label: "Cancel \u2014 leave it running" }
|
|
324
|
+
],
|
|
325
|
+
// Default to MIGRATE whenever there are sites we can import: it's the only
|
|
326
|
+
// option that both gets Openship onto 80/443 and keeps the operator's
|
|
327
|
+
// existing sites served, so it's the choice they almost always want and
|
|
328
|
+
// the one that loses nothing on a reflexive Enter.
|
|
329
|
+
// With nothing importable there's no safe default: a known proxy falls
|
|
330
|
+
// back to cancel (blind Enter would stop sites that are being served),
|
|
331
|
+
// and an unidentified port holder to takeover.
|
|
332
|
+
initialValue: importable > 0 ? "migrate" : known ? "cancel" : "takeover"
|
|
333
|
+
});
|
|
334
|
+
return isCancel(choice) ? "cancel" : choice;
|
|
335
|
+
}
|
|
336
|
+
async function remediateUnreachableStaticRoots(opts) {
|
|
337
|
+
const { unreachable } = opts;
|
|
338
|
+
if (unreachable.length === 0) return void 0;
|
|
339
|
+
note(
|
|
340
|
+
unreachable.map((u) => `${chalk.bold(u.host)} \u2192 ${chalk.dim(u.root)}`).join("\n"),
|
|
341
|
+
`${unreachable.length} static site${unreachable.length === 1 ? "" : "s"} rooted outside the edge`
|
|
342
|
+
);
|
|
343
|
+
log.warn("Their files live outside the edge container's mounts, so they'd return 500 after cutover.");
|
|
344
|
+
let copy = true;
|
|
345
|
+
if (opts.interactive) {
|
|
346
|
+
const choice = await select({
|
|
347
|
+
message: "Copy these sites' files into the edge so they keep serving?",
|
|
348
|
+
options: [
|
|
349
|
+
{
|
|
350
|
+
value: "copy",
|
|
351
|
+
label: `Copy ${unreachable.length === 1 ? "it" : "all"} into the edge`,
|
|
352
|
+
hint: "snapshot the files under /opt/openship/static (recommended)"
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
value: "leave",
|
|
356
|
+
label: "Leave as-is",
|
|
357
|
+
hint: "they stop serving until you mount their directory yourself"
|
|
358
|
+
}
|
|
359
|
+
],
|
|
360
|
+
initialValue: "copy"
|
|
361
|
+
});
|
|
362
|
+
copy = !isCancel(choice) && choice === "copy";
|
|
363
|
+
} else {
|
|
364
|
+
log.info("Non-interactive: copying unreachable static roots into the edge.");
|
|
365
|
+
}
|
|
366
|
+
if (!copy) return void 0;
|
|
367
|
+
const overrides = {};
|
|
368
|
+
for (const u of unreachable) {
|
|
369
|
+
try {
|
|
370
|
+
const newRoot = await copyStaticRootIntoEdge(opts.executor, { root: u.root, host: u.host });
|
|
371
|
+
overrides[u.host] = newRoot;
|
|
372
|
+
log.success(`Copied ${u.host} \u2192 ${newRoot}`);
|
|
373
|
+
} catch (err) {
|
|
374
|
+
log.warn(
|
|
375
|
+
`Couldn't copy ${u.host} (${u.root}) \u2014 it will migrate as-is: ${err instanceof Error ? err.message : String(err)}`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return Object.keys(overrides).length > 0 ? overrides : void 0;
|
|
380
|
+
}
|
|
381
|
+
function defaultDeps() {
|
|
382
|
+
return {
|
|
383
|
+
platform: process.platform,
|
|
384
|
+
interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
|
385
|
+
makeExecutor: () => new LocalExecutor(),
|
|
386
|
+
foreignProxyOnEdge,
|
|
387
|
+
importSites,
|
|
388
|
+
beginEdgeTakeover: (executor, status, onLog) => beginEdgeTakeover(executor, status, (entry) => onLog(entry.message, entry.level)),
|
|
389
|
+
rollbackHostEdge,
|
|
390
|
+
recoverInterruptedTakeover: (executor, onLog, isEdgeHealthy) => recoverInterruptedTakeover(
|
|
391
|
+
executor,
|
|
392
|
+
(entry) => onLog(entry.message, entry.level),
|
|
393
|
+
isEdgeHealthy
|
|
394
|
+
),
|
|
395
|
+
ourEdgeContainerRunning,
|
|
396
|
+
detectInstalledProxy,
|
|
397
|
+
scanProxySites: scanImportableSites,
|
|
398
|
+
edgeServedHostnames,
|
|
399
|
+
confirmStoppedImport: async ({ proxy, count }) => {
|
|
400
|
+
const take = await select({
|
|
401
|
+
message: `${proxy} is stopped but still has ${count} site(s) not served by Openship \u2014 import them?`,
|
|
402
|
+
options: [
|
|
403
|
+
{ value: "import", label: `Import ${count} site(s)`, hint: "serve them from Openship's edge" },
|
|
404
|
+
{ value: "skip", label: "Skip", hint: "leave them unserved" }
|
|
405
|
+
],
|
|
406
|
+
initialValue: "import"
|
|
407
|
+
});
|
|
408
|
+
return !isCancel(take) && take === "import";
|
|
409
|
+
},
|
|
410
|
+
collectCerts: collectCertsFromProxy,
|
|
411
|
+
render: renderEdgeConflict,
|
|
412
|
+
confirm: confirmEdgeAction,
|
|
413
|
+
warn: (message) => console.log(chalk.yellow(` ${message}`))
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export {
|
|
418
|
+
planAndApplyHostEdge,
|
|
419
|
+
previewHostEdge,
|
|
420
|
+
rollbackHostEdge,
|
|
421
|
+
markStoppedProxyImported,
|
|
422
|
+
diagnoseEdge,
|
|
423
|
+
repairEdgeConflict,
|
|
424
|
+
completeHostEdge,
|
|
425
|
+
renderEdgeConflict,
|
|
426
|
+
confirmEdgeAction,
|
|
427
|
+
remediateUnreachableStaticRoots
|
|
428
|
+
};
|