first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import { GenericContainer, Wait } from 'testcontainers';
|
||||
|
||||
import { createSilentLogConsumer } from '../helpers/utils';
|
||||
import { TEST_CONTAINER_IMAGES } from '../test-containers';
|
||||
import { EXTERNAL_HOST, type Service, type ServiceResult, type StartContext } from './types';
|
||||
|
||||
export interface CloudflaredMeta {
|
||||
publicUrl: string;
|
||||
proxyHops: number;
|
||||
}
|
||||
|
||||
export type CloudflaredResult = ServiceResult<CloudflaredMeta>;
|
||||
|
||||
const METRICS_PORT = 2000;
|
||||
|
||||
function getTunnelTarget(ctx: StartContext): string {
|
||||
if (ctx.external) {
|
||||
return `${EXTERNAL_HOST}:5678`;
|
||||
}
|
||||
if (ctx.needsLoadBalancer) {
|
||||
return `${ctx.projectName}-caddy-lb:80`;
|
||||
}
|
||||
return `${ctx.projectName}-n8n:5678`;
|
||||
}
|
||||
|
||||
export const cloudflared: Service<CloudflaredResult> = {
|
||||
description: 'Cloudflare Tunnel',
|
||||
dependsOn: ['loadBalancer'],
|
||||
|
||||
shouldStart: (ctx) => ctx.config.services?.includes('cloudflared') ?? false,
|
||||
|
||||
getOptions(ctx) {
|
||||
const proxyHops = ctx.needsLoadBalancer ? 2 : 1;
|
||||
return { tunnelTarget: getTunnelTarget(ctx), proxyHops };
|
||||
},
|
||||
|
||||
env(result) {
|
||||
return {
|
||||
WEBHOOK_URL: result.meta.publicUrl,
|
||||
N8N_PROXY_HOPS: String(result.meta.proxyHops),
|
||||
};
|
||||
},
|
||||
|
||||
async start(
|
||||
network,
|
||||
projectName,
|
||||
config?: unknown,
|
||||
ctx?: StartContext,
|
||||
): Promise<CloudflaredResult> {
|
||||
const { tunnelTarget, proxyHops } = config as { tunnelTarget: string; proxyHops: number };
|
||||
const { consumer, throwWithLogs } = createSilentLogConsumer();
|
||||
|
||||
try {
|
||||
let builder = new GenericContainer(TEST_CONTAINER_IMAGES.cloudflared)
|
||||
.withNetwork(network)
|
||||
.withNetworkAliases('cloudflared')
|
||||
.withName(`${projectName}-cloudflared`)
|
||||
.withExposedPorts(METRICS_PORT)
|
||||
.withCommand([
|
||||
'tunnel',
|
||||
'--url',
|
||||
`http://${tunnelTarget}`,
|
||||
'--metrics',
|
||||
`0.0.0.0:${METRICS_PORT}`,
|
||||
'--no-autoupdate',
|
||||
])
|
||||
.withWaitStrategy(Wait.forHttp('/quicktunnel', METRICS_PORT).forStatusCode(200))
|
||||
.withLabels({
|
||||
'com.docker.compose.project': projectName,
|
||||
'com.docker.compose.service': 'cloudflared',
|
||||
})
|
||||
.withReuse()
|
||||
.withLogConsumer(consumer);
|
||||
|
||||
// On Linux, host.docker.internal is not available without explicit mapping
|
||||
if (ctx?.external) {
|
||||
builder = builder.withExtraHosts([{ host: EXTERNAL_HOST, ipAddress: 'host-gateway' }]);
|
||||
}
|
||||
|
||||
const container = await builder.start();
|
||||
|
||||
const hostPort = container.getMappedPort(METRICS_PORT);
|
||||
const response = await fetch(`http://${container.getHost()}:${hostPort}/quicktunnel`);
|
||||
const data = (await response.json()) as { hostname: string };
|
||||
const publicUrl = `https://${data.hostname}`;
|
||||
|
||||
return {
|
||||
container,
|
||||
meta: { publicUrl, proxyHops },
|
||||
};
|
||||
} catch (error) {
|
||||
return throwWithLogs(error);
|
||||
}
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user