Stack

LayerTechnology
Backend APICloudflare Workers (TypeScript) — single file worker/index.ts
DatabaseCloudflare D1 (SQLite-compatible)
FrontendReact 18 + Vite + TypeScript
HostingCloudflare Pages

Authentication

Password hashing: PBKDF2-SHA256, 100,000 iterations, salt = SONAN_SP_2026

import hashlib, binascii
dk = hashlib.pbkdf2_hmac('sha256', password.encode(), b'SONAN_SP_2026', 100000)
hash_hex = binascii.hexlify(dk).decode()

Session tokens: HMAC-SHA256 JWT, 24-hour TTL, stored in localStorage key cc_admin_token.

Database Schema (Key Tables)

admins          (id, email, password_hash, created_at)
clients         (id, name, email, phone, address, notes, created_at, updated_at)
service_requests(id, client_id, full_name, phone, email, address,
                 preferred_contact, service_types JSON, travel_details JSON,
                 home_care_details JSON, notes, internal_notes JSON,
                 status, created_at, updated_at)
appointments    (id, client_id, request_id, title, start_time, end_time,
                 notes, status, created_at)
invoices        (id, client_id, request_id, line_items JSON,
                 subtotal_cents, tax_cents, total_cents, status,
                 due_date, created_at, updated_at)
settings        (key, value, updated_at)

API Endpoints

All endpoints require Authorization: Bearer <jwt> except POST /api/auth/login.

MethodPathDescription
POST/api/auth/loginLogin, returns JWT
GET/api/requestsList all service requests
POST/api/requestsCreate request (public form)
GET/api/requests/:idGet single request
PATCH/api/requests/:idUpdate request fields
DELETE/api/requests/:idDelete request
GET/POST/api/clientsList / create clients
GET/PATCH/DELETE/api/clients/:idGet / update / delete client
GET/POST/api/appointmentsList / create appointments
PATCH/DELETE/api/appointments/:idUpdate / delete appointment
GET/POST/api/invoicesList / create invoices
PATCH/api/invoices/:idUpdate invoice
GET/PATCH/api/settingsGet / update settings
PATCH/api/settings/passwordChange admin password

CSS Variable System

Critical: --accent: #f0e8d8 is beige — decorative only. Never use for interactive elements (invisible on white background).
VariableValueUse
--primary#2d6a4fAll interactive elements, buttons, links
--primary-light#52b788Hover states
--accent#f0e8d8Decorative backgrounds ONLY
--white#ffffffCards, surfaces
--bg#f8f6f3Page background
--text#1a1a1aBody text

FUSE-Safe Git Pattern

The workspace is FUSE-mounted. Standard git add causes corruption. Always:

  1. Write content to /tmp (never directly to the FUSE path)
  2. Hash from /tmp via Python stdin: git hash-object -w --stdin
  3. Build tree with Python ls_tree/mktree helpers
  4. Create commit via git commit-tree
  5. Update .git/refs/heads/main via Python file write

Push from user's Windows terminal (sandbox gets 403):

cd E:\Claude_Projects\sonan-trackers\caretaker-portal-Claude\caretaker-portal
git push origin <sha>:refs/heads/main

Deployment Commands

# Deploy Worker API
npx wrangler deploy --env castlecheckers

# Apply D1 migrations
npx wrangler d1 migrations apply caretaker_db --remote --env castlecheckers

Frontend (React/Vite) auto-deploys on push to main via Cloudflare Pages.