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>
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>
Fix type errors that caused CI Lint & Typecheck job to fail:
- setup.ts: replace unavailable isNull import with sql template tag
(isNull not exported from @groombook/db; sql IS exported)
- setup.ts: add non-null assertion on newStaff after insert.returning()
- setup.test.ts: add sql mock template tag to @groombook/db mock
- setup.test.ts: fix evaluateCond to handle sql template tag type
- setup.test.ts: add type assertions for body.staff in OOBE regression tests
- setup.test.ts: fix dbStaffRows type casts in mock insert function
All 18 tests pass, full typecheck clean.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Exempt POST /api/setup from resolveStaffMiddleware so OOBE users (with no
pre-existing staff record) can complete the out-of-box experience without
getting blocked by the "no staff record found" 403 error.
Changes:
- rbac.ts: add /api/setup to path exemption alongside /api/auth/
- setup.ts POST /: add find-or-create logic that:
- Looks up existing staff by userId from JWT
- Auto-links legacy staff records by email if userId is null
- Creates a new staff record if none exists (OOBE case)
- Returns 400 if JWT has no email and no staff record found
- setup.test.ts: add regression tests for all scenarios
Fixes GRO-485 (OOBE regression introduced by GRO-480).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
These steps always fail because the runner has no kubeconfig. Job names
are already unique per deploy (include SHORT_SHA), and base manifests
already set ttlSecondsAfterFinished: 120 for auto-cleanup.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Both promote-to-uat and promote-prod workflows now delete any
existing completed Jobs with the same short SHA suffix before Flux
reconciles. This prevents the immutable-podTemplate error that was
blocking UAT at image tag a67e541:
Job.batch "migrate-schema-xxx" is invalid: spec.template: field is immutable
Also added missing failure notification step to promote-prod workflow.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
drizzle-orm is not a direct dependency of @groombook/api, causing
TS2307 at typecheck time. Re-export isNull from @groombook/db and
update the import in rbac.ts.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
When a staff record exists with a matching email but no userId (e.g. seed data
or admin UI-created records), resolveStaffMiddleware now auto-links it to the
Better-Auth user record on first SSO login instead of returning 403.
Safety: only links when userId IS NULL, never overwrites an existing link.
Email matching is safe since it comes from the trusted SSO provider (Authentik).
Staff emails are unique by schema.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Both workflows now update base migration/seed Job names with short SHA
extracted from the image tag, matching the dev CI cd job pattern.
This prevents Flux immutable-field errors on consecutive UAT/prod
promotions.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The NetworkFirst route for /api/* was intercepting the OIDC callback
(/api/auth/oauth2/callback/authentik?code=...), returning a cached
index.html instead of forwarding to the API server.
Added navigateFallbackDenylist regex to exclude the callback path
from service worker navigation handling, allowing the callback request
to reach the API server normally.
Fixes GRO-472.
Co-authored-by: Flea Flicker <flea-flicker@groombook.farh.net>
Co-authored-by: Paperclip <noreply@paperclip.ing>
The authProviderRouter was registered twice at /admin/auth-provider in
apps/api/src/index.ts. The second registration is a no-op but creates
confusion. Remove the duplicate line.
Co-authored-by: Paperclip <noreply@paperclip.ing>