Crussell

Crussell is a fullstack application that powers a nailbar / salon booking service. The repository is split into a Go backend and a SvelteKit frontend, both of which are containerised with Docker. A lightweight SabreDAV instance is also exposed so that the salon can offer WebDAV access to clients.

📦 Project Structure

Crussell/
├─ backend/            # Go 1.22 + chi router API
├─ frontend/           # SvelteKit 5 SPA (static build)
├─ sabredav/           # PHP + Composer for DAV
├─ nginx/              # Nginx reverseproxy for HTTP & HTTPS
├─ init-scripts/       # PostgreSQL init SQL
├─ compose.yml         # DockerCompose definition
├─ local-dev.sh        # Development helper using tmux
├─ local-dev-2.sh      # Enhanced seeding script with more test data
└─ README.md

⚙️ Prerequisites

Tool Version
Docker & DockerCompose ≥ 20.10
Go ≥ 1.22
Node ≥ 18 (npm)
tmux ≥ 3.0

Tip

: If you already have Docker Desktop or Docker Engine installed, you are good to go.

📥 Getting Started

# Clone the repository
git clone http://git.popertots.com/popertots/Crussell.git
cd Crussell

# Copy the example environment file and edit it
cp .env.example .env
# Open .env and provide values for POSTGRES_*, JWT_SECRET_KEY, etc.

DockerCompose

The simplest way to bring the whole stack up is with DockerCompose.

docker compose up --build -d

postgres PostgreSQL 17 backend Go API (exposed on :8080) sabredav PHPbased WebDAV (served by Nginx) nginx Reverseproxy (HTTP on :80 and HTTPS on :443)

After the containers are running, the frontend is reachable at http://localhost. The API is available at http://localhost/api. SabreDAV can be accessed via http://localhost/dav.

Development with local-dev.sh

For a more interactive dev experience the repository ships a small helper script that launches Docker, starts a tmux session with three panes (PostgreSQL console, Go dev server, Svelte dev server) and seeds the database with an admin and a regular user plus a handful of sample services.

chmod +x local-dev.sh
./local-dev.sh

The script performs the following steps:

  1. Docker checks starts Docker if it isn't already running.
  2. PostgreSQL reset removes the old volume and starts a fresh container.
  3. tmux session creates crussell-dev with panes:
    • psql console
    • Go server (go run -tags dev ./main.go)
    • Svelte dev server (npm run dev -- --host)
  4. Seeding creates an admin (admin@example.com) and a regular user (user@example.com), updates the admin role, and registers six example services.

Note

: The script uses a temporary shell script to perform the HTTP calls, so no external tooling like jq is required.

🔧 Building & Testing

Backend

cd backend
# Build the binary
go build -o bin/backend ./main.go

The binary is then copied into the Docker image via the Dockerfile.

Frontend

cd frontend
npm ci
npm run build   # Production build (static)
npm run dev     # Development server

SabreDAV

SabreDAV is bundled with PHPFPM and Composer. The Docker image installs dependencies automatically during the container startup.

📂 Environment Variables

Variable Purpose Example
POSTGRES_USER DB username myuser
POSTGRES_PASSWORD DB password mysecret
POSTGRES_DB DB name mydb
JWT_SECRET_KEY HMAC key for JWT (HS256) supersecret
SABRE_DAV_* Optional SabreDAV overrides

Create a .env file in the project root based on the provided .env.example.

Note

: JWT tokens expire after 30 days. Login is rate-limited to 1 attempt per 5 seconds.

📊 Seeding Data

The local-dev.sh script automatically seeds:

  • Admin user (admin@example.com / password)
  • Regular user (user@example.com / password)
  • Six example nailbar services

The enhanced local-dev-2.sh script provides additional test data including:

  • Multiple users with different roles and account types
  • Working hours configurations
  • Exceptional working hours groups (holiday schedules)
  • Sample bookings with various statuses
  • Payment records
  • Admin notifications
  • User referrals

If you want to seed manually, use the provided init-scripts/init-script.sql and your favourite Postgres client.

📌 Useful Commands

# Show Docker containers
docker ps

# Rebuild the Go binary and restart containers
make build-backend
docker compose up -d backend

# Tail logs
docker compose logs -f

# Open a shell inside the backend container
docker compose exec backend sh

🏗️ Implementation Status

Complete

Feature Backend Frontend Notes
JWT Authentication Login, register, refresh, middleware; HS256, 30-day expiry, auto-refresh
Booking CRUD ⚠️ Backend complete; frontend submit is stub (logs only)
Admin Endpoints Services, users, today view, notifications
Scheduling System Default hours, exceptional hours, working/available hours
Holiday Hours Integrated in all 3 booking flows
Loyalty Backend DB schema ready, no frontend component
VAT System get_vat_return_data(), calculate_vat() functions exist
User Referrals user_referrals table, backend logic exists
Token Refresh POST /api/refresh-token, auto-refresh in auth store

⚠️ Partially Complete

Feature Status Details
Notifications Backend only Admin notifications wired; acknowledgment on confirm/cancel; no frontend panel, no push (email/SMS), no regular user notifications
User Notification Preferences DB ready Table user_notification_preferences exists; waiting on user notification system
GDPR Compliance DELETE only anonymize_user(), export_all_user_data() exist but only DELETE is wired to endpoint
Analytics Handler exists handlers/admin/analytics.go exists but NOT wired in router

Not Wired (Handlers Exist)

Handler File Notes
Social Auth handlers/auth/social.go OAuth integration placeholder
Analytics handlers/admin/analytics.go Statistics/dashboard endpoint
Portfolio Images handlers/portfolio/images.go Gallery management

🚧 Critical TODOs

Location Issue Priority
BookingFlow.svelte:600 submitBooking() only logs, needs POST implementation High
BookingCreateModal.svelte:224 Remove console.log(users) debug statement Low
/api/users/guest Guest endpoint for walk-ins not implemented Medium
GDPR Export Need endpoint for export_all_user_data() Medium
Tax Export Endpoint for VAT return data export Medium

📋 Feature Requests

Feature Description
One-off Custom Services Allow creating single-use services not in regular catalog
One-off Exceptional Hours Single-day overrides without creating a group
S3/R2 Image Hosting Portfolio image storage with admin upload

🔄 Booking Flows

Crussell supports three distinct booking flows:

Flow User Entry Point Status
Self-Service Customer BookingFlow.svelte/api/bookings ⚠️ Stub submit
Walk-In Admin Admin panel → walk-in modal
Call/Message-In Admin Admin panel → booking modal

All three flows integrate with the holiday/exceptional hours system to prevent bookings during closed periods.


🔍 Helpful Greps

All Three Booking Flows

Backend - All Booking Creation Endpoints:

grep -rn "func.*Create.*Booking\|func.*WalkIn\|func.*Walk.*In\|POST.*booking" backend/handlers/ --include="*.go"

Frontend - All Booking Flow Components:

grep -rln "BookingFlow\|WalkIn\|walk-in\|call.*in\|message.*in" frontend/src/lib/components/ --include="*.svelte"

Combined - Single View of All Booking Flows:

grep -rn "CreateBooking\|CreateWalkIn\|BookingFlow\|submitBooking" backend/ frontend/ --include="*.go" --include="*.svelte"

Database Schema

All Enums:

grep -n "CREATE TYPE" init-scripts/init-script.sql

All Tables:

grep -n "CREATE TABLE" init-scripts/init-script.sql

All Functions:

grep -n "CREATE OR REPLACE FUNCTION" init-scripts/init-script.sql

Router Endpoints

All Wired Routes:

grep -n "r\.\(Get\|Post\|Put\|Delete\|Patch\)" backend/main.go

Middleware Chain:

grep -n "r\.Use\|r\.Group" backend/main.go

Authentication

JWT Middleware:

grep -rn "JWTMiddleware\|VerifyToken\|ParseToken" backend/ --include="*.go"

Protected Routes:

grep -n "r\.Group.*Auth" backend/main.go

Holiday/Exceptional Hours

Usage Across All Flows:

grep -rn "exceptional.*hours\|holiday.*hours\|getExceptional\|getHoliday" backend/handlers/ frontend/src/ --include="*.go" --include="*.svelte"

Notifications

Admin Notifications:

grep -rn "admin_notifications\|AdminNotification" backend/ --include="*.go"

Notification Reasons (Enum Values):

grep -A10 "admin_notification_reason" init-scripts/init-script.sql

GDPR Functions

Data Export/Anonymization:

grep -rn "anonymize_user\|export_all_user_data\|delete_guest_user" backend/ --include="*.go" init-scripts/

Frontend Debug Artifacts

Console Logs to Remove:

grep -rn "console\.log" frontend/src/ --include="*.svelte" --include="*.ts"

🗄️ Database Schema Overview

Enums

Enum Values
account_role user, admin
account_type standard, vip, guest
booking_status pending, confirmed, in_progress, completed, cancelled, no_show
payment_type deposit, full, refund
payment_method cash, card, bank_transfer, square
payment_status pending, completed, failed, refunded
admin_notification_reason pending_booking, cancelled_booking, rescheduled_booking, 1_week_no_pay, 1_month_no_pay, affiliate_claim

Key Tables

Table Purpose
users Customer and admin accounts
services Salon service catalog
bookings Appointment records
booking_services Services per booking (many-to-many)
payments Payment transactions
working_hours Default weekly schedule
exceptional_working_hours_groups Holiday/special schedules
admin_notifications Admin alerts
user_notification_preferences User notification preferences (email/sms/push)
user_referrals Referral tracking

Validation Rules

Field Rules
Names 1-50 chars, unicode letters/spaces/hyphen/apostrophe/dot
Phone UK format → E.164 (+44...)
Age Must be 16+ years
Login Rate Limit 1 attempt per 5 seconds

Happy coding!

S
Description
Nail salon website with booking system and portfolio
Readme
48 MiB
Languages
Go 64.6%
JavaScript 16.5%
Svelte 14.8%
TypeScript 1.9%
PLpgSQL 1.2%
Other 0.9%