From 7b4d1586c622914ed9585c74215b821add13a5e1 Mon Sep 17 00:00:00 2001 From: AtherBilal Date: Wed, 1 Apr 2026 11:31:51 -0500 Subject: [PATCH] feat: add Docker Compose for single-command local development Self-hosters and contributors can now run both services with `docker compose up` instead of installing Node, Python, and uv separately. Includes Dockerfiles for core-api and core-web with volume mounts for hot reload during development. Co-Authored-By: Claude Opus 4.6 (1M context) --- .env.example | 82 ++++++++++++++++++++++++++++++++++++++++++ README.md | 30 ++++++++++++++++ core-api/.dockerignore | 5 +++ core-api/Dockerfile | 23 ++++++++++++ core-web/.dockerignore | 4 +++ core-web/Dockerfile | 21 +++++++++++ docker-compose.yml | 33 +++++++++++++++++ 7 files changed, 198 insertions(+) create mode 100644 .env.example create mode 100644 core-api/.dockerignore create mode 100644 core-api/Dockerfile create mode 100644 core-web/.dockerignore create mode 100644 core-web/Dockerfile create mode 100644 docker-compose.yml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e1d9bda --- /dev/null +++ b/.env.example @@ -0,0 +1,82 @@ +# ============================================================================== +# Core — Docker Compose Environment Variables +# ============================================================================== +# Copy this file to .env and fill in the values. +# Used by `docker compose up` to configure both the API and frontend. +# +# For non-Docker setups, use core-api/.env and core-web/.env instead. +# ============================================================================== + +# ------------------------------------------------------------------------------ +# [REQUIRED] Supabase +# ------------------------------------------------------------------------------ +# Create a project at https://supabase.com and grab these from Settings > API +SUPABASE_URL=https://your-project.supabase.co +SUPABASE_ANON_KEY=your-anon-key +SUPABASE_SERVICE_ROLE_KEY=your-service-role-key +SUPABASE_JWT_SECRET=your-jwt-secret + +# Frontend Supabase config (must match the values above) +VITE_SUPABASE_URL=https://your-project.supabase.co +VITE_SUPABASE_ANON_KEY=your-anon-key + +# ------------------------------------------------------------------------------ +# [REQUIRED] API +# ------------------------------------------------------------------------------ +API_ENV=development +DEBUG=false +# If you override CORE_WEB_PORT, update this to match (e.g. http://localhost:4000) +FRONTEND_URL=http://localhost:3000 +# If you override CORE_API_PORT, update this to match (e.g. http://localhost:9000/api) +VITE_API_URL=http://localhost:8000/api +# Comma-separated CORS origins. Only needed if running on non-default ports. +# ALLOWED_ORIGINS_ENV=http://localhost:4000 + +# ------------------------------------------------------------------------------ +# [OPTIONAL] Email/Password Auth — Self-hosted +# ------------------------------------------------------------------------------ +# Set to "true" to enable email/password sign-in (skips OAuth setup). +# Tip: disable "Enable email confirmations" in Supabase (Auth > Settings) +# to skip the verification email step during local development. +# VITE_ENABLE_EMAIL_AUTH=true + +# ------------------------------------------------------------------------------ +# [OPTIONAL] Sentry — Error Tracking +# ------------------------------------------------------------------------------ +# SENTRY_DSN= +# VITE_SENTRY_DSN= + +# ------------------------------------------------------------------------------ +# [OPTIONAL] PostHog — Analytics +# ------------------------------------------------------------------------------ +# VITE_POSTHOG_KEY= +# VITE_POSTHOG_HOST= + +# ------------------------------------------------------------------------------ +# [FEATURE] AI Chat — OpenAI / Anthropic +# ------------------------------------------------------------------------------ +# OPENAI_API_KEY=sk-... +# ANTHROPIC_API_KEY=sk-ant-... + +# ------------------------------------------------------------------------------ +# [FEATURE] File Storage — Cloudflare R2 +# ------------------------------------------------------------------------------ +# R2_ACCOUNT_ID=your-account-id +# R2_ACCESS_KEY_ID=your-access-key +# R2_SECRET_ACCESS_KEY=your-secret-key +# R2_BUCKET_NAME=core-os-files +# R2_S3_API=https://your-account-id.r2.cloudflarestorage.com +# R2_PUBLIC_URL=https://files.yourdomain.com + +# ------------------------------------------------------------------------------ +# [FEATURE] Google OAuth — Gmail & Calendar Sync +# ------------------------------------------------------------------------------ +# GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com +# GOOGLE_CLIENT_SECRET=your-client-secret + +# ------------------------------------------------------------------------------ +# [FEATURE] Microsoft OAuth — Outlook & Microsoft 365 Sync +# ------------------------------------------------------------------------------ +# MICROSOFT_CLIENT_ID=your-client-id +# MICROSOFT_CLIENT_SECRET=your-client-secret +# MICROSOFT_TENANT_ID=common diff --git a/README.md b/README.md index d35abdf..5bb64fa 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,36 @@ npm run dev # App runs at http://localhost:5173 ``` +### Alternative: Docker Compose + +Run both services with a single command. Requires [Docker](https://docs.docker.com/get-docker/). + +```bash +# 1. Configure environment (single file for both services) +cp .env.example .env +# Edit .env with your Supabase credentials + +# 2. Set up the database (requires Supabase CLI on host) +cd core-api +supabase link --project-ref YOUR_PROJECT_REF +supabase db push +cd .. + +# 3. Start everything +docker compose up + +# API at http://localhost:8000 +# Frontend at http://localhost:3000 +``` + +Both services run in development mode with **hot reload** — edit files locally and changes appear immediately without restarting containers. + +To customize ports: + +```bash +CORE_API_PORT=9000 CORE_WEB_PORT=4000 docker compose up +``` + ## Architecture ``` diff --git a/core-api/.dockerignore b/core-api/.dockerignore new file mode 100644 index 0000000..1a0002b --- /dev/null +++ b/core-api/.dockerignore @@ -0,0 +1,5 @@ +.venv +__pycache__ +.pytest_cache +.env +.git diff --git a/core-api/Dockerfile b/core-api/Dockerfile new file mode 100644 index 0000000..da9b071 --- /dev/null +++ b/core-api/Dockerfile @@ -0,0 +1,23 @@ +FROM python:3.13-slim + +RUN useradd --create-home appuser + +WORKDIR /app + +# Install dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy only the application code needed to run +COPY api/ api/ +COPY lib/ lib/ +COPY index.py dev.py ./ + +RUN chown -R appuser:appuser /app + +USER appuser + +EXPOSE 8000 + +# Development server with hot reload +CMD ["python", "dev.py"] diff --git a/core-web/.dockerignore b/core-web/.dockerignore new file mode 100644 index 0000000..f2ccdb4 --- /dev/null +++ b/core-web/.dockerignore @@ -0,0 +1,4 @@ +node_modules +dist +.env +.git diff --git a/core-web/Dockerfile b/core-web/Dockerfile new file mode 100644 index 0000000..1651466 --- /dev/null +++ b/core-web/Dockerfile @@ -0,0 +1,21 @@ +FROM node:22-slim + +WORKDIR /app + +# Install dependencies +COPY package.json package-lock.json ./ +RUN npm ci + +# Copy only the application code needed to run +COPY index.html vite.config.ts tsconfig.json tsconfig.app.json tsconfig.node.json ./ +COPY src/ src/ +COPY public/ public/ + +RUN chown -R node:node /app + +USER node + +EXPOSE 3000 + +# Development server with hot reload +CMD ["npm", "run", "dev", "--", "--host"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..8d53a34 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,33 @@ +services: + api: + build: ./core-api + user: "${UID:-1000}:${GID:-1000}" + ports: + - "${CORE_API_PORT:-8000}:8000" + env_file: + - .env + volumes: + - ./core-api:/app + - /app/__pycache__ + healthcheck: + test: ["CMD", "python", "-c", "import httpx; httpx.get('http://localhost:8000/')"] + interval: 10s + timeout: 15s + retries: 5 + start_period: 30s + restart: unless-stopped + + web: + build: ./core-web + user: "${UID:-1000}:${GID:-1000}" + ports: + - "${CORE_WEB_PORT:-3000}:3000" + env_file: + - .env + volumes: + - ./core-web:/app + - /app/node_modules + depends_on: + api: + condition: service_healthy + restart: unless-stopped