The deploy workflow pulled, built and started the app, but never applied migrations. Shipping a change that adds a table therefore broke the app until someone remembered to migrate by hand -- the new api_keys table would have done exactly that to the settings page. The workflow now builds, applies pending migrations, and only then starts the app. A failed migration exits non-zero, so the deploy fails instead of starting an app whose schema is behind its code. Because `up -d` is never reached in that case, production keeps serving the previous containers. Migrations run through a new `migrate` compose service built from the Dockerfile's builder stage, rather than `docker compose exec app npx prisma migrate deploy` as the README previously suggested. The production image copies prisma/ and node_modules/.prisma but not the Prisma CLI, which is a devDependency and is absent from the standalone bundle, so that command would have had npx download Prisma inside the container at runtime as the unprivileged nextjs user. The builder stage has the CLI and its engines already. The service sits behind a `tools` profile so `docker compose up` never starts it, and takes DATABASE_URL from compose rather than a .env file -- dotenv does not override variables that are already set, so the compose value wins. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
xBid — VFX Quoting & Bidding SaaS
A professional full-stack VFX quoting application built with Next.js 15, TypeScript, Tailwind CSS v4, Prisma ORM, and PostgreSQL.
Features
- Clients — manage client companies with full contact and billing info
- Jobs / Quotes — versioned quote documents with status workflow (Draft ? Sent ? Approved/Rejected)
- Shots — VFX shot breakdown with complexity multipliers and drag-and-drop reordering
- Assets — CG asset budget tracking (characters, environments, vehicles, etc.)
- Line Items — per-task cost rows with hours/days/artists/rates, markup, discount, tax
- Rate Cards — studio rate card library organized by department
- PDF Export — professional quote PDF with front page + detailed appendix
- Decimal Precision — all financials use
decimal.js(no floating-point drift) - REST API - create and manage quotes, shots, assets and tasks programmatically (API.md)
Tech Stack
| Layer | Technology |
|---|---|
| Framework | Next.js 15 (App Router, Server Actions) |
| Language | TypeScript |
| Styling | Tailwind CSS v4 + shadcn/ui |
| ORM | Prisma 7 + PostgreSQL 16 |
| Forms | React Hook Form + Zod |
| Drag & Drop | @dnd-kit/core + @dnd-kit/sortable |
| Finance Math | decimal.js |
| Notifications | sonner |
HTML render via /api/pdf/[id] |
Prerequisites
- Node.js 20+
- PostgreSQL 16 (or Docker)
- npm 10+
ts-nodefor seeding:npm install -g ts-node typescript
Quick Start
1. Clone & install
git clone <repo-url>
cd xBid
npm install
2. Configure environment
cp .env.example .env
Edit .env:
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/xbid"
NEXTAUTH_SECRET="your-random-secret-here"
NEXTAUTH_URL="http://localhost:3000"
3. Start PostgreSQL
Option A — Docker (recommended):
docker compose up -d postgres
Option B — local PostgreSQL:
CREATE DATABASE xbid;
4. Run database migrations
npm run db:generate # generate Prisma client
npm run db:migrate # run migrations (enter "init" as migration name)
5. Seed sample data
npm run db:seed
This creates 15 rate cards, 2 sample clients, 2 jobs with shots, assets, and line items.
6. Start development server
npm run dev
Production (Docker)
docker compose up --build -d
Pushing to main triggers the Gitea workflow in .gitea/workflows/deploy.yml,
which pulls, builds, applies pending migrations, and only then starts the app.
A failed migration fails the deploy rather than starting an app whose schema is
behind its code, so there is no manual migration step.
Migrations run through the migrate service, which builds from the Dockerfile
builder stage. That matters: the production image ships runtime dependencies
only, and the Prisma CLI is a devDependency, so docker compose exec app npx prisma ... would try to download Prisma inside the container at runtime.
To run migrations by hand (or to seed on a first deploy):
docker compose --profile tools run --rm migrate
docker compose --profile tools run --rm migrate npx prisma db seed
PDF Export
Navigate to any job and click Download PDF. The route at /api/pdf/[id] generates HTML with:
- Front page: client info, project details, shot/asset summary, financial totals, T&C
- Appendix: full per-shot and per-asset line item breakdown
Print to PDF via browser (Ctrl+P ? Save as PDF).
REST API
xBid exposes a REST API at /api/v1 so another system can create and manage
quotes, shots, assets and tasks without anyone opening the UI.
Requests authenticate with an API key, created under Settings -> API Keys (admins only):
curl -s http://localhost:3000/api/v1 \
-H "Authorization: Bearer $XBID_API_KEY"
That endpoint lists every available route and confirms what the key may do. See API.md for the full reference: endpoints, pricing rules, shot code generation and error codes.
Note: GET_SHOTS.md documents the opposite direction - the review app's API
that xBid pulls shots from.
Project Structure
src/
app/ Next.js App Router pages
api/pdf/[id]/ PDF generation endpoint
clients/ Client management
jobs/ Job management
rate-cards/ Rate card management
actions/ Server actions (CRUD)
components/
assets/ Asset editor
clients/ Client form & table
dashboard/ Stats & recent jobs
jobs/ Job form, table, detail, totals
layout/ Sidebar navigation
line-items/ Line item table & dialog
rate-cards/ Rate card management
shots/ Shot editor
ui/ shadcn/ui components
lib/
calculations.ts Decimal.js financial math
constants.ts Domain constants & defaults
prisma.ts Prisma client singleton
validations.ts Zod schemas
prisma/
schema.prisma Full database schema
seed.ts Sample data seeder
Database Scripts
npm run db:generate # npx prisma generate
npm run db:migrate # npx prisma migrate dev
npm run db:seed # ts-node prisma/seed.ts
npm run db:studio # npx prisma studio (DB GUI at :5555)