@forge/tunnel 6.2.2-next.3-experimental-1a0bb6a → 6.3.0-next.10
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/CHANGELOG.md +58 -6
- package/out/command/interactors/tunnel-interactor.d.ts +1 -0
- package/out/command/interactors/tunnel-interactor.d.ts.map +1 -1
- package/out/command/interactors/tunnel-interactor.js +9 -4
- package/out/command/start-tunnel-command.d.ts +6 -1
- package/out/command/start-tunnel-command.d.ts.map +1 -1
- package/out/command/start-tunnel-command.js +92 -15
- package/out/servers/dev-server.d.ts +13 -2
- package/out/servers/dev-server.d.ts.map +1 -1
- package/out/servers/dev-server.js +46 -3
- package/out/servers/resource-tunnel-server.d.ts +1 -1
- package/package.json +8 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,14 +1,66 @@
|
|
|
1
1
|
# @forge/tunnel
|
|
2
2
|
|
|
3
|
-
## 6.
|
|
3
|
+
## 6.3.0-next.10
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
-
- Updated dependencies [
|
|
8
|
-
-
|
|
9
|
-
- @forge/
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
- Updated dependencies [d3008b5]
|
|
8
|
+
- @forge/cli-shared@8.8.0-next.8
|
|
9
|
+
- @forge/bundler@6.1.9-next.10
|
|
10
|
+
|
|
11
|
+
## 6.3.0-next.9
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [a1c8e19]
|
|
16
|
+
- @forge/cli-shared@8.8.0-next.7
|
|
17
|
+
- @forge/bundler@6.1.9-next.9
|
|
18
|
+
|
|
19
|
+
## 6.3.0-next.8
|
|
20
|
+
|
|
21
|
+
### Minor Changes
|
|
22
|
+
|
|
23
|
+
- 4947176: Adding self managed tunnel support
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- Updated dependencies [4947176]
|
|
28
|
+
- @forge/cli-shared@8.8.0-next.6
|
|
29
|
+
- @forge/bundler@6.1.9-next.8
|
|
30
|
+
|
|
31
|
+
## 6.3.0-next.7
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- @forge/bundler@6.1.9-next.7
|
|
36
|
+
- @forge/cli-shared@8.8.0-next.5
|
|
37
|
+
|
|
38
|
+
## 6.3.0-next.6
|
|
39
|
+
|
|
40
|
+
### Patch Changes
|
|
41
|
+
|
|
42
|
+
- @forge/bundler@6.1.9-next.6
|
|
43
|
+
- @forge/cli-shared@8.8.0-next.4
|
|
44
|
+
|
|
45
|
+
## 6.3.0-next.5
|
|
46
|
+
|
|
47
|
+
### Patch Changes
|
|
48
|
+
|
|
49
|
+
- @forge/bundler@6.1.9-next.5
|
|
50
|
+
- @forge/cli-shared@8.8.0-next.3
|
|
51
|
+
|
|
52
|
+
## 6.3.0-next.4
|
|
53
|
+
|
|
54
|
+
### Minor Changes
|
|
55
|
+
|
|
56
|
+
- 8ff224b: Add automatic spinning up of docker compose stacks for containers when tunneling
|
|
57
|
+
|
|
58
|
+
### Patch Changes
|
|
59
|
+
|
|
60
|
+
- Updated dependencies [8ff224b]
|
|
61
|
+
- @forge/runtime@6.1.0-next.0
|
|
62
|
+
- @forge/cli-shared@8.8.0-next.2
|
|
63
|
+
- @forge/bundler@6.1.9-next.4
|
|
12
64
|
|
|
13
65
|
## 6.2.2-next.3
|
|
14
66
|
|
|
@@ -6,6 +6,7 @@ export declare class TunnelInteractor {
|
|
|
6
6
|
private readonly logger;
|
|
7
7
|
private readonly statsigService;
|
|
8
8
|
constructor(logger: Logger, statsigService: StatsigService);
|
|
9
|
+
private logTunnelStatus;
|
|
9
10
|
handleUserExitEvent(stopFunction: () => Promise<void>, bundleMonitor: WatcherMonitor | undefined, manifestFileWatcher: FSWatcher): Promise<void>;
|
|
10
11
|
watchAndWarnOnManifestChanges(manifestFilePath: string): chokidar.FSWatcher;
|
|
11
12
|
watchApp: (startTunnelResult: StartTunnelResult, tunnelOptions?: TunnelOptions) => Promise<WatcherMonitor | undefined>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tunnel-interactor.d.ts","sourceRoot":"","sources":["../../../src/command/interactors/tunnel-interactor.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EAAyC,MAAM,EAAQ,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACvH,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAG5D,OAAO,QAAQ,EAAE,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE/C,qBAAa,gBAAgB;IAGzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFzB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;gBAE7B,MAAM,EAAE,MAAM,EAC/B,cAAc,EAAE,cAAc;
|
|
1
|
+
{"version":3,"file":"tunnel-interactor.d.ts","sourceRoot":"","sources":["../../../src/command/interactors/tunnel-interactor.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EAAyC,MAAM,EAAQ,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACvH,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAG5D,OAAO,QAAQ,EAAE,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE/C,qBAAa,gBAAgB;IAGzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFzB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;gBAE7B,MAAM,EAAE,MAAM,EAC/B,cAAc,EAAE,cAAc;IAKhC,OAAO,CAAC,eAAe;IAShB,mBAAmB,CACxB,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EACjC,aAAa,EAAE,cAAc,GAAG,SAAS,EACzC,mBAAmB,EAAE,SAAS,GAC7B,OAAO,CAAC,IAAI,CAAC;IAsCT,6BAA6B,CAAC,gBAAgB,EAAE,MAAM;IAUtD,QAAQ,sBACM,iBAAiB,kBACrB,aAAa,KAC3B,QAAQ,cAAc,GAAG,SAAS,CAAC,CAsDpC;CACH"}
|
|
@@ -15,6 +15,13 @@ class TunnelInteractor {
|
|
|
15
15
|
this.logger = logger;
|
|
16
16
|
this.statsigService = statsigService;
|
|
17
17
|
}
|
|
18
|
+
logTunnelStatus(localPort, tunnelOptions) {
|
|
19
|
+
this.logger.info('');
|
|
20
|
+
this.logger.info(cli_shared_1.Text.tunnel.startedServer(localPort, this.logger.debugEnabled) + '\n');
|
|
21
|
+
if (tunnelOptions.url) {
|
|
22
|
+
this.logger.warn(cli_shared_1.Text.tunnel.selfManagedTunnel(tunnelOptions.url, localPort));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
18
25
|
handleUserExitEvent(stopFunction, bundleMonitor, manifestFileWatcher) {
|
|
19
26
|
return new Promise((resolve, reject) => {
|
|
20
27
|
const rl = readline_1.default.createInterface({
|
|
@@ -74,8 +81,7 @@ class TunnelInteractor {
|
|
|
74
81
|
if (kind === 'runtime') {
|
|
75
82
|
await reloadSandboxes(output, tunnelOptions);
|
|
76
83
|
}
|
|
77
|
-
this.
|
|
78
|
-
this.logger.info(cli_shared_1.Text.tunnel.startedServer(localPort, this.logger.debugEnabled) + '\n');
|
|
84
|
+
this.logTunnelStatus(localPort, tunnelOptions);
|
|
79
85
|
};
|
|
80
86
|
const multiCompiler = new multi_compiler_watcher_1.MultiCompilerWatcher(faasServer, uiServers);
|
|
81
87
|
if (tunnelOptions.verify) {
|
|
@@ -88,8 +94,7 @@ class TunnelInteractor {
|
|
|
88
94
|
onUIChange: { onBuildWillStart, onBuildFinished: onBuildFinished('ui') }
|
|
89
95
|
}, tunnelOptions);
|
|
90
96
|
await reloadSandboxes(output, tunnelOptions);
|
|
91
|
-
this.
|
|
92
|
-
this.logger.info(cli_shared_1.Text.tunnel.startedServer(localPort, this.logger.debugEnabled) + '\n');
|
|
97
|
+
this.logTunnelStatus(localPort, tunnelOptions);
|
|
93
98
|
}
|
|
94
99
|
catch (_) {
|
|
95
100
|
}
|
|
@@ -9,6 +9,7 @@ export interface StartTunnelOptions {
|
|
|
9
9
|
host: string;
|
|
10
10
|
environmentKey: string;
|
|
11
11
|
resourcePortMap: Record<string, number>;
|
|
12
|
+
url?: string;
|
|
12
13
|
}
|
|
13
14
|
export interface StartTunnelResult {
|
|
14
15
|
localPort: number;
|
|
@@ -29,9 +30,13 @@ export declare class StartTunnelCommand {
|
|
|
29
30
|
private tunnelServers;
|
|
30
31
|
private cspReporterServer;
|
|
31
32
|
constructor(getAppConfig: AppConfigProvider, devServer: DevelopmentServer, tunnelFactory: CreateTunnelService, tunnelClient: RegisterTunnelService, functionHost: FunctionChangeWatcher, logger: Logger, configFile: ConfigFile);
|
|
33
|
+
private validateTunnelUrl;
|
|
34
|
+
private validateTunnelPort;
|
|
32
35
|
private stopServices;
|
|
36
|
+
stopDockerComposeStack: (composeFiles?: Record<string, string>) => Promise<void>;
|
|
37
|
+
private deleteDockerComposeFile;
|
|
33
38
|
private startFaaSTunnelServer;
|
|
34
39
|
private startResourceBasedTunnelsServers;
|
|
35
|
-
execute(options: StartTunnelOptions): Promise<StartTunnelResult>;
|
|
40
|
+
execute(options: StartTunnelOptions, containersDockerComposeFiles?: Record<string, string>): Promise<StartTunnelResult>;
|
|
36
41
|
}
|
|
37
42
|
//# sourceMappingURL=start-tunnel-command.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start-tunnel-command.d.ts","sourceRoot":"","sources":["../../src/command/start-tunnel-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,
|
|
1
|
+
{"version":3,"file":"start-tunnel-command.d.ts","sourceRoot":"","sources":["../../src/command/start-tunnel-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EACL,iBAAiB,EACjB,UAAU,EACV,MAAM,EAEN,aAAa,EAId,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EACL,iBAAiB,EAKlB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAKzE,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,UAAU,EAAE,iBAAiB,CAAC;IAC9B,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC/B,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,eAAe,CAAC,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1F;AAQD,qBAAa,kBAAkB;IAK3B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAV7B,OAAO,CAAC,aAAa,CAAmE;IACxF,OAAO,CAAC,iBAAiB,CAAgC;gBAGtC,YAAY,EAAE,iBAAiB,EAC/B,SAAS,EAAE,iBAAiB,EAC5B,aAAa,EAAE,mBAAmB,EAClC,YAAY,EAAE,qBAAqB,EACnC,YAAY,EAAE,qBAAqB,EACnC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU;IAGzC,OAAO,CAAC,iBAAiB;IAiBzB,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,YAAY,CAclB;IAMK,sBAAsB,kBAAyB,OAAO,MAAM,EAAE,MAAM,CAAC,KAAG,QAAQ,IAAI,CAAC,CAyB1F;IAMF,OAAO,CAAC,uBAAuB,CAY7B;IAEF,OAAO,CAAC,qBAAqB,CA+B3B;IAEF,OAAO,CAAC,gCAAgC,CAiCtC;IAEW,OAAO,CAClB,OAAO,EAAE,kBAAkB,EAC3B,4BAA4B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACpD,OAAO,CAAC,iBAAiB,CAAC;CA0D9B"}
|
|
@@ -2,9 +2,18 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.StartTunnelCommand = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
+
const cli_shared_1 = require("@forge/cli-shared");
|
|
5
6
|
const node_cache_1 = tslib_1.__importDefault(require("node-cache"));
|
|
6
7
|
const portfinder_1 = require("portfinder");
|
|
7
8
|
const servers_1 = require("../servers");
|
|
9
|
+
const docker_compose_1 = require("docker-compose");
|
|
10
|
+
const path = tslib_1.__importStar(require("path"));
|
|
11
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
12
|
+
class InvalidTunnelConfigError extends cli_shared_1.UserError {
|
|
13
|
+
constructor(message) {
|
|
14
|
+
super(message);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
8
17
|
class StartTunnelCommand {
|
|
9
18
|
getAppConfig;
|
|
10
19
|
devServer;
|
|
@@ -24,26 +33,87 @@ class StartTunnelCommand {
|
|
|
24
33
|
this.logger = logger;
|
|
25
34
|
this.configFile = configFile;
|
|
26
35
|
}
|
|
27
|
-
|
|
36
|
+
validateTunnelUrl(tunnelUrl) {
|
|
37
|
+
if (!tunnelUrl) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
let parsedUrl;
|
|
41
|
+
try {
|
|
42
|
+
parsedUrl = new URL(tunnelUrl);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
throw new InvalidTunnelConfigError(cli_shared_1.Text.tunnel.error.invalidSelfManagedTunnelUrl(tunnelUrl));
|
|
46
|
+
}
|
|
47
|
+
if (parsedUrl.protocol !== 'https:') {
|
|
48
|
+
throw new InvalidTunnelConfigError(cli_shared_1.Text.tunnel.error.invalidSelfManagedTunnelUrlProtocol(parsedUrl.protocol));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
validateTunnelPort(port) {
|
|
52
|
+
if (port === undefined) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (port < 1 || port > 65535) {
|
|
56
|
+
throw new InvalidTunnelConfigError(cli_shared_1.Text.tunnel.error.invalidSelfManagedTunnelPort(port.toString()));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
stopServices = async (appId, environmentKey, containersDockerComposeFiles) => {
|
|
28
60
|
await Promise.all([
|
|
29
61
|
this.functionHost.stopWatching(),
|
|
30
62
|
this.tunnelClient.unregisterTunnels(appId, environmentKey),
|
|
31
63
|
this.tunnelFactory.closeTunnel(),
|
|
32
64
|
this.devServer.stop(),
|
|
33
65
|
...Object.values(this.tunnelServers).map((server) => server.stop()),
|
|
34
|
-
this.cspReporterServer?.stop()
|
|
66
|
+
this.cspReporterServer?.stop(),
|
|
67
|
+
this.stopDockerComposeStack(containersDockerComposeFiles)
|
|
35
68
|
]);
|
|
36
69
|
};
|
|
37
|
-
|
|
70
|
+
stopDockerComposeStack = async (composeFiles) => {
|
|
71
|
+
if (!composeFiles || Object.keys(composeFiles).length === 0)
|
|
72
|
+
return;
|
|
73
|
+
const { services } = await this.configFile.readConfig();
|
|
74
|
+
const serviceWithTunnelConfigExists = services?.some((service) => service.containers?.some((container) => {
|
|
75
|
+
return !!container.tunnel;
|
|
76
|
+
}));
|
|
77
|
+
if (!services || services.length === 0 || !serviceWithTunnelConfigExists)
|
|
78
|
+
return;
|
|
79
|
+
await Promise.all(Object.entries(composeFiles).map(async ([serviceKey, file]) => {
|
|
80
|
+
try {
|
|
81
|
+
await (0, docker_compose_1.downAll)({ cwd: '.', log: true, config: file, composeOptions: [`-p${serviceKey}`] });
|
|
82
|
+
await this.deleteDockerComposeFile(file);
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
throw new Error(cli_shared_1.Text.tunnel.unableToStopDockerComposeStack(serviceKey, err.message ?? 'Unknown Error Occurred.'));
|
|
86
|
+
}
|
|
87
|
+
}));
|
|
88
|
+
};
|
|
89
|
+
deleteDockerComposeFile = async (composeFile) => {
|
|
90
|
+
if (fs.existsSync(composeFile)) {
|
|
91
|
+
fs.unlinkSync(composeFile);
|
|
92
|
+
}
|
|
93
|
+
const hiddenDir = path.join(process.cwd(), '.services');
|
|
94
|
+
if (fs.existsSync(hiddenDir)) {
|
|
95
|
+
const files = fs.readdirSync(hiddenDir);
|
|
96
|
+
if (files.length == 0) {
|
|
97
|
+
fs.rmdirSync(hiddenDir);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
startFaaSTunnelServer = async ({ port, appId, environmentKey, tunnelUrl }) => {
|
|
38
102
|
const { permissions = {}, remotes = [] } = await this.configFile.readConfig();
|
|
39
103
|
const serverInfo = await this.devServer.start(port, permissions, remotes);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
104
|
+
let faasTunnelUrl;
|
|
105
|
+
if (tunnelUrl) {
|
|
106
|
+
faasTunnelUrl = new URL(tunnelUrl);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
const { id, token, url } = await this.tunnelClient.setupTunnel(appId, environmentKey);
|
|
110
|
+
faasTunnelUrl = await this.tunnelFactory.establishTunnel({
|
|
111
|
+
port: serverInfo.port,
|
|
112
|
+
id,
|
|
113
|
+
token,
|
|
114
|
+
tunnelUrl: url
|
|
115
|
+
});
|
|
116
|
+
}
|
|
47
117
|
return {
|
|
48
118
|
...serverInfo,
|
|
49
119
|
tunnelUrl: faasTunnelUrl
|
|
@@ -73,15 +143,18 @@ class StartTunnelCommand {
|
|
|
73
143
|
return this.tunnelServers[key].start();
|
|
74
144
|
}));
|
|
75
145
|
};
|
|
76
|
-
async execute(options) {
|
|
146
|
+
async execute(options, containersDockerComposeFiles) {
|
|
77
147
|
const { id: appId } = await this.getAppConfig();
|
|
78
|
-
const { port, environmentKey } = options;
|
|
148
|
+
const { port, environmentKey, url } = options;
|
|
149
|
+
this.validateTunnelUrl(url);
|
|
150
|
+
this.validateTunnelPort(port);
|
|
79
151
|
const allResources = await this.configFile.getResources();
|
|
80
152
|
try {
|
|
81
153
|
const faasTunnelServer = await this.startFaaSTunnelServer({
|
|
82
154
|
port,
|
|
83
155
|
appId,
|
|
84
|
-
environmentKey
|
|
156
|
+
environmentKey,
|
|
157
|
+
tunnelUrl: url
|
|
85
158
|
});
|
|
86
159
|
const customUITunnelsServers = await this.startResourceBasedTunnelsServers(allResources, options);
|
|
87
160
|
const tunnelDefinitions = {
|
|
@@ -92,7 +165,11 @@ class StartTunnelCommand {
|
|
|
92
165
|
}))
|
|
93
166
|
};
|
|
94
167
|
await this.tunnelClient.registerTunnels(appId, environmentKey, tunnelDefinitions);
|
|
95
|
-
const stopFunction = async () => this.stopServices(appId, environmentKey);
|
|
168
|
+
const stopFunction = async () => this.stopServices(appId, environmentKey, containersDockerComposeFiles);
|
|
169
|
+
this.devServer.setErrorHandler?.(async (error) => {
|
|
170
|
+
await this.stopServices(appId, environmentKey, containersDockerComposeFiles);
|
|
171
|
+
await (0, cli_shared_1.exitOnError)(this.logger, error);
|
|
172
|
+
});
|
|
96
173
|
const reloadSandboxes = async (bundledCode, tunnelOptions) => {
|
|
97
174
|
await this.functionHost.stopWatching();
|
|
98
175
|
await this.functionHost.startWatching(bundledCode, tunnelOptions);
|
|
@@ -108,7 +185,7 @@ class StartTunnelCommand {
|
|
|
108
185
|
}
|
|
109
186
|
catch (e) {
|
|
110
187
|
try {
|
|
111
|
-
await this.stopServices(appId, environmentKey);
|
|
188
|
+
await this.stopServices(appId, environmentKey, containersDockerComposeFiles);
|
|
112
189
|
}
|
|
113
190
|
catch { }
|
|
114
191
|
throw e;
|
|
@@ -4,16 +4,19 @@ import http from 'http';
|
|
|
4
4
|
import { URL } from 'url';
|
|
5
5
|
import express from 'express';
|
|
6
6
|
import { BundlerWatch, BundlerOutput } from '@forge/bundler';
|
|
7
|
-
import { ConfigFile, FileSystemReader, I18nResourceBundlingService, Logger, StatsigService, TunnelOptions } from '@forge/cli-shared';
|
|
7
|
+
import { AppEnvironmentsGraphqlClient, ConfigFile, FileSystemReader, I18nResourceBundlingService, Logger, StatsigService, TunnelOptions, UserError } from '@forge/cli-shared';
|
|
8
8
|
import { ExternalRequestBody } from '@forge/runtime';
|
|
9
9
|
import { Permissions, Remotes } from '@forge/manifest';
|
|
10
10
|
export interface ProxyServerError extends Error {
|
|
11
11
|
code?: string;
|
|
12
12
|
}
|
|
13
|
+
export declare class TunnelInvocationValidationError extends UserError {
|
|
14
|
+
}
|
|
13
15
|
export interface DevelopmentServer {
|
|
14
16
|
start(port?: number, permissions?: Permissions, remotes?: Remotes): Promise<StartDevServerResult>;
|
|
15
17
|
stop(): Promise<void>;
|
|
16
18
|
compileAndWatch(hooks: BundlerWatch, tunnelOptions?: TunnelOptions): Promise<BundlerOutput>;
|
|
19
|
+
setErrorHandler?: (handler: (err: Error) => Promise<void>) => void;
|
|
17
20
|
}
|
|
18
21
|
export interface StartDevServerResult {
|
|
19
22
|
port: number;
|
|
@@ -32,14 +35,22 @@ export declare class LocalDevelopmentServer implements DevelopmentServer {
|
|
|
32
35
|
private readonly fileSystemReader;
|
|
33
36
|
private readonly statsigService;
|
|
34
37
|
private readonly i18nResourceBundlingService;
|
|
38
|
+
private readonly appEnvironmentClient;
|
|
35
39
|
private app;
|
|
36
40
|
private httpServer;
|
|
37
|
-
|
|
41
|
+
private tunnelOptions;
|
|
42
|
+
private environmentId;
|
|
43
|
+
private appId;
|
|
44
|
+
onFatalError?: (err: Error) => Promise<void>;
|
|
45
|
+
constructor(invocationService: InvocationService, logger: Logger, configFile: ConfigFile, fileSystemReader: FileSystemReader, statsigService: StatsigService, i18nResourceBundlingService: I18nResourceBundlingService, appEnvironmentClient: AppEnvironmentsGraphqlClient);
|
|
38
46
|
start(port?: number, _permissions?: Permissions, _remotes?: Remotes): Promise<StartDevServerResult>;
|
|
39
47
|
stop(): Promise<void>;
|
|
40
48
|
compileAndWatch(watch: BundlerWatch, tunnelOptions?: TunnelOptions): Promise<BundlerOutput>;
|
|
41
49
|
getApp(): express.Application;
|
|
50
|
+
setErrorHandler(handler: (err: Error) => Promise<void>): void;
|
|
42
51
|
handleInvocation: express.Handler;
|
|
43
52
|
configureProxyMiddleware: express.Handler;
|
|
53
|
+
validateApp(request: ExternalRequestBody): Promise<void>;
|
|
54
|
+
private setEnvironmentAndAppId;
|
|
44
55
|
}
|
|
45
56
|
//# sourceMappingURL=dev-server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../../src/servers/dev-server.ts"],"names":[],"mappings":";;AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,EAKL,YAAY,EACZ,aAAa,EACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,UAAU,EAEV,gBAAgB,EAChB,2BAA2B,EAC3B,MAAM,EACN,cAAc,EAEd,aAAa,
|
|
1
|
+
{"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../../src/servers/dev-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LocalDevelopmentServer = exports.stopServer = void 0;
|
|
3
|
+
exports.LocalDevelopmentServer = exports.stopServer = exports.TunnelInvocationValidationError = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const express_1 = tslib_1.__importDefault(require("express"));
|
|
6
6
|
const bundler_1 = require("@forge/bundler");
|
|
@@ -8,6 +8,10 @@ const cli_shared_1 = require("@forge/cli-shared");
|
|
|
8
8
|
const runtime_1 = require("@forge/runtime");
|
|
9
9
|
const util_1 = require("../util");
|
|
10
10
|
const http_proxy_middleware_1 = require("http-proxy-middleware");
|
|
11
|
+
const ari_1 = require("@forge/util/packages/ari");
|
|
12
|
+
class TunnelInvocationValidationError extends cli_shared_1.UserError {
|
|
13
|
+
}
|
|
14
|
+
exports.TunnelInvocationValidationError = TunnelInvocationValidationError;
|
|
11
15
|
async function stopServer(server) {
|
|
12
16
|
if (!server) {
|
|
13
17
|
return;
|
|
@@ -24,15 +28,21 @@ class LocalDevelopmentServer {
|
|
|
24
28
|
fileSystemReader;
|
|
25
29
|
statsigService;
|
|
26
30
|
i18nResourceBundlingService;
|
|
31
|
+
appEnvironmentClient;
|
|
27
32
|
app;
|
|
28
33
|
httpServer;
|
|
29
|
-
|
|
34
|
+
tunnelOptions;
|
|
35
|
+
environmentId;
|
|
36
|
+
appId;
|
|
37
|
+
onFatalError;
|
|
38
|
+
constructor(invocationService, logger, configFile, fileSystemReader, statsigService, i18nResourceBundlingService, appEnvironmentClient) {
|
|
30
39
|
this.invocationService = invocationService;
|
|
31
40
|
this.logger = logger;
|
|
32
41
|
this.configFile = configFile;
|
|
33
42
|
this.fileSystemReader = fileSystemReader;
|
|
34
43
|
this.statsigService = statsigService;
|
|
35
44
|
this.i18nResourceBundlingService = i18nResourceBundlingService;
|
|
45
|
+
this.appEnvironmentClient = appEnvironmentClient;
|
|
36
46
|
this.app = (0, express_1.default)();
|
|
37
47
|
this.app.disable('x-powered-by');
|
|
38
48
|
this.app.disable('etag');
|
|
@@ -53,7 +63,8 @@ class LocalDevelopmentServer {
|
|
|
53
63
|
await stopServer(this.httpServer);
|
|
54
64
|
}
|
|
55
65
|
async compileAndWatch(watch, tunnelOptions = cli_shared_1.defaultNoDebugTunnelOptions) {
|
|
56
|
-
|
|
66
|
+
this.tunnelOptions = tunnelOptions;
|
|
67
|
+
const watchMode = this.tunnelOptions.debug ? 'debug' : 'watch';
|
|
57
68
|
const handlers = await this.configFile.getAppHandlers();
|
|
58
69
|
const entryPoints = (0, bundler_1.getEntryPoints)(handlers);
|
|
59
70
|
if (!entryPoints.length) {
|
|
@@ -74,6 +85,9 @@ class LocalDevelopmentServer {
|
|
|
74
85
|
getApp() {
|
|
75
86
|
return this.app;
|
|
76
87
|
}
|
|
88
|
+
setErrorHandler(handler) {
|
|
89
|
+
this.onFatalError = handler;
|
|
90
|
+
}
|
|
77
91
|
handleInvocation = async (req, res) => {
|
|
78
92
|
const request = req.body;
|
|
79
93
|
const envVars = (0, runtime_1.getUserVars)();
|
|
@@ -82,6 +96,7 @@ class LocalDevelopmentServer {
|
|
|
82
96
|
envVars.push(item);
|
|
83
97
|
}
|
|
84
98
|
});
|
|
99
|
+
await this.validateApp(request);
|
|
85
100
|
request.variables = [...envVars].sort((a, b) => a.key.localeCompare(b.key));
|
|
86
101
|
try {
|
|
87
102
|
const invokeResults = await this.invocationService.invoke(request.handler, request);
|
|
@@ -130,5 +145,33 @@ class LocalDevelopmentServer {
|
|
|
130
145
|
}
|
|
131
146
|
})(req, res, next);
|
|
132
147
|
};
|
|
148
|
+
async validateApp(request) {
|
|
149
|
+
try {
|
|
150
|
+
if (!(await this.statsigService.isSelfManagedTunnelEnabled())) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
await this.setEnvironmentAndAppId();
|
|
154
|
+
const requestAppId = request?._meta?.appContext?.appId;
|
|
155
|
+
if (requestAppId && requestAppId !== this.appId) {
|
|
156
|
+
throw new TunnelInvocationValidationError(cli_shared_1.Text.tunnel.error.invalidInvokeRequestForAppId(requestAppId));
|
|
157
|
+
}
|
|
158
|
+
const requestEnvId = request?._meta?.appContext?.environmentId;
|
|
159
|
+
if (requestEnvId && requestEnvId !== this.environmentId) {
|
|
160
|
+
throw new TunnelInvocationValidationError(cli_shared_1.Text.tunnel.error.invalidInvokeRequestForAppEnv(requestEnvId));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
await this.onFatalError?.(error);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async setEnvironmentAndAppId() {
|
|
168
|
+
if (this.environmentId && this.appId) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const config = await this.configFile.readConfig();
|
|
172
|
+
this.appId = ari_1.EcosystemAppAri.parse(config.app.id).appId;
|
|
173
|
+
const environmentKey = this.tunnelOptions?.environment || 'default';
|
|
174
|
+
this.environmentId = await this.appEnvironmentClient.getAppEnvironmentId(config.app.id, environmentKey);
|
|
175
|
+
}
|
|
133
176
|
}
|
|
134
177
|
exports.LocalDevelopmentServer = LocalDevelopmentServer;
|
|
@@ -35,7 +35,7 @@ export declare abstract class ResourceTunnelServer {
|
|
|
35
35
|
scripts?: import("@forge/manifest").Scripts1 | undefined;
|
|
36
36
|
styles?: import("@forge/manifest").Styles1 | undefined;
|
|
37
37
|
};
|
|
38
|
-
|
|
38
|
+
enforcement?: "app-managed" | undefined;
|
|
39
39
|
configurable?: {
|
|
40
40
|
[k: string]: unknown;
|
|
41
41
|
enabled: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forge/tunnel",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.0-next.10",
|
|
4
4
|
"description": "Tunnel functionality for Forge CLI",
|
|
5
5
|
"author": "Atlassian",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
@@ -11,13 +11,15 @@
|
|
|
11
11
|
"compile": "tsc -b -v"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@forge/bundler": "6.1.9-next.
|
|
15
|
-
"@forge/cli-shared": "8.
|
|
16
|
-
"@forge/csp": "5.1.1-next.0
|
|
17
|
-
"@forge/runtime": "6.0.
|
|
14
|
+
"@forge/bundler": "6.1.9-next.10",
|
|
15
|
+
"@forge/cli-shared": "8.8.0-next.8",
|
|
16
|
+
"@forge/csp": "5.1.1-next.0",
|
|
17
|
+
"@forge/runtime": "6.1.0-next.0",
|
|
18
|
+
"@forge/util": "^2.0.0",
|
|
18
19
|
"cheerio": "^1.1.0",
|
|
19
20
|
"chokidar": "^3.6.0",
|
|
20
21
|
"cloudflared": "^0.7.0",
|
|
22
|
+
"docker-compose": "^1.3.0",
|
|
21
23
|
"express": "^4.18.3",
|
|
22
24
|
"express-intercept": "^1.1.1",
|
|
23
25
|
"http-proxy-middleware": "^2.0.9",
|
|
@@ -31,7 +33,7 @@
|
|
|
31
33
|
},
|
|
32
34
|
"devDependencies": {
|
|
33
35
|
"@atlassian/xen-test-util": "^4.2.0",
|
|
34
|
-
"@forge/manifest": "10.7.0-next.
|
|
36
|
+
"@forge/manifest": "10.7.0-next.4",
|
|
35
37
|
"@types/express": "^4.17.21",
|
|
36
38
|
"@types/jest": "^29.5.14",
|
|
37
39
|
"@types/node": "20.19.1",
|