Files
blackheart-website/CLAUDE.md
diana.dolgolyova 6cbdba2197 feat: add CSRF protection for admin API routes
Double-submit cookie pattern: login sets bh-csrf-token cookie,
proxy.ts validates X-CSRF-Token header on POST/PUT/DELETE to /api/admin/*.
New adminFetch() helper in src/lib/csrf.ts auto-includes the header.
All admin pages migrated from fetch() to adminFetch().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 17:53:02 +03:00

7.6 KiB
Raw Blame History

BLACK HEART DANCE HOUSE — Project Context

About

Landing page for "BLACK HEART DANCE HOUSE" — a pole dance school in Minsk, Belarus. Instagram: @blackheartdancehouse Content language: Russian

Tech Stack

  • Next.js 16 (App Router, TypeScript, Turbopack)
  • Tailwind CSS v4 (dark mode only, gold/black theme)
  • lucide-react for icons
  • better-sqlite3 for SQLite database
  • Fonts: Inter (body) + Oswald (headings) via next/font
  • Hosting: Vercel (planned)

Code Style

  • Function declarations for components (not arrow functions)
  • PascalCase for component files, camelCase for utils
  • @/ path alias for imports
  • next/image with unoptimized for PNGs that need transparency preserved
  • Header nav uses lg: breakpoint (1024px) for desktop/mobile switch (9 nav links + CTA need the space)

Project Structure

src/
├── app/
│   ├── layout.tsx          # Root layout, fonts, metadata
│   ├── page.tsx            # Landing: Hero → About → Team → Classes → MasterClasses → Schedule → Pricing → News → FAQ → Contact
│   ├── globals.css         # Tailwind imports
│   ├── styles/
│   │   ├── theme.css       # Theme variables, semantic classes
│   │   └── animations.css  # Keyframes, scroll reveal, modal animations
│   ├── admin/
│   │   ├── page.tsx        # Dashboard with 11 section cards
│   │   ├── login/          # Password auth
│   │   ├── layout.tsx      # Sidebar nav shell
│   │   ├── _components/    # SectionEditor, FormField, ArrayEditor
│   │   ├── meta/           # SEO editor
│   │   ├── hero/           # Hero editor
│   │   ├── about/          # About editor
│   │   ├── team/           # Team list + [id] editor
│   │   ├── classes/        # Classes editor with icon picker
│   │   ├── master-classes/ # MC editor with registrations
│   │   ├── schedule/       # Schedule editor
│   │   ├── pricing/        # Pricing editor
│   │   ├── faq/            # FAQ editor
│   │   ├── news/           # News editor
│   │   └── contact/        # Contact editor
│   └── api/
│       ├── auth/login/     # POST login
│       ├── logout/         # POST logout
│       ├── admin/
│       │   ├── sections/[key]/ # GET/PUT section data
│       │   ├── team/           # CRUD team members
│       │   ├── team/[id]/      # GET/PUT/DELETE single member
│       │   ├── team/reorder/   # PUT reorder
│       │   ├── upload/         # POST file upload (whitelisted folders)
│       │   ├── mc-registrations/ # CRUD registrations
│       │   └── validate-instagram/ # GET check username
│       └── master-class-register/ # POST public signup
├── components/
│   ├── layout/
│   │   ├── Header.tsx      # Sticky nav, mobile menu, booking modal ("use client")
│   │   └── Footer.tsx
│   ├── sections/
│   │   ├── Hero.tsx        # Hero with animated logo, floating hearts
│   │   ├── About.tsx       # About with stats (trainers, classes, locations)
│   │   ├── Team.tsx        # Carousel + profile view
│   │   ├── Classes.tsx     # Showcase layout with icon selector
│   │   ├── MasterClasses.tsx # Cards with signup modal
│   │   ├── Schedule.tsx    # Day/group views with filters
│   │   ├── Pricing.tsx     # Tabs: prices, rental, rules
│   │   ├── News.tsx        # Featured + compact articles
│   │   ├── FAQ.tsx         # Accordion with show more
│   │   └── Contact.tsx     # Info + Yandex Maps iframe
│   └── ui/
│       ├── Button.tsx
│       ├── SectionHeading.tsx
│       ├── BookingModal.tsx     # Booking form → Instagram DM
│       ├── MasterClassSignupModal.tsx # MC registration form → API
│       ├── NewsModal.tsx        # News detail popup
│       ├── Reveal.tsx           # Intersection Observer scroll reveal
│       ├── BackToTop.tsx
│       └── ...
├── data/
│   └── content.ts          # Fallback Russian text (DB takes priority)
├── lib/
│   ├── constants.ts        # BRAND constants, NAV_LINKS
│   ├── config.ts           # UI_CONFIG (thresholds, counts)
│   ├── db.ts               # SQLite DB, migrations, CRUD
│   ├── auth.ts             # Token signing (Node.js)
│   ├── auth-edge.ts        # Token verification (Edge/Web Crypto)
│   └── content.ts          # getContent() — DB with fallback
├── proxy.ts                # Middleware: auth guard for /admin/*
└── types/
    ├── index.ts
    ├── content.ts           # SiteContent, TeamMember, ClassItem, MasterClassItem, etc.
    └── navigation.ts

Brand / Styling

  • Accent: gold (#c9a96e / hsl(37, 42%, 61%))
  • Background: #050505 #0a0a0a (dark only)
  • Surface: #171717 dark cards
  • Logo: transparent PNG heart with gold glow, uses unoptimized

Content Data

  • Primary source: SQLite database (db/blackheart.db)
  • Fallback: src/data/content.ts (auto-seeds DB on first access)
  • Admin panel edits go to DB, site reads from DB via getContent()
  • 12 team members with photos, Instagram links, bios, victories, education
  • 6 class types (Exotic Pole Dance, Pole Dance, Body Plastic, etc.)
  • Master classes with date/time slots and public registration
  • 2 addresses in Minsk, Yandex Maps embed with markers
  • Contact: phone, Instagram (no email)

Admin Panel

  • Password-based auth with HMAC-SHA256 signed JWT (24h TTL)
  • Cookie: bh-admin-token (httpOnly, secure in prod)
  • Auto-save with 800ms debounce on all section editors
  • Team members: drag-reorder, photo upload, rich bio (experience, victories, education)
  • Master classes: slots, registration viewer, trainer/style autocomplete from existing data
  • File upload: whitelisted folders (team, master-classes, news, classes), max 5MB, image types only

Security Notes

  • CSRF protection: Double-submit cookie pattern. Login sets bh-csrf-token cookie (JS-readable). All admin fetch calls use adminFetch() from src/lib/csrf.ts which sends the token as X-CSRF-Token header. Middleware (proxy.ts) validates header matches cookie on POST/PUT/DELETE to /api/admin/*. Always use adminFetch() instead of fetch() for admin API calls.
  • File upload validates: MIME type, file extension, whitelisted folder (no path traversal)
  • API routes validate: input types, string lengths, numeric IDs
  • Public MC registration: length-limited but no rate limiting yet (add before production)

AST Index

  • Always use the AST index at memory/ast-index.md when searching for components, props, hooks, types, or styles
  • Contains: component tree, all exports, props, hooks, client/server status, CSS classes, keyframes
  • Update the index when adding/removing/renaming files or exports

Database Migrations

  • Never drop/recreate the database — admin data (photos, edits, registrations) lives there
  • Schema changes go through versioned migrations in src/lib/db.ts (migrations array)
  • Add a new entry with the next version number; never modify existing migrations
  • Migrations run automatically on server start via runMigrations() and are tracked in the _migrations table
  • Use CREATE TABLE IF NOT EXISTS and column-existence checks (PRAGMA table_info) for safety

Git

  • Remote: Gitea at git.dolgolyov-family.by
  • User: diana.dolgolyova
  • Branch: main