@iann29/synapse 1.8.7 → 1.8.8

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.
@@ -0,0 +1,154 @@
1
+ // `synapse https status [domain]` — diagnose what's already
2
+ // configured. With a domain: deep dive on that one. Without: list
3
+ // every domain under ~/.config/dev-certs/.
4
+
5
+ const fs = require("node:fs");
6
+ const path = require("node:path");
7
+ const colors = require("../colors");
8
+ const { extractFlags } = require("./_resource");
9
+ const detectMod = require("../https/detect");
10
+
11
+ function renderDomainStatus(detection, { stdout }) {
12
+ const sym = (b) => (b ? colors.green("✓") : colors.dim("·"));
13
+ stdout.write(`${colors.bold(detection.domain)}\n`);
14
+ stdout.write(
15
+ ` ${sym(detection.mkcert.present)} mkcert ${
16
+ detection.mkcert.present ? colors.dim(detection.mkcert.version) : colors.red("missing")
17
+ }\n`,
18
+ );
19
+ stdout.write(
20
+ ` ${sym(detection.caTrusted)} CA trusted${
21
+ detection.caTrusted ? "" : colors.red(" (run `mkcert -install`)")
22
+ }\n`,
23
+ );
24
+ const certPresent = detection.existingCerts && detection.existingCerts.present;
25
+ stdout.write(
26
+ ` ${sym(certPresent)} cert pair ${
27
+ certPresent ? colors.dim(detection.existingCerts.certPath) : colors.dim("(not generated yet)")
28
+ }\n`,
29
+ );
30
+ if (certPresent && detection.existingCerts.expiry) {
31
+ stdout.write(colors.dim(` expires ${detection.existingCerts.expiry}\n`));
32
+ }
33
+ const resolves = detection.resolution.resolvesToLoopback;
34
+ const src = detection.resolution.source;
35
+ stdout.write(
36
+ ` ${sym(resolves)} resolves to 127.0.0.1 ${
37
+ resolves ? colors.dim(`(via ${src})`) : colors.red("(no — add to hosts or DNS A record)")
38
+ }\n`,
39
+ );
40
+ const scriptOk = !!detection.pkg.existingDevHttps;
41
+ stdout.write(
42
+ ` ${sym(scriptOk)} package.json dev:https ${
43
+ scriptOk
44
+ ? colors.dim("set")
45
+ : detection.pkg.present
46
+ ? colors.dim("(missing in cwd's package.json)")
47
+ : colors.dim("(no package.json in cwd)")
48
+ }\n`,
49
+ );
50
+ }
51
+
52
+ module.exports = {
53
+ name: "https status",
54
+ summary: "Show local-HTTPS state for one domain or every configured domain.",
55
+ usage: "synapse https status [domain] [--json]",
56
+ description: `Without a domain: lists every cert directory in ~/.config/dev-certs/.
57
+ With a domain: prints a deep diagnostic (mkcert, CA trust, cert presence + expiry, DNS resolution, package.json script).`,
58
+
59
+ async run(args, ctx) {
60
+ const { flags, rest } = extractFlags(args, {
61
+ string: [],
62
+ boolean: ["json"],
63
+ });
64
+ const domain = rest[0];
65
+ if (rest.length > 1) {
66
+ throw new Error(`Unexpected positional: ${rest[1]}.`);
67
+ }
68
+
69
+ if (!domain) {
70
+ // List mode. We accept TWO layouts in the cert store:
71
+ // 1. Canonical (v1.8.8+): `<root>/<domain>/<domain>.pem` + `-key.pem`
72
+ // 2. Flat (operator's pre-existing intermediate layout):
73
+ // `<root>/<domain>.pem` + `<domain>{-,.}key.pem`
74
+ // Flat-format certs aren't moved automatically — they show up
75
+ // here with a `flat: true` hint so `https migrate --root` can
76
+ // reorganise them.
77
+ const root = detectMod.certsRoot();
78
+ let entries = [];
79
+ try {
80
+ entries = fs.readdirSync(root, { withFileTypes: true });
81
+ } catch {
82
+ entries = [];
83
+ }
84
+ const domains = new Set();
85
+ const flatDomains = new Set();
86
+ // (1) Subdirectories (canonical).
87
+ for (const e of entries) {
88
+ if (e.isDirectory()) domains.add(e.name);
89
+ }
90
+ // (2) Flat pairs in the root.
91
+ const flat = detectMod.detectFlatCertsInStore();
92
+ for (const p of flat) {
93
+ domains.add(p.domain);
94
+ flatDomains.add(p.domain);
95
+ }
96
+ const rows = await Promise.all(
97
+ [...domains].sort().map(async (d) => {
98
+ const det = await detectMod.scan({ domain: d, cwd: ctx.cwd });
99
+ return {
100
+ domain: d,
101
+ cert: det.existingCerts?.present ?? false,
102
+ expiry: det.existingCerts?.expiry ?? null,
103
+ resolves: det.resolution.resolvesToLoopback,
104
+ resolutionSource: det.resolution.source,
105
+ flat: flatDomains.has(d),
106
+ };
107
+ }),
108
+ );
109
+ ctx.out.result(
110
+ { root, count: rows.length, domains: rows },
111
+ (_d, { stdout }) => {
112
+ stdout.write(colors.dim(`Cert store: ${root}\n`));
113
+ if (rows.length === 0) {
114
+ stdout.write(
115
+ colors.dim("(no certs configured — run `synapse https setup <domain>`)\n"),
116
+ );
117
+ return;
118
+ }
119
+ ctx.out.table(
120
+ rows.map((r) => ({
121
+ domain: colors.bold(r.domain) + (r.flat ? colors.dim(" (flat)") : ""),
122
+ cert: r.cert ? colors.green("yes") : colors.dim("no"),
123
+ expiry: r.expiry ? colors.dim(r.expiry.slice(0, 10)) : colors.dim("?"),
124
+ resolves: r.resolves
125
+ ? colors.green(r.resolutionSource)
126
+ : colors.red("no"),
127
+ })),
128
+ [
129
+ { key: "domain", header: "DOMAIN" },
130
+ { key: "cert", header: "CERT" },
131
+ { key: "expiry", header: "EXPIRES" },
132
+ { key: "resolves", header: "LOOPBACK" },
133
+ ],
134
+ );
135
+ if (rows.some((r) => r.flat)) {
136
+ stdout.write(
137
+ colors.dim(
138
+ "\n(flat) = cert lives directly in ~/.config/dev-certs/ instead of a subdirectory. Run `synapse https migrate --root=~/.config/dev-certs` to reorganise.\n",
139
+ ),
140
+ );
141
+ }
142
+ },
143
+ );
144
+ return;
145
+ }
146
+
147
+ // Single-domain detail mode.
148
+ const det = await detectMod.scan({ domain, cwd: ctx.cwd });
149
+ ctx.out.result(det, (d, { stdout }) => renderDomainStatus(d, { stdout }));
150
+ },
151
+
152
+ // Exports for tests.
153
+ renderDomainStatus,
154
+ };