Commit Graph

317 Commits

Author SHA1 Message Date
Flea Flicker dd646fb273 feat: add Google/GitHub social login for Demo environment (GRO-531)
- 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>
2026-04-10 02:06:44 +00:00
Flea Flicker 63256b8bc0 feat: implement SEED_ADMIN_EMAIL support in seedKnownUsers and full seed
- 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>
2026-04-09 22:33:15 +00:00
Flea Flicker bb9f677a9b fix: confirm reset-demo-data CronJob only in dev/UAT overlays
Verification (kubectl kustomize):
- dev:  CronJob present
- uat:  CronJob present
- prod: CronJob absent

No structural changes needed — state already correct in main.
2026-04-08 19:47:03 +00:00
groombook-cto[bot] d12c3b4a60 fix: set isSuperUser=false for Jordan Lee in full seed path (#242)
fix: set isSuperUser=false for Jordan Lee in full seed path
2026-04-08 03:05:14 +00:00
Paperclip a84d5e7b9a fix: set isSuperUser=false for Jordan Lee in full seed
Jordan Lee was being created with isSuperUser=true in the full seed path,
causing GET /api/setup/status to return needsSetup=false after UAT reset.
2026-04-08 02:56:31 +00:00
groombook-cto[bot] 4261058565 Merge pull request #241 from groombook/fleaflicker/gro-505-paginated-invoices
GRO-505: Use paginated invoices API, eliminate over-fetching
2026-04-07 21:49:17 +00:00
Flea Flicker 94764d8532 Frontend: use paginated invoices API, eliminate over-fetching
- 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>
2026-04-07 20:11:24 +00:00
Flea Flicker 9be6a87105 chore: implement hourly reset CronJob for prod and UAT
- 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>
2026-04-07 20:01:56 +00:00
groombook-cto[bot] 19880e36de feat(invoices): add indexes, pagination, and client name enrichment (GRO-504)
feat(invoices): add indexes, pagination, and client name enrichment (GRO-504)
2026-04-07 19:43:07 +00:00
Flea Flicker 0fe10434e1 feat(invoices): add indexes, pagination, and client name enrichment
- 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>
2026-04-07 19:23:03 +00:00
groombook-cto[bot] 83f0c8d3db Merge pull request #236 from groombook/fix/gro-493-oobe-loop
fix(web): resolve OOBE loop after setup completion (GRO-494)
2026-04-05 23:19:27 +00:00
Flea Flicker e435fe344e fix(web): clear needsSetup state after OOBE completion to prevent loop
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>
2026-04-05 23:09:47 +00:00
groombook-qa[bot] f56e2de165 fix(web): redirect authenticated admin users to /admin (#235)
fix(web): redirect authenticated admin users to /admin
2026-04-05 21:35:06 +00:00
Flea Flicker e576c06c92 Merge remote-tracking branch 'origin/main' into fix/gro-488-admin-login-redirect 2026-04-05 21:26:34 +00:00
Flea Flicker 5effe07cd6 fix(web): redirect authenticated admin users to /admin
Preserve customer portal impersonation flow via ?sessionId= query param.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-05 21:16:53 +00:00
groombook-qa[bot] 8e6bfaaaa0 Merge pull request #234 from groombook/fix/gro-485-oobe-staff-middleware
fix(api): exempt OOBE setup from staff middleware and auto-create staff (GRO-485)
2026-04-05 20:22:40 +00:00
groombook-qa[bot] aa36d4ba87 Merge branch 'main' into fix/gro-485-oobe-staff-middleware 2026-04-05 20:16:12 +00:00
Flea Flicker 8348f1c152 fix(api): resolve CI typecheck failures in GRO-485 fix
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>
2026-04-05 20:05:53 +00:00
Flea Flicker fa18c41677 fix(api): exempt OOBE setup from staff middleware and auto-create staff (GRO-485)
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>
2026-04-05 19:37:23 +00:00
groombook-cto[bot] 8ab47a738d Merge pull request #233 from groombook/fix/gro-478-auto-link-staff-user
fix(ci): remove dead kubectl steps from promote workflows (GRO-482)
2026-04-05 17:33:33 +00:00
Flea Flicker 25ac34828f fix(ci): remove dead kubectl steps and misleading TTL fallback lines
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>
2026-04-05 16:32:23 +00:00
groombook-cto[bot] 736c94664e Merge pull request #232 from groombook/fix/gro-478-auto-link-staff-user
fix(ci): delete completed Jobs before Flux reconciles (GRO-481)
2026-04-05 15:57:29 +00:00
Flea Flicker 006c05ac77 fix(ci): delete completed Jobs before Flux reconciles (GRO-481)
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>
2026-04-05 15:47:04 +00:00
groombook-qa[bot] a67e541657 Merge pull request #231 from groombook/fix/gro-478-auto-link-staff-user
fix(api): auto-link staff to Better-Auth user via email on first SSO login (GRO-480)
2026-04-05 15:05:50 +00:00
groombook-qa[bot] 9a3d8d1516 Merge branch 'main' into fix/gro-478-auto-link-staff-user 2026-04-05 14:59:21 +00:00
Flea Flicker e39924b236 fix(api): import isNull from @groombook/db instead of drizzle-orm directly
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>
2026-04-05 14:39:22 +00:00
Flea Flicker 711981e6f3 fix(api): auto-link staff to Better-Auth user via email on first SSO login (GRO-480)
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>
2026-04-05 14:30:25 +00:00
groombook-cto[bot] 6ed4b5bc93 fix(ci): rename base Jobs in promote-to-uat and promote-prod workflows (GRO-311) (#230)
fix(ci): rename base Jobs in promote-to-uat and promote-prod workflows (GRO-311)
2026-04-05 11:31:08 +00:00
groombook-cto[bot] bb8030e49a Merge branch 'main' into fix/gro-311-promote-job-names 2026-04-05 11:24:27 +00:00
Flea Flicker 90ad46f0d5 fix(ci): rename base Jobs in promote-to-uat and promote-prod workflows (GRO-311)
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>
2026-04-05 11:14:17 +00:00
groombook-engineer[bot] b090f8b810 fix(GRO-472): exclude OAuth callback from service worker caching (#228)
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>
2026-04-05 08:55:07 +00:00
groombook-engineer[bot] ff216ea54c fix(api): remove duplicate authProviderRouter registration (#226)
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>
2026-04-04 23:29:18 +00:00
groombook-qa[bot] 0bb70b291d Merge pull request #225 from groombook/fix/gro-454-test-schema
fix(db): generate unique random salt per encryptSecret call (GRO-453)
2026-04-04 22:22:51 +00:00
Paperclip 78a6758349 fix(db): generate unique random salt per encryptSecret call (GRO-453)
Use a 16-byte random salt per encryption instead of the fixed
"groombook-auth-provider-config" salt. This prevents identical
plaintexts from producing identical ciphertexts, closing the
timing/anagram security gap identified in GRO-452.

New format: salt:iv:ciphertext:authTag (all base64).
Legacy format (iv:ciphertext:authTag) is still accepted for
backward-compatible decryption of existing stored values.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 21:37:30 +00:00
groombook-cto[bot] a2cb3bfc21 Merge pull request #223 from groombook/fix/gro-453-random-salt-crypto
fix(db): use random per-encryption salt in crypto.ts (GRO-453)
2026-04-04 14:06:14 +00:00
groombook-cto[bot] bad4a4845c Merge branch 'main' into fix/gro-453-random-salt-crypto 2026-04-04 13:59:57 +00:00
groombook-cto[bot] e891580b20 Merge pull request #224 from groombook/fix/gro-454-test-schema
fix(api): use correct schema in POST /admin/auth-provider/test (GRO-454)
2026-04-04 13:34:55 +00:00
Paperclip 6819bff2bf fix(api): use correct schema in POST /admin/auth-provider/test (GRO-454)
Switch the test endpoint from putAuthProviderSchema.omit({ clientSecret })
(which requires providerId, displayName, clientId, scopes) to the
minimal authProviderTestSchema (issuerUrl, internalBaseUrl?) that matches
what the Settings.tsx frontend actually sends.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 13:34:14 +00:00
groombook-cto[bot] d47c730e7c Merge branch 'main' into fix/gro-454-test-schema 2026-04-04 13:26:03 +00:00
Paperclip d9e6b09fe5 fix(api): use correct schema in POST /admin/auth-provider/test (GRO-454)
Switch the test endpoint from putAuthProviderSchema.omit({ clientSecret })
(which requires providerId, displayName, clientId, scopes) to the
minimal authProviderTestSchema (issuerUrl, internalBaseUrl?) that matches
what the Settings.tsx frontend actually sends.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 13:16:30 +00:00
Paperclip 1c7628459f fix(db): use random per-encryption salt in crypto.ts (GRO-453)
Generate a unique 16-byte random salt for each encryptSecret() call
and store it as a prefix in the ciphertext. Format changed from
  iv:ciphertext:authTag → salt:iv:ciphertext:authTag

decryptSecret() detects legacy 3-part format and uses the fixed
package salt for backward compatibility with existing encrypted rows.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 13:14:18 +00:00
groombook-cto[bot] c9fbbbfa5c fix(GRO-451): re-seal UAT secrets with correct cluster certificate (#222)
fix(GRO-451): re-seal UAT secrets with correct cluster certificate
2026-04-04 12:34:28 +00:00
Paperclip 0eda43e930 fix(GRO-451): re-seal UAT secrets with correct cluster certificate
UAT is down (503) because sealed secrets were encrypted with the wrong
key. This commit:

- Adds groombook/overlays/uat/ with fresh postgres and auth sealed
  secrets sealed with the correct UAT cluster certificate
- Adds kustomization.yaml that:
  - Uses correct image tags (2026.04.03-90be1be)
  - Injects all auth env vars from groombook-auth-uat
  - Points to groombook-postgres-credentials-uat
  - Uses UAT hostname (groombook.uat.farh.net)
  - Deletes the base component's postgres-credentials SealedSecret
    (namespace-scoped, not namespace-wide, causes noise in UAT)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 12:27:23 +00:00
groombook-cto[bot] f6fc994e6c fix(api): wrap encryptSecret in try/catch to return proper JSON error (GRO-441)
fix(api): wrap encryptSecret in try/catch to return proper JSON error (GRO-441)
2026-04-04 00:24:40 +00:00
Flea Flicker 2453e3a0ae fix(db): remove duplicate encryptSecret/decryptSecret exports
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 00:03:48 +00:00
Flea Flicker f37cf16b1f fix(api): export reinitAuth from lib/auth.ts
reinitAuth was imported by authProvider.ts but never defined.
Added a stub implementation that resolves immediately — proper
restart mechanism is tracked in GRO-390.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 00:02:35 +00:00
Flea Flicker 1c502bb165 fix(api): wrap encryptSecret in try/catch to return proper JSON error
PUT /api/admin/auth-provider was returning HTTP 500 with an HTML error page
when BETTER_AUTH_SECRET was missing, because encryptSecret() throws an
unhandled error. This change wraps both the encryption step and the DB
transaction in try/catch blocks to return a proper JSON error response.

Also adds the missing authProviderConfig schema and encryptSecret crypto
helpers from the feat/gro-392-oobe-auth-provider-bootstrap branch.

Fixes: GRO-441

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 00:02:11 +00:00
groombook-cto[bot] 90be1be8fb Merge pull request #220 from groombook/chore/gro-429-add-promote-to-uat-workflow
chore(GRO-429): add promote-to-uat workflow for CTO-triggered UAT promotion
2026-04-03 20:50:33 +00:00
Pawla Abdul 784a79b284 chore(GRO-429): add promote-to-uat workflow for CTO-triggered UAT promotion
Adds a manual workflow_dispatch workflow to promote a specific image tag
to the UAT environment. This separates UAT promotion from the automated
dev pipeline, enforcing the 3-stage SDLC review gate.

- Triggers via workflow_dispatch with image_tag input
- Updates UAT overlay image tags in groombook/infra
- Creates and auto-merges infra PR for UAT only
- Requires GRO-427 (UAT overlay) to be complete first

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-03 20:44:31 +00:00
groombook-cto[bot] 85650e43d2 fix(GRO-424): reinitAuth on config change, SSRF timeout, trailing-slash URL fix
fix(GRO-392): reinitAuth on config change, SSRF timeout, and trailing-slash URL fix
2026-04-03 13:28:38 +00:00