fbi-proxy 1.4.0 → 1.5.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
@@ -1,184 +1,67 @@
1
1
  # fbi-proxy
2
2
 
3
- FBI Proxy is a super easy way to turn your local network over HTTPS with intelligent domain routing.
3
+ FBI-Proxy provides easy HTTPS access to your local services with intelligent domain routing.
4
4
 
5
- ## Quick Start
5
+ ## Usage
6
6
 
7
- ```bash
8
- # Install dependencies
9
- bun install
7
+ ```sh
8
+ # launch
9
+ bunx fbi-proxy
10
10
 
11
- # Build and start the proxy system
12
- bun run start
11
+ # expose to LAN
12
+ bunx fbi-proxy --host 0.0.0.0 --port=2432
13
13
 
14
- # Development mode with auto-reload
15
- bun run dev
16
- ```
14
+ # with caddy, forwarding *.fbi.com
15
+ bunx fbi-proxy --caddy=fbi.com
17
16
 
18
- ## Build Scripts
17
+ # or
18
+ docker start
19
+ ```
19
20
 
20
- ### Available Commands:
21
+ ### Routing Examples
21
22
 
22
23
  ```bash
23
- # Build TypeScript CLI
24
- bun run build:ts
25
-
26
- # Build Rust proxy binary
27
- bun run build:rs
24
+ # Port forwarding
25
+ https://3000.fbi.com → localhost:3000
26
+ https://8080.fbi.com → localhost:8080
28
27
 
29
- # Full build (both TypeScript and Rust)
30
- bun run build
28
+ # Host--Port forwarding
29
+ https://api--3001.fbi.com → api:3001
30
+ https://db--5432.fbi.com → db:5432
31
31
 
32
- # Development with hot reload
33
- bun run dev
32
+ # Subdomain routing (with Host header)
33
+ https://admin.app.fbi.com → app:80 (Host: admin)
34
+ https://v2.api.fbi.com → api:80 (Host: v2)
34
35
 
35
- # Production start
36
- bun run start
36
+ # Direct host forwarding
37
+ https://myserver.fbi.com → myserver:80
37
38
  ```
38
39
 
39
- ## Architecture
40
-
41
- The FBI Proxy consists of three main components:
42
-
43
- - **TypeScript CLI** (`ts/cli.ts`): Orchestrates the entire system, manages Caddy and Rust proxy processes
44
- - **Rust Proxy Server** (`proxy.rs`): High-performance HTTP/WebSocket proxy using Hyper framework
45
- - **Caddy Server**: Handles TLS termination, SSL certificates, and domain routing
46
-
47
- ### How It Works
40
+ WebSocket connections are supported for all patterns.
48
41
 
49
- 1. **CLI** downloads/manages Caddy binary and builds the Rust proxy
50
- 2. **Caddy** receives incoming HTTPS requests and forwards them to the Rust proxy
51
- 3. **Rust Proxy** intelligently routes requests based on hostname patterns
52
-
53
- ## Development Workflow
54
-
55
- ### First Time Setup:
42
+ ## Development
56
43
 
57
44
  ```bash
58
- # 1. Install dependencies (requires Bun)
45
+ # Install dependencies
59
46
  bun install
60
47
 
61
- # 2. Start development (automatically builds and runs)
48
+ # Start development
62
49
  bun run dev
63
- ```
64
-
65
- ### Daily Development:
66
-
67
- ```bash
68
- # Development with hot reload for both Caddy and Rust
69
- bun run dev
70
-
71
- # Or run individual components:
72
- bun run dev:caddy # Start Caddy with file watching
73
- bun run dev:rs # Start Rust proxy with bacon (auto-rebuild)
74
- ```
75
-
76
- ### Production Deployment:
77
-
78
- ```bash
79
- bun run build # Build optimized binaries
80
- bun run start # Start production services
81
- ```
82
-
83
- ## Prerequisites
84
-
85
- - **Bun**: JavaScript runtime and package manager (install from https://bun.sh/)
86
- - **Rust**: For building the proxy binary (install from https://rustup.rs/)
87
- - **Caddy**: Automatically downloaded by the CLI if not found
88
-
89
- ## Routing Features
90
50
 
91
- The FBI Proxy supports intelligent hostname-based routing with the following patterns:
92
-
93
- ### 1. Port Forwarding
94
-
95
- - `https://3000.fbi.com` → `http://localhost:3000`
96
- - `https://8080.fbi.com` → `http://localhost:8080`
97
-
98
- ### 2. Host--Port Forwarding
99
-
100
- - `https://localhost--3000.fbi.com` → `http://localhost:3000`
101
- - `https://myserver--8080.fbi.com` → `http://myserver:8080`
102
-
103
- ### 3. Subdomain Hoisting
104
-
105
- - `https://api.myserver.fbi.com` → `http://myserver:80` (with Host: `api`)
106
- - `https://admin.localhost.fbi.com` → `http://localhost:80` (with Host: `admin`)
107
-
108
- ### 4. Direct Host Forwarding
109
-
110
- - `https://myserver.fbi.com` → `http://myserver:80`
111
- - `https://localhost.fbi.com` → `http://localhost:80`
112
-
113
- ### WebSocket Support
114
-
115
- All routing patterns support WebSocket connections with automatic upgrade handling.
116
-
117
- ## Configuration
118
-
119
- ### Environment Variables
120
-
121
- ```env
122
- FBIHOST="fbi.com" # Default domain (configurable via --fbihost)
123
- FBIPROXY_PORT="24306" # Internal proxy port (auto-assigned)
124
- ```
125
-
126
- ### CLI Options
127
-
128
- ```bash
129
- bun run ts/cli.ts --help
130
-
131
- Options:
132
- --help Show help message
133
- --fbihost Set the FBI host (default: fbi.com)
134
- ```
135
-
136
- ## Service Configuration Examples
137
-
138
- Create custom service mappings by modifying the routing logic or using the built-in patterns:
139
-
140
- ```bash
141
- # Direct port access
142
- https://3000.fbi.com # → localhost:3000
143
- https://5173.fbi.com # → localhost:5173 (Vite dev server)
144
- https://8000.fbi.com # → localhost:8000 (VS Code server)
145
-
146
- # Named services with custom ports
147
- https://api--3001.fbi.com # → api:3001
148
- https://db--5432.fbi.com # → db:5432
149
-
150
- # Subdomain routing
151
- # Subdomain routing
152
- https://admin.app.fbi.com # → app:80 with Host: admin
153
- https://v2.api.fbi.com # → api:80 with Host: v2
51
+ # Or production
52
+ bun run build && bun run start
154
53
  ```
155
54
 
156
- ## Technical Details
157
-
158
- ### Rust Proxy Implementation (`proxy.rs`)
159
-
160
- The Rust proxy server implements the following routing logic:
161
-
162
- 1. **Number Host Detection**: Pure numeric hosts (e.g., `3000`) route to `localhost:3000`
163
- 2. **Double-Dash Parsing**: `host--port` format routes to `host:port`
164
- 3. **Subdomain Hoisting**: Multi-level domains route to the root domain with subdomain as Host header
165
- 4. **Default Port 80**: Simple hostnames default to port 80
166
-
167
- ### TypeScript CLI (`ts/cli.ts`)
168
-
169
- The CLI manages:
55
+ ### Prerequisites
170
56
 
171
- - Binary discovery and building (Rust proxy, Caddy download)
172
- - Process orchestration with proper signal handling
173
- - Port management and environment variable passing
174
- - Hot reloading during development
57
+ - **Bun**: https://bun.sh/
58
+ - **Rust**: https://rustup.rs/
59
+ - **Caddy**: Auto-downloaded if not found
175
60
 
176
- ### Caddy Configuration (`Caddyfile`)
61
+ ### Configuration
177
62
 
178
- - Automatic HTTPS with on-demand TLS certificates
179
- - Regex-based host header matching
180
- - Reverse proxy configuration with header manipulation
181
- - Support for wildcard subdomain routing
63
+ - Default domain: `fbi.com` (change with `--fbihost`)
64
+ - Internal proxy port: Auto-assigned to `FBIPROXY_PORT`
182
65
 
183
66
  ## License
184
67
 
package/package.json CHANGED
@@ -15,6 +15,7 @@
15
15
  "semantic-release": "^22.0.0"
16
16
  },
17
17
  "peerDependencies": {
18
+ "@snomiao/caddy-baron": "^3.0.4",
18
19
  "typescript": "^5"
19
20
  },
20
21
  "files": [
@@ -24,10 +25,13 @@
24
25
  "dist",
25
26
  "Caddyfile"
26
27
  ],
28
+ "trustedDependencies": [
29
+ "@radically-straightforward/caddy"
30
+ ],
27
31
  "dependencies": {
28
- "@snomiao/caddy-baron": "^3.0.4",
32
+ "@radically-straightforward/caddy": "^2.0.6",
29
33
  "bun": "^1.2.19",
30
- "caddy-baron": "^3.0.2",
34
+ "execa": "^9.6.0",
31
35
  "from-node-stream": "^0.1.0",
32
36
  "get-port": "^7.1.0",
33
37
  "hot-memo": "^1.1.1",
@@ -38,6 +42,7 @@
38
42
  "sflow": "^1.23.0",
39
43
  "ts-toolbelt": "^9.6.0",
40
44
  "tsa-composer": "^1.0.0",
45
+ "yargs": "^17.7.2",
41
46
  "zx": "^8.7.2"
42
47
  },
43
48
  "scripts": {
@@ -55,7 +60,7 @@
55
60
  "start:js": "node dist/cli.js",
56
61
  "start:caddy": "./caddy run",
57
62
  "start:rs": "./target/release/fbi-proxy",
58
- "prepare": "husky && bunx rustup && bacon --version || cargo install bacon"
63
+ "prepare": "husky"
59
64
  },
60
65
  "lint-staged": {
61
66
  "*.{ts,js}": [
@@ -68,5 +73,5 @@
68
73
  "bin": {
69
74
  "fbi-proxy": "dist/cli.js"
70
75
  },
71
- "version": "1.4.0"
76
+ "version": "1.5.0"
72
77
  }
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -2,7 +2,7 @@ import fsp from "fs/promises";
2
2
  import { existsSync } from "fs";
3
3
  import { getProxyFilename } from "./getProxyFilename";
4
4
  import { copyFile } from "fs/promises";
5
- import { $ } from "./dRun";
5
+ import { $ } from "./dSpawn";
6
6
  import { mkdir } from "fs/promises";
7
7
 
8
8
  if (import.meta.main) {
package/ts/cli.ts CHANGED
@@ -1,49 +1,43 @@
1
1
  #!/usr/bin/env bun
2
2
  import getPort from "get-port";
3
3
  import hotMemo from "hot-memo";
4
- import minimist from "minimist";
5
4
  import path from "path";
6
- import promiseAllProperties from "promise-all-properties";
5
+ import yargs from "yargs";
6
+ import { hideBin } from "yargs/helpers";
7
7
  import { buildFbiProxy } from "./buildFbiProxy";
8
- import { $ } from "./dRun";
8
+ import { $ } from "./dSpawn";
9
9
  import { downloadCaddy } from "./downloadCaddy";
10
+ import { execa } from "execa";
10
11
 
11
12
  process.chdir(path.resolve(__dirname, "..")); // Change to project root directory
12
13
 
13
- console.log("Preparing Binaries");
14
-
15
- const { proxy, caddy } = await promiseAllProperties({
16
- proxy: buildFbiProxy(),
17
- caddy: downloadCaddy(),
18
- });
14
+ // Parse command line arguments with yargs
15
+ const argv = await yargs(hideBin(process.argv))
16
+ .option("fbihost", {
17
+ type: "string",
18
+ default: "fbi.com",
19
+ description: "Set the FBI host",
20
+ })
21
+ .option("caddy", {
22
+ type: "boolean",
23
+ default: false,
24
+ description: "Start Caddy server",
25
+ })
26
+ .option("dev", {
27
+ alias: "d",
28
+ type: "boolean",
29
+ default: false,
30
+ description: "Run in development mode",
31
+ })
32
+ .help().argv;
19
33
 
20
- console.log("Running fbi-proxy", JSON.stringify({ caddy, proxy }));
34
+ console.log("Preparing Binaries");
21
35
 
22
- // assume caddy is installed, launch proxy server now
23
- const argv = minimist(process.argv.slice(2), {
24
- default: {
25
- dev: false,
26
- d: false,
27
- fbihost: "fbi.com", // Default FBI host
28
- },
29
- alias: {
30
- dev: "d",
31
- },
32
- boolean: ["dev", "d", "help"],
33
- });
34
- // console.log(argv);
35
- if (argv.help) {
36
- console.log(`Usage: fbi-proxy [options]
37
- Options:
38
- --help Show this help message
39
- --fbihost Set the FBI host (default: fbi.com)
40
- `);
41
- process.exit(0);
42
- }
36
+ const FBIHOST = argv.fbihost;
37
+ const FBIPROXY_PORT = String(await getPort({ port: 2432 }));
43
38
 
44
- const FBIHOST = argv.fbihost || "fbi.com"; // Default FBI host
45
- const FBIPROXY_PORT = String(await getPort({ port: 24306 }));
46
39
  const proxyProcess = await hotMemo(async () => {
40
+ const proxy = await buildFbiProxy();
47
41
  console.log("Starting Rust proxy server");
48
42
  const p = $.opt({
49
43
  env: {
@@ -59,25 +53,35 @@ const proxyProcess = await hotMemo(async () => {
59
53
  return p;
60
54
  });
61
55
 
62
- const caddyProcess = await hotMemo(async () => {
63
- const p = $.opt({
64
- env: {
65
- ...process.env,
66
- FBIPROXY_PORT, // Rust proxy server port
67
- FBIHOST,
68
- },
69
- })`${caddy} run`.process;
70
- p.on("exit", (code) => {
71
- console.log(`Caddy exited with code ${code}`);
72
- process.exit(code || 0);
56
+ let caddyProcess: any = null;
57
+
58
+ // Only start Caddy if --caddy flag is passed
59
+ if (argv.caddy) {
60
+ const caddy = await downloadCaddy();
61
+ caddyProcess = await hotMemo(async () => {
62
+ const p = $.opt({
63
+ env: {
64
+ ...process.env,
65
+ FBIPROXY_PORT, // Rust proxy server port
66
+ FBIHOST,
67
+ },
68
+ })`${caddy} run`.process;
69
+ p.on("exit", (code) => {
70
+ console.log(`Caddy exited with code ${code}`);
71
+ process.exit(code || 0);
72
+ });
73
+ return p;
73
74
  });
74
- return p;
75
- });
75
+ }
76
76
 
77
77
  console.log("all done");
78
78
  // show process pids
79
79
  console.log(`Proxy server PID: ${proxyProcess.pid}`);
80
- console.log(`Caddy server PID: ${caddyProcess.pid}`);
80
+ if (caddyProcess) {
81
+ console.log(`Caddy server PID: ${caddyProcess.pid}`);
82
+ } else {
83
+ console.log("Caddy server not started (use --caddy to start it)");
84
+ }
81
85
 
82
86
  const exit = () => {
83
87
  console.log("Shutting down...");
@@ -15,17 +15,18 @@ type DRunProc = Promise<{
15
15
  process: ChildProcess;
16
16
  };
17
17
 
18
- const dRun = ({
18
+ const dSpawn = ({
19
19
  cwd = process.cwd(),
20
20
  env = process.env as Record<string, string>,
21
21
  } = {}) =>
22
22
  tsaComposer(
23
- (s: string | { raw: string }) =>
24
- typeof s === "string"
23
+ // slot is un dividable
24
+ (slot: string | { raw: string }) =>
25
+ typeof slot === "string"
25
26
  ? {
26
- raw: String(s), // todo: escape quotes
27
+ raw: String(slot), // todo: escape quotes
27
28
  }
28
- : s,
29
+ : slot,
29
30
  (...slots): DRunProc => {
30
31
  try {
31
32
  // TODO: parse slots for quotes
@@ -106,7 +107,7 @@ const dRun = ({
106
107
  },
107
108
  );
108
109
 
109
- export const $ = Object.assign(dRun(), {
110
- opt: dRun,
111
- cwd: (path: string) => dRun({ cwd: path }),
110
+ export const $ = Object.assign(dSpawn(), {
111
+ opt: dSpawn,
112
+ cwd: (path: string) => dSpawn({ cwd: path }),
112
113
  });
@@ -1,31 +1,24 @@
1
1
  import { existsSync } from "fs";
2
- import fsp from "fs/promises";
3
- import { $ } from "./dRun";
2
+ import { $ } from "./dSpawn";
4
3
 
5
- export const downloadCaddy = async () => {
6
- // use pwdCaddy if already downloaded
7
- const pwdCaddy = "./caddy";
4
+ if (import.meta.main) {
5
+ // if this file is run directly, download caddy
6
+ const caddy = await downloadCaddy();
7
+ console.log(`Caddy downloaded to: ${caddy}`);
8
+ process.exit(0);
9
+ }
8
10
 
9
- // if ./caddy exists in pwd, return it
11
+ export async function downloadCaddy() {
12
+ // use pwdCaddy if already downloaded
13
+ const pwdCaddy = "./node_modules/.bin/caddy";
10
14
  if (existsSync(pwdCaddy)) return pwdCaddy;
11
15
 
12
- // or use system caddy if installed, run `caddy --version` to check
16
+ // // or use system caddy if installed, run `caddy --version` to check
13
17
  if (await $`caddy --version`.catch(() => false)) {
14
18
  return "caddy";
15
19
  }
16
20
 
17
- // or if system caddy is not installed, download caddy using caddy-baron
18
- if (!existsSync(pwdCaddy)) {
19
- // download latest caddy to ./caddy
20
- console.log("Downloading Caddy...");
21
- // @ts-ignore
22
- await import("../node_modules/caddy-baron/index.mjs");
23
-
24
- if (!existsSync(pwdCaddy))
25
- throw new Error(
26
- "Failed to download Caddy. Please install Caddy manually or check your network connection.",
27
- );
28
- }
29
-
30
- return pwdCaddy;
31
- };
21
+ throw new Error(
22
+ "Failed to download Caddy. Please install Caddy manually or check your network connection.",
23
+ );
24
+ }