vite-plugin-caddy-multiple-tls 1.8.1 → 1.9.0
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 +8 -5
- package/dist/index.js +301 -164
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -130,7 +130,11 @@ export default config;
|
|
|
130
130
|
|
|
131
131
|
This derives a host like `<repo>.<branch>.web-1.localhost`.
|
|
132
132
|
|
|
133
|
-
The plugin
|
|
133
|
+
The plugin treats hostname ownership as latest-started-wins. If another live Vite server already owns a resolved hostname, the newly started server replaces that hostname's Caddy route so the domain points at the server you just started.
|
|
134
|
+
|
|
135
|
+
When a plugin instance has multiple domains, each hostname is managed independently. Reusing `app.localhost` from another server will not remove a sibling route like `api.localhost`.
|
|
136
|
+
|
|
137
|
+
If the plugin replaces one hostname from an older combined multi-domain route, it preserves the remaining sibling hostnames before removing the old combined route.
|
|
134
138
|
|
|
135
139
|
If a previous Vite process crashed and left stale ownership behind, the plugin will reclaim it automatically and clean up the stale Caddy route before continuing.
|
|
136
140
|
|
|
@@ -179,12 +183,11 @@ export default config;
|
|
|
179
183
|
|
|
180
184
|
## Troubleshooting
|
|
181
185
|
|
|
182
|
-
###
|
|
186
|
+
### A hostname points to the newest matching server
|
|
183
187
|
|
|
184
|
-
|
|
188
|
+
If two dev servers resolve to the same hostname, the most recently started server owns that hostname.
|
|
185
189
|
|
|
186
|
-
-
|
|
187
|
-
- Add `instanceLabel` if both servers should run at the same time.
|
|
190
|
+
- Add `instanceLabel` if both servers should stay reachable at the same time.
|
|
188
191
|
- Pass an explicit `domain` if you want total control over the hostname.
|
|
189
192
|
|
|
190
193
|
### `client is not allowed to access from origin ''`
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,6 @@ var ROUTE_ID_PREFIX = "vite-proxy-";
|
|
|
14
14
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
15
15
|
var LOCK_RETRY_MS = 50;
|
|
16
16
|
var ROUTE_OWNERSHIP_VERSION = 1;
|
|
17
|
-
var ROUTE_OWNERSHIP_STALE_AFTER_MS = 3e4;
|
|
18
17
|
var ROUTE_OWNERSHIP_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
19
18
|
var CONNECTIVITY_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
20
19
|
"ECONNREFUSED",
|
|
@@ -33,16 +32,6 @@ function isRecord(value) {
|
|
|
33
32
|
function normalizeRouteOwnershipDomains(domains) {
|
|
34
33
|
return Array.from(new Set(domains)).sort();
|
|
35
34
|
}
|
|
36
|
-
function haveSameDomains(left, right) {
|
|
37
|
-
if (left.length !== right.length) return false;
|
|
38
|
-
return left.every((domain, index) => domain === right[index]);
|
|
39
|
-
}
|
|
40
|
-
function getRouteOwnershipProjectRoot(record) {
|
|
41
|
-
return record.configRoot ?? record.cwd;
|
|
42
|
-
}
|
|
43
|
-
function canReclaimLiveOwnership(currentRecord, existingRecord) {
|
|
44
|
-
return getRouteOwnershipProjectRoot(currentRecord) === getRouteOwnershipProjectRoot(existingRecord) && haveSameDomains(currentRecord.domains, existingRecord.domains);
|
|
45
|
-
}
|
|
46
35
|
function getRouteOwnershipDirectory() {
|
|
47
36
|
return path.join(os.tmpdir(), "vite-plugin-caddy-multiple-tls", "owners");
|
|
48
37
|
}
|
|
@@ -225,7 +214,14 @@ async function withFileLock(lockPath, fn) {
|
|
|
225
214
|
}
|
|
226
215
|
}
|
|
227
216
|
async function withApiLock(apiUrl, fn) {
|
|
228
|
-
|
|
217
|
+
let result;
|
|
218
|
+
await withFileLock(getLockPath(apiUrl), async () => {
|
|
219
|
+
result = await fn();
|
|
220
|
+
});
|
|
221
|
+
return result;
|
|
222
|
+
}
|
|
223
|
+
async function withCaddyApiLock(apiUrl, fn) {
|
|
224
|
+
return withApiLock(apiUrl, fn);
|
|
229
225
|
}
|
|
230
226
|
async function readRouteOwnershipByPath(recordPath) {
|
|
231
227
|
try {
|
|
@@ -271,20 +267,6 @@ async function listRouteOwnershipRecords(scope) {
|
|
|
271
267
|
);
|
|
272
268
|
});
|
|
273
269
|
}
|
|
274
|
-
function isProcessAlive(pid) {
|
|
275
|
-
try {
|
|
276
|
-
process.kill(pid, 0);
|
|
277
|
-
return true;
|
|
278
|
-
} catch (e) {
|
|
279
|
-
return isNodeError(e) && e.code === "EPERM";
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
function isRouteOwnershipActive(record, now = Date.now()) {
|
|
283
|
-
if (isProcessAlive(record.pid)) {
|
|
284
|
-
return true;
|
|
285
|
-
}
|
|
286
|
-
return record.pid <= 0 && now - record.lastSeenAt <= ROUTE_OWNERSHIP_STALE_AFTER_MS;
|
|
287
|
-
}
|
|
288
270
|
async function claimRouteOwnership(record) {
|
|
289
271
|
const normalizedRecord = normalizeRouteOwnershipRecord(record);
|
|
290
272
|
const { lockPath, recordPath } = getRouteOwnershipPaths(normalizedRecord);
|
|
@@ -304,29 +286,12 @@ async function claimRouteOwnership(record) {
|
|
|
304
286
|
return candidate.ownerId !== normalizedRecord.ownerId && intersectsDomains(candidate.domains, normalizedRecord.domains);
|
|
305
287
|
}
|
|
306
288
|
);
|
|
307
|
-
const reclaimableRecords = overlappingRecords.filter((candidate) => {
|
|
308
|
-
if (!isRouteOwnershipActive(candidate)) {
|
|
309
|
-
return true;
|
|
310
|
-
}
|
|
311
|
-
return canReclaimLiveOwnership(normalizedRecord, candidate);
|
|
312
|
-
});
|
|
313
|
-
const activeConflict = overlappingRecords.find((candidate) => {
|
|
314
|
-
return isRouteOwnershipActive(candidate) && !canReclaimLiveOwnership(normalizedRecord, candidate);
|
|
315
|
-
});
|
|
316
|
-
if (activeConflict) {
|
|
317
|
-
claimResult = {
|
|
318
|
-
status: "active-conflict",
|
|
319
|
-
currentRecord: normalizedRecord,
|
|
320
|
-
existingRecord: activeConflict
|
|
321
|
-
};
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
289
|
await writeRouteOwnership(normalizedRecord);
|
|
325
|
-
if (
|
|
290
|
+
if (overlappingRecords.length > 0) {
|
|
326
291
|
claimResult = {
|
|
327
|
-
status: "
|
|
292
|
+
status: "replaced",
|
|
328
293
|
currentRecord: normalizedRecord,
|
|
329
|
-
previousRecords:
|
|
294
|
+
previousRecords: overlappingRecords
|
|
330
295
|
};
|
|
331
296
|
return;
|
|
332
297
|
}
|
|
@@ -625,6 +590,106 @@ function intersectsDomains(targetDomains, routeDomains) {
|
|
|
625
590
|
const targetSet = new Set(targetDomains);
|
|
626
591
|
return routeDomains.some((domain) => targetSet.has(domain));
|
|
627
592
|
}
|
|
593
|
+
async function readManagedConfigById(id, apiUrl, adminOrigin) {
|
|
594
|
+
const res = await caddyFetch(`${getApiUrl(apiUrl)}/id/${id}`, void 0, apiUrl, adminOrigin);
|
|
595
|
+
if (res.status === 404) return null;
|
|
596
|
+
await assertCaddyResponse(res, `Failed to read managed Caddy resource ${id}`);
|
|
597
|
+
const text = await res.text();
|
|
598
|
+
const parsed = parseConfig(text);
|
|
599
|
+
return isRecord(parsed) ? parsed : null;
|
|
600
|
+
}
|
|
601
|
+
function createPreservedOwnershipRecord(record, domains) {
|
|
602
|
+
const normalizedDomains = normalizeRouteOwnershipDomains(domains);
|
|
603
|
+
const hash = createHash("sha1").update(`${record.routeId}:${normalizedDomains.join(",")}:${process.pid}:${Date.now()}`).digest("hex").slice(0, 12);
|
|
604
|
+
const ownerId = `${record.ownerId}-preserved-${hash}`;
|
|
605
|
+
const routeId = `${ROUTE_ID_PREFIX}preserved-${hash}`;
|
|
606
|
+
const now = Date.now();
|
|
607
|
+
return {
|
|
608
|
+
...record,
|
|
609
|
+
ownerId,
|
|
610
|
+
domains: normalizedDomains,
|
|
611
|
+
routeId,
|
|
612
|
+
tlsPolicyId: record.tlsPolicyId ? `${routeId}-tls` : null,
|
|
613
|
+
startedAt: now,
|
|
614
|
+
lastSeenAt: now
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
async function postManagedRouteConfig(route, serverName = DEFAULT_SERVER_NAME, apiUrl, adminOrigin) {
|
|
618
|
+
const res = await caddyFetch(
|
|
619
|
+
`${getApiUrl(apiUrl)}/config/apps/http/servers/${serverName}/routes`,
|
|
620
|
+
{
|
|
621
|
+
method: "POST",
|
|
622
|
+
headers: { "Content-Type": "application/json" },
|
|
623
|
+
body: JSON.stringify(route)
|
|
624
|
+
},
|
|
625
|
+
apiUrl,
|
|
626
|
+
adminOrigin
|
|
627
|
+
);
|
|
628
|
+
await assertCaddyResponse(res, "Failed to preserve managed Caddy route");
|
|
629
|
+
}
|
|
630
|
+
async function postManagedTlsPolicyConfig(policy, apiUrl, adminOrigin) {
|
|
631
|
+
const res = await caddyFetch(
|
|
632
|
+
`${getApiUrl(apiUrl)}/config/apps/tls/automation/policies`,
|
|
633
|
+
{
|
|
634
|
+
method: "POST",
|
|
635
|
+
headers: { "Content-Type": "application/json" },
|
|
636
|
+
body: JSON.stringify(policy)
|
|
637
|
+
},
|
|
638
|
+
apiUrl,
|
|
639
|
+
adminOrigin
|
|
640
|
+
);
|
|
641
|
+
if (!res.ok) {
|
|
642
|
+
const text = await res.text();
|
|
643
|
+
if (isTlsPolicyOverlapError(text)) {
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
throw buildCaddyRequestError("Failed to preserve managed Caddy TLS policy", res.status, text);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
async function createManagedResourcePreservation(record, domains, serverName = DEFAULT_SERVER_NAME, apiUrl, adminOrigin) {
|
|
650
|
+
const remainingDomains = normalizeRouteOwnershipDomains(
|
|
651
|
+
domains.filter((domain) => record.domains.includes(domain))
|
|
652
|
+
);
|
|
653
|
+
if (remainingDomains.length === 0) return null;
|
|
654
|
+
const routeConfig = await readManagedConfigById(record.routeId, apiUrl, adminOrigin);
|
|
655
|
+
if (!routeConfig) return null;
|
|
656
|
+
const preservedRecord = createPreservedOwnershipRecord(record, remainingDomains);
|
|
657
|
+
const preservedRoute = {
|
|
658
|
+
...routeConfig,
|
|
659
|
+
"@id": preservedRecord.routeId,
|
|
660
|
+
match: [{ host: preservedRecord.domains }]
|
|
661
|
+
};
|
|
662
|
+
const sourceTlsPolicy = record.tlsPolicyId && preservedRecord.tlsPolicyId ? await readManagedConfigById(record.tlsPolicyId, apiUrl, adminOrigin) : null;
|
|
663
|
+
const preservedTlsPolicy = preservedRecord.tlsPolicyId ? {
|
|
664
|
+
...sourceTlsPolicy ?? {
|
|
665
|
+
issuers: [
|
|
666
|
+
{
|
|
667
|
+
module: "internal"
|
|
668
|
+
}
|
|
669
|
+
]
|
|
670
|
+
},
|
|
671
|
+
"@id": preservedRecord.tlsPolicyId,
|
|
672
|
+
subjects: preservedRecord.domains
|
|
673
|
+
} : null;
|
|
674
|
+
await postManagedRouteConfig(preservedRoute, serverName, apiUrl, adminOrigin);
|
|
675
|
+
return {
|
|
676
|
+
record: preservedRecord,
|
|
677
|
+
tlsPolicy: preservedTlsPolicy
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
async function commitManagedResourcePreservation(preservation, apiUrl, adminOrigin) {
|
|
681
|
+
if (preservation.tlsPolicy) {
|
|
682
|
+
await postManagedTlsPolicyConfig(preservation.tlsPolicy, apiUrl, adminOrigin);
|
|
683
|
+
}
|
|
684
|
+
await writeRouteOwnership(preservation.record);
|
|
685
|
+
}
|
|
686
|
+
async function discardManagedResourcePreservation(preservation, apiUrl, adminOrigin) {
|
|
687
|
+
let discarded = await removeRoute(preservation.record.routeId, apiUrl, adminOrigin);
|
|
688
|
+
if (preservation.record.tlsPolicyId) {
|
|
689
|
+
discarded = await removeTlsPolicy(preservation.record.tlsPolicyId, apiUrl, adminOrigin) && discarded;
|
|
690
|
+
}
|
|
691
|
+
return discarded;
|
|
692
|
+
}
|
|
628
693
|
async function findManagedRoutesForDomains(domains, serverName = DEFAULT_SERVER_NAME, apiUrl, adminOrigin) {
|
|
629
694
|
if (domains.length === 0) return [];
|
|
630
695
|
const res = await caddyFetch(
|
|
@@ -999,15 +1064,6 @@ function viteCaddyTlsPlugin({
|
|
|
999
1064
|
lastSeenAt: now
|
|
1000
1065
|
};
|
|
1001
1066
|
}
|
|
1002
|
-
function buildOwnershipConflictMessage(domains, existingRecord) {
|
|
1003
|
-
const ownerLocation = existingRecord.configRoot ?? existingRecord.cwd;
|
|
1004
|
-
const domainLabel = domains.join(", ");
|
|
1005
|
-
return [
|
|
1006
|
-
`Cannot claim ${domainLabel}: another Vite server already owns ${domains.length > 1 ? "these domains" : "this domain"}.`,
|
|
1007
|
-
`Existing owner pid ${existingRecord.pid} from ${ownerLocation}.`,
|
|
1008
|
-
"Stop the other server first, or use `instanceLabel` or `domain` to make the hostname unique."
|
|
1009
|
-
].join(" ");
|
|
1010
|
-
}
|
|
1011
1067
|
async function releaseOwnershipRecord(record) {
|
|
1012
1068
|
if (!record) return;
|
|
1013
1069
|
await releaseRouteOwnership(record);
|
|
@@ -1015,6 +1071,10 @@ function viteCaddyTlsPlugin({
|
|
|
1015
1071
|
async function releaseOwnershipRecords(records) {
|
|
1016
1072
|
await Promise.all(records.map((record) => releaseOwnershipRecord(record)));
|
|
1017
1073
|
}
|
|
1074
|
+
function getPreservedDomains(record, replacedDomains) {
|
|
1075
|
+
const replacedDomainSet = new Set(replacedDomains);
|
|
1076
|
+
return record.domains.filter((domain2) => !replacedDomainSet.has(domain2));
|
|
1077
|
+
}
|
|
1018
1078
|
async function cleanupClaimedResources(record, removeWithRetry) {
|
|
1019
1079
|
let cleaned = true;
|
|
1020
1080
|
if (record.tlsPolicyId) {
|
|
@@ -1046,6 +1106,17 @@ function viteCaddyTlsPlugin({
|
|
|
1046
1106
|
}
|
|
1047
1107
|
return cleaned;
|
|
1048
1108
|
}
|
|
1109
|
+
async function discardManagedResourcePreservations(preservations) {
|
|
1110
|
+
await Promise.all(
|
|
1111
|
+
preservations.map((preservation) => {
|
|
1112
|
+
return discardManagedResourcePreservation(
|
|
1113
|
+
preservation,
|
|
1114
|
+
pluginCaddyApiUrl,
|
|
1115
|
+
pluginCaddyAdminOrigin
|
|
1116
|
+
);
|
|
1117
|
+
})
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1049
1120
|
function isPreviewServer(server) {
|
|
1050
1121
|
return server.config.isProduction;
|
|
1051
1122
|
}
|
|
@@ -1077,15 +1148,14 @@ function viteCaddyTlsPlugin({
|
|
|
1077
1148
|
instanceLabel
|
|
1078
1149
|
});
|
|
1079
1150
|
const domainArray = resolvedDomains ?? [];
|
|
1080
|
-
const
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
const tlsPolicyId = ownershipRecord.tlsPolicyId;
|
|
1151
|
+
const ownershipRecords = domainArray.map((resolvedDomain) => {
|
|
1152
|
+
return createRouteOwnershipRecord(createOwnerId(), [resolvedDomain], config.root);
|
|
1153
|
+
});
|
|
1084
1154
|
let cleanupStarted = false;
|
|
1085
1155
|
let resolvedPort = null;
|
|
1086
1156
|
let resolvedHost = null;
|
|
1087
1157
|
let setupStarted = false;
|
|
1088
|
-
let
|
|
1158
|
+
let activeOwnershipRecords = [];
|
|
1089
1159
|
let ownershipHeartbeat = null;
|
|
1090
1160
|
function buildDomainResolutionMessage() {
|
|
1091
1161
|
const issues = [];
|
|
@@ -1116,7 +1186,6 @@ function viteCaddyTlsPlugin({
|
|
|
1116
1186
|
console.error(buildDomainResolutionMessage());
|
|
1117
1187
|
return;
|
|
1118
1188
|
}
|
|
1119
|
-
let tlsPolicyAdded = false;
|
|
1120
1189
|
function getPortFromAddress(address) {
|
|
1121
1190
|
if (address && typeof address === "object" && "port" in address) {
|
|
1122
1191
|
const port = address.port;
|
|
@@ -1196,12 +1265,21 @@ function viteCaddyTlsPlugin({
|
|
|
1196
1265
|
clearInterval(ownershipHeartbeat);
|
|
1197
1266
|
ownershipHeartbeat = null;
|
|
1198
1267
|
}
|
|
1199
|
-
if (
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1268
|
+
if (activeOwnershipRecords.length === 0) return;
|
|
1269
|
+
await cleanupOwnershipRecords(activeOwnershipRecords);
|
|
1270
|
+
}
|
|
1271
|
+
async function cleanupOwnershipRecords(records) {
|
|
1272
|
+
const cleanedOwnerIds = /* @__PURE__ */ new Set();
|
|
1273
|
+
for (const activeOwnershipRecord of records) {
|
|
1274
|
+
const cleaned = await cleanupClaimedResources(activeOwnershipRecord, removeWithRetry);
|
|
1275
|
+
if (cleaned) {
|
|
1276
|
+
await releaseOwnershipRecord(activeOwnershipRecord);
|
|
1277
|
+
cleanedOwnerIds.add(activeOwnershipRecord.ownerId);
|
|
1278
|
+
}
|
|
1204
1279
|
}
|
|
1280
|
+
activeOwnershipRecords = activeOwnershipRecords.filter((record) => {
|
|
1281
|
+
return !cleanedOwnerIds.has(record.ownerId);
|
|
1282
|
+
});
|
|
1205
1283
|
}
|
|
1206
1284
|
function onServerClose() {
|
|
1207
1285
|
void cleanupRoute();
|
|
@@ -1246,16 +1324,28 @@ function viteCaddyTlsPlugin({
|
|
|
1246
1324
|
console.error(`Failed to remove ${label} after ${maxAttempts} attempts.`);
|
|
1247
1325
|
return false;
|
|
1248
1326
|
}
|
|
1249
|
-
function startOwnershipHeartbeat(
|
|
1327
|
+
function startOwnershipHeartbeat() {
|
|
1250
1328
|
ownershipHeartbeat = setInterval(() => {
|
|
1251
|
-
|
|
1252
|
-
|
|
1329
|
+
const records = [...activeOwnershipRecords];
|
|
1330
|
+
if (records.length === 0) {
|
|
1331
|
+
if (ownershipHeartbeat) {
|
|
1332
|
+
clearInterval(ownershipHeartbeat);
|
|
1333
|
+
ownershipHeartbeat = null;
|
|
1334
|
+
}
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
void Promise.all(records.map((record) => touchRouteOwnership(record))).then((touchResults) => {
|
|
1338
|
+
if (touchResults.every(Boolean) || !ownershipHeartbeat) {
|
|
1253
1339
|
return;
|
|
1254
1340
|
}
|
|
1341
|
+
const lostRecords = records.filter((_, index) => {
|
|
1342
|
+
return !touchResults[index];
|
|
1343
|
+
});
|
|
1344
|
+
const lostDomains = lostRecords.flatMap((record) => record.domains);
|
|
1255
1345
|
console.error(
|
|
1256
|
-
`Lost route ownership for ${
|
|
1346
|
+
`Lost route ownership for ${lostDomains.join(", ")}. Cleaning up managed Caddy resources.`
|
|
1257
1347
|
);
|
|
1258
|
-
void
|
|
1348
|
+
void cleanupOwnershipRecords(lostRecords);
|
|
1259
1349
|
}).catch((error) => {
|
|
1260
1350
|
console.error(
|
|
1261
1351
|
`Failed to refresh route ownership for ${domainArray.join(", ")}.`,
|
|
@@ -1280,105 +1370,152 @@ function viteCaddyTlsPlugin({
|
|
|
1280
1370
|
}
|
|
1281
1371
|
const port = getServerPort();
|
|
1282
1372
|
const upstreamHost = getUpstreamHost();
|
|
1283
|
-
let
|
|
1373
|
+
let configuredRoutes = false;
|
|
1284
1374
|
try {
|
|
1285
|
-
|
|
1375
|
+
configuredRoutes = await withCaddyApiLock(pluginCaddyApiUrl, async () => {
|
|
1376
|
+
for (const ownershipRecord of ownershipRecords) {
|
|
1377
|
+
const routeDomains = ownershipRecord.domains;
|
|
1378
|
+
const routeId = ownershipRecord.routeId;
|
|
1379
|
+
const tlsPolicyId = ownershipRecord.tlsPolicyId;
|
|
1380
|
+
try {
|
|
1381
|
+
const claimResult = await claimRouteOwnership(ownershipRecord);
|
|
1382
|
+
activeOwnershipRecords.push(claimResult.currentRecord);
|
|
1383
|
+
if (claimResult.status === "replaced") {
|
|
1384
|
+
const preservations = [];
|
|
1385
|
+
let replaced = true;
|
|
1386
|
+
for (const previousRecord of claimResult.previousRecords) {
|
|
1387
|
+
const preservedDomains = getPreservedDomains(previousRecord, routeDomains);
|
|
1388
|
+
if (preservedDomains.length > 0) {
|
|
1389
|
+
const preservation = await createManagedResourcePreservation(
|
|
1390
|
+
previousRecord,
|
|
1391
|
+
preservedDomains,
|
|
1392
|
+
serverName,
|
|
1393
|
+
pluginCaddyApiUrl,
|
|
1394
|
+
pluginCaddyAdminOrigin
|
|
1395
|
+
);
|
|
1396
|
+
if (preservation) {
|
|
1397
|
+
preservations.push(preservation);
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
replaced = await cleanupClaimedResources(previousRecord, removeWithRetry) && replaced;
|
|
1401
|
+
}
|
|
1402
|
+
if (!replaced) {
|
|
1403
|
+
console.error(
|
|
1404
|
+
`Failed to replace previous ownership for ${routeDomains.join(", ")}. Try stopping the other server or removing stale Caddy state manually.`
|
|
1405
|
+
);
|
|
1406
|
+
await discardManagedResourcePreservations(preservations);
|
|
1407
|
+
await cleanupRoute();
|
|
1408
|
+
return false;
|
|
1409
|
+
}
|
|
1410
|
+
try {
|
|
1411
|
+
await Promise.all(
|
|
1412
|
+
preservations.map((preservation) => {
|
|
1413
|
+
return commitManagedResourcePreservation(
|
|
1414
|
+
preservation,
|
|
1415
|
+
pluginCaddyApiUrl,
|
|
1416
|
+
pluginCaddyAdminOrigin
|
|
1417
|
+
);
|
|
1418
|
+
})
|
|
1419
|
+
);
|
|
1420
|
+
} catch (e) {
|
|
1421
|
+
console.error(
|
|
1422
|
+
`Failed to preserve sibling domains while replacing ${routeDomains.join(", ")}.`,
|
|
1423
|
+
e
|
|
1424
|
+
);
|
|
1425
|
+
await discardManagedResourcePreservations(preservations);
|
|
1426
|
+
await cleanupRoute();
|
|
1427
|
+
return false;
|
|
1428
|
+
}
|
|
1429
|
+
await releaseOwnershipRecords(claimResult.previousRecords);
|
|
1430
|
+
}
|
|
1431
|
+
} catch (e) {
|
|
1432
|
+
console.error("Failed to claim route ownership for the resolved domains.", e);
|
|
1433
|
+
await cleanupRoute();
|
|
1434
|
+
return false;
|
|
1435
|
+
}
|
|
1436
|
+
const conflictingRouteIds = (await findManagedRoutesForDomains(
|
|
1437
|
+
routeDomains,
|
|
1438
|
+
serverName,
|
|
1439
|
+
pluginCaddyApiUrl,
|
|
1440
|
+
pluginCaddyAdminOrigin
|
|
1441
|
+
)).filter((existingRouteId) => {
|
|
1442
|
+
return existingRouteId !== routeId;
|
|
1443
|
+
});
|
|
1444
|
+
const conflictingTlsPolicyIds = (await findManagedTlsPoliciesForDomains(
|
|
1445
|
+
routeDomains,
|
|
1446
|
+
pluginCaddyApiUrl,
|
|
1447
|
+
pluginCaddyAdminOrigin
|
|
1448
|
+
)).filter((existingTlsPolicyId) => {
|
|
1449
|
+
return existingTlsPolicyId !== tlsPolicyId;
|
|
1450
|
+
});
|
|
1451
|
+
if (conflictingRouteIds.length > 0 || conflictingTlsPolicyIds.length > 0) {
|
|
1452
|
+
const cleanedOrphans = await cleanupManagedResources(
|
|
1453
|
+
conflictingRouteIds,
|
|
1454
|
+
conflictingTlsPolicyIds,
|
|
1455
|
+
removeWithRetry
|
|
1456
|
+
);
|
|
1457
|
+
if (cleanedOrphans) {
|
|
1458
|
+
console.warn(
|
|
1459
|
+
`Reclaimed orphaned managed Caddy resources for ${routeDomains.join(", ")}.`
|
|
1460
|
+
);
|
|
1461
|
+
} else {
|
|
1462
|
+
console.error(
|
|
1463
|
+
`Cannot point ${routeDomains.join(", ")} to this dev server because Caddy still has orphaned managed resources. Remove the stale Caddy state manually.`
|
|
1464
|
+
);
|
|
1465
|
+
await cleanupRoute();
|
|
1466
|
+
return false;
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
if (tlsPolicyId) {
|
|
1470
|
+
try {
|
|
1471
|
+
await addTlsPolicy(
|
|
1472
|
+
tlsPolicyId,
|
|
1473
|
+
routeDomains,
|
|
1474
|
+
pluginCaddyApiUrl,
|
|
1475
|
+
pluginCaddyAdminOrigin
|
|
1476
|
+
);
|
|
1477
|
+
} catch (e) {
|
|
1478
|
+
console.error(
|
|
1479
|
+
`Failed to add TLS policy to Caddy. Is the Caddy Admin API reachable at ${pluginCaddyApiUrl}?`,
|
|
1480
|
+
e
|
|
1481
|
+
);
|
|
1482
|
+
await cleanupRoute();
|
|
1483
|
+
return false;
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
try {
|
|
1487
|
+
await addRoute(
|
|
1488
|
+
routeId,
|
|
1489
|
+
routeDomains,
|
|
1490
|
+
port,
|
|
1491
|
+
cors,
|
|
1492
|
+
serverName,
|
|
1493
|
+
upstreamHost,
|
|
1494
|
+
upstreamHostHeader,
|
|
1495
|
+
pluginCaddyApiUrl,
|
|
1496
|
+
pluginCaddyAdminOrigin
|
|
1497
|
+
);
|
|
1498
|
+
} catch (e) {
|
|
1499
|
+
console.error(
|
|
1500
|
+
`Failed to add route to Caddy. Is the Caddy Admin API reachable at ${pluginCaddyApiUrl}?`,
|
|
1501
|
+
e
|
|
1502
|
+
);
|
|
1503
|
+
await cleanupRoute();
|
|
1504
|
+
return false;
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
return true;
|
|
1508
|
+
});
|
|
1286
1509
|
} catch (e) {
|
|
1287
|
-
console.error("Failed to
|
|
1510
|
+
console.error("Failed to update Caddy routes.", e);
|
|
1511
|
+
await cleanupRoute();
|
|
1288
1512
|
return;
|
|
1289
1513
|
}
|
|
1290
|
-
if (
|
|
1291
|
-
console.error(buildOwnershipConflictMessage(domainArray, claimResult.existingRecord));
|
|
1292
|
-
return;
|
|
1293
|
-
}
|
|
1294
|
-
activeOwnershipRecord = claimResult.currentRecord;
|
|
1295
|
-
if (claimResult.status === "reclaimed") {
|
|
1296
|
-
let reclaimed = true;
|
|
1297
|
-
for (const previousRecord of claimResult.previousRecords) {
|
|
1298
|
-
reclaimed = await cleanupClaimedResources(previousRecord, removeWithRetry) && reclaimed;
|
|
1299
|
-
}
|
|
1300
|
-
if (!reclaimed) {
|
|
1301
|
-
console.error(
|
|
1302
|
-
`Failed to reclaim stale ownership for ${domainArray.join(", ")}. Try stopping the other server or removing stale Caddy state manually.`
|
|
1303
|
-
);
|
|
1304
|
-
await releaseOwnershipRecord(activeOwnershipRecord);
|
|
1305
|
-
activeOwnershipRecord = null;
|
|
1306
|
-
return;
|
|
1307
|
-
}
|
|
1308
|
-
await releaseOwnershipRecords(claimResult.previousRecords);
|
|
1309
|
-
}
|
|
1310
|
-
const conflictingRouteIds = (await findManagedRoutesForDomains(
|
|
1311
|
-
domainArray,
|
|
1312
|
-
serverName,
|
|
1313
|
-
pluginCaddyApiUrl,
|
|
1314
|
-
pluginCaddyAdminOrigin
|
|
1315
|
-
)).filter((existingRouteId) => {
|
|
1316
|
-
return existingRouteId !== routeId;
|
|
1317
|
-
});
|
|
1318
|
-
const conflictingTlsPolicyIds = (await findManagedTlsPoliciesForDomains(
|
|
1319
|
-
domainArray,
|
|
1320
|
-
pluginCaddyApiUrl,
|
|
1321
|
-
pluginCaddyAdminOrigin
|
|
1322
|
-
)).filter((existingTlsPolicyId) => {
|
|
1323
|
-
return existingTlsPolicyId !== tlsPolicyId;
|
|
1324
|
-
});
|
|
1325
|
-
if (conflictingRouteIds.length > 0 || conflictingTlsPolicyIds.length > 0) {
|
|
1326
|
-
const reclaimedOrphans = await cleanupManagedResources(
|
|
1327
|
-
conflictingRouteIds,
|
|
1328
|
-
conflictingTlsPolicyIds,
|
|
1329
|
-
removeWithRetry
|
|
1330
|
-
);
|
|
1331
|
-
if (reclaimedOrphans) {
|
|
1332
|
-
console.warn(`Reclaimed orphaned managed Caddy resources for ${domainArray.join(", ")}.`);
|
|
1333
|
-
} else {
|
|
1334
|
-
console.error(
|
|
1335
|
-
`Cannot claim ${domainArray.join(", ")} because Caddy still has orphaned managed resources. Remove the stale Caddy state or use \`instanceLabel\` or \`domain\` to make the hostname unique.`
|
|
1336
|
-
);
|
|
1337
|
-
await releaseOwnershipRecord(activeOwnershipRecord);
|
|
1338
|
-
activeOwnershipRecord = null;
|
|
1339
|
-
return;
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
if (tlsPolicyId) {
|
|
1343
|
-
try {
|
|
1344
|
-
await addTlsPolicy(tlsPolicyId, domainArray, pluginCaddyApiUrl, pluginCaddyAdminOrigin);
|
|
1345
|
-
tlsPolicyAdded = true;
|
|
1346
|
-
} catch (e) {
|
|
1347
|
-
console.error(
|
|
1348
|
-
`Failed to add TLS policy to Caddy. Is the Caddy Admin API reachable at ${pluginCaddyApiUrl}?`,
|
|
1349
|
-
e
|
|
1350
|
-
);
|
|
1351
|
-
await releaseOwnershipRecord(activeOwnershipRecord);
|
|
1352
|
-
activeOwnershipRecord = null;
|
|
1353
|
-
return;
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
try {
|
|
1357
|
-
await addRoute(
|
|
1358
|
-
routeId,
|
|
1359
|
-
domainArray,
|
|
1360
|
-
port,
|
|
1361
|
-
cors,
|
|
1362
|
-
serverName,
|
|
1363
|
-
upstreamHost,
|
|
1364
|
-
upstreamHostHeader,
|
|
1365
|
-
pluginCaddyApiUrl,
|
|
1366
|
-
pluginCaddyAdminOrigin
|
|
1367
|
-
);
|
|
1368
|
-
} catch (e) {
|
|
1369
|
-
if (tlsPolicyAdded && tlsPolicyId) {
|
|
1370
|
-
await removeTlsPolicy(tlsPolicyId, pluginCaddyApiUrl, pluginCaddyAdminOrigin);
|
|
1371
|
-
}
|
|
1372
|
-
await releaseOwnershipRecord(activeOwnershipRecord);
|
|
1373
|
-
activeOwnershipRecord = null;
|
|
1374
|
-
console.error(
|
|
1375
|
-
`Failed to add route to Caddy. Is the Caddy Admin API reachable at ${pluginCaddyApiUrl}?`,
|
|
1376
|
-
e
|
|
1377
|
-
);
|
|
1514
|
+
if (!configuredRoutes) {
|
|
1378
1515
|
return;
|
|
1379
1516
|
}
|
|
1380
|
-
if (
|
|
1381
|
-
startOwnershipHeartbeat(
|
|
1517
|
+
if (activeOwnershipRecords.length > 0) {
|
|
1518
|
+
startOwnershipHeartbeat();
|
|
1382
1519
|
}
|
|
1383
1520
|
console.log("\n\u{1F512} Caddy is proxying your traffic on https");
|
|
1384
1521
|
console.log(`
|