Cloudflare publishing platform

Aktuaalne Kaamera

A single Cloudflare Worker serves the Vite frontend, public briefing APIs, protected CMS APIs, and private R2 media; D1 stores the dated content and synchronization metadata.

Purpose

The repository rebuilds the news briefing site and its editorial CMS around one Worker, with content imported from a read-only Google Drive and media kept private.

Public experience

Published dates expose briefing sections, artifact metadata, and a canonical PDF rundown with text, navigation, sharing, and accessible loading or error states.

Editorial control

Protected administrators edit days and sections, manage artifacts, review Drive sync history, and publish or unpublish content.

Operational boundary

Private media lives in R2 and structured records in D1. Generated images, documents, transcripts, and raw source files are not tracked in Git.

Architecture

The Worker routes browser requests and scheduled work while Vite builds the frontend assets. Admin requests are guarded by Cloudflare Access; Drive sync writes validated drafts and metadata to the storage bindings.

Browser (public or Access-protected admin)
  -> Cloudflare Worker (src/worker.ts)
      -> Vite assets and public APIs
      -> Admin APIs -> Access JWT verification
      -> D1 (content, settings, sync history)
      -> R2 (private artifacts and media)
      <- PDF.js briefing rundown and media responses
Hourly cron -> read-only Google Drive sync -> validated drafts or complete imports

Frontend

Plain browser DOM code in src/client.ts and src/styles.css, with PDF.js rendering in src/pdf-rundown.ts.

Worker APIs

src/worker.ts handles public content, admin CMS operations, uploads, private media delivery, and scheduled synchronization.

Storage

D1 (AK_CONTENT) stores days, artifacts, mappings, settings, and sync history; R2 (AK_MEDIA) stores private files with metadata and range-aware delivery.

Capabilities

Implemented behavior is documented from the current source and task checklist. Deployment and live Access verification are intentionally reported as remaining rollout work.

Briefing delivery

  • List published dates and return a selected day’s sections and artifacts.
  • Render canonical PDFs with text layer, keyboard/touch navigation, reduced motion, and date/slide URL state.

CMS operations

  • Edit days and sections; upload, preview, and remove artifacts.
  • Publish or unpublish days; manage Drive settings and sync history; start manual sync.

Protection & validation

  • Verify Access JWT signature, issuer, audience, and authorized email.
  • Validate filename, MIME, byte size, magic bytes, briefing text, and CSV before writes.

Drive synchronization

Discover YYYY-MM-DD folders and supported infographic-, briefing-, data-, and slides- prefixes; use checksums and idempotent updates.

Media delivery

Private R2 responses support ETags, HEAD, byte ranges, immutable caching, and security headers for public assets.

Scheduled work

The root Worker declares an hourly cron (0 * * * *) for Drive synchronization dispatch.

Tech stack

Dependencies and platform bindings are taken from package.json and wrangler.jsonc.

Application

  • TypeScript and Vite 7
  • Cloudflare Workers with nodejs_compat
  • Plain browser DOM and CSS

Cloudflare services

  • D1: aktuaalnekaamera-content
  • R2: aktuaalnekaamera-media
  • Access JWT and scheduled cron

Libraries & checks

  • PDF.js and jose
  • ESLint and TypeScript
  • Vitest test suite and GitHub Actions CI

Structure

These are the principal entry points and operational files; TASKS.md references earlier headline-only RAG infrastructure as pending retirement after the root rollout.

PathResponsibility
index.html, src/client.ts, src/styles.cssVite entry point and public/CMS browser UI.
src/worker.tsRequest routing, APIs, uploads, media, and scheduled sync.
src/pdf-rundown.tsPDF.js rundown rendering and page navigation.
src/access.ts, src/env.tsAccess authorization and Worker binding definitions.
src/db.ts, migrations/0001_initial.sqlD1 data access and initial schema.
src/drive.ts, src/validation.tsDrive import/export and content/file validation helpers.
wrangler.jsonc, TASKS.mdBindings, preview environment, custom domain, cron, and rollout gates.
test/, .github/workflows/ci.ymlVitest coverage and CI check/deploy dry-run workflow.

Content workflow

Drive content is discovered and synchronized as structured records and private artifacts; review mode keeps changes unpublished until an editor verifies them.

1Prepare

Use a dated Drive folder and supported file prefixes.

2Discover

Read-only service account finds supported files and exports.

3Validate

Checksums, file formats, and briefing data are checked.

4Draft

Idempotent D1/R2 updates remain drafts by default.

5Review

CMS editor corrects mappings and previews the day.

6Publish

Publish makes the date and public artifacts available.

Run & verify

Local development uses Vite plus a Wrangler Worker with local D1/R2 emulation. Deployment commands are documented, but the repository does not prove that remote resources or the production domain are currently live.

Local development

npm install
npm run db:migrate:local
cp .dev.vars.example .dev.vars
npm run dev:worker  # Worker on :8787
npm run dev         # Vite on :5173

Open http://localhost:5173/admin. Vite proxies /api and /media to the Worker.

Checks and build

npm run lint
npm run typecheck
npm test
npm run build
npm run check
npm run deploy:dry

CI runs npm run check and a Wrangler deploy dry run on pushes and pull requests.

Cloudflare rollout

  1. Enable R2, create the private media bucket, and apply remote D1 migrations.
  2. Set Access and Google Drive service-account secrets; configure path-scoped Access for the admin surfaces.
  3. Run checks, deploy a preview, import and publish one test day, and smoke-test public content.
  4. Only then attach ak.kliimask.ee and retire the superseded staging resources.

Local-only secret boundary. Keep .dev.vars, real Access values, service-account credentials, private media, and source briefing files out of Git. Never deploy DEV_ADMIN_BYPASS=true.