- 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>
4.9 KiB
Architecture
Monorepo Structure
lunarfront/
packages/
shared/ @lunarfront/shared — Zod schemas, types, business logic, utils
backend/ @lunarfront/backend — Fastify API server
admin/ @lunarfront/admin — Admin UI (React + Vite)
planning/ Domain planning docs (01-26)
docs/ Technical documentation
Managed with Turborepo and Bun workspaces. @lunarfront/shared is a dependency of both backend and admin.
Backend
Fastify API server with a plugin-based architecture.
src/
main.ts App entry, plugin registration, server start
plugins/
database.ts Drizzle ORM + PostgreSQL connection
auth.ts JWT auth, permission checking, inheritance
redis.ts Valkey/Redis (ioredis)
cors.ts CORS configuration
storage.ts File storage provider registration
error-handler.ts Centralized error → HTTP response mapping
dev-auth.ts Dev-only auth bypass
routes/v1/
auth.ts Register, login, password change, reset, profile
accounts.ts Accounts, members, identifiers, payment methods, etc.
rbac.ts Users list, roles CRUD, role assignments, permissions
files.ts File upload/download/delete
services/
account.service.ts Account + member business logic
rbac.service.ts Roles, permissions, user role management
file.service.ts File validation, storage, metadata
product.service.ts Products + inventory
lookup.service.ts Lookup table management
inventory.service.ts Stock receipts, unit tracking
db/
schema/ Drizzle table definitions
migrations/ SQL migrations (drizzle-kit)
seeds/ System permission + role definitions
storage/
provider.ts StorageProvider interface
local.ts Local filesystem provider
s3.ts S3 provider (placeholder)
utils/
pagination.ts withPagination, withSort, buildSearchCondition, paginatedResponse
Request Flow
- Fastify receives request
authenticatepreHandler verifies JWT, loads permissions, checksis_activerequirePermissionpreHandler checks user has required permission slug- Route handler validates input with Zod, calls service, returns response
- Error handler catches typed errors and maps to HTTP status codes
Permission Inheritance
Permissions follow a hierarchy: admin implies edit, which implies view for the same domain. Having accounts.admin automatically grants accounts.edit and accounts.view. Non-hierarchical actions (upload, delete, send, export) don't cascade.
Frontend (Admin UI)
React SPA with TanStack Router (file-based routing) and TanStack Query (data fetching).
src/
routes/
_authenticated.tsx Layout with sidebar, permission-gated nav
_authenticated/
accounts/ Account list + detail pages
members/ Member list + detail pages
users.tsx Users admin page
roles/ Roles list + create/edit pages
profile.tsx User profile + settings
help.tsx In-app wiki
login.tsx Login page
api/ React Query options + mutations per domain
components/
ui/ shadcn/ui primitives
shared/ DataTable, AvatarUpload
accounts/ Domain-specific forms
stores/
auth.store.ts Zustand — token, user, permissions
theme.store.ts Zustand — color theme + light/dark mode
hooks/
use-pagination.ts URL-based pagination state
lib/
api-client.ts Fetch wrapper with JWT + error handling
themes.ts Color theme definitions
wiki/
index.ts In-app help content
State Management
| Concern | Solution |
|---|---|
| Auth + permissions | Zustand store, persisted to sessionStorage |
| Server data | TanStack Query (cache, refetch, invalidation) |
| URL state (pagination) | TanStack Router search params |
| Theme | Zustand store, persisted to localStorage |
| Component state | React useState |
Deployment Model
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
PostgreSQL 16 with Drizzle ORM. Migrations are generated with bunx drizzle-kit generate and applied with bunx drizzle-kit migrate. Schema files live in packages/backend/src/db/schema/.
Key tables: company, location, user, account, member, member_identifier, product, inventory_unit, role, permission, user_role_assignment, role_permission, file.