Adds PATCH and DELETE endpoints for brands and shops that mirror the
existing bins pattern: deleting a brand or shop nullifies referencing
products (and strains, for brands) inside a transaction so nothing is
lost. Brand renames return 409 when the new name collides with the
UNIQUE constraint, surfaced inline in the edit modal.
The Brands and Shops views now show inline edit/trash icons on each
card; the trash button confirms with a preview of how many products
will be unparented.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Multi-stage Dockerfile builds server + web into a single node:20-alpine
image; runtime serves API on /api and the SPA from /app/public.
- Express now serves web/dist with an SPA fallback that skips /api so API
misses still 404 cleanly.
- docker-compose.yml is a single-service deploy with a named volume for
the SQLite database at /data/apothecary.db.
- .gitea/workflows/build.yml pushes :latest, :<sha>, and :semver tags to
the Gitea container registry on main and v* tags.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>