2025-10-07 00:23:17 -06:00
2025-10-07 00:23:17 -06:00
2025-10-30 05:10:48 -06:00
2025-10-07 00:23:17 -06:00
2025-10-29 15:58:55 -06:00
2025-10-30 05:10:48 -06:00
2025-10-30 03:57:12 -06:00
2025-10-20 21:13:06 -06:00
2025-09-16 21:36:20 -06:00
2025-09-16 21:36:20 -06:00
2025-09-16 21:36:20 -06:00
2025-10-29 15:58:55 -06:00
2025-09-16 21:36:20 -06:00
2025-09-16 21:36:20 -06:00
2025-09-16 21:36:20 -06:00
2025-10-07 01:58:10 -06:00

DEPLOYMENT COMMAND

npm run pages:build && wrangler deploy

Contributors Forks Stargazers Issues LinkedIn


United Tattoo Logo

United Tattoo

Professional tattoo studio platform built on Cloudflare's edge network
Lightning-fast • Artist-first • Calendar-integrated

View Live Site »

Quick Start · Report Bug · Request Feature


Table of Contents

  1. About The Project
  2. Tech Stack
  3. Architecture
  4. Getting Started
  5. Development
  6. Deployment
  7. Documentation
  8. Roadmap
  9. Contributing
  10. License
  11. Contact

About The Project

United Tattoo Studio

United Tattoo is a comprehensive tattoo studio management platform designed for United Tattoo, a professional tattoo studio in Fountain, Colorado. This platform seamlessly integrates artist portfolios, appointment booking, flash tattoo marketplace, and real-time calendar synchronization—all running on Cloudflare's global edge network for exceptional performance.

Key Features

Artist Management

  • Dynamic portfolio galleries with Instagram integration
  • SEO-friendly artist pages (/artists/artist-name)
  • Specialty tracking and hourly rate management
  • Active/inactive status control
  • Self-service artist dashboard

Flash Tattoo Marketplace

  • Pre-drawn designs available for instant booking
  • Responsive carousel UI with smooth scrolling
  • Pricing and availability management
  • Direct integration with booking system

Smart Booking System

  • Client appointment scheduling
  • Multiple status workflows (PENDING → CONFIRMED → IN_PROGRESS → COMPLETED)
  • Deposit and payment tracking
  • Real-time conflict detection

CalDAV/Nextcloud Integration

  • Bidirectional calendar sync with Nextcloud
  • Real-time availability checking
  • Per-artist calendar configuration
  • Sync logging and monitoring
  • Automatic conflict prevention

Nextcloud OAuth Authentication

  • Auto-provisioning based on Nextcloud groups
  • Role-based access control (SUPER_ADMIN, SHOP_ADMIN, ARTIST, CLIENT)
  • Seamless integration with existing artist portal
  • Emergency admin credentials fallback

Comprehensive Dashboards

  • Admin Dashboard: Analytics, portfolio management, calendar oversight, artist management
  • Artist Dashboard: Profile editing, portfolio management, appointment view
  • File upload management with R2 cloud storage

Why United Tattoo?

Edge-First Architecture Built on Cloudflare Workers for global performance with <100ms response times worldwide.

Nextcloud-First Integration Seamless authentication and calendar sync with existing artist infrastructure—no duplicate account management.

Zero-ORM Overhead Direct Cloudflare D1 integration via bindings for maximum performance.

Bundle Size Enforced CI/CD enforces 3MB static budget to ensure lightning-fast page loads.

Test Coverage Comprehensive Vitest test suite with coverage tracking.

(back to top)


Tech Stack

Core Framework

Next.js React TypeScript

Cloudflare Infrastructure

Cloudflare Workers OpenNext

UI & Styling

ShadCN UI Tailwind CSS Framer Motion

Database & Storage

Cloudflare D1 Cloudflare R2

Authentication & Integration

NextAuth.js CalDAV

Testing & Quality

Vitest ESLint Prettier

Complete Dependency List

Core Dependencies:

  • Next.js 14.2.33 (App Router)
  • React 18
  • TypeScript 5
  • OpenNext for Cloudflare 1.8.2

UI Components:

  • 30+ Radix UI components (via ShadCN)
  • Framer Motion 12.23.24 (animations)
  • Lenis (smooth scrolling)
  • next-themes 0.4.6 (dark mode)

Data & State:

  • TanStack React Query 5.89.0
  • Zod 3.25.67 (validation)
  • React Hook Form 7.60.0

Calendar & CalDAV:

  • tsdav 2.1.5 (CalDAV client)
  • react-big-calendar 1.19.4
  • ical.js 1.5.0
  • date-fns

File Processing:

  • Sharp 0.34.4 (image processing)
  • heic-convert 2.1.0 (HEIC → JPEG)
  • AWS SDK for S3 3.890.0 (R2 compatible)

Testing:

  • Vitest 3.2.4
  • React Testing Library 16.3.0
  • @vitest/coverage-v8

(back to top)


Architecture

graph TB
    subgraph "Client Layer"
        Browser[Browser]
    end

    subgraph "Edge Layer - Cloudflare Workers"
        NextJS[Next.js 14<br/>App Router]
        OpenNext[OpenNext<br/>Adapter]
    end

    subgraph "Cloudflare Services"
        D1[(D1 Database<br/>SQLite)]
        R2[R2 Storage<br/>Files & Images]
    end

    subgraph "External Services"
        Nextcloud[Nextcloud<br/>OAuth + CalDAV]
    end

    Browser --> NextJS
    NextJS --> OpenNext
    OpenNext --> D1
    OpenNext --> R2
    OpenNext <--> Nextcloud

    style Browser fill:#667eea,color:#fff
    style NextJS fill:#000,color:#fff
    style OpenNext fill:#f38020,color:#fff
    style D1 fill:#f38020,color:#fff
    style R2 fill:#f38020,color:#fff
    style Nextcloud fill:#0082c9,color:#fff

System Flow

Authentication Flow
User Request → NextAuth.js
    ↓
    ├─ Nextcloud OAuth (Primary)
    │   ├─ Check user groups (via OCS API)
    │   ├─ Assign role based on group
    │   │   ├─ "admins" → SUPER_ADMIN
    │   │   ├─ "shop_admins" → SHOP_ADMIN
    │   │   ├─ "artists" → ARTIST (auto-create profile)
    │   │   └─ Other → Deny access
    │   └─ Create/update user in D1
    │
    ├─ Credentials (Fallback - Admin Only)
    │   ├─ Query parameter: ?admin=true
    │   ├─ Email/password validation
    │   └─ Dev mode: Auto-create SUPER_ADMIN
    │
    └─ Session Created (JWT)
CalDAV Sync Flow
Appointment Created/Updated
    ↓
syncAppointmentToCalendar()
    ↓
    ├─ Get artist's Nextcloud calendar config
    ├─ Connect to CalDAV server
    ├─ Create/update VEVENT
    ├─ Store calendar_event_uid in D1
    └─ Log sync operation

Background Sync (Periodic)
    ↓
pullCalendarEventsToDatabase()
    ↓
    ├─ Fetch events from CalDAV
    ├─ Compare with D1 appointments
    ├─ Update changed appointments
    ├─ Import external events
    └─ Log sync results
Database Schema

Core Tables:

Table Description Key Fields
users Authentication & profiles id, email, name, role, nextcloud_user_id
artists Artist profiles id, user_id, slug, bio, specialties (JSON), hourly_rate
portfolio_images Artist work gallery id, artist_id, image_url, tags (JSON), order, is_public
flash_items Pre-drawn designs id, artist_id, title, price, is_available
appointments Booking system id, artist_id, client_id, status, start_time, end_time, deposit
availability Artist schedules id, artist_id, day_of_week, start_time, end_time
artist_calendars CalDAV configuration id, artist_id, calendar_url, username, password
calendar_sync_logs Sync monitoring id, artist_id, operation, status, details
site_settings Global config key, value, type
file_uploads R2 file tracking id, user_id, file_url, file_size, mime_type

Indexes: Optimized for artist lookups, appointment queries, and calendar sync operations.

Project Structure

united-tattoo/
├── app/                      # Next.js App Router
│   ├── (public)/            # Public pages (no auth)
│   │   ├── artists/         # Artist profiles & portfolios
│   │   ├── book/            # Booking pages
│   │   └── page.tsx         # Homepage
│   ├── admin/               # Admin dashboard (SUPER_ADMIN, SHOP_ADMIN)
│   ├── artist-dashboard/    # Artist self-service (ARTIST)
│   ├── api/                 # API routes
│   │   ├── artists/         # Artist CRUD
│   │   ├── appointments/    # Booking endpoints
│   │   ├── caldav/          # Calendar sync
│   │   ├── flash/           # Flash items
│   │   └── upload/          # R2 file uploads
│   └── auth/                # NextAuth pages
├── lib/                     # Core logic
│   ├── db.ts               # D1 database functions
│   ├── auth.ts             # NextAuth config + helpers
│   ├── caldav-client.ts    # CalDAV integration
│   ├── calendar-sync.ts    # Sync logic
│   ├── nextcloud-client.ts # Nextcloud API client
│   ├── r2-upload.ts        # R2 file handling
│   └── env.ts              # Environment validation
├── components/              # React components
│   ├── ui/                 # ShadCN components
│   └── ...                 # Feature components
├── sql/                     # Database
│   ├── schema.sql          # Main schema
│   └── migrations/         # Migration files
├── docs/                    # Documentation
├── .gitea/workflows/       # CI/CD pipelines
└── wrangler.toml           # Cloudflare config

(back to top)


Getting Started

Prerequisites

Required Accounts & Access

Before starting, ensure you have:

  • Cloudflare Account with access to Workers, D1, R2, and Pages
  • Nextcloud Instance with admin access for OAuth app creation
  • Node.js 18+ and npm installed
  • Wrangler CLI version 3+

Install Wrangler:

npm install -g wrangler

Cloudflare Resources Required:

  • Workers & Pages: For hosting the application
  • D1 Database: SQLite database (named united-tattoo)
  • R2 Buckets: File storage (united-tattoo, united-tattoo-inc-cache)

Installation

  1. Clone the repository

    git clone https://git.biohazardvfx.com/nicholai/united-tattoo.git
    cd united-tattoo
    
  2. Install dependencies

    npm install
    
  3. Configure environment variables

    cp .env.example .env.local
    # Edit .env.local with your credentials (see Environment Variables section)
    
  4. Set up Cloudflare D1 database

    # Create D1 database (if not exists)
    wrangler d1 create united-tattoo
    
    # Apply schema to local D1
    npm run db:migrate:local
    
    # Apply schema to preview/production
    npm run db:migrate:latest:preview
    
  5. Configure Nextcloud OAuth

    See docs/NEXTCLOUD-OAUTH-SETUP.md for detailed instructions on:

    • Creating OAuth application in Nextcloud
    • Configuring group-based role assignment
    • Setting up service account for CalDAV
  6. Run locally

    # Next.js dev server (port 3000)
    npm run dev
    
    # OR with Cloudflare Workers simulation
    npm run dev:wrangler
    
  7. Access the application

    • Local: http://localhost:3000
    • Sign in: http://localhost:3000/auth/signin
    • Admin signin: http://localhost:3000/auth/signin?admin=true

(back to top)

Environment Variables

Required Variables
Variable Description Example
Database
DATABASE_URL PostgreSQL URL (legacy, using D1 via bindings) postgresql://...
DIRECT_URL Direct database connection (optional) postgresql://...
Authentication
NEXTAUTH_URL Application URL https://united-tattoos.com
NEXTAUTH_SECRET NextAuth secret key Generate with openssl rand -base64 32
File Storage (Cloudflare R2)
AWS_ACCESS_KEY_ID R2 access key ID From Cloudflare dashboard
AWS_SECRET_ACCESS_KEY R2 secret access key From Cloudflare dashboard
AWS_REGION Region (any valid AWS region) us-east-1
AWS_BUCKET_NAME R2 bucket name united-tattoo
AWS_ENDPOINT_URL R2 endpoint URL https://<account-id>.r2.cloudflarestorage.com
Nextcloud OAuth
NEXTCLOUD_BASE_URL Nextcloud instance URL https://portal.united-tattoos.com
NEXTCLOUD_OAUTH_CLIENT_ID OAuth app client ID From Nextcloud admin
NEXTCLOUD_OAUTH_CLIENT_SECRET OAuth app client secret From Nextcloud admin
NEXTCLOUD_ARTISTS_GROUP Group name for artists artists
NEXTCLOUD_ADMINS_GROUP Group name for shop admins shop_admins
Optional Variables
Variable Description Default
CalDAV (Calendar Sync)
NEXTCLOUD_USERNAME Service account username
NEXTCLOUD_PASSWORD Service account password/app password
NEXTCLOUD_CALENDAR_BASE_PATH CalDAV base path /remote.php/dav/calendars
OAuth Providers (Deprecated)
GOOGLE_CLIENT_ID Google OAuth client ID
GOOGLE_CLIENT_SECRET Google OAuth client secret
GITHUB_CLIENT_ID GitHub OAuth client ID
GITHUB_CLIENT_SECRET GitHub OAuth client secret
Migration
MIGRATE_TOKEN Token for public migration endpoint Generate random string
Analytics
VERCEL_ANALYTICS_ID Vercel Analytics ID
Pro Tip: Use .env.local for local development and configure production variables in Cloudflare dashboard under Settings → Environment Variables.

(back to top)


Development

Common Commands

Command Description
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 interactive UI
npm run test:run Run tests once (CI mode)
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
Code Quality
npm run ci:lint Run ESLint
npm run ci:typecheck TypeScript type checking (noEmit)
npm run ci:test Run tests with coverage (CI)
npm run ci:build Build for production (CI)
npm run ci:budgets Check bundle size budgets
Formatting
npm run lint Run ESLint
npm run format Format code with Prettier
npm run format:check Check formatting without changing files

Database Management

Migration Commands

Local Database:

# Apply schema to local D1
npm run db:migrate:local

# View tables in local D1
npm run db:studio:local

# Backup local database
npm run db:backup:local

Preview Environment (default):

# Apply schema to preview D1
npm run db:migrate

# Apply all migrations from sql/migrations/
npm run db:migrate:latest:preview

# View tables in preview D1
npm run db:studio

# Backup preview database
npm run db:backup

Production Environment:

# Apply specific migration to production
npm run db:migrate:up:prod

# Apply all migrations to production
npm run db:migrate:latest:prod

Direct Wrangler Commands:

# Execute SQL query on local D1
wrangler d1 execute united-tattoo --local --command="SELECT * FROM artists"

# Apply schema file
wrangler d1 execute united-tattoo --file=./sql/schema.sql

# Execute with preview (remote)
wrangler d1 execute united-tattoo --command="SELECT * FROM users"
Creating New Migrations
  1. Create migration file in sql/migrations/ with format:

    YYYYMMDD_NNNN_description.sql
    

    Example: 20250130_0001_add_flash_items_table.sql

  2. Write your SQL migration:

    -- Add flash_items table
    CREATE TABLE IF NOT EXISTS flash_items (
      id TEXT PRIMARY KEY,
      artist_id TEXT NOT NULL,
      title TEXT NOT NULL,
      price REAL NOT NULL,
      is_available INTEGER DEFAULT 1,
      created_at TEXT DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (artist_id) REFERENCES artists(id) ON DELETE CASCADE
    );
    
  3. Test locally:

    npm run db:migrate:local
    
  4. Apply to preview:

    npm run db:migrate:latest:preview
    
  5. Apply to production (when ready):

    npm run db:migrate:latest:prod
    

(back to top)


Deployment

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

Deployment Process

Quick Deploy Command

npm run pages:build && wrangler deploy

Step-by-Step:

  1. Build with OpenNext

    npm run pages:build
    

    This creates a Cloudflare-compatible build in .vercel/output/static

  2. Deploy to Cloudflare Pages

    wrangler pages deploy .vercel/output/static
    
  3. Verify deployment

CI/CD Pipeline

The project uses Gitea workflows for automated CI/CD:

Workflows:

  • ci.yaml - Main CI pipeline

    • ESLint
    • TypeScript type checking
    • Vitest tests with coverage
    • Production build
    • Bundle size budgets
  • deploy.yaml - Automated deployments

    • Triggers on push to main branch
    • Builds and deploys to Cloudflare
  • security.yaml - Security audits

    • npm audit
    • Dependency vulnerability scanning
  • performance.yaml - Performance checks

    • Bundle size analysis
    • Preview smoke tests

Bundle Size Budgets:

  • Total static assets: 3MB max
  • Individual assets: 1.5MB max

Enforced by scripts/budgets.mjs in CI.

Docker Support

Docker Deployment (Alternative)

The project includes a Dockerfile for self-hosting:

# Build image
docker build -t united-tattoo:latest .

# Run container
docker run --rm -p 3000:3000 \
  -e PORT=3000 \
  -e NEXTAUTH_URL=http://localhost:3000 \
  -e NEXTAUTH_SECRET=your-secret \
  # ... other env vars
  united-tattoo:latest

Note: Docker deployment bypasses Cloudflare Workers and uses Next.js standalone mode. For production, Cloudflare deployment is recommended.

(back to top)


Documentation

Comprehensive documentation is available in the docs/ directory:

Key Documentation

Document Description
NEXTCLOUD-OAUTH-SETUP.md Complete guide to setting up Nextcloud OAuth and group-based authentication
CALDAV-SETUP.md Instructions for configuring CalDAV calendar synchronization
CI-CD-PIPELINE.md Detailed CI/CD pipeline documentation and troubleshooting
BOOKING-WORKFLOW-FINAL-PLAN.md Complete booking system architecture and workflow

Additional Documentation

View All Documentation Files

Authentication & Integration:

Booking & Calendar:

CI/CD & DevOps:

Performance & SEO:

Project Management:

AI Development Guide

The project includes CLAUDE.md with comprehensive instructions for AI assistants (like Claude Code) working with this codebase, including:

  • Complete architecture overview
  • Common commands reference
  • Database layer patterns
  • Authentication flows
  • CalDAV integration details
  • Development best practices

(back to top)


Roadmap

Completed Features

  • Artist portfolio system with galleries
  • Nextcloud OAuth with auto-provisioning
  • CalDAV bidirectional sync
  • Flash tattoo marketplace
  • Admin dashboard with analytics
  • Artist self-service dashboard
  • Appointment booking system
  • R2 file storage integration
  • Role-based access control
  • CI/CD pipeline with Gitea
  • Bundle size enforcement
  • HEIC image conversion
  • Artist slug-based URLs

In Progress

  • Email notifications for appointments
  • SMS reminders for clients
  • Advanced calendar conflict resolution
  • Payment integration (Stripe/Square)
  • Gift card system enhancements
  • Enhanced analytics dashboard

Future Enhancements

  • Client self-service portal
  • Online deposit payments
  • Artist earnings reports
  • Inventory management
  • Social media auto-posting
  • Mobile app (React Native)
  • Webhook integrations
  • Advanced reporting

See the open issues for a full list of proposed features and known issues.

(back to top)


Contributing

Contributions are welcome! This project follows standard Git workflows and conventional commits.

Development Workflow

  1. Fork the project

    # Via Gitea UI or git clone
    git clone https://git.biohazardvfx.com/nicholai/united-tattoo.git
    
  2. Create your feature branch

    git checkout -b feat/amazing-feature
    
  3. Make your changes

    • Follow existing code style (enforced by ESLint/Prettier)
    • Add tests for new features
    • Update documentation as needed
  4. Run quality checks

    npm run lint              # Check linting
    npm run format            # Format code
    npm run ci:typecheck      # Check types
    npm run test:run          # Run tests
    npm run ci:budgets        # Check bundle sizes
    
  5. Commit your changes

    git add .
    git commit -m "feat: add amazing feature"
    

    Use Conventional Commits format:

    • feat: New feature
    • fix: Bug fix
    • docs: Documentation changes
    • style: Formatting, missing semicolons, etc.
    • refactor: Code refactoring
    • test: Adding tests
    • chore: Maintenance tasks
  6. Push to your branch

    git push origin feat/amazing-feature
    
  7. Open a Pull Request

    • Via Gitea UI
    • Provide clear description of changes
    • Reference any related issues

Code Style Guidelines

  • TypeScript: Prefer strict typing, avoid any
  • React: Use functional components with hooks
  • File organization: Keep components modular
  • Comments: Explain "why", not "what"
  • Tests: Test user-facing behavior, not implementation

Top Contributors

Contributors

(back to top)


License

License Status

This project currently does not have a LICENSE file in the repository. If you intend to use GNU GPLv3 (as referenced in the original README template), please add a LICENSE or COPYING file with the full license text.

Alternatively, consider:

  • MIT License - Permissive, allows commercial use
  • Apache 2.0 - Permissive with patent grant
  • GNU GPLv3 - Copyleft, requires source disclosure
  • Proprietary - All rights reserved

Choose A License can help you decide.

Current Status: No license specified. Please add a LICENSE file to clarify usage terms.

(back to top)


Contact


Star this repository if you find it helpful!

(back to top)


Made with love for United Tattoo Studio, Fountain, CO

Description
United Tattoo's website - made in nextjs
Readme 812 MiB
Languages
TypeScript 95.4%
Shell 2.1%
CSS 1.3%
JavaScript 1.1%