Improved Dockerfile

This commit is contained in:
Jose Selesan
2026-04-10 19:44:33 -03:00
parent 8210a4bd9b
commit 91dfdce66d
5 changed files with 120 additions and 22 deletions

67
AGENTS.md Normal file
View File

@@ -0,0 +1,67 @@
# AGENTS.md
## Commands
```sh
# Root (runs both frontend + backend)
bun run dev
# Individual packages
bun run dev:frontend
bun run dev:backend
bun run build:frontend
# Linting (Biome - ESLint was replaced)
bun run lint
bun run lint:fix # fixes and formats
# Prisma (run from backend)
bun --filter backend prisma:generate # regenerate client after schema changes
bun --filter backend prisma:migrate # apply migrations
```
## Architecture
```
apps/frontend/ # React + Vite + TanStack Router/Query
apps/backend/ # Bun + Hono + Prisma
packages/api-contract/ # Shared Zod schemas, types, route definitions
```
**Shared contract pattern:**
1. Update `packages/api-contract` first (schemas/types)
2. Implement handler in `apps/backend`
3. Consume from `apps/frontend` via workspace import `@repo/api-contract`
## Key Quirks
- **Prisma 7**: Uses `prisma.config.ts`, requires `DATABASE_URL` env var at generate time
- **Zod v4**: Use `.issues` instead of `.errors` for validation errors
- **Generated files**: `routeTree.gen.ts` and `tsconfig*.json` are auto-generated (ignored by Biome)
- **TanStack Router**: Routes are code-generated. Use `--filter frontend build` not raw vite build.
## Docker Deployment (Dokploy)
- **Build Context**: `./` (monorepo root)
- **Dockerfile**: `Dockerfile` (in root, not `apps/backend/`)
- **Required args**: `DATABASE_URL`, `VITE_API_BASE_URL`, `VITE_SUPABASE_URL`, `VITE_SUPABASE_ANON_KEY`
- **bun.lock must be tracked**: It's in `.gitignore` by default - remove it for Docker builds
## Linting
Biome is used (not ESLint). Config in `biome.json`:
- Line width: 100
- Quote style: single
- Semicolons: always
- `noUnusedVariables`: warn
## Environment Variables
**Backend** (`apps/backend/.env`):
- `DATABASE_URL` - PostgreSQL connection (required for Prisma)
- `SUPABASE_URL`, `SUPABASE_ANON_KEY` - Auth
- `CORS_ORIGIN` - Frontend URL
**Frontend** (`apps/frontend/.env`):
- `VITE_*` prefix required (Vite embeds these at build time)
- `VITE_API_BASE_URL` - Backend URL (default: http://localhost:3000)

View File

@@ -1,39 +1,64 @@
# syntax=docker/dockerfile:1.7 # syntax=docker/dockerfile:1.7
FROM oven/bun:1.3 AS deps # =============================================================================
# Stage 1: Install frontend dependencies
# =============================================================================
FROM oven/bun:1.3.11 AS deps-frontend
WORKDIR /app WORKDIR /app
COPY package.json bun.lock ./ COPY package.json bun.lock ./
COPY apps/backend/package.json apps/backend/
COPY apps/frontend/package.json apps/frontend/ COPY apps/frontend/package.json apps/frontend/
COPY packages/api-contract/package.json packages/api-contract/ COPY packages/api-contract/package.json packages/api-contract/
RUN bun install --frozen-lockfile RUN bun install
FROM deps AS build # =============================================================================
# Stage 2: Build Frontend
# =============================================================================
FROM deps-frontend AS build-frontend
WORKDIR /app WORKDIR /app
COPY . . COPY apps/frontend ./apps/frontend
COPY packages ./packages
ARG VITE_API_BASE_URL ARG VITE_API_BASE_URL
ARG VITE_SUPABASE_URL ARG VITE_SUPABASE_URL
ARG VITE_SUPABASE_ANON_KEY ARG VITE_SUPABASE_ANON_KEY
ENV VITE_API_BASE_URL=${VITE_API_BASE_URL} ENV VITE_API_BASE_URL=${VITE_API_BASE_URL}
ENV VITE_SUPABASE_URL=${VITE_SUPABASE_URL} ENV VITE_SUPABASE_URL=${VITE_SUPABASE_URL}
ENV VITE_SUPABASE_ANON_KEY=${VITE_SUPABASE_ANON_KEY} ENV VITE_SUPABASE_ANON_KEY=${VITE_SUPABASE_ANON_KEY}
ARG DATABASE_URL=postgresql://placeholder:placeholder@placeholder:5432/placeholder
ENV DATABASE_URL=${DATABASE_URL}
RUN bun run build:frontend RUN bun run build:frontend
RUN bun --cwd apps/backend prisma generate
FROM deps AS runner # =============================================================================
# Stage 3: Install backend dependencies
# =============================================================================
FROM oven/bun:1.3.11 AS deps-backend
WORKDIR /app
COPY package.json bun.lock ./
COPY apps/backend/package.json apps/backend/
COPY packages/api-contract/package.json packages/api-contract/
RUN bun install
# =============================================================================
# Stage 4: Build Backend
# =============================================================================
FROM deps-backend AS build-backend
WORKDIR /app
COPY apps/backend ./apps/backend
COPY packages ./packages
RUN bun --cwd apps/backend build
# =============================================================================
# Stage 5: Runner - Production image
# =============================================================================
FROM oven/bun:1.3.11 AS runner
WORKDIR /app WORKDIR /app
ENV NODE_ENV=production HOST=0.0.0.0 PORT=3000 ENV NODE_ENV=production HOST=0.0.0.0 PORT=3000
COPY --from=build /app/node_modules ./node_modules COPY --from=deps-backend /app/node_modules ./node_modules
COPY --from=build /app/apps/backend ./apps/backend COPY --from=build-frontend /app/apps/frontend/dist ./apps/frontend/dist
COPY --from=build /app/packages ./packages COPY --from=build-backend /app/apps/backend ./apps/backend
COPY --from=build-backend /app/packages ./packages
COPY package.json ./ COPY package.json ./
COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
WORKDIR /app/apps/backend
EXPOSE 3000 EXPOSE 3000
CMD ["sh", "-lc", "bun run prisma migrate deploy && bun src/server.ts"] CMD ["/app/entrypoint.sh"]

View File

@@ -3,6 +3,7 @@
"scripts": { "scripts": {
"dev": "bun run --hot src/server.ts", "dev": "bun run --hot src/server.ts",
"start": "bun src/server.ts", "start": "bun src/server.ts",
"build": "tsc -b",
"lint": "biome check .", "lint": "biome check .",
"lint:fix": "biome check --write .", "lint:fix": "biome check --write .",
"format": "biome format --write .", "format": "biome format --write .",
@@ -25,11 +26,11 @@
"pino": "10.3.1", "pino": "10.3.1",
"pino-pretty": "13.1.3", "pino-pretty": "13.1.3",
"pino-std-serializers": "7.1.0", "pino-std-serializers": "7.1.0",
"prisma": "^7",
"uuid": "^13.0.0" "uuid": "^13.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/bun": "latest", "@types/bun": "latest",
"@types/nodemailer": "^8.0.0", "@types/nodemailer": "^8.0.0"
"prisma": "^7"
} }
} }

View File

@@ -28,12 +28,12 @@
"pino": "10.3.1", "pino": "10.3.1",
"pino-pretty": "13.1.3", "pino-pretty": "13.1.3",
"pino-std-serializers": "7.1.0", "pino-std-serializers": "7.1.0",
"prisma": "^7",
"uuid": "^13.0.0", "uuid": "^13.0.0",
}, },
"devDependencies": { "devDependencies": {
"@types/bun": "latest", "@types/bun": "latest",
"@types/nodemailer": "^8.0.0", "@types/nodemailer": "^8.0.0",
"prisma": "^7",
}, },
}, },
"apps/frontend": { "apps/frontend": {
@@ -571,7 +571,7 @@
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="], "@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="],
"@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="], "@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="],
@@ -643,7 +643,7 @@
"browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="],
"bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="], "bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="],
"bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],

5
entrypoint.sh Normal file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
cd /app/apps/backend
bun --cwd /app/apps/backend prisma migrate deploy
bun --cwd /app/apps/backend prisma generate
bun run start