System topology
Cloudflare Workers is the single application runtime. SvelteKit serves the user interface and server endpoints, while Workers Static Assets serves the generated client assets. The Worker reaches D1 for durable identity state and Google for OAuth and YouTube data.
| Layer | Implementation | Responsibility |
|---|---|---|
| UI | Svelte 5 + Tailwind CSS 4 | Responsive dashboard, date selection, loading and error states |
| Server | SvelteKit on Workers | Remote functions, OAuth endpoints, request-scoped session lookup |
| Storage | Cloudflare D1 | Accounts, encrypted OAuth tokens, channel IDs, hashed sessions |
| External data | YouTube Data API v3 | Owned-channel totals, titles, thumbnails, publication metadata |
| External analytics | YouTube Analytics API v2 | Summary metrics, daily views, and ranked video performance |
Analytics request path
The dashboard no longer uses page load functions. Its two server boundaries live in src/routes/data.remote.ts: getViewer resolves the header identity, and getDashboard resolves the complete report.
- The browser derives the query input from the URL’s
startandendparameters. - The remote query validates the input with Valibot and captures the current request’s locals and Cloudflare platform bindings.
- The request hook has already resolved the opaque session cookie against its SHA-256 hash in D1.
- The account’s access token is decrypted. If it is within one minute of expiry, the refresh token is used and the new access token is encrypted back into D1.
- The Worker fetches the owned channel, then requests the summary, daily series, and top videos concurrently.
- Svelte renders the returned result. Changing the date parameters produces a distinct remote-query key.
OAuth, token protection & sessions
Google authentication uses the authorization-code flow with PKCE. A short-lived, HMAC-signed state cookie binds the callback to the initiating browser, while the long-lived application session remains independently revocable in D1.
| Asset or threat | Control | Lifetime / behavior |
|---|---|---|
| Authorization-code interception | PKCE with S256 challenge | Verifier exists only in the signed OAuth state cookie |
| Callback forgery / CSRF | HMAC-SHA-256 state cookie and returned-state comparison | 10 minutes |
| Google access + refresh tokens | AES-GCM encryption before D1 persistence | Access token refreshed when near expiry |
| Application session | Opaque random token in HttpOnly SameSite=Lax cookie; only SHA-256 hash stored | 30 days, revocable by deleting the D1 row |
| Redirect abuse | Return path must begin with one slash and not two | Invalid values fall back to / |
| Secret exposure | Wrangler secrets / local .dev.vars | Never committed or placed in wrangler.jsonc |
D1 data model
The initial migration creates only two tables. The deliberately small schema makes revocation and account cleanup straightforward: deleting an account cascades to every session.
| Table | Key fields | Purpose |
|---|---|---|
accounts | id, unique google_sub, profile fields, channel_id, encrypted access/refresh tokens, expiry, scopes | One durable identity and Google authorization record per Google subject |
sessions | token_hash, account_id, expires_at, created_at | Revocable application sessions without persisting the bearer token itself |
Indexes cover session lookup by account and expiry. The foreign key uses ON DELETE CASCADE.
Local setup
1. Prepare Google Cloud
- Create or select a Google Cloud project.
- Enable YouTube Data API v3 and YouTube Analytics API.
- Configure the OAuth consent screen. While publishing status is Testing, add each Google account that should be allowed to sign in as a test user.
- Create an OAuth 2.0 Client ID of type Web application.
- Add
http://localhost:5173/auth/google/callbackas an authorized redirect URI.
2. Install and configure the project
pnpm install
cp .dev.vars.example .dev.vars
Fill .dev.vars locally with four values:
GOOGLE_CLIENT_ID- The Google web OAuth client ID
GOOGLE_CLIENT_SECRET- The corresponding Google client secret
TOKEN_ENCRYPTION_KEY- A long random value used to protect persisted Google tokens
SESSION_SECRET- A different long random value used to sign OAuth state
3. Generate bindings and migrate local D1
pnpm gen
pnpm db:migrate:local
pnpm dev
pnpm gen refreshes the typed Cloudflare environment. The migration command applies migrations/0001_initial.sql to Wrangler’s local D1 database. The Vite development server then runs the app at http://localhost:5173.
Production rollout
The Wrangler configuration declares a D1 binding named DB, four required secrets, Workers Static Assets, observability, the nodejs_compat compatibility flag, a Workers.dev route, and preview URLs.
Add the deployed callback URL—https://YOUR-WORKER.YOUR-SUBDOMAIN.workers.dev/auth/google/callback—to the same Google OAuth client. The string must match exactly.
First deployment
pnpm exec wrangler deploy --secrets-file .dev.vars
pnpm db:migrate:remote
Automatic provisioning creates the declared D1 database and writes generated resource details back to wrangler.jsonc. The production migration is applied after that database exists.
Later deployments
pnpm deploy
Rotate a secret
pnpm exec wrangler secret put NAME
Enter the value only at Wrangler’s interactive prompt. Do not include a secret in a shell argument, source file, or Wrangler configuration.
| Capability | Configuration |
|---|---|
| Application entry | .svelte-kit/cloudflare/_worker.js |
| Static assets | ASSETS binding from .svelte-kit/cloudflare |
| Database | D1 binding DB |
| Logs | Observability enabled; head sampling rate 1.0 |
| Traces | Enabled; head sampling rate 0.1 |
| Access | Workers.dev and preview URLs enabled |
Operations & validation
Repository quality gate
pnpm format
pnpm check
pnpm lint
pnpm test
The test suite runs with Cloudflare’s Workers Vitest pool, provisions an isolated local D1 binding, and applies the real migration before each Workers-runtime test group.
Failure behavior worth knowing
- A missing or expired application session returns the disconnected landing state rather than attempting a Google request.
- An expired Google access token is refreshed automatically when a refresh token is available.
- A reversed date range returns a typed user-facing error before any YouTube analytics request.
- A Google
403produces guidance to confirm both APIs and approved scopes. - Server failures are logged as structured JSON without logging token values.
The Analytics API exposes views, watch time, average duration, average percentage viewed, subscriber movement, and engagement. It does not guarantee every card available in YouTube Studio; do not assume impressions or click-through rate are available.