Add Docker support with Gunicorn, SQLite volume persistence, and optional PostgreSQL
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
10
.dockerignore
Normal file
10
.dockerignore
Normal file
@@ -0,0 +1,10 @@
|
||||
.env
|
||||
.venv
|
||||
__pycache__
|
||||
*.py[cod]
|
||||
*.sqlite3
|
||||
db.sqlite3
|
||||
*.log
|
||||
.git
|
||||
.gitignore
|
||||
README.md
|
||||
@@ -10,4 +10,7 @@ ALLOWED_HOSTS=localhost,127.0.0.1,0.0.0.0,testserver
|
||||
|
||||
# CORS (the accounting frontend dev server runs on port 5176)
|
||||
CORS_ALLOW_ALL_ORIGINS=True
|
||||
CORS_ALLOWED_ORIGINS=http://localhost:5176,http://127.0.0.1:5176
|
||||
CORS_ALLOWED_ORIGINS=http://localhost:5176,http://127.0.0.1:5176,http://localhost:3006,http://127.0.0.1:3006
|
||||
|
||||
# Docker: persist SQLite in a named volume
|
||||
# SQLITE_PATH=/data/db.sqlite3
|
||||
|
||||
36
Dockerfile
Normal file
36
Dockerfile
Normal file
@@ -0,0 +1,36 @@
|
||||
# FunZone Accounting Backend Dockerfile
|
||||
FROM python:3.11-slim
|
||||
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
postgresql-client \
|
||||
build-essential \
|
||||
libpq-dev \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY requirements.txt /app/
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . /app/
|
||||
|
||||
RUN mkdir -p /data \
|
||||
&& adduser --disabled-password --gecos '' appuser \
|
||||
&& chown -R appuser:appuser /app /data \
|
||||
&& chmod +x /app/docker-entrypoint.sh
|
||||
|
||||
USER appuser
|
||||
|
||||
EXPOSE 8010
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
|
||||
CMD curl -f http://localhost:8010/health || exit 1
|
||||
|
||||
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
||||
CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8010", "--workers", "2", "--timeout", "120", "--access-logfile", "-", "--error-logfile", "-"]
|
||||
24
README.md
24
README.md
@@ -52,3 +52,27 @@ default company settings/account mapping. No demo business data is created.
|
||||
The frontend logs in via the FunZone admin endpoint
|
||||
(`/api/auth/token/admin/`) and reuses the returned access token for this service.
|
||||
Make sure `SECRET_KEY` here is identical to the FunZone backend's.
|
||||
|
||||
## Docker
|
||||
|
||||
SQLite (default, zero extra services):
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
The API listens on **http://localhost:8010** (`/health`, `/api/accounting/...`).
|
||||
|
||||
Set `SECRET_KEY` to match the FunZone backend before starting:
|
||||
|
||||
```bash
|
||||
SECRET_KEY=your-shared-secret docker compose up -d --build
|
||||
```
|
||||
|
||||
Optional PostgreSQL instead of SQLite:
|
||||
|
||||
```bash
|
||||
docker compose --profile postgres up -d --build
|
||||
```
|
||||
|
||||
Then set `DATABASE_URL=postgresql://accounting_user:accounting_password@accounting-db:5432/accounting_db` in your environment or `.env` file.
|
||||
|
||||
@@ -71,12 +71,17 @@ if DATABASE_URL:
|
||||
}
|
||||
}
|
||||
else:
|
||||
DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3'}}
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': config('SQLITE_PATH', default=str(BASE_DIR / 'db.sqlite3')),
|
||||
}
|
||||
}
|
||||
else:
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
'NAME': config('SQLITE_PATH', default=str(BASE_DIR / 'db.sqlite3')),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
53
docker-compose.yml
Normal file
53
docker-compose.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
services:
|
||||
accounting-backend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: funzone_accounting_backend
|
||||
ports:
|
||||
- "127.0.0.1:8010:8010"
|
||||
environment:
|
||||
SECRET_KEY: ${SECRET_KEY:-your-super-secret-key-change-in-production}
|
||||
DEBUG: ${DEBUG:-False}
|
||||
ALLOWED_HOSTS: ${ALLOWED_HOSTS:-localhost,127.0.0.1,accounting-backend}
|
||||
CORS_ALLOW_ALL_ORIGINS: ${CORS_ALLOW_ALL_ORIGINS:-True}
|
||||
CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-http://localhost:3006,http://localhost:5176,http://127.0.0.1:3006,http://127.0.0.1:5176}
|
||||
SQLITE_PATH: /data/db.sqlite3
|
||||
DATABASE_URL: ${DATABASE_URL:-}
|
||||
DB_HOST: accounting-db
|
||||
POSTGRES_USER: ${POSTGRES_USER:-accounting_user}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-accounting_password}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-accounting_db}
|
||||
volumes:
|
||||
- accounting_sqlite:/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8010/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
start_period: 40s
|
||||
retries: 3
|
||||
|
||||
accounting-db:
|
||||
image: postgres:16-alpine
|
||||
container_name: funzone_accounting_postgres
|
||||
profiles:
|
||||
- postgres
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB:-accounting_db}
|
||||
POSTGRES_USER: ${POSTGRES_USER:-accounting_user}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-accounting_password}
|
||||
volumes:
|
||||
- accounting_postgres:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "127.0.0.1:5434:5432"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-accounting_user} -d ${POSTGRES_DB:-accounting_db}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
accounting_sqlite:
|
||||
accounting_postgres:
|
||||
14
docker-entrypoint.sh
Normal file
14
docker-entrypoint.sh
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
if [ -n "$DATABASE_URL" ]; then
|
||||
until PGPASSWORD="${POSTGRES_PASSWORD:-accounting_password}" psql -h "${DB_HOST:-accounting-db}" -U "${POSTGRES_USER:-accounting_user}" -d "${POSTGRES_DB:-accounting_db}" -c '\q' 2>/dev/null; do
|
||||
>&2 echo "PostgreSQL is unavailable - sleeping"
|
||||
sleep 1
|
||||
done
|
||||
>&2 echo "PostgreSQL is up"
|
||||
fi
|
||||
|
||||
python manage.py migrate --noinput
|
||||
|
||||
exec "$@"
|
||||
@@ -9,3 +9,6 @@ python-decouple==3.8
|
||||
|
||||
# Optional PostgreSQL driver (SQLite is used by default)
|
||||
psycopg2-binary==2.9.9
|
||||
|
||||
# Production server
|
||||
gunicorn==21.2.0
|
||||
|
||||
Reference in New Issue
Block a user