| 1 | import * as Effect from "effect/Effect"; |
| 2 | import * as Binding from "../../Binding.ts"; |
| 3 | import type { Worker } from "../Workers/Worker.ts"; |
| 4 | import type { CloudflareEnvironment } from "../CloudflareEnvironment.ts"; |
| 5 | import { type TunnelAuth } from "./TunnelBinding.ts"; |
| 6 | import { readClient, type ReadTunnelClient } from "./ReadTunnel.ts"; |
| 7 | import { writeClient, type WriteTunnelClient } from "./WriteTunnel.ts"; |
| 8 | |
| 9 | /** |
| 10 | * Binding that lets a Worker perform the full Cloudflare Tunnel CRUD surface at |
| 11 | * runtime. |
| 12 | * |
| 13 | * Creates a scoped {@link AccountApiToken} with both the `Cloudflare Tunnel |
| 14 | * Read` and `Cloudflare Tunnel Write` permissions and binds its outputs into |
| 15 | * the Worker (the token value as a `secret_text` binding) so runtime code can |
| 16 | * authenticate. |
| 17 | * |
| 18 | * @remarks |
| 19 | * |
| 20 | * `ReadWriteTunnel` is a single identifier that is simultaneously the binding's |
| 21 | * Context tag, its type, and the callable — |
| 22 | * `yield* Cloudflare.Tunnel.ReadWriteTunnel()`. |
| 23 | * |
| 24 | * ### Managing tunnels at runtime |
| 25 | * **Example:** Create, configure, and delete a tunnel from a request handler |
| 26 | * ```typescript |
| 27 | * // init |
| 28 | * const tunnels = yield* Cloudflare.Tunnel.ReadWriteTunnel(); |
| 29 | * |
| 30 | * return { |
| 31 | * fetch: Effect.gen(function* () { |
| 32 | * const tunnel = yield* tunnels.create({ name: "on-demand-tunnel" }); |
| 33 | * yield* tunnels.putConfiguration(tunnel.id!, { |
| 34 | * ingress: [ |
| 35 | * { hostname: "app.example.com", service: "http://localhost:3000" }, |
| 36 | * { service: "http_status:404" }, |
| 37 | * ], |
| 38 | * }); |
| 39 | * const token = yield* tunnels.getToken(tunnel.id!); |
| 40 | * return HttpServerResponse.json({ id: tunnel.id, token }); |
| 41 | * }), |
| 42 | * }; |
| 43 | * ``` |
| 44 | * |
| 45 | * ### Runtime Layer |
| 46 | * Provide {@link ReadWriteTunnelBinding} in the Worker's runtime layer. |
| 47 | * ```typescript |
| 48 | * Effect.provide(Cloudflare.Tunnel.ReadWriteTunnelBinding) |
| 49 | * ``` |
| 50 | * |
| 51 | * @binding |
| 52 | * @product Tunnels |
| 53 | * @category Cloudflare One (Zero Trust) |
| 54 | */ |
| 55 | export interface ReadWriteTunnel extends Binding.Service< |
| 56 | ReadWriteTunnel, |
| 57 | "Cloudflare.Tunnel.ReadWriteTunnel", |
| 58 | () => Effect.Effect< |
| 59 | ReadWriteTunnelClient, |
| 60 | never, |
| 61 | Worker | CloudflareEnvironment |
| 62 | > |
| 63 | > {} |
| 64 | |
| 65 | export const ReadWriteTunnel = Binding.Service<ReadWriteTunnel>( |
| 66 | "Cloudflare.Tunnel.ReadWriteTunnel", |
| 67 | ); |
| 68 | |
| 69 | /** Combined read + write tunnel operations. */ |
| 70 | export interface ReadWriteTunnelClient |
| 71 | extends ReadTunnelClient, WriteTunnelClient {} |
| 72 | |
| 73 | /** Build the combined read + write client over an injectable {@link TunnelAuth}. */ |
| 74 | export const readWriteClient = (auth: TunnelAuth): ReadWriteTunnelClient => ({ |
| 75 | ...readClient(auth), |
| 76 | ...writeClient(auth), |
| 77 | }); |