unplugin-https-reverse-proxy 4.0.2 → 4.0.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/vite.cjs CHANGED
@@ -13455,7 +13455,7 @@ function validateTemplateContext(ctx) {
13455
13455
  }
13456
13456
  var caddyTemplate = `{
13457
13457
  debug
13458
- auto_https disable_redirects
13458
+ auto_https disable_redirects ignore_loaded_certs
13459
13459
  }
13460
13460
 
13461
13461
  :7601 {
@@ -13722,6 +13722,97 @@ function useCaddyConfig() {
13722
13722
  };
13723
13723
  }
13724
13724
 
13725
+ // src/caddy/log.ts
13726
+ function isCaddyReloadCancellation(line) {
13727
+ try {
13728
+ const entry = JSON.parse(line);
13729
+ return entry.logger === "tls" && typeof entry.error === "string" && entry.error.endsWith("context canceled");
13730
+ } catch {
13731
+ return false;
13732
+ }
13733
+ }
13734
+
13735
+ // src/caddy/tls-health.ts
13736
+ var import_node_tls = require("tls");
13737
+ function assessTlsCertificate(certificate, hostname, now = Date.now(), verifyHostname = import_node_tls.checkServerIdentity) {
13738
+ if (!certificate.valid_to)
13739
+ return { healthy: false, reason: "certificate_missing" };
13740
+ const expiresAt = new Date(certificate.valid_to);
13741
+ if (Number.isNaN(expiresAt.getTime()))
13742
+ return { healthy: false, reason: "certificate_invalid" };
13743
+ if (expiresAt.getTime() <= now)
13744
+ return { healthy: false, reason: "certificate_expired", expiresAt };
13745
+ try {
13746
+ if (verifyHostname(hostname, certificate))
13747
+ return { healthy: false, reason: "hostname_mismatch" };
13748
+ } catch {
13749
+ return { healthy: false, reason: "certificate_invalid" };
13750
+ }
13751
+ return { healthy: true, expiresAt };
13752
+ }
13753
+ function probeLocalTls(hostname, options = {}) {
13754
+ const {
13755
+ connect: openTlsConnection = import_node_tls.connect,
13756
+ port = 443,
13757
+ timeoutMs = 1e3
13758
+ } = options;
13759
+ return new Promise((resolve2) => {
13760
+ let settled = false;
13761
+ let timeout;
13762
+ const socket = openTlsConnection({
13763
+ host: "127.0.0.1",
13764
+ port,
13765
+ servername: hostname,
13766
+ rejectUnauthorized: false
13767
+ });
13768
+ const finish = (result) => {
13769
+ if (settled)
13770
+ return;
13771
+ settled = true;
13772
+ if (timeout)
13773
+ clearTimeout(timeout);
13774
+ socket.destroy();
13775
+ resolve2(result);
13776
+ };
13777
+ timeout = setTimeout(() => {
13778
+ finish({ healthy: false, reason: "connection_failed" });
13779
+ }, timeoutMs);
13780
+ socket.once("secureConnect", () => {
13781
+ finish(assessTlsCertificate(socket.getPeerCertificate(), hostname));
13782
+ });
13783
+ socket.once("error", () => {
13784
+ finish({ healthy: false, reason: "connection_failed" });
13785
+ });
13786
+ });
13787
+ }
13788
+ function sleep(ms) {
13789
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
13790
+ }
13791
+ async function waitForLocalTls(hostname, options = {}) {
13792
+ const {
13793
+ probe: checkTls = probeLocalTls,
13794
+ retryIntervalMs = 250,
13795
+ retryWindowMs = 3e4,
13796
+ sleep: wait = sleep,
13797
+ ...probeOptions
13798
+ } = options;
13799
+ const deadline = Date.now() + retryWindowMs;
13800
+ let result = { healthy: false, reason: "connection_failed" };
13801
+ do {
13802
+ result = await checkTls(hostname, probeOptions);
13803
+ if (result.healthy)
13804
+ return result;
13805
+ const remaining = deadline - Date.now();
13806
+ if (remaining > 0)
13807
+ await wait(Math.min(retryIntervalMs, remaining));
13808
+ } while (Date.now() < deadline);
13809
+ return result;
13810
+ }
13811
+ function formatTlsHealthFailure(hostname, result) {
13812
+ const reason = result.reason === "certificate_expired" && result.expiresAt ? `the certificate expired at ${result.expiresAt.toISOString()}` : result.reason.replaceAll("_", " ");
13813
+ return `[unplugin-https-reverse-proxy] Caddy cannot serve valid HTTPS for ${hostname}: ${reason}. Restart Caddy to retry its automatic certificate renewal. Do not remove Caddy PKI data automatically because that replaces the trusted local root certificate.`;
13814
+ }
13815
+
13725
13816
  // src/caddy/utils.ts
13726
13817
  var import_node_child_process3 = require("child_process");
13727
13818
  var import_node_net = require("net");
@@ -13911,6 +14002,39 @@ var CaddyInstant = class {
13911
14002
  await download();
13912
14003
  this.inited = true;
13913
14004
  }
14005
+ async ensureTlsReady(targetHost) {
14006
+ const health = await waitForLocalTls(targetHost);
14007
+ if (!health.healthy)
14008
+ throw new Error(formatTlsHealthFailure(targetHost, health));
14009
+ }
14010
+ async updateCaddyfile() {
14011
+ const { configJsonRef } = getCaddyConfig();
14012
+ const proxies = Object.values(configJsonRef() || {});
14013
+ const ctx = validateTemplateContext({
14014
+ caddy_root: this.caddyRoot,
14015
+ proxies
14016
+ });
14017
+ this.caddyfile = await genCaddyfile(ctx);
14018
+ await this.writeCaddyfile();
14019
+ }
14020
+ async stopCaddy() {
14021
+ var _a4;
14022
+ try {
14023
+ await import_got_cjs.got.post("http://127.0.0.1:2019/stop", {
14024
+ headers: {
14025
+ Origin: "http://localhost:2019"
14026
+ },
14027
+ retry: {
14028
+ limit: 0
14029
+ },
14030
+ timeout: {
14031
+ request: 2e3
14032
+ }
14033
+ });
14034
+ } catch {
14035
+ }
14036
+ (_a4 = this._caddyChild) == null ? void 0 : _a4.kill();
14037
+ }
13914
14038
  /**
13915
14039
  * @param source like `127.0.0.1:8080`
13916
14040
  * @param target like `test.abc.com`. And if you provide a port ,it will be ignore.
@@ -13955,16 +14079,16 @@ var CaddyInstant = class {
13955
14079
  ];
13956
14080
  const { writeCaddyConfig } = getCaddyConfig();
13957
14081
  writeCaddyConfig(proxies);
14082
+ await this.updateCaddyfile();
14083
+ let skipInitialCaddyfileEffect = true;
13958
14084
  this._stopEffects.push(
13959
- effect(async () => {
13960
- const { configJsonRef } = getCaddyConfig();
13961
- const proxies2 = Object.values(configJsonRef() || {});
13962
- const ctx = validateTemplateContext({
13963
- caddy_root: this.caddyRoot,
13964
- proxies: proxies2
13965
- });
13966
- this.caddyfile = await genCaddyfile(ctx);
13967
- await this.writeCaddyfile();
14085
+ effect(() => {
14086
+ getCaddyConfig().configJsonRef();
14087
+ if (skipInitialCaddyfileEffect) {
14088
+ skipInitialCaddyfileEffect = false;
14089
+ return;
14090
+ }
14091
+ void this.updateCaddyfile();
13968
14092
  })
13969
14093
  );
13970
14094
  if (!(0, import_node_fs3.existsSync)(caddyLockFilePath))
@@ -13977,6 +14101,21 @@ var CaddyInstant = class {
13977
14101
  }
13978
14102
  return new Promise((resolve2, reject2) => {
13979
14103
  var _a4, _b3;
14104
+ let startupCheckStarted = false;
14105
+ const resolveWhenReady = async () => {
14106
+ if (startupCheckStarted)
14107
+ return;
14108
+ startupCheckStarted = true;
14109
+ try {
14110
+ if (https)
14111
+ await this.ensureTlsReady(targetHost);
14112
+ resolve2();
14113
+ } catch (error) {
14114
+ if (!this.locked)
14115
+ await this.baseCleanup(restore);
14116
+ reject2(error);
14117
+ }
14118
+ };
13980
14119
  const setupCleanup = () => {
13981
14120
  import_node_process4.default.on("SIGINT", async () => {
13982
14121
  if (this.stoped)
@@ -14036,6 +14175,10 @@ var CaddyInstant = class {
14036
14175
  const lines = data.toString().split("\n").map((line) => line.trim());
14037
14176
  for (const line of lines) {
14038
14177
  showCaddyLog && line && console.info(line);
14178
+ if (isCaddyReloadCancellation(line)) {
14179
+ consola.info("Caddy restarted before TLS issuance completed; retrying.\n");
14180
+ continue;
14181
+ }
14039
14182
  try {
14040
14183
  if (line.includes("Error:") || line && JSON.parse(line).level === "error") {
14041
14184
  if (line.includes("parsing caddyfile tokens")) {
@@ -14056,23 +14199,30 @@ ${line}
14056
14199
  if (line.includes("config file changed"))
14057
14200
  consola.success("Detect added a new reverse proxy config, restarted.\n");
14058
14201
  if (line.includes("caddy proxying") || line.includes("serving initial"))
14059
- return resolve2();
14202
+ void resolveWhenReady();
14060
14203
  }
14061
14204
  });
14062
14205
  (_b3 = this._caddyChild.stdout) == null ? void 0 : _b3.on("data", (_data) => {
14063
14206
  consola.info(_data.toString());
14064
- resolve2();
14207
+ void resolveWhenReady();
14065
14208
  });
14066
14209
  setupCleanup();
14067
14210
  } else {
14068
14211
  consola.info("Caddy is running in another process.");
14069
- setupCleanup();
14070
- return resolve2();
14212
+ void (async () => {
14213
+ try {
14214
+ if (https)
14215
+ await this.ensureTlsReady(targetHost);
14216
+ setupCleanup();
14217
+ resolve2();
14218
+ } catch (error) {
14219
+ reject2(error);
14220
+ }
14221
+ })();
14071
14222
  }
14072
14223
  });
14073
14224
  }
14074
14225
  async baseCleanup(restore = true) {
14075
- var _a4;
14076
14226
  this._stopEffects.forEach((fn) => fn());
14077
14227
  try {
14078
14228
  if (restore && this._source && this._target) {
@@ -14086,7 +14236,7 @@ ${line}
14086
14236
  consola.fail("Restore host failed.\n");
14087
14237
  }
14088
14238
  if (!this.locked && !this.stoped) {
14089
- (_a4 = this._caddyChild) == null ? void 0 : _a4.kill();
14239
+ await this.stopCaddy();
14090
14240
  try {
14091
14241
  await lockfile.unlock(caddyLockFilePath).catch(() => {
14092
14242
  });
package/dist/vite.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  unpluginFactory
3
- } from "./chunk-JA3D5AWS.js";
3
+ } from "./chunk-MD5CVZP7.js";
4
4
  import "./chunk-RSBXFJ3Z.js";
5
5
  import "./chunk-2P7SUCQ3.js";
6
6
 
package/dist/webpack.cjs CHANGED
@@ -13455,7 +13455,7 @@ function validateTemplateContext(ctx) {
13455
13455
  }
13456
13456
  var caddyTemplate = `{
13457
13457
  debug
13458
- auto_https disable_redirects
13458
+ auto_https disable_redirects ignore_loaded_certs
13459
13459
  }
13460
13460
 
13461
13461
  :7601 {
@@ -13722,6 +13722,97 @@ function useCaddyConfig() {
13722
13722
  };
13723
13723
  }
13724
13724
 
13725
+ // src/caddy/log.ts
13726
+ function isCaddyReloadCancellation(line) {
13727
+ try {
13728
+ const entry = JSON.parse(line);
13729
+ return entry.logger === "tls" && typeof entry.error === "string" && entry.error.endsWith("context canceled");
13730
+ } catch {
13731
+ return false;
13732
+ }
13733
+ }
13734
+
13735
+ // src/caddy/tls-health.ts
13736
+ var import_node_tls = require("tls");
13737
+ function assessTlsCertificate(certificate, hostname, now = Date.now(), verifyHostname = import_node_tls.checkServerIdentity) {
13738
+ if (!certificate.valid_to)
13739
+ return { healthy: false, reason: "certificate_missing" };
13740
+ const expiresAt = new Date(certificate.valid_to);
13741
+ if (Number.isNaN(expiresAt.getTime()))
13742
+ return { healthy: false, reason: "certificate_invalid" };
13743
+ if (expiresAt.getTime() <= now)
13744
+ return { healthy: false, reason: "certificate_expired", expiresAt };
13745
+ try {
13746
+ if (verifyHostname(hostname, certificate))
13747
+ return { healthy: false, reason: "hostname_mismatch" };
13748
+ } catch {
13749
+ return { healthy: false, reason: "certificate_invalid" };
13750
+ }
13751
+ return { healthy: true, expiresAt };
13752
+ }
13753
+ function probeLocalTls(hostname, options = {}) {
13754
+ const {
13755
+ connect: openTlsConnection = import_node_tls.connect,
13756
+ port = 443,
13757
+ timeoutMs = 1e3
13758
+ } = options;
13759
+ return new Promise((resolve2) => {
13760
+ let settled = false;
13761
+ let timeout;
13762
+ const socket = openTlsConnection({
13763
+ host: "127.0.0.1",
13764
+ port,
13765
+ servername: hostname,
13766
+ rejectUnauthorized: false
13767
+ });
13768
+ const finish = (result) => {
13769
+ if (settled)
13770
+ return;
13771
+ settled = true;
13772
+ if (timeout)
13773
+ clearTimeout(timeout);
13774
+ socket.destroy();
13775
+ resolve2(result);
13776
+ };
13777
+ timeout = setTimeout(() => {
13778
+ finish({ healthy: false, reason: "connection_failed" });
13779
+ }, timeoutMs);
13780
+ socket.once("secureConnect", () => {
13781
+ finish(assessTlsCertificate(socket.getPeerCertificate(), hostname));
13782
+ });
13783
+ socket.once("error", () => {
13784
+ finish({ healthy: false, reason: "connection_failed" });
13785
+ });
13786
+ });
13787
+ }
13788
+ function sleep(ms) {
13789
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
13790
+ }
13791
+ async function waitForLocalTls(hostname, options = {}) {
13792
+ const {
13793
+ probe: checkTls = probeLocalTls,
13794
+ retryIntervalMs = 250,
13795
+ retryWindowMs = 3e4,
13796
+ sleep: wait = sleep,
13797
+ ...probeOptions
13798
+ } = options;
13799
+ const deadline = Date.now() + retryWindowMs;
13800
+ let result = { healthy: false, reason: "connection_failed" };
13801
+ do {
13802
+ result = await checkTls(hostname, probeOptions);
13803
+ if (result.healthy)
13804
+ return result;
13805
+ const remaining = deadline - Date.now();
13806
+ if (remaining > 0)
13807
+ await wait(Math.min(retryIntervalMs, remaining));
13808
+ } while (Date.now() < deadline);
13809
+ return result;
13810
+ }
13811
+ function formatTlsHealthFailure(hostname, result) {
13812
+ const reason = result.reason === "certificate_expired" && result.expiresAt ? `the certificate expired at ${result.expiresAt.toISOString()}` : result.reason.replaceAll("_", " ");
13813
+ return `[unplugin-https-reverse-proxy] Caddy cannot serve valid HTTPS for ${hostname}: ${reason}. Restart Caddy to retry its automatic certificate renewal. Do not remove Caddy PKI data automatically because that replaces the trusted local root certificate.`;
13814
+ }
13815
+
13725
13816
  // src/caddy/utils.ts
13726
13817
  var import_node_child_process3 = require("child_process");
13727
13818
  var import_node_net = require("net");
@@ -13911,6 +14002,39 @@ var CaddyInstant = class {
13911
14002
  await download();
13912
14003
  this.inited = true;
13913
14004
  }
14005
+ async ensureTlsReady(targetHost) {
14006
+ const health = await waitForLocalTls(targetHost);
14007
+ if (!health.healthy)
14008
+ throw new Error(formatTlsHealthFailure(targetHost, health));
14009
+ }
14010
+ async updateCaddyfile() {
14011
+ const { configJsonRef } = getCaddyConfig();
14012
+ const proxies = Object.values(configJsonRef() || {});
14013
+ const ctx = validateTemplateContext({
14014
+ caddy_root: this.caddyRoot,
14015
+ proxies
14016
+ });
14017
+ this.caddyfile = await genCaddyfile(ctx);
14018
+ await this.writeCaddyfile();
14019
+ }
14020
+ async stopCaddy() {
14021
+ var _a4;
14022
+ try {
14023
+ await import_got_cjs.got.post("http://127.0.0.1:2019/stop", {
14024
+ headers: {
14025
+ Origin: "http://localhost:2019"
14026
+ },
14027
+ retry: {
14028
+ limit: 0
14029
+ },
14030
+ timeout: {
14031
+ request: 2e3
14032
+ }
14033
+ });
14034
+ } catch {
14035
+ }
14036
+ (_a4 = this._caddyChild) == null ? void 0 : _a4.kill();
14037
+ }
13914
14038
  /**
13915
14039
  * @param source like `127.0.0.1:8080`
13916
14040
  * @param target like `test.abc.com`. And if you provide a port ,it will be ignore.
@@ -13955,16 +14079,16 @@ var CaddyInstant = class {
13955
14079
  ];
13956
14080
  const { writeCaddyConfig } = getCaddyConfig();
13957
14081
  writeCaddyConfig(proxies);
14082
+ await this.updateCaddyfile();
14083
+ let skipInitialCaddyfileEffect = true;
13958
14084
  this._stopEffects.push(
13959
- effect(async () => {
13960
- const { configJsonRef } = getCaddyConfig();
13961
- const proxies2 = Object.values(configJsonRef() || {});
13962
- const ctx = validateTemplateContext({
13963
- caddy_root: this.caddyRoot,
13964
- proxies: proxies2
13965
- });
13966
- this.caddyfile = await genCaddyfile(ctx);
13967
- await this.writeCaddyfile();
14085
+ effect(() => {
14086
+ getCaddyConfig().configJsonRef();
14087
+ if (skipInitialCaddyfileEffect) {
14088
+ skipInitialCaddyfileEffect = false;
14089
+ return;
14090
+ }
14091
+ void this.updateCaddyfile();
13968
14092
  })
13969
14093
  );
13970
14094
  if (!(0, import_node_fs3.existsSync)(caddyLockFilePath))
@@ -13977,6 +14101,21 @@ var CaddyInstant = class {
13977
14101
  }
13978
14102
  return new Promise((resolve2, reject2) => {
13979
14103
  var _a4, _b3;
14104
+ let startupCheckStarted = false;
14105
+ const resolveWhenReady = async () => {
14106
+ if (startupCheckStarted)
14107
+ return;
14108
+ startupCheckStarted = true;
14109
+ try {
14110
+ if (https)
14111
+ await this.ensureTlsReady(targetHost);
14112
+ resolve2();
14113
+ } catch (error) {
14114
+ if (!this.locked)
14115
+ await this.baseCleanup(restore);
14116
+ reject2(error);
14117
+ }
14118
+ };
13980
14119
  const setupCleanup = () => {
13981
14120
  import_node_process4.default.on("SIGINT", async () => {
13982
14121
  if (this.stoped)
@@ -14036,6 +14175,10 @@ var CaddyInstant = class {
14036
14175
  const lines = data.toString().split("\n").map((line) => line.trim());
14037
14176
  for (const line of lines) {
14038
14177
  showCaddyLog && line && console.info(line);
14178
+ if (isCaddyReloadCancellation(line)) {
14179
+ consola.info("Caddy restarted before TLS issuance completed; retrying.\n");
14180
+ continue;
14181
+ }
14039
14182
  try {
14040
14183
  if (line.includes("Error:") || line && JSON.parse(line).level === "error") {
14041
14184
  if (line.includes("parsing caddyfile tokens")) {
@@ -14056,23 +14199,30 @@ ${line}
14056
14199
  if (line.includes("config file changed"))
14057
14200
  consola.success("Detect added a new reverse proxy config, restarted.\n");
14058
14201
  if (line.includes("caddy proxying") || line.includes("serving initial"))
14059
- return resolve2();
14202
+ void resolveWhenReady();
14060
14203
  }
14061
14204
  });
14062
14205
  (_b3 = this._caddyChild.stdout) == null ? void 0 : _b3.on("data", (_data) => {
14063
14206
  consola.info(_data.toString());
14064
- resolve2();
14207
+ void resolveWhenReady();
14065
14208
  });
14066
14209
  setupCleanup();
14067
14210
  } else {
14068
14211
  consola.info("Caddy is running in another process.");
14069
- setupCleanup();
14070
- return resolve2();
14212
+ void (async () => {
14213
+ try {
14214
+ if (https)
14215
+ await this.ensureTlsReady(targetHost);
14216
+ setupCleanup();
14217
+ resolve2();
14218
+ } catch (error) {
14219
+ reject2(error);
14220
+ }
14221
+ })();
14071
14222
  }
14072
14223
  });
14073
14224
  }
14074
14225
  async baseCleanup(restore = true) {
14075
- var _a4;
14076
14226
  this._stopEffects.forEach((fn) => fn());
14077
14227
  try {
14078
14228
  if (restore && this._source && this._target) {
@@ -14086,7 +14236,7 @@ ${line}
14086
14236
  consola.fail("Restore host failed.\n");
14087
14237
  }
14088
14238
  if (!this.locked && !this.stoped) {
14089
- (_a4 = this._caddyChild) == null ? void 0 : _a4.kill();
14239
+ await this.stopCaddy();
14090
14240
  try {
14091
14241
  await lockfile.unlock(caddyLockFilePath).catch(() => {
14092
14242
  });
package/dist/webpack.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  unpluginFactory
3
- } from "./chunk-JA3D5AWS.js";
3
+ } from "./chunk-MD5CVZP7.js";
4
4
  import "./chunk-RSBXFJ3Z.js";
5
5
  import "./chunk-2P7SUCQ3.js";
6
6
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "unplugin-https-reverse-proxy",
3
3
  "type": "module",
4
- "version": "4.0.2",
4
+ "version": "4.0.3",
5
5
  "description": "",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/zcf0508/unplugin-https-reverse-proxy#readme",
@@ -25,7 +25,7 @@ export function validateTemplateContext(ctx: unknown): typeof CaddyContext.infer
25
25
 
26
26
  export const caddyTemplate = `{
27
27
  debug
28
- auto_https disable_redirects
28
+ auto_https disable_redirects ignore_loaded_certs
29
29
  }
30
30
 
31
31
  :7601 {
@@ -14,6 +14,8 @@ import { chmodRecursive, consola, once } from '../utils'
14
14
  import { genCaddyfile, validateTemplateContext } from './caddyfile'
15
15
  import { useCaddyConfig } from './config'
16
16
  import { caddyFilePath, caddyLockFilePath, caddyPath, supportList, TEMP_DIR } from './constants'
17
+ import { isCaddyReloadCancellation } from './log'
18
+ import { formatTlsHealthFailure, waitForLocalTls } from './tls-health'
17
19
  import { kill, logProgress, logProgressOver, tryPort } from './utils'
18
20
 
19
21
  export async function download(): Promise<string> {
@@ -187,6 +189,43 @@ export class CaddyInstant {
187
189
  this.inited = true
188
190
  }
189
191
 
192
+ private async ensureTlsReady(targetHost: string): Promise<void> {
193
+ const health = await waitForLocalTls(targetHost)
194
+ if (!health.healthy)
195
+ throw new Error(formatTlsHealthFailure(targetHost, health))
196
+ }
197
+
198
+ private async updateCaddyfile(): Promise<void> {
199
+ const { configJsonRef } = getCaddyConfig()
200
+ const proxies = Object.values(configJsonRef() || {})
201
+ const ctx = validateTemplateContext({
202
+ caddy_root: this.caddyRoot,
203
+ proxies,
204
+ })
205
+
206
+ this.caddyfile = await genCaddyfile(ctx)
207
+ await this.writeCaddyfile()
208
+ }
209
+
210
+ private async stopCaddy(): Promise<void> {
211
+ try {
212
+ await got.post('http://127.0.0.1:2019/stop', {
213
+ headers: {
214
+ Origin: 'http://localhost:2019',
215
+ },
216
+ retry: {
217
+ limit: 0,
218
+ },
219
+ timeout: {
220
+ request: 2000,
221
+ },
222
+ })
223
+ }
224
+ catch {}
225
+
226
+ this._caddyChild?.kill()
227
+ }
228
+
190
229
  /**
191
230
  * @param source like `127.0.0.1:8080`
192
231
  * @param target like `test.abc.com`. And if you provide a port ,it will be ignore.
@@ -239,17 +278,17 @@ export class CaddyInstant {
239
278
  const { writeCaddyConfig } = getCaddyConfig()
240
279
  writeCaddyConfig(proxies)
241
280
 
242
- this._stopEffects.push(
243
- effect(async () => {
244
- const { configJsonRef } = getCaddyConfig()
245
- const proxies = Object.values(configJsonRef() || {})
246
- const ctx = validateTemplateContext({
247
- caddy_root: this.caddyRoot,
248
- proxies,
249
- })
281
+ await this.updateCaddyfile()
250
282
 
251
- this.caddyfile = await genCaddyfile(ctx)
252
- await this.writeCaddyfile()
283
+ let skipInitialCaddyfileEffect = true
284
+ this._stopEffects.push(
285
+ effect(() => {
286
+ getCaddyConfig().configJsonRef()
287
+ if (skipInitialCaddyfileEffect) {
288
+ skipInitialCaddyfileEffect = false
289
+ return
290
+ }
291
+ void this.updateCaddyfile()
253
292
  }),
254
293
  )
255
294
 
@@ -264,6 +303,25 @@ export class CaddyInstant {
264
303
  }
265
304
 
266
305
  return new Promise<void>((resolve, reject) => {
306
+ let startupCheckStarted = false
307
+
308
+ const resolveWhenReady = async (): Promise<void> => {
309
+ if (startupCheckStarted)
310
+ return
311
+ startupCheckStarted = true
312
+
313
+ try {
314
+ if (https)
315
+ await this.ensureTlsReady(targetHost)
316
+ resolve()
317
+ }
318
+ catch (error) {
319
+ if (!this.locked)
320
+ await this.baseCleanup(restore)
321
+ reject(error)
322
+ }
323
+ }
324
+
267
325
  const setupCleanup = (): void => {
268
326
  process.on('SIGINT', async () => {
269
327
  if (this.stoped)
@@ -340,6 +398,10 @@ export class CaddyInstant {
340
398
  // caddy log
341
399
  // eslint-disable-next-line no-console
342
400
  showCaddyLog && line && console.info(line)
401
+ if (isCaddyReloadCancellation(line)) {
402
+ consola.info('Caddy restarted before TLS issuance completed; retrying.\n')
403
+ continue
404
+ }
343
405
  try {
344
406
  if (line.includes('Error:') || (line && JSON.parse(line).level === 'error')) {
345
407
  if (line.includes('parsing caddyfile tokens')) {
@@ -360,13 +422,13 @@ export class CaddyInstant {
360
422
  consola.success('Detect added a new reverse proxy config, restarted.\n')
361
423
 
362
424
  if (line.includes('caddy proxying') || line.includes('serving initial'))
363
- return resolve()
425
+ void resolveWhenReady()
364
426
  }
365
427
  })
366
428
 
367
429
  this._caddyChild!.stdout?.on('data', (_data) => {
368
430
  consola.info(_data.toString())
369
- resolve()
431
+ void resolveWhenReady()
370
432
  })
371
433
 
372
434
  setupCleanup()
@@ -374,9 +436,17 @@ export class CaddyInstant {
374
436
  else {
375
437
  consola.info('Caddy is running in another process.')
376
438
 
377
- setupCleanup()
378
-
379
- return resolve()
439
+ void (async () => {
440
+ try {
441
+ if (https)
442
+ await this.ensureTlsReady(targetHost)
443
+ setupCleanup()
444
+ resolve()
445
+ }
446
+ catch (error) {
447
+ reject(error)
448
+ }
449
+ })()
380
450
  }
381
451
  })
382
452
  }
@@ -398,7 +468,7 @@ export class CaddyInstant {
398
468
  consola.fail('Restore host failed.\n')
399
469
  }
400
470
  if (!this.locked && !this.stoped) {
401
- this._caddyChild?.kill()
471
+ await this.stopCaddy()
402
472
  try {
403
473
  await lockfile.unlock(caddyLockFilePath).catch(() => { })
404
474
  await unlink(caddyFilePath).catch(() => { })