Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion apps/web/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,10 @@ Or run repo-wide via root `pnpm typecheck` / `pnpm test` / `pnpm build`.
- **Build settings:** root directory `apps/web`, build command `pnpm run build`,
deploy command `npx wrangler deploy`.
- **Build variables:** `VITE_TURNSTILE_SITE_KEY`, the widget's site key. Vite
inlines it at build time, so it is a _build_ variable. Also set
inlines it at build time, so it is a _build_ variable. Optionally
`VITE_CF_BEACON_TOKEN`, the Web Analytics token: `vite.config.ts` adds the
beacon script to `index.html` only when it is set (production builds only, and
the dashboard site on manual installation, so there is one beacon). Also set
`NODE_VERSION=24.21.0` and `PNPM_VERSION=12.4.2`: the version files sit
outside the `apps/web` root, and the build image's defaults are older.
- **Runtime secret:** `TURNSTILE_SECRET` under the Worker's Settings → Variables
Expand Down
10 changes: 10 additions & 0 deletions apps/web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,16 @@ Without the site key the GitHub field is hidden and the folder picker is the
page's only loader. The Worker verifies each token with Turnstile's `siteverify`
before it fetches, and rate-limits each IP to 5 loads a minute.

### Web Analytics

Cloudflare Web Analytics is optional. Add the site in the dashboard (Web
Analytics → Add a site) with manual JS snippet installation, not automatic
injection, which would load a second beacon. Copy the `token` from its snippet
into the build variable `VITE_CF_BEACON_TOKEN` (Settings → Build → Variables),
and set it for production builds only so previews don't report into the site.
The token is public: the build puts the beacon `<script>` into `index.html`.
Without the variable the page has no beacon.

To deploy from your machine instead, run `pnpm --filter web exec wrangler login` once, then
`pnpm --filter web deploy`.

Expand Down
43 changes: 39 additions & 4 deletions apps/web/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,46 @@
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import { defineConfig, loadEnv, type Plugin } from "vite";

/**
* Cloudflare Web Analytics, only in a build given `VITE_CF_BEACON_TOKEN` (a
* Workers Builds build variable). The token is public, it ships in the page, but
* keeping it out of the repository keeps forks, previews and local builds from
* reporting into the production site. Without it the page has no beacon.
*/
const cloudflareBeacon = (token: string): Plugin => ({
name: "cloudflare-beacon",
apply: "build",
transformIndexHtml: () => {
if (token === "") {
return [];
}
if (!/^[0-9a-f]{32}$/.test(token)) {
throw new Error("VITE_CF_BEACON_TOKEN must be 32 lowercase hex digits");
}
return [
{
tag: "script",
attrs: {
type: "module",
src: "https://static.cloudflareinsights.com/beacon.min.js",
"data-cf-beacon": JSON.stringify({ token }),
},
injectTo: "head",
},
];
},
});

// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
export default defineConfig(({ mode }) => ({
plugins: [
react(),
cloudflareBeacon(
loadEnv(mode, process.cwd(), "VITE_").VITE_CF_BEACON_TOKEN ?? ""
),
],
resolve: { tsconfigPaths: true },
// The GitHub zip route lives in the Worker (`src/api/`): run
// `pnpm --filter web dev:worker` beside `dev` to load repositories from GitHub.
server: { proxy: { "/api": "http://localhost:8787" } },
});
}));
Loading