feat: add CI/CD pipeline, production Dockerfile, and deployment architecture

- Add production Dockerfile with bun build --compile, multi-stage Alpine build
- Add .dockerignore
- Swap bcrypt -> bcryptjs (pure JS, no native addons)
- Add programmatic migrations on startup via drizzle migrator
- Add /v1/version endpoint with APP_VERSION baked in at build time
- Add .gitea/workflows/ci.yml (lint + test with postgres/valkey services)
- Add .gitea/workflows/build.yml (version bump, build, push to registry)
- Update CLAUDE.md and docs/architecture.md to remove multi-tenancy
- Add docs/deployment.md covering DOKS + ArgoCD architecture

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ryan Moon
2026-04-01 19:50:37 -05:00
parent ffef4c8727
commit c2b1073fef
15 changed files with 419 additions and 26 deletions

View File

@@ -56,11 +56,10 @@ src/
### Request Flow
1. Fastify receives request
2. `onRequest` hook sets `companyId` from header
3. `authenticate` preHandler verifies JWT, loads permissions, checks `is_active`
4. `requirePermission` preHandler checks user has required permission slug
5. Route handler validates input with Zod, calls service, returns response
6. Error handler catches typed errors and maps to HTTP status codes
2. `authenticate` preHandler verifies JWT, loads permissions, checks `is_active`
3. `requirePermission` preHandler checks user has required permission slug
4. Route handler validates input with Zod, calls service, returns response
5. Error handler catches typed errors and maps to HTTP status codes
### Permission Inheritance
@@ -109,9 +108,9 @@ src/
| Theme | Zustand store, persisted to localStorage |
| Component state | React `useState` |
## Multi-Tenancy
## Deployment Model
Every domain table has a `company_id` column. All queries filter by the authenticated user's company. Location-scoped tables (inventory, transactions) additionally filter by `location_id`.
Each customer runs as a fully isolated deployment — their own Kubernetes namespace on DOKS, their own database on the shared managed Postgres instance. There is no multi-tenancy in the application layer. No `company_id`, no row-level isolation. One instance = one customer.
## Database