united-tattoo/AGENTS.md
Nicholai f372ab56de chore: add project configuration and agent files
Add BMAD, Claude, Cursor, and OpenCode configuration directories along with AGENTS.md documentation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 04:31:56 -07:00

13 KiB

Critial Instructions!

  • When you need to search docs, use context7 tools. Always search docs before writing any code.
  • When working with UI components, use shadcn or magicui tools. Always prefer premade components over coming up with them from scratch.
  • When working on UI or making large changes, use the playwright tools to navigate in the browser.
  • Whenever needing broader domain research, use the brave-search tool to search the web.

Project Overview

United Tattoo is a Next.js-based website for a tattoo studio in Fountain, CO. The application includes artist portfolios, booking systems, appointment management with CalDAV integration, and admin dashboards.

Stack:

  • Next.js 14 (App Router) with TypeScript
  • Cloudflare D1 (SQLite) for database
  • Cloudflare R2 for file storage
  • NextAuth.js for authentication
  • Deployed via OpenNext on Cloudflare Workers
  • ShadCN UI components + Tailwind CSS
  • Vitest for testing

Common Commands

Development

npm run dev                    # Start Next.js dev server (port 3000)
npm run dev:wrangler          # Build and preview with OpenNext/Cloudflare

Testing

npm run test                  # Run Vitest in watch mode
npm run test:ui              # Run Vitest with UI
npm run test:run             # Run tests once
npm run test:coverage        # Run tests with coverage report

Build & Deployment

npm run pages:build          # Build with OpenNext for Cloudflare
npm run build                # Standard Next.js build (standalone)
npm run preview              # Preview OpenNext build locally
npm run deploy               # Deploy to Cloudflare Pages

CI Commands

npm run ci:lint              # ESLint
npm run ci:typecheck         # TypeScript type checking (noEmit)
npm run ci:test              # Run tests with coverage
npm run ci:build             # Build for production
npm run ci:budgets           # Check bundle size budgets

Database Management

# Local database
npm run db:migrate:local                    # Apply schema to local D1
npm run db:studio:local                     # Show tables in local D1

# Preview (default) environment
npm run db:migrate                          # Apply schema to preview D1
npm run db:migrate:latest:preview           # Apply all migrations from sql/migrations/
npm run db:studio                           # Show tables in preview D1
npm run db:backup                           # Backup preview database

# Production environment
npm run db:migrate:up:prod                  # Apply specific migration to production
npm run db:migrate:latest:prod              # Apply all migrations to production
npm run db:backup:local                     # Backup local database

# Direct Wrangler commands
wrangler d1 execute united-tattoo --local --command="SELECT * FROM artists"
wrangler d1 execute united-tattoo --file=./sql/schema.sql

Code Quality

npm run lint                 # Run ESLint
npm run format               # Format code with Prettier
npm run format:check         # Check formatting without changing files
npm run security:audit       # Run npm audit

Architecture

Database Layer (lib/db.ts)

The database layer provides type-safe functions for interacting with Cloudflare D1. Key patterns:

  • Binding access: getDB(env) retrieves D1 from Cloudflare bindings via OpenNext's global symbol
  • R2 access: getR2Bucket(env) retrieves R2 bucket binding for file uploads
  • Namespace-style exports: Use db.artists.findMany(), db.portfolioImages.create(), etc.
  • JSON fields: specialties and tags are stored as JSON strings and parsed/stringified automatically

Main tables:

  • users - Authentication and user profiles with roles (SUPER_ADMIN, SHOP_ADMIN, ARTIST, CLIENT)
  • artists - Artist profiles linked to users, includes slug for URLs
  • portfolio_images - Artist portfolio work with tags and ordering
  • appointments - Booking appointments with CalDAV sync support
  • flash_items - Flash tattoo designs available for booking
  • site_settings - Global site configuration
  • artist_calendars - Nextcloud CalDAV calendar configuration per artist

Authentication (lib/auth.ts)

NextAuth.js setup with role-based access control and Nextcloud OAuth integration:

  • Primary Provider: Nextcloud OAuth (recommended for all users)

    • Artists and admins sign in via their Nextcloud credentials
    • Auto-provisioning: Users in 'artists' or 'shop_admins' Nextcloud groups are automatically created
    • Group-based role assignment:
      • admin or admins group → SUPER_ADMIN
      • shop_admins group (configurable) → SHOP_ADMIN
      • artists group (configurable) → ARTIST (with auto-created artist profile)
      • Users not in authorized groups are denied access
    • Requires: NEXTCLOUD_OAUTH_CLIENT_ID, NEXTCLOUD_OAUTH_CLIENT_SECRET, NEXTCLOUD_BASE_URL
  • Fallback Provider: Credentials (email/password)

    • Available only via /auth/signin?admin=true query parameter
    • Admin emergency access only
    • Dev mode: Any email/password combo creates a SUPER_ADMIN user for testing
    • Seed admin: nicholai@biohazardvfx.com is hardcoded as admin
  • Deprecated Providers: Google/GitHub OAuth (still configured but not actively used)

  • Session strategy: JWT (no database adapter currently)

  • Nextcloud Integration (lib/nextcloud-client.ts):

    • getNextcloudUserProfile(userId) - Fetch user details from Nextcloud OCS API
    • getNextcloudUserGroups(userId) - Get user's group memberships
    • determineUserRole(userId) - Auto-assign role based on Nextcloud groups
    • Uses service account credentials (NEXTCLOUD_USERNAME/PASSWORD) for API access
  • Helper functions:

    • requireAuth(role?) - Protect routes, throws if unauthorized
    • getArtistSession() - Get artist profile for logged-in artist users
    • canEditArtist(userId, artistId) - Check edit permissions
    • hasRole(userRole, requiredRole) - Check role hierarchy

CalDAV Integration (lib/calendar-sync.ts, lib/caldav-client.ts)

Bidirectional sync between database appointments and Nextcloud calendars:

  • Push to calendar: syncAppointmentToCalendar() - Called when creating/updating appointments
  • Pull from calendar: pullCalendarEventsToDatabase() - Background sync to import calendar events
  • Availability checking: checkArtistAvailability() - Real-time conflict detection using CalDAV
  • Per-artist calendars: Each artist can have their own Nextcloud calendar configured in artist_calendars table

Environment variables required:

  • NEXTCLOUD_BASE_URL
  • NEXTCLOUD_USERNAME
  • NEXTCLOUD_PASSWORD
  • NEXTCLOUD_CALENDAR_BASE_PATH (defaults to /remote.php/dav/calendars)

File Uploads (lib/r2-upload.ts, lib/upload.ts)

  • R2 storage: Files uploaded to Cloudflare R2 bucket
  • Image processing: HEIC to JPEG conversion, resizing, AVIF format support
  • Public URLs: Files served from R2 public URL
  • Upload API: /api/upload handles multipart form data

Environment Configuration (lib/env.ts)

Validates required environment variables at boot using Zod. Critical vars:

  • Database: DATABASE_URL, DIRECT_URL (Supabase URLs, though using D1)
  • Auth: NEXTAUTH_URL, NEXTAUTH_SECRET
  • Storage: AWS/R2 credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_BUCKET_NAME, AWS_ENDPOINT_URL)
  • Nextcloud OAuth (required for artist authentication):
    • NEXTCLOUD_BASE_URL - Nextcloud instance URL (e.g., https://portal.united-tattoos.com)
    • NEXTCLOUD_OAUTH_CLIENT_ID - OAuth app client ID from Nextcloud
    • NEXTCLOUD_OAUTH_CLIENT_SECRET - OAuth app client secret from Nextcloud
    • NEXTCLOUD_ARTISTS_GROUP - Group name for artists (default: "artists")
    • NEXTCLOUD_ADMINS_GROUP - Group name for admins (default: "shop_admins")
  • Nextcloud CalDAV (optional, for calendar sync):
    • NEXTCLOUD_USERNAME - Service account username
    • NEXTCLOUD_PASSWORD - Service account password or app-specific password
    • NEXTCLOUD_CALENDAR_BASE_PATH - CalDAV path (default: "/remote.php/dav/calendars")

Note: The env validation expects Supabase URLs but actual runtime uses Cloudflare D1 via bindings.

API Routes

All API routes follow Next.js App Router conventions (app/api/*/route.ts):

Public APIs:

  • /api/artists - List public artists with portfolio images
  • /api/artists/[id] - Get single artist by ID or slug
  • /api/public/migrate - Public migration endpoint (token-protected)

Protected APIs (require authentication):

  • /api/artists/me - Current artist's profile (ARTIST role)
  • /api/portfolio - CRUD for portfolio images
  • /api/flash/[artistId] - Manage flash tattoo items
  • /api/appointments - Appointment management
  • /api/upload - File upload to R2
  • /api/admin/* - Admin-only endpoints (stats, migrations, calendars)
  • /api/caldav/sync - Trigger CalDAV sync
  • /api/caldav/availability - Check artist availability

Frontend Structure

Pages:

  • / - Homepage (hero, artists, services, contact)
  • /artists - Artist listing
  • /artists/[id] - Individual artist portfolio (supports slug or ID)
  • /artists/[id]/book - Book with specific artist
  • /book - General booking page
  • /admin/* - Admin dashboard (analytics, portfolio, calendar, artist management, uploads)
  • /artist-dashboard/* - Artist-specific dashboard (profile, portfolio editing)
  • /auth/signin - Login page

Data Sources:

  • data/artists.ts - Static artist data (may be legacy, check if still used vs database)

Routing Notes

  • Middleware (middleware.ts): Handles permanent redirects (e.g., /artists/amari-rodriguez/artists/amari-kyss)
  • Dynamic routes: Artist pages work with both database IDs and slugs
  • Authentication: Admin and artist dashboard routes require appropriate roles

Testing

  • Framework: Vitest with React Testing Library
  • Config: vitest.config.ts (check for any custom setup)
  • Tests located alongside components or in __tests__/ directories

Bundle Size Budgets

Defined in package.json under budgets key:

  • TOTAL_STATIC_MAX_BYTES: 3,000,000 (3MB)
  • MAX_ASSET_BYTES: 1,500,000 (1.5MB)

Checked by scripts/budgets.mjs during CI.

Development Workflow

Working with Migrations

  1. Create new migration file in sql/migrations/ with format YYYYMMDD_NNNN_description.sql
  2. For local testing: npm run db:migrate:local
  3. For preview: npm run db:migrate:latest:preview
  4. For production: npm run db:migrate:latest:prod

Migrations are also tracked in sql/migrations_up/ for Wrangler's built-in migration system.

Working with Artists

Artists have both a user account and an artist profile:

  1. User created in users table with role ARTIST
  2. Artist profile in artists table linked via user_id
  3. Slug auto-generated from name, handles duplicates with numeric suffix
  4. Portfolio images in portfolio_images table
  5. Flash items in flash_items table (optional, new feature)

Adding New Features

When adding database tables:

  1. Add to sql/schema.sql
  2. Create migration file in sql/migrations/
  3. Update TypeScript types in types/database.ts
  4. Add CRUD functions to lib/db.ts
  5. Create API routes if needed
  6. Update this CLAUDE.md if it's a major architectural change

CI/CD Pipeline

Located in .gitea/workflows/:

  • ci.yaml - Main CI pipeline (lint, typecheck, test, build, budgets)
  • security.yaml - Security audits
  • performance.yaml - Performance checks
  • deploy.yaml - Deployment to Cloudflare

The CI enforces:

  • ESLint passing
  • TypeScript compilation (with ignoreBuildErrors: true for builds but strict for typecheck)
  • Test coverage
  • Bundle size budgets
  • Migration dry-run (best-effort with local D1)

Known Configuration Quirks

  • TypeScript errors ignored during build (next.config.mjs): typescript.ignoreBuildErrors: true allows builds to succeed even with type errors. CI separately runs tsc --noEmit to catch them.
  • Images unoptimized: Next.js Image optimization disabled for Cloudflare Workers compatibility
  • Standalone output: Docker builds use output: "standalone" mode
  • Node.js compatibility: Cloudflare Workers use nodejs_compat flag in wrangler.toml

Deployment

Production URL: https://united-tattoos.com

Deploy command: npm run pages:build && wrangler deploy

The deployment process:

  1. Build with OpenNext: npm run pages:build → outputs to .vercel/output/static
  2. Deploy to Cloudflare: wrangler pages deploy .vercel/output/static

Docker Support

Dockerfile included for self-hosting:

docker build -t united-tattoo:latest .
docker run --rm -p 3000:3000 -e PORT=3000 united-tattoo:latest

Uses Next.js standalone mode for minimal image size.

Important Notes

  • Always test database changes locally first with --local flag
  • The migration token (MIGRATE_TOKEN) protects public migration endpoints
  • CalDAV integration is optional; appointments work without it
  • Role hierarchy: CLIENT < ARTIST < SHOP_ADMIN < SUPER_ADMIN
  • Flash items feature may not exist in older database schemas (lib/db.ts tolerates missing table)