|
| 1 | +# LLM Instructions: Create an “edge” repo with a Cloudflare Worker reverse-proxy for `caza.la/party` |
| 2 | + |
| 3 | +You are implementing a **new GitHub repository** (an “edge” repo) that deploys a **Cloudflare Worker** to the `caza.la` Cloudflare zone. |
| 4 | + |
| 5 | +The Worker must **serve the Party playground at `https://caza.la/party`** by **reverse-proxying** to an upstream origin that already serves the playground: |
| 6 | + |
| 7 | +- **Upstream origin (MUST)**: `https://party-assets.caza.la` |
| 8 | + - This is a **second Cloudflare Pages deployment** of the Party playground that is built with Vite `base="/party/"`. |
| 9 | + - `party.caza.la` remains the “normal” root build and is **not** the Worker upstream for `caza.la/party`. |
| 10 | + |
| 11 | +## Non‑negotiable requirements |
| 12 | + |
| 13 | +- **No redirect to `*.pages.dev`** at any point. |
| 14 | + - The browser URL must remain **`caza.la/party...`** while using the playground. |
| 15 | +- Root website must remain at **`https://caza.la/`** (served by the separate `caza.la` repo / its own Cloudflare Pages project). |
| 16 | +- The Worker must only handle the `/party` subtree (route-scoped), and must not affect other paths. |
| 17 | + |
| 18 | +## What you are building |
| 19 | + |
| 20 | +### Behavior summary |
| 21 | + |
| 22 | +- Requests to `https://caza.la/party` should 308 to `https://caza.la/party/` (same-host, same-scheme). |
| 23 | + This is only a trailing-slash normalization and still satisfies “no redirect to pages.dev”. |
| 24 | +- Requests to `https://caza.la/party/*` should be proxied to the upstream by **stripping** the `/party` prefix: |
| 25 | + - `GET /party/` → upstream `GET /` |
| 26 | + - `GET /party/assets/x.js` → upstream `GET /assets/x.js` |
| 27 | + - `GET /party/some/spa/route` → upstream `GET /some/spa/route` |
| 28 | +- The Worker must preserve: |
| 29 | + - method (GET/POST/etc) |
| 30 | + - query string |
| 31 | + - request body |
| 32 | + - most headers (with a few safe adjustments described below) |
| 33 | +- The Worker must return upstream responses mostly as-is (status, headers, body). |
| 34 | + |
| 35 | +### Important dependency (already decided) |
| 36 | + |
| 37 | +This Worker is **route-scoped to `caza.la/party*`**, so the app must request its assets/chunks under `/party/...` on `caza.la`. |
| 38 | + |
| 39 | +That is why the upstream is **`party-assets.caza.la`**, which is built with: |
| 40 | + |
| 41 | +- Vite `base: "/party/"` |
| 42 | +- Cloudflare Pages SPA fallback (`/* /index.html 200`) |
| 43 | + |
| 44 | +## Repo deliverables |
| 45 | + |
| 46 | +Create a repo with: |
| 47 | + |
| 48 | +- `src/index.ts` (or `src/worker.ts`): Worker implementation (TypeScript) |
| 49 | +- `wrangler.toml`: Wrangler config |
| 50 | +- `package.json`: scripts for typecheck + deploy |
| 51 | +- `tsconfig.json` |
| 52 | +- `README.md`: setup + deploy instructions |
| 53 | +- `.github/workflows/deploy.yml`: CI deploy on push to `main` |
| 54 | + |
| 55 | +Prefer minimal dependencies (no frameworks needed). |
| 56 | + |
| 57 | +## Cloudflare + Wrangler configuration |
| 58 | + |
| 59 | +### Worker name |
| 60 | + |
| 61 | +Use a clear name, e.g.: |
| 62 | + |
| 63 | +- `cazala-edge-party` |
| 64 | + |
| 65 | +### Route |
| 66 | + |
| 67 | +The Worker must be attached to the `caza.la` zone with **route**: |
| 68 | + |
| 69 | +- `caza.la/party*` |
| 70 | + |
| 71 | +### Configuration style (recommended) |
| 72 | + |
| 73 | +Use environment variables so upstream can be changed without code changes: |
| 74 | + |
| 75 | +- `UPSTREAM_ORIGIN = "https://party-assets.caza.la"` |
| 76 | + |
| 77 | +In `wrangler.toml`: |
| 78 | + |
| 79 | +- define `vars.UPSTREAM_ORIGIN` |
| 80 | +- set a pinned `compatibility_date` |
| 81 | + |
| 82 | +## Worker implementation details (must follow) |
| 83 | + |
| 84 | +### 1) Routing rules |
| 85 | + |
| 86 | +- If path is exactly `/party`, redirect to `/party/` (308). |
| 87 | +- If path does not start with `/party/`, return 404 (defensive; route should already constrain it). |
| 88 | +- Otherwise proxy: |
| 89 | + - Strip the `/party` prefix from `pathname` |
| 90 | + - Use upstream host from `UPSTREAM_ORIGIN` |
| 91 | + - Keep query string |
| 92 | + |
| 93 | +### 2) Headers handling |
| 94 | + |
| 95 | +- Forward most request headers, but: |
| 96 | + - Remove `Host` (it must match the upstream host) |
| 97 | + - Consider removing `Accept-Encoding` (Cloudflare will manage compression; leaving it is usually okay, but removing avoids rare edge-cases) |
| 98 | + - Add a helpful header for debugging: |
| 99 | + - `x-edge-proxy: cazala-edge-party` |
| 100 | + |
| 101 | +### 3) Caching (keep it simple) |
| 102 | + |
| 103 | +Default: do not implement custom caching at first. |
| 104 | + |
| 105 | +Optionally: |
| 106 | + |
| 107 | +- For requests that look like hashed assets (e.g. `/assets/*.js`, `/assets/*.css`), you may set `Cache-Control: public, max-age=31536000, immutable` **only if** upstream doesn’t already do this correctly. |
| 108 | +- For `index.html` and SPA routes, keep caching conservative (or unchanged). |
| 109 | + |
| 110 | +### 4) Content rewriting (do NOT do unless necessary) |
| 111 | + |
| 112 | +Do not attempt HTML rewriting unless tests show it’s needed. |
| 113 | +The primary mechanism should be path stripping + upstream build base configuration. |
| 114 | + |
| 115 | +### 5) Error handling |
| 116 | + |
| 117 | +- If upstream fetch fails, return a 502 with a short message. |
| 118 | +- Never leak secrets. |
| 119 | + |
| 120 | +## Acceptance tests (must pass) |
| 121 | + |
| 122 | +Assume the Worker is deployed and route is active. |
| 123 | + |
| 124 | +- Visiting `https://caza.la/party` loads the playground and ends at `https://caza.la/party/` (same host). |
| 125 | +- Hard refresh on a nested route works (SPA): |
| 126 | + - Open `https://caza.la/party/some/deep/route` |
| 127 | + - Refresh page → it still loads (no 404). |
| 128 | +- Static assets load from `/party/assets/...` (verify in DevTools network). |
| 129 | +- `https://caza.la/` and other non-`/party` routes continue to work and are not served by this Worker. |
| 130 | +- There is **no** redirect to `party-assets.caza.la`, `party.caza.la`, or any `*.pages.dev` URL. |
| 131 | + |
| 132 | +## GitHub Actions deploy |
| 133 | + |
| 134 | +Implement `.github/workflows/deploy.yml` that: |
| 135 | + |
| 136 | +- runs on push to `main` |
| 137 | +- installs deps |
| 138 | +- typechecks (or `tsc --noEmit`) |
| 139 | +- deploys with `wrangler deploy` |
| 140 | + |
| 141 | +Use secrets: |
| 142 | + |
| 143 | +- `CLOUDFLARE_API_TOKEN` |
| 144 | +- `CLOUDFLARE_ACCOUNT_ID` |
| 145 | + |
| 146 | +Do NOT require interactive login. |
| 147 | + |
| 148 | +## Suggested file contents (outline) |
| 149 | + |
| 150 | +### `package.json` |
| 151 | + |
| 152 | +- scripts: |
| 153 | + - `typecheck`: `tsc --noEmit` |
| 154 | + - `deploy`: `wrangler deploy` |
| 155 | + |
| 156 | +### `wrangler.toml` |
| 157 | + |
| 158 | +- `name = "cazala-edge-party"` |
| 159 | +- `main = "src/index.ts"` |
| 160 | +- `compatibility_date = "YYYY-MM-DD"` |
| 161 | +- `routes = [{ pattern = "caza.la/party*", zone_name = "caza.la" }]` |
| 162 | +- `vars = { UPSTREAM_ORIGIN = "https://party-assets.caza.la" }` |
| 163 | + |
| 164 | +### `src/index.ts` |
| 165 | + |
| 166 | +- implement the routing + proxy described above |
| 167 | + |
| 168 | +## Notes / pitfalls |
| 169 | + |
| 170 | +- The Worker route must be **path-scoped**. Do not bind it to all of `caza.la/*`. |
| 171 | +- Be careful when stripping paths: |
| 172 | + - `/party/` must become `/` (not empty) |
| 173 | +- Don’t accidentally proxy `/party` without normalizing slash; relative URLs can behave differently. |
| 174 | +- Don’t rely on `*.pages.dev` anywhere (it may change). |
| 175 | + |
| 176 | +## Definition of done |
| 177 | + |
| 178 | +- Repo can be cloned and deployed by CI with only the two Cloudflare secrets. |
| 179 | +- Worker is deployed and mounted on `caza.la/party*`. |
| 180 | +- All acceptance tests above pass. |
| 181 | + |
| 182 | + |
0 commit comments