A Firebase + Google Cloud Run powered site for Denuo Web, LLC. The frontend is a Vite/React app with a Firebase-authenticated admin dashboard that edits Firestore-driven content. The backend is a minimal Express API deployed to Cloud Run for contact capture and admin utilities. CI/CD is wired through GitHub Actions.
- Node.js 20 (use
nvm use 20or install via nvm) + npm - Firebase CLI:
npm install -g firebase-tools - Terraform >= 1.6 (if using
infra/terraform) - Google Cloud SDK (
gcloud) if you plan to deploy/run Cloud Run manually or authenticate Terraform via ADC - GitHub access to set repo secrets for CI/CD
web/– React + Vite SPA with Firebase Auth + Firestore-driven content and admin dashboardapi/– Express API for Cloud Run (/health,/contact,/admin/status)firebase.json/.firebaserc– Hosting + Cloud Run rewrite configfirestore.rules– Secures site content + contact submissions.github/workflows/– Deploy pipelines for Hosting and Cloud Rundocs/SPEC.md– Specification and feature outline for the site.env.example– Sample values for CI/CD secrets (set in GitHub, not committed)web/src/i18n/– Localization strings (EN/JA) used by the UI togglesinfra/terraform/– Scaffold to enable APIs, create deploy service account, Artifact Registry, and push GitHub secrets
- Copy
web/.env.exampletoweb/.envand fill your Firebase web app values. - Install deps and run locally:
cd web npm install npm run dev - Build for release:
npm run build(outputs toweb/dist).
The admin dashboard writes to Firestore siteContent/public (plus work sub-documents under siteContent/public/work/{slug} for case studies). Update security rules (firestore.rules) before going live.
- Toggle
VITE_USE_FIREBASE_EMULATORS=trueinweb/.envto point the SPA to local emulators. - Start emulators (requires
firebase-tools):
firebase emulators:start --only auth,firestore --project $FIREBASE_PROJECT_ID - Auth emulator runs on :9099 and Firestore emulator on :8080 (matching
firebase.json). Hosting emulator is configured on :5000 if you prefer serving the built app viafirebase emulators:start --only hosting,auth,firestore. - Radix UI Themes handles the light/dark toggle; appearance choice persists locally.
- Language toggle (EN/JA) is driven by
src/i18n/uiCopy.ts; UI labels fall back to English when translations are missing.
- Install deps:
cd api && npm install. - Local run (requires application default or
FIREBASE_SERVICE_ACCOUNTenv):npm run dev. - Optional Stripe invoicing: set
STRIPE_SECRET_KEYto enable/billing/invoice(admin-only). - Deploy to Cloud Run (example):
gcloud builds submit api --tag gcr.io/$PROJECT_ID/denuo-api gcloud run deploy denuo-api \ --image gcr.io/$PROJECT_ID/denuo-api \ --region us-central1 \ --allow-unauthenticated
Endpoints:
GET /health– service healthPOST /contact–{ name, email, project?, message }; stores to FirestorecontactRequestswhen credentials are presentGET /admin/status– requires Firebase ID token withadmin: truecustom claimPOST /billing/invoice– admin-only; creates and emails a Stripe invoice ({ email, name, amountCents, description? })
Deploy rules to lock down writes to admin-only users and allow public reads for marketing content:
firebase deploy --only firestore:rulesUse a service account with Firebase Auth admin permissions:
export FIREBASE_SERVICE_ACCOUNT="$(cat path/to/serviceAccount.json)"
node api/scripts/setAdminClaim.js --email=you@example.comSet these repo secrets before enabling CI/CD (Terraform will populate them by default using the generated deployer key for both Hosting and Cloud Run):
FIREBASE_SERVICE_ACCOUNT– JSON for a Firebase Hosting deploy service accountFIREBASE_PROJECT_ID– Firebase project idGCP_SERVICE_ACCOUNT_KEY– JSON key with Cloud Run + Artifact Registry permissionsGCP_PROJECT_ID– Google Cloud project idGCP_REGION– Cloud Run region (e.g.,us-central1)STRIPE_SECRET_KEY– Stripe secret (for invoicing API)- A sample
.env.exampleat repo root lists these keys (for local reference only; do not commit secrets). - Optionally, set (or let Terraform set) GitHub Actions variables for the Vite web config so production builds enable auth:
VITE_FIREBASE_API_KEY,VITE_FIREBASE_AUTH_DOMAIN,VITE_FIREBASE_PROJECT_ID,VITE_FIREBASE_STORAGE_BUCKET,VITE_FIREBASE_MESSAGING_SENDER_ID,VITE_FIREBASE_APP_ID, andVITE_USE_FIREBASE_EMULATORS(usuallyfalse).
deploy-hosting.yml builds web and deploys Hosting. deploy-cloudrun.yml builds and deploys api to Cloud Run when api/** changes.
- Live content stored at Firestore
siteContent/public; case studies are stored as documents atsiteContent/public/work/{slug}. Fallback content inweb/src/content/fallback.tsis built from Jaron Rosenau's resume and recent work (QuestByCycle, Moonshine Art, CrowdPM). - Services, projects, differentiators, process, contact info, and work metadata (
servicePackages,testimonials) are editable via/adminafter signing in. - Admin can send Stripe invoices from
/adminwhen the API hasSTRIPE_SECRET_KEYset; the UI uses the authenticated Firebase ID token.
- Keep UI strings in
web/src/i18n/uiCopy.ts; avoid hard-coding labels inside components. - For translated site content, mirror the schema per locale (e.g.,
siteContent/public/translations/ja) and keep keys consistent. - Default to English when translations are absent; prefer full-sentence keys to preserve Japanese line breaks.
- Ship locale files through CI (export/import JSON) to keep translators and releases in sync.
/adminnow includesLocalization JSON (EN/JA)tools to export a language pack, import updated JSON, and save it tositeContent/public/translations/{en|ja}.
Requests to /api/** are proxied to the Cloud Run service denuo-api in us-central1 via the firebase.json rewrite. Update serviceId/region if you change the Cloud Run deployment.
npm installin bothweb/andapi/.- For a zero-config preview, skip env files and run
npm run devinweb/; the site will use bundled fallback content and disable saves/admin actions (auth/API not configured). - To enable live Firestore/auth, fill
web/.envwith Firebase web config. - (Optional) Set
VITE_USE_FIREBASE_EMULATORS=trueand runfirebase emulators:start --only auth,firestorefor local auth/content editing. - Run
npm run devinweb/for the SPA; runnpm run devinapi/for the API. - Visit
/adminto sign in and edit content (admin claim required to save to Firestore).
- See
infra/terraform/README.mdfor automating API enablement, deploy service account, Artifact Registry, and pushing GitHub Actions secrets (including Stripe). Squarespace DNS is not managed; migrate DNS to a Terraform-capable provider if you want full automation for records.