Next.js frontend for a multi-tenant digital library. Firebase Functions / Firestore / RTDB / Storage / R2 backend is frozen — clients call the same endpoints as before.
- Readers open
/: browse published editions from the Realtime Database mirror—featured plus all publications (series grouped byseries_id). The main grid loads 12 series cards at a time with infinite scroll; search filters the full in-memory catalog. Read flipbooks without signing in. Publication pages live at/[seriesSlug]; the reader at/[seriesSlug]/[editionSlug]. - Editors / owners open
/studio: Google sign-in, Publications + Team tabs (uploads, covers, invites). Prefer a full page load into/studioso the studio boot script binds cleanly. - Platform staff open
/admin: Publishers, Catalog, Platform team (mirror rebuild, cover-thumb backfill for full admins).
Reader stack: PDF.js + StPageFlip. UI: Inter, Rotaract cranberry pink primary (#d81a6a), cream light-only chrome; the flipbook reader overlay stays dark.
URLs: see AGENTS.md. Canonical shapes only: /, /[seriesSlug], /[seriesSlug]/[editionSlug], /studio, /admin, /privacy, /terms.
npm install
npm run devOpen http://localhost:3000. Production-like Netlify preview: connect the repo and use netlify.toml (@netlify/plugin-nextjs, npm run build).
Defaults for the live Firebase web project and site URL are in lib/firebase/config.ts. Override with NEXT_PUBLIC_* in Netlify or a local .env.local only if needed. R2 keys never go in the frontend.
-
Create a project in the Firebase console.
-
Project settings → Your apps → Web — set
NEXT_PUBLIC_FIREBASE_*(includingNEXT_PUBLIC_FIREBASE_DATABASE_URL). -
Authentication → Sign-in method: enable Google. Authorized domains: production host +
localhost. -
Deploy rules/indexes:
firebase use rsapublicationhub firebase deploy --only firestore,database,storage
-
After Functions deploy, open
/adminas a platform admin and run Rebuild mirror (backfillMirror) once. -
Cloud Functions (region
us-central1— must matchlib/firebase/init.ts):cd functions && npm install && cp .env.example .env && cd .. # Edit functions/.env: R2_ACCOUNT_ID, R2_BUCKET_NAME, R2_PUBLIC_BASE_URL echo -n 'YOUR_R2_ACCESS_KEY_ID' | firebase functions:secrets:set R2_ACCESS_KEY_ID echo -n 'YOUR_R2_SECRET_ACCESS_KEY' | firebase functions:secrets:set R2_SECRET_ACCESS_KEY firebase deploy --only functions
See
docs/STORAGE.mdfor R2 CORS and public URL setup. -
Data model —
docs/FIRESTORE_SCHEMA.md. -
SEO — titles, OG/Twitter, sitemap/robots:
docs/SEO.md.
Create Firestore platform_admins/{uid} with tier: "admin" for your Firebase Auth UID (Console one-time). Then sign in at /admin. Invite more staff from Platform team. Run backfillMirror so platformAdmins/{uid} exists in RTDB.
See docs/STORAGE.md. For invite index issues: firebase deploy --only firestore:indexes.
NEXT_PUBLIC_FIREBASE_*/lib/firebase/config.tsmatch production; Auth authorized domains include the live hostname.- Canonical/OG via Next
metadata(NEXT_PUBLIC_SITE_URL). - Deploy Functions + rules; run backfillMirror once.
- Netlify builds with Next plugin (
netlify.toml); confirmpublicationshub.netlify.app301s topublications.rsamdio.org.
app/ # Next.js App Router pages (+ robots.ts / sitemap.ts)
components/ # React UI (shelf, publication, studio/admin shells)
lib/
urls.ts # Canonical URL helpers
firebase/ # Auth, RTDB/Firestore clients, uploads
catalog/ # Series grouping, covers, icons
client/ # Viewer + studio/admin imperative UIs
public/ # images, fonts, vendor (pdf.js / page-flip), st-page-flip.css
functions/ # Frozen Cloud Functions + mirrors
netlify.toml # Next plugin + 301 netlify.app → canonical host
docs/
AGENTS.md # Persistent agent / architecture context
- Next.js 15 (App Router), React 19, TypeScript, Tailwind 3
- Firebase Auth, Firestore, RTDB, 2nd gen Functions (
us-central1) - Cloudflare R2 for PDFs/covers (server-side only)
- PDF.js 3.11.174, StPageFlip 2.0.7 (same-origin under
public/vendor/)
When cloning to a new environment, update together:
NEXT_PUBLIC_FIREBASE_*/lib/firebase/config.ts.firebasercNEXT_PUBLIC_SITE_URLand metadata- Functions
.env/ R2 secrets for that GCP project