From 30814e66876e00726423a31c699063c428f6f128 Mon Sep 17 00:00:00 2001 From: Harish-Naruto Date: Wed, 24 Jun 2026 19:07:21 +0530 Subject: [PATCH] feat: add docker-compose configurations for full stack development environments --- .gitignore | 1 + DOCKER.md | 235 ++++++++++++++++++ README.md | 90 +++++-- docker-compose.yml | 15 +- .../callofcode.in/.env.local.frontend.example | 6 + docker/callofcode.in/docker-compose.yml | 56 +++++ docker/coc-admin/.env.local.backend.example | 38 +++ docker/coc-admin/.env.local.frontend.example | 8 + docker/coc-admin/docker-compose.yml | 75 ++++++ docker/coc-member/.env.local.backend.example | 31 +++ docker/coc-member/.env.local.frontend.example | 5 + docker/coc-member/docker-compose.yml | 75 ++++++ 12 files changed, 607 insertions(+), 28 deletions(-) create mode 100644 DOCKER.md create mode 100644 docker/callofcode.in/.env.local.frontend.example create mode 100644 docker/callofcode.in/docker-compose.yml create mode 100644 docker/coc-admin/.env.local.backend.example create mode 100644 docker/coc-admin/.env.local.frontend.example create mode 100644 docker/coc-admin/docker-compose.yml create mode 100644 docker/coc-member/.env.local.backend.example create mode 100644 docker/coc-member/.env.local.frontend.example create mode 100644 docker/coc-member/docker-compose.yml diff --git a/.gitignore b/.gitignore index 11aecae..4d22288 100644 --- a/.gitignore +++ b/.gitignore @@ -67,6 +67,7 @@ web_modules/ # dotenv environment variable files .env .env.* +!.env.local.*.example !.env.example # parcel-bundler cache (https://parceljs.org/) diff --git a/DOCKER.md b/DOCKER.md new file mode 100644 index 0000000..d0623c1 --- /dev/null +++ b/DOCKER.md @@ -0,0 +1,235 @@ +# 🐳 Docker — Local Development Guide + +This guide covers everything you need to run any part of the Call of Code platform locally using Docker Compose. + +--- + +## Overview + +The repository ships with **four Docker Compose configurations**: + +| File | Purpose | Services | +|------|---------|----------| +| `docker-compose.yml` | **API-only** dev (standalone) | `coc-api` | +| `docker/coc-member/docker-compose.yml` | Full **COC Member** stack | `coc-api` + `server` + `web` | +| `docker/coc-admin/docker-compose.yml` | Full **COC Admin** stack | `coc-api` + `server` + `web` | +| `docker/callofcode.in/docker-compose.yml` | Full **callofcode.in** website stack | `coc-api` + `frontend` | + +> All platform stacks build `coc-api` locally from the repo root `Dockerfile` and pull their respective frontend/backend images from Docker Hub. + +--- + +## Prerequisites + +- [Docker Desktop](https://docs.docker.com/get-docker/) or Docker Engine + Docker Compose plugin (v2.22+) +- Git + +No need to install Bun, Node, or any other runtime locally — everything runs inside containers. + +--- + +## Environment Setup + +### 1. API environment (required by all stacks) + +Copy the example and fill in your Supabase credentials: + +```bash +cp .env.example .env.local +``` + +| Variable | Description | +|----------|-------------| +| `DATABASE_URL` | Supabase connection-pooling URL (Prisma runtime) | +| `DIRECT_URL` | Direct DB connection URL (Prisma Migrate) | +| `SUPABASE_URL` | Your Supabase project URL | +| `SUPABASE_SERVICE_ROLE_KEY` | Supabase service-role secret key | + +### 2. Platform-specific environments + +Each platform directory ships **example files** with all variables documented and placeholder values. Copy and fill in only the secrets you need: + +```bash +# COC Member +cp docker/coc-member/.env.local.backend.example docker/coc-member/.env.local.backend +cp docker/coc-member/.env.local.frontend.example docker/coc-member/.env.local.frontend + +# COC Admin +cp docker/coc-admin/.env.local.backend.example docker/coc-admin/.env.local.backend +cp docker/coc-admin/.env.local.frontend.example docker/coc-admin/.env.local.frontend + +# callofcode.in +cp docker/callofcode.in/.env.local.frontend.example docker/callofcode.in/.env.local.frontend +``` + +| Platform | File | Key Variables | +|----------|------|---------------| +| All backends | `.env.local.backend` | `JWT_SECRET`, `REFRESH_SECRET`, `RESEND_API_KEY` | +| `coc-admin` backend | `.env.local.backend` | + `WHATSAPP_LINK`, `DISCORD_LINK` | +| `coc-member` frontend | `.env.local.frontend` | `VITE_API_URL` | +| `coc-admin` frontend | `.env.local.frontend` | `VITE_API_URL`, `VITE_GIF_URL` | +| `callofcode.in` frontend | `.env.local.frontend` | `API_BASE_URL`, `GITHUB_TOKEN` (optional) | + +> **Never commit real secrets.** All `*.env.local*` files are git-ignored by default. + +--- + +## Running the Stacks + +### API-only (standalone development) + +Use this when you're only working on the `coc-api` itself: + +```bash +# Standard mode +docker compose up --build + +# Watch mode — Docker syncs src/ changes and restarts bun automatically +docker compose watch +``` + +The API will be available at **http://localhost:3000** + +Health check: **http://localhost:3000/health** + +--- + +### COC Member stack + +```bash +cd docker/coc-member +docker compose up --build +``` + +| Service | URL | +|---------|-----| +| `coc-api` | http://localhost:3000 | +| `server` (member backend) | http://localhost:8000 | +| `web` (member frontend) | http://localhost:5173 | + +--- + +### COC Admin stack + +```bash +cd docker/coc-admin +docker compose up --build +``` + +| Service | URL | +|---------|-----| +| `coc-api` | http://localhost:3000 | +| `server` (admin backend) | http://localhost:8000 | +| `web` (admin frontend) | http://localhost:5173 | + +--- + +### callofcode.in website + +```bash +cd docker/callofcode.in +docker compose up --build +``` + +| Service | URL | +|---------|-----| +| `coc-api` | http://localhost:3000 | +| `frontend` | http://localhost:3001 | + +--- + +## Startup Order & Health Checks + +All stacks use health-checked `depends_on` to ensure correct startup ordering: + +``` +coc-api (healthy) → server/backend (healthy) → web/frontend +``` + +The `coc-api` health check polls `GET /health` every 30 seconds with a 15-second grace period on startup. Downstream services only start once `coc-api` reports healthy. + +--- + +## Hot Reload (API-only stack) + +The root `docker-compose.yml` supports `docker compose watch` with the following rules: + +| Path changed | Action | +|---|---| +| `src/**` | **Sync** — files copied into container instantly; `bun --watch` picks up the change | +| `package.json` / `bun.lock` | **Rebuild** — full image rebuild to reinstall dependencies | +| `prisma/**` | **Rebuild** — triggers `prisma generate` on next container start | + +```bash +# Start in watch mode (preferred for API development) +docker compose watch +``` + +--- + +## Common Commands + +```bash +# Start in the background (detached) +docker compose up -d --build + +# Follow logs +docker compose logs -f + +# Follow logs for a specific service +docker compose logs -f coc-api + +# Stop all containers +docker compose down + +# Stop and remove volumes (clean slate) +docker compose down -v + +# Rebuild a single service without restarting others +docker compose build coc-api + +# Open a shell inside a running container +docker compose exec coc-api sh + +# Run Prisma Studio (from inside the coc-api container) +docker compose exec coc-api bunx prisma studio +``` + +--- + +## Dockerfile Stages + +The root `Dockerfile` uses a multi-stage build: + +| Stage | Target | Purpose | +|-------|--------|---------| +| `deps` | — | Installs production dependencies only | +| `builder` | `target: builder` | Installs all deps + generates Prisma client; **used by all dev compose files** | +| `runner` | — | Lean production image; runs as non-root `cocuser` | + +The dev compose files use `target: builder` so that devDependencies and the Prisma CLI are available inside the container. + +--- + +## Troubleshooting + +**`open Dockerfile: no such file or directory`** +The `dockerfile:` path in compose is relative to the `context`, not the compose file. Our configs set `context: ../..` (repo root) so `dockerfile: Dockerfile` resolves correctly. + +**Port already in use** +Set a custom port via the `PORT` env variable before running: +```bash +PORT=3001 docker compose up +``` + +**Prisma migration errors on startup** +The `coc-api` container runs `prisma migrate deploy` on every start. If the DB is unreachable, the container will exit. Verify your `DATABASE_URL` and `DIRECT_URL` in `.env.local`. + +**Container exits immediately after `healthy`** +Check logs with `docker compose logs coc-api`. Common causes: missing env vars or a failed migration. + +**Stale node_modules in container** +The anonymous volume `/app/node_modules` is intentionally excluded from the host mount to avoid cross-OS binary conflicts. If you change `package.json`, let Docker rebuild: +```bash +docker compose build --no-cache coc-api +``` diff --git a/README.md b/README.md index defde45..e8f198e 100644 --- a/README.md +++ b/README.md @@ -5,17 +5,11 @@ This repository contains the shared Express.js API for the backends of our Codin ## 📂 Folder Structure ``` -/ +COC-API/ ├── prisma/ # Prisma schema and migration files │ ├── schema.prisma │ └── migrations/ # auto-generated by `bun run migrate` │ -├── seed/ -│ └── dump.sql # local seed file loaded by the setup script -│ -├── scripts/ -│ └── setup-local.sh # automates local environment bring-up -│ ├── src/ │ ├── config/ # environment/configuration loaders │ │ @@ -75,7 +69,21 @@ This repository contains the shared Express.js API for the backends of our Codin │ ├── SiteContent.test.ts │ └── imageUtils.test.ts │ -├── .env.example # template for environment variables +├── docker/ # Platform-specific Docker Compose stacks +│ ├── coc-member/ # Full COC Member platform stack +│ │ ├── docker-compose.yml +│ │ ├── .env.local.backend.example +│ │ └── .env.local.frontend.example +│ ├── coc-admin/ # Full COC Admin platform stack +│ │ ├── docker-compose.yml +│ │ ├── .env.local.backend.example +│ │ └── .env.local.frontend.example +│ └── callofcode.in/ # Full callofcode.in website stack +│ ├── docker-compose.yml +│ └── .env.local.frontend.example +│ +├── .env.example # API environment variable template +├── docker-compose.yml # API-only dev stack (standalone) ├── package.json └── tsconfig.json ``` @@ -84,8 +92,8 @@ This repository contains the shared Express.js API for the backends of our Codin ### Prerequisites -- Install [Bun](https://bun.sh/) on your machine. -- Install [Docker & Docker Compose](https://docs.docker.com/get-docker/) for the local database. +- [Docker Desktop](https://docs.docker.com/get-docker/) or Docker Engine + Compose plugin v2.22+ — **required for all workflows** +- [Bun](https://bun.sh/) — only needed if you want to run the API outside Docker ### 1. Clone the repo @@ -94,40 +102,68 @@ git clone https://github.com/call-0f-code/COC-API.git cd COC-API ``` -### 2. Install dependencies +### 2. Configure environment + +**API credentials** (required by all stacks): ```bash -bun install +cp .env.example .env.local ``` -### 3. Configure environment +| Variable | Description | +|----------|-------------| +| `DATABASE_URL` | Supabase connection-pooling URL (Prisma runtime) | +| `DIRECT_URL` | Direct DB connection URL (Prisma Migrate) | +| `SUPABASE_URL` | Your Supabase project URL | +| `SUPABASE_SERVICE_ROLE_KEY` | Supabase service-role secret key | +| `NODE_ENV` | `development` \| `production` | -Copy `.env.example` to `.env` and fill in the required values: +**Platform-specific credentials** (only needed for the stack you're running): ```bash -cp .env.example .env +# COC Member +cp docker/coc-member/.env.local.backend.example docker/coc-member/.env.local.backend +cp docker/coc-member/.env.local.frontend.example docker/coc-member/.env.local.frontend + +# COC Admin +cp docker/coc-admin/.env.local.backend.example docker/coc-admin/.env.local.backend +cp docker/coc-admin/.env.local.frontend.example docker/coc-admin/.env.local.frontend + +# callofcode.in +cp docker/callofcode.in/.env.local.frontend.example docker/callofcode.in/.env.local.frontend ``` -Key variables: +Edit each file and replace the `change_me_*` and `your_*` placeholders with real values. See [DOCKER.md](DOCKER.md) for a full variable reference. + +### 3. Install dependencies (local dev only) -| Variable | Description | -| ------------------------- | --------------------------------------------------------------------------- | -| `DATABASE_URL` | Supabase connection-pooling URL (used by Prisma at runtime) | -| `DIRECT_URL` | Direct DB connection URL (used by Prisma Migrate) | -| `SUPABASE_URL` | Your Supabase project URL | -| `SUPABASE_SERVICE_ROLE_KEY` | Supabase service-role secret key | -| `SESSION_POOLER` | Session-mode pooler URL — used by `setup-local.sh` for `pg_dump` (IPv4) | -| `NODE_ENV` | `development` \| `production` | +Only needed if you are running the API outside Docker: + +```bash +bun install +``` ### 4. Local development (Docker) -The setup script starts a local Postgres container and seeds it automatically: +The project ships **four Docker Compose stacks**. Choose the one you need: ```bash -bun run local +# API only (for coc-api development) +docker compose up --build + +# Full COC Member platform +cd docker/coc-member && docker compose up --build + +# Full COC Admin platform +cd docker/coc-admin && docker compose up --build + +# Full callofcode.in website +cd docker/callofcode.in && docker compose up --build ``` -> See [LOCAL_DEVELOPMENT.md](LOCAL_DEVELOPMENT.md) for full details, flags, and troubleshooting. +All stacks automatically run Prisma migrations on startup and use health checks to ensure correct service startup order. + +> 📖 See [DOCKER.md](DOCKER.md) for full details — environment files, watch mode, port mappings, common commands, and troubleshooting. ### 5. Initialize / run migrations diff --git a/docker-compose.yml b/docker-compose.yml index bf2d25b..00c06bb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,6 @@ services: context: . dockerfile: Dockerfile target: builder # Use builder stage in dev (has devDeps + bun watch) - container_name: coc-api-dev restart: unless-stopped ports: - "${PORT:-3000}:3000" @@ -23,6 +22,20 @@ services: sh -c "bunx prisma generate && bunx prisma migrate deploy && bun --watch src/server.ts" + develop: + watch: + # Sync source changes into container instantly + - path: ./src + action: sync + target: /app/src + # Rebuild if package.json or lock file changes + - path: ./package.json + action: rebuild + - path: ./bun.lock + action: rebuild + # Rebuild if Prisma schema changes (needs prisma generate) + - path: ./prisma + action: rebuild healthcheck: test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/health"] interval: 30s diff --git a/docker/callofcode.in/.env.local.frontend.example b/docker/callofcode.in/.env.local.frontend.example new file mode 100644 index 0000000..754c0bf --- /dev/null +++ b/docker/callofcode.in/.env.local.frontend.example @@ -0,0 +1,6 @@ + +# Points to the coc-api service on the shared Docker network +API_BASE_URL=http://coc-api:3000 + +# GitHub token for fetching contribution data (optional) +GITHUB_TOKEN=your_github_token_here diff --git a/docker/callofcode.in/docker-compose.yml b/docker/callofcode.in/docker-compose.yml new file mode 100644 index 0000000..516ba25 --- /dev/null +++ b/docker/callofcode.in/docker-compose.yml @@ -0,0 +1,56 @@ +name: coc-callofcode + +services: + + coc-api: + image: coc-api + build: + context: ../.. + dockerfile: Dockerfile + target: builder # Use builder stage in dev (has devDeps + bun watch) + restart: unless-stopped + ports: + - "${PORT:-3000}:3000" + env_file: + - ../../.env.local # Local dev env vars (Supabase connection strings, etc.) + environment: + NODE_ENV: development + volumes: + # Mount source for live-reload (everything except node_modules) + - ../../:/app + - /app/node_modules + - /app/node_modules/.prisma + command: > + sh -c "bunx prisma generate && + bunx prisma migrate deploy && + bun --watch src/server.ts" + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 15s + networks: + - coc-local + + frontend: + image: callofcode07/callofcode:latest + pull_policy: missing + restart: unless-stopped + ports: + - "3001:3001" + env_file: + - .env.local.frontend + environment: + - NODE_ENV=development + - API_BASE_URL=http://coc-api:3000 + depends_on: + coc-api: + condition: service_healthy + networks: + - coc-local + command: ["npm", "run", "dev"] + +networks: + coc-local: + driver: bridge diff --git a/docker/coc-admin/.env.local.backend.example b/docker/coc-admin/.env.local.backend.example new file mode 100644 index 0000000..2dfc30a --- /dev/null +++ b/docker/coc-admin/.env.local.backend.example @@ -0,0 +1,38 @@ +## local setup ENV — COC Admin Backend +## Copy this file: cp .env.local.backend.example .env.local.backend + +# URL to the coc-api service on the Docker network +API_URL=http://coc-api:3000 + +# BCrypt salt rounds +SALTING=8 + +# JWT signing secrets — change these to strong random values +JWT_SECRET=change_me_admin_jwt_secret +REFRESH_SECRET=change_me_admin_refresh_secret + +# Token TTL (in minutes / days) +ACCESS_TTL=15 +REFRESH_TTL=7 + +# Rate limiting +RATE_LIMIT_WINDOW_MINUTES=15 +RATE_LIMIT_MAX_REQUESTS=100 + +# CORS — set to your frontend's origin +ALLOWED_ORIGINS=http://localhost:5173 + +# Email service (Resend) +EMAIL_ID=your_sender@example.com +CONTACT_EMAIL_ID=callofcode07@gmail.com +RESEND_API_KEY=your_resend_api_key_here + +# Community links (shown in emails / UI) +WHATSAPP_LINK=https://chat.whatsapp.com/your-group +DISCORD_LINK=https://discord.gg/your-server + +# Keep as production so refresh token cookie works correctly +NODE_ENV=production + +# Server port +PORT=8000 diff --git a/docker/coc-admin/.env.local.frontend.example b/docker/coc-admin/.env.local.frontend.example new file mode 100644 index 0000000..3d481d9 --- /dev/null +++ b/docker/coc-admin/.env.local.frontend.example @@ -0,0 +1,8 @@ +## local setup ENV — COC Admin Frontend +## Copy this file: cp .env.local.frontend.example .env.local.frontend + +# Points to the admin backend on the host +VITE_API_URL=http://localhost:8000 + +# Fun GIF shown on certain pages +VITE_GIF_URL=https://media.tenor.com/WqfUsqUQWRsAAAAC/chala-ja.gif diff --git a/docker/coc-admin/docker-compose.yml b/docker/coc-admin/docker-compose.yml new file mode 100644 index 0000000..974949d --- /dev/null +++ b/docker/coc-admin/docker-compose.yml @@ -0,0 +1,75 @@ +name: coc-admin + +services: + + coc-api: + image: coc-api + build: + context: ../.. + dockerfile: Dockerfile + target: builder # Use builder stage in dev (has devDeps + bun watch) + restart: unless-stopped + ports: + - "${PORT:-3000}:3000" + env_file: + - ../../.env.local # Local dev env vars (Supabase connection strings, etc.) + environment: + NODE_ENV: development + volumes: + # Mount source for live-reload (everything except node_modules) + - ../../:/app + - /app/node_modules + - /app/node_modules/.prisma + command: > + sh -c "bunx prisma generate && + bunx prisma migrate deploy && + bun --watch src/server.ts" + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 15s + networks: + - coc-local + + server: + image: callofcode07/coc-admin-backend:latest + pull_policy: missing + restart: unless-stopped + ports: + - "8000:8000" + env_file: + - .env.local.backend + environment: + - PORT=8000 + - API_URL=http://coc-api:3000 + depends_on: + coc-api: + condition: service_healthy + healthcheck: + test: ["CMD", "wget", "-qO-", "http://localhost:8000/health"] + interval: 30s + timeout: 3s + retries: 3 + start_period: 10s + networks: + - coc-local + + web: + image: callofcode07/coc-admin-frontend:latest + pull_policy: missing + restart: unless-stopped + ports: + - "5173:5173" + env_file: + - .env.local.frontend + depends_on: + server: + condition: service_healthy + networks: + - coc-local + +networks: + coc-local: + driver: bridge diff --git a/docker/coc-member/.env.local.backend.example b/docker/coc-member/.env.local.backend.example new file mode 100644 index 0000000..d6f5ece --- /dev/null +++ b/docker/coc-member/.env.local.backend.example @@ -0,0 +1,31 @@ +## local setup ENV — COC Member Backend +## Copy this file: cp .env.local.backend.example .env.local.backend + +# URL to the coc-api service on the Docker network +API_URL=http://coc-api:3000 + +# BCrypt salt rounds +SALTING=8 + +# JWT signing secrets — change these to strong random values +JWT_SECRET=change_me_member_jwt_secret +REFRESH_SECRET=change_me_member_refresh_secret + +# Token TTL (in minutes / days) +ACCESS_TTL=15 +REFRESH_TTL=7 + +# Rate limiting +RATE_LIMIT_WINDOW_MINUTES=1 +RATE_LIMIT_MAX_REQUESTS=1000 + +# CORS — set to your frontend's origin +ALLOWED_ORIGINS=http://localhost:5173 + +# Email service (Resend) +EMAIL_ID=your_sender@example.com +CONTACT_EMAIL_ID=callofcode07@gmail.com +RESEND_API_KEY=your_resend_api_key_here + +# Keep as production so refresh token cookie works correctly +NODE_ENV=production diff --git a/docker/coc-member/.env.local.frontend.example b/docker/coc-member/.env.local.frontend.example new file mode 100644 index 0000000..e2d96e9 --- /dev/null +++ b/docker/coc-member/.env.local.frontend.example @@ -0,0 +1,5 @@ +## local setup ENV — COC Member Frontend +## Copy this file: cp .env.local.frontend.example .env.local.frontend + +# Points to the member backend on the host +VITE_API_URL=http://localhost:8000 diff --git a/docker/coc-member/docker-compose.yml b/docker/coc-member/docker-compose.yml new file mode 100644 index 0000000..814c0cc --- /dev/null +++ b/docker/coc-member/docker-compose.yml @@ -0,0 +1,75 @@ +name: coc-member + +services: + + coc-api: + image: coc-api + build: + context: ../.. + dockerfile: Dockerfile + target: builder # Use builder stage in dev (has devDeps + bun watch) + restart: unless-stopped + ports: + - "${PORT:-3000}:3000" + env_file: + - ../../.env.local # Local dev env vars (Supabase connection strings, etc.) + environment: + NODE_ENV: development + volumes: + # Mount source for live-reload (everything except node_modules) + - ../../:/app + - /app/node_modules + - /app/node_modules/.prisma + command: > + sh -c "bunx prisma generate && + bunx prisma migrate deploy && + bun --watch src/server.ts" + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 15s + networks: + - coc-local + + server: + image: callofcode07/coc-member-backend:latest + pull_policy: missing + restart: unless-stopped + ports: + - "8000:8000" + env_file: + - .env.local.backend + environment: + - PORT=8000 + - API_URL=http://coc-api:3000 + depends_on: + coc-api: + condition: service_healthy + healthcheck: + test: ["CMD", "wget", "-qO-", "http://localhost:8000/health"] + interval: 30s + timeout: 3s + retries: 3 + start_period: 10s + networks: + - coc-local + + web: + image: callofcode07/coc-member-frontend:latest + pull_policy: missing + restart: unless-stopped + ports: + - "5173:5173" + env_file: + - .env.local.frontend + depends_on: + server: + condition: service_healthy + networks: + - coc-local + +networks: + coc-local: + driver: bridge