feat(booking): add service eligibility based on age and patch tests

- Add eligibility filtering to /api/services: exclude services below
  user's
  age, gray out services requiring patch tests that are missing/expired
- Add new endpoint /api/services/eligible-for/{user_id} for admin
  booking
  flows to check eligibility for a specific user
- Add image metadata stripping: uploads now strip all EXIF/GPS data
  via imaging library (security improvement)
- Update ServiceCard frontend: show grayed-out state for ineligible
  services with "contact us" link (public) or just warning (admin)
- Add 2 patch test services to seed data: Gel Polish Full Set,
  Luxury Gel Manicure (48h each)
- Remove deprecated local-dev.sh script
This commit is contained in:
2026-02-20 18:46:38 +00:00
parent eb1a719fc3
commit 41dc839830
13 changed files with 385 additions and 1647 deletions
+13 -2
View File
@@ -21,6 +21,14 @@
- [x] Login rate limiting (1 attempt per 5 seconds)
- [x] Global rate limiting middleware (per-endpoint: 120/min public, 10/min register, 60/min filters, none admin)
- [x] Security headers (X-Content-Type-Options, X-Frame-Options, X-XSS-Protection)
- [x] **Image metadata stripping** - All EXIF/GPS stripped on upload via `imaging` library (security)
- [x] Service eligibility system - Age and patch test filtering for bookings
- `/api/services` - Returns services with eligibility for authenticated users
- `/api/services/eligible-for/{user_id}` - Returns services with eligibility for specific user (admin booking flows)
- Age < minimum_age_required → Service EXCLUDED
- Patch test required + no record → Service GRAYED OUT
- Patch test expired → Service GRAYED OUT
- Patch test valid / not required → Normal
- [ ] **Strict-Transport-Security (HSTS)** - Tell browsers to only access via HTTPS, prevents downgrade attacks. Add after HTTPS is working in prod.
- [ ] **Referrer-Policy** - Track referrer sources for analytics (social media tracking). Use `strict-origin-when-cross-origin` to send origin but not full URLs.
- [ ] **Rate limiter + Cloudflare** - Currently doesn't read CF-Connecting-IP header, so behind Cloudflare all users share one rate limit bucket.
@@ -106,10 +114,12 @@
#### Booking Flow
- [x] Service selection with pricing/duration
- [x] **Service eligibility display** - Gray out services requiring patch test or below minimum age
- [x] Calendar with availability detection
- [x] Time slot generation with gap logic
- [x] Customer details form (guest or authenticated)
- [x] Auth store with token refresh logic
- [x] **Admin booking flows** - Call-in and walk-in use `/api/services/eligible-for/{user_id}` for user-specific eligibility
- [ ] **Customer booking submit** - `submitBooking()` only logs, needs `POST /api/bookings`
- [ ] Payment integration (Square placeholder)
@@ -203,7 +213,8 @@ flowchart TD
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/services` | List active services |
| GET | `/api/services` | List active services (with eligibility for authenticated users) |
| GET | `/api/services/eligible-for/{user_id}` | List services filtered by user's age and patch test status (admin only) |
| POST | `/api/register` | Create new user account |
| POST | `/api/login` | Authenticate and receive JWT |
| GET | `/api/scheduling/default-hours` | Get weekly default hours |
@@ -502,7 +513,7 @@ Running `local-dev-2.sh` creates:
| Resource | Count | Details |
|----------|-------|---------|
| Users | 18 | 1 admin, 17 regular users |
| Services | 6 | Classic Manicure, Gel Manicure (BIAB), Luxury Pedicure, Express Mani & Pedi, Gel Removal, Nail Art Add-on |
| Services | 8 | 6 standard (no patch test) + 2 requiring patch tests (Gel Polish Full Set 48h, Luxury Gel Manicure 48h) |
| Bookings | 45 | 8 past, 3 today, 4 tomorrow, 30 future (spread over 15 days) |
| Confirmed | ~50% | Random selection of upcoming bookings auto-confirmed |
| Exceptional | 2 | November Break (closed), Christmas Holiday (reduced hours) |