- apps/web: upgrade better-auth from ^1.0.0 to ^1.5.6 (matches API)
- apps/web/vite.config.ts: exclude /api/auth/* from service worker caching
- apps/api/index.ts: return 503 when auth not configured
- apps/api/middleware/auth.ts: return 503 when auth not initialized
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The OAuth callback was failing with "please_restart_the_process" because
Better-Auth's default DB-backed state (verification table) was unreliable —
the UAT hourly reset wipes all tables including verification records. Switch
to cookie-based state storage so the encrypted state survives in the browser
cookie across the redirect flow.
Also removes explicit redirectURI from socialProviders (Better-Auth derives
it from baseURL) and adds visible error feedback on the login page when
OAuth callbacks fail.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The networkidle wait causes flakiness in CI due to slow external resource loading.
Use domcontentloaded which fires earlier and is sufficient for SPA navigation checks.
Co-authored-by: Pawla Abdul (Bot) <pawla@groombook.dev>
Co-authored-by: Paperclip <noreply@paperclip.ing>
fix(e2e): add paginated mock for /api/invoices in navigation.spec.ts
Fixes GRO-557. The generic E2E API mock returned [] for /api/invoices, but the InvoicesPage component expects { data: [], total: 0 }. This crashed React and prevented the page from rendering, causing the admin invoices test to fail consistently.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
CTO approved. Infra submodule update adds social auth env vars (GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET) to UAT api-patch.yaml.
Update submodule pointer to include commit that adds GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET env vars
to UAT api-patch.yaml referencing groombook-social-auth secret.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Generated diverse set of professional pet photos covering:
- Large breeds: German Shepherds (3), Golden Retrievers (2), Labradors (1)
- Medium breeds: Beagle, Cocker Spaniel, Boxer, Bulldog, Corgi, Dachshund, English Springer Spaniel, Husky
- Small breeds: Maltese, Shih Tzu, Pomeranian, Poodle, Pug, Yorkshire Terrier
- Mixed breeds: 4 variations
Total demo pet images: 55 (11MB)
Puggle-specific: 4 images for the 250+ seeded Puggles
This maximizes the MiniMax image generation quota to provide a rich,
diverse visual library for the grooming demo site.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Explicitly set redirectURI in social provider configs to ensure
Better-Auth uses the correct callback URL for OAuth providers.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Removed reference to tricolor-outdoor image that experienced API timeout.
Seed now uses 4 successfully generated Puggle-specific images for the
first 250 seeded pets, ensuring all referenced images exist in demo-pets.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Updates infra submodule to include OIDC_ISSUER, OIDC_CLIENT_ID,
OIDC_CLIENT_SECRET, and OIDC_INTERNAL_BASE env vars in prod api-patch.yaml.
This fixes the auth initialization failure where initAuth() found no
provider config because OIDC env vars were missing from the prod deployment
even though the groombook-auth sealed secret contained the credentials.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add Puggle breed to dogBreeds array and modify seed logic to guarantee first 250 pets are assigned Puggle breed. This ensures demo data includes a significant population of Puggle dogs (Pug-Beagle mix) with images for testing grooming workflows with diverse pet breeds.
- Add Puggle to available dog breeds list
- Track pet creation index across all clients
- Assign Puggle breed to first 250 pets regardless of randomization
- Assign Puggle-specific images to first 250 pets
- Remaining pets use general demo image pool
This satisfies requirement: "at least 250 of the 2500 pets are first or second generation puggles with photos"
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Generated 13 new diverse dog images using MiniMax (Afghan Hound, Basset Hound, Bichon Frise variants, Boxer, Cavalier, Cocker Spaniel variants, Corgi, Dachshund variants, Pomeranian variants, Schnauzer variants, Setter, Sheepdog)
- Updated seed script to include all 28 dog images in demoPetImages array
- Ensures wider variety of dog breeds and grooming styles in demo seed data
- All images are photorealistic and suitable for pet grooming demo site
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add demoPetImages array with 15 available dog images
- Assign random pet images during seed for regular, UAT, and demo profiles
- Ensures demo site displays pet images instead of missing images
The seed script previously only added images for seedKnownUsers mode.
Now all seeded pets get assigned a random demo pet image.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Updates infra submodule to include OIDC_ISSUER, OIDC_CLIENT_ID,
OIDC_CLIENT_SECRET, and OIDC_INTERNAL_BASE env vars in prod api-patch.yaml.
This fixes the auth initialization failure where initAuth() found no
provider config because OIDC env vars were missing from the prod deployment
even though the groombook-auth sealed secret contained the credentials.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
In seedKnownUsers(), add staff records for UAT Super User
(manager, superuser) and UAT Staff Groomer (groomer) with oidcSub
read from SEED_UAT_SUPER_OIDC_SUB and SEED_UAT_STAFF_OIDC_SUB
env vars. Only creates records when the env vars are present.
Idempotent: skips if email already exists.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The promote-to-uat workflow was bypassing the Kustomize images transformer
by hardcoding image tags directly on the Job spec containers. Since Jobs
use immutable templates, Flux cannot update a running Job's pod template
when the image tag changes. Instead, let the UAT overlay's images: newTag
field handle tag injection via the images transformer, which correctly
produces the updated image reference in the rendered manifest before Flux
reconciles it.
This reverts the explicit image tag writes added in 916a207 for migrate
and seed, while keeping the Job name (with short SHA) and deploy-version
annotation updates which are correctly handled separately.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The CTO correctly identified that the delete step was dead code:
- gcloud/kubectl silently fail in the runner (no GKE credentials)
- Architecturally wrong for GitOps (Flux handles reconciliation)
- Unique Job names + ttlSecondsAfterFinished handle lifecycle
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The workflow was not updating the seed job image tag when promoting to UAT,
causing Flux to apply a stale image. Now it updates the image like it
does for the migrate job.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The hardcoded image update for seedJob conflicts with Kustomize images transformer
override. Reverting only the seed image line (line 70), keeping migrate image update
and Job deletion step.
Root cause: Kustomize images transformer correctly overrides ghcr.io/groombook/seed
when newTag is set in UAT overlay. Overwriting the container[0].image directly in
the workflow causes the old tag (2026.04.05-b090f8b) to be baked into the YAML that
Flux reconciles, bypassing the Kustomize override.
Fix: groombook/groombook#247
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Previously the Kustomize images transformer was not overriding the hardcoded
image tags in migrate-job.yaml and seed-job.yaml (base/ containers), causing
UAT deployments to use stale image tags. This change adds explicit yq updates
to set the correct image tag on both Job containers during promotion.
Fixes: groombook/groombook#247
- Add UAT Super User and Staff User staff records creation in seedKnownUsers()
- Staff records created with oidcSub from SEED_UAT_*_OIDC_SUB env vars
- Supports linking Terraform-provisioned Authentik users to staff records
GRO-528
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- auth.ts: add google/github social providers from better-auth/social-providers
- auth.ts: add getActiveProviders() to enumerate configured OAuth/social providers
- index.ts: add /api/auth/providers public endpoint for frontend
- App.tsx: update LoginPage to show Google/GitHub buttons based on /api/auth/providers response
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add admin upsert to seedKnownUsers() for prod path
- Add admin upsert to seed() for UAT path
- Idempotent: skips if SEED_ADMIN_EMAIL not set
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Replace loadAll() with single GET /api/invoices?limit=50&offset=0
- Remove parallel fetches of clients/appointments/services/staff from list load
- Use clientName from API response instead of client-side enrichment
- Add offset-based pagination controls with Previous/Next buttons
- Lazy-load staff/appointments only when opening invoice detail modal
- Lazy-load clients/appointments/services only when opening create form
- Filter changes only re-fetch invoices, not all endpoints
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add ALLOW_RESET env var override to reset.ts safety guard
- Add reset Docker build target to Dockerfile
- Add reset image build step to CI docker job
- Add reset image tag update to CD job dev overlay update
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add database migration 0024 with indexes on invoices, invoice_line_items, and invoice_tip_splits
- Update Drizzle schema with index definitions for sync
- Add pagination (limit/offset) to GET /api/invoices with max 200 limit
- Add LEFT JOIN to include clientName in invoice list response
- Return { data: [...], total: N } response shape for pagination
Co-Authored-By: Paperclip <noreply@paperclip.ing>
When SetupWizard completes POST /api/setup and navigates to /admin,
App.tsx still has needsSetup=true in React state, causing an immediate
redirect back to /setup. Pass onSetupComplete callback to SetupWizard
which clears the state before navigating, breaking the loop.
Co-Authored-By: Paperclip <noreply@paperclip.ing>