united-tattoo/docs/brownfield-architecture.md

17 KiB

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:

# 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.

  • 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.