Skip to content

Repository files navigation

Team 7 Personal Network Graph

A full-stack relationship graph app for CS 144. The client is a Vite React SPA, the API is Express, authentication is Firebase Auth, user data is stored in Firestore, and AI/network-chat calls run through the backend.

Prerequisites

  • Node.js 20+
  • npm
  • Docker Desktop, for container testing
  • Firebase project with Email/Password Authentication enabled
  • Firebase/GCP service-account JSON for Firebase Admin
  • Gemini API key from Google AI Studio, for AI chat/follow-up features

Project Layout

team7/
├── client/                 # Vite React frontend
│   ├── src/
│   ├── public/
│   ├── .env.example        # browser-safe Firebase/Vite values
│   └── package.json
├── server/                 # Express backend
│   ├── lib/
│   ├── routes/
│   ├── .env.example        # backend secrets and runtime config
│   └── package.json
├── k8s/                    # Kubernetes manifests
├── Dockerfile
├── .dockerignore
├── .env.example            # env file index
└── package.json            # root scripts

Environment Files

Real env files are ignored by git. Create them once:

cp client/.env.example client/.env
cp server/.env.example server/.env

client/.env

Put only browser-safe Firebase Web App values here. Find these in Firebase Console -> Project settings -> General -> Your apps -> Web app.

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_MEASUREMENT_ID=...
VITE_API_BASE_URL=http://localhost:3001

Do not put Firebase Admin credentials, service-account JSON, private keys, or GEMINI_API_KEY in client/.env. Vite exposes VITE_* values to browser JavaScript.

server/.env

Put backend-only config and secrets here.

NODE_ENV=development
PORT=3001
CLIENT_ORIGIN=http://localhost:5173
ALLOWED_ORIGINS=http://localhost:5173,http://127.0.0.1:5173,http://localhost:3000,http://127.0.0.1:3000
FIREBASE_PROJECT_ID=...
FIREBASE_CLIENT_EMAIL=...
FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
GEMINI_API_KEY=...
GEMINI_MODELS=gemini-3.5-flash,gemini-3-flash-preview,gemini-3.1-flash-lite,gemini-2.5-flash,gemini-2.5-flash-lite,gemini-3.1-pro-preview,gemini-2.5-pro
GEMINI_API_VERSION=v1beta
GEMINI_TIMEOUT_MS=12000
GEMINI_MAX_OUTPUT_TOKENS=1024

Map Firebase service-account JSON fields like this:

  • project_id -> FIREBASE_PROJECT_ID
  • client_email -> FIREBASE_CLIENT_EMAIL
  • private_key -> FIREBASE_PRIVATE_KEY

Keep FIREBASE_PRIVATE_KEY as one quoted line with escaped \n characters. GEMINI_API_KEY belongs only in server/.env. GEMINI_MODELS is tried in order when Google's API reports temporary model overload, an empty candidate, or a truncated candidate. GEMINI_MAX_OUTPUT_TOKENS controls the normal Gemini answer budget; retries can temporarily expand it up to 4096 tokens so chat messages do not get cut off.

Install

From the repository root:

npm install
npm install --prefix client
npm install --prefix server

Run Locally

Start both Vite and Express:

npm run dev

Local URLs:

Useful single-process commands:

npm run dev:client
npm run dev:server

Verify Locally

npm run lint
npm run build --prefix client

Then open the frontend, sign up or sign in, and confirm:

  • contacts can be added, edited, and deleted
  • Meta Instagram followers/following JSON exports can be bulk imported into contacts without reimporting duplicate handles
  • graph nodes can be dragged and reset
  • network chat responds and marks whether the answer was made by Gemini
  • sign out works

Docker

The Dockerfile builds the React client inside the image, copies the built SPA into the Express server, and runs production Express on port 3000.

Important env split:

  • client/.env is read at Docker build time because Vite bakes Firebase Web App VITE_* values into the browser bundle.
  • VITE_API_BASE_URL is intentionally not baked into Docker. Production uses same-origin API calls on localhost:3000.
  • server/.env is read at Docker run time through --env-file server/.env.

Build:

npm run docker:build

npm run docker:build passes only browser-safe Firebase Web App values from client/.env through a temporary BuildKit secret file. It does not pass Firebase Admin credentials, GEMINI_API_KEY, or the local dev VITE_API_BASE_URL.

Run:

npm run docker:run

This script is equivalent to:

docker run --env-file server/.env -e NODE_ENV=production -e PORT=3000 -p 3000:3000 team7:latest

Docker URLs:

Verify from another terminal:

curl http://localhost:3000/health
curl http://localhost:3000/api/v1/health

Stop the container with Ctrl+C if using npm run docker:run.

For detached testing:

docker run --name team7-check --env-file server/.env -e NODE_ENV=production -e PORT=3000 -p 3000:3000 -d team7:latest
docker ps --filter name=team7-check
docker logs team7-check
curl http://localhost:3000/health
docker rm -f team7-check

Docker Troubleshooting

  • If port 3000 is busy, stop the old container or run with another host port:
    docker run --env-file server/.env -e NODE_ENV=production -e PORT=3000 -p 3002:3000 team7:latest
  • If protected API routes return 503, Firebase Admin is not configured. Check server/.env.
  • If Firebase Admin reports a private-key parse error, make sure FIREBASE_PRIVATE_KEY is one quoted line with escaped \n characters.
  • If Gemini answers show as local fallback, make sure GEMINI_API_KEY is set in server/.env and restart the server/container.
  • Real env files are excluded by .dockerignore; Docker receives secrets only through --env-file server/.env at runtime.

Production Notes

  • Docker/GKE should run on container port 3000.
  • Local Express development defaults to 3001.
  • The built SPA is served by Express in production, including non-API route fallback to index.html.
  • Kubernetes manifests live in k8s/.

Deploy to GKE

Production runs on Google Kubernetes Engine. The full step-by-step guide (secrets, config map, service account, ingress, managed certificate, scaling, and self-healing) is in k8s/README.md. The short path:

# 1. Build and push the image to GCR (replace PROJECT_ID)
docker build --secret id=client_env,src=client/.env.production \
  -t gcr.io/PROJECT_ID/team7:latest .
docker push gcr.io/PROJECT_ID/team7:latest

# 2. Point kubectl at the cluster
gcloud container clusters get-credentials CLUSTER_NAME --zone ZONE --project PROJECT_ID

# 3. Apply manifests (namespace first, then the rest)
kubectl apply -f k8s/namespace.yaml
kubectl create secret generic firebase-secrets \
  --from-literal=project_id=... --from-literal=client_email=... \
  --from-literal=private_key=... -n team7
kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/serviceaccount.yaml
sed 's/PROJECT_ID/YOUR_PROJECT_ID/g' k8s/deployment.yaml | kubectl apply -f -
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/managed-certificate.yaml
kubectl apply -f k8s/ingress.yaml

# 4. Verify the rollout, scaling, and self-healing
kubectl rollout status deployment/team7-app -n team7
kubectl get pods -n team7
kubectl scale deployment team7-app --replicas=3 -n team7   # manual scaling

Continuous deployment

.github/workflows/deploy.yml builds and pushes the image, runs lint/tests, then rolls the new image onto GKE on every push to main. It requires these GitHub repository secrets: GCP_PROJECT_ID, GCP_SA_KEY, GKE_CLUSTER_NAME, GKE_ZONE, and CLIENT_ENV_PRODUCTION.

Common Scripts

npm run dev            # client + server
npm run dev:client     # Vite only
npm run dev:server     # Express only
npm run lint           # client ESLint
npm run build          # client build + server build script
npm run build:client   # Vite production build
npm run docker:build   # build team7:latest
npm run docker:run     # run team7:latest on localhost:3000

About

CS 144 Final Project

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages