From b184d74ad466671881e482d13a1ec40315a5a8fa Mon Sep 17 00:00:00 2001 From: Mannu Date: Fri, 15 May 2026 09:45:37 +0530 Subject: [PATCH] Update CLAUDE.md with Dokploy deployment docs and fixes --- CLAUDE.md | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 189 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index b87268e..d14edac 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -77,4 +77,192 @@ Key modules (read in this order for domain understanding): - **routers/** — REST endpoints (scenarios, sensitivities, templates) - **workers/** — Arq async tasks (run via Redis queue) - **db/** — SQLAlchemy models + migrations -- **main.py** — FastAPI app factory \ No newline at end of file +- **main.py** — FastAPI app factory + +## Deployment (Dokploy) + +### Docker Configuration + +**API Dockerfile** (`packages/api/Dockerfile`): +```dockerfile +FROM python:3.12-slim + +WORKDIR /app + +RUN pip install poetry + +COPY packages /app/packages + +WORKDIR /app/packages/api +RUN poetry install --no-interaction + +ENV VENV_PATH=/root/.cache/pypoetry/virtualenvs/remodel-api-cufy8KWC-py3.12/bin +ENV PATH=$VENV_PATH:$PATH +ENV PYTHONPATH=/app/packages/engine/src:/app/packages/api/src + +WORKDIR /app/packages/api + +EXPOSE 8000 + +CMD ["uvicorn", "remodel_api.main:app", "--host", "0.0.0.0", "--port", "8000"] +``` + +**Web Dockerfile** (`packages/web/Dockerfile`): +```dockerfile +FROM node:22-alpine AS builder + +WORKDIR /app +RUN corepack enable && corepack prepare pnpm@latest --activate + +COPY package.json pnpm-lock.yaml ./ +COPY . . +RUN test -f .env.local || echo "# placeholder" > .env.local +RUN pnpm install --frozen-lockfile --ignore-scripts +RUN pnpm build + +FROM node:22-alpine +WORKDIR /app +RUN corepack enable && corepack prepare pnpm@latest --activate + +COPY --from=builder /app/.next ./.next +COPY --from=builder /app/public ./public +COPY --from=builder /app/package.json . +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/.env.local ./.env.local + +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 + +EXPOSE 3000 + +CMD ["/app/node_modules/.bin/next", "start"] +``` + +### docker-compose.yml (Dokploy) + +```yaml +services: + redis: + image: redis:7-alpine + restart: unless-stopped + volumes: + - redis_data:/data + networks: + - internal + + api: + build: + context: . + dockerfile: packages/api/Dockerfile + restart: unless-stopped + environment: + - DATABASE_URL=sqlite+aiosqlite:///./remodel.db + - REDIS_URL=redis://redis:6379 + depends_on: + - redis + labels: + - "traefik.enable=true" + - "traefik.docker.network=web" + - "traefik.http.routers.api.rule=Host(`model.manohargupta.com`) && PathPrefix(`/api`)" + - "traefik.http.routers.api.entrypoints=websecure" + - "traefik.http.routers.api.tls.certresolver=letsencrypt" + - "traefik.http.services.api.loadbalancer.server.port=8000" + networks: + - internal + - web + + worker: + build: + context: . + dockerfile: packages/api/Dockerfile + command: python -m arq remodel_api.workers.main.WorkerSettings + restart: unless-stopped + environment: + - DATABASE_URL=sqlite+aiosqlite:///./remodel.db + - REDIS_URL=redis://redis:6379 + depends_on: + - redis + networks: + - internal + + web: + build: + context: ./packages/web + dockerfile: Dockerfile + restart: unless-stopped + environment: + - NEXT_PUBLIC_API_URL=https://model.manohargupta.com/api + labels: + - "traefik.enable=true" + - "traefik.docker.network=web" + - "traefik.http.routers.web.rule=Host(`model.manohargupta.com`)" + - "traefik.http.routers.web.entrypoints=websecure" + - "traefik.http.routers.web.tls.certresolver=letsencrypt" + - "traefik.http.services.web.loadbalancer.server.port=3000" + networks: + - web + +networks: + internal: + internal: true + web: + name: dokploy-network + external: true + +volumes: + redis_data: +``` + +### Environment Variables + +| Variable | Description | Required | +|----------|-------------|----------| +| `DATABASE_URL` | SQLite with async driver | `sqlite+aiosqlite:///./remodel.db` | +| `REDIS_URL` | Redis connection string | `redis://redis:6379` | +| `NEXT_PUBLIC_API_URL` | Public API URL for web | `https://model.manohargupta.com/api` | + +### CORS Configuration + +In `packages/api/src/remodel_api/main.py`: +```python +from fastapi.middleware.cors import CORSMiddleware + +app.add_middleware( + CORSMiddleware, + allow_origins=["http://localhost:3000", "https://model.manohargupta.com"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) +``` + +### Config Settings + +In `packages/api/src/remodel_api/config.py`: +```python +class Settings(BaseSettings): + model_config = SettingsConfigDict(env_prefix="", extra="ignore") + database_url: str = "sqlite+aiosqlite:///./remodel.db" + redis_url: str = "redis://localhost:6379" +``` + +**Important**: No prefix for env vars - they must match docker-compose exactly. + +### Network Requirements + +- `dokploy-network` must exist on Dokploy server (external network) +- Traefik uses `websecure` endpoint (HTTPS with Let's Encrypt TLS) + +## Deployment Issues & Fixes + +| Issue | Cause | Fix | +|-------|-------|-----| +| Poetry pyproject.toml not found | COPY syntax wrong in Dockerfile | Copy entire `packages/` directory | +| `--no-venv-seeding` flag error | Old Poetry version | Remove the flag | +| Missing `.env.local` | Not in git | Create placeholder in builder stage | +| `uvicorn` not in PATH | Poetry venv not in PATH | Set `ENV PATH=$VENV_PATH:$PATH` | +| `next` not found | pnpm stores bins differently | Use absolute path `/app/node_modules/.bin/next` | +| 504 Gateway Timeout | Redis/DB not accessible | Check network, restart containers | +| Worker can't connect to Redis | Wrong env prefix | Use `REDIS_URL` not `REMODEL_REDIS_URL` | +| SQLite async error | Wrong driver | Use `sqlite+aiosqlite://` not `sqlite://` | +| CORS blocked | Origin mismatch | Add production domain to allow_origins | \ No newline at end of file