costco-grocery-list/.github/copilot-instructions.md
2026-01-01 22:55:39 -08:00

8.6 KiB

Costco Grocery List - AI Agent Instructions

Architecture Overview

This is a full-stack grocery list management app with role-based access control (RBAC):

  • Backend: Node.js + Express + PostgreSQL (port 5000)
  • Frontend: React 19 + TypeScript + Vite (port 3000/5173)
  • Deployment: Docker Compose with separate dev/prod configurations

Key Design Patterns

Three-tier RBAC system (viewer, editor, admin):

Middleware chain pattern for protected routes:

router.post("/add", auth, requireRole(ROLES.EDITOR, ROLES.ADMIN), controller.addItem);
  • auth middleware extracts JWT from Authorization: Bearer <token> header
  • requireRole checks if user's role matches allowed roles
  • See backend/routes/list.routes.js for examples

Frontend route protection:

  • <PrivateRoute>: Requires authentication, redirects to /login if no token
  • <RoleGuard allowed={[ROLES.ADMIN]}>: Requires specific role(s), redirects to / if unauthorized
  • Example in frontend/src/App.jsx

Database Schema

PostgreSQL server runs externally - not in Docker Compose. Connection configured in backend/.env via standard environment variables.

Tables (inferred from models, no formal migrations):

  • users: id, username, password (bcrypt hashed), name, role
  • grocery_list: id, item_name, quantity, bought, added_by
  • grocery_history: Junction table tracking which users added which items

Important patterns:

  • No migration system - schema changes are manual SQL
  • Items use case-insensitive matching (ILIKE) to prevent duplicates
  • JOINs with ARRAY_AGG for multi-contributor queries (see backend/models/list.model.js)

Development Workflow

Local Development

# Start all services with hot-reload against LOCAL database
docker-compose -f docker-compose.dev.yml up

# Backend runs nodemon (watches backend/*.js)
# Frontend runs Vite dev server with HMR on port 3000

Key dev setup details:

  • Volume mounts preserve node_modules in containers while syncing source code
  • Backend uses Dockerfile (standard) with npm run dev override
  • Frontend uses Dockerfile.dev with CHOKIDAR_USEPOLLING=true for file watching
  • Both connect to external PostgreSQL server (configured in backend/.env)
  • No database container in compose - DB is managed separately

Production Build

# Local production build (for testing)
docker-compose -f docker-compose.prod.yml up --build

# Actual production uses pre-built images
docker-compose up  # Pulls from private registry

CI/CD Pipeline (Gitea Actions)

See .gitea/workflows/deploy.yml for full workflow:

Build stage (on push to main):

  1. Run backend tests (npm test --if-present)
  2. Build backend image with tags: :latest and :<commit-sha>
  3. Build frontend image with tags: :latest and :<commit-sha>
  4. Push both images to private registry

Deploy stage:

  1. SSH to production server
  2. Upload docker-compose.yml to deployment directory
  3. Pull latest images and restart containers with docker compose up -d
  4. Prune old images

Notify stage:

  • Sends deployment status via webhook

Required secrets:

  • REGISTRY_USER, REGISTRY_PASS: Docker registry credentials
  • DEPLOY_HOST, DEPLOY_USER, DEPLOY_KEY: SSH deployment credentials

Backend Scripts

  • npm run dev: Start with nodemon
  • npm run build: esbuild compilation + copy public assets to dist/
  • npm test: Run Jest tests (currently no tests exist)

Frontend Scripts

  • npm run dev: Vite dev server (port 5173)
  • npm run build: TypeScript compilation + Vite production build

Docker Configurations

docker-compose.yml (production):

  • Pulls pre-built images from private registry
  • Backend on port 5000, frontend on port 3000 (nginx serves on port 80)
  • Requires backend.env and frontend.env files

docker-compose.dev.yml (local development):

  • Builds images locally from Dockerfile/Dockerfile.dev
  • Volume mounts for hot-reload: ./backend:/app and ./frontend:/app
  • Named volumes preserve node_modules between rebuilds
  • Backend uses backend/.env directly
  • Frontend uses Dockerfile.dev with polling enabled for cross-platform compatibility

docker-compose.prod.yml (local production testing):

  • Builds images locally using production Dockerfiles
  • Backend: Standard Node.js server
  • Frontend: Multi-stage build with nginx serving static files

Configuration & Environment

Backend (backend/.env):

  • Database connection variables (host, user, password, database name)
  • JWT_SECRET: Token signing key
  • ALLOWED_ORIGINS: Comma-separated CORS whitelist (supports static origins + 192.168.*.* IP ranges)
  • PORT: Server port (default 5000)

Frontend (environment variables):

  • VITE_API_URL: Backend base URL

Config accessed via:

Authentication Flow

  1. User logs in → backend returns {token, role, username} (backend/controllers/auth.controller.js)
  2. Frontend stores in localStorage and AuthContext (frontend/src/context/AuthContext.jsx)
  3. Axios interceptor auto-attaches Authorization: Bearer <token> header (frontend/src/api/axios.js)
  4. Backend validates JWT on protected routes (backend/middleware/auth.js)
  5. On 401 "Invalid or expired token" response, frontend clears storage and redirects to login

Critical Conventions

Security Practices

  • Never expose credentials: Do not hardcode or document actual values for JWT_SECRET, database passwords, API keys, or any sensitive configuration
  • No infrastructure details: Avoid documenting specific IP addresses, domain names, deployment paths, or server locations in code or documentation
  • Environment variables: Reference .env files conceptually - never include actual contents
  • Secrets in CI/CD: Document that secrets are required, not their values
  • Code review: Scan all changes for accidentally committed credentials before pushing

Backend

Frontend

  • Mixed JSX/TSX: Some components are .jsx (JavaScript), others .tsx (TypeScript) - maintain existing file extensions
  • API calls: Use centralized api instance from frontend/src/api/axios.js, not raw axios
  • Role checks: Access role from AuthContext, compare with constants from frontend/src/constants/roles.js
  • Navigation: Use React Router's <Navigate> for redirects, not window.location (except in interceptor)

Common Tasks

Add a new protected route:

  1. Backend: Add route with auth + requireRole(...) middleware
  2. Frontend: Add route in frontend/src/App.jsx wrapped in <PrivateRoute> and/or <RoleGuard>

Access user info in backend controller:

const { id, role } = req.user; // Set by auth middleware

Query grocery items with contributors: Use the JOIN pattern in backend/models/list.model.js - aggregates user names via grocery_history table.

Testing

Backend:

  • Jest configured at root level (package.json)
  • Currently no test files exist - testing infrastructure needs development
  • CI/CD runs npm test --if-present but will pass if no tests found
  • Focus area: API endpoint testing (use supertest with Express)

Frontend:

To add backend tests:

  1. Create backend/__tests__/ directory
  2. Use Jest + Supertest pattern for API tests
  3. Mock database calls or use test database