feat: add Docker Compose setup for local development
Dockerize the full stack so developers can run everything with `docker compose up`. Replaces the outdated Dockerfile and docker-compose.yml with a working 6-service setup: PostgreSQL, Redis, Django backend, Celery worker, Celery beat, and SvelteKit frontend. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1aa676fc98
commit
1f6b797909
9 changed files with 297 additions and 55 deletions
42
.dockerignore
Normal file
42
.dockerignore
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.github
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
venv/
|
||||||
|
env/
|
||||||
|
.venv/
|
||||||
|
*.egg-info
|
||||||
|
.pytest_cache/
|
||||||
|
.coverage
|
||||||
|
htmlcov/
|
||||||
|
|
||||||
|
# Node / Frontend (built separately)
|
||||||
|
frontend/node_modules/
|
||||||
|
frontend/.svelte-kit/
|
||||||
|
frontend/build/
|
||||||
|
|
||||||
|
# Mobile app
|
||||||
|
mobile/
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
docker-compose.yml
|
||||||
|
.dockerignore
|
||||||
|
|
||||||
|
# Env files (injected at runtime)
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
backend/.env
|
||||||
|
backend/.env.*
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
*.md
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
.playwright-mcp
|
||||||
41
.env.docker
Normal file
41
.env.docker
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
# Docker development environment variables
|
||||||
|
# Copy to .env.docker.local and customize if needed
|
||||||
|
|
||||||
|
# Django
|
||||||
|
SECRET_KEY=django-insecure-docker-dev-key-change-in-production
|
||||||
|
DEBUG=True
|
||||||
|
ENV_TYPE=dev
|
||||||
|
ALLOWED_HOSTS=localhost,127.0.0.1,backend
|
||||||
|
DOMAIN_NAME=http://localhost:8000
|
||||||
|
SWAGGER_ROOT_URL=http://localhost:8000
|
||||||
|
|
||||||
|
# Database (must match docker-compose db service)
|
||||||
|
DBNAME=crm_db
|
||||||
|
DBUSER=crm_user
|
||||||
|
DBPASSWORD=crm_password
|
||||||
|
DBHOST=db
|
||||||
|
DBPORT=5432
|
||||||
|
|
||||||
|
# Postgres superuser (used by db service to create the database)
|
||||||
|
POSTGRES_DB=crm_db
|
||||||
|
POSTGRES_USER=postgres
|
||||||
|
POSTGRES_PASSWORD=postgres_password
|
||||||
|
|
||||||
|
# Celery / Redis
|
||||||
|
CELERY_BROKER_URL=redis://redis:6379/0
|
||||||
|
CELERY_RESULT_BACKEND=redis://redis:6379/0
|
||||||
|
|
||||||
|
# Email (console backend in dev — emails print to stdout)
|
||||||
|
DEFAULT_FROM_EMAIL=noreply@localhost
|
||||||
|
ADMIN_EMAIL=admin@localhost
|
||||||
|
|
||||||
|
# CORS
|
||||||
|
CORS_ALLOW_ALL=True
|
||||||
|
|
||||||
|
# Google OAuth (leave blank to disable)
|
||||||
|
GOOGLE_CLIENT_ID=
|
||||||
|
GOOGLE_CLIENT_SECRET=
|
||||||
|
GOOGLE_REDIRECT_URI=
|
||||||
|
|
||||||
|
# Frontend
|
||||||
|
PUBLIC_DJANGO_API_URL=http://localhost:8000
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -54,6 +54,9 @@ frontend/pnpm-lock.yaml
|
||||||
logs/
|
logs/
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
|
# Docker local overrides
|
||||||
|
.env.docker.local
|
||||||
|
|
||||||
# OS
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
|
||||||
56
Dockerfile
56
Dockerfile
|
|
@ -1,41 +1,27 @@
|
||||||
FROM ubuntu:24.04
|
FROM python:3.12-slim-bookworm
|
||||||
|
|
||||||
# invalidate cache
|
# Prevent Python from buffering stdout/stderr (useful for Docker logs)
|
||||||
ARG APP_NAME
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
# test arg
|
# Install system dependencies for WeasyPrint (cairo, pango) and PostgreSQL
|
||||||
RUN test -n "$APP_NAME"
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
libpq-dev \
|
||||||
|
libcairo2 \
|
||||||
|
libpango-1.0-0 \
|
||||||
|
libpangocairo-1.0-0 \
|
||||||
|
libgdk-pixbuf2.0-0 \
|
||||||
|
libffi-dev \
|
||||||
|
shared-mime-info \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# install system packages
|
WORKDIR /app
|
||||||
RUN apt-get update -y
|
|
||||||
RUN apt-get install -y \
|
|
||||||
python3-pip \
|
|
||||||
python3-venv \
|
|
||||||
build-essential \
|
|
||||||
libpq-dev \
|
|
||||||
libmariadbclient-dev \
|
|
||||||
libjpeg62-dev \
|
|
||||||
zlib1g-dev \
|
|
||||||
libwebp-dev \
|
|
||||||
curl \
|
|
||||||
vim \
|
|
||||||
net-tools
|
|
||||||
|
|
||||||
# setup user
|
# Install Python dependencies (layer cached)
|
||||||
RUN useradd -ms /bin/bash ubuntu
|
COPY backend/requirements.txt .
|
||||||
USER ubuntu
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
# install app
|
# Copy backend source
|
||||||
RUN mkdir -p /home/ubuntu/"$APP_NAME"/"$APP_NAME"
|
COPY backend/ .
|
||||||
WORKDIR /home/ubuntu/"$APP_NAME"/"$APP_NAME"
|
|
||||||
COPY . .
|
|
||||||
RUN python3 -m venv ../venv
|
|
||||||
RUN . ../venv/bin/activate
|
|
||||||
RUN /home/ubuntu/"$APP_NAME"/venv/bin/pip install -U pip
|
|
||||||
RUN /home/ubuntu/"$APP_NAME"/venv/bin/pip install -r requirements.txt
|
|
||||||
RUN /home/ubuntu/"$APP_NAME"/venv/bin/pip install gunicorn
|
|
||||||
|
|
||||||
# setup path
|
EXPOSE 8000
|
||||||
ENV PATH="${PATH}:/home/ubuntu/$APP_NAME/$APP_NAME/scripts"
|
|
||||||
|
|
||||||
USER ubuntu
|
|
||||||
|
|
|
||||||
55
README.md
55
README.md
|
|
@ -120,12 +120,49 @@ celery -A crm worker --loglevel=INFO
|
||||||
|
|
||||||
## Docker Setup
|
## Docker Setup
|
||||||
|
|
||||||
|
Run the full stack (backend, frontend, PostgreSQL, Redis, Celery) with a single command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build and run with Docker Compose
|
# Start all services (first run will build images)
|
||||||
docker build -t bottlecrm:latest -f docker/dockerfile .
|
docker compose up --build
|
||||||
docker-compose -f docker/docker-compose.yml up
|
|
||||||
|
# Create an admin user
|
||||||
|
docker compose exec backend python manage.py createsuperuser
|
||||||
|
|
||||||
|
# (Optional) Load sample data
|
||||||
|
docker compose exec backend python manage.py seed_data --email admin@example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Once running:
|
||||||
|
- **Frontend**: http://localhost:5173
|
||||||
|
- **API / Swagger**: http://localhost:8000/swagger-ui/
|
||||||
|
- **PostgreSQL**: localhost:5432
|
||||||
|
- **Redis**: localhost:6379
|
||||||
|
|
||||||
|
### Daily workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up # start all services (code changes auto-reload)
|
||||||
|
docker compose down # stop all services
|
||||||
|
docker compose down -v # stop and delete all data (full reset)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running commands inside containers
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec backend python manage.py migrate
|
||||||
|
docker compose exec backend python -m pytest
|
||||||
|
docker compose exec backend python manage.py manage_rls --status
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom environment overrides
|
||||||
|
|
||||||
|
The default env vars live in `.env.docker` (committed). To override locally without touching git:
|
||||||
|
|
||||||
|
1. Copy `.env.docker` to `.env.docker.local`
|
||||||
|
2. Edit values as needed
|
||||||
|
3. Update `env_file` in `docker-compose.yml` to point to `.env.docker.local`
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -146,8 +183,16 @@ Django-CRM/
|
||||||
│ │ └── routes/ # SvelteKit routes
|
│ │ └── routes/ # SvelteKit routes
|
||||||
│ │ ├── (app)/ # Authenticated app routes
|
│ │ ├── (app)/ # Authenticated app routes
|
||||||
│ │ └── (no-layout)/ # Auth pages (login, etc.)
|
│ │ └── (no-layout)/ # Auth pages (login, etc.)
|
||||||
│ └── static/ # Static assets
|
│ ├── static/ # Static assets
|
||||||
└── docker/ # Docker configuration
|
│ └── Dockerfile # Frontend dev container
|
||||||
|
├── docker/ # Docker support files
|
||||||
|
│ ├── backend/
|
||||||
|
│ │ └── entrypoint.sh # DB wait + migrate + runserver
|
||||||
|
│ └── postgres/
|
||||||
|
│ └── init-rls-user.sql # Creates non-superuser for RLS
|
||||||
|
├── Dockerfile # Backend / Celery image
|
||||||
|
├── docker-compose.yml # Full-stack dev environment
|
||||||
|
└── .env.docker # Docker env vars (dev defaults)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Multi-Tenancy & Security
|
## Multi-Tenancy & Security
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,84 @@
|
||||||
version: '2.0'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
image: postgres
|
image: postgres:16-alpine
|
||||||
# volumes:
|
env_file: .env.docker
|
||||||
# - /home/db:/var/lib/postgresql/data
|
volumes:
|
||||||
env_file:
|
- postgres_data:/var/lib/postgresql/data
|
||||||
- ./db.env
|
- ./docker/postgres/init-rls-user.sql:/docker-entrypoint-initdb.d/init-rls-user.sql:ro
|
||||||
|
healthcheck:
|
||||||
web:
|
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||||
build: .
|
interval: 5s
|
||||||
image: micropyramid/django-crm:1
|
timeout: 3s
|
||||||
env_file:
|
retries: 5
|
||||||
- ./db.env
|
|
||||||
ports:
|
ports:
|
||||||
- "8001:8000"
|
- "5432:5432"
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 5
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
|
||||||
|
backend:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
env_file: .env.docker
|
||||||
|
volumes:
|
||||||
|
- ./backend:/app
|
||||||
|
- ./docker/backend/entrypoint.sh:/entrypoint.sh:ro
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
command: /bin/bash /entrypoint.sh
|
||||||
|
|
||||||
|
celery-worker:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
env_file: .env.docker
|
||||||
|
volumes:
|
||||||
|
- ./backend:/app
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
command: celery -A crm worker --loglevel=info
|
||||||
|
|
||||||
|
celery-beat:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
env_file: .env.docker
|
||||||
|
volumes:
|
||||||
|
- ./backend:/app
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
command: celery -A crm beat --loglevel=info
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: ./frontend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
env_file: .env.docker
|
||||||
|
volumes:
|
||||||
|
- ./frontend:/app
|
||||||
|
- frontend_node_modules:/app/node_modules
|
||||||
|
ports:
|
||||||
|
- "5173:5173"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
frontend_node_modules:
|
||||||
|
|
|
||||||
22
docker/backend/entrypoint.sh
Executable file
22
docker/backend/entrypoint.sh
Executable file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Waiting for PostgreSQL..."
|
||||||
|
while ! python -c "
|
||||||
|
import socket, os
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
s.connect((os.environ['DBHOST'], int(os.environ['DBPORT'])))
|
||||||
|
s.close()
|
||||||
|
" 2>/dev/null; do
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
echo "PostgreSQL is ready."
|
||||||
|
|
||||||
|
echo "Running migrations..."
|
||||||
|
python manage.py migrate --noinput
|
||||||
|
|
||||||
|
echo "Collecting static files..."
|
||||||
|
python manage.py collectstatic --noinput
|
||||||
|
|
||||||
|
echo "Starting development server..."
|
||||||
|
exec python manage.py runserver 0.0.0.0:8000
|
||||||
24
docker/postgres/init-rls-user.sql
Normal file
24
docker/postgres/init-rls-user.sql
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
-- Create the application database user (non-superuser) for RLS enforcement.
|
||||||
|
-- This script runs automatically on first `docker compose up` via
|
||||||
|
-- PostgreSQL's /docker-entrypoint-initdb.d/ mechanism.
|
||||||
|
|
||||||
|
-- The database itself (crm_db) is created by POSTGRES_DB env var.
|
||||||
|
-- We just need to create the app user and grant privileges.
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'crm_user') THEN
|
||||||
|
CREATE ROLE crm_user WITH LOGIN PASSWORD 'crm_password';
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
|
||||||
|
-- Grant privileges on the database
|
||||||
|
GRANT ALL PRIVILEGES ON DATABASE crm_db TO crm_user;
|
||||||
|
|
||||||
|
-- Allow crm_user to create schemas and objects in the public schema
|
||||||
|
\connect crm_db;
|
||||||
|
GRANT ALL ON SCHEMA public TO crm_user;
|
||||||
|
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO crm_user;
|
||||||
|
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO crm_user;
|
||||||
|
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON FUNCTIONS TO crm_user;
|
||||||
14
frontend/Dockerfile
Normal file
14
frontend/Dockerfile
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
FROM node:22-slim
|
||||||
|
|
||||||
|
# Enable pnpm via corepack
|
||||||
|
RUN corepack enable && corepack prepare pnpm@latest --activate
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dependencies (layer cached)
|
||||||
|
COPY package.json pnpm-lock.yaml* ./
|
||||||
|
RUN pnpm install --frozen-lockfile || pnpm install
|
||||||
|
|
||||||
|
EXPOSE 5173
|
||||||
|
|
||||||
|
CMD ["pnpm", "dev", "--host", "0.0.0.0"]
|
||||||
Loading…
Reference in a new issue