1import * as Cloudflare from "@/Cloudflare";
2import * as Effect from "effect/Effect";
3import * as Redacted from "effect/Redacted";
4import * as HttpClientRequest from "effect/http/HttpClientRequest";
5import { HttpServerRequest } from "effect/http/HttpServerRequest";
6
7/**
8 * A sibling resource whose output is threaded into the container's `env`,
9 * proving that a pre-built image (no Effect bundling, no `.make()`) receives
10 * environment variables that resolve from other resources in the stack.
11 */
12export const EnvBucket = Cloudflare.R2.Bucket("RemoteContainerEnvBucket", {
13 forceDestroy: true,
14});
15
16/** Plain-text env value the container is expected to echo back verbatim. */
17export const DEMO_PLAIN = "hello-from-env";
18/** `Redacted` env value — encrypted in state, plain inside the container. */
19export const DEMO_SECRET = "sh-hh-its-a-secret";
20
21export class RemoteContainer extends Cloudflare.Container<RemoteContainer>()(
22 "RemoteContainer",
23 Effect.gen(function* () {
24 const bucket = yield* EnvBucket;
25 return {
26 image: "mendhak/http-https-echo:latest",
27 vcpu: 1,
28 memoryMib: 4096,
29 observability: { logs: { enabled: true } },
30 env: {
31 // Tells the echo image to include `process.env` in its JSON response.
32 ECHO_INCLUDE_ENV_VARS: "1",
33 DEMO_PLAIN,
34 DEMO_SECRET: Redacted.make(DEMO_SECRET),
35 DEMO_BUCKET: bucket.bucketName,
36 },
37 };
38 }),
39) {}
40
41/**
42 * Durable Object that binds and starts the {@link RemoteContainer} and
43 * proxies an HTTP request to the echo server running on port 8080 inside it.
44 */
45export class RemoteContainerObject extends Cloudflare.DurableObject<RemoteContainerObject>()(
46 "RemoteContainerObject",
47 Effect.gen(function* () {
48 const container = yield* RemoteContainer;
49
50 return Effect.gen(function* () {
51 const { fetch } = yield* container.getTcpPort(8080);
52
53 return {
54 hello: () =>
55 Effect.gen(function* () {
56 const response = yield* fetch(
57 HttpClientRequest.get("http://container/"),
58 );
59 return yield* response.text;
60 }),
61 // The proxy pattern from #1334: forward the incoming request to the
62 // container verbatim. In production the incoming web Request carries
63 // an https:// URL, which workerd's container ports reject — the
64 // runtime must downgrade the scheme on the container hop.
65 fetch: Effect.gen(function* () {
66 const request = yield* HttpServerRequest;
67 return yield* fetch(request);
68 }),
69 };
70 });
71 }).pipe(
72 Effect.provide(
73 Cloudflare.Containers.layer(RemoteContainer, {
74 enableInternet: true,
75 }),
76 ),
77 ),
78) {}