diff --git a/Makefile b/Makefile index 7764d84..34e0270 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: dev build up down logs migrate test shell-backend shell-frontend seed backup prod-build prod-up +.PHONY: dev build up down logs migrate test shell-backend shell-frontend seed backup prod-build prod-up prod-down prod-logs # Development dev: @@ -43,3 +43,9 @@ prod-build: prod-up: docker compose up -d + +prod-down: + docker compose down + +prod-logs: + docker compose logs -f diff --git a/docker-compose.yml b/docker-compose.yml index 27435be..69b827d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,25 +1,33 @@ services: backend: build: ./backend - restart: unless-stopped + restart: always + env_file: .env volumes: - - ./backend/data:/app/data + - 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 + start_period: 15s frontend: build: ./frontend - restart: unless-stopped + restart: always ports: - "80:80" depends_on: backend: condition: service_healthy + +volumes: + backend-data: + driver: local + driver_opts: + type: none + o: bind + device: ./backend/data diff --git a/docs/DEPLOY.md b/docs/DEPLOY.md new file mode 100644 index 0000000..d17cd49 --- /dev/null +++ b/docs/DEPLOY.md @@ -0,0 +1,120 @@ +# ROA AUTO - Deploy Guide + +## Prerequisites + +- Docker & Docker Compose installed +- Domain configured (e.g., roaauto.romfast.ro) +- Cloudflare Tunnel configured (or reverse proxy) + +## Production Deploy on Dokploy + +### 1. Clone and configure + +```bash +git clone roaauto +cd roaauto +cp .env.example .env +``` + +Edit `.env` with production values: + +``` +SECRET_KEY= +DATABASE_URL=sqlite+aiosqlite:///./data/roaauto.db +SMSAPI_TOKEN= +CORS_ORIGINS=https://roaauto.romfast.ro +``` + +Generate a secret key: + +```bash +python3 -c "import secrets; print(secrets.token_urlsafe(64))" +``` + +### 2. Build and start + +```bash +make prod-build +make prod-up +``` + +Verify: + +```bash +curl http://localhost/api/health +# {"status":"ok"} +``` + +### 3. Run initial migration + +```bash +docker compose exec backend alembic upgrade head +``` + +### 4. Seed catalog data (first deploy only) + +```bash +docker compose exec backend python -m app.db.seed +``` + +## Cloudflare Tunnel Setup + +1. Install `cloudflared` on the Proxmox host +2. Create a tunnel: `cloudflared tunnel create roaauto` +3. Configure the tunnel to route `roaauto.romfast.ro` to `http://localhost:80` +4. Run as a service: `cloudflared service install` + +The tunnel provides HTTPS termination - nginx listens on port 80 internally. + +## Dokploy Configuration + +If using Dokploy instead of manual Docker: + +1. Create a new project in Dokploy +2. Set source to your Git repository +3. Set compose file to `docker-compose.yml` +4. Add environment variables from `.env.example` +5. Set the domain to `roaauto.romfast.ro` +6. Deploy + +## Maintenance + +### Logs + +```bash +make prod-logs +``` + +### Database backup + +```bash +make backup +``` + +### Update deployment + +```bash +git pull +make prod-build +make prod-up +``` + +### Rollback + +```bash +git checkout +make prod-build +make prod-up +``` + +## Architecture + +``` +Internet → Cloudflare Tunnel → nginx (:80) + ├── / → SPA (static files) + └── /api → backend (:8000) + +backend: Python 3.12 + FastAPI + SQLite (file-based) +frontend: Vue 3 SPA served by nginx +data: ./backend/data/ (bind mount, persisted) +``` diff --git a/frontend/nginx.conf b/frontend/nginx.conf index f5c9bf8..1bc18d3 100644 --- a/frontend/nginx.conf +++ b/frontend/nginx.conf @@ -3,10 +3,38 @@ server { root /usr/share/nginx/html; index index.html; + # Gzip compression + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_min_length 256; + gzip_types + text/plain + text/css + text/javascript + application/javascript + application/json + application/manifest+json + image/svg+xml; + + # SPA routing location / { try_files $uri $uri/ /index.html; } + # Static assets - long cache + location /assets { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Service worker - no cache + location /sw.js { + expires off; + add_header Cache-Control "no-store, no-cache, must-revalidate"; + } + + # API proxy location /api { proxy_pass http://backend:8000; proxy_set_header Host $host;