unplugin-https-reverse-proxy 1.0.0-beta.9 → 1.0.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 CHANGED
@@ -220,7 +220,17 @@ export default defineNuxtConfig({
220
220
  })
221
221
  ```
222
222
 
223
- > This module works for both Nuxt 2 and [Nuxt Vite](https://github.com/nuxt/vite)
223
+ **!important:** You need to update the `nuxt dev` script by adding `--fork=false` to enable host restoration.
224
+
225
+ ```json
226
+ // package.json
227
+
228
+ {
229
+ "scripts": {
230
+ "dev": "nuxt dev --fork=false"
231
+ }
232
+ }
233
+ ```
224
234
 
225
235
  <br></details>
226
236
 
@@ -0,0 +1,45 @@
1
+ // src/utils.ts
2
+ import process from "node:process";
3
+ import path from "node:path";
4
+ import { chmod, readdir, stat } from "node:fs/promises";
5
+ import { execSync } from "node:child_process";
6
+ import { createConsola } from "consola";
7
+ var consola = createConsola({
8
+ level: 3
9
+ }).withTag("reverse-proxy");
10
+ async function chmodRecursive(_path, mode) {
11
+ await chmod(_path, mode);
12
+ if ((await stat(_path)).isDirectory()) {
13
+ const children = await readdir(_path);
14
+ for (const child of children)
15
+ await chmodRecursive(path.join(_path, child), mode);
16
+ }
17
+ }
18
+ function once(fn) {
19
+ let called = false;
20
+ return (...args) => {
21
+ if (called)
22
+ return;
23
+ called = true;
24
+ return fn(...args);
25
+ };
26
+ }
27
+ function isAdmin() {
28
+ if (process.platform === "win32") {
29
+ try {
30
+ execSync("net session", { stdio: "ignore" });
31
+ return true;
32
+ } catch {
33
+ return false;
34
+ }
35
+ } else {
36
+ return process.getuid && process.getuid() === 0;
37
+ }
38
+ }
39
+
40
+ export {
41
+ consola,
42
+ chmodRecursive,
43
+ once,
44
+ isAdmin
45
+ };
@@ -1,36 +1,34 @@
1
1
  import {
2
- __spreadValues,
3
2
  chmodRecursive,
4
3
  consola,
5
4
  isAdmin,
6
5
  once
7
- } from "./chunk-RUJE3YFB.js";
6
+ } from "./chunk-2VGRJ3QS.js";
8
7
 
9
8
  // src/index.ts
10
- import process4 from "process";
9
+ import process4 from "node:process";
11
10
  import { createUnplugin } from "unplugin";
12
11
  import c from "picocolors";
13
12
 
14
13
  // src/caddy/index.ts
15
- import { platform } from "os";
16
- import process3 from "process";
17
- import { chmodSync as chmodSync2, createWriteStream, existsSync, mkdirSync, unlinkSync } from "fs";
18
- import { spawn } from "child_process";
14
+ import { platform } from "node:os";
15
+ import process3 from "node:process";
16
+ import { chmodSync as chmodSync2, createWriteStream, existsSync, mkdirSync, unlinkSync, writeFileSync } from "node:fs";
17
+ import { spawn } from "node:child_process";
18
+ import { readFile, unlink, writeFile } from "node:fs/promises";
19
19
  import { got } from "got-cjs";
20
20
  import { HttpProxyAgent } from "http-proxy-agent";
21
21
  import { HttpsProxyAgent } from "https-proxy-agent";
22
- import kill from "kill-port";
22
+ import * as lockfile from "proper-lockfile";
23
23
 
24
24
  // src/host/index.ts
25
- import process from "process";
26
- import { exec } from "child_process";
27
- import { chmodSync } from "fs";
28
- import _Hosts from "hosts-so-easy";
29
- var Hosts = _Hosts.default || _Hosts;
30
- var hostInstance = new Hosts({
31
- debounceTime: 50,
32
- hostsFile: getPathOfSystemHostsPath()
33
- });
25
+ import process from "node:process";
26
+ import { exec } from "node:child_process";
27
+ import { chmodSync } from "node:fs";
28
+ import { promisify } from "node:util";
29
+ import hostile from "hostile";
30
+ var _setHost = promisify(hostile.set);
31
+ var _removeHost = promisify(hostile.remove);
34
32
  function getPathOfSystemHostsPath() {
35
33
  return process.platform === "win32" ? `${process.env.windir || "C:\\WINDOWS"}\\system32\\drivers\\etc\\hosts` : "/etc/hosts";
36
34
  }
@@ -51,27 +49,25 @@ async function syncHost() {
51
49
  async function addHost(ip, host) {
52
50
  if (process.platform !== "win32")
53
51
  chmodSync(getPathOfSystemHostsPath(), 511);
54
- hostInstance.add("127.0.0.1", "localhost");
55
- hostInstance.add(ip, host);
52
+ await _setHost("127.0.0.1", "localhost");
53
+ await _setHost(ip, host);
56
54
  if (ip === "localhost")
57
- hostInstance.add("127.0.0.1", host);
58
- await hostInstance.updateFinish();
55
+ await _setHost("127.0.0.1", host);
59
56
  return await syncHost();
60
57
  }
61
58
  async function removeHost(ip, host) {
62
59
  if (process.platform !== "win32")
63
60
  chmodSync(getPathOfSystemHostsPath(), 511);
64
- hostInstance.remove(ip, host);
61
+ await _removeHost(ip, host);
65
62
  if (ip === "localhost")
66
- hostInstance.remove("127.0.0.1", host);
67
- await hostInstance.updateFinish();
63
+ await _removeHost("127.0.0.1", host);
68
64
  return await syncHost();
69
65
  }
70
66
 
71
67
  // src/caddy/constants.ts
72
- import { join } from "path";
73
- import os from "os";
74
- import process2 from "process";
68
+ import { join } from "node:path";
69
+ import os from "node:os";
70
+ import process2 from "node:process";
75
71
  import dotenvFlow from "dotenv-flow";
76
72
  dotenvFlow.config({
77
73
  silent: true
@@ -84,6 +80,7 @@ function resolvePath(path) {
84
80
  var TEMP_DIR = resolvePath(process2.env.UHRP_CADDY_PATH || process2.env.TEMP || process2.env.TMPDIR || "/tmp");
85
81
  var caddyPath = join(TEMP_DIR, `caddy${process2.platform === "win32" ? ".exe" : ""}`);
86
82
  var caddyFilePath = join(TEMP_DIR, "CADDYFILE");
83
+ var caddyLockFilePath = join(TEMP_DIR, "caddylock");
87
84
  var supportList = [
88
85
  {
89
86
  platform: "dragonfly",
@@ -196,7 +193,8 @@ var supportList = [
196
193
  ];
197
194
 
198
195
  // src/caddy/utils.ts
199
- import { createServer } from "net";
196
+ import { createServer } from "node:net";
197
+ import { exec as exec2 } from "node:child_process";
200
198
  import ora from "ora";
201
199
  var spinner;
202
200
  function tryPort(port) {
@@ -217,6 +215,18 @@ function tryPort(port) {
217
215
  }
218
216
  });
219
217
  }
218
+ async function kill(port) {
219
+ return new Promise((resolve, reject) => {
220
+ exec2(`npx @maxbo/free-port ${port} -s`, (err, stdout, stderr) => {
221
+ if (err) {
222
+ reject(err);
223
+ } else {
224
+ consola.log(stdout);
225
+ resolve();
226
+ }
227
+ });
228
+ });
229
+ }
220
230
  function getNumber(number) {
221
231
  if (number <= 0)
222
232
  return "0%";
@@ -316,9 +326,41 @@ var CaddyInstant = class {
316
326
  constructor() {
317
327
  this.inited = false;
318
328
  this.stoped = false;
319
- testCaddy().then(() => {
329
+ /** has locked */
330
+ this.locked = false;
331
+ testCaddy().then(async () => {
332
+ await this.getCaddyfile();
320
333
  this.inited = true;
321
334
  });
335
+ this.locked = this.checkLock();
336
+ }
337
+ checkLock() {
338
+ if (!existsSync(caddyLockFilePath))
339
+ writeFileSync(caddyLockFilePath, "");
340
+ return lockfile.checkSync(caddyLockFilePath);
341
+ }
342
+ async getCaddyfile() {
343
+ if (!existsSync(caddyFilePath))
344
+ await this.initCaddyfile();
345
+ this.caddyfile = await readFile(caddyFilePath, "utf-8");
346
+ return this.caddyfile;
347
+ }
348
+ async initCaddyfile() {
349
+ const contet = `
350
+ {
351
+ debug
352
+ auto_https disable_redirects
353
+ }
354
+
355
+ `;
356
+ await writeFile(caddyFilePath, contet);
357
+ this.caddyfile = contet;
358
+ }
359
+ async writeCaddyfile() {
360
+ return await writeFile(caddyFilePath, this.caddyfile || "");
361
+ }
362
+ async deleteCaddyfile() {
363
+ return await unlink(caddyFilePath);
322
364
  }
323
365
  async init() {
324
366
  await download();
@@ -337,86 +379,169 @@ var CaddyInstant = class {
337
379
  showCaddyLog = false,
338
380
  https = false
339
381
  } = options || {};
382
+ this._source = source;
383
+ this._target = target;
340
384
  if (!this.inited)
341
385
  await this.init();
342
- if (https && await tryPort(443))
343
- await kill(443, "tcp");
344
- if ((process3.platform === "win32" || !https) && await tryPort(80))
345
- await kill(80, "tcp");
346
386
  if (!await addHost(source.split(":")[0], target.split(":")[0]))
347
387
  throw new Error("update host failed");
348
388
  consola.success("update host success\n");
389
+ await this.getCaddyfile();
390
+ if (!(this.caddyfile || "").includes(`${target.split(":")[0]}${https ? "" : ":80"}`)) {
391
+ this.caddyfile = `${this.caddyfile}
392
+ ${target.split(":")[0]}${https ? "" : ":80"} {
393
+ ${https ? "tls internal" : ""}
394
+ reverse_proxy http://${source} {
395
+ health_uri ${base}
396
+ health_interval 2s
397
+ health_timeout 5s
398
+ fail_duration 2s
399
+ unhealthy_status 502 503 504 404
400
+ lb_try_duration 3s
401
+ lb_try_interval 300ms
402
+ health_headers {
403
+ Accept text/html
404
+ Accept-Encoding gzip, deflate, br
405
+ User-Agent Caddy-Health-Check
406
+ }
407
+
408
+ @error status 502 503 504 404
409
+ handle_response @error {
410
+ respond * 503 {
411
+ body "Service Unavailable"
412
+ close
413
+ }
414
+ }
415
+ }
416
+ }
417
+
418
+ `;
419
+ await this.writeCaddyfile();
420
+ }
421
+ if (!existsSync(caddyLockFilePath))
422
+ writeFileSync(caddyLockFilePath, "");
423
+ if (!this.locked) {
424
+ if (await tryPort(443))
425
+ await kill(443);
426
+ if ((process3.platform === "win32" || !https) && await tryPort(80))
427
+ await kill(80);
428
+ }
349
429
  return new Promise((resolve, reject) => {
350
- const child = process3.platform !== "win32" ? spawn("sudo", ["-E", caddyPath, "reverse-proxy", "--from", `${target.split(":")[0]}${https ? "" : ":80"}`, "--to", `${source}`, "--internal-certs", "--insecure", "--disable-redirects"]) : spawn(caddyPath, ["reverse-proxy", "--from", `${target.split(":")[0]}`, "--to", `${source}`, "--internal-certs"]);
351
- child.on("error", (err) => {
352
- return reject(err);
353
- });
354
- process3.on("SIGINT", async () => {
355
- if (!restore || this.stoped)
356
- return;
357
- try {
358
- if (await removeHost(source.split(":")[0], target.split(":")[0]))
359
- consola.success("restore host success\n");
360
- else
361
- consola.fail("restore host failed\n");
362
- } catch (e) {
363
- consola.error(e);
364
- consola.fail("restore host failed\n");
365
- } finally {
366
- this.stoped = true;
367
- process3.nextTick(() => {
368
- process3.exit();
369
- });
370
- }
371
- });
372
- const originalExit = process3.exit.bind(process3);
373
- process3.exit = async (code) => {
374
- const port = Number(source.split(":")[1]);
375
- if (!Number.isNaN(port) && await tryPort(port))
376
- await kill(port, "tcp");
377
- const pwd = process3.cwd();
378
- const viteCacheDir = `${pwd}/node_modules/.vite`;
379
- if (existsSync(viteCacheDir))
380
- chmodRecursive(viteCacheDir, 511);
381
- const nuxtCacheDir = `${pwd}/.nuxt`;
382
- if (existsSync(nuxtCacheDir))
383
- await chmodRecursive(nuxtCacheDir, 511);
384
- const webpackCacheDir = `${pwd}/node_modules/.cache`;
385
- if (existsSync(webpackCacheDir))
386
- chmodRecursive(webpackCacheDir, 511);
387
- if (!restore || this.stoped)
388
- return originalExit(code);
389
- try {
390
- if (await removeHost(source.split(":")[0], target.split(":")[0]))
391
- consola.success("restore host success\n");
392
- else
393
- consola.fail("restore host failed\n");
394
- } catch (e) {
395
- consola.error(e);
396
- consola.fail("restore host failed\n");
397
- } finally {
398
- this.stoped = true;
399
- process3.nextTick(() => {
400
- originalExit(code);
401
- });
402
- }
430
+ var _a, _b;
431
+ const setupCleanup = () => {
432
+ process3.on("SIGINT", async () => {
433
+ if (this.stoped)
434
+ return;
435
+ process3.stdin.resume();
436
+ try {
437
+ await this.baseCleanup(restore);
438
+ } finally {
439
+ this.stoped = true;
440
+ process3.nextTick(() => {
441
+ process3.exit();
442
+ });
443
+ }
444
+ });
445
+ const originalExit = process3.exit.bind(process3);
446
+ process3.exit = async (code) => {
447
+ const port = Number(source.split(":")[1]);
448
+ if (!Number.isNaN(port) && await tryPort(port))
449
+ await kill(port);
450
+ const pwd = process3.cwd();
451
+ const viteCacheDir = `${pwd}/node_modules/.vite`;
452
+ if (existsSync(viteCacheDir))
453
+ await chmodRecursive(viteCacheDir, 511).catch(() => {
454
+ });
455
+ const nuxtCacheDir = `${pwd}/.nuxt`;
456
+ if (existsSync(nuxtCacheDir))
457
+ await chmodRecursive(nuxtCacheDir, 511).catch(() => {
458
+ });
459
+ const webpackCacheDir = `${pwd}/node_modules/.cache`;
460
+ if (existsSync(webpackCacheDir))
461
+ await chmodRecursive(webpackCacheDir, 511).catch(() => {
462
+ });
463
+ if (this.stoped)
464
+ return originalExit(code);
465
+ process3.stdin.resume();
466
+ try {
467
+ await this.baseCleanup();
468
+ } finally {
469
+ this.stoped = true;
470
+ process3.nextTick(() => {
471
+ originalExit(code);
472
+ });
473
+ }
474
+ };
403
475
  };
404
- child.stderr.on("data", (data) => {
405
- const lines = data.toString().split("\n").map((line) => line.trim());
406
- for (const line of lines) {
407
- showCaddyLog && line && console.info(line);
408
- if (line.includes("Error:") || line && JSON.parse(line).level === "error") {
409
- consola.error(line);
410
- return reject(line);
476
+ if (!this.locked) {
477
+ this._caddyChild = process3.platform !== "win32" ? spawn("sudo", ["-E", caddyPath, "run", "--config", caddyFilePath, "--watch"]) : spawn(caddyPath, ["run", "--config", caddyFilePath, "--watch"]);
478
+ lockfile.lockSync(caddyLockFilePath);
479
+ this._caddyChild.on("error", (err) => {
480
+ return reject(err);
481
+ });
482
+ (_a = this._caddyChild.stderr) == null ? void 0 : _a.on("data", async (data) => {
483
+ const lines = data.toString().split("\n").map((line) => line.trim());
484
+ for (const line of lines) {
485
+ showCaddyLog && line && console.info(line);
486
+ if (line.includes("Error:") || line && JSON.parse(line).level === "error") {
487
+ if (line.includes("parsing caddyfile tokens")) {
488
+ consola.error("Caddyfile syntax error, please contact the author:\n", `${this.caddyfile}
489
+
490
+ ${line}
491
+ `);
492
+ await this.baseCleanup();
493
+ process3.exit();
494
+ } else {
495
+ consola.error(`${line}
496
+ `);
497
+ return reject(line);
498
+ }
499
+ }
500
+ if (line.includes("config file changed"))
501
+ consola.success("Detect added a new reverse proxy config, restarted.\n");
502
+ if (line.includes("caddy proxying") || line.includes("serving initial"))
503
+ return resolve();
411
504
  }
412
- }
413
- });
414
- child.stdout.on("data", (_data) => {
415
- consola.info(_data.toString());
416
- resolve();
417
- });
505
+ });
506
+ (_b = this._caddyChild.stdout) == null ? void 0 : _b.on("data", (_data) => {
507
+ consola.info(_data.toString());
508
+ resolve();
509
+ });
510
+ setupCleanup();
511
+ } else {
512
+ consola.info("Caddy is running in another process.");
513
+ setupCleanup();
514
+ return resolve();
515
+ }
418
516
  });
419
517
  }
518
+ async baseCleanup(restore = true) {
519
+ var _a;
520
+ try {
521
+ if (restore && this._source && this._target) {
522
+ if (await removeHost(this._source.split(":")[0], this._target.split(":")[0]))
523
+ consola.success("Restore host success.\n");
524
+ else
525
+ consola.fail("Restore host failed.\n");
526
+ }
527
+ } catch (e) {
528
+ consola.error(e);
529
+ consola.fail("Restore host failed.\n");
530
+ }
531
+ if (!this.locked && !this.stoped) {
532
+ (_a = this._caddyChild) == null ? void 0 : _a.kill();
533
+ try {
534
+ await lockfile.unlock(caddyLockFilePath).catch(() => {
535
+ });
536
+ await unlink(caddyFilePath).catch(() => {
537
+ });
538
+ await unlink(caddyLockFilePath).catch(() => {
539
+ });
540
+ } catch (e) {
541
+ consola.error(e);
542
+ }
543
+ }
544
+ }
420
545
  };
421
546
 
422
547
  // src/index.ts
@@ -428,9 +553,10 @@ var vitePrintUrls = once((options, source, target, base = "/", _printUrls) => {
428
553
  if (caddy)
429
554
  return;
430
555
  caddy = new CaddyInstant();
431
- caddy.run(source, target, __spreadValues({
432
- base
433
- }, options)).then(() => {
556
+ caddy.run(source, target, {
557
+ base,
558
+ ...options
559
+ }).then(() => {
434
560
  const colorUrl = (url) => c.green(url.replace(/:(\d+)\//, (_, port) => `:${c.bold(port)}/`));
435
561
  consola.success(` ${c.green("\u279C")} ${c.bold("run caddy reverse proxy success")}: ${colorUrl(`${options.https ? "https" : "http"}://${target}${base}`)}`);
436
562
  }).catch((e) => {
@@ -448,16 +574,16 @@ var unpluginFactory = (options) => ({
448
574
  var _a;
449
575
  if (config.command !== "serve")
450
576
  return;
451
- if (!isAdmin()) {
452
- consola.warn("please run as administrator");
453
- return;
454
- }
455
577
  const {
456
578
  enable = true,
457
579
  target = ""
458
580
  } = options;
459
581
  if (!enable)
460
582
  return;
583
+ if (!isAdmin()) {
584
+ consola.warn("please run as administrator");
585
+ return;
586
+ }
461
587
  if (!target) {
462
588
  consola.fail("please provide target");
463
589
  return;
@@ -477,24 +603,27 @@ var unpluginFactory = (options) => ({
477
603
  var _a, _b;
478
604
  if (process4.env.NODE_ENV !== "development")
479
605
  return;
480
- if (!isAdmin()) {
481
- consola.warn("please run as administrator");
482
- return;
483
- }
484
606
  const {
485
607
  enable = true,
486
608
  target = ""
487
609
  } = options;
488
610
  if (!enable)
489
611
  return;
612
+ if (!isAdmin()) {
613
+ consola.warn("please run as administrator");
614
+ return;
615
+ }
490
616
  if (!target) {
491
617
  consola.fail("please provide target");
492
618
  return;
493
619
  }
494
- const devServer = __spreadValues(__spreadValues({
620
+ const devServer = {
495
621
  host: "localhost",
496
- port: 8080
497
- }, compiler.options.devServer), (_a = process4.VUE_CLI_SERVICE) == null ? void 0 : _a.projectOptions.devServer);
622
+ port: 8080,
623
+ ...compiler.options.devServer,
624
+ // @ts-expect-error vuecli
625
+ ...(_a = process4.VUE_CLI_SERVICE) == null ? void 0 : _a.projectOptions.devServer
626
+ };
498
627
  const source = `${devServer.host}:${devServer.port}`;
499
628
  if (caddy)
500
629
  return;
@@ -509,7 +638,9 @@ var unpluginFactory = (options) => ({
509
638
  options.https = false;
510
639
  }
511
640
  caddy = new CaddyInstant();
512
- caddy.run(source, target, __spreadValues({}, options)).then(() => {
641
+ caddy.run(source, target, {
642
+ ...options
643
+ }).then(() => {
513
644
  const colorUrl = (url) => c.green(url.replace(/:(\d+)\//, (_, port) => `:${c.bold(port)}/`));
514
645
  compiler.hooks.done.tap("unplugin-https-reverse-proxy", () => {
515
646
  consola.success(` ${c.green("\u279C")} ${c.bold("run caddy reverse proxy success")}: ${colorUrl(`${options.https ? "https" : "http"}://${target}`)}`);
@@ -522,31 +653,35 @@ var unpluginFactory = (options) => ({
522
653
  var _a, _b, _c;
523
654
  if (process4.env.NODE_ENV !== "development")
524
655
  return;
525
- if (!isAdmin()) {
526
- consola.warn("please run as administrator");
527
- return;
528
- }
529
656
  const {
530
657
  enable = true,
531
658
  target = ""
532
659
  } = options;
533
660
  if (!enable)
534
661
  return;
662
+ if (!isAdmin()) {
663
+ consola.warn("please run as administrator");
664
+ return;
665
+ }
535
666
  if (!target) {
536
667
  consola.fail("please provide target");
537
668
  return;
538
669
  }
539
- const devServer = __spreadValues(__spreadValues({
670
+ const devServer = {
540
671
  host: "localhost",
541
- port: 8080
542
- }, compiler.options.devServer), (_c = (_b = await ((_a = await import("@rsbuild/core")) == null ? void 0 : _a.loadConfig({
543
- cwd
544
- }))) == null ? void 0 : _b.content) == null ? void 0 : _c.server);
672
+ port: 8080,
673
+ ...compiler.options.devServer,
674
+ ...(_c = (_b = await ((_a = await import("@rsbuild/core")) == null ? void 0 : _a.loadConfig({
675
+ cwd
676
+ }))) == null ? void 0 : _b.content) == null ? void 0 : _c.server
677
+ };
545
678
  const source = `${devServer.host}:${devServer.port}`;
546
679
  if (caddy)
547
680
  return;
548
681
  caddy = new CaddyInstant();
549
- caddy.run(source, target, __spreadValues({}, options)).then(() => {
682
+ caddy.run(source, target, {
683
+ ...options
684
+ }).then(() => {
550
685
  const colorUrl = (url) => c.green(url.replace(/:(\d+)\//, (_, port) => `:${c.bold(port)}/`));
551
686
  consola.success(` ${c.green("\u279C")} ${c.bold("run caddy reverse proxy success")}: ${colorUrl(`${options.https ? "https" : "http"}://${target}`)}`);
552
687
  }).catch((e) => {