diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7764d84 --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +.PHONY: dev build up down logs migrate test shell-backend shell-frontend seed backup prod-build prod-up + +# Development +dev: + docker compose -f docker-compose.dev.yml up + +build: + docker compose -f docker-compose.dev.yml build + +up: + docker compose -f docker-compose.dev.yml up -d + +down: + docker compose -f docker-compose.dev.yml down + +logs: + docker compose -f docker-compose.dev.yml logs -f + +# Database +migrate: + docker compose -f docker-compose.dev.yml exec backend alembic upgrade head + +seed: + docker compose -f docker-compose.dev.yml exec backend python -m app.db.seed + +backup: + cp backend/data/roaauto.db backend/data/backup-$(shell date +%Y%m%d-%H%M%S).db + +# Testing +test: + docker compose -f docker-compose.dev.yml exec backend pytest tests/ -v + +# Shell access +shell-backend: + docker compose -f docker-compose.dev.yml exec backend bash + +shell-frontend: + docker compose -f docker-compose.dev.yml exec frontend sh + +# Production +prod-build: + docker compose build + +prod-up: + docker compose up -d diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..3555f97 --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,11 @@ +__pycache__ +*.pyc +*.pyo +.pytest_cache +.env +data/*.db +data/backup-* +.git +.gitignore +tests/ +*.md diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..6c1c9b2 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,20 @@ +FROM python:3.12-slim + +RUN apt-get update && apt-get install -y \ + libpango-1.0-0 libpangoft2-1.0-0 libpangocairo-1.0-0 \ + libgdk-pixbuf2.0-0 libcairo2 curl \ + && rm -rf /var/lib/apt/lists/* + +RUN useradd -m -s /bin/bash appuser + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +RUN chown -R appuser:appuser /app +USER appuser + +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"] diff --git a/backend/Dockerfile.dev b/backend/Dockerfile.dev new file mode 100644 index 0000000..bddfead --- /dev/null +++ b/backend/Dockerfile.dev @@ -0,0 +1,20 @@ +FROM python:3.12-slim + +RUN apt-get update && apt-get install -y \ + libpango-1.0-0 libpangoft2-1.0-0 libpangocairo-1.0-0 \ + libgdk-pixbuf2.0-0 libcairo2 \ + && rm -rf /var/lib/apt/lists/* + +RUN useradd -m -s /bin/bash appuser + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +RUN chown -R appuser:appuser /app +USER appuser + +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..481459a --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,26 @@ +services: + backend: + build: + context: ./backend + dockerfile: Dockerfile.dev + ports: + - "8000:8000" + volumes: + - ./backend:/app + - ./backend/data:/app/data + environment: + - DATABASE_URL=sqlite+aiosqlite:///./data/roaauto.db + - SECRET_KEY=dev-secret-key-change-in-prod + - CORS_ORIGINS=http://localhost:5173 + command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload + + frontend: + image: node:20-alpine + ports: + - "5173:5173" + volumes: + - ./frontend:/app + working_dir: /app + environment: + - VITE_API_URL=http://localhost:8000/api + command: sh -c "npm install && npm run dev -- --host" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..27435be --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,25 @@ +services: + backend: + build: ./backend + restart: unless-stopped + volumes: + - ./backend/data:/app/data + environment: + - DATABASE_URL=sqlite+aiosqlite:///./data/roaauto.db + - SECRET_KEY=${SECRET_KEY} + - SMSAPI_TOKEN=${SMSAPI_TOKEN:-} + - CORS_ORIGINS=https://roaauto.romfast.ro + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:8000/api/health || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + + frontend: + build: ./frontend + restart: unless-stopped + ports: + - "80:80" + depends_on: + backend: + condition: service_healthy diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..b2fef1e --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,7 @@ +node_modules +dist +.git +.gitignore +*.md +.env +.env.* diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..7dc77e8 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,12 @@ +FROM node:20-alpine AS build +WORKDIR /app +COPY package*.json . +RUN npm ci +COPY . . +RUN npm run build + +FROM nginx:alpine +COPY --from=build /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/frontend/nginx.conf b/frontend/nginx.conf new file mode 100644 index 0000000..f5c9bf8 --- /dev/null +++ b/frontend/nginx.conf @@ -0,0 +1,18 @@ +server { + listen 80; + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } + + location /api { + proxy_pass http://backend:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 60s; + } +}