- Add new '💰 Deposit System' section with full business logic
- Document 24-hour late cancellation rule
- Explain optional forgive_no_show boolean for admin forgiveness
- Explain optional enforce_deposits boolean for admin booking override
- Document no-show accumulation (2+ in 6 months = 3 deposits)
- Document deposit reduction on payment
- Add API examples and implementation files reference
- Update payment_type enum to include all types (deposit, full, tip, balance, partial)
- Add deposit examples showing different scenarios
- Update Simplified Deposits status line with current behavior
- Remove outdated 48h notice reference
683 lines
24 KiB
Markdown
683 lines
24 KiB
Markdown
# Crussell
|
||
#KM|> **Last Updated:** March 2026
|
||
Crussell is a **full‑stack application** that powers a nail‑bar / salon booking service. The repository is split into a **Go** backend and a **SvelteKit** front‑end, 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.25 + chi router API
|
||
├─ frontend/ # SvelteKit 5 SPA (static build)
|
||
├─ sabredav/ # PHP + Composer for DAV
|
||
├─ nginx/ # Nginx reverse‑proxy for HTTP & HTTPS
|
||
├─ init-scripts/ # PostgreSQL init SQL
|
||
├─ compose.yml # Docker‑Compose definition
|
||
├─ local-dev-2.sh # Development helper using tmux (seeded with 8 services incl. 2 with patch tests)
|
||
└─ README.md
|
||
```
|
||
|
||
## ⚙️ Prerequisites
|
||
|
||
| Tool | Version |
|
||
|------|---------|
|
||
| Docker & Docker‑Compose | ≥ 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
|
||
|
||
```bash
|
||
# 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.
|
||
```
|
||
|
||
### Docker‑Compose
|
||
|
||
The simplest way to bring the whole stack up is with Docker‑Compose.
|
||
|
||
```bash
|
||
docker compose up --build -d
|
||
```
|
||
|
||
> `postgres` – PostgreSQL 17
|
||
> `backend` – Go API (exposed on `:8080`)
|
||
> `sabredav` – PHP‑based WebDAV (served by Nginx)
|
||
> `nginx` – Reverse‑proxy (HTTP on `:80` and HTTPS on `:443`)
|
||
|
||
After the containers are running, the front‑end 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-2.sh`
|
||
|
||
For a more interactive dev experience the repository ships a small helper script that launches Docker, starts a tmux session with four panes (PostgreSQL console, Go dev server, Svelte dev server, Rustfs logs) and seeds the database with an admin, regular users, and sample services including patch test services.
|
||
|
||
```bash
|
||
chmod +x local-dev-2.sh
|
||
./local-dev-2.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`)
|
||
* Rustfs logs
|
||
4. **Seeding** – creates admin (`admin@example.com`), regular users (`user@example.com`), 8 services (6 standard + 2 requiring patch tests), bookings, exceptional hours.
|
||
|
||
> **Note**: The script uses a temporary shell script to perform the HTTP calls, so no external tooling like `jq` is required.
|
||
|
||
## 🔧 Building & Testing
|
||
|
||
### Backend
|
||
|
||
```bash
|
||
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
|
||
|
||
```bash
|
||
cd frontend
|
||
npm ci
|
||
npm run build # Production build (static)
|
||
npm run dev # Development server
|
||
```
|
||
|
||
### SabreDAV
|
||
|
||
SabreDAV is bundled with PHP‑FPM and Composer. The Docker image installs dependencies automatically during the container start‑up.
|
||
|
||
## 🧪 Testing
|
||
|
||
### Test Infrastructure
|
||
|
||
Crussell has a comprehensive Go testing infrastructure located in `backend/testutils/`:
|
||
|
||
| Component | File | Description |
|
||
|-----------|------|-------------|
|
||
| **Database** | `testutils/testdb/testdb.go` | PostgreSQL test pool, migrations, table truncation |
|
||
| **HTTP Helpers** | `testutils/helpers.go` | Request builders, auth helpers, assertions |
|
||
| **JWT** | `testutils/jwt/jwt.go` | Test token generation for users/admins |
|
||
| **HTTP Client** | `testutils/httptest/client.go` | REST client wrapper with auth support |
|
||
| **Fixtures** | `testutils/fixtures/fixtures.go` | Factory functions for test data |
|
||
| **Validators** | `internal/validators/validators.go` | ID validation utilities |
|
||
|
||
### Test Files
|
||
|
||
The project includes 16 test files covering all major handlers:
|
||
|
||
```
|
||
backend/
|
||
├── bookings.test # Main booking integration tests
|
||
├── portfolio.test # Portfolio system tests
|
||
├── handlers/
|
||
│ ├── admin/
|
||
│ │ ├── bookings_test.go # Admin booking management
|
||
│ │ ├── today_test.go # Today's view
|
||
│ │ ├── users_test.go # User management
|
||
│ │ └── services_test.go # Service CRUD
|
||
│ ├── auth/
|
||
│ │ └── auth_test.go # Authentication
|
||
│ ├── bookings/
|
||
│ │ └── bookings_test.go # User booking flow
|
||
│ ├── portfolio/
|
||
│ │ └── images_test.go # Image upload/management
|
||
│ ├── scheduling/
|
||
│ │ └── scheduling_test.go # Availability logic
|
||
│ ├── services/
|
||
│ │ └── services_test.go # Service eligibility
|
||
│ ├── user/
|
||
│ │ └── profile_test.go # User profile
|
||
│ └── handlers_test.go # Common handler tests
|
||
```
|
||
|
||
### Running Tests
|
||
|
||
```bash
|
||
cd backend
|
||
|
||
# Run all tests
|
||
go test ./...
|
||
|
||
# Run with verbose output
|
||
go test -v ./...
|
||
|
||
# Run specific test file
|
||
go test -v ./handlers/bookings
|
||
|
||
# Run tests matching pattern
|
||
go test -v -run "TestBooking" ./...
|
||
```
|
||
|
||
### Test Database Setup
|
||
|
||
Tests use a dedicated PostgreSQL database. Set the connection string via:
|
||
|
||
```bash
|
||
export TEST_DB_DSN="postgres://user:pass@localhost:5432/crussell_test?sslmode=disable"
|
||
go test ./...
|
||
```
|
||
|
||
Default DSN: `postgres://myuser:mypassword@localhost:5432/crussell_test?sslmode=disable`
|
||
|
||
### Test Utilities Usage
|
||
|
||
```go
|
||
import (
|
||
"crussell/testutils"
|
||
"crussell/testutils/testdb"
|
||
"crussell/testutils/fixtures"
|
||
"crussell/testutils/jwt"
|
||
)
|
||
|
||
func TestMyHandler(t *testing.T) {
|
||
// Setup test database
|
||
cleanup := testutils.SetupTestDB(t)
|
||
defer cleanup()
|
||
|
||
// Create test data
|
||
adminID, _ := fixtures.CreateTestAdminUser(db.DB)
|
||
userID, _ := fixtures.CreateTestUser(db.DB)
|
||
serviceID, _ := fixtures.CreateTestService(db.DB)
|
||
|
||
// Generate tokens
|
||
adminToken := jwt.GenerateAdminToken()
|
||
userToken := jwt.GenerateUserToken(userID)
|
||
|
||
// Make authenticated requests
|
||
w := testutils.MakeAdminRequest(router, "GET", "/api/admin/bookings", nil, adminID)
|
||
w := testutils.MakeUserRequest(router, "GET", "/api/bookings", nil, userID)
|
||
|
||
// Assert results
|
||
testutils.AssertStatusCode(t, w, http.StatusOK)
|
||
testutils.AssertJSONResponse(t, w, &response)
|
||
}
|
||
```
|
||
|
||
### Test Conventions
|
||
|
||
- All test files use `//go:build test` build tag
|
||
- Database is migrated fresh per test run via `testdb.Migrate()`
|
||
- Tables are truncated between tests via `testdb.TruncateTables()`
|
||
- Test tokens use a fixed secret: `test-secret-key-for-testing-only`
|
||
- Fixtures auto-generate unique emails to avoid conflicts
|
||
|
||
### Recent Testing & Bug Fixes
|
||
|
||
Since the February 2026 update, extensive testing has been conducted which uncovered and fixed several issues:
|
||
|
||
**Testing Improvements:**
|
||
- Added comprehensive test suite for `notifications` handler (600+ lines)
|
||
- Added new booking flow tests covering user booking creation
|
||
- Added extensive test docstrings across all test files
|
||
- Fixed test database setup and fixture issues
|
||
|
||
**Issues Discovered & Fixed Through Testing:**
|
||
- **Edit Request Bugs**: Multiple issues in booking edit request handling:
|
||
- Fixed validation for overlapping bookings during edits
|
||
- Fixed edit request approval flow
|
||
- Fixed edit request rejection handling
|
||
- Fixed status transition logic for edit requests
|
||
- **SQL Function Updates**: Refactored and optimized database functions in `init-script.sql`
|
||
- **Test Infrastructure**: Fixed manual test corruption issues, improved test isolation
|
||
|
||
**Test Coverage:**
|
||
- 16+ test files covering all major handlers
|
||
- Tests use `//go:build test` build tag
|
||
- Comprehensive fixtures for users, services, bookings
|
||
- Auth helper with JWT token generation for both user and admin roles
|
||
|
||
## 📂 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-2.sh` script automatically seeds:
|
||
|
||
* Admin user (`admin@example.com` / `password`)
|
||
* Regular user (`user@example.com` / `password`)
|
||
* 8 services:
|
||
* 6 standard (no patch test)
|
||
* 2 with patch test requirement (24h) - Gel Polish Full Set, Luxury Gel Manicure
|
||
* 6 standard (no patch test)
|
||
* 2 with patch test requirement (48h) - Gel Polish Full Set, Luxury Gel Manicure
|
||
|
||
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
|
||
|
||
```bash
|
||
# 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 |
|
||
| Portfolio System | ✅ | ✅ | S3/R2 storage abstraction, tag-based filtering, category filters, admin upload, ?img= featured image param |
|
||
| Service Eligibility | ✅ | ✅ | Age + patch test filtering (dedicated `patch_tests` table); `/api/services/eligible-for/{user_id}` for admin booking flows |
|
||
| Patch Test System | ✅ | ✅ | Dedicated `patch_tests` table, notice periods (24h), expiry (6 months), admin can record in UserModal |
|
||
| Image Metadata Stripping | ✅ | ❌ | EXIF/GPS stripped on upload via `imaging` library |
|
||
| Profile Pictures | ✅ | ✅ | Upload to separate bucket, cropper, circular display, CalDAV sync |
|
||
| Auto-Booking Status | ✅ | ✅ | Auto-transition: confirmed → in_progress → completed based on time |
|
||
|| Simplified Deposits | ✅ | ⚠️ | **24h late cancellation rule**: < 24h = no-show (+3 deposits, optional forgiveness), ≥ 24h = normal cancellation. Admin can bypass checks. Reduces by 1 per payment on completed booking.
|
||
| Contact Page | ✅ | ✅ | Dynamic data from first admin user via `/api/contact` endpoint |
|
||
| Email Verification | ✅ | ❌ | Verification codes, generate/check endpoints |
|
||
| Calendar Export | ✅ | ⚠️ | ICS download endpoint, Add to Calendar button (backend complete) |
|
||
| Loyalty Stamps | ✅ | ✅ | Backend complete, displayed in account page |
|
||
|
||
### ⚠️ 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 |
|
||
|
||
### 🚧 Critical TODOs
|
||
|
||
| Location | Issue | Priority |
|
||
|----------|-------|----------|
|
||
| `BookingFlow.svelte:600` | `submitBooking()` only logs, needs POST implementation | High |
|
||
| `/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 |
|
||
|
||
---
|
||
|
||
## 🔄 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.
|
||
|
||
---
|
||
|
||
## 💰 Deposit System
|
||
|
||
### Overview
|
||
|
||
The deposit system is a **simplified user-level tracking mechanism** that enforces a 3-deposit requirement before new bookings are allowed. It's designed to reduce booking abandonment and protect against chronic no-shows.
|
||
|
||
**Key Concepts**:
|
||
- `deposits_required`: Integer (0-3) on users table tracking outstanding deposit obligations
|
||
- `deposit_required`: Boolean on bookings table, snapshotted at creation time
|
||
- `deposit_amount`: Calculated as 20% of booking total for display
|
||
- `deposit_paid`: True when pre-start payments cover the deposit amount
|
||
|
||
### Cancellation Rules (Late = < 24 Hours)
|
||
|
||
| Scenario | Time Until Start | Forgiveness | Result Status | Penalty | User Blocked? |
|
||
|----------|------------------|-------------|---------------|---------|---------------|
|
||
| **Late cancellation (normal)** | < 24h | No/Omitted | `no_show` | +3 deposits (resets to 3, not +=) | Yes* |
|
||
| **Late cancellation (forgiven)** | < 24h | Yes | `client_cancelled` | None | No |
|
||
| **Normal cancellation** | ≥ 24h | Any | `client_cancelled` | None | No |
|
||
| **Pending cancellation** | Any | Any | Deleted | None | No |
|
||
|
||
*Assuming `deposits_required` > 0 after penalty
|
||
|
||
### API: User Cancellation
|
||
|
||
**Endpoint**: `PUT /api/bookings/{id}/cancel`
|
||
|
||
**Request**:
|
||
```json
|
||
{
|
||
"reason": "no_show",
|
||
"forgive_no_show": true // Optional: true = forgive penalty, false/omitted = enforce penalty
|
||
}
|
||
```
|
||
|
||
**Behavior**:
|
||
- `< 24h` without forgiveness → `status = no_show`, `deposits_required = 3`
|
||
- `< 24h` with forgiveness → `status = client_cancelled`, no penalty
|
||
- `≥ 24h` → `status = client_cancelled`, no penalty (always)
|
||
|
||
### Admin Booking Creation with Deposit Control
|
||
|
||
**Endpoint**: `POST /api/admin/bookings`
|
||
|
||
**Request**:
|
||
```json
|
||
{
|
||
"user_id": "USR123",
|
||
"start_time": "2026-03-10T10:00:00Z",
|
||
"service_ids": ["SVC1"],
|
||
"enforce_deposits": false // Optional: true (default) = enforce checks, false = bypass checks
|
||
}
|
||
```
|
||
|
||
**Behavior**:
|
||
- `enforce_deposits = true` (default): Apply one-active-booking limit if `deposits_required > 0`
|
||
- `enforce_deposits = false`: Bypass all deposit checks, allow multiple active bookings
|
||
|
||
**Use Cases**:
|
||
- `true`: Standard workflow, ensure users clear deposits before booking again
|
||
- `false`: Emergency/special cases where admin needs to override deposit restrictions
|
||
|
||
### No-Show Accumulation
|
||
|
||
**Rule**: 2+ unforgiven no-shows in rolling 6 months → `deposits_required = 3`
|
||
|
||
**Trigger**: Automatic via `ApplyDepositsIfNeeded()` when:
|
||
1. User has status = `no_show` (without forgiveness flag)
|
||
2. Booking occurred within last 6 months
|
||
3. Not yet marked as "forgiven" in `forgiven_no_shows` table
|
||
4. Count ≥ 2
|
||
|
||
**Consequence**: User blocked from new bookings (one-active-booking limit enforced)
|
||
|
||
### Deposit Reduction
|
||
|
||
**Rule**: When booking transitions to `completed` with ≥ 1 payment → `deposits_required -= 1`
|
||
|
||
**Logic**:
|
||
- Minimum: 0 (never negative)
|
||
- Reduction happens once per booking (not per payment)
|
||
- User must complete bookings with payments to clear all 3 deposits
|
||
|
||
### Deposit Fields in Booking API Response
|
||
|
||
```json
|
||
{
|
||
"id": "BK123",
|
||
"deposit_required": true, // Snapshot: was deposit required at booking time?
|
||
"deposit_amount": 24.50, // 20% of total_amount
|
||
"deposit_paid": false, // Sum of pre-start payments >= deposit_amount?
|
||
"deposit_deadline": "2026-03-09T10:00:00Z", // start_time - 24 hours
|
||
"total_amount": 122.50,
|
||
"amount_paid": 0.00,
|
||
"amount_due": 122.50
|
||
}
|
||
```
|
||
|
||
### Implementation Files
|
||
|
||
| File | Changes | Details |
|
||
|------|---------|----------|
|
||
| `backend/handlers/bookings/bookings.go` | Cancellation logic | 24h threshold, optional `forgive_no_show` boolean |
|
||
| `backend/handlers/bookings/manage.go` | Admin booking creation | Optional `enforce_deposits` boolean, deposit checks |
|
||
| `backend/handlers/bookings/manage.go` | Removed function | `ForgiveNoShowsForUser()` (now per-cancellation) |
|
||
|
||
### Database Schema
|
||
|
||
| Table | Column | Type | Purpose |
|
||
|-------|--------|------|----------|
|
||
| `users` | `deposits_required` | INT | Outstanding deposit count (0-3) |
|
||
| `bookings` | `deposit_required` | BOOLEAN | Snapshot of requirement at booking time |
|
||
| `payments` | `payment_type` | ENUM | Includes `deposit`, `full`, `tip`, `balance`, `partial` |
|
||
| `forgiven_no_shows` | `booking_id` | CHAR(12) | Tracks forgiven no-shows for 6-month accumulation |
|
||
|
||
### Deposit Examples
|
||
|
||
**Example 1: New User Books**
|
||
```
|
||
User: deposits_required = 0
|
||
→ Booking: deposit_required = false
|
||
→ Response: deposit_amount shown, but deposit_paid always false
|
||
```
|
||
|
||
**Example 2: User with Deposits Late Cancels**
|
||
```
|
||
User: deposits_required = 1
|
||
Cancels < 24h without forgiveness
|
||
→ Result: deposits_required = 3 (reset, not incremented)
|
||
→ User blocked from new bookings
|
||
```
|
||
|
||
**Example 3: Admin Overrides Deposits**
|
||
```
|
||
Admin creates booking with enforce_deposits = false
|
||
User has deposits_required = 2 + active booking
|
||
→ Result: Booking created successfully
|
||
→ Deposit check bypassed
|
||
```
|
||
---
|
||
|
||
## 🔍 Helpful Greps
|
||
|
||
### All Three Booking Flows
|
||
|
||
**Backend - All Booking Creation Endpoints:**
|
||
```bash
|
||
grep -rn "func.*Create.*Booking\|func.*WalkIn\|func.*Walk.*In\|POST.*booking" backend/handlers/ --include="*.go"
|
||
```
|
||
|
||
**Frontend - All Booking Flow Components:**
|
||
```bash
|
||
grep -rln "BookingFlow\|WalkIn\|walk-in\|call.*in\|message.*in" frontend/src/lib/components/ --include="*.svelte"
|
||
```
|
||
|
||
**Combined - Single View of All Booking Flows:**
|
||
```bash
|
||
grep -rn "CreateBooking\|CreateWalkIn\|BookingFlow\|submitBooking" backend/ frontend/ --include="*.go" --include="*.svelte"
|
||
```
|
||
|
||
### Database Schema
|
||
|
||
**All Enums:**
|
||
```bash
|
||
grep -n "CREATE TYPE" init-scripts/init-script.sql
|
||
```
|
||
|
||
**All Tables:**
|
||
```bash
|
||
grep -n "CREATE TABLE" init-scripts/init-script.sql
|
||
```
|
||
|
||
**All Functions:**
|
||
```bash
|
||
grep -n "CREATE OR REPLACE FUNCTION" init-scripts/init-script.sql
|
||
```
|
||
|
||
### Router Endpoints
|
||
|
||
**All Wired Routes:**
|
||
```bash
|
||
grep -n "r\.\(Get\|Post\|Put\|Delete\|Patch\)" backend/main.go
|
||
```
|
||
|
||
**Middleware Chain:**
|
||
- RequestID, RealIP, Logger, Recoverer, Timeout(15s)
|
||
- Security headers (X-Content-Type-Options, X-Frame-Options, X-XSS-Protection)
|
||
- Rate limiting (per-endpoint):
|
||
- Public read-only: 120/min
|
||
- Registration: 10/min
|
||
- Portfolio filters: 60/min
|
||
- Portfolio single image: 120/min
|
||
- Portfolio admin: 60/min
|
||
- Authenticated users: 120/min
|
||
- Admin search: 60/min
|
||
- Admin-only routes: none (trusted)
|
||
- ⚠️ **Gap: Rate limiter doesn't read CF-Connecting-IP** - behind Cloudflare all users share one bucket
|
||
- ⚠️ **Gap: No HSTS header** - add when HTTPS working
|
||
- ⚠️ **Gap: No Referrer-Policy** - for analytics tracking
|
||
- ✅ **Image metadata stripping** - EXIF/GPS stripped on upload (security improvement)
|
||
|
||
**Input Validation:**
|
||
- Backend validates all inputs against DB schema constraints
|
||
- Frontend adds maxlength attributes matching DB limits
|
||
- Registration: name (1-50), email (255), phone (20), password (72)
|
||
- Services: name (100), price (>0), duration (1-480), patch test (0-168), age (0-100)
|
||
- Portfolio: tags/filters (256 char max)
|
||
```bash
|
||
grep -n "r\.Use\|r\.Group" backend/main.go
|
||
```
|
||
|
||
### Authentication
|
||
|
||
**JWT Middleware:**
|
||
```bash
|
||
grep -rn "JWTMiddleware\|VerifyToken\|ParseToken" backend/ --include="*.go"
|
||
```
|
||
|
||
**Protected Routes:**
|
||
```bash
|
||
grep -n "r\.Group.*Auth" backend/main.go
|
||
```
|
||
|
||
### Holiday/Exceptional Hours
|
||
|
||
**Usage Across All Flows:**
|
||
```bash
|
||
grep -rn "exceptional.*hours\|holiday.*hours\|getExceptional\|getHoliday" backend/handlers/ frontend/src/ --include="*.go" --include="*.svelte"
|
||
```
|
||
|
||
### Notifications
|
||
|
||
**Admin Notifications:**
|
||
```bash
|
||
grep -rn "admin_notifications\|AdminNotification" backend/ --include="*.go"
|
||
```
|
||
|
||
**Notification Reasons (Enum Values):**
|
||
```bash
|
||
grep -A10 "admin_notification_reason" init-scripts/init-script.sql
|
||
```
|
||
|
||
### GDPR Functions
|
||
|
||
**Data Export/Anonymization:**
|
||
```bash
|
||
grep -rn "anonymize_user\|export_all_user_data\|delete_guest_user" backend/ --include="*.go" init-scripts/
|
||
```
|
||
|
||
### Frontend Debug Artifacts
|
||
|
||
**Console Logs to Remove:**
|
||
```bash
|
||
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`, `client_cancelled`, `we_cancelled`, `re-schedule`, `no_show`, `no_deposit` |
|
||
|| `payment_type` | `deposit`, `full`, `tip`, `balance`, `partial` |
|
||
|| `payment_method` | `online_square`, `in_person_card`, `cash`, `giftcard`, `discount` |
|
||
| `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`, `late_cancellation`, `no_deposit`, `deposit_paid` |
|
||
|
||
### Key Tables
|
||
|
||
| Table | Purpose |
|
||
|-------|--------|
|
||
KR|| `users` | Customer and admin accounts |
|
||
WW|| `verification_codes` | Email verification and password reset codes |
|
||
TW|| `services` | Salon service catalog |
|
||
QB|| `patch_tests` | Patch test definitions with notice periods and expiry |
|
||
SP|| `user_patch_tests` | User patch test completion records |
|
||
SP|| `bookings` | Appointment records |
|
||
SP|| `booking_services` | Services per booking (many-to-many) |
|
||
SP|| `booking_edit_requests` | Pending customer edit requests |
|
||
| `verification_codes` | Email verification and password reset codes |
|
||
| `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!**
|