Improved Dockerfile
This commit is contained in:
67
AGENTS.md
Normal file
67
AGENTS.md
Normal 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)
|
||||||
59
Dockerfile
59
Dockerfile
@@ -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"]
|
||||||
@@ -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"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
bun.lock
6
bun.lock
@@ -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
5
entrypoint.sh
Normal 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
|
||||||
Reference in New Issue
Block a user