Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ FIREBASE_MEASUREMENT_ID=<your_firebase_measurement_id>
GEMINI_API_KEY=<your_gemini_api_key>

FIREBASE_SERVICE_ACCOUNT=<path_to_your_firebase_service_account_json>

RAZORPAY_KEY_ID=<your_razorpay_key_id>
RAZORPAY_KEY_SECRET=<your_razorpay_key_secret>
RAZORPAY_WEBHOOK_SECRET=<your_razorpay_webhook_secret>
157 changes: 84 additions & 73 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,28 @@
# GreenPlate — Backend (FastAPI)

A lightweight FastAPI backend for managing college food stalls, staff, and menus.
It uses Firebase Authentication + Firestore and supports AI-assisted menu extraction (Google Gemini).
A compact FastAPI backend for managing college food stalls, staff, menus and orders.

Purpose
- Manage stalls, staff and menus for colleges.
- Staff can upload and edit menus for their assigned stall only.
- Managers can add/remove staff for their stall.
- Optional image-based menu extraction returns JSON of item names and prices.

Tech stack
- Python 3.13, FastAPI, Uvicorn
- Firebase Authentication (pyrebase + firebase_admin) and Firestore
- Google Generative AI (Gemini) for menu image parsing (optional)
- Docker (Dockerfile + compose.yaml) provided
Quick summary
- Auth: Firebase ID tokens (verify with firebase_admin).
- Data: Firestore collections (colleges, stalls, staffs, users, menu_items).
- AI menu extraction: optional (Google Gemini) — returns JSON list of {name, price, description}.
- Payments: Razorpay integration for creating orders and a webhook to record payments.
- Docker ready (Dockerfile + compose.yaml).

Quick start (local)
1. Create and activate a virtual environment:
1. Create & activate a virtualenv:

```bash
python -m venv .venv && source .venv/bin/activate
```

2. Install dependencies:
2. Install deps:

```bash
pip install -r requirements.txt
```

3. Provide Firebase service account and env vars.
- Recommended: set FIREBASE_SERVICE_ACCOUNT to the full JSON content of the service account file.
- Alternatively mount a local file into the container (see Docker section) and set FIREBASE_SERVICE_ACCOUNT to its path.
3. Provide Firebase & other secrets (recommended via `.env` or env vars). Important vars listed below.

4. Run the app:

Expand All @@ -41,83 +33,102 @@ uvicorn app.app:app --reload --host 0.0.0.0 --port 8000
```

Docker
- Build image and run (from repo root):
- Build and run container:

```bash
docker build -t greenplate-backend .
docker run --rm -p 8000:8000 --env-file .env -v $(pwd)/secrets:/app/secrets:ro greenplate-backend
```

- With Docker Compose (recommended for local development):
- With Compose (repo root):

```bash
docker compose up --build
docker compose down
```

Environment variables (important)
- FIREBASE_SERVICE_ACCOUNT: JSON string or path to the service account key file used by firebase_admin.
- FIREBASE_API_KEY, FIREBASE_AUTH_DOMAIN, FIREBASE_DATABASE_URL, FIREBASE_PROJECT_ID, FIREBASE_STORAGE_BUCKET, FIREBASE_MESSAGING_SENDER_ID, FIREBASE_APP_ID, FIREBASE_MEASUREMENT_ID — used by pyrebase client (see `app/config.py`).
- GEMINI_API_KEY — required only if you use image scanning (`/staff/menu/scan-image`).

Security / Auth headers
- All protected endpoints require a Firebase ID token (not the UID) in the Authorization header:
Authentication
- Use Firebase ID token (NOT the UID). Send in Authorization header as a Bearer token:

```
Authorization: Bearer {idToken}
Authorization: Bearer <idToken>
```

- You can obtain an ID token via the `/login/users` endpoint (or use `get_token.py` for tests).
- If you receive {"message": "Authorization header required"} or 401, ensure the header key is exactly `Authorization` and the value starts with `Bearer ` followed by the idToken.

Main endpoints (summary)
- GET /health — health check

Auth / User
- POST /signup/users — Sign up a student (payload: email, password, confirm_password)
- POST /login/users — Sign in (returns idToken)
- GET /user/menu — List available menus for the student’s college (requires Bearer idToken)

Staff / Manager
- POST /auth/verify-staff — Verify token and initialize manager account (if email matches a stall email) — returns role, stall_id, college_id
- POST /staff/menu — Upload menu JSON for a stall (staff only; stall_id must match the authenticated staff's stall)
- GET /staff/menu — Get menu for the authenticated staff's stall
- POST /staff/menu/scan-image — Upload an image; AI returns JSON array of items (name, price, description)
- PATCH /staff/menu/{item_id} — Update a menu item (staff/manager for that stall)
- DELETE /staff/menu/{item_id} — Delete a menu item (staff/manager for that stall)

Manager-only
- POST /staff/add-member — Add a staff member (manager only)
- GET /staff/list — List staff members for the manager's stall
- DELETE /staff/{staff_uid} — Remove a staff (manager only)
- PUT /staff/{staff_uid}/email — Update staff email (manager only)

Key behavior / rules
- Stall-level authorization: staff/manager tokens are checked against `staffs` collection. A staff can only modify menu items for their assigned `stall_id`.
- Manager initialization: when a verified token's email matches a stall's email and no staff document exists, the endpoint will create a manager record automatically.
- Menu upload expects JSON matching the Pydantic schema `MenuSchema`:
- stall_id: string (must equal the staff's stall)
- Obtain a test idToken via `/login/users` or the helper `get_token.py` (dev-only).

Key environment variables
- FIREBASE_SERVICE_ACCOUNT: JSON content or path for firebase_admin credentials (required).
- FIREBASE_API_KEY, FIREBASE_AUTH_DOMAIN, FIREBASE_DATABASE_URL, FIREBASE_PROJECT_ID, FIREBASE_STORAGE_BUCKET, FIREBASE_MESSAGING_SENDER_ID, FIREBASE_APP_ID, FIREBASE_MEASUREMENT_ID — used by the auth client and helper scripts.
- GEMINI_API_KEY — optional, required for image-based menu scanning.
- RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET — required for payment order creation.
- RAZORPAY_WEBHOOK_SECRET — secret used to validate incoming Razorpay webhook signatures (used by `/webhook/razorpay`).

Important files
- `app/firebase_init.py` — initializes firebase_admin and exposes `db` (Firestore client).
- `app/auth.py` — verifies tokens and initializes manager records when a manager signs in using the stall email.
- `app/schema.py` — Pydantic models (MenuSchema, MenuItemSchema, MenuScanResponse, CreateOrderSchema, etc.).
- `app/staff.py` — staff routes logic: upload/get/update/delete menus, add staff, image scan.
- `app/manager.py` — manager-only helpers: list staff, remove staff, update staff email.
- `app/user.py` — student-facing: list menus and create payment orders.
- `app/webhook.py` — Razorpay webhook: validates HMAC signature (X-Razorpay-Signature) and saves successful payments to Firestore `orders` collection.
- `get_token.py` — helper to exchange email/password for idToken (dev/test only).

Core rules / behavior (short)
- Staff authorization is enforced by token -> `staffs` lookup. A staff can only manage the stall they belong to (stall_id check).
- Manager init: when a user signs in and their email matches a stall's email (in that college), a manager `staffs` document is created automatically.
- Menu upload expects JSON matching `MenuSchema` (see `app/schema.py`):
- stall_id: string (must equal authenticated staff's stall)
- items: list of {name: str, price: float (>0), description?: str, is_available?: bool}
- Image scan returns a list of extracted items; results should be reviewed before saving (the AI may be imperfect).
- Image scan returns a JSON array of extracted items. Results must be reviewed before saving.

Testing tokens
- Use `get_token.py` (dev helper) to exchange an existing Firebase user email/password for an idToken.
- Or call POST /login/users to get idToken on successful sign-in.
API (selected endpoints)
- GET /health — health check

Troubleshooting
- Authorization header required: Header missing or malformed. Ensure `Authorization: Bearer <idToken>`.
- 401 Unauthorized: token invalid or expired — re-login to get a fresh idToken.
- 403 Forbidden: trying to modify a stall you don’t belong to or role mismatch.
- 413 Request Entity Too Large: uploaded image exceeds 5MB limit.
Auth
- POST /auth/verify-staff — Verify staff token; initializes manager if needed.
- POST /auth/verify-student — Verify student token and auto-register student (by college domain).

Useful paths
- API docs (Swagger): http://localhost:8000/docs
User (student)
- GET /user/menu — List menus for the student's college (only active & verified stalls returned).
- POST /user/order/create — Create a Razorpay order (payload: CreateOrderSchema)

Contributing
- Keep secrets out of version control: do not commit `.env` or `secrets/serviceAccountKey.json`.
- See `app/firebase_init.py` to understand service account loading.
Staff / Manager
- POST /staff/add-member — Manager adds a staff (payload: {email})
- GET /staff/list — Manager: list staff for manager's stall
- DELETE /staff/{staff_uid} — Manager: remove a staff member (must be same stall)
- PUT /staff/{staff_uid}/email — Manager: change a staff's email (creates user if needed)

Menu management (staff/manager)
- POST /staff/menu — Upload menu JSON for the authenticated staff's stall (MenuSchema)
- GET /staff/menu — Get menu for authenticated staff's stall
- POST /staff/menu/scan-image — Upload image (JPEG/PNG, <5MB) → returns MenuScanResponse (requires GEMINI_API_KEY)
- PATCH /staff/menu/{item_id} — Update a menu item
- DELETE /staff/menu/{item_id} — Delete a menu item

Webhook
- POST /webhook/razorpay — Receives Razorpay events, validates `X-Razorpay-Signature` using `RAZORPAY_WEBHOOK_SECRET`, and stores successful payments in Firestore `orders` collection. Configure your Razorpay webhook to point to this endpoint.

Testing & troubleshooting
- Swagger UI: http://localhost:8000/docs — use the Authorize button and paste the idToken (Bearer token).
- If you see {"message":"Authorization header required"} or 401: ensure header name is exactly `Authorization` and value starts with `Bearer ` followed by the idToken.
- If token expired or invalid: re-login to get a fresh idToken.

Security notes
- Do NOT commit secrets. The repo includes a `secrets/` folder in .gitignore — keep service account JSON and .env out of VCS.
- The server uses Firestore security via server-side checks: stall_id and college_id are validated in code before writes.

Where to look next (dev pointers)
- To change menu schema, edit `app/schema.py` (Pydantic models used for validation).
- To modify staff authorization behavior, check `app/auth.py` and `app/staff.py:get_staff_details`.
- GEMINI integration is in `app/staff.py` (_extract_menu_from_image) and requires `GEMINI_API_KEY`.

Short checklist for running locally
1. Populate `.env` (or export env vars). Ensure FIREBASE_SERVICE_ACCOUNT is set.
2. pip install -r requirements.txt
3. uvicorn app.app:app --reload --host 0.0.0.0 --port 8000
4. Use Swagger or a REST client. Authorize with `Authorization: Bearer <idToken>`.

License
- See LICENSE in repo root.

Last updated: 2026-01-07
Last updated: 2026-01-11
2 changes: 1 addition & 1 deletion app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,4 @@ async def delete_menu_item_endpoint(
return await delete_menu_item(
item_id,
credentials.credentials
)
)
1 change: 0 additions & 1 deletion app/auth.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# app/auth.py

import os
from fastapi.responses import JSONResponse
from starlette import status
from firebase_admin import auth, firestore
Expand Down
2 changes: 1 addition & 1 deletion app/webhook.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# app/webhook.py

import os
import hmac
import hashlib
import json
from fastapi import APIRouter, Request, HTTPException
from firebase_admin import firestore
from .firebase_init import db
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,5 @@ tqdm==4.67.1
typing-inspection==0.4.2
typing_extensions==4.15.0
uritemplate==4.2.0
urllib3==1.26.20
urllib3==2.6.3
uvicorn==0.40.0
Loading