vite-plugin-caddy-multiple-tls 1.8.0 → 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 +299 -146
- 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",
|
|
@@ -215,7 +214,14 @@ async function withFileLock(lockPath, fn) {
|
|
|
215
214
|
}
|
|
216
215
|
}
|
|
217
216
|
async function withApiLock(apiUrl, fn) {
|
|
218
|
-
|
|
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);
|
|
219
225
|
}
|
|
220
226
|
async function readRouteOwnershipByPath(recordPath) {
|
|
221
227
|
try {
|
|
@@ -261,20 +267,6 @@ async function listRouteOwnershipRecords(scope) {
|
|
|
261
267
|
);
|
|
262
268
|
});
|
|
263
269
|
}
|
|
264
|
-
function isProcessAlive(pid) {
|
|
265
|
-
try {
|
|
266
|
-
process.kill(pid, 0);
|
|
267
|
-
return true;
|
|
268
|
-
} catch (e) {
|
|
269
|
-
return isNodeError(e) && e.code === "EPERM";
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
function isRouteOwnershipActive(record, now = Date.now()) {
|
|
273
|
-
if (isProcessAlive(record.pid)) {
|
|
274
|
-
return true;
|
|
275
|
-
}
|
|
276
|
-
return record.pid <= 0 && now - record.lastSeenAt <= ROUTE_OWNERSHIP_STALE_AFTER_MS;
|
|
277
|
-
}
|
|
278
270
|
async function claimRouteOwnership(record) {
|
|
279
271
|
const normalizedRecord = normalizeRouteOwnershipRecord(record);
|
|
280
272
|
const { lockPath, recordPath } = getRouteOwnershipPaths(normalizedRecord);
|
|
@@ -294,21 +286,10 @@ async function claimRouteOwnership(record) {
|
|
|
294
286
|
return candidate.ownerId !== normalizedRecord.ownerId && intersectsDomains(candidate.domains, normalizedRecord.domains);
|
|
295
287
|
}
|
|
296
288
|
);
|
|
297
|
-
const activeConflict = overlappingRecords.find((candidate) => {
|
|
298
|
-
return isRouteOwnershipActive(candidate);
|
|
299
|
-
});
|
|
300
|
-
if (activeConflict) {
|
|
301
|
-
claimResult = {
|
|
302
|
-
status: "active-conflict",
|
|
303
|
-
currentRecord: normalizedRecord,
|
|
304
|
-
existingRecord: activeConflict
|
|
305
|
-
};
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
289
|
await writeRouteOwnership(normalizedRecord);
|
|
309
290
|
if (overlappingRecords.length > 0) {
|
|
310
291
|
claimResult = {
|
|
311
|
-
status: "
|
|
292
|
+
status: "replaced",
|
|
312
293
|
currentRecord: normalizedRecord,
|
|
313
294
|
previousRecords: overlappingRecords
|
|
314
295
|
};
|
|
@@ -609,6 +590,106 @@ function intersectsDomains(targetDomains, routeDomains) {
|
|
|
609
590
|
const targetSet = new Set(targetDomains);
|
|
610
591
|
return routeDomains.some((domain) => targetSet.has(domain));
|
|
611
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
|
+
}
|
|
612
693
|
async function findManagedRoutesForDomains(domains, serverName = DEFAULT_SERVER_NAME, apiUrl, adminOrigin) {
|
|
613
694
|
if (domains.length === 0) return [];
|
|
614
695
|
const res = await caddyFetch(
|
|
@@ -983,15 +1064,6 @@ function viteCaddyTlsPlugin({
|
|
|
983
1064
|
lastSeenAt: now
|
|
984
1065
|
};
|
|
985
1066
|
}
|
|
986
|
-
function buildOwnershipConflictMessage(domains, existingRecord) {
|
|
987
|
-
const ownerLocation = existingRecord.configRoot ?? existingRecord.cwd;
|
|
988
|
-
const domainLabel = domains.join(", ");
|
|
989
|
-
return [
|
|
990
|
-
`Cannot claim ${domainLabel}: another Vite server already owns ${domains.length > 1 ? "these domains" : "this domain"}.`,
|
|
991
|
-
`Existing owner pid ${existingRecord.pid} from ${ownerLocation}.`,
|
|
992
|
-
"Stop the other server first, or use `instanceLabel` or `domain` to make the hostname unique."
|
|
993
|
-
].join(" ");
|
|
994
|
-
}
|
|
995
1067
|
async function releaseOwnershipRecord(record) {
|
|
996
1068
|
if (!record) return;
|
|
997
1069
|
await releaseRouteOwnership(record);
|
|
@@ -999,6 +1071,10 @@ function viteCaddyTlsPlugin({
|
|
|
999
1071
|
async function releaseOwnershipRecords(records) {
|
|
1000
1072
|
await Promise.all(records.map((record) => releaseOwnershipRecord(record)));
|
|
1001
1073
|
}
|
|
1074
|
+
function getPreservedDomains(record, replacedDomains) {
|
|
1075
|
+
const replacedDomainSet = new Set(replacedDomains);
|
|
1076
|
+
return record.domains.filter((domain2) => !replacedDomainSet.has(domain2));
|
|
1077
|
+
}
|
|
1002
1078
|
async function cleanupClaimedResources(record, removeWithRetry) {
|
|
1003
1079
|
let cleaned = true;
|
|
1004
1080
|
if (record.tlsPolicyId) {
|
|
@@ -1030,6 +1106,17 @@ function viteCaddyTlsPlugin({
|
|
|
1030
1106
|
}
|
|
1031
1107
|
return cleaned;
|
|
1032
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
|
+
}
|
|
1033
1120
|
function isPreviewServer(server) {
|
|
1034
1121
|
return server.config.isProduction;
|
|
1035
1122
|
}
|
|
@@ -1061,15 +1148,14 @@ function viteCaddyTlsPlugin({
|
|
|
1061
1148
|
instanceLabel
|
|
1062
1149
|
});
|
|
1063
1150
|
const domainArray = resolvedDomains ?? [];
|
|
1064
|
-
const
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
const tlsPolicyId = ownershipRecord.tlsPolicyId;
|
|
1151
|
+
const ownershipRecords = domainArray.map((resolvedDomain) => {
|
|
1152
|
+
return createRouteOwnershipRecord(createOwnerId(), [resolvedDomain], config.root);
|
|
1153
|
+
});
|
|
1068
1154
|
let cleanupStarted = false;
|
|
1069
1155
|
let resolvedPort = null;
|
|
1070
1156
|
let resolvedHost = null;
|
|
1071
1157
|
let setupStarted = false;
|
|
1072
|
-
let
|
|
1158
|
+
let activeOwnershipRecords = [];
|
|
1073
1159
|
let ownershipHeartbeat = null;
|
|
1074
1160
|
function buildDomainResolutionMessage() {
|
|
1075
1161
|
const issues = [];
|
|
@@ -1100,7 +1186,6 @@ function viteCaddyTlsPlugin({
|
|
|
1100
1186
|
console.error(buildDomainResolutionMessage());
|
|
1101
1187
|
return;
|
|
1102
1188
|
}
|
|
1103
|
-
let tlsPolicyAdded = false;
|
|
1104
1189
|
function getPortFromAddress(address) {
|
|
1105
1190
|
if (address && typeof address === "object" && "port" in address) {
|
|
1106
1191
|
const port = address.port;
|
|
@@ -1180,12 +1265,21 @@ function viteCaddyTlsPlugin({
|
|
|
1180
1265
|
clearInterval(ownershipHeartbeat);
|
|
1181
1266
|
ownershipHeartbeat = null;
|
|
1182
1267
|
}
|
|
1183
|
-
if (
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
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
|
+
}
|
|
1188
1279
|
}
|
|
1280
|
+
activeOwnershipRecords = activeOwnershipRecords.filter((record) => {
|
|
1281
|
+
return !cleanedOwnerIds.has(record.ownerId);
|
|
1282
|
+
});
|
|
1189
1283
|
}
|
|
1190
1284
|
function onServerClose() {
|
|
1191
1285
|
void cleanupRoute();
|
|
@@ -1230,16 +1324,28 @@ function viteCaddyTlsPlugin({
|
|
|
1230
1324
|
console.error(`Failed to remove ${label} after ${maxAttempts} attempts.`);
|
|
1231
1325
|
return false;
|
|
1232
1326
|
}
|
|
1233
|
-
function startOwnershipHeartbeat(
|
|
1327
|
+
function startOwnershipHeartbeat() {
|
|
1234
1328
|
ownershipHeartbeat = setInterval(() => {
|
|
1235
|
-
|
|
1236
|
-
|
|
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) {
|
|
1237
1339
|
return;
|
|
1238
1340
|
}
|
|
1341
|
+
const lostRecords = records.filter((_, index) => {
|
|
1342
|
+
return !touchResults[index];
|
|
1343
|
+
});
|
|
1344
|
+
const lostDomains = lostRecords.flatMap((record) => record.domains);
|
|
1239
1345
|
console.error(
|
|
1240
|
-
`Lost route ownership for ${
|
|
1346
|
+
`Lost route ownership for ${lostDomains.join(", ")}. Cleaning up managed Caddy resources.`
|
|
1241
1347
|
);
|
|
1242
|
-
void
|
|
1348
|
+
void cleanupOwnershipRecords(lostRecords);
|
|
1243
1349
|
}).catch((error) => {
|
|
1244
1350
|
console.error(
|
|
1245
1351
|
`Failed to refresh route ownership for ${domainArray.join(", ")}.`,
|
|
@@ -1264,105 +1370,152 @@ function viteCaddyTlsPlugin({
|
|
|
1264
1370
|
}
|
|
1265
1371
|
const port = getServerPort();
|
|
1266
1372
|
const upstreamHost = getUpstreamHost();
|
|
1267
|
-
let
|
|
1373
|
+
let configuredRoutes = false;
|
|
1268
1374
|
try {
|
|
1269
|
-
|
|
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
|
+
});
|
|
1270
1509
|
} catch (e) {
|
|
1271
|
-
console.error("Failed to
|
|
1272
|
-
|
|
1273
|
-
}
|
|
1274
|
-
if (claimResult.status === "active-conflict") {
|
|
1275
|
-
console.error(buildOwnershipConflictMessage(domainArray, claimResult.existingRecord));
|
|
1510
|
+
console.error("Failed to update Caddy routes.", e);
|
|
1511
|
+
await cleanupRoute();
|
|
1276
1512
|
return;
|
|
1277
1513
|
}
|
|
1278
|
-
|
|
1279
|
-
if (claimResult.status === "reclaimed") {
|
|
1280
|
-
let reclaimed = true;
|
|
1281
|
-
for (const previousRecord of claimResult.previousRecords) {
|
|
1282
|
-
reclaimed = await cleanupClaimedResources(previousRecord, removeWithRetry) && reclaimed;
|
|
1283
|
-
}
|
|
1284
|
-
if (!reclaimed) {
|
|
1285
|
-
console.error(
|
|
1286
|
-
`Failed to reclaim stale ownership for ${domainArray.join(", ")}. Try stopping the other server or removing stale Caddy state manually.`
|
|
1287
|
-
);
|
|
1288
|
-
await releaseOwnershipRecord(activeOwnershipRecord);
|
|
1289
|
-
activeOwnershipRecord = null;
|
|
1290
|
-
return;
|
|
1291
|
-
}
|
|
1292
|
-
await releaseOwnershipRecords(claimResult.previousRecords);
|
|
1293
|
-
}
|
|
1294
|
-
const conflictingRouteIds = (await findManagedRoutesForDomains(
|
|
1295
|
-
domainArray,
|
|
1296
|
-
serverName,
|
|
1297
|
-
pluginCaddyApiUrl,
|
|
1298
|
-
pluginCaddyAdminOrigin
|
|
1299
|
-
)).filter((existingRouteId) => {
|
|
1300
|
-
return existingRouteId !== routeId;
|
|
1301
|
-
});
|
|
1302
|
-
const conflictingTlsPolicyIds = (await findManagedTlsPoliciesForDomains(
|
|
1303
|
-
domainArray,
|
|
1304
|
-
pluginCaddyApiUrl,
|
|
1305
|
-
pluginCaddyAdminOrigin
|
|
1306
|
-
)).filter((existingTlsPolicyId) => {
|
|
1307
|
-
return existingTlsPolicyId !== tlsPolicyId;
|
|
1308
|
-
});
|
|
1309
|
-
if (conflictingRouteIds.length > 0 || conflictingTlsPolicyIds.length > 0) {
|
|
1310
|
-
const reclaimedOrphans = await cleanupManagedResources(
|
|
1311
|
-
conflictingRouteIds,
|
|
1312
|
-
conflictingTlsPolicyIds,
|
|
1313
|
-
removeWithRetry
|
|
1314
|
-
);
|
|
1315
|
-
if (reclaimedOrphans) {
|
|
1316
|
-
console.warn(`Reclaimed orphaned managed Caddy resources for ${domainArray.join(", ")}.`);
|
|
1317
|
-
} else {
|
|
1318
|
-
console.error(
|
|
1319
|
-
`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.`
|
|
1320
|
-
);
|
|
1321
|
-
await releaseOwnershipRecord(activeOwnershipRecord);
|
|
1322
|
-
activeOwnershipRecord = null;
|
|
1323
|
-
return;
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
if (tlsPolicyId) {
|
|
1327
|
-
try {
|
|
1328
|
-
await addTlsPolicy(tlsPolicyId, domainArray, pluginCaddyApiUrl, pluginCaddyAdminOrigin);
|
|
1329
|
-
tlsPolicyAdded = true;
|
|
1330
|
-
} catch (e) {
|
|
1331
|
-
console.error(
|
|
1332
|
-
`Failed to add TLS policy to Caddy. Is the Caddy Admin API reachable at ${pluginCaddyApiUrl}?`,
|
|
1333
|
-
e
|
|
1334
|
-
);
|
|
1335
|
-
await releaseOwnershipRecord(activeOwnershipRecord);
|
|
1336
|
-
activeOwnershipRecord = null;
|
|
1337
|
-
return;
|
|
1338
|
-
}
|
|
1339
|
-
}
|
|
1340
|
-
try {
|
|
1341
|
-
await addRoute(
|
|
1342
|
-
routeId,
|
|
1343
|
-
domainArray,
|
|
1344
|
-
port,
|
|
1345
|
-
cors,
|
|
1346
|
-
serverName,
|
|
1347
|
-
upstreamHost,
|
|
1348
|
-
upstreamHostHeader,
|
|
1349
|
-
pluginCaddyApiUrl,
|
|
1350
|
-
pluginCaddyAdminOrigin
|
|
1351
|
-
);
|
|
1352
|
-
} catch (e) {
|
|
1353
|
-
if (tlsPolicyAdded && tlsPolicyId) {
|
|
1354
|
-
await removeTlsPolicy(tlsPolicyId, pluginCaddyApiUrl, pluginCaddyAdminOrigin);
|
|
1355
|
-
}
|
|
1356
|
-
await releaseOwnershipRecord(activeOwnershipRecord);
|
|
1357
|
-
activeOwnershipRecord = null;
|
|
1358
|
-
console.error(
|
|
1359
|
-
`Failed to add route to Caddy. Is the Caddy Admin API reachable at ${pluginCaddyApiUrl}?`,
|
|
1360
|
-
e
|
|
1361
|
-
);
|
|
1514
|
+
if (!configuredRoutes) {
|
|
1362
1515
|
return;
|
|
1363
1516
|
}
|
|
1364
|
-
if (
|
|
1365
|
-
startOwnershipHeartbeat(
|
|
1517
|
+
if (activeOwnershipRecords.length > 0) {
|
|
1518
|
+
startOwnershipHeartbeat();
|
|
1366
1519
|
}
|
|
1367
1520
|
console.log("\n\u{1F512} Caddy is proxying your traffic on https");
|
|
1368
1521
|
console.log(`
|