vite-plugin-caddy-multiple-tls 1.1.0 → 1.3.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.
Files changed (3) hide show
  1. package/README.md +4 -1
  2. package/dist/index.js +231 -108
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # vite-plugin-caddy-multiple-tls
2
2
 
3
+ ## What it does
4
+ Runs Caddy alongside Vite to give you HTTPS locally with automatic, per-branch domains like `<repo>.<branch>.localhost`, so you can use real hostnames, cookies, and secure APIs without manual proxy setup.
5
+
3
6
  ## Usage
4
7
 
5
8
  ```js
@@ -112,7 +115,7 @@ api.app.localhost
112
115
  > - `nip.io`: dynamic parsing of the IP in the hostname (e.g. `app.192.168.1.50.nip.io`) so you can target LAN devices.
113
116
  > Why use them: subdomains behave like real domains, no `/etc/hosts` edits, and closer parity for cookies/CORS rules.
114
117
  >
115
- > When using loopback domains, ensure your Vite config allows the Host header, e.g. `server: { allowedHosts: true }`.
118
+ > When using loopback domains, ensure your Vite config allows the Host header and binds to all interfaces, e.g. `server: { allowedHosts: true, host: true }`.
116
119
  >
117
120
  > For a permanent fix that handles all `*.localhost` domains automatically, install dnsmasq:
118
121
  > ```bash
package/dist/index.js CHANGED
@@ -174,7 +174,13 @@ async function ensureTlsAutomation() {
174
174
  throw new Error(`Failed to initialize Caddy TLS automation: ${text}`);
175
175
  }
176
176
  }
177
- async function addRoute(id, domains, port, cors, serverName = DEFAULT_SERVER_NAME) {
177
+ function formatDialAddress(host, port) {
178
+ if (host.includes(":") && !host.startsWith("[")) {
179
+ return `[${host}]:${port}`;
180
+ }
181
+ return `${host}:${port}`;
182
+ }
183
+ async function addRoute(id, domains, port, cors, serverName = DEFAULT_SERVER_NAME, upstreamHost = "127.0.0.1") {
178
184
  const handlers = [];
179
185
  if (cors) {
180
186
  handlers.push({
@@ -197,7 +203,7 @@ async function addRoute(id, domains, port, cors, serverName = DEFAULT_SERVER_NAM
197
203
  }
198
204
  handlers.push({
199
205
  handler: "reverse_proxy",
200
- upstreams: [{ dial: `localhost:${port}` }]
206
+ upstreams: [{ dial: formatDialAddress(upstreamHost, port) }]
201
207
  });
202
208
  const route = {
203
209
  "@id": id,
@@ -311,6 +317,15 @@ function resolveBaseDomain(options) {
311
317
  }
312
318
  return "localhost";
313
319
  }
320
+ function resolveUpstreamHost(host) {
321
+ if (typeof host === "string") {
322
+ const trimmed = host.trim();
323
+ if (trimmed && trimmed !== "0.0.0.0" && trimmed !== "::") {
324
+ return trimmed;
325
+ }
326
+ }
327
+ return "127.0.0.1";
328
+ }
314
329
  function sanitizeDomainLabel(value) {
315
330
  return value.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
316
331
  }
@@ -351,132 +366,240 @@ function viteCaddyTlsPlugin({
351
366
  serverName,
352
367
  internalTls
353
368
  } = {}) {
354
- return {
355
- name: "vite:caddy-tls",
356
- configureServer({ httpServer, config }) {
357
- const fallbackPort = config.server.port || 5173;
358
- const resolvedDomain = resolveDomain({
359
- domain,
360
- baseDomain,
361
- loopbackDomain,
362
- repo,
363
- branch
364
- });
365
- const domainArray = resolvedDomain ? [resolvedDomain] : [];
366
- const routeId = `vite-proxy-${Date.now()}-${Math.floor(Math.random() * 1e3)}`;
367
- const shouldUseInternalTls = internalTls ?? (baseDomain !== void 0 || loopbackDomain !== void 0 || domain !== void 0);
368
- const tlsPolicyId = shouldUseInternalTls ? `${routeId}-tls` : null;
369
- let cleanupStarted = false;
370
- if (domainArray.length === 0) {
371
- console.error(
372
- "No domain resolved. Provide domain, or run inside a git repo, or pass repo/branch."
373
- );
374
- return;
369
+ function isPreviewServer(server) {
370
+ return server.config.isProduction;
371
+ }
372
+ function getPreviewPort(config) {
373
+ if (typeof config.preview?.port === "number") {
374
+ return config.preview.port;
375
+ }
376
+ return null;
377
+ }
378
+ function getPreviewHost(config) {
379
+ if (config.preview && "host" in config.preview) {
380
+ return resolveUpstreamHost(config.preview.host);
381
+ }
382
+ return null;
383
+ }
384
+ function hasListen(server) {
385
+ return !!server && typeof server === "object" && "listen" in server && typeof server.listen === "function";
386
+ }
387
+ function setupServer(server) {
388
+ const { httpServer, config } = server;
389
+ const previewMode = isPreviewServer(server);
390
+ const fallbackPort = previewMode ? getPreviewPort(config) ?? 4173 : config.server.port || 5173;
391
+ const resolvedDomain = resolveDomain({
392
+ domain,
393
+ baseDomain,
394
+ loopbackDomain,
395
+ repo,
396
+ branch
397
+ });
398
+ const domainArray = resolvedDomain ? [resolvedDomain] : [];
399
+ const routeId = `vite-proxy-${Date.now()}-${Math.floor(Math.random() * 1e3)}`;
400
+ const shouldUseInternalTls = internalTls ?? (baseDomain !== void 0 || loopbackDomain !== void 0 || domain !== void 0);
401
+ const tlsPolicyId = shouldUseInternalTls ? `${routeId}-tls` : null;
402
+ let cleanupStarted = false;
403
+ let resolvedPort = null;
404
+ let resolvedHost = null;
405
+ let setupStarted = false;
406
+ if (domainArray.length === 0) {
407
+ console.error(
408
+ "No domain resolved. Provide domain, or run inside a git repo, or pass repo/branch."
409
+ );
410
+ return;
411
+ }
412
+ let tlsPolicyAdded = false;
413
+ function getPortFromAddress(address) {
414
+ if (address && typeof address === "object" && "port" in address) {
415
+ const port = address.port;
416
+ if (typeof port === "number") {
417
+ return port;
418
+ }
375
419
  }
376
- let tlsPolicyAdded = false;
377
- function getServerPort() {
378
- if (!httpServer) return fallbackPort;
379
- const address = httpServer.address();
380
- if (address && typeof address === "object" && "port" in address) {
381
- return address.port;
420
+ return null;
421
+ }
422
+ function updateResolvedTarget() {
423
+ if (resolvedPort !== null && resolvedHost !== null) return;
424
+ const resolvedUrl = server.resolvedUrls?.local?.[0];
425
+ if (resolvedUrl) {
426
+ try {
427
+ const url = new URL(resolvedUrl);
428
+ if (resolvedHost === null && url.hostname) {
429
+ resolvedHost = url.hostname === "localhost" ? "127.0.0.1" : url.hostname;
430
+ }
431
+ const port = Number(url.port);
432
+ if (resolvedPort === null && !Number.isNaN(port)) {
433
+ resolvedPort = port;
434
+ }
435
+ } catch (e) {
382
436
  }
383
- return fallbackPort;
384
437
  }
385
- async function cleanupRoute() {
386
- if (cleanupStarted) return;
387
- cleanupStarted = true;
388
- if (tlsPolicyId) {
389
- await removeTlsPolicy(tlsPolicyId);
438
+ if (httpServer) {
439
+ const address = httpServer.address();
440
+ if (address && typeof address === "object") {
441
+ const port = getPortFromAddress(address);
442
+ if (resolvedPort === null && port !== null) {
443
+ resolvedPort = port;
444
+ }
445
+ if (resolvedHost === null && "address" in address) {
446
+ const host = address.address;
447
+ if (typeof host === "string" && host !== "0.0.0.0" && host !== "::") {
448
+ resolvedHost = host;
449
+ }
450
+ }
390
451
  }
391
- await removeRoute(routeId);
392
452
  }
393
- function onServerClose() {
394
- void cleanupRoute();
453
+ if (resolvedPort === null && typeof config.server.port === "number") {
454
+ resolvedPort = config.server.port;
395
455
  }
396
- function handleSignal(signal) {
397
- process.off("SIGINT", onSigint);
398
- process.off("SIGTERM", onSigterm);
399
- void cleanupRoute().finally(() => {
400
- process.kill(process.pid, signal);
401
- });
456
+ if (previewMode && resolvedPort === null) {
457
+ const previewPort = getPreviewPort(config);
458
+ if (previewPort !== null) {
459
+ resolvedPort = previewPort;
460
+ }
402
461
  }
403
- function onSigint() {
404
- handleSignal("SIGINT");
462
+ if (resolvedHost === null) {
463
+ if (previewMode) {
464
+ resolvedHost = getPreviewHost(config);
465
+ }
466
+ if (resolvedHost === null) {
467
+ resolvedHost = resolveUpstreamHost(config.server.host);
468
+ }
405
469
  }
406
- function onSigterm() {
407
- handleSignal("SIGTERM");
470
+ }
471
+ function getServerPort() {
472
+ updateResolvedTarget();
473
+ return resolvedPort ?? fallbackPort;
474
+ }
475
+ function getUpstreamHost() {
476
+ updateResolvedTarget();
477
+ return resolvedHost ?? "127.0.0.1";
478
+ }
479
+ async function cleanupRoute() {
480
+ if (cleanupStarted) return;
481
+ cleanupStarted = true;
482
+ if (tlsPolicyId) {
483
+ await removeTlsPolicy(tlsPolicyId);
408
484
  }
409
- function registerProcessCleanup() {
410
- process.once("SIGINT", onSigint);
411
- process.once("SIGTERM", onSigterm);
485
+ await removeRoute(routeId);
486
+ }
487
+ function onServerClose() {
488
+ void cleanupRoute();
489
+ }
490
+ function handleSignal(signal) {
491
+ process.off("SIGINT", onSigint);
492
+ process.off("SIGTERM", onSigterm);
493
+ void cleanupRoute().finally(() => {
494
+ process.kill(process.pid, signal);
495
+ });
496
+ }
497
+ function onSigint() {
498
+ handleSignal("SIGINT");
499
+ }
500
+ function onSigterm() {
501
+ handleSignal("SIGTERM");
502
+ }
503
+ function registerProcessCleanup() {
504
+ process.once("SIGINT", onSigint);
505
+ process.once("SIGTERM", onSigterm);
506
+ }
507
+ async function setupRoute() {
508
+ if (!validateCaddyIsInstalled()) {
509
+ return;
412
510
  }
413
- async function setupRoute() {
414
- if (!validateCaddyIsInstalled()) {
415
- return;
416
- }
417
- let running = await isCaddyRunning();
511
+ let running = await isCaddyRunning();
512
+ if (!running) {
513
+ running = await startCaddy();
418
514
  if (!running) {
419
- running = await startCaddy();
420
- if (!running) {
421
- console.error("Failed to start Caddy server.");
422
- return;
423
- }
424
- }
425
- try {
426
- await ensureBaseConfig(serverName);
427
- } catch (e) {
428
- console.error("Failed to configure Caddy base settings.", e);
515
+ console.error("Failed to start Caddy server.");
429
516
  return;
430
517
  }
431
- const port = getServerPort();
432
- if (tlsPolicyId) {
433
- try {
434
- await addTlsPolicy(tlsPolicyId, domainArray);
435
- tlsPolicyAdded = true;
436
- } catch (e) {
437
- console.error("Failed to add TLS policy to Caddy.", e);
438
- return;
439
- }
440
- }
518
+ }
519
+ try {
520
+ await ensureBaseConfig(serverName);
521
+ } catch (e) {
522
+ console.error("Failed to configure Caddy base settings.", e);
523
+ return;
524
+ }
525
+ const port = getServerPort();
526
+ const upstreamHost = getUpstreamHost();
527
+ if (tlsPolicyId) {
441
528
  try {
442
- await addRoute(routeId, domainArray, port, cors, serverName);
529
+ await addTlsPolicy(tlsPolicyId, domainArray);
530
+ tlsPolicyAdded = true;
443
531
  } catch (e) {
444
- if (tlsPolicyAdded && tlsPolicyId) {
445
- await removeTlsPolicy(tlsPolicyId);
446
- }
447
- console.error("Failed to add route to Caddy.", e);
532
+ console.error("Failed to add TLS policy to Caddy.", e);
448
533
  return;
449
534
  }
450
- console.log();
451
- console.log("\u{1F512} Caddy is proxying your traffic on https");
452
- console.log();
453
- console.log(
454
- `\u{1F517} Access your local ${domainArray.length > 1 ? "servers" : "server"} `
455
- );
456
- domainArray.forEach((domain2) => {
457
- console.log(`\u{1F30D} https://${domain2}`);
458
- });
459
- if (process.platform === "linux" && !loopbackDomain) {
460
- console.log();
461
- console.log("\u{1F427} Linux users: if the domain doesn't resolve, run:");
462
- domainArray.forEach((domain2) => {
463
- console.log(` echo "127.0.0.1 ${domain2}" | sudo tee -a /etc/hosts`);
464
- });
465
- }
466
- console.log();
467
- registerProcessCleanup();
468
- httpServer?.once("close", onServerClose);
469
535
  }
470
- function onListening() {
471
- void setupRoute();
536
+ try {
537
+ await addRoute(routeId, domainArray, port, cors, serverName, upstreamHost);
538
+ } catch (e) {
539
+ if (tlsPolicyAdded && tlsPolicyId) {
540
+ await removeTlsPolicy(tlsPolicyId);
541
+ }
542
+ console.error("Failed to add route to Caddy.", e);
543
+ return;
472
544
  }
473
- if (httpServer?.listening) {
474
- void setupRoute();
475
- } else if (httpServer) {
476
- httpServer.once("listening", onListening);
477
- } else {
478
- void setupRoute();
545
+ console.log("\n\u{1F512} Caddy is proxying your traffic on https");
546
+ console.log(
547
+ `
548
+ \u{1F517} Access your local ${domainArray.length > 1 ? "servers" : "server"}!`
549
+ );
550
+ domainArray.forEach((domain2) => {
551
+ console.log(`\u{1F30D} https://${domain2}`);
552
+ });
553
+ if (process.platform === "linux" && !loopbackDomain) {
554
+ console.log("\n\u{1F427} Linux users: if the domain doesn't resolve, run:");
555
+ domainArray.forEach((domain2) => {
556
+ console.log(` echo "127.0.0.1 ${domain2}" | sudo tee -a /etc/hosts`);
557
+ });
479
558
  }
559
+ console.log();
560
+ registerProcessCleanup();
561
+ httpServer?.once("close", onServerClose);
562
+ }
563
+ function runSetupOnce() {
564
+ if (setupStarted) return;
565
+ setupStarted = true;
566
+ void setupRoute();
567
+ }
568
+ function wrapServerListen() {
569
+ if (!hasListen(server)) return false;
570
+ const originalListen = server.listen.bind(server);
571
+ server.listen = async function(port, isRestart) {
572
+ const result = await originalListen(port, isRestart);
573
+ if (typeof port === "number") {
574
+ resolvedPort = port;
575
+ } else {
576
+ updateResolvedTarget();
577
+ }
578
+ runSetupOnce();
579
+ return result;
580
+ };
581
+ return true;
582
+ }
583
+ function onListening() {
584
+ updateResolvedTarget();
585
+ runSetupOnce();
586
+ }
587
+ const listenWrapped = wrapServerListen();
588
+ if (httpServer?.listening) {
589
+ runSetupOnce();
590
+ } else if (httpServer) {
591
+ httpServer.once("listening", onListening);
592
+ } else if (!listenWrapped) {
593
+ runSetupOnce();
594
+ }
595
+ }
596
+ return {
597
+ name: "vite:caddy-tls",
598
+ configureServer(server) {
599
+ setupServer(server);
600
+ },
601
+ configurePreviewServer(server) {
602
+ setupServer(server);
480
603
  }
481
604
  };
482
605
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-caddy-multiple-tls",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "Vite plugin that uses Caddy to provide local HTTPS with derived domains.",
5
5
  "keywords": [
6
6
  "vite",