united-tattoo/docs/brownfield-architecture.md

378 lines
17 KiB
Markdown

# United Tattoo — Brownfield Architecture Document (Focused: Epic A — Admin Dashboard & Artist Management)
This document captures the CURRENT STATE of the United Tattoo codebase relevant to Admin Dashboard & Artist Management (Epic A). It reflects actual patterns, technical debt, and constraints to enable AI agents to work effectively on enhancements in this area.
## Document Scope
Focused on areas relevant to: Admin invitations & onboarding, RBAC, artist profiles, portfolio and asset management, settings, and admin-only API routes.
### Change Log
| Date | Version | Description | Author |
| ---------- | ------- | ------------------------------------------- | ---------------- |
| 2025-09-18 | 1.0 | Initial brownfield analysis (Admin focus) | Architect Agent |
---
## Quick Reference — Key Files and Entry Points
### Critical Files for Understanding the System
- App entry and layouts
- app/layout.tsx, app/ClientLayout.tsx, app/page.tsx
- app/admin/layout.tsx, app/admin/page.tsx, plus nested admin pages
- Routing and security
- middleware.ts (route protection and public-route policy)
- lib/auth.ts (NextAuth config, JWT callbacks, role assignment)
- Cloudflare/OpenNext deployment
- wrangler.toml (D1/R2 bindings, compatibility flags)
- next.config.mjs (output: standalone, images.unoptimized)
- open-next.config.ts (present; not analyzed in depth here)
- Data layer and storage
- sql/schema.sql (Cloudflare D1 schema)
- lib/db.ts (D1 helpers and CRUD for artists, portfolio, appointments, settings; R2 bucket getter)
- lib/r2-upload.ts (R2 upload manager, portfolio/profile helpers)
- Validation and types
- lib/validations.ts (Zod schemas for users, artists, portfolio images, appointments, site settings, forms)
- types/database.ts (domain models and Cloudflare D1/R2 ambient types)
- Public docs (reference)
- docs/PRD.md (feature scope; this doc is scoped to Epic A)
- docs/Architecture.md, docs/architecture.md (legacy/other architecture docs)
### Admin UI Pages (App Router)
- app/admin/analytics/page.tsx
- app/admin/artists/page.tsx
- app/admin/artists/[id]/page.tsx
- app/admin/artists/new/page.tsx
- app/admin/calendar/page.tsx
- app/admin/portfolio/page.tsx
- app/admin/settings/page.tsx
- app/admin/uploads/page.tsx
### Admin/Related API Routes (Route Handlers)
- app/api/admin/migrate/route.ts
- app/api/admin/stats/route.ts
- app/api/artists/route.ts
- app/api/portfolio/route.ts
- app/api/portfolio/[id]/route.ts
- app/api/portfolio/bulk-delete/route.ts
- app/api/portfolio/stats/route.ts
- app/api/files/route.ts
- app/api/files/bulk-delete/route.ts
- app/api/files/folder/route.ts
- app/api/files/stats/route.ts
- app/api/settings/route.ts
- app/api/users/route.ts
- app/api/appointments/route.ts (admin-usable but spans Booking epic as well)
- app/api/auth/[...nextauth]/ (NextAuth core)
---
## High-Level Architecture
### Technical Summary (Actual)
- Next.js 14.2.16 (App Router), React 18, Tailwind 4.x, shadcn/ui patterns
- Cloudflare Pages + Workers via OpenNext adapter
- Cloudflare D1 for relational data (env.DB); Cloudflare R2 for object storage (env.R2_BUCKET)
- Auth via next-auth (JWT session strategy, Credentials + optional Google/GitHub)
- Validation via Zod across routes and forms
- Client state: TanStack Query; forms via react-hook-form
### Actual Tech Stack (from package.json and code)
| Category | Technology | Version | Notes |
| -------------- | -------------------------------- | ----------- | ----- |
| Runtime | Cloudflare Pages/Workers | Wrangler 4 | OpenNext adapter, nodejs_compat enabled |
| Framework | Next.js (App Router) | 14.2.16 | output: standalone; images.unoptimized |
| UI | shadcn/ui + Radix primitives | mixed | shadcn patterns across pages/components |
| State | @tanstack/react-query | ^5.89.0 | Devtools present |
| Forms | react-hook-form + zod resolver | ^7.60.0 | Zod schemas in lib/validations.ts |
| Auth | next-auth (JWT) | ^4.24.11 | Credentials; optional Google/GitHub |
| DB | Cloudflare D1 | — | Access via global env bindings |
| Storage | Cloudflare R2 | — | via env.R2_BUCKET; custom public URL expected |
| Dev/Test | Vitest + RTL | ^3.2.4 | tests under __tests__/ |
| Deploy | OpenNext Cloudflare | ^1.8.2 | pages:build → .vercel/output/static |
### Repository Structure Reality Check
- Polyrepo (single app)
- Package manager: npm (scripts define build/preview/deploy and D1 ops)
- Notable:
- next.config.mjs ignores TS and ESLint errors during build (risk: hidden issues)
- images.unoptimized: Cloudflare Images or custom loader recommended for prod
- Zod env validator requires many variables not used by D1 codepaths (see debt)
---
## Source Tree and Module Organization
### Project Structure (Actual, abridged)
```
project-root/
├── app/
│ ├── admin/ # Admin UI pages
│ ├── api/ # Route handlers (REST-ish)
│ ├── (public sections) # /artists, /aftercare, etc.
│ └── auth/ # auth/signin pages
├── components/ # UI components (public/admin)
├── lib/ # auth, db (D1/R2), uploads, validations, utils
├── sql/schema.sql # D1 schema
├── types/database.ts # domain types and Cloudflare ambient types
├── docs/ # PRD and architecture docs
├── wrangler.toml # Cloudflare bindings/config
├── next.config.mjs # Next build config
└── open-next.config.ts # OpenNext adapter config
```
### Key Admin Modules and Their Purpose
- RBAC and route protection
- middleware.ts: protects /admin and API subsets; maintains public routes list.
- lib/auth.ts: next-auth config. Credentials provider returns SUPER_ADMIN for dev users; JWT carries role.
- Data layer and storage
- lib/db.ts: D1 CRUD for artists, portfolio images, appointments, site settings; getDB/getR2Bucket read bindings from Cloudflare context or globals (OpenNext).
- lib/r2-upload.ts: Upload manager wrapping R2; bulk uploads; portfolio/profile helpers; expects R2_PUBLIC_URL for public reads.
- Validation and types
- lib/validations.ts: Comprehensive Zod schemas for admin entities (artists, portfolio images, settings) and form payloads.
- types/database.ts: Roles, entities, appointment status; Cloudflare D1/R2 ambient types to ease dev.
- Admin APIs (examples)
- app/api/artists/route.ts:
- GET: lists artists with filters and pagination (in-memory filtering after fetch)
- POST: requires SHOP_ADMIN (or higher); validates body; creates artist tied to session user
---
## Data Models and APIs
### Data Models (from sql/schema.sql and types)
- users (id TEXT PK, email UNIQUE, name, role enum, avatar, timestamps)
- artists (id TEXT PK, user_id FK users, name, bio, specialties JSON string, social, is_active, hourly_rate, timestamps)
- portfolio_images (id TEXT PK, artist_id FK, url, caption, tags JSON string, order_index, is_public, created_at)
- appointments (id TEXT PK, artist_id FK, client_id FK users, title, description, times, status enum, amounts, notes, timestamps)
- availability (id TEXT PK, artist_id FK, day_of_week int, start_time/end_time HH:mm, is_active)
- site_settings (id TEXT PK, fields for studio and branding; id is 'default' row by convention)
- file_uploads (id TEXT PK, metadata, url, uploaded_by FK users)
Notes:
- IDs are TEXT and often UUIDs; site_settings uses a constant id 'default'.
- JSON stored as TEXT (specialties, tags, social_media, business_hours).
### Admin-Relevant Route Handlers (observed)
- /api/admin/migrate, /api/admin/stats
- /api/artists (GET, POST)
- /api/portfolio, /api/portfolio/[id], /api/portfolio/bulk-delete, /api/portfolio/stats
- /api/files, /api/files/bulk-delete, /api/files/folder, /api/files/stats
- /api/settings
- /api/users
- /api/appointments (shared with booking)
Patterns:
- Validations with Zod schemas from lib/validations.ts
- D1 access through lib/db.ts helpers using Cloudflare env context (context?.env in handlers)
- Role checks via middleware + requireAuth(UserRole.*) on sensitive operations
---
## Technical Debt and Known Issues (REALITY)
1. Env validation vs Cloudflare bindings
- lib/env.ts requires DATABASE_URL, DIRECT_URL, and multiple AWS_* variables.
- Actual DB access in lib/db.ts uses Cloudflare D1 binding (env.DB); no DATABASE_URL is used.
- R2 uploads build public URLs using process.env.R2_PUBLIC_URL, but env.ts does not validate R2_PUBLIC_URL, and wrangler.toml does not set it. Missing/invalid public URL will break returned URLs for uploaded assets.
2. SiteSettings id handling
- DB uses id='default' singleton row.
- lib/validations.ts siteSettingsSchema expects id to be a UUID; mismatch with actual data causes invalidation/confusion and could break validation workflows.
3. Portfolio image ordering field name mismatch
- DB column: order_index
- lib/validations.ts schemas use 'order' as the property name; not aligned with DB and lib/db.ts update semantics. Risk of incorrect mapping when integrating UI forms → API → DB.
4. Auth and security (development shortcuts)
- Credentials provider in lib/auth.ts accepts any credentials and assigns SUPER_ADMIN by default for non-whitelisted users (dev convenience).
- No DB adapter (JWT-only). RBAC is token-based; no persistent user store beyond D1 writes that may occur when creating artists (which auto-creates ARTIST users).
- This is acceptable for local/dev but must be hardened before production.
5. Middleware routing inconsistencies
- middleware.ts checks pathname.startsWith("/artist") (singular) for “Artist-specific routes” role gating.
- Public pages are under /artists/... (plural). There is also an allow rule for /^\/artists\/[^\/]+$/ as public. Mixed naming increases cognitive load; the singular check may be a stale/unused path.
6. Build config hides issues
- next.config.mjs ignores TypeScript and ESLint errors during build. This can allow broken types or lint issues to ship; not suitable for CI/CD production gates.
7. Schema/tooling inconsistencies
- sql/schema.sql header suggests executing “wrangler d1 execute united-tattoo-db …” while package.json uses database name “united-tattoo”. Mismatch in naming in comments could confuse operators.
8. R2 public access patterns
- r2-upload.ts assumes a simple base URL concatenation for public reads. Cloudflare R2 often requires either a custom public domain or R2 public buckets; the base URL must be configured and documented. No presigned upload flow yet (stubbed).
---
## Integration Points and External Dependencies
### External Services
| Service | Purpose | Integration Type | Key Files |
| ------------- | ---------------- | ---------------- | -------------------- |
| Cloudflare D1 | Relational DB | Worker binding | wrangler.toml, lib/db.ts |
| Cloudflare R2 | Object storage | Worker binding | wrangler.toml, lib/db.ts, lib/r2-upload.ts |
| NextAuth | Authentication | Providers/JWT | lib/auth.ts, app/api/auth/[...nextauth]/ |
| OpenNext | Next→Workers | Build adapter | package.json scripts, open-next.config.ts, wrangler.toml |
### Internal Integration Points
- Admin UI → API routes: Admin pages consume /api/* endpoints for CRUD on artists, portfolio images, settings, and files.
- Validation: UI forms align to Zod schemas (lib/validations.ts); ensure property names match DB contract (see debt on order/order_index).
- Role enforcement: middleware.ts + requireAuth(UserRole.*) enforce admin-only access.
---
## Development and Deployment
### Local Development Setup (Actual)
- Install deps: npm install
- D1 DB create/migrate:
- npm run db:create (creates DB)
- npm run db:migrate or npm run db:migrate:local (executes sql/schema.sql)
- Preview Workers runtime locally:
- npm run dev:wrangler (build via OpenNext then preview)
- or npm run preview (OpenNext preview)
- App dev server:
- npm run dev (Next dev server; note some Cloudflare bindings are only available via OpenNext preview)
Required environment (observed/assumed):
- Wrangler configured/login
- Cloudflare bindings as per wrangler.toml
- NEXTAUTH_SECRET and NEXTAUTH_URL set appropriately
- R2_PUBLIC_URL should be set for correct public asset URLs (not currently validated by env.ts)
### Build and Deployment Process
- Build (OpenNext): npm run pages:build
- Preview: npm run preview
- Deploy to Cloudflare Pages: npm run deploy (wrangler pages deploy .vercel/output/static)
- wrangler.toml:
- compatibility_date >= 2024-09-23
- compatibility_flags ["nodejs_compat"]
- D1 and R2 bindings configured
---
## Testing Reality
- Unit/component tests: Vitest with RTL (see __tests__/)
- E2E: Not observed in repo
- Coverage: Test scripts available; actual coverage not measured here
- QA: Manual likely; shadcn components + form/zod patterns amenable to RTL coverage
---
## If Enhancement PRD Provided — Impact Analysis (Admin Focus)
Based on PRD Epic A, the following files/modules are most likely to be affected:
### Files/Modules Likely to Need Modification
- UI
- app/admin/artists/* (listing, detail, new)
- app/admin/uploads/page.tsx (batch upload flows, progress)
- app/admin/settings/page.tsx (site settings form)
- components/admin/* (admin-specific components)
- APIs
- app/api/artists/route.ts (filters, pagination, create/linking behaviors)
- app/api/portfolio/route.ts and /[id]/route.ts (CRUD, ordering, tags)
- app/api/files/* (upload metadata, deletion, folder organization)
- app/api/settings/route.ts (singleton settings updates)
- app/api/users/route.ts (invite flows and role assignment)
- Lib
- lib/r2-upload.ts (public URL handling, presigned URL path, folder conventions)
- lib/db.ts (query optimizations, joining, business rules, activity logs)
- lib/validations.ts (resolve property mismatches; align with DB)
- lib/auth.ts (tighten dev-only flows, enforce role creation pathways)
- middleware.ts (route gate consistency for admin vs artists)
### New Files/Modules Potentially Needed
- Activity Logs: D1 table and APIs for admin auditing per PRD (FR-A5.x)
- Invite & Onboarding APIs: Route handlers and email delivery for A1.x
- Moderation Queue: Table and APIs for uploads moderation hook (FR-A5.3)
- Image Processing: Server-side transformations (could leverage Cloudflare Images; not present now)
### Integration Considerations
- Enforce consistent role model end-to-end (Invite → Signup → Role assignment)
- Align Zod schemas with DB column names and types (e.g., order_index)
- R2 public URL and folder conventions should be codified and validated
- Consider introducing DB migrations governance and seed paths for admin roles
---
## Appendix — Useful Commands and Scripts
From package.json:
```bash
# Dev & Build
npm run dev
npm run pages:build
npm run preview
npm run deploy
# D1 Management
npm run db:create
npm run db:migrate
npm run db:migrate:local
npm run db:studio
npm run db:studio:local
# Tests
npm run test
npm run test:ui
npm run test:run
npm run test:coverage
```
---
## Gotchas and Practical Notes (Must Read)
- To use D1/R2 in preview, prefer OpenNext preview (npm run preview) where bindings are available via global Cloudflare context. Running plain next dev may not expose env.DB/env.R2_BUCKET without additional shims.
- Set NEXTAUTH_SECRET and NEXTAUTH_URL for auth to function correctly; in preview/production, wrangler.toml provides NEXTAUTH_URL for env scopes.
- Configure R2_PUBLIC_URL; otherwise URLs returned by upload endpoints may be unusable externally.
- next.config.mjs ignoring errors is risky; fix warnings and enable strict CI gates for production readiness.
- Site settings expect a singleton row with id 'default'; update APIs rely on this assumption.
---
## Recommended Fixes (Non-Blocking, Advisory)
- Update env validation to match Cloudflare bindings reality
- Either remove DATABASE_URL from required env, or separate “Worker runtime” env from “local dev” .env with appropriate fallbacks.
- Add R2_PUBLIC_URL to Zod-validated schema.
- Align schemas and properties
- Rename portfolio image 'order' → 'orderIndex' in Zod schemas and UI forms to match DB.
- SiteSettings schema: do not require UUID id; or adapt DB to UUID if desired (and update code).
- Security hardening
- Replace dev-only SUPER_ADMIN behavior with an invite/token-based onboarding flow.
- Implement NextAuth adapter (e.g., D1 via Drizzle/Kysely or Supabase per .clinerules) if persistence is required for sessions and users.
- Middleware consistency
- Remove/rename singular '/artist' gating or align with actual '/artists' conventions; centralize route constants.
- CI/CD quality
- Re-enable TypeScript and ESLint checks to catch regressions early.
- Add component and route handler tests for admin flows (artists, portfolio, settings).
---
This document reflects the actual state of the system for Admin Dashboard & Artist Management, including technical debt and real-world constraints. It references concrete files and paths to accelerate development work by AI agents and maintainers.