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.
Current public positioning: Denuo Web is Jaron Rosenau's independent software-delivery and portfolio umbrella for practical API-driven web apps, integrations, civic/public-systems tooling, marketplace workflows, internal tools, operational tooling, and supportable launches. It should be presented as an umbrella for project evidence and delivery work, not as conventional employment history. Public case studies are organized around three offers: Discovery / Implementation Plan, Web App / Integration Build, and Stabilize / Support Handoff.
- 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). Contact submissions are backend-only (/api/contact) and should not be client-written to Firestore.
- 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, captchaToken?, website? }; stores normalized payload in 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? })
Contact endpoint protections:
- In-memory IP-based rate limiting (
CONTACT_RATE_LIMIT_WINDOW_MS,CONTACT_RATE_LIMIT_MAX_REQUESTS) - Optional Firebase App Check verification (
CONTACT_REQUIRE_APP_CHECK=true, sendx-firebase-appcheckheader) - Optional Turnstile CAPTCHA verification (
CONTACT_REQUIRE_CAPTCHA=true,TURNSTILE_SECRET_KEY, sendcaptchaToken) - Optional origin allowlist (
CONTACT_ALLOWED_ORIGINS) - Payload normalization + length limits + honeypot field (
website)
Deploy rules to lock down writes to admin-only users, allow public reads for marketing content, and deny client-side writes to contactRequests:
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)TURNSTILE_SECRET_KEY– Turnstile secret for contact CAPTCHA verificationCONTACT_IP_HASH_SALT– random salt used to hash IP metadata before persistence- 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/App Check:
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,VITE_FIREBASE_APP_CHECK_SITE_KEY, 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 current positioning and recent work (QuestByCycle, CrowdPM Platform, Moonshine Art, APK Workbench, DripCopy). - 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.
This repository is part of Jaron Rosenau's implementation, developer-support, and integration engineering portfolio. The public case study summarizes the problem, delivery scope, architecture, and operational result.
- Case study: Denuo Web site implementation case study
- Full portfolio: Jaron Rosenau
- Summary: Firebase Hosting, Firestore content admin, Cloud Run API, and CI/CD for a production service site.