@lead-routing/cli 0.1.2 → 0.1.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/dist/index.js +71 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -744,18 +744,41 @@ async function checkRemoteDockerCompose(ssh) {
|
|
|
744
744
|
return { ok: true, label: `Docker Compose \u2014 ${stdout.trim()}` };
|
|
745
745
|
}
|
|
746
746
|
async function checkRemotePort(ssh, port) {
|
|
747
|
-
const {
|
|
748
|
-
|
|
749
|
-
);
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
747
|
+
const portCheckCmd = `ss -tlnp 2>/dev/null | grep ':${port} ' || netstat -tlnp 2>/dev/null | grep ':${port} ' || echo "free"`;
|
|
748
|
+
const { stdout: initial } = await ssh.execSilent(portCheckCmd);
|
|
749
|
+
const isBound = initial.trim() !== "free" && initial.trim() !== "";
|
|
750
|
+
if (!isBound) {
|
|
751
|
+
return { ok: true, label: `Port ${port} \u2014 available` };
|
|
752
|
+
}
|
|
753
|
+
const knownServices = ["nginx", "apache2", "httpd", "lighttpd", "caddy"];
|
|
754
|
+
for (const svc of knownServices) {
|
|
755
|
+
const { code: activeCode } = await ssh.execSilent(
|
|
756
|
+
`systemctl is-active --quiet ${svc} 2>/dev/null`
|
|
757
|
+
);
|
|
758
|
+
if (activeCode === 0) {
|
|
759
|
+
await ssh.execSilent(
|
|
760
|
+
`systemctl stop ${svc} 2>/dev/null; systemctl disable ${svc} 2>/dev/null`
|
|
761
|
+
);
|
|
762
|
+
const { stdout: recheck } = await ssh.execSilent(portCheckCmd);
|
|
763
|
+
if (recheck.trim() === "free" || !recheck.trim()) {
|
|
764
|
+
return {
|
|
765
|
+
ok: true,
|
|
766
|
+
label: `Port ${port} \u2014 freed (stopped and disabled system ${svc} service)`
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
}
|
|
757
770
|
}
|
|
758
|
-
|
|
771
|
+
const { stdout: occupant } = await ssh.execSilent(
|
|
772
|
+
`ss -tlnp 2>/dev/null | grep ':${port} ' | head -1 || echo "unknown process"`
|
|
773
|
+
);
|
|
774
|
+
return {
|
|
775
|
+
ok: false,
|
|
776
|
+
// Hard error — Caddy cannot get TLS certs without these ports
|
|
777
|
+
label: `Port ${port} is occupied and could not be freed automatically.
|
|
778
|
+
Occupant: ${occupant.trim()}
|
|
779
|
+
Stop the conflicting process on the server, then re-run:
|
|
780
|
+
lead-routing init`
|
|
781
|
+
};
|
|
759
782
|
}
|
|
760
783
|
|
|
761
784
|
// src/steps/upload-files.ts
|
|
@@ -986,7 +1009,7 @@ ON CONFLICT ("orgId", email) DO NOTHING;
|
|
|
986
1009
|
|
|
987
1010
|
// src/steps/verify-health.ts
|
|
988
1011
|
import { spinner as spinner5, log as log5 } from "@clack/prompts";
|
|
989
|
-
async function verifyHealth(appUrl, engineUrl) {
|
|
1012
|
+
async function verifyHealth(appUrl, engineUrl, ssh, remoteDir) {
|
|
990
1013
|
const checks = [
|
|
991
1014
|
{ service: "Web app", url: `${appUrl}/api/health` },
|
|
992
1015
|
{ service: "Routing engine", url: `${engineUrl}/health` }
|
|
@@ -996,9 +1019,42 @@ async function verifyHealth(appUrl, engineUrl) {
|
|
|
996
1019
|
if (r.ok) {
|
|
997
1020
|
log5.success(`${r.service} \u2014 ${r.url}`);
|
|
998
1021
|
} else {
|
|
999
|
-
log5.warn(`${r.service} not
|
|
1022
|
+
log5.warn(`${r.service} \u2014 did not respond after ${r.detail}`);
|
|
1000
1023
|
}
|
|
1001
1024
|
}
|
|
1025
|
+
const failed = results.filter((r) => !r.ok);
|
|
1026
|
+
if (failed.length === 0) return;
|
|
1027
|
+
log5.info("Fetching remote diagnostics\u2026");
|
|
1028
|
+
try {
|
|
1029
|
+
const { stdout: ps } = await ssh.execSilent("docker compose ps --format table", remoteDir);
|
|
1030
|
+
if (ps.trim()) log5.info(`Container status:
|
|
1031
|
+
${ps.trim()}`);
|
|
1032
|
+
} catch {
|
|
1033
|
+
}
|
|
1034
|
+
try {
|
|
1035
|
+
const { stdout: caddyLogs } = await ssh.execSilent(
|
|
1036
|
+
"docker compose logs caddy --tail 30 --no-color 2>&1",
|
|
1037
|
+
remoteDir
|
|
1038
|
+
);
|
|
1039
|
+
if (caddyLogs.trim()) log5.info(`Caddy logs (last 30 lines):
|
|
1040
|
+
${caddyLogs.trim()}`);
|
|
1041
|
+
} catch {
|
|
1042
|
+
}
|
|
1043
|
+
const failedNames = failed.map((r) => r.service).join(" and ");
|
|
1044
|
+
throw new Error(
|
|
1045
|
+
`${failedNames} did not respond after 2 minutes.
|
|
1046
|
+
|
|
1047
|
+
Common causes (check Caddy logs above):
|
|
1048
|
+
\u2022 Let's Encrypt rate limit \u2014 wait until tomorrow and re-run
|
|
1049
|
+
\u2022 Port 80/443 still blocked by another process
|
|
1050
|
+
\u2022 Container crashed \u2014 check container status above
|
|
1051
|
+
|
|
1052
|
+
To resume once fixed:
|
|
1053
|
+
1. SSH into your server:
|
|
1054
|
+
cd ${remoteDir} && docker compose restart caddy
|
|
1055
|
+
2. Then re-run Salesforce setup:
|
|
1056
|
+
lead-routing sfdc deploy`
|
|
1057
|
+
);
|
|
1002
1058
|
}
|
|
1003
1059
|
async function pollHealth(service, url, maxAttempts = 24, intervalMs = 5e3) {
|
|
1004
1060
|
const s = spinner5();
|
|
@@ -1022,7 +1078,7 @@ async function pollHealth(service, url, maxAttempts = 24, intervalMs = 5e3) {
|
|
|
1022
1078
|
service,
|
|
1023
1079
|
url,
|
|
1024
1080
|
ok: false,
|
|
1025
|
-
detail:
|
|
1081
|
+
detail: `${maxAttempts * intervalMs / 1e3}s`
|
|
1026
1082
|
};
|
|
1027
1083
|
}
|
|
1028
1084
|
function sleep2(ms) {
|
|
@@ -1479,7 +1535,7 @@ Files created: docker-compose.yml, Caddyfile, .env.web, .env.engine, lead-routin
|
|
|
1479
1535
|
log8.step("Step 7/9 Database migrations");
|
|
1480
1536
|
await runMigrations(ssh, dir, cfg.adminEmail, cfg.adminPassword);
|
|
1481
1537
|
log8.step("Step 8/9 Verifying health");
|
|
1482
|
-
await verifyHealth(cfg.appUrl, cfg.engineUrl);
|
|
1538
|
+
await verifyHealth(cfg.appUrl, cfg.engineUrl, ssh, remoteDir);
|
|
1483
1539
|
log8.step("Step 9/9 Deploying Salesforce package");
|
|
1484
1540
|
await sfdcDeployInline({
|
|
1485
1541
|
appUrl: cfg.appUrl,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lead-routing/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Self-hosted deployment CLI for Lead Routing",
|
|
5
5
|
"homepage": "https://github.com/lead-routing/lead-routing",
|
|
6
6
|
"keywords": ["salesforce", "lead-routing", "self-hosted", "deployment", "cli"],
|