From e7b5b35fcdc56eaca202025986707eb94b0bb055 Mon Sep 17 00:00:00 2001 From: Kresna Date: Sun, 26 Jul 2026 23:11:24 +0700 Subject: [PATCH 1/3] fix(theme): re-apply saved theme on View Transitions navigation Dark mode reset to light when navigating between pages: the inline theme script ran only on initial load, so after a swap the runtime 'dark' class was lost when the incoming (light) static page swapped in. Re-apply the stored theme on astro:after-swap (before paint, no flash). --- src/layouts/Base.astro | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/layouts/Base.astro b/src/layouts/Base.astro index 232cd0d..98dfd2a 100644 --- a/src/layouts/Base.astro +++ b/src/layouts/Base.astro @@ -102,10 +102,18 @@ const jsonLdBlocks = [siteJsonLd, ...(Array.isArray(jsonLd) ? jsonLd : jsonLd ? From 9e8a52ee6be67892cf21498de32e94317a541c27 Mon Sep 17 00:00:00 2001 From: Kresna Date: Sun, 26 Jul 2026 23:17:02 +0700 Subject: [PATCH 2/3] docs: merge DEPLOYMENT-GIT into a single improved DEPLOYMENT.md Consolidate the two deploy docs; modernize the env-var section (SITE_GA_ID build var + branch-based auto-gating, split prod/staging R2 buckets, sync:r2 scripts). Remove DEPLOYMENT-GIT.md; fix the wrangler.jsonc reference. --- DEPLOYMENT-GIT.md | 152 ------------------------ DEPLOYMENT.md | 289 ++++++++++++++++++++++++++++------------------ wrangler.jsonc | 2 +- 3 files changed, 179 insertions(+), 264 deletions(-) delete mode 100644 DEPLOYMENT-GIT.md diff --git a/DEPLOYMENT-GIT.md b/DEPLOYMENT-GIT.md deleted file mode 100644 index 1edfb01..0000000 --- a/DEPLOYMENT-GIT.md +++ /dev/null @@ -1,152 +0,0 @@ -# Deployment (Git-based auto-deploy, no GitHub Actions) - -This guide sets up **push-to-deploy** with Cloudflare Workers Builds — Cloudflare -watches your GitHub branches directly and builds + deploys on every push. **No -GitHub Actions, no `wrangler` in CI you maintain.** - -- Push to **`main`** → deploys **production** (`goodwebtools.com`). -- Push to **`develop`** → deploys **staging** (`goodwebtools-staging.workers.dev`). - -> This is an alternative to the manual/CLI flow in [DEPLOYMENT.md](./DEPLOYMENT.md). -> The architecture (static `dist/` + a Worker that streams models from R2) and the -> one-time R2 model upload are identical — read those sections there first. This -> doc only covers the two-branch git wiring. - ---- - -## How it maps - -Two Cloudflare **Worker** services, both connected to the **same** GitHub repo, -each watching a different production branch: - -| Environment | Worker service | Production branch | Deploy command | URL | -|-------------|----------------|-------------------|----------------|-----| -| Production | `goodwebtools` | `main` | `npx wrangler deploy` | `goodwebtools.com` | -| Staging | `goodwebtools-staging` | `develop` | `npx wrangler deploy --env staging` | `goodwebtools-staging.workers.dev` | - -The `staging` environment is defined in [`wrangler.jsonc`](./wrangler.jsonc) -(`env.staging`, worker name `goodwebtools-staging`). It reuses the **same R2 -bucket** — the ML models are immutable and safe to share between environments. - -``` -git push origin main ─▶ Cloudflare Workers Builds ─▶ wrangler deploy ─▶ production -git push origin develop ─▶ Cloudflare Workers Builds ─▶ wrangler deploy --env staging ─▶ staging -``` - ---- - -## One-time setup - -### 0. Prerequisites (shared with DEPLOYMENT.md) - -- Create the R2 bucket and upload the model assets **once** (see - [DEPLOYMENT.md → Upload the ML model assets to R2](./DEPLOYMENT.md#upload-the-ml-model-assets-to-r2)). - Both environments read from the same bucket, so you only do this once. - -### 1. Production Worker (branch `main`) - -Cloudflare dashboard → **Workers & Pages → Create → Workers → Import a repository**: - -1. Select the `slaveofcode/goodwebtools` repo. -2. **Worker name:** `goodwebtools` -3. **Production branch:** `main` -4. **Build command:** `npm run build` -5. **Deploy command:** `npx wrangler deploy` -6. **Build variables & secrets** (these are read at *build* time by Vite): - - `PUBLIC_GA_ID` = your GA4 id, e.g. `G-XXXXXXXXXX` (omit to disable analytics) - - leave `PUBLIC_NOINDEX` **unset** (production must be indexable) -7. Save & deploy. Then add the custom domain `goodwebtools.com` under the - Worker's **Settings → Domains & Routes**. - -### 2. Staging Worker (branch `develop`) - -Repeat **Create → Import a repository** for the *same* repo, as a second Worker: - -1. Select the same repo. -2. **Worker name:** `goodwebtools-staging` -3. **Production branch:** `develop` ← the branch this Worker deploys -4. **Build command:** `npm run build` -5. **Deploy command:** `npx wrangler deploy --env staging` -6. **Build variables & secrets:** - - **do not** set `PUBLIC_GA_ID` (no analytics on staging) - - set `PUBLIC_NOINDEX` = `1` (emits `noindex` so staging stays out of search) -7. Save & deploy. It's reachable at `goodwebtools-staging.workers.dev` (or attach - `staging.goodwebtools.com`). - -That's it. From now on every push to `main` or `develop` auto-builds and deploys -the matching environment. - ---- - -## Why `PUBLIC_*` variables go in the *build* settings - -`PUBLIC_GA_ID` and `PUBLIC_NOINDEX` are consumed by `npm run build` (Vite inlines -`import.meta.env.PUBLIC_*` into the static output) — they are **not** Worker -runtime bindings. So they must be set as **Build variables** on each Worker's -Workers Builds configuration, and they differ per environment: - -| Variable | Production (`main`) | Staging (`develop`) | -|----------|---------------------|---------------------| -| `PUBLIC_GA_ID` | your GA4 id | *(unset)* | -| `PUBLIC_NOINDEX` | *(unset)* | `1` | - -Changing a build variable takes effect on the **next push/redeploy**. - ---- - -## Feature-branch previews (optional) - -Each Worker only auto-**deploys** its own production branch. Pushing any *other* -branch to a connected Worker triggers a **preview build** instead (Cloudflare runs -`npx wrangler versions upload`, giving a temporary versioned preview URL) without -touching the live environment. You can turn this off per Worker under **Settings -→ Builds → Non-production branches** if you don't want preview builds. - ---- - -## Promoting staging → production - -Because both environments build from the same commit graph, promotion is just a -fast-forward merge and a push: - -```bash -git checkout main -git merge --ff-only develop -git push origin main # ← triggers the production deploy -git checkout develop -``` - ---- - -## Verifying a deploy - -- **Production indexable, staging not:** - ```bash - curl -s https://goodwebtools.com/ | grep -o ']*>' - # → index, follow - curl -s https://goodwebtools-staging.workers.dev/ | grep -o ']*>' - # → noindex, nofollow - ``` -- **Analytics only in production:** `PUBLIC_GA_ID` is present in the production - HTML and absent in staging (and it still only loads after cookie consent). -- **Models serve:** open a page that uses an AI tool and confirm `/models/…` - returns `200` from R2 (see DEPLOYMENT.md troubleshooting). - ---- - -## Rollback - -Every deploy is a Worker **version**. In the dashboard → the Worker → **Deployments**, -pick a previous version and **Rollback** — instant, no rebuild. Or push a revert -commit to the branch to redeploy the previous state. - ---- - -## Notes - -- **Local manual deploys still work:** `npm run deploy` (production) and - `npm run deploy:staging` (staging) run the same commands Cloudflare runs. -- **`wrangler.jsonc` is the single source of truth** for both environments; the - staging worker name and bindings live under `env.staging`. -- **Secrets:** none are committed. `PUBLIC_*` build vars are configured in the - Cloudflare dashboard per Worker; `.env`/`.dev.vars` stay local and gitignored. diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index b97b0ca..56ec53c 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -1,175 +1,246 @@ # Deployment Guide GoodWebTools is a **static Astro site** deployed to **Cloudflare Workers** (with -Static Assets), plus a small Worker that streams the AI-tool model files from an -**R2 bucket**. Everything runs client-side; the Worker only serves files. +Static Assets), fronted by a small Worker that streams the AI-tool model files +from an **R2 bucket**. Everything runs client-side; the Worker only serves files. -> **Prefer push-to-deploy?** For a two-branch, git-based auto-deploy -> (`main` → production, `develop` → staging) with **no GitHub Actions**, see -> [DEPLOYMENT-GIT.md](./DEPLOYMENT-GIT.md). This guide covers the architecture, -> the one-time R2 model upload (shared by both), and manual/CLI deploys. +Deploys are **push-to-deploy** via Cloudflare Workers Builds — Cloudflare watches +the GitHub branches and builds + deploys on every push. **No GitHub Actions.** + +- Push to **`main`** → **production** (`goodwebtools.com`) +- Push to **`develop`** → **staging** (`goodwebtools-staging.workers.dev`) ## Architecture - `npm run build` → static site in `dist/` (HTML, JS, CSS, WASM). -- `worker/index.js` → serves `/models/*` from the R2 bucket `MODELS`, and - delegates everything else to the static build (`ASSETS` binding). -- `wrangler.jsonc` → wires it together (`main`, `assets`, `r2_buckets`). - -> **Workers, not Pages.** This project deploys **one Cloudflare Worker with -> Static Assets** — *not* Cloudflare Pages. A Worker is required because a plain -> static/Pages site can't stream the large ML model files at `/models/*`; the -> Worker sits in front and routes `/models/*` to **R2** while serving everything -> else from the bundled **Static Assets**. (The static-asset half plays the role -> Pages would, but it's all a single Worker.) +- `worker/index.js` → serves `/models/*` from the R2 bucket (`MODELS` binding), + and delegates everything else to the static build (`ASSETS` binding). +- `wrangler.jsonc` → wires it together (worker name, `assets`, `r2_buckets`, and + the `env.staging` environment). -### Deployment flow (build → deploy → runtime) +> **Workers, not Pages.** This deploys **one Cloudflare Worker with Static +> Assets** — *not* Cloudflare Pages. A Worker is required because a plain static +> site can't stream the large ML model files at `/models/*`; the Worker routes +> `/models/*` to **R2** and serves everything else from the bundled Static Assets. ``` - BUILD & DEPLOY — automatic on every push (Cloudflare Workers Builds; no GitHub Actions) + BUILD & DEPLOY — automatic on every push (Cloudflare Workers Builds) ┌─────────────┐ git push ┌────────────────────────┐ npm run build ┌────────────────┐ │ GitHub repo │ ──────────▶ │ Cloudflare Workers │ ───────────────▶ │ dist/ │ - │ main / │ (branch │ Builds (CI runner) │ + postbuild │ static site: │ - │ develop │ watched) │ │ prune │ HTML·JS·CSS· │ - └─────────────┘ │ then: wrangler deploy │ │ fonts·small │ - └───────────┬─────────────┘ │ wasm (mupdf, │ - │ deploys │ libarchive, │ - │ the Worker │ sqlite) │ - ▼ └───────┬────────┘ - ┌──────────────────────────────────────────────────────────┐ │ uploaded as - │ Cloudflare Worker (worker/index.js) │◀─────────────┘ Static Assets + │ main / │ (branch │ Builds (CI runner) │ + postbuild │ static site │ + │ develop │ watched) │ then: wrangler deploy │ prune │ │ + └─────────────┘ └───────────┬─────────────┘ └───────┬────────┘ + │ deploys the Worker │ uploaded as + ▼ │ Static Assets + ┌──────────────────────────────────────────────────────────┐◀─────────────┘ + │ Cloudflare Worker (worker/index.js) │ │ binding ASSETS ─▶ dist/ · binding MODELS ─▶ R2 │ └──────────────────────────────────────────────────────────┘ - ▲ - │ read at runtime (NOT part of the git build) + ▲ read at runtime (NOT part of the git build) ┌──────────────────────────────────────────────────────────┐ │ R2 bucket "goodwebtools-models" — uploaded ONCE, by hand │ - │ /imgly /mediapipe /esrgan-slim /ort /lama /ffmpeg (~540 MB of ML models + wasm) + │ /imgly /mediapipe /esrgan-slim /ort /lama /ffmpeg (~540 MB) └──────────────────────────────────────────────────────────┘ - RUNTIME — every visitor request - - Browser ──▶ Cloudflare Worker (worker/index.js) - │ - ├─ path starts with /models/ ─▶ env.MODELS.get(key) ─▶ R2 bucket (big models) - │ hit → stream + immutable cache; miss → 404 - │ - └─ anything else ─▶ env.ASSETS.fetch() ─▶ dist/ static assets - (HTML, JS, CSS, fonts, small wasm) + Browser ──▶ Worker ──┬─ /models/* ─▶ env.MODELS.get(key) ─▶ R2 (hit → stream + immutable cache; miss → 404) + └─ everything ─▶ env.ASSETS.fetch() ─▶ dist/ static assets ``` **What goes where** | Content | Lives in | Served by | Deployed by | |---------|----------|-----------|-------------| -| HTML · JS · CSS · fonts · small wasm (mupdf, libarchive, sqlite) | Worker **Static Assets** (from `dist/`) | `ASSETS` binding | `git push` → Workers Builds | -| The routing logic (`/models/*` → R2, else → assets) | The **Worker** (`worker/index.js`) | the Worker itself | `git push` → Workers Builds | -| Large ML models + ORT/ffmpeg wasm (~540 MB) | **R2** bucket `goodwebtools-models` | `MODELS` binding (Worker streams) | **one-time manual** upload (`wrangler r2 object put`) | +| HTML · JS · CSS · fonts · small wasm (mupdf, libarchive, sqlite) | Worker Static Assets (from `dist/`) | `ASSETS` binding | `git push` → Workers Builds | +| Routing logic (`/models/*` → R2, else → assets) | The Worker (`worker/index.js`) | the Worker itself | `git push` → Workers Builds | +| Large ML models + ORT/ffmpeg wasm (~540 MB) | **R2** bucket | `MODELS` binding | **one-time manual** upload (`npm run sync:r2`) | + +The models are the only piece **not** part of the git build — uploaded to R2 once +(see below) and immutable, so a normal push never re-uploads ~540 MB. -The models are the only piece that is **not** part of the git build — they're -uploaded to R2 once (see below) and are immutable, so a normal push never -re-uploads ~540 MB. For the two-branch git wiring (main → production, -develop → staging), see [DEPLOYMENT-GIT.md](./DEPLOYMENT-GIT.md). +## Environments + +Two Cloudflare **Worker** services, both connected to the **same** GitHub repo, +each watching a different production branch: + +| Environment | Worker service | Branch | Deploy command | URL | +|-------------|----------------|--------|----------------|-----| +| Production | `goodwebtools` | `main` | `npx wrangler deploy` | `goodwebtools.com` | +| Staging | `goodwebtools-staging` | `develop` | `npx wrangler deploy --env staging` | `goodwebtools-staging.workers.dev` | + +The `staging` environment is defined in [`wrangler.jsonc`](./wrangler.jsonc) +(`env.staging`). Each environment has its **own** R2 bucket +(`goodwebtools-models` / `goodwebtools-models-staging`). + +``` +git push origin main ─▶ Workers Builds ─▶ wrangler deploy ─▶ production +git push origin develop ─▶ Workers Builds ─▶ wrangler deploy --env staging ─▶ staging +``` ## One-time setup You need a Cloudflare account and the Wrangler CLI (pinned as a devDependency). +### 1. Authenticate + create the R2 buckets + ```bash -npx wrangler login # authenticate -npx wrangler r2 bucket create goodwebtools-models # production model bucket -npx wrangler r2 bucket create goodwebtools-models-staging # staging model bucket +npx wrangler login +npx wrangler r2 bucket create goodwebtools-models # production models +npx wrangler r2 bucket create goodwebtools-models-staging # staging models ``` -### Upload the ML model assets to R2 +### 2. Upload the ML model assets to R2 -The Phase-5 AI tools each need model + runtime files (**~540 MB total**) that are -**not** committed — they live in R2 under `/models/`, mirroring the on-disk -layout. `npm run stage:models` fetches/copies them all into `public/models/`: +The AI tools need model + runtime files (**~540 MB total**) that are **not** +committed — they live in R2 under `/models/`, mirroring the on-disk layout. | Prefix | Tool(s) | Approx size | |--------|---------|-------------| -| `imgly/` | Background Remover, Portrait Blur (ISNet + ort) | ~211 MB | -| `mediapipe/` | Face Blur (BlazeFace + tasks-vision WASM) | ~35 MB | -| `esrgan-slim/` | Image Upscaler (ESRGAN weights) | ~4 MB | +| `imgly/` | Background Remover, Portrait Blur | ~211 MB | +| `mediapipe/` | Face Blur | ~35 MB | +| `esrgan-slim/` | Image Upscaler | ~4 MB | | `ort/` | Object Remover (onnxruntime-web WASM) | ~76 MB | -| `lama/` | Object Remover (LaMa ONNX, fixed 512×512) | ~200 MB | -| `ffmpeg/` | Video → GIF and other Media tools (ffmpeg.wasm **ESM** core) | ~31 MB | +| `lama/` | Object Remover (LaMa ONNX) | ~200 MB | +| `ffmpeg/` | Video/audio tools (ffmpeg.wasm) | ~31 MB | -Stage, then sync the whole tree to R2 (keys keep their subfolders). `sync:r2` -reads the bucket name(s) from `wrangler.jsonc`, so it targets whatever -production/staging are configured to use: +Stage them locally, then sync to R2 (`sync:r2` reads the bucket names from +`wrangler.jsonc`, so it targets whatever prod/staging are configured to use): ```bash npm run stage:models # → public/models/** (gitignored; downloads LaMa + face model) -# Cloudflare auth first: set CLOUDFLARE_API_TOKEN, or run `npx wrangler login` -npm run sync:r2 # upload to every configured bucket (prod + staging, deduped) -# or target one environment: -npm run sync:r2:prod # production bucket only -npm run sync:r2:staging # staging bucket only -# preview without uploading: -npm run sync:r2 -- --dry-run +# Cloudflare auth first: set CLOUDFLARE_API_TOKEN, or `npx wrangler login` +npm run sync:r2 # upload to every configured bucket (prod + staging) +npm run sync:r2:prod # …or just production +npm run sync:r2:staging # …or just staging +npm run sync:r2 -- --dry-run # preview without uploading ``` Uploads are idempotent (`wrangler r2 object put --remote`), so re-running retries -any failures and re-staging + re-syncing updates a bumped model version. (You can -also drag the folders into the bucket via the Cloudflare dashboard, or use -`rclone`.) **Until the models are in R2, the AI tools 404 in prod.** - -> Production uses `goodwebtools-models` and staging uses -> `goodwebtools-models-staging`, so `sync:r2` uploads to **both**. Create the -> staging bucket once: `npx wrangler r2 bucket create goodwebtools-models-staging`. +failures and re-staging + re-syncing updates a bumped model version. (You can +also drag the folders into the bucket via the dashboard, or use `rclone`.) +**Until the models are in R2, the AI tools 404.** > **Local dev:** `npm run stage:models` also lets `npm run dev` serve the models -> from `public/models/**`. Remove that folder before a *local* `wrangler deploy` -> so ~540 MB isn't uploaded as static assets — production loads them from R2. -> (CI never has it: `public/models/` is gitignored.) +> from `public/models/**`. `public/models/` is gitignored, so CI never uploads it. + +### 3. Connect Workers Builds (one Worker per environment) + +In the Cloudflare dashboard → **Workers & Pages → Create → Workers → Import a +repository**, create **two** Workers from the same repo: -## Deploying +| | Production | Staging | +|--|-----------|---------| +| **Worker name** | `goodwebtools` | `goodwebtools-staging` | +| **Production branch** | `main` | `develop` | +| **Build command** | `npm run build` | `npm run build` | +| **Deploy command** | `npx wrangler deploy` | `npx wrangler deploy --env staging` | -### Option A — Cloudflare Workers Builds (recommended, git-based) +Then attach the custom domain (`goodwebtools.com`) to the production Worker under +**Settings → Domains & Routes**. From now on, every push to `main`/`develop` +auto-builds and deploys the matching environment. -Connect the GitHub repo in the Cloudflare dashboard → Workers & Pages → Create → -Import a repository, then set: +### 4. Build variables -- **Build command:** `npm run build` -- **Deploy command:** `npx wrangler deploy` -- **Production branch:** the branch you want live (currently the app lives on - `develop` — set this to `develop`, or merge `develop → main` first). +Analytics and indexing are **auto-gated by branch** in `astro.config.mjs` (using +the `WORKERS_CI_BRANCH` Cloudflare injects), so you barely configure anything: -Cloudflare rebuilds and deploys on every push to that branch. +| | Production (`main`) | Staging / other branches | +|--|--------------------|--------------------------| +| Google Analytics | on — from `SITE_GA_ID` | off | +| `robots` | `index, follow` | `noindex, nofollow` | -### Option B — Manual +The **only** build variable to set is `SITE_GA_ID` on the **production** Worker +(Workers Builds → Settings → Build → Variables) — your GA4 id, e.g. +`G-XXXXXXXXXX`. Leave it unset to disable analytics. Everything else is automatic; +you do **not** set `PUBLIC_NOINDEX` by hand (the branch gate handles it). GA is +also consent-gated — it only loads after the visitor accepts the cookie banner. + +> These are **build-time** variables (Vite inlines `import.meta.env.PUBLIC_*` into +> the static output), so they must be **Build variables**, not Worker runtime +> bindings. Changes take effect on the next push/redeploy. + +## Everyday deploys + +Just push — Workers Builds does the rest: ```bash -npm run deploy # = npm run build && wrangler deploy +git push origin develop # → staging +git push origin main # → production ``` +**Manual/CLI** (runs the same commands Cloudflare runs) still works: + +```bash +npm run deploy # production (= npm run build && wrangler deploy) +npm run deploy:staging # staging (= … && wrangler deploy --env staging) +``` + +## Promoting staging → production + +Both environments build from the same commit graph, so promotion is a +fast-forward merge and a push: + +```bash +git checkout main +git merge --ff-only develop +git push origin main # ← triggers the production deploy +git checkout develop +``` + +## Feature-branch previews + +Each Worker only auto-**deploys** its own production branch. Pushing any *other* +branch triggers a **preview build** (Cloudflare runs `npx wrangler versions +upload`, giving a temporary versioned preview URL) without touching the live +environment. Toggle this per Worker under **Settings → Builds → Non-production +branches**. + +## Rollback + +Every deploy is a Worker **version**. In the dashboard → the Worker → +**Deployments**, pick a previous version and **Rollback** — instant, no rebuild. +Or push a revert commit to the branch to redeploy the previous state. + +## Verifying a deploy + +```bash +# Production indexable, staging not: +curl -s https://goodwebtools.com/ | grep -o ']*>' # → index, follow +curl -s https://goodwebtools-staging.workers.dev/ | grep -o ']*>' # → noindex, nofollow + +# Models serve from R2 (200): +curl -sI https://goodwebtools.com/models/lama/lama_fp32.onnx | head -1 # → HTTP/2 200 +``` + +Analytics: `SITE_GA_ID` is inlined in the production HTML and absent in staging. + ## Notes & troubleshooting -- **`wrangler deploy` needs the R2 bucket to exist** — create it first (above), - or the deploy fails on the `MODELS` binding. -- **Per-file asset limit is 25 MB.** The site's own WASM (mupdf ~10 MB, - libarchive ~1 MB) is fine; large ML models go to R2 precisely to avoid this. - onnxruntime-web's wasm gets emitted into `dist/_astro` at ~26 MB but is unused - (ORT loads from `/models/ort/`); the `postbuild` step (`scripts/prune-dist.mjs`) - removes it automatically so deploys stay under the limit. -- **Model versions must match.** `@imgly/background-removal` and - `@imgly/background-removal-data` are pinned to the same version; a mismatch - causes "Resource … not found" at runtime. -- **404s / routing:** Astro emits `dist/tools//index.html`; Cloudflare - serves trailing-slash variants automatically and uses `dist/404.html` +- **`wrangler deploy` needs the R2 bucket to exist** — create it first, or the + deploy fails on the `MODELS` binding. +- **Per-file asset limit is 25 MB.** Site WASM (mupdf ~10 MB, libarchive ~1 MB) + is fine; large ML models go to R2 to avoid this. onnxruntime-web's ~26 MB wasm + is emitted into `dist/_astro` but unused (ORT loads from `/models/ort/`); the + `postbuild` step (`scripts/prune-dist.mjs`) removes it so deploys stay under the + limit. +- **Node heap:** the asset-heavy build sets `--max-old-space-size=8192` in the + `build` script (via `cross-env`) so Cloudflare's build doesn't OOM. +- **Peer deps:** `.npmrc` sets `legacy-peer-deps=true` so `npm ci` resolves the + tfjs/upscaler peer conflict on Cloudflare. +- **Model versions must match.** `@imgly/background-removal` and its `-data` + package are pinned together; a mismatch causes "Resource … not found". +- **404s / routing:** Astro emits `dist/tools//index.html`; Cloudflare serves + trailing-slash variants and uses `dist/404.html` (`not_found_handling: "404-page"`). -- **Secrets:** none are committed. Wrangler auth is via `wrangler login` or the +- **Secrets:** none are committed. Wrangler auth is `wrangler login` or the `CLOUDFLARE_API_TOKEN` env var; `.dev.vars`, `.env*`, and `.wrangler/` are gitignored. ## Cost -Cloudflare's free tier covers this comfortably: static assets have unlimited +Cloudflare's free tier covers this comfortably: Static Assets have unlimited bandwidth, and R2 has a generous free tier for storage + egress (models are immutable and hard-cached, so they download once per visitor). @@ -180,15 +251,11 @@ GoodWebTools is self-hostable on Cloudflare Workers. To run your own copy: 1. **Fork** this repo and clone it. 2. **Rename the deployment identifiers** in `wrangler.jsonc` (`name`, `env.staging.name`) and the R2 bucket names to values you own. -3. **Create the R2 buckets:** - `npx wrangler r2 bucket create ` (and a `-staging` one). +3. **Create the R2 buckets** (production + `-staging`, step 1 above). 4. **Point branding at your domain** in `src/config.ts` (`SITE_URL`, `REPO_URL`) and `astro.config.mjs` (`site`); update `public/robots.txt`. -5. **(Optional) analytics:** set `SITE_GA_ID` as a production build variable - (Workers Builds → Settings → Build). Leave unset to disable. -6. **Stage & upload the ML models to R2:** `npm run stage:models` then - `npm run sync:r2` (see the section above). -7. **Connect Workers Builds** to your fork (production branch `main`, build - command `npm run build`) and push. +5. **(Optional) analytics:** set `SITE_GA_ID` as a production build variable. +6. **Stage & upload the models:** `npm run stage:models` then `npm run sync:r2`. +7. **Connect Workers Builds** to your fork (production branch `main`) and push. Nothing sends data anywhere except your own Cloudflare account. diff --git a/wrangler.jsonc b/wrangler.jsonc index e35073d..2caaeb2 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -41,7 +41,7 @@ // Staging environment (deployed from the `develop` branch). Bindings and // `assets` are NOT inherited by named environments, so they're repeated here. // Deploy with: npx wrangler deploy --env staging - // See DEPLOYMENT-GIT.md. + // See DEPLOYMENT.md. "env": { "staging": { "name": "goodwebtools-staging", From 84daf58e7ae388c265177d9e2ee75bbc1dd1d09e Mon Sep 17 00:00:00 2001 From: Kresna Date: Sun, 26 Jul 2026 23:20:47 +0700 Subject: [PATCH 3/3] docs(env): drop PUBLIC_GA_ID from .env.example; clarify SITE_GA_ID is a build var Reduces confusion: one GA knob (SITE_GA_ID, set as a production BUILD variable on Cloudflare). PUBLIC_GA_ID remains internal plumbing (astro.config sets it from SITE_GA_ID on main builds). --- .env.example | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/.env.example b/.env.example index d7a0209..2b7f48f 100644 --- a/.env.example +++ b/.env.example @@ -1,17 +1,13 @@ -# These are auto-set by branch on Cloudflare Workers Builds (see astro.config.mjs): -# main branch → Google Analytics ON, indexable -# other branch → Google Analytics OFF, robots noindex -# Only set them below to override that locally. +# Analytics + indexing are auto-set by branch on Cloudflare Workers Builds +# (see astro.config.mjs): main → Google Analytics ON + indexable; other branches +# → GA OFF + robots noindex. You normally don't set anything here. -# Google Analytics 4 measurement ID (e.g. G-XXXXXXXXXX). Leave empty to disable. -# Analytics only loads after the visitor accepts cookies in the consent banner. -PUBLIC_GA_ID= +# Production Google Analytics 4 ID (e.g. G-XXXXXXXXXX). Set this as a BUILD +# variable on the production Cloudflare Worker (Workers Builds → Settings → +# Build → Variables and Secrets), NOT as a runtime variable. Leave unset to +# disable analytics. GA loads only after the visitor accepts the cookie banner. +SITE_GA_ID= -# Set to 1 to emit robots noindex on every page. Auto-set to 1 for non-main -# branch builds in CI; leave empty otherwise. +# Force robots noindex on every page (auto-set to 1 for non-main branch builds +# in CI; leave empty otherwise). PUBLIC_NOINDEX= - -# Production Google Analytics 4 ID, injected by the deploy env (e.g. Cloudflare -# build variable SITE_GA_ID). Consumed by astro.config.mjs on `main` builds. -# Leave unset to disable analytics. Local dev: set PUBLIC_GA_ID above instead. -SITE_GA_ID=