feat(scheduling): dezky Scheduling — Calendly-style booking on Stalwart calendars
First-party booking system on top of Stalwart calendars (no third-party scheduling dependency). Hosts expose public booking pages; visitors pick a slot computed from the host's live Stalwart free/busy, and confirming writes the event to the host's calendar and sends a dezky-branded confirmation with an .ics. platform-api (services/platform-api/src/scheduling): - Schemas: Host, StalwartCredential (AES-256-GCM at rest), AvailabilitySchedule, EventType, Booking, SlotLock (unique (hostId,startUtc) + TTL). - StalwartCalendarModule: JMAP gateway (free/busy via Principal/getAvailability, event create/delete, scheduleAgent=client) + on-behalf app-password provisioning. CredentialCipher for at-rest encryption. - DST-correct slot engine (Luxon) with unit tests; two-layer double-booking guard (atomic SlotLock + live free/busy re-check). - Booking confirm/cancel/reschedule, branded email + .ics via JMAP submission, self-service manage tokens. /api/v1 public + tenant-gated admin routes, per-IP rate limiting. apps/booking: standalone public, whitelabel booking app (booking.dezky.eu) — path-based tenant resolution, per-tenant brand colour, booking + manage flows. apps/portal: admin scheduling page (hosts, event types, availability, bookings with edit/delete + admin cancel/reschedule) and proxy routes. infra: booking dev service in docker-compose; scheduling env vars.
This commit is contained in:
@@ -33,6 +33,7 @@ volumes:
|
||||
platform_api_node_modules:
|
||||
operator_node_modules:
|
||||
website_node_modules:
|
||||
booking_node_modules:
|
||||
# MinIO data (S3-compatible cold storage for audit archives). Production
|
||||
# swaps the endpoint to Hetzner Object Storage and this volume goes away.
|
||||
minio_data:
|
||||
@@ -570,6 +571,38 @@ services:
|
||||
- traefik.http.routers.website.tls=true
|
||||
- traefik.http.services.website.loadbalancer.server.port=3000
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# Booking — dezky Scheduling public booking app (booking.dezky.local).
|
||||
# Fully public, no OIDC: only calls platform-api's /api/v1/public/* via its
|
||||
# own nitro proxy. Whitelabel-themed per tenant from the API response.
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
booking:
|
||||
image: node:20-alpine
|
||||
container_name: dezky-booking
|
||||
restart: unless-stopped
|
||||
working_dir: /app
|
||||
command: sh -c "corepack enable && corepack prepare pnpm@latest --activate && pnpm install && pnpm dev"
|
||||
environment:
|
||||
NODE_ENV: development
|
||||
NUXT_HOST: 0.0.0.0
|
||||
NUXT_PORT: 3000
|
||||
# Served behind Traefik TLS → Vite HMR over wss:443.
|
||||
DEZKY_TRAEFIK: "1"
|
||||
# How nitro reaches platform-api inside the docker network.
|
||||
PLATFORM_API_INTERNAL_URL: http://platform-api:3001
|
||||
volumes:
|
||||
- ../../apps/booking:/app
|
||||
- booking_node_modules:/app/node_modules
|
||||
networks: [dezky]
|
||||
depends_on:
|
||||
mongo:
|
||||
condition: service_healthy
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.booking.rule=Host(`booking.dezky.local`)
|
||||
- traefik.http.routers.booking.tls=true
|
||||
- traefik.http.services.booking.loadbalancer.server.port=3000
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# platform-api — NestJS service. Owns tenants, partners, users,
|
||||
# subscriptions, and provisioning orchestration.
|
||||
@@ -647,6 +680,12 @@ services:
|
||||
STRIPE_PUBLISHABLE_KEY: ${STRIPE_PUBLISHABLE_KEY:-}
|
||||
STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET:-}
|
||||
BILLING_STRIPE_ENABLED: ${BILLING_STRIPE_ENABLED:-false}
|
||||
# dezky Scheduling. AES-256-GCM key (64 hex chars) encrypting per-host
|
||||
# Stalwart app passwords at rest — never logged. Public booking app origin
|
||||
# for manage links; Jitsi base for generated meeting rooms.
|
||||
SCHEDULING_CREDENTIAL_KEY: ${SCHEDULING_CREDENTIAL_KEY}
|
||||
BOOKING_PUBLIC_URL: ${BOOKING_PUBLIC_URL:-https://booking.dezky.local}
|
||||
MEET_PUBLIC_URL: ${MEET_PUBLIC_URL:-https://meet.dezky.local}
|
||||
volumes:
|
||||
- ../../services/platform-api:/app
|
||||
- platform_api_node_modules:/app/node_modules
|
||||
|
||||
Reference in New Issue
Block a user