English | Tiếng Việt
A Next.js 15 app with a real-time gallery powered by Firebase and an automated Cloud Functions pipeline that:
- Optimizes uploaded images (Sharp), generates previews, and applies a watermark
- Publishes assets to a public Google Cloud Storage bucket
- Stores metadata in Firestore
- Uses Gemini 2.0 Flash to auto-generate Vietnamese descriptions and Q&A for each image
- Modern frontend: Next.js App Router, Tailwind CSS, Radix UI, Framer Motion, lucide icons
- Realtime gallery: Live updates from Firestore
optimized_images - High-perf media: Optimized and preview variants served from Google Cloud Storage
- AI captions & Q&A: Generated via Google Generative AI (Gemini) using Secret Manager
- Deep links & SEO:
/gallery/[name]detail page with dynamic metadata and JSON-LD - Social share: One-click share to Facebook, LinkedIn, and X
- Web: Next.js 15, React 19, Tailwind v4, Radix UI, Framer Motion
- Media:
video.js+ HLS for hero video playback - Firebase: Firestore, Analytics
- Cloud Functions: Node.js 22,
firebase-functions@v2,sharp,@google-cloud/storage,@google/generative-ai, Secret Manager
app/— Next.js App Router pages, routes, and OG image endpoint (app/og/route.tsx)components/— UI components (gallery, hero video, header/footer, UI primitives)lib/firebase.ts— Firebase client initialization for webfunctions/— Cloud Functions source (image processing + AI integration)
- Upload an image to the source bucket (
gdg-cloud-hanoi-devby default). optimizer_devCloud Function (StorageonObjectFinalized) triggers:- Skips non-image files and already-optimized uploads
- Creates an optimized image and a preview (Sharp), adds a watermark
- Writes
optimized_images/{docId}in Firestore with metadata - Calls Gemini 2.0 Flash to generate:
ai_description(Vietnamese, up to ~50 words)qaarray with auto-generated question/answer pairs
- Saves optimized assets to the public bucket (
gdg-cloud-hanoi).
- The frontend listens to
optimized_imagesand renders the grid and detail modal in real time.
- Node.js: Web app works with Node 18+, Functions require Node 22 (see
functions/package.json) - Firebase CLI:
npm i -g firebase-tools - A Google Cloud project with:
- Two Storage buckets: source and public
- Secret Manager enabled (for Gemini API key)
- Firestore (in native mode)
Create .env.local in the project root with your Firebase config and site URL.
NEXT_PUBLIC_FIREBASE_API_KEY=...
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=...
NEXT_PUBLIC_FIREBASE_PROJECT_ID=...
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=...
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=...
NEXT_PUBLIC_FIREBASE_APP_ID=...
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=...
NEXT_PUBLIC_SITE_URL=https://your-domain.exampleNEXT_PUBLIC_SITE_URLis used for social share links and the OG image route.
Defaults are defined in functions/index.js:
SOURCE_BUCKET = "gdg-cloud-hanoi-dev"PUBLIC_BUCKET = "gdg-cloud-hanoi"
Change these if your bucket names differ. Ensure the Functions service account has read on SOURCE_BUCKET and write on PUBLIC_BUCKET.
The function reads the Gemini API key from Secret Manager:
- Secret name:
projects/<PROJECT_NUMBER>/secrets/AI_API/versions/latest - Update the
secretNameinfunctions/index.jsif your resource path differs
You can create the secret via the console or CLI. Example CLI:
# Replace with your project ID
PROJECT_ID=your-project-id
# Get project number
gcloud projects describe "$PROJECT_ID" --format='value(projectNumber)'
# Create a secret named AI_API
printf "%s" "YOUR_GEMINI_API_KEY" | gcloud secrets create AI_API \
--data-file=- --project="$PROJECT_ID"Grant the Cloud Functions runtime service account Secret Manager Secret Accessor on this secret.
# In project root
npm install
npm run devOpen http://localhost:3000.
# One-time: install deps for functions
cd functions
npm install
# Option A: Deploy functions to Firebase
npm run deploy
# Option B: Run local emulators (functions only)
npm run serveNotes:
- The provided emulator script runs
--only functions. Add Storage/Firestore emulators if you want to test end-to-end triggers locally. - Ensure your local credentials can access GCS if you test against real buckets.
npm run dev— Next.js dev server (Turbopack)npm run build— Production buildnpm run start— Start built appnpm run lint— Lint web appnpm run functions— Deploy Cloud Functions only (shortcut)
npm run lint— Lint functionsnpm run serve— Start functions emulatornpm run deploy— Deploy functions onlynpm run logs— Tail function logs
- Web
app/page.tsx— Landing page with hero, FAQ, galleryapp/gallery/[name]/page.tsx— Deep link to an image detailapp/og/route.tsx— OG image generator usingNEXT_PUBLIC_SITE_URLcomponents/Gallery.tsx— Realtime grid, modal, and share actionslib/firebase.ts— Firebase client initialization
- Functions
functions/index.js—optimizer_devStorage trigger (Sharp, GCS, Firestore, Gemini)
- Images not loading: Verify
next.config.tsallowsstorage.googleapis.comand your public bucket paths are correct. - No gallery items: Confirm the function is writing to Firestore
optimized_imagesand your web app is pointed at the same project. - Gemini errors: Ensure the
AI_APIsecret exists, the service account can access it, and the modelgemini-2.0-flashis available in your region. - Permissions: Check IAM for Functions service account on Storage and Secret Manager.
- Cloud Functions, Storage, Firestore, and Generative AI incur costs. Set appropriate quotas and budgets.
- The function sets
maxInstances: 10globally andmaxInstances: 5for the optimizer. Adjust to control spend.