@envsync-cloud/deploy-cli 0.8.14 → 0.8.15
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/dist/index.js +139 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4756,6 +4756,39 @@ function renderFrontendRuntimeConfig(config, generated) {
|
|
|
4756
4756
|
}, null, 2)};
|
|
4757
4757
|
`;
|
|
4758
4758
|
}
|
|
4759
|
+
function renderNetutilsService(config) {
|
|
4760
|
+
const netutils = config.netutils;
|
|
4761
|
+
if (!netutils?.enabled) return "";
|
|
4762
|
+
const forwards = Array.isArray(netutils.forwards) ? netutils.forwards : [];
|
|
4763
|
+
if (forwards.length === 0) return "";
|
|
4764
|
+
const commands = forwards.map((forward) => {
|
|
4765
|
+
const protocol = forward.protocol ?? "tcp";
|
|
4766
|
+
if (protocol === "udp") {
|
|
4767
|
+
return `socat UDP-LISTEN:${forward.host_port},fork,reuseaddr UDP:${forward.service}:${forward.service_port} &`;
|
|
4768
|
+
}
|
|
4769
|
+
return `socat TCP-LISTEN:${forward.host_port},fork,reuseaddr TCP:${forward.service}:${forward.service_port} &`;
|
|
4770
|
+
}).join("\n ");
|
|
4771
|
+
const ports = forwards.map((forward) => {
|
|
4772
|
+
const protocol = forward.protocol ?? "tcp";
|
|
4773
|
+
return ` - target: ${forward.host_port}
|
|
4774
|
+
published: ${forward.host_port}
|
|
4775
|
+
protocol: ${protocol}
|
|
4776
|
+
mode: ingress`;
|
|
4777
|
+
}).join("\n");
|
|
4778
|
+
return `
|
|
4779
|
+
netutils:
|
|
4780
|
+
image: alpine/socat
|
|
4781
|
+
command:
|
|
4782
|
+
- sh
|
|
4783
|
+
- -c
|
|
4784
|
+
- |
|
|
4785
|
+
set -eu
|
|
4786
|
+
${commands}
|
|
4787
|
+
wait
|
|
4788
|
+
ports:
|
|
4789
|
+
${ports}
|
|
4790
|
+
networks: [envsync]`;
|
|
4791
|
+
}
|
|
4759
4792
|
function renderOtelAgentConfig(config) {
|
|
4760
4793
|
return [
|
|
4761
4794
|
"receivers:",
|
|
@@ -4812,6 +4845,7 @@ function renderStack(config, runtimeEnv, generated, mode, paths) {
|
|
|
4812
4845
|
const s3ServiceName = `${stackName}-s3-service`;
|
|
4813
4846
|
const s3ConsoleRouterName = `${stackName}-s3-console-router`;
|
|
4814
4847
|
const s3ConsoleServiceName = `${stackName}-s3-console-service`;
|
|
4848
|
+
const netutilsService = renderNetutilsService(config);
|
|
4815
4849
|
const apiEnvironment = {
|
|
4816
4850
|
...runtimeEnv,
|
|
4817
4851
|
OTEL_EXPORTER_OTLP_ENDPOINT: "http://otel-agent:4318",
|
|
@@ -5079,7 +5113,7 @@ ${renderEnvList({
|
|
|
5079
5113
|
${apiLicenseVolume}
|
|
5080
5114
|
networks: [envsync]
|
|
5081
5115
|
deploy:
|
|
5082
|
-
replicas: ${slotHasApiDeployment(deployment.slots.green) ? 1 : 0}` : ""}
|
|
5116
|
+
replicas: ${slotHasApiDeployment(deployment.slots.green) ? 1 : 0}` : ""}${netutilsService}
|
|
5083
5117
|
|
|
5084
5118
|
networks:
|
|
5085
5119
|
envsync:
|
|
@@ -5448,6 +5482,91 @@ function randomStrongPassword() {
|
|
|
5448
5482
|
function randomWireguardPort() {
|
|
5449
5483
|
return randomInt(5e4, 65535);
|
|
5450
5484
|
}
|
|
5485
|
+
function parsePort(value, label) {
|
|
5486
|
+
const numericValue = typeof value === "string" ? Number(value.trim()) : Number(value);
|
|
5487
|
+
if (!Number.isInteger(numericValue) || numericValue < 1 || numericValue > 65535) {
|
|
5488
|
+
throw new Error(`Invalid ${label}: expected an integer from 1 to 65535.`);
|
|
5489
|
+
}
|
|
5490
|
+
return numericValue;
|
|
5491
|
+
}
|
|
5492
|
+
function parseNetutilsForwardsFromInput(raw, stackName) {
|
|
5493
|
+
const chunks = asStringList(raw);
|
|
5494
|
+
if (chunks.length === 0) {
|
|
5495
|
+
return [];
|
|
5496
|
+
}
|
|
5497
|
+
const candidates = chunks.map((chunk) => {
|
|
5498
|
+
const parts = chunk.split(":");
|
|
5499
|
+
if (parts.length < 3 || parts.length > 4) {
|
|
5500
|
+
throw new Error(
|
|
5501
|
+
`Invalid netutils forward '${chunk}'. Expected format service:service_port:host_port[:protocol].`
|
|
5502
|
+
);
|
|
5503
|
+
}
|
|
5504
|
+
return {
|
|
5505
|
+
service: parts[0].trim(),
|
|
5506
|
+
service_port: parts[1].trim(),
|
|
5507
|
+
host_port: parts[2].trim(),
|
|
5508
|
+
protocol: parts[3] ?? "tcp"
|
|
5509
|
+
};
|
|
5510
|
+
});
|
|
5511
|
+
return normalizeNetutilsForwards(candidates, stackName, true).forwards;
|
|
5512
|
+
}
|
|
5513
|
+
function normalizeNetutilsProtocol(raw) {
|
|
5514
|
+
if (typeof raw === "string") {
|
|
5515
|
+
const protocol = raw.toLowerCase();
|
|
5516
|
+
if (protocol === "tcp" || protocol === "udp") return protocol;
|
|
5517
|
+
throw new Error(`Invalid protocol '${raw}'. Supported values are tcp or udp.`);
|
|
5518
|
+
}
|
|
5519
|
+
return "tcp";
|
|
5520
|
+
}
|
|
5521
|
+
function normalizeNetutilsForwards(raw, stackName, enabled) {
|
|
5522
|
+
if (!Array.isArray(raw)) {
|
|
5523
|
+
return {
|
|
5524
|
+
enabled,
|
|
5525
|
+
forwards: []
|
|
5526
|
+
};
|
|
5527
|
+
}
|
|
5528
|
+
const usedHostPorts = /* @__PURE__ */ new Set();
|
|
5529
|
+
const unique = /* @__PURE__ */ new Set();
|
|
5530
|
+
const forwards = [];
|
|
5531
|
+
for (const entry of raw) {
|
|
5532
|
+
const candidate = entry ?? {};
|
|
5533
|
+
const rawService = typeof candidate.service === "string" ? candidate.service.trim() : "";
|
|
5534
|
+
if (!rawService) {
|
|
5535
|
+
throw new Error("Invalid netutils forward: missing service");
|
|
5536
|
+
}
|
|
5537
|
+
const service = rawService.includes(".") || rawService.includes(":") || rawService.startsWith(`${stackName}_`) ? rawService : rawService;
|
|
5538
|
+
const servicePort = parsePort(candidate.service_port, "service_port");
|
|
5539
|
+
const hostPort = parsePort(candidate.host_port, "host_port");
|
|
5540
|
+
const protocol = normalizeNetutilsProtocol(candidate.protocol);
|
|
5541
|
+
const mapKey = `${service}:${servicePort}:${hostPort}:${protocol}`;
|
|
5542
|
+
if (unique.has(mapKey)) {
|
|
5543
|
+
continue;
|
|
5544
|
+
}
|
|
5545
|
+
const hostKey = `${protocol}:${hostPort}`;
|
|
5546
|
+
if (usedHostPorts.has(hostKey)) {
|
|
5547
|
+
throw new Error(
|
|
5548
|
+
`Duplicate netutils host port mapping detected for ${protocol.toUpperCase()} ${hostPort}. Use unique host ports per protocol.`
|
|
5549
|
+
);
|
|
5550
|
+
}
|
|
5551
|
+
unique.add(mapKey);
|
|
5552
|
+
usedHostPorts.add(hostKey);
|
|
5553
|
+
forwards.push({
|
|
5554
|
+
service,
|
|
5555
|
+
service_port: servicePort,
|
|
5556
|
+
host_port: hostPort,
|
|
5557
|
+
protocol
|
|
5558
|
+
});
|
|
5559
|
+
}
|
|
5560
|
+
forwards.sort((a, b) => {
|
|
5561
|
+
if (a.protocol !== b.protocol) return a.protocol.localeCompare(b.protocol);
|
|
5562
|
+
if (a.host_port !== b.host_port) return a.host_port - b.host_port;
|
|
5563
|
+
return a.service.localeCompare(b.service);
|
|
5564
|
+
});
|
|
5565
|
+
return {
|
|
5566
|
+
enabled,
|
|
5567
|
+
forwards
|
|
5568
|
+
};
|
|
5569
|
+
}
|
|
5451
5570
|
function loadWireguardState() {
|
|
5452
5571
|
if (!exists2(WIREGUARD_STATE_FILE)) return null;
|
|
5453
5572
|
const raw = JSON.parse(fs3.readFileSync(WIREGUARD_STATE_FILE, "utf8"));
|
|
@@ -5581,6 +5700,9 @@ function parseEnvFile(content) {
|
|
|
5581
5700
|
}
|
|
5582
5701
|
return out;
|
|
5583
5702
|
}
|
|
5703
|
+
function asStringList(value) {
|
|
5704
|
+
return value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
|
|
5705
|
+
}
|
|
5584
5706
|
async function ask(question, fallback = "") {
|
|
5585
5707
|
if (!process.stdin.isTTY) return fallback;
|
|
5586
5708
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -5915,6 +6037,7 @@ function normalizeConfig(raw) {
|
|
|
5915
6037
|
db_snapshot_on_api_upgrade: raw.upgrade?.db_snapshot_on_api_upgrade ?? true,
|
|
5916
6038
|
keep_failed_upgrade_db_snapshot: raw.upgrade?.keep_failed_upgrade_db_snapshot ?? true
|
|
5917
6039
|
},
|
|
6040
|
+
netutils: normalizeNetutilsForwards(raw.netutils?.forwards, stackName, raw.netutils?.enabled ?? false),
|
|
5918
6041
|
license: raw.license ? {
|
|
5919
6042
|
server_url: raw.license.server_url,
|
|
5920
6043
|
key: raw.license.key,
|
|
@@ -7342,6 +7465,14 @@ async function cmdSetup() {
|
|
|
7342
7465
|
const publicAuth = await ask("Expose auth.<domain> publicly (true/false)", "true") === "true";
|
|
7343
7466
|
const publicObs = await ask("Expose obs.<domain> publicly (true/false)", "true") === "true";
|
|
7344
7467
|
const mailpitEnabled = await ask("Enable mailpit (true/false)", "false") === "true";
|
|
7468
|
+
const netutilsEnabled = await ask("Enable WireGuard jump host access (true/false)", "false") === "true";
|
|
7469
|
+
const netutilsForwards = netutilsEnabled ? parseNetutilsForwardsFromInput(
|
|
7470
|
+
await ask(
|
|
7471
|
+
"Netutils forwards (format service:service_port:host_port[:protocol], comma-separated)",
|
|
7472
|
+
"postgres:5432:15432:tcp,keycloak:8080:18080:tcp"
|
|
7473
|
+
),
|
|
7474
|
+
"envsync"
|
|
7475
|
+
) : [];
|
|
7345
7476
|
const licenseServerUrl = await ask("Enterprise license server URL", DEFAULT_ENTERPRISE_LICENSE_SERVER_URL);
|
|
7346
7477
|
const licenseKey = licenseServerUrl ? await ask("Enterprise license key", "") : "";
|
|
7347
7478
|
const certificateBundleFile = licenseServerUrl ? "" : await ask("Enterprise certificate bundle file (optional)", "");
|
|
@@ -7418,6 +7549,10 @@ async function cmdSetup() {
|
|
|
7418
7549
|
install_fingerprint: installFingerprint,
|
|
7419
7550
|
certificate_bundle_file: certificateBundleFile || void 0,
|
|
7420
7551
|
certificate_validity_days: 1095
|
|
7552
|
+
},
|
|
7553
|
+
netutils: {
|
|
7554
|
+
enabled: netutilsEnabled,
|
|
7555
|
+
forwards: netutilsForwards
|
|
7421
7556
|
}
|
|
7422
7557
|
};
|
|
7423
7558
|
saveDesiredConfig(config);
|
|
@@ -8397,9 +8532,9 @@ function listRunningContainerEndpoints(serviceName) {
|
|
|
8397
8532
|
for (const taskId of taskIds) {
|
|
8398
8533
|
const taskRaw = tryRun("docker", ["inspect", taskId, "--format", "{{json .}}"], { quiet: true });
|
|
8399
8534
|
if (!taskRaw) continue;
|
|
8400
|
-
const
|
|
8401
|
-
|
|
8402
|
-
|
|
8535
|
+
const parsedTaskInspect = parseJsonOrDefault(taskRaw, null);
|
|
8536
|
+
const inspect = Array.isArray(parsedTaskInspect) ? parsedTaskInspect[0] : parsedTaskInspect;
|
|
8537
|
+
if (!inspect || typeof inspect !== "object") continue;
|
|
8403
8538
|
const containerId = inspect.Status?.ContainerStatus?.ContainerID ?? "";
|
|
8404
8539
|
const addresses = Array.isArray(inspect.NetworksAttachments) ? inspect.NetworksAttachments.flatMap((attachment) => {
|
|
8405
8540
|
const networkName = attachment.Network?.Spec?.Name ?? "overlay";
|