@dbx-tools/cli 0.9.58 → 0.9.61
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 +594 -57
- package/bin/dbx-genie.ts +13 -0
- package/bin/dbx-graphiti.ts +5 -0
- package/bin/dbx-lakebase-proxy.ts +4 -0
- package/bin/dbx-model-gateway.ts +5 -0
- package/index.ts +47 -1
- package/lib/bin/dbx-genie.d.ts +2 -0
- package/lib/bin/dbx-genie.js +10 -0
- package/lib/bin/dbx-graphiti.d.ts +2 -0
- package/lib/bin/dbx-graphiti.js +4 -0
- package/lib/bin/dbx-lakebase-proxy.d.ts +2 -0
- package/lib/bin/dbx-lakebase-proxy.js +4 -0
- package/lib/bin/dbx-model-gateway.d.ts +2 -0
- package/lib/bin/dbx-model-gateway.js +4 -0
- package/lib/index.d.ts +47 -1
- package/lib/index.js +34 -2
- package/lib/src/appkit/cli.d.ts +24 -0
- package/lib/src/appkit/cli.js +54 -0
- package/lib/src/appkit/env-export.d.ts +40 -0
- package/lib/src/appkit/env-export.js +99 -0
- package/lib/src/auth/cli.d.ts +20 -0
- package/lib/src/auth/cli.js +129 -0
- package/lib/src/auth/options.d.ts +45 -0
- package/lib/src/auth/options.js +68 -0
- package/lib/src/cli.d.ts +2 -0
- package/lib/src/cli.js +19 -9
- package/lib/src/genie-code/cli.d.ts +19 -0
- package/lib/src/genie-code/cli.js +36 -0
- package/lib/src/genie-code/config.d.ts +38 -0
- package/lib/src/genie-code/config.js +111 -0
- package/lib/src/genie-code/install.d.ts +21 -0
- package/lib/src/genie-code/install.js +69 -0
- package/lib/src/genie-code/options.d.ts +7 -0
- package/lib/src/genie-code/options.js +7 -0
- package/lib/src/genie-code/runner.d.ts +21 -0
- package/lib/src/genie-code/runner.js +90 -0
- package/lib/src/genie-code/runtime.d.ts +48 -0
- package/lib/src/genie-code/runtime.js +162 -0
- package/lib/src/graphiti/cli.d.ts +18 -0
- package/lib/src/graphiti/cli.js +43 -0
- package/lib/src/graphiti/options.d.ts +10 -0
- package/lib/src/graphiti/options.js +10 -0
- package/lib/src/lakebase-proxy/cancellation.d.ts +22 -0
- package/lib/src/lakebase-proxy/cancellation.js +100 -0
- package/lib/src/lakebase-proxy/cli.d.ts +13 -0
- package/lib/src/lakebase-proxy/cli.js +54 -0
- package/lib/src/lakebase-proxy/options.d.ts +23 -0
- package/lib/src/lakebase-proxy/options.js +41 -0
- package/lib/src/lakebase-proxy/protocol.d.ts +37 -0
- package/lib/src/lakebase-proxy/protocol.js +124 -0
- package/lib/src/lakebase-proxy/proxy.d.ts +22 -0
- package/lib/src/lakebase-proxy/proxy.js +200 -0
- package/lib/src/model-gateway/cli.d.ts +19 -0
- package/lib/src/model-gateway/cli.js +63 -0
- package/lib/src/model-gateway/server.d.ts +16 -0
- package/lib/src/model-gateway/server.js +83 -0
- package/lib/src/tunnel/app.d.ts +20 -0
- package/lib/src/tunnel/app.js +52 -0
- package/lib/src/tunnel/cli.d.ts +27 -0
- package/lib/src/tunnel/cli.js +190 -0
- package/lib/src/tunnel/options.d.ts +72 -0
- package/lib/src/tunnel/options.js +138 -0
- package/lib/src/tunnel/proxy.d.ts +51 -0
- package/lib/src/tunnel/proxy.js +143 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +146 -9
- package/src/appkit/cli.ts +72 -0
- package/src/appkit/env-export.ts +124 -0
- package/src/auth/cli.ts +175 -0
- package/src/auth/options.ts +83 -0
- package/src/cli.ts +35 -8
- package/src/genie-code/cli.ts +58 -0
- package/src/genie-code/config.ts +161 -0
- package/src/genie-code/install.ts +103 -0
- package/src/genie-code/options.ts +14 -0
- package/src/genie-code/runner.ts +106 -0
- package/src/genie-code/runtime.ts +212 -0
- package/src/graphiti/cli.ts +75 -0
- package/src/graphiti/options.ts +20 -0
- package/src/lakebase-proxy/cancellation.ts +116 -0
- package/src/lakebase-proxy/cli.ts +80 -0
- package/src/lakebase-proxy/options.ts +56 -0
- package/src/lakebase-proxy/protocol.ts +170 -0
- package/src/lakebase-proxy/proxy.ts +256 -0
- package/src/model-gateway/cli.ts +93 -0
- package/src/model-gateway/server.ts +94 -0
- package/src/tunnel/app.ts +58 -0
- package/src/tunnel/cli.ts +218 -0
- package/src/tunnel/options.ts +169 -0
- package/src/tunnel/proxy.ts +194 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `dbx tunnel` - front a command with a public portr tunnel and passwordless gate.
|
|
3
|
+
*
|
|
4
|
+
* This is the WRAPPER path, and it exists for one case: a project that does not
|
|
5
|
+
* use `@dbx-tools/appkit`'s `createApp`, and therefore cannot register
|
|
6
|
+
* `tunnelInterceptor()` + the `authGate` plugin in-process. An AppKit app should
|
|
7
|
+
* still take the plugin path - one process, no proxy hop, no duplicated header
|
|
8
|
+
* handling.
|
|
9
|
+
*
|
|
10
|
+
* The wrapper claims the PUBLIC port (`DATABRICKS_APP_PORT`, the port the
|
|
11
|
+
* platform and portr route to), moves the wrapped app to a private one, and
|
|
12
|
+
* reverse-proxies between them so the gate sits in front of traffic it would
|
|
13
|
+
* otherwise have no way to intercept. Everything else is delegated: the gate
|
|
14
|
+
* config comes from `plugin.resolveAuthGateConfig`, the portr lifecycle from
|
|
15
|
+
* `portr.*` - both the same functions the in-process path uses.
|
|
16
|
+
*
|
|
17
|
+
* Ships no bin. `@dbx-tools/cli` mounts `buildProgram()` as `dbx tunnel` lazily,
|
|
18
|
+
* so `dbx dev` pays for none of this, and `--insecure` / `status` / `install`
|
|
19
|
+
* never load AppKit or the SMTP stack either (the gate app is behind a dynamic
|
|
20
|
+
* import).
|
|
21
|
+
*
|
|
22
|
+
* @module
|
|
23
|
+
*/
|
|
24
|
+
import { createServer } from "node:net";
|
|
25
|
+
import { AppKitChildProcess } from "@dbx-tools/appkit/child-process";
|
|
26
|
+
import { addArgs, parseArgs } from "@dbx-tools/cli-args";
|
|
27
|
+
import { log } from "@dbx-tools/shared-core";
|
|
28
|
+
import { frp, interceptor, portr } from "@dbx-tools/tunnel";
|
|
29
|
+
import { Command, CommanderError } from "commander";
|
|
30
|
+
import { resolveTunnelOptions, TunnelOptionsSchema } from "./options.js";
|
|
31
|
+
import { startProxy } from "./proxy.js";
|
|
32
|
+
export { CommanderError };
|
|
33
|
+
const logger = log.logger("tunnel");
|
|
34
|
+
/**
|
|
35
|
+
* A free loopback port, from the OS rather than a random guess: binding `0` and
|
|
36
|
+
* reading back what was assigned is the only way to know the port is actually
|
|
37
|
+
* available, so two tunnels can run side by side without colliding.
|
|
38
|
+
*/
|
|
39
|
+
function freePort() {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
const probe = createServer();
|
|
42
|
+
probe.on("error", reject);
|
|
43
|
+
probe.listen(0, "127.0.0.1", () => {
|
|
44
|
+
const address = probe.address();
|
|
45
|
+
const port = typeof address === "object" && address ? address.port : 0;
|
|
46
|
+
probe.close(() => (port > 0 ? resolve(port) : reject(new Error("no free port"))));
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Tie the wrapper's lifetime to its children's, in both directions: a child that
|
|
52
|
+
* exits takes the wrapper down with its code, and a signal to the wrapper is
|
|
53
|
+
* forwarded before it leaves. Without this a crashed app leaves a portr tunnel
|
|
54
|
+
* serving a dead port, which looks like a hang rather than a failure.
|
|
55
|
+
*/
|
|
56
|
+
function supervise(children) {
|
|
57
|
+
let stopping = false;
|
|
58
|
+
const stop = (code) => {
|
|
59
|
+
if (stopping)
|
|
60
|
+
return;
|
|
61
|
+
stopping = true;
|
|
62
|
+
void Promise.allSettled(children.map((child) => child.shutdown())).then(() => {
|
|
63
|
+
process.exit(code);
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
for (const child of children) {
|
|
67
|
+
void child.process?.on("exit", (code) => stop(code ?? 1));
|
|
68
|
+
}
|
|
69
|
+
for (const signal of ["SIGTERM", "SIGINT", "SIGHUP"]) {
|
|
70
|
+
process.on(signal, () => stop(0));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function run(raw, command) {
|
|
74
|
+
const [executable, ...args] = command;
|
|
75
|
+
const resolved = resolveTunnelOptions(raw);
|
|
76
|
+
const children = [];
|
|
77
|
+
// Two upstream modes:
|
|
78
|
+
// - WRAP: a command after `--`. The wrapper spawns it on a private loopback
|
|
79
|
+
// port and is the only thing that talks to it.
|
|
80
|
+
// - ATTACH: no command, but `--app-port` names an already-running upstream
|
|
81
|
+
// (e.g. a local reverse proxy). The gate fronts it without spawning a
|
|
82
|
+
// child. This is what lets the gate sit on an interface in front of a
|
|
83
|
+
// separately-supervised stack.
|
|
84
|
+
let appPort;
|
|
85
|
+
if (executable) {
|
|
86
|
+
appPort = resolved.appPort ?? (await freePort());
|
|
87
|
+
const app = new AppKitChildProcess([
|
|
88
|
+
executable,
|
|
89
|
+
args,
|
|
90
|
+
{
|
|
91
|
+
env: {
|
|
92
|
+
...process.env,
|
|
93
|
+
DATABRICKS_APP_PORT: String(appPort),
|
|
94
|
+
PORT: String(appPort),
|
|
95
|
+
HOST: "127.0.0.1",
|
|
96
|
+
},
|
|
97
|
+
stdin: "inherit",
|
|
98
|
+
stdout: "inherit",
|
|
99
|
+
stderr: "inherit",
|
|
100
|
+
},
|
|
101
|
+
], { gracefulTimeoutMs: 10_000 });
|
|
102
|
+
void app.start();
|
|
103
|
+
children.push(app);
|
|
104
|
+
}
|
|
105
|
+
else if (resolved.appPort) {
|
|
106
|
+
appPort = resolved.appPort;
|
|
107
|
+
logger.info("attaching gate to existing upstream", { appPort });
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
throw new CommanderError(1, "tunnel.no-upstream", "no command given (pass it after `--`) and no --app-port to attach to");
|
|
111
|
+
}
|
|
112
|
+
// Dynamic import: the gate is the only thing here that needs AppKit + SMTP, so
|
|
113
|
+
// an `--insecure` run never loads either.
|
|
114
|
+
const gate = resolved.gate.insecure
|
|
115
|
+
? undefined
|
|
116
|
+
: await (await import("./app.js")).startGateApp({
|
|
117
|
+
...resolved.gateConfig,
|
|
118
|
+
publicDomain: resolved.gate.publicDomain ?? `localhost:${resolved.publicPort}`,
|
|
119
|
+
});
|
|
120
|
+
if (!gate)
|
|
121
|
+
logger.warn("running OPEN - no gate is in front of this tunnel");
|
|
122
|
+
await startProxy({
|
|
123
|
+
publicPort: resolved.publicPort,
|
|
124
|
+
appPort,
|
|
125
|
+
gate,
|
|
126
|
+
forwardHeaders: resolved.gate.forwardHeaders,
|
|
127
|
+
gatePaths: resolved.gate.gatePaths,
|
|
128
|
+
brandName: resolved.gate.brandName,
|
|
129
|
+
bindHosts: resolved.bindHosts,
|
|
130
|
+
});
|
|
131
|
+
if ((resolved.transport === "portr" || resolved.transport === "both") && resolved.portr) {
|
|
132
|
+
const portrEnv = await portr.installPortr();
|
|
133
|
+
await portr.writePortrConfig(resolved.portr, portrEnv);
|
|
134
|
+
children.push(await portr.startPortr(resolved.portr, portrEnv));
|
|
135
|
+
}
|
|
136
|
+
if ((resolved.transport === "frp" || resolved.transport === "both") && resolved.frp) {
|
|
137
|
+
const frpEnv = await frp.installFrp();
|
|
138
|
+
const configPath = await frp.writeFrpConfig(resolved.frp, frpEnv);
|
|
139
|
+
children.push(frp.startFrp(resolved.frp, frpEnv, configPath));
|
|
140
|
+
}
|
|
141
|
+
const activeTunnelCount = children.length - (executable ? 1 : 0);
|
|
142
|
+
if (!activeTunnelCount) {
|
|
143
|
+
logger.info("no selected public tunnel is configured", {
|
|
144
|
+
transport: resolved.transport,
|
|
145
|
+
publicPort: resolved.publicPort,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
supervise(children);
|
|
149
|
+
}
|
|
150
|
+
/** The `dbx tunnel` program. No side effects until parsed. */
|
|
151
|
+
export function buildProgram(name = "dbx tunnel") {
|
|
152
|
+
const program = addArgs(new Command()
|
|
153
|
+
.name(name)
|
|
154
|
+
.description("Front a command with a public tunnel and passwordless auth"), TunnelOptionsSchema);
|
|
155
|
+
// `run` is the DEFAULT action as well as a named subcommand, preserving the old
|
|
156
|
+
// wrapper's ergonomics (`dbx tunnel --allow x -- bun src/server.ts`) while
|
|
157
|
+
// leaving somewhere for `status` and `install` to live.
|
|
158
|
+
program
|
|
159
|
+
.argument("[command...]", "the command to wrap, after `--`")
|
|
160
|
+
.action(async (command) => {
|
|
161
|
+
await run(parseArgs(program, TunnelOptionsSchema), command);
|
|
162
|
+
});
|
|
163
|
+
const runCommand = addArgs(program.command("run").description("Wrap a command (the default action)"), TunnelOptionsSchema);
|
|
164
|
+
runCommand
|
|
165
|
+
.argument("<command...>", "the command to wrap, after `--`")
|
|
166
|
+
.action(async (command) => {
|
|
167
|
+
await run(parseArgs(runCommand, TunnelOptionsSchema), command);
|
|
168
|
+
});
|
|
169
|
+
// Worth its own command: the most common failure is a tunnel that silently
|
|
170
|
+
// does nothing because no token or domain resolved, and this prints exactly
|
|
171
|
+
// what would happen without starting anything.
|
|
172
|
+
const statusCommand = addArgs(program.command("status").description("Resolve the configuration and print it"), TunnelOptionsSchema);
|
|
173
|
+
statusCommand.action(() => {
|
|
174
|
+
const resolved = resolveTunnelOptions(parseArgs(statusCommand, TunnelOptionsSchema));
|
|
175
|
+
process.stdout.write(`${JSON.stringify(resolved, null, 2)}\n`);
|
|
176
|
+
});
|
|
177
|
+
program
|
|
178
|
+
.command("install")
|
|
179
|
+
.argument("[transport]", "portr, frp, or both", "portr")
|
|
180
|
+
.description("Install public tunnel client binaries and exit")
|
|
181
|
+
.action(async (transport) => {
|
|
182
|
+
const parsed = interceptor.TunnelTransportSchema.parse(transport);
|
|
183
|
+
if (parsed === "portr" || parsed === "both")
|
|
184
|
+
await portr.installPortr();
|
|
185
|
+
if (parsed === "frp" || parsed === "both")
|
|
186
|
+
await frp.installFrp();
|
|
187
|
+
});
|
|
188
|
+
return program;
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3R1bm5lbC9jbGkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FzQkc7QUFFSCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sVUFBVSxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ3JFLE9BQU8sRUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDekQsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQzdDLE9BQU8sRUFBRSxHQUFHLEVBQUUsV0FBVyxFQUFFLEtBQUssRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQzVELE9BQU8sRUFBRSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBRXBELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxtQkFBbUIsRUFBc0IsTUFBTSxjQUFjLENBQUM7QUFDN0YsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUV4QyxPQUFPLEVBQUUsY0FBYyxFQUFFLENBQUM7QUFFMUIsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztBQUVwQzs7OztHQUlHO0FBQ0gsU0FBUyxRQUFRO0lBQ2YsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtRQUNyQyxNQUFNLEtBQUssR0FBRyxZQUFZLEVBQUUsQ0FBQztRQUM3QixLQUFLLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztRQUMxQixLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsRUFBRSxXQUFXLEVBQUUsR0FBRyxFQUFFO1lBQ2hDLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNoQyxN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LEdBQUcsb0JBQW9CLENBQUMsU0FBUyxDQUFDLGFBQWEsRUFBRSxtQkFBbUIsQ0FBQyxDQUFDLENBQUM7UUFDckYsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ2pFLENBQUMsQ0FBQyxDQUFDO0lBRUgsT0FBTztTQUNKLE9BQU8sQ0FBQyxTQUFTLENBQUM7U0FDbEIsUUFBUSxDQUFDLGFBQWEsRUFBRSxxQkFBcUIsRUFBRSxPQUFPLENBQUM7U0FDdkQsV0FBVyxDQUFDLGdEQUFnRCxDQUFDO1NBQzdELE1BQU0sQ0FBQyxLQUFLLEVBQUUsU0FBaUIsRUFBRSxFQUFFO1FBQ2xDLE1BQU0sTUFBTSxHQUFHLFdBQVcsQ0FBQyxxQkFBcUIsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDbEUsSUFBSSxNQUFNLEtBQUssT0FBTyxJQUFJLE1BQU0sS0FBSyxNQUFNO1lBQUUsTUFBTSxLQUFLLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDeEUsSUFBSSxNQUFNLEtBQUssS0FBSyxJQUFJLE1BQU0sS0FBSyxNQUFNO1lBQUUsTUFBTSxHQUFHLENBQUMsVUFBVSxFQUFFLENBQUM7SUFDcEUsQ0FBQyxDQUFDLENBQUM7SUFFTCxPQUFPLE9BQU8sQ0FBQztBQUNqQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBgZGJ4IHR1bm5lbGAgLSBmcm9udCBhIGNvbW1hbmQgd2l0aCBhIHB1YmxpYyBwb3J0ciB0dW5uZWwgYW5kIHBhc3N3b3JkbGVzcyBnYXRlLlxuICpcbiAqIFRoaXMgaXMgdGhlIFdSQVBQRVIgcGF0aCwgYW5kIGl0IGV4aXN0cyBmb3Igb25lIGNhc2U6IGEgcHJvamVjdCB0aGF0IGRvZXMgbm90XG4gKiB1c2UgYEBkYngtdG9vbHMvYXBwa2l0YCdzIGBjcmVhdGVBcHBgLCBhbmQgdGhlcmVmb3JlIGNhbm5vdCByZWdpc3RlclxuICogYHR1bm5lbEludGVyY2VwdG9yKClgICsgdGhlIGBhdXRoR2F0ZWAgcGx1Z2luIGluLXByb2Nlc3MuIEFuIEFwcEtpdCBhcHAgc2hvdWxkXG4gKiBzdGlsbCB0YWtlIHRoZSBwbHVnaW4gcGF0aCAtIG9uZSBwcm9jZXNzLCBubyBwcm94eSBob3AsIG5vIGR1cGxpY2F0ZWQgaGVhZGVyXG4gKiBoYW5kbGluZy5cbiAqXG4gKiBUaGUgd3JhcHBlciBjbGFpbXMgdGhlIFBVQkxJQyBwb3J0IChgREFUQUJSSUNLU19BUFBfUE9SVGAsIHRoZSBwb3J0IHRoZVxuICogcGxhdGZvcm0gYW5kIHBvcnRyIHJvdXRlIHRvKSwgbW92ZXMgdGhlIHdyYXBwZWQgYXBwIHRvIGEgcHJpdmF0ZSBvbmUsIGFuZFxuICogcmV2ZXJzZS1wcm94aWVzIGJldHdlZW4gdGhlbSBzbyB0aGUgZ2F0ZSBzaXRzIGluIGZyb250IG9mIHRyYWZmaWMgaXQgd291bGRcbiAqIG90aGVyd2lzZSBoYXZlIG5vIHdheSB0byBpbnRlcmNlcHQuIEV2ZXJ5dGhpbmcgZWxzZSBpcyBkZWxlZ2F0ZWQ6IHRoZSBnYXRlXG4gKiBjb25maWcgY29tZXMgZnJvbSBgcGx1Z2luLnJlc29sdmVBdXRoR2F0ZUNvbmZpZ2AsIHRoZSBwb3J0ciBsaWZlY3ljbGUgZnJvbVxuICogYHBvcnRyLipgIC0gYm90aCB0aGUgc2FtZSBmdW5jdGlvbnMgdGhlIGluLXByb2Nlc3MgcGF0aCB1c2VzLlxuICpcbiAqIFNoaXBzIG5vIGJpbi4gYEBkYngtdG9vbHMvY2xpYCBtb3VudHMgYGJ1aWxkUHJvZ3JhbSgpYCBhcyBgZGJ4IHR1bm5lbGAgbGF6aWx5LFxuICogc28gYGRieCBkZXZgIHBheXMgZm9yIG5vbmUgb2YgdGhpcywgYW5kIGAtLWluc2VjdXJlYCAvIGBzdGF0dXNgIC8gYGluc3RhbGxgXG4gKiBuZXZlciBsb2FkIEFwcEtpdCBvciB0aGUgU01UUCBzdGFjayBlaXRoZXIgKHRoZSBnYXRlIGFwcCBpcyBiZWhpbmQgYSBkeW5hbWljXG4gKiBpbXBvcnQpLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG5pbXBvcnQgeyBjcmVhdGVTZXJ2ZXIgfSBmcm9tIFwibm9kZTpuZXRcIjtcbmltcG9ydCB7IEFwcEtpdENoaWxkUHJvY2VzcyB9IGZyb20gXCJAZGJ4LXRvb2xzL2FwcGtpdC9jaGlsZC1wcm9jZXNzXCI7XG5pbXBvcnQgeyBhZGRBcmdzLCBwYXJzZUFyZ3MgfSBmcm9tIFwiQGRieC10b29scy9jbGktYXJnc1wiO1xuaW1wb3J0IHsgbG9nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB7IGZycCwgaW50ZXJjZXB0b3IsIHBvcnRyIH0gZnJvbSBcIkBkYngtdG9vbHMvdHVubmVsXCI7XG5pbXBvcnQgeyBDb21tYW5kLCBDb21tYW5kZXJFcnJvciB9IGZyb20gXCJjb21tYW5kZXJcIjtcblxuaW1wb3J0IHsgcmVzb2x2ZVR1bm5lbE9wdGlvbnMsIFR1bm5lbE9wdGlvbnNTY2hlbWEsIHR5cGUgVHVubmVsT3B0aW9ucyB9IGZyb20gXCIuL29wdGlvbnMudHNcIjtcbmltcG9ydCB7IHN0YXJ0UHJveHkgfSBmcm9tIFwiLi9wcm94eS50c1wiO1xuXG5leHBvcnQgeyBDb21tYW5kZXJFcnJvciB9O1xuXG5jb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwidHVubmVsXCIpO1xuXG4vKipcbiAqIEEgZnJlZSBsb29wYmFjayBwb3J0LCBmcm9tIHRoZSBPUyByYXRoZXIgdGhhbiBhIHJhbmRvbSBndWVzczogYmluZGluZyBgMGAgYW5kXG4gKiByZWFkaW5nIGJhY2sgd2hhdCB3YXMgYXNzaWduZWQgaXMgdGhlIG9ubHkgd2F5IHRvIGtub3cgdGhlIHBvcnQgaXMgYWN0dWFsbHlcbiAqIGF2YWlsYWJsZSwgc28gdHdvIHR1bm5lbHMgY2FuIHJ1biBzaWRlIGJ5IHNpZGUgd2l0aG91dCBjb2xsaWRpbmcuXG4gKi9cbmZ1bmN0aW9uIGZyZWVQb3J0KCk6IFByb21pc2U8bnVtYmVyPiB7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgY29uc3QgcHJvYmUgPSBjcmVhdGVTZXJ2ZXIoKTtcbiAgICBwcm9iZS5vbihcImVycm9yXCIsIHJlamVjdCk7XG4gICAgcHJvYmUubGlzdGVuKDAsIFwiMTI3LjAuMC4xXCIsICgpID0+IHtcbiAgICAgIGNvbnN0IGFkZHJlc3MgPSBwcm9iZS5hZGRyZXNzKCk7XG4gICAgICBjb25zdCBwb3J0ID0gdHlwZW9mIGFkZHJlc3MgPT09IFwib2JqZWN0XCIgJiYgYWRkcmVzcyA/IGFkZHJlc3MucG9ydCA6IDA7XG4gICAgICBwcm9iZS5jbG9zZSgoKSA9PiAocG9ydCA+IDAgPyByZXNvbHZlKHBvcnQpIDogcmVqZWN0KG5ldyBFcnJvcihcIm5vIGZyZWUgcG9ydFwiKSkpKTtcbiAgICB9KTtcbiAgfSk7XG59XG5cbi8qKlxuICogVGllIHRoZSB3cmFwcGVyJ3MgbGlmZXRpbWUgdG8gaXRzIGNoaWxkcmVuJ3MsIGluIGJvdGggZGlyZWN0aW9uczogYSBjaGlsZCB0aGF0XG4gKiBleGl0cyB0YWtlcyB0aGUgd3JhcHBlciBkb3duIHdpdGggaXRzIGNvZGUsIGFuZCBhIHNpZ25hbCB0byB0aGUgd3JhcHBlciBpc1xuICogZm9yd2FyZGVkIGJlZm9yZSBpdCBsZWF2ZXMuIFdpdGhvdXQgdGhpcyBhIGNyYXNoZWQgYXBwIGxlYXZlcyBhIHBvcnRyIHR1bm5lbFxuICogc2VydmluZyBhIGRlYWQgcG9ydCwgd2hpY2ggbG9va3MgbGlrZSBhIGhhbmcgcmF0aGVyIHRoYW4gYSBmYWlsdXJlLlxuICovXG5mdW5jdGlvbiBzdXBlcnZpc2UoY2hpbGRyZW46IHJlYWRvbmx5IEFwcEtpdENoaWxkUHJvY2Vzc1tdKTogdm9pZCB7XG4gIGxldCBzdG9wcGluZyA9IGZhbHNlO1xuICBjb25zdCBzdG9wID0gKGNvZGU6IG51bWJlcik6IHZvaWQgPT4ge1xuICAgIGlmIChzdG9wcGluZykgcmV0dXJuO1xuICAgIHN0b3BwaW5nID0gdHJ1ZTtcbiAgICB2b2lkIFByb21pc2UuYWxsU2V0dGxlZChjaGlsZHJlbi5tYXAoKGNoaWxkKSA9PiBjaGlsZC5zaHV0ZG93bigpKSkudGhlbigoKSA9PiB7XG4gICAgICBwcm9jZXNzLmV4aXQoY29kZSk7XG4gICAgfSk7XG4gIH07XG4gIGZvciAoY29uc3QgY2hpbGQgb2YgY2hpbGRyZW4pIHtcbiAgICB2b2lkIGNoaWxkLnByb2Nlc3M/Lm9uKFwiZXhpdFwiLCAoY29kZSkgPT4gc3RvcChjb2RlID8/IDEpKTtcbiAgfVxuICBmb3IgKGNvbnN0IHNpZ25hbCBvZiBbXCJTSUdURVJNXCIsIFwiU0lHSU5UXCIsIFwiU0lHSFVQXCJdIGFzIGNvbnN0KSB7XG4gICAgcHJvY2Vzcy5vbihzaWduYWwsICgpID0+IHN0b3AoMCkpO1xuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHJ1bihyYXc6IFR1bm5lbE9wdGlvbnMsIGNvbW1hbmQ6IHJlYWRvbmx5IHN0cmluZ1tdKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IFtleGVjdXRhYmxlLCAuLi5hcmdzXSA9IGNvbW1hbmQ7XG4gIGNvbnN0IHJlc29sdmVkID0gcmVzb2x2ZVR1bm5lbE9wdGlvbnMocmF3KTtcbiAgY29uc3QgY2hpbGRyZW46IEFwcEtpdENoaWxkUHJvY2Vzc1tdID0gW107XG5cbiAgLy8gVHdvIHVwc3RyZWFtIG1vZGVzOlxuICAvLyAgIC0gV1JBUDogYSBjb21tYW5kIGFmdGVyIGAtLWAuIFRoZSB3cmFwcGVyIHNwYXducyBpdCBvbiBhIHByaXZhdGUgbG9vcGJhY2tcbiAgLy8gICAgIHBvcnQgYW5kIGlzIHRoZSBvbmx5IHRoaW5nIHRoYXQgdGFsa3MgdG8gaXQuXG4gIC8vICAgLSBBVFRBQ0g6IG5vIGNvbW1hbmQsIGJ1dCBgLS1hcHAtcG9ydGAgbmFtZXMgYW4gYWxyZWFkeS1ydW5uaW5nIHVwc3RyZWFtXG4gIC8vICAgICAoZS5nLiBhIGxvY2FsIHJldmVyc2UgcHJveHkpLiBUaGUgZ2F0ZSBmcm9udHMgaXQgd2l0aG91dCBzcGF3bmluZyBhXG4gIC8vICAgICBjaGlsZC4gVGhpcyBpcyB3aGF0IGxldHMgdGhlIGdhdGUgc2l0IG9uIGFuIGludGVyZmFjZSBpbiBmcm9udCBvZiBhXG4gIC8vICAgICBzZXBhcmF0ZWx5LXN1cGVydmlzZWQgc3RhY2suXG4gIGxldCBhcHBQb3J0OiBudW1iZXI7XG4gIGlmIChleGVjdXRhYmxlKSB7XG4gICAgYXBwUG9ydCA9IHJlc29sdmVkLmFwcFBvcnQgPz8gKGF3YWl0IGZyZWVQb3J0KCkpO1xuICAgIGNvbnN0IGFwcCA9IG5ldyBBcHBLaXRDaGlsZFByb2Nlc3MoXG4gICAgICBbXG4gICAgICAgIGV4ZWN1dGFibGUsXG4gICAgICAgIGFyZ3MsXG4gICAgICAgIHtcbiAgICAgICAgICBlbnY6IHtcbiAgICAgICAgICAgIC4uLnByb2Nlc3MuZW52LFxuICAgICAgICAgICAgREFUQUJSSUNLU19BUFBfUE9SVDogU3RyaW5nKGFwcFBvcnQpLFxuICAgICAgICAgICAgUE9SVDogU3RyaW5nKGFwcFBvcnQpLFxuICAgICAgICAgICAgSE9TVDogXCIxMjcuMC4wLjFcIixcbiAgICAgICAgICB9LFxuICAgICAgICAgIHN0ZGluOiBcImluaGVyaXRcIixcbiAgICAgICAgICBzdGRvdXQ6IFwiaW5oZXJpdFwiLFxuICAgICAgICAgIHN0ZGVycjogXCJpbmhlcml0XCIsXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgICAgeyBncmFjZWZ1bFRpbWVvdXRNczogMTBfMDAwIH0sXG4gICAgKTtcbiAgICB2b2lkIGFwcC5zdGFydCgpO1xuICAgIGNoaWxkcmVuLnB1c2goYXBwKTtcbiAgfSBlbHNlIGlmIChyZXNvbHZlZC5hcHBQb3J0KSB7XG4gICAgYXBwUG9ydCA9IHJlc29sdmVkLmFwcFBvcnQ7XG4gICAgbG9nZ2VyLmluZm8oXCJhdHRhY2hpbmcgZ2F0ZSB0byBleGlzdGluZyB1cHN0cmVhbVwiLCB7IGFwcFBvcnQgfSk7XG4gIH0gZWxzZSB7XG4gICAgdGhyb3cgbmV3IENvbW1hbmRlckVycm9yKFxuICAgICAgMSxcbiAgICAgIFwidHVubmVsLm5vLXVwc3RyZWFtXCIsXG4gICAgICBcIm5vIGNvbW1hbmQgZ2l2ZW4gKHBhc3MgaXQgYWZ0ZXIgYC0tYCkgYW5kIG5vIC0tYXBwLXBvcnQgdG8gYXR0YWNoIHRvXCIsXG4gICAgKTtcbiAgfVxuXG4gIC8vIER5bmFtaWMgaW1wb3J0OiB0aGUgZ2F0ZSBpcyB0aGUgb25seSB0aGluZyBoZXJlIHRoYXQgbmVlZHMgQXBwS2l0ICsgU01UUCwgc29cbiAgLy8gYW4gYC0taW5zZWN1cmVgIHJ1biBuZXZlciBsb2FkcyBlaXRoZXIuXG4gIGNvbnN0IGdhdGUgPSByZXNvbHZlZC5nYXRlLmluc2VjdXJlXG4gICAgPyB1bmRlZmluZWRcbiAgICA6IGF3YWl0IChcbiAgICAgICAgYXdhaXQgaW1wb3J0KFwiLi9hcHAudHNcIilcbiAgICAgICkuc3RhcnRHYXRlQXBwKHtcbiAgICAgICAgLi4ucmVzb2x2ZWQuZ2F0ZUNvbmZpZyxcbiAgICAgICAgcHVibGljRG9tYWluOiByZXNvbHZlZC5nYXRlLnB1YmxpY0RvbWFpbiA/PyBgbG9jYWxob3N0OiR7cmVzb2x2ZWQucHVibGljUG9ydH1gLFxuICAgICAgfSk7XG4gIGlmICghZ2F0ZSkgbG9nZ2VyLndhcm4oXCJydW5uaW5nIE9QRU4gLSBubyBnYXRlIGlzIGluIGZyb250IG9mIHRoaXMgdHVubmVsXCIpO1xuXG4gIGF3YWl0IHN0YXJ0UHJveHkoe1xuICAgIHB1YmxpY1BvcnQ6IHJlc29sdmVkLnB1YmxpY1BvcnQsXG4gICAgYXBwUG9ydCxcbiAgICBnYXRlLFxuICAgIGZvcndhcmRIZWFkZXJzOiByZXNvbHZlZC5nYXRlLmZvcndhcmRIZWFkZXJzLFxuICAgIGdhdGVQYXRoczogcmVzb2x2ZWQuZ2F0ZS5nYXRlUGF0aHMsXG4gICAgYnJhbmROYW1lOiByZXNvbHZlZC5nYXRlLmJyYW5kTmFtZSxcbiAgICBiaW5kSG9zdHM6IHJlc29sdmVkLmJpbmRIb3N0cyxcbiAgfSk7XG5cbiAgaWYgKChyZXNvbHZlZC50cmFuc3BvcnQgPT09IFwicG9ydHJcIiB8fCByZXNvbHZlZC50cmFuc3BvcnQgPT09IFwiYm90aFwiKSAmJiByZXNvbHZlZC5wb3J0cikge1xuICAgIGNvbnN0IHBvcnRyRW52ID0gYXdhaXQgcG9ydHIuaW5zdGFsbFBvcnRyKCk7XG4gICAgYXdhaXQgcG9ydHIud3JpdGVQb3J0ckNvbmZpZyhyZXNvbHZlZC5wb3J0ciwgcG9ydHJFbnYpO1xuICAgIGNoaWxkcmVuLnB1c2goYXdhaXQgcG9ydHIuc3RhcnRQb3J0cihyZXNvbHZlZC5wb3J0ciwgcG9ydHJFbnYpKTtcbiAgfVxuICBpZiAoKHJlc29sdmVkLnRyYW5zcG9ydCA9PT0gXCJmcnBcIiB8fCByZXNvbHZlZC50cmFuc3BvcnQgPT09IFwiYm90aFwiKSAmJiByZXNvbHZlZC5mcnApIHtcbiAgICBjb25zdCBmcnBFbnYgPSBhd2FpdCBmcnAuaW5zdGFsbEZycCgpO1xuICAgIGNvbnN0IGNvbmZpZ1BhdGggPSBhd2FpdCBmcnAud3JpdGVGcnBDb25maWcocmVzb2x2ZWQuZnJwLCBmcnBFbnYpO1xuICAgIGNoaWxkcmVuLnB1c2goZnJwLnN0YXJ0RnJwKHJlc29sdmVkLmZycCwgZnJwRW52LCBjb25maWdQYXRoKSk7XG4gIH1cbiAgY29uc3QgYWN0aXZlVHVubmVsQ291bnQgPSBjaGlsZHJlbi5sZW5ndGggLSAoZXhlY3V0YWJsZSA/IDEgOiAwKTtcbiAgaWYgKCFhY3RpdmVUdW5uZWxDb3VudCkge1xuICAgIGxvZ2dlci5pbmZvKFwibm8gc2VsZWN0ZWQgcHVibGljIHR1bm5lbCBpcyBjb25maWd1cmVkXCIsIHtcbiAgICAgIHRyYW5zcG9ydDogcmVzb2x2ZWQudHJhbnNwb3J0LFxuICAgICAgcHVibGljUG9ydDogcmVzb2x2ZWQucHVibGljUG9ydCxcbiAgICB9KTtcbiAgfVxuICBzdXBlcnZpc2UoY2hpbGRyZW4pO1xufVxuXG4vKiogVGhlIGBkYnggdHVubmVsYCBwcm9ncmFtLiBObyBzaWRlIGVmZmVjdHMgdW50aWwgcGFyc2VkLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJ1aWxkUHJvZ3JhbShuYW1lID0gXCJkYnggdHVubmVsXCIpOiBDb21tYW5kIHtcbiAgY29uc3QgcHJvZ3JhbSA9IGFkZEFyZ3MoXG4gICAgbmV3IENvbW1hbmQoKVxuICAgICAgLm5hbWUobmFtZSlcbiAgICAgIC5kZXNjcmlwdGlvbihcIkZyb250IGEgY29tbWFuZCB3aXRoIGEgcHVibGljIHR1bm5lbCBhbmQgcGFzc3dvcmRsZXNzIGF1dGhcIiksXG4gICAgVHVubmVsT3B0aW9uc1NjaGVtYSxcbiAgKTtcblxuICAvLyBgcnVuYCBpcyB0aGUgREVGQVVMVCBhY3Rpb24gYXMgd2VsbCBhcyBhIG5hbWVkIHN1YmNvbW1hbmQsIHByZXNlcnZpbmcgdGhlIG9sZFxuICAvLyB3cmFwcGVyJ3MgZXJnb25vbWljcyAoYGRieCB0dW5uZWwgLS1hbGxvdyB4IC0tIGJ1biBzcmMvc2VydmVyLnRzYCkgd2hpbGVcbiAgLy8gbGVhdmluZyBzb21ld2hlcmUgZm9yIGBzdGF0dXNgIGFuZCBgaW5zdGFsbGAgdG8gbGl2ZS5cbiAgcHJvZ3JhbVxuICAgIC5hcmd1bWVudChcIltjb21tYW5kLi4uXVwiLCBcInRoZSBjb21tYW5kIHRvIHdyYXAsIGFmdGVyIGAtLWBcIilcbiAgICAuYWN0aW9uKGFzeW5jIChjb21tYW5kOiBzdHJpbmdbXSkgPT4ge1xuICAgICAgYXdhaXQgcnVuKHBhcnNlQXJncyhwcm9ncmFtLCBUdW5uZWxPcHRpb25zU2NoZW1hKSwgY29tbWFuZCk7XG4gICAgfSk7XG5cbiAgY29uc3QgcnVuQ29tbWFuZCA9IGFkZEFyZ3MoXG4gICAgcHJvZ3JhbS5jb21tYW5kKFwicnVuXCIpLmRlc2NyaXB0aW9uKFwiV3JhcCBhIGNvbW1hbmQgKHRoZSBkZWZhdWx0IGFjdGlvbilcIiksXG4gICAgVHVubmVsT3B0aW9uc1NjaGVtYSxcbiAgKTtcbiAgcnVuQ29tbWFuZFxuICAgIC5hcmd1bWVudChcIjxjb21tYW5kLi4uPlwiLCBcInRoZSBjb21tYW5kIHRvIHdyYXAsIGFmdGVyIGAtLWBcIilcbiAgICAuYWN0aW9uKGFzeW5jIChjb21tYW5kOiBzdHJpbmdbXSkgPT4ge1xuICAgICAgYXdhaXQgcnVuKHBhcnNlQXJncyhydW5Db21tYW5kLCBUdW5uZWxPcHRpb25zU2NoZW1hKSwgY29tbWFuZCk7XG4gICAgfSk7XG5cbiAgLy8gV29ydGggaXRzIG93biBjb21tYW5kOiB0aGUgbW9zdCBjb21tb24gZmFpbHVyZSBpcyBhIHR1bm5lbCB0aGF0IHNpbGVudGx5XG4gIC8vIGRvZXMgbm90aGluZyBiZWNhdXNlIG5vIHRva2VuIG9yIGRvbWFpbiByZXNvbHZlZCwgYW5kIHRoaXMgcHJpbnRzIGV4YWN0bHlcbiAgLy8gd2hhdCB3b3VsZCBoYXBwZW4gd2l0aG91dCBzdGFydGluZyBhbnl0aGluZy5cbiAgY29uc3Qgc3RhdHVzQ29tbWFuZCA9IGFkZEFyZ3MoXG4gICAgcHJvZ3JhbS5jb21tYW5kKFwic3RhdHVzXCIpLmRlc2NyaXB0aW9uKFwiUmVzb2x2ZSB0aGUgY29uZmlndXJhdGlvbiBhbmQgcHJpbnQgaXRcIiksXG4gICAgVHVubmVsT3B0aW9uc1NjaGVtYSxcbiAgKTtcbiAgc3RhdHVzQ29tbWFuZC5hY3Rpb24oKCkgPT4ge1xuICAgIGNvbnN0IHJlc29sdmVkID0gcmVzb2x2ZVR1bm5lbE9wdGlvbnMocGFyc2VBcmdzKHN0YXR1c0NvbW1hbmQsIFR1bm5lbE9wdGlvbnNTY2hlbWEpKTtcbiAgICBwcm9jZXNzLnN0ZG91dC53cml0ZShgJHtKU09OLnN0cmluZ2lmeShyZXNvbHZlZCwgbnVsbCwgMil9XFxuYCk7XG4gIH0pO1xuXG4gIHByb2dyYW1cbiAgICAuY29tbWFuZChcImluc3RhbGxcIilcbiAgICAuYXJndW1lbnQoXCJbdHJhbnNwb3J0XVwiLCBcInBvcnRyLCBmcnAsIG9yIGJvdGhcIiwgXCJwb3J0clwiKVxuICAgIC5kZXNjcmlwdGlvbihcIkluc3RhbGwgcHVibGljIHR1bm5lbCBjbGllbnQgYmluYXJpZXMgYW5kIGV4aXRcIilcbiAgICAuYWN0aW9uKGFzeW5jICh0cmFuc3BvcnQ6IHN0cmluZykgPT4ge1xuICAgICAgY29uc3QgcGFyc2VkID0gaW50ZXJjZXB0b3IuVHVubmVsVHJhbnNwb3J0U2NoZW1hLnBhcnNlKHRyYW5zcG9ydCk7XG4gICAgICBpZiAocGFyc2VkID09PSBcInBvcnRyXCIgfHwgcGFyc2VkID09PSBcImJvdGhcIikgYXdhaXQgcG9ydHIuaW5zdGFsbFBvcnRyKCk7XG4gICAgICBpZiAocGFyc2VkID09PSBcImZycFwiIHx8IHBhcnNlZCA9PT0gXCJib3RoXCIpIGF3YWl0IGZycC5pbnN0YWxsRnJwKCk7XG4gICAgfSk7XG5cbiAgcmV0dXJuIHByb2dyYW07XG59XG4iXX0=
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flag -> config -> default resolution for `dbx tunnel`.
|
|
3
|
+
*
|
|
4
|
+
* The point of this module is that it does almost NOTHING itself: every gate
|
|
5
|
+
* setting is handed straight to `@dbx-tools/tunnel`'s own
|
|
6
|
+
* `plugin.resolveAuthGateConfig`, which is the same function the in-process
|
|
7
|
+
* plugin path calls. The CLI therefore cannot drift from the plugin on a default,
|
|
8
|
+
* an env name, or a coercion rule - a flag is just a value passed where the
|
|
9
|
+
* plugin's `config` object would go, and `config.*` fills the rest in from the
|
|
10
|
+
* environment, a `.env` file, or `databricks.yml`.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
import { type AuthGateConfig, frp, interceptor, plugin, portr } from "@dbx-tools/tunnel";
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
export declare const TunnelOptionsSchema: z.ZodObject<{
|
|
17
|
+
transport: z.ZodOptional<z.ZodEnum<{
|
|
18
|
+
both: "both";
|
|
19
|
+
portr: "portr";
|
|
20
|
+
frp: "frp";
|
|
21
|
+
}>>;
|
|
22
|
+
publicDomain: z.ZodOptional<z.ZodString>;
|
|
23
|
+
subdomain: z.ZodOptional<z.ZodString>;
|
|
24
|
+
port: z.ZodOptional<z.ZodCoercedNumber<number>>;
|
|
25
|
+
appPort: z.ZodOptional<z.ZodCoercedNumber<number>>;
|
|
26
|
+
allow: z.ZodOptional<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodArray<z.ZodString>>>;
|
|
27
|
+
subject: z.ZodOptional<z.ZodString>;
|
|
28
|
+
brandName: z.ZodOptional<z.ZodString>;
|
|
29
|
+
message: z.ZodOptional<z.ZodString>;
|
|
30
|
+
sessionTtlSeconds: z.ZodOptional<z.ZodCoercedNumber<number>>;
|
|
31
|
+
codeTtlSeconds: z.ZodOptional<z.ZodCoercedNumber<number>>;
|
|
32
|
+
sessionCutoff: z.ZodOptional<z.ZodString>;
|
|
33
|
+
storage: z.ZodOptional<z.ZodEnum<{
|
|
34
|
+
auto: "auto";
|
|
35
|
+
lakebase: "lakebase";
|
|
36
|
+
sqlite: "sqlite";
|
|
37
|
+
}>>;
|
|
38
|
+
sqlitePath: z.ZodOptional<z.ZodString>;
|
|
39
|
+
forwardHeaders: z.ZodOptional<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodArray<z.ZodString>>>;
|
|
40
|
+
gatePaths: z.ZodOptional<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodArray<z.ZodString>>>;
|
|
41
|
+
bindHosts: z.ZodOptional<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodArray<z.ZodString>>>;
|
|
42
|
+
insecure: z.ZodOptional<z.ZodBoolean>;
|
|
43
|
+
frpServer: z.ZodOptional<z.ZodString>;
|
|
44
|
+
frpPublicDomain: z.ZodOptional<z.ZodString>;
|
|
45
|
+
frpServerPort: z.ZodOptional<z.ZodCoercedNumber<number>>;
|
|
46
|
+
frpProtocol: z.ZodOptional<z.ZodString>;
|
|
47
|
+
frpToken: z.ZodOptional<z.ZodString>;
|
|
48
|
+
frpProxyName: z.ZodOptional<z.ZodString>;
|
|
49
|
+
}, z.core.$strict>;
|
|
50
|
+
export type TunnelOptions = z.output<typeof TunnelOptionsSchema>;
|
|
51
|
+
/** Fully resolved listener, gate, and transport settings used to start a tunnel. */
|
|
52
|
+
export interface ResolvedTunnelOptions {
|
|
53
|
+
/** The port the wrapper itself listens on - what portr forwards to. */
|
|
54
|
+
publicPort: number;
|
|
55
|
+
/** The private port the wrapped app is told to bind. Unset means "pick one". */
|
|
56
|
+
appPort?: number;
|
|
57
|
+
/** Interface IPs the gate listens on. Empty means the default (0.0.0.0). */
|
|
58
|
+
bindHosts: string[];
|
|
59
|
+
transport: interceptor.TunnelTransport;
|
|
60
|
+
/**
|
|
61
|
+
* The gate config as the `authGate` PLUGIN takes it - flags only, nothing
|
|
62
|
+
* resolved. Passed straight to the plugin so it applies its own fallbacks
|
|
63
|
+
* exactly once, in the one place that owns them.
|
|
64
|
+
*/
|
|
65
|
+
gateConfig: AuthGateConfig;
|
|
66
|
+
/** The same config after the plugin's resolution, for `status` and for routing. */
|
|
67
|
+
gate: plugin.ResolvedAuthGateConfig;
|
|
68
|
+
portr: ReturnType<typeof portr.resolvePortrConfig>;
|
|
69
|
+
frp: ReturnType<typeof frp.resolveFrpConfig>;
|
|
70
|
+
}
|
|
71
|
+
/** Resolve CLI flag values through the owning tunnel and auth-gate configuration rules. */
|
|
72
|
+
export declare function resolveTunnelOptions(options: TunnelOptions): ResolvedTunnelOptions;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flag -> config -> default resolution for `dbx tunnel`.
|
|
3
|
+
*
|
|
4
|
+
* The point of this module is that it does almost NOTHING itself: every gate
|
|
5
|
+
* setting is handed straight to `@dbx-tools/tunnel`'s own
|
|
6
|
+
* `plugin.resolveAuthGateConfig`, which is the same function the in-process
|
|
7
|
+
* plugin path calls. The CLI therefore cannot drift from the plugin on a default,
|
|
8
|
+
* an env name, or a coercion rule - a flag is just a value passed where the
|
|
9
|
+
* plugin's `config` object would go, and `config.*` fills the rest in from the
|
|
10
|
+
* environment, a `.env` file, or `databricks.yml`.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
import { configUtils } from "@dbx-tools/core";
|
|
15
|
+
import { options as sharedOptions, stringUtils } from "@dbx-tools/shared-core";
|
|
16
|
+
import { frp, interceptor, plugin, portr } from "@dbx-tools/tunnel";
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
const text = (description) => z.string().trim().min(1).optional().describe(description);
|
|
19
|
+
const port = (description) => sharedOptions.tcpPortOrZeroSchema.optional().describe(description);
|
|
20
|
+
const list = (description) => z
|
|
21
|
+
.preprocess((value) => (typeof value === "string" ? stringUtils.parseList(value) : value), z.array(z.string().trim().min(1)))
|
|
22
|
+
.optional()
|
|
23
|
+
.describe(description);
|
|
24
|
+
export const TunnelOptionsSchema = z
|
|
25
|
+
.object({
|
|
26
|
+
transport: interceptor.TunnelTransportSchema.optional()
|
|
27
|
+
.describe("Public tunnel transport.")
|
|
28
|
+
.meta({ env: "TUNNEL_TRANSPORT" }),
|
|
29
|
+
publicDomain: text("Public tunnel domain.").meta({ env: "TUNNEL_PUBLIC_DOMAIN" }),
|
|
30
|
+
subdomain: text("Portr subdomain."),
|
|
31
|
+
port: port("Public listener port.").meta({
|
|
32
|
+
env: sharedOptions.databricksEnvironmentNames.appPort,
|
|
33
|
+
}),
|
|
34
|
+
appPort: port("Private wrapped application port.").meta({
|
|
35
|
+
env: ["TUNNEL_APP_PORT", "APP_PORT"],
|
|
36
|
+
}),
|
|
37
|
+
allow: list("Email allow-list patterns.").meta({ env: "TUNNEL_AUTH_ALLOW" }),
|
|
38
|
+
subject: text("Verification email subject.").meta({ env: "TUNNEL_AUTH_SUBJECT" }),
|
|
39
|
+
brandName: text("Verification email brand name.").meta({ env: "TUNNEL_AUTH_BRAND_NAME" }),
|
|
40
|
+
message: text("Verification email message.").meta({ env: "TUNNEL_AUTH_MESSAGE" }),
|
|
41
|
+
sessionTtlSeconds: z.coerce
|
|
42
|
+
.number()
|
|
43
|
+
.positive()
|
|
44
|
+
.optional()
|
|
45
|
+
.describe("Session lifetime in seconds.")
|
|
46
|
+
.meta({ env: "TUNNEL_AUTH_SESSION_TTL" }),
|
|
47
|
+
codeTtlSeconds: z.coerce
|
|
48
|
+
.number()
|
|
49
|
+
.positive()
|
|
50
|
+
.optional()
|
|
51
|
+
.describe("One-time-code lifetime in seconds.")
|
|
52
|
+
.meta({ env: "TUNNEL_AUTH_CODE_TTL" }),
|
|
53
|
+
sessionCutoff: text("Invalidate sessions issued before this value.").meta({
|
|
54
|
+
env: "TUNNEL_AUTH_SESSION_CUTOFF",
|
|
55
|
+
}),
|
|
56
|
+
storage: z
|
|
57
|
+
.enum(["auto", "lakebase", "sqlite"])
|
|
58
|
+
.optional()
|
|
59
|
+
.describe("Authentication database mode.")
|
|
60
|
+
.meta({ env: "TUNNEL_AUTH_STORAGE" }),
|
|
61
|
+
sqlitePath: text("Local authentication SQLite file.").meta({
|
|
62
|
+
env: "TUNNEL_AUTH_SQLITE_PATH",
|
|
63
|
+
}),
|
|
64
|
+
forwardHeaders: list("Additional forwarded request header patterns.").meta({
|
|
65
|
+
env: "TUNNEL_FORWARD_HEADERS",
|
|
66
|
+
}),
|
|
67
|
+
gatePaths: list("Additional path prefixes requiring authentication.").meta({
|
|
68
|
+
env: "TUNNEL_GATE_PATHS",
|
|
69
|
+
}),
|
|
70
|
+
bindHosts: list("Interface IPs the gate listens on."),
|
|
71
|
+
insecure: z
|
|
72
|
+
.boolean()
|
|
73
|
+
.optional()
|
|
74
|
+
.describe("Run without an authentication gate.")
|
|
75
|
+
.meta({ env: "TUNNEL_INSECURE" }),
|
|
76
|
+
frpServer: text("FRP control host.").meta({ env: "FRP_SERVER" }),
|
|
77
|
+
frpPublicDomain: text("FRP public HTTP domain.").meta({
|
|
78
|
+
env: "TUNNEL_FRP_PUBLIC_DOMAIN",
|
|
79
|
+
}),
|
|
80
|
+
frpServerPort: port("FRP control port.").meta({ env: "FRP_SERVER_PORT" }),
|
|
81
|
+
frpProtocol: text("FRP transport protocol.").meta({ env: "FRP_PROTOCOL" }),
|
|
82
|
+
frpToken: text("FRP authentication token.").meta({ env: ["FRP_TOKEN", "TUNNEL_TOKEN"] }),
|
|
83
|
+
frpProxyName: text("FRP proxy registration name.").meta({ env: "FRP_PROXY_NAME" }),
|
|
84
|
+
})
|
|
85
|
+
.strict()
|
|
86
|
+
.describe("Public tunnel wrapper command-line options.");
|
|
87
|
+
/** Resolve CLI flag values through the owning tunnel and auth-gate configuration rules. */
|
|
88
|
+
export function resolveTunnelOptions(options) {
|
|
89
|
+
// The Databricks Apps runtime contract: the platform routes to
|
|
90
|
+
// DATABRICKS_APP_PORT, so the WRAPPER claims it and the wrapped app is moved
|
|
91
|
+
// to a private one.
|
|
92
|
+
const publicPort = configUtils.port(options.port, "DATABRICKS_APP_PORT", 8000);
|
|
93
|
+
const appPort = configUtils.port(options.appPort, "APP_PORT", 0, { prefix: "TUNNEL" });
|
|
94
|
+
const gateConfig = {
|
|
95
|
+
allow: options.allow,
|
|
96
|
+
subject: options.subject,
|
|
97
|
+
brandName: options.brandName,
|
|
98
|
+
message: options.message,
|
|
99
|
+
// Coerced, not resolved: the plugin owns the env name and the default for
|
|
100
|
+
// these, so the flag is passed through as the `config` value it expects and
|
|
101
|
+
// only needs a string -> number nudge on the way.
|
|
102
|
+
sessionTtlSeconds: options.sessionTtlSeconds,
|
|
103
|
+
codeTtlSeconds: options.codeTtlSeconds,
|
|
104
|
+
sessionCutoff: options.sessionCutoff,
|
|
105
|
+
storage: options.storage,
|
|
106
|
+
sqlitePath: options.sqlitePath,
|
|
107
|
+
forwardHeaders: options.forwardHeaders,
|
|
108
|
+
gatePaths: options.gatePaths,
|
|
109
|
+
insecure: options.insecure,
|
|
110
|
+
publicDomain: options.publicDomain,
|
|
111
|
+
publicDomains: options.frpPublicDomain ? [options.frpPublicDomain] : undefined,
|
|
112
|
+
};
|
|
113
|
+
const gate = plugin.resolveAuthGateConfig(gateConfig);
|
|
114
|
+
const transport = interceptor.resolveTunnelTransport(options.transport);
|
|
115
|
+
return {
|
|
116
|
+
publicPort,
|
|
117
|
+
...(appPort > 0 ? { appPort } : {}),
|
|
118
|
+
bindHosts: options.bindHosts ?? [],
|
|
119
|
+
transport,
|
|
120
|
+
gateConfig,
|
|
121
|
+
gate,
|
|
122
|
+
portr: portr.resolvePortrConfig({
|
|
123
|
+
publicDomain: gate.publicDomain,
|
|
124
|
+
subdomain: options.subdomain,
|
|
125
|
+
port: publicPort,
|
|
126
|
+
}),
|
|
127
|
+
frp: frp.resolveFrpConfig({
|
|
128
|
+
publicDomain: options.frpPublicDomain,
|
|
129
|
+
server: options.frpServer,
|
|
130
|
+
serverPort: options.frpServerPort,
|
|
131
|
+
protocol: options.frpProtocol,
|
|
132
|
+
token: options.frpToken,
|
|
133
|
+
proxyName: options.frpProxyName,
|
|
134
|
+
port: publicPort,
|
|
135
|
+
}),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3B0aW9ucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy90dW5uZWwvb3B0aW9ucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7O0dBWUc7QUFFSCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDOUMsT0FBTyxFQUFFLE9BQU8sSUFBSSxhQUFhLEVBQUUsV0FBVyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDL0UsT0FBTyxFQUF1QixHQUFHLEVBQUUsV0FBVyxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUN6RixPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBRXhCLE1BQU0sSUFBSSxHQUFHLENBQUMsV0FBbUIsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLElBQUksRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDLENBQUM7QUFDaEcsTUFBTSxJQUFJLEdBQUcsQ0FBQyxXQUFtQixFQUFFLEVBQUUsQ0FDbkMsYUFBYSxDQUFDLG1CQUFtQixDQUFDLFFBQVEsRUFBRSxDQUFDLFFBQVEsQ0FBQyxXQUFXLENBQUMsQ0FBQztBQUNyRSxNQUFNLElBQUksR0FBRyxDQUFDLFdBQW1CLEVBQUUsRUFBRSxDQUNuQyxDQUFDO0tBQ0UsVUFBVSxDQUNULENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDLE9BQU8sS0FBSyxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEVBQzdFLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLElBQUksRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxD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O1FBQ2xDLFNBQVM7UUFDVCxVQUFVO1FBQ1YsSUFBSTtRQUNKLEtBQUssRUFBRSxLQUFLLENBQUMsa0JBQWtCLENBQUM7WUFDOUIsWUFBWSxFQUFFLElBQUksQ0FBQyxZQUFZO1lBQy9CLFNBQVMsRUFBRSxPQUFPLENBQUMsU0FBUztZQUM1QixJQUFJLEVBQUUsVUFBVTtTQUNqQixDQUFDO1FBQ0YsR0FBRyxFQUFFLEdBQUcsQ0FBQyxnQkFBZ0IsQ0FBQztZQUN4QixZQUFZLEVBQUUsT0FBTyxDQUFDLGVBQWU7WUFDckMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxTQUFTO1lBQ3pCLFVBQVUsRUFBRSxPQUFPLENBQUMsYUFBYTtZQUNqQyxRQUFRLEVBQUUsT0FBTyxDQUFDLFdBQVc7WUFDN0IsS0FBSyxFQUFFLE9BQU8sQ0FBQyxRQUFRO1lBQ3ZCLFNBQVMsRUFBRSxPQUFPLENBQUMsWUFBWTtZQUMvQixJQUFJLEVBQUUsVUFBVTtTQUNqQixDQUFDO0tBQ0gsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEZsYWcgLT4gY29uZmlnIC0+IGRlZmF1bHQgcmVzb2x1dGlvbiBmb3IgYGRieCB0dW5uZWxgLlxuICpcbiAqIFRoZSBwb2ludCBvZiB0aGlzIG1vZHVsZSBpcyB0aGF0IGl0IGRvZXMgYWxtb3N0IE5PVEhJTkcgaXRzZWxmOiBldmVyeSBnYXRlXG4gKiBzZXR0aW5nIGlzIGhhbmRlZCBzdHJhaWdodCB0byBgQGRieC10b29scy90dW5uZWxgJ3Mgb3duXG4gKiBgcGx1Z2luLnJlc29sdmVBdXRoR2F0ZUNvbmZpZ2AsIHdoaWNoIGlzIHRoZSBzYW1lIGZ1bmN0aW9uIHRoZSBpbi1wcm9jZXNzXG4gKiBwbHVnaW4gcGF0aCBjYWxscy4gVGhlIENMSSB0aGVyZWZvcmUgY2Fubm90IGRyaWZ0IGZyb20gdGhlIHBsdWdpbiBvbiBhIGRlZmF1bHQsXG4gKiBhbiBlbnYgbmFtZSwgb3IgYSBjb2VyY2lvbiBydWxlIC0gYSBmbGFnIGlzIGp1c3QgYSB2YWx1ZSBwYXNzZWQgd2hlcmUgdGhlXG4gKiBwbHVnaW4ncyBgY29uZmlnYCBvYmplY3Qgd291bGQgZ28sIGFuZCBgY29uZmlnLipgIGZpbGxzIHRoZSByZXN0IGluIGZyb20gdGhlXG4gKiBlbnZpcm9ubWVudCwgYSBgLmVudmAgZmlsZSwgb3IgYGRhdGFicmlja3MueW1sYC5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgY29uZmlnVXRpbHMgfSBmcm9tIFwiQGRieC10b29scy9jb3JlXCI7XG5pbXBvcnQgeyBvcHRpb25zIGFzIHNoYXJlZE9wdGlvbnMsIHN0cmluZ1V0aWxzIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB7IHR5cGUgQXV0aEdhdGVDb25maWcsIGZycCwgaW50ZXJjZXB0b3IsIHBsdWdpbiwgcG9ydHIgfSBmcm9tIFwiQGRieC10b29scy90dW5uZWxcIjtcbmltcG9ydCB7IHogfSBmcm9tIFwiem9kXCI7XG5cbmNvbnN0IHRleHQgPSAoZGVzY3JpcHRpb246IHN0cmluZykgPT4gei5zdHJpbmcoKS50cmltKCkubWluKDEpLm9wdGlvbmFsKCkuZGVzY3JpYmUoZGVzY3JpcHRpb24pO1xuY29uc3QgcG9ydCA9IChkZXNjcmlwdGlvbjogc3RyaW5nKSA9PlxuICBzaGFyZWRPcHRpb25zLnRjcFBvcnRPclplcm9TY2hlbWEub3B0aW9uYWwoKS5kZXNjcmliZShkZXNjcmlwdGlvbik7XG5jb25zdCBsaXN0ID0gKGRlc2NyaXB0aW9uOiBzdHJpbmcpID0+XG4gIHpcbiAgICAucHJlcHJvY2VzcyhcbiAgICAgICh2YWx1ZSkgPT4gKHR5cGVvZiB2YWx1ZSA9PT0gXCJzdHJpbmdcIiA/IHN0cmluZ1V0aWxzLnBhcnNlTGlzdCh2YWx1ZSkgOiB2YWx1ZSksXG4gICAgICB6LmFycmF5KHouc3RyaW5nKCkudHJpbSgpLm1pbigxKSksXG4gICAgKVxuICAgIC5vcHRpb25hbCgpXG4gICAgLmRlc2NyaWJlKGRlc2NyaXB0aW9uKTtcblxuZXhwb3J0IGNvbnN0IFR1bm5lbE9wdGlvbnNTY2hlbWEgPSB6XG4gIC5vYmplY3Qoe1xuICAgIHRyYW5zcG9ydDogaW50ZXJjZXB0b3IuVHVubmVsVHJhbnNwb3J0U2NoZW1hLm9wdGlvbmFsKClcbiAgICAgIC5kZXNjcmliZShcIlB1YmxpYyB0dW5uZWwgdHJhbnNwb3J0LlwiKVxuICAgICAgLm1ldGEoeyBlbnY6IFwiVFVOTkVMX1RSQU5TUE9SVFwiIH0pLFxuICAgIHB1YmxpY0RvbWFpbjogdGV4dChcIlB1YmxpYyB0dW5uZWwgZG9tYWluLlwiKS5tZXRhKHsgZW52OiBcIlRVTk5FTF9QVUJMSUNfRE9NQUlOXCIgfSksXG4gICAgc3ViZG9tYWluOiB0ZXh0KFwiUG9ydHIgc3ViZG9tYWluLlwiKSxcbiAgICBwb3J0OiBwb3J0KFwiUHVibGljIGxpc3RlbmVyIHBvcnQuXCIpLm1ldGEoe1xuICAgICAgZW52OiBzaGFyZWRPcHRpb25zLmRhdGFicmlja3NFbnZpcm9ubWVudE5hbWVzLmFwcFBvcnQsXG4gICAgfSksXG4gICAgYXBwUG9ydDogcG9ydChcIlByaXZhdGUgd3JhcHBlZCBhcHBsaWNhdGlvbiBwb3J0LlwiKS5tZXRhKHtcbiAgICAgIGVudjogW1wiVFVOTkVMX0FQUF9QT1JUXCIsIFwiQVBQX1BPUlRcIl0sXG4gICAgfSksXG4gICAgYWxsb3c6IGxpc3QoXCJFbWFpbCBhbGxvdy1saXN0IHBhdHRlcm5zLlwiKS5tZXRhKHsgZW52OiBcIlRVTk5FTF9BVVRIX0FMTE9XXCIgfSksXG4gICAgc3ViamVjdDogdGV4dChcIlZlcmlmaWNhdGlvbiBlbWFpbCBzdWJqZWN0LlwiKS5tZXRhKHsgZW52OiBcIlRVTk5FTF9BVVRIX1NVQkpFQ1RcIiB9KSxcbiAgICBicmFuZE5hbWU6IHRleHQoXCJWZXJpZmljYXRpb24gZW1haWwgYnJhbmQgbmFtZS5cIikubWV0YSh7IGVudjogXCJUVU5ORUxfQVVUSF9CUkFORF9OQU1FXCIgfSksXG4gICAgbWVzc2FnZTogdGV4dChcIlZlcmlmaWNhdGlvbiBlbWFpbCBtZXNzYWdlLlwiKS5tZXRhKHsgZW52OiBcIlRVTk5FTF9BVVRIX01FU1NBR0VcIiB9KSxcbiAgICBzZXNzaW9uVHRsU2Vjb25kczogei5jb2VyY2VcbiAgICAgIC5udW1iZXI8bnVtYmVyPigpXG4gICAgICAucG9zaXRpdmUoKVxuICAgICAgLm9wdGlvbmFsKClcbiAgICAgIC5kZXNjcmliZShcIlNlc3Npb24gbGlmZXRpbWUgaW4gc2Vjb25kcy5cIilcbiAgICAgIC5tZXRhKHsgZW52OiBcIlRVTk5FTF9BVVRIX1NFU1NJT05fVFRMXCIgfSksXG4gICAgY29kZVR0bFNlY29uZHM6IHouY29lcmNlXG4gICAgICAubnVtYmVyPG51bWJlcj4oKVxuICAgICAgLnBvc2l0aXZlKClcbiAgICAgIC5vcHRpb25hbCgpXG4gICAgICAuZGVzY3JpYmUoXCJPbmUtdGltZS1jb2RlIGxpZmV0aW1lIGluIHNlY29uZHMuXCIpXG4gICAgICAubWV0YSh7IGVudjogXCJUVU5ORUxfQVVUSF9DT0RFX1RUTFwiIH0pLFxuICAgIHNlc3Npb25DdXRvZmY6IHRleHQoXCJJbnZhbGlkYXRlIHNlc3Npb25zIGlzc3VlZCBiZWZvcmUgdGhpcyB2YWx1ZS5cIikubWV0YSh7XG4gICAgICBlbnY6IFwiVFVOTkVMX0FVVEhfU0VTU0lPTl9DVVRPRkZcIixcbiAgICB9KSxcbiAgICBzdG9yYWdlOiB6XG4gICAgICAuZW51bShbXCJhdXRvXCIsIFwibGFrZWJhc2VcIiwgXCJzcWxpdGVcIl0pXG4gICAgICAub3B0aW9uYWwoKVxuICAgICAgLmRlc2NyaWJlKFwiQXV0aGVudGljYXRpb24gZGF0YWJhc2UgbW9kZS5cIilcbiAgICAgIC5tZXRhKHsgZW52OiBcIlRVTk5FTF9BVVRIX1NUT1JBR0VcIiB9KSxcbiAgICBzcWxpdGVQYXRoOiB0ZXh0KFwiTG9jYWwgYXV0aGVudGljYXRpb24gU1FMaXRlIGZpbGUuXCIpLm1ldGEoe1xuICAgICAgZW52OiBcIlRVTk5FTF9BVVRIX1NRTElURV9QQVRIXCIsXG4gICAgfSksXG4gICAgZm9yd2FyZEhlYWRlcnM6IGxpc3QoXCJBZGRpdGlvbmFsIGZvcndhcmRlZCByZXF1ZXN0IGhlYWRlciBwYXR0ZXJucy5cIikubWV0YSh7XG4gICAgICBlbnY6IFwiVFVOTkVMX0ZPUldBUkRfSEVBREVSU1wiLFxuICAgIH0pLFxuICAgIGdhdGVQYXRoczogbGlzdChcIkFkZGl0aW9uYWwgcGF0aCBwcmVmaXhlcyByZXF1aXJpbmcgYXV0aGVudGljYXRpb24uXCIpLm1ldGEoe1xuICAgICAgZW52OiBcIlRVTk5FTF9HQVRFX1BBVEhTXCIsXG4gICAgfSksXG4gICAgYmluZEhvc3RzOiBsaXN0KFwiSW50ZXJmYWNlIElQcyB0aGUgZ2F0ZSBsaXN0ZW5zIG9uLlwiKSxcbiAgICBpbnNlY3VyZTogelxuICAgICAgLmJvb2xlYW4oKVxuICAgICAgLm9wdGlvbmFsKClcbiAgICAgIC5kZXNjcmliZShcIlJ1biB3aXRob3V0IGFuIGF1dGhlbnRpY2F0aW9uIGdhdGUuXCIpXG4gICAgICAubWV0YSh7IGVudjogXCJUVU5ORUxfSU5TRUNVUkVcIiB9KSxcbiAgICBmcnBTZXJ2ZXI6IHRleHQoXCJGUlAgY29udHJvbCBob3N0LlwiKS5tZXRhKHsgZW52OiBcIkZSUF9TRVJWRVJcIiB9KSxcbiAgICBmcnBQdWJsaWNEb21haW46IHRleHQoXCJGUlAgcHVibGljIEhUVFAgZG9tYWluLlwiKS5tZXRhKHtcbiAgICAgIGVudjogXCJUVU5ORUxfRlJQX1BVQkxJQ19ET01BSU5cIixcbiAgICB9KSxcbiAgICBmcnBTZXJ2ZXJQb3J0OiBwb3J0KFwiRlJQIGNvbnRyb2wgcG9ydC5cIikubWV0YSh7IGVudjogXCJGUlBfU0VSVkVSX1BPUlRcIiB9KSxcbiAgICBmcnBQcm90b2NvbDogdGV4dChcIkZSUCB0cmFuc3BvcnQgcHJvdG9jb2wuXCIpLm1ldGEoeyBlbnY6IFwiRlJQX1BST1RPQ09MXCIgfSksXG4gICAgZnJwVG9rZW46IHRleHQoXCJGUlAgYXV0aGVudGljYXRpb24gdG9rZW4uXCIpLm1ldGEoeyBlbnY6IFtcIkZSUF9UT0tFTlwiLCBcIlRVTk5FTF9UT0tFTlwiXSB9KSxcbiAgICBmcnBQcm94eU5hbWU6IHRleHQoXCJGUlAgcHJveHkgcmVnaXN0cmF0aW9uIG5hbWUuXCIpLm1ldGEoeyBlbnY6IFwiRlJQX1BST1hZX05BTUVcIiB9KSxcbiAgfSlcbiAgLnN0cmljdCgpXG4gIC5kZXNjcmliZShcIlB1YmxpYyB0dW5uZWwgd3JhcHBlciBjb21tYW5kLWxpbmUgb3B0aW9ucy5cIik7XG5cbmV4cG9ydCB0eXBlIFR1bm5lbE9wdGlvbnMgPSB6Lm91dHB1dDx0eXBlb2YgVHVubmVsT3B0aW9uc1NjaGVtYT47XG5cbi8qKiBGdWxseSByZXNvbHZlZCBsaXN0ZW5lciwgZ2F0ZSwgYW5kIHRyYW5zcG9ydCBzZXR0aW5ncyB1c2VkIHRvIHN0YXJ0IGEgdHVubmVsLiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZXNvbHZlZFR1bm5lbE9wdGlvbnMge1xuICAvKiogVGhlIHBvcnQgdGhlIHdyYXBwZXIgaXRzZWxmIGxpc3RlbnMgb24gLSB3aGF0IHBvcnRyIGZvcndhcmRzIHRvLiAqL1xuICBwdWJsaWNQb3J0OiBudW1iZXI7XG4gIC8qKiBUaGUgcHJpdmF0ZSBwb3J0IHRoZSB3cmFwcGVkIGFwcCBpcyB0b2xkIHRvIGJpbmQuIFVuc2V0IG1lYW5zIFwicGljayBvbmVcIi4gKi9cbiAgYXBwUG9ydD86IG51bWJlcjtcbiAgLyoqIEludGVyZmFjZSBJUHMgdGhlIGdhdGUgbGlzdGVucyBvbi4gRW1wdHkgbWVhbnMgdGhlIGRlZmF1bHQgKDAuMC4wLjApLiAqL1xuICBiaW5kSG9zdHM6IHN0cmluZ1tdO1xuICB0cmFuc3BvcnQ6IGludGVyY2VwdG9yLlR1bm5lbFRyYW5zcG9ydDtcbiAgLyoqXG4gICAqIFRoZSBnYXRlIGNvbmZpZyBhcyB0aGUgYGF1dGhHYXRlYCBQTFVHSU4gdGFrZXMgaXQgLSBmbGFncyBvbmx5LCBub3RoaW5nXG4gICAqIHJlc29sdmVkLiBQYXNzZWQgc3RyYWlnaHQgdG8gdGhlIHBsdWdpbiBzbyBpdCBhcHBsaWVzIGl0cyBvd24gZmFsbGJhY2tzXG4gICAqIGV4YWN0bHkgb25jZSwgaW4gdGhlIG9uZSBwbGFjZSB0aGF0IG93bnMgdGhlbS5cbiAgICovXG4gIGdhdGVDb25maWc6IEF1dGhHYXRlQ29uZmlnO1xuICAvKiogVGhlIHNhbWUgY29uZmlnIGFmdGVyIHRoZSBwbHVnaW4ncyByZXNvbHV0aW9uLCBmb3IgYHN0YXR1c2AgYW5kIGZvciByb3V0aW5nLiAqL1xuICBnYXRlOiBwbHVnaW4uUmVzb2x2ZWRBdXRoR2F0ZUNvbmZpZztcbiAgcG9ydHI6IFJldHVyblR5cGU8dHlwZW9mIHBvcnRyLnJlc29sdmVQb3J0ckNvbmZpZz47XG4gIGZycDogUmV0dXJuVHlwZTx0eXBlb2YgZnJwLnJlc29sdmVGcnBDb25maWc+O1xufVxuXG4vKiogUmVzb2x2ZSBDTEkgZmxhZyB2YWx1ZXMgdGhyb3VnaCB0aGUgb3duaW5nIHR1bm5lbCBhbmQgYXV0aC1nYXRlIGNvbmZpZ3VyYXRpb24gcnVsZXMuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzb2x2ZVR1bm5lbE9wdGlvbnMob3B0aW9uczogVHVubmVsT3B0aW9ucyk6IFJlc29sdmVkVHVubmVsT3B0aW9ucyB7XG4gIC8vIFRoZSBEYXRhYnJpY2tzIEFwcHMgcnVudGltZSBjb250cmFjdDogdGhlIHBsYXRmb3JtIHJvdXRlcyB0b1xuICAvLyBEQVRBQlJJQ0tTX0FQUF9QT1JULCBzbyB0aGUgV1JBUFBFUiBjbGFpbXMgaXQgYW5kIHRoZSB3cmFwcGVkIGFwcCBpcyBtb3ZlZFxuICAvLyB0byBhIHByaXZhdGUgb25lLlxuICBjb25zdCBwdWJsaWNQb3J0ID0gY29uZmlnVXRpbHMucG9ydChvcHRpb25zLnBvcnQsIFwiREFUQUJSSUNLU19BUFBfUE9SVFwiLCA4MDAwKTtcbiAgY29uc3QgYXBwUG9ydCA9IGNvbmZpZ1V0aWxzLnBvcnQob3B0aW9ucy5hcHBQb3J0LCBcIkFQUF9QT1JUXCIsIDAsIHsgcHJlZml4OiBcIlRVTk5FTFwiIH0pO1xuICBjb25zdCBnYXRlQ29uZmlnOiBBdXRoR2F0ZUNvbmZpZyA9IHtcbiAgICBhbGxvdzogb3B0aW9ucy5hbGxvdyxcbiAgICBzdWJqZWN0OiBvcHRpb25zLnN1YmplY3QsXG4gICAgYnJhbmROYW1lOiBvcHRpb25zLmJyYW5kTmFtZSxcbiAgICBtZXNzYWdlOiBvcHRpb25zLm1lc3NhZ2UsXG4gICAgLy8gQ29lcmNlZCwgbm90IHJlc29sdmVkOiB0aGUgcGx1Z2luIG93bnMgdGhlIGVudiBuYW1lIGFuZCB0aGUgZGVmYXVsdCBmb3JcbiAgICAvLyB0aGVzZSwgc28gdGhlIGZsYWcgaXMgcGFzc2VkIHRocm91Z2ggYXMgdGhlIGBjb25maWdgIHZhbHVlIGl0IGV4cGVjdHMgYW5kXG4gICAgLy8gb25seSBuZWVkcyBhIHN0cmluZyAtPiBudW1iZXIgbnVkZ2Ugb24gdGhlIHdheS5cbiAgICBzZXNzaW9uVHRsU2Vjb25kczogb3B0aW9ucy5zZXNzaW9uVHRsU2Vjb25kcyxcbiAgICBjb2RlVHRsU2Vjb25kczogb3B0aW9ucy5jb2RlVHRsU2Vjb25kcyxcbiAgICBzZXNzaW9uQ3V0b2ZmOiBvcHRpb25zLnNlc3Npb25DdXRvZmYsXG4gICAgc3RvcmFnZTogb3B0aW9ucy5zdG9yYWdlLFxuICAgIHNxbGl0ZVBhdGg6IG9wdGlvbnMuc3FsaXRlUGF0aCxcbiAgICBmb3J3YXJkSGVhZGVyczogb3B0aW9ucy5mb3J3YXJkSGVhZGVycyxcbiAgICBnYXRlUGF0aHM6IG9wdGlvbnMuZ2F0ZVBhdGhzLFxuICAgIGluc2VjdXJlOiBvcHRpb25zLmluc2VjdXJlLFxuICAgIHB1YmxpY0RvbWFpbjogb3B0aW9ucy5wdWJsaWNEb21haW4sXG4gICAgcHVibGljRG9tYWluczogb3B0aW9ucy5mcnBQdWJsaWNEb21haW4gPyBbb3B0aW9ucy5mcnBQdWJsaWNEb21haW5dIDogdW5kZWZpbmVkLFxuICB9O1xuICBjb25zdCBnYXRlID0gcGx1Z2luLnJlc29sdmVBdXRoR2F0ZUNvbmZpZyhnYXRlQ29uZmlnKTtcbiAgY29uc3QgdHJhbnNwb3J0ID0gaW50ZXJjZXB0b3IucmVzb2x2ZVR1bm5lbFRyYW5zcG9ydChvcHRpb25zLnRyYW5zcG9ydCk7XG4gIHJldHVybiB7XG4gICAgcHVibGljUG9ydCxcbiAgICAuLi4oYXBwUG9ydCA+IDAgPyB7IGFwcFBvcnQgfSA6IHt9KSxcbiAgICBiaW5kSG9zdHM6IG9wdGlvbnMuYmluZEhvc3RzID8/IFtdLFxuICAgIHRyYW5zcG9ydCxcbiAgICBnYXRlQ29uZmlnLFxuICAgIGdhdGUsXG4gICAgcG9ydHI6IHBvcnRyLnJlc29sdmVQb3J0ckNvbmZpZyh7XG4gICAgICBwdWJsaWNEb21haW46IGdhdGUucHVibGljRG9tYWluLFxuICAgICAgc3ViZG9tYWluOiBvcHRpb25zLnN1YmRvbWFpbixcbiAgICAgIHBvcnQ6IHB1YmxpY1BvcnQsXG4gICAgfSksXG4gICAgZnJwOiBmcnAucmVzb2x2ZUZycENvbmZpZyh7XG4gICAgICBwdWJsaWNEb21haW46IG9wdGlvbnMuZnJwUHVibGljRG9tYWluLFxuICAgICAgc2VydmVyOiBvcHRpb25zLmZycFNlcnZlcixcbiAgICAgIHNlcnZlclBvcnQ6IG9wdGlvbnMuZnJwU2VydmVyUG9ydCxcbiAgICAgIHByb3RvY29sOiBvcHRpb25zLmZycFByb3RvY29sLFxuICAgICAgdG9rZW46IG9wdGlvbnMuZnJwVG9rZW4sXG4gICAgICBwcm94eU5hbWU6IG9wdGlvbnMuZnJwUHJveHlOYW1lLFxuICAgICAgcG9ydDogcHVibGljUG9ydCxcbiAgICB9KSxcbiAgfTtcbn1cbiJdfQ==
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The reverse proxy that makes the wrapper path possible.
|
|
3
|
+
*
|
|
4
|
+
* The wrapper claims the PUBLIC port and the wrapped app runs as a child process
|
|
5
|
+
* on a private loopback port, so - unlike the in-process plugin - there is no
|
|
6
|
+
* middleware chain to insert the gate into. This proxy is that insertion point: it
|
|
7
|
+
* answers the login routes itself, applies the gate to everything else, and
|
|
8
|
+
* forwards what survives to the child.
|
|
9
|
+
*
|
|
10
|
+
* The gating DECISION is not reimplemented here. `@dbx-tools/tunnel`'s
|
|
11
|
+
* `gate.gateRequest` makes it - the same function the Express middleware calls -
|
|
12
|
+
* and this module only differs in how the outcome is written: a proxied request
|
|
13
|
+
* instead of `next()`, a `writeHead` instead of `res.json`. That is deliberate:
|
|
14
|
+
* two independent implementations of "which requests are gated and which headers
|
|
15
|
+
* are stripped" is the one way this package could become a security bug.
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
import { type AuthGateApi } from "@dbx-tools/tunnel";
|
|
20
|
+
/** Listener, upstream application, and optional authentication settings for the wrapper proxy. */
|
|
21
|
+
export interface ProxyOptions {
|
|
22
|
+
/** The port this proxy listens on - the port portr and the platform route to. */
|
|
23
|
+
publicPort: number;
|
|
24
|
+
/** The private loopback port the wrapped app listens on. */
|
|
25
|
+
appPort: number;
|
|
26
|
+
/** The gate handlers. Omitted (an `--insecure` run) forwards everything. */
|
|
27
|
+
gate?: AuthGateApi;
|
|
28
|
+
/** Extra `x-` headers tunnel traffic may forward. */
|
|
29
|
+
forwardHeaders?: readonly string[];
|
|
30
|
+
/** Path prefixes to gate beyond `/api/` (e.g. `/ws` for a WebSocket app). */
|
|
31
|
+
gatePaths?: readonly string[];
|
|
32
|
+
/** Brand name for the hosted login page shown on a denied browser navigation. */
|
|
33
|
+
brandName?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Addresses to listen on. Defaults to a single `0.0.0.0` (every interface),
|
|
36
|
+
* the portr/platform case. Pass specific interface IPs to expose the gate on
|
|
37
|
+
* only those - e.g. an overlay/LAN address while loopback reaches the upstream
|
|
38
|
+
* directly and ungated.
|
|
39
|
+
*/
|
|
40
|
+
bindHosts?: readonly string[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Start the proxy and resolve once it is listening.
|
|
44
|
+
*
|
|
45
|
+
* `publicDomain` is intentionally NOT passed to `gateRequest`: on this path every
|
|
46
|
+
* request arrived on the public port, so it IS tunnel traffic by construction -
|
|
47
|
+
* whereas the in-process gate shares a port with the platform front door and has
|
|
48
|
+
* to tell them apart by `Host`. Passing the request's own host keeps the shared
|
|
49
|
+
* decision function's contract satisfied without weakening it.
|
|
50
|
+
*/
|
|
51
|
+
export declare function startProxy(options: ProxyOptions): Promise<void>;
|