Commit Graph

23 Commits

Author SHA1 Message Date
Ryan Moon
58bf54a251 Add roles and users admin UI with role management API
Backend:
- GET /v1/users (list company users)
- GET/POST/PATCH/DELETE /v1/roles (role CRUD with permissions)
- GET/POST/DELETE /v1/users/:userId/roles (role assignment)
- GET /v1/me/permissions (current user's effective permissions)

Frontend:
- Roles list page with kebab menu (edit permissions, delete custom)
- Role detail page with grouped permission checkboxes and inheritance note
- New role page with auto-generated slug
- Users list page showing assigned roles per user
- Manage Roles dialog for adding/removing roles per user
- Sidebar: Admin section with Users, Roles, Help links
2026-03-28 17:16:53 -05:00
Ryan Moon
4a1fc608f0 Implement RBAC with permissions, roles, and route guards
- permission, role, role_permission, user_role_assignment tables
- 42 system permissions across 13 domains
- 6 default roles: Admin, Manager, Sales Associate, Technician, Instructor, Viewer
- Permission inheritance: admin implies edit implies view
- requirePermission() Fastify decorator on ALL routes
- System permissions and roles seeded per company
- Test helpers and API test runner seed RBAC data
- All 42 API tests pass with permissions enforced
2026-03-28 17:00:42 -05:00
Ryan Moon
e0493814f7 Add audit logging for sensitive operations
Structured logging with request ID correlation throughout:
- Auth: register, login success, login failure (warn level)
- Accounts: soft-delete
- Members: move between accounts
- Tax exemptions: approve (info), revoke (warn with reason)
- Files: upload, delete (already had logging)

All logs include userId, entityId, and contextual data for debugging.
4xx errors logged as warn, 5xx as error.
2026-03-28 16:23:20 -05:00
Ryan Moon
e44d461de1 Fix MEDIUM security issues, add logging and request timeout
- Password minimum increased from 8 to 12 characters
- CORS configurable via CORS_ORIGINS env var (comma-separated whitelist)
- Pagination empty string q param handled via preprocess
- Request timeout set to 30 seconds
- Log file output via LOG_FILE env var (stdout + file in production)
- Pino-pretty in development, JSON to stdout + file in production
2026-03-28 16:14:05 -05:00
Ryan Moon
e65175ef19 Fix security issues: path traversal, typed errors, file validation
- Fix path traversal in file serve endpoint (validate company prefix, block ..)
- Add typed error classes: ValidationError, NotFoundError, ForbiddenError,
  ConflictError, StorageError
- Global error handler catches AppError subclasses with correct status codes
- 4xx logged as warn, 5xx as error with request ID
- File upload validates entityType whitelist, UUID format, category pattern
- Remove fragile string-matching error handling from routes
- Services throw typed errors instead of plain Error
- Health endpoint documented as intentionally public
2026-03-28 16:03:45 -05:00
Ryan Moon
760e995ae3 Implement file storage layer with local provider, upload/download API, tests
- StorageProvider interface with LocalProvider (S3 placeholder)
- File table with entity_type/entity_id references, content type, path
- POST /v1/files (multipart upload), GET /v1/files (list by entity),
  GET /v1/files/:id (metadata), GET /v1/files/serve/* (content),
  DELETE /v1/files/:id
- member_identifier drops base64 columns, uses file_id FKs
- File validation: type whitelist, size limits, per-entity max
- Fastify storage plugin injects provider into app
- 6 API tests for upload, list, get, delete, validation
- Test runner kills stale port before starting backend
2026-03-28 15:29:06 -05:00
Ryan Moon
ce560cc568 Add comprehensive account and member API tests
12 new tests: search by email/phone, pagination params, sort order,
billing mode, scoped member list, isMinor from DOB, DOB override,
isMinor recalculation on update, 404 for missing member. Total: 36.
2026-03-28 12:57:57 -05:00
Ryan Moon
f47bfdbcdb Add standalone API test runner with accounts and members suites
Custom test framework that starts the backend, creates a test DB, runs
migrations, and hits real HTTP endpoints. Supports --suite and --tag
filtering. 24 tests covering account CRUD, member inheritance, state
normalization, move, search, and auto-generated numbers. Run with
bun run api-test.
2026-03-28 12:52:04 -05:00
Ryan Moon
b9e984cfa3 Add member address, state normalization, account inheritance, fix member form
- Address field on member table (jsonb, same format as account)
- Members inherit email, phone, address from account when not provided
- State normalization: "Texas" → "TX", "california" → "CA" via shared util
- Member form drops zodResolver to fix optional field validation flashing
- Account name auto-format: "First Last - Account"
- US state lookup with full name + code support
2026-03-28 12:31:02 -05:00
Ryan Moon
c7b460c0bf Add member identifiers table for ID documents (DL, passport, school ID)
member_identifier table with type, value, issuing authority, expiry,
front/back image storage (base64 in Postgres), primary flag. CRUD
endpoints under /members/:memberId/identifiers. Zod schemas with
constrained type enum.
2026-03-28 09:38:01 -05:00
Ryan Moon
727275af59 Add integration tests for members list, move, auto-numbers, primary member, isMinor flag 2026-03-28 09:18:32 -05:00
Ryan Moon
8ea3b8dffb Add auto-generated account numbers and member numbers
6-digit random numbers generated on create, unique per company. Member
number column added to member table. Both displayed in UI tables.
2026-03-28 09:15:27 -05:00
Ryan Moon
572af05a3f Add top-level members list, primary member on account, member move, combined create flows
- GET /v1/members with search across all members (includes account name)
- POST /members/:id/move with optional accountId (creates new account if omitted)
- primary_member_id on account table, auto-set when first member added
- isMinor flag on member create (manual override when no DOB provided)
- Account search now includes member names
- New account form includes primary contact fields, auto-generates name
- Members page in sidebar with global search
2026-03-28 09:08:06 -05:00
Ryan Moon
01d6ff3fa3 Move tests to __tests__ folders, add unit tests for shared utils and services
Restructure tests into __tests__/ directories at package root so they can
be excluded from production builds. Add unit tests for dates, currency,
lookup service, payment method default logic, and tax exemption state
transitions.
2026-03-27 21:14:42 -05:00
Ryan Moon
0a2d6e23af Add lookup tables, payment methods, tax exemptions, and processor link APIs
Replace unit_status and item_condition pgEnums with company-scoped lookup
tables that support custom values. Add account_payment_method table,
tax_exemption table with approve/revoke workflow, and CRUD routes for
processor links. Validate inventory unit status/condition against lookup
tables at service layer.
2026-03-27 20:53:30 -05:00
Ryan Moon
750dcf4046 Refactor all list APIs for server-side pagination, search, and sort
All list endpoints now return paginated responses:
  { data: [...], pagination: { page, limit, total, totalPages } }

Query params: ?page=1&limit=25&q=search&sort=name&order=asc

Changes:
- Added PaginationSchema in @forte/shared for consistent param parsing
- Added pagination utils (withPagination, withSort, buildSearchCondition,
  paginatedResponse) in backend
- Refactored all services: AccountService, MemberService, CategoryService,
  SupplierService, ProductService, InventoryUnitService
- Merged separate /search endpoints into list endpoints via ?q= param
- Removed AccountSearchSchema and ProductSearchSchema (replaced by
  PaginationSchema)
- Added pagination test (5 items, page 1 limit 2, expect totalPages=3)
- Updated CLAUDE.md with API conventions
- 34 tests passing
2026-03-27 19:53:59 -05:00
Ryan Moon
c34ad27b86 Fix auth security issues, add rate limiting, write Phase 2 audit
Security fixes:
- Register route validates company exists before creating user
- Rate limiting on auth routes (10 per 15min per IP)
- Dev auth plugin guards against production use
- Main.ts throws if JWT_SECRET missing in production

Added Phase 2 audit doc (22) covering:
- Built vs planning doc comparison
- Security review with fixes applied
- Duplicate code patterns identified
- Standard POS feature gap analysis
- Music-specific feature gaps

33 tests passing.
2026-03-27 19:21:33 -05:00
Ryan Moon
a782b2098f Add consignment_detail table for consignment inventory
Separate table linked to product — keeps product table clean
when most items are not consignment. Tracks consignor (account),
commission percentage, min price, and agreement date.
33 tests passing.
2026-03-27 18:27:35 -05:00
Ryan Moon
1132e0999b Add products, inventory units, stock receipts, and price history
- product table (catalog definition, no cost column — cost tracked
  per receipt/unit)
- inventory_unit table (serialized items with serial number,
  condition, status)
- stock_receipt table (FIFO cost tracking — records every stock
  receive event with cost_per_unit, supplier, date)
- price_history table (logs every retail price change for margin
  analysis over time)
- product_supplier join table (many-to-many, tracks supplier SKU
  and preferred supplier)
- Full CRUD routes + search (name, SKU, UPC, brand)
- Inventory unit routes nested under products
- Price changes auto-logged on product update
- 33 tests passing
2026-03-27 18:22:39 -05:00
Ryan Moon
77a3a6baa9 Add categories and suppliers with CRUD routes
- category table with hierarchical parent_id, sort ordering, soft-delete
- supplier table with contact info, account number, payment terms
- CRUD routes for both with search on suppliers
- Zod validation schemas in @forte/shared
- Products will link to suppliers via join table (many-to-many)
- 26 tests passing
2026-03-27 18:07:46 -05:00
Ryan Moon
5ff31ad782 Add accounts, members, and processor-agnostic payment linking
- account table (billing entity, soft-delete, company-scoped)
- member table (people on an account, is_minor from DOB)
- account_processor_link table (maps accounts to any payment
  processor — stripe, global_payments — instead of stripe_customer_id
  directly on account)
- Full CRUD routes + search (name, email, phone, account_number)
- Member routes nested under accounts with isMinor auto-calculation
- Zod validation schemas in @forte/shared
- 19 tests passing
2026-03-27 17:41:33 -05:00
Ryan Moon
979a9a2c00 Add user auth with JWT, switch to bun test
- User table with company_id FK, unique email, role enum
- Register/login routes with bcrypt + JWT token generation
- Auth plugin with authenticate decorator and role guards
- Login uses globally unique email (no company header needed)
- Dev-auth plugin kept as fallback when JWT_SECRET not set
- Switched from vitest to bun:test (vitest had ESM resolution
  issues with zod in Bun's module structure)
- Upgraded to zod 4
- Added Dockerfile.dev and API service to docker-compose
- 8 tests passing (health + auth)
2026-03-27 17:33:05 -05:00
Ryan Moon
c1cddd6b74 Phase 1: Monorepo scaffold, database, and dev environment
Turborepo monorepo with @forte/shared and @forte/backend workspaces.
Docker Compose dev env with PostgreSQL 16 + Valkey 8.
Fastify server with Pino JSON logging, request ID tracing, and
health endpoint. Drizzle ORM with company + location tables.

Includes:
- Root config (turbo, tsconfig, eslint, prettier)
- @forte/shared: types, schemas, currency/date utils
- @forte/backend: Fastify entry, plugins (database, redis, cors,
  error-handler, dev-auth), health route, Drizzle schema + migration
- Dev auth bypass via X-Dev-Company/Location/User headers
- Vitest integration test with clean DB per test (forte_test)
- Seed script for dev company + location
2026-03-27 14:51:46 -05:00