Commit Graph

16 Commits

Author SHA1 Message Date
twotalesanimation 396ebf9837 Run database migrations as part of the deploy
Deploy / deploy (push) Successful in 3m45s
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>
2026-11-11 09:30:58 +02:00
twotalesanimation 8a93a18bb9 Add REST API for quotes, shots, assets and tasks
Adds a key-authenticated REST API at /api/v1 so another system can create
and manage quotes without going through the UI.

Endpoints cover quotes (list, create, read, update, delete, recalculate),
shots, assets and line items, plus read-only clients and rate cards.
POST /api/v1/quotes builds a whole priced quote -- client, shots, assets
and tasks -- in a single transaction. Routes are also mounted under
/api/v1/jobs, since a quote and a job are the same record here.

Keys are stored as SHA-256 with a display prefix, so the plaintext is
shown once at creation and is not recoverable. They carry read/write
scopes, can be revoked (effective on the next request), and record when
they were last used. Minting and revoking live in Settings and are
restricted to signed-in admins.

/api/v1 is exempt from the session proxy because it authenticates with a
bearer key rather than a cookie -- without this, every API call was
redirected to /login as a 307. It is not unprotected: each route handler
requires a live, unrevoked key with the right scope.

Shot numbering and shot-code construction move into lib/shot-codes.ts so
the UI and the API cannot drift. This also fixes numbering restarting at
0010 when a scene was entered in a different case to the stored one: the
lookup upper-cased the scene but the stored value was left as typed.

Unspecified currency and markup/tax/discount on an API-created quote fall
back to the studio's configured quote defaults.

Decimal fields are serialised as strings to preserve the exact precision
the app's decimal arithmetic maintains.

Note: this needs `prisma migrate deploy` for the new api_keys table.
Settings will error for admins until that runs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-11-11 09:10:45 +02:00
twotalesanimation 2ed7ae46a4 Add CSV shotlist export for jobs/quotes
Deploy / deploy (push) Successful in 9m11s
Adds a GET /api/csv/shots/[jobId] endpoint with two modes:

  ?detail=shots (default) - one row per shot, with rolled-up task
    count, hours, days, subtotal and total.
  ?detail=tasks - one row per line item, adding artist role, rate
    card, rates and the markup/discount/tax/total split.

Totals run through the existing calcLineItem/sumLineItems helpers so
they match the editor and the PDF. Amounts are emitted as plain 2dp
numbers with the currency named in the header, so spreadsheets can sum
them. Rows follow the editor's display order (grouped by episode or
shot group, Ungrouped last, then alphabetical by shot code) and the
group column header adapts to the job type.

Shotlist column headers deliberately match the CSV importer's aliases
so an export can be edited and re-imported. Free text is flattened to a
single line to keep one shot per row: the importer's parser splits on
newlines before handling quotes, so multi-line quoted fields would
otherwise corrupt a re-import.

Exposed as an "Export CSV" dropdown in the shots editor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 16:06:11 +02:00
twotalesanimation 6cc4751c9f Remove unused puppeteer dependency
Deploy / deploy (push) Successful in 4m37s
puppeteer was declared in package.json but never imported anywhere in
the codebase -- PDF generation goes through @react-pdf/renderer. Its
postinstall step downloaded a ~150-200MB Chrome build on every clean
`npm ci`, which exhausted the disk on the deploy host and failed the
build with ENOSPC.

That download was wasted work even when it succeeded: the runner stage
never copies /root/.cache/puppeteer out of the builder, so the browser
was discarded when the builder stage ended.

Also drops 633 lines of transitive dependencies from the lockfile.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 14:12:20 +02:00
twotalesanimation 9496c70155 Pass SHOTS_API_KEY through to the app container
Deploy / deploy (push) Failing after 2m19s
The import-shots feature calls review.twotalesvfx.com from a server
action that reads process.env.SHOTS_API_KEY. That variable existed only
in the local gitignored .env, and the compose file passed just
DATABASE_URL, NEXTAUTH_SECRET and NEXTAUTH_URL to the app service. In
production the key was therefore undefined, so the action threw before
issuing any request -- and because Next.js masks server-action errors in
production, the UI showed a generic failure that looked like a network
or CDN problem rather than missing configuration.

Interpolate SHOTS_API_KEY from the deploy environment instead of
hardcoding it, so the secret stays out of the repo. The :? guard makes
compose abort with an explanatory message when the variable is missing,
rather than silently starting an app that cannot import shots.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 13:54:30 +02:00
twotalesanimation f1c6020e5c Remove bootstrap signup page
Deploy / deploy (push) Failing after 4m11s
Admin account has been created; the one-time setup page is no longer
needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-18 16:33:54 +02:00
twotalesanimation 457469decd Add bootstrap signup page for creating the first admin user
Deploy / deploy (push) Successful in 3m43s
The CLI create-user scripts hit environment issues on the VPS (missing
ts-node, missing node_modules on the host). A /signup page sidesteps
that entirely since it runs inside the already-working Next.js server.
It's a one-time-use setup form: the server action refuses to run once
any user already exists, so it's safe to leave in place, though it can
be deleted after the first account is created.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-18 16:26:50 +02:00
twotalesanimation 29d5caa76a updated dockerfile
Deploy / deploy (push) Successful in 3m29s
2026-08-18 16:14:17 +02:00
twotalesanimation 3d0a6acaf1 Add plain-JS create-user script for production
Deploy / deploy (push) Successful in 3m28s
ts-node is a devDependency and isn't available after a production
install; scripts/create-user.js only needs packages already in
dependencies so it runs on the deployed server.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-18 15:40:16 +02:00
twotalesanimation 9dcf310806 User Access
Deploy / deploy (push) Successful in 2m3s
2026-08-18 15:23:22 +02:00
twotalesanimation ecf47e56cc Fix: enable standalone output for Docker
Deploy / deploy (push) Successful in 2m0s
2026-08-18 13:15:00 +02:00
twotalesanimation dd83dbb0c7 Fix: force-dynamic on all pages to prevent build-time DB queries
Deploy / deploy (push) Failing after 1m1s
2026-08-18 12:46:09 +02:00
twotalesanimation 76ca160c90 Docker compose update
Deploy / deploy (push) Failing after 1s
2026-08-18 12:28:50 +02:00
twotalesanimation 4d555d1a22 Add Gitea Actions deploy workflow
Deploy / deploy (push) Failing after 1s
2026-08-18 12:27:33 +02:00
twotalesanimation 95ed30812e Add full project files 2026-08-18 12:11:20 +02:00
twotalesanimation 8ce915bcac Initial commit from Create Next App 2026-05-12 14:54:08 +02:00