Commit Graph

12 Commits

Author SHA1 Message Date
Flea Flicker 4c46cec4e3 fix(GRO-867): proxy logo download through API server — eliminate mixed content
All logo S3 interactions are now server-proxied:
- GET /api/admin/settings/logo streams image bytes directly instead of
  returning a presigned S3 URL to the browser
- Upload already went through POST /api/admin/settings/logo/upload
- Frontend uses relative /api/admin/settings/logo path as img src,
  never a raw S3 URL
- Appends cache-buster query param (?t=Date.now()) after upload so
  the browser fetches the fresh image instead of serving a stale cache

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-21 22:07:21 +00:00
groombook-engineer[bot] 77971a1ac9 fix(GRO-769): proxy logo uploads through API server to fix mixed content (#325)
* fix(GRO-766): prevent horizontal overflow on portal mobile pages

- Add overflow-x-hidden to main content area in CustomerPortal
- Add w-full overflow-hidden to content wrapper div
- Add flex-wrap to BillingPayments tab button row

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(GRO-769): proxy logo uploads through API server to fix mixed content

The pre-signed URL flow used an internal HTTP endpoint for S3 uploads,
which browsers blocked as mixed content on HTTPS pages. Instead of
generating a pre-signed URL that the browser uploads to directly,
the new /logo/upload endpoint receives the file via multipart POST
and streams it to S3 from the API server using the internal endpoint.

This resolves the mixed content error that was blocking logo uploads
on dev.groombook.dev.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

---------

Co-authored-by: Test User <test@example.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-04-17 17:13:44 +00:00
Test User b00d6a8ca0 fix(GRO-642): restrict allowed logo MIME types to bitmap formats only
Exclude image/svg+xml from the frontend allowlist since SVG poses greater
XSS risk due to its ability to contain scripts, even with proper Content-Type
validation. The server-side validation (commit 8182870) still accepts SVG
and validates magic bytes, but the frontend restrict to safer bitmap formats
as specified in the issue.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-17 06:46:24 +00:00
Test User f8ea417799 fix(GRO-642): sanitize logo MIME type to prevent XSS in data URL rendering
Add ALLOWED_LOGO_TYPES allowlist check before constructing data URL from
user-controlled logoBase64 and logoMimeType fields. Only MIME types that
the API explicitly accepts (image/png, image/jpeg, image/gif, image/webp,
image/svg+xml) can be rendered as data URLs.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-17 06:45:14 +00:00
Paperclip c786544369 Fix frontend error handling and code quality (GRO-642)
HIGH Priority:
1. SetupWizard.jsx -> SetupWizard.tsx: renamed to .tsx with proper TypeScript types
2. deleteAppt missing error handling: added try/catch, response.ok check, alert on failure
3. GlobalSearch missing error state: added error state with user-visible error message

MEDIUM Priority:
4. CustomerPortal unsafe type cast: fixed 'as any' to proper PortalAppointment type
5. Logo upload XSS risk: sanitized MIME types to png/jpeg/gif/webp only, removed SVG
6. Reports error handling: added ok checks before json() parsing to guard against invalid JSON on error responses

LOW Priority:
8. Modal accessibility: added role='dialog', aria-modal='true', focus trap, Escape key handler, restore focus on close
9. PetPhotoUpload file size: added 50MB max file size check before resize
10. Types package: added photoKey and photoUploadedAt to Pet interface

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-16 22:38:23 +00:00
groombook-engineer[bot] d9c8cf91e8 Merge remote-tracking branch 'origin/main' into feat/gro-392-oobe-auth-provider-bootstrap
# Conflicts:
#	apps/web/src/pages/Settings.tsx
2026-04-03 07:51:46 +00:00
groombook-engineer[bot] 624bb14ccb fix(GRO-391): remove clientSecret from test schema; use internalBaseUrl
Test connection was always 400 because testAuthProviderSchema required
clientSecret, but OIDC discovery only needs issuer/internal URLs.
Aligned admin test endpoint with setup.ts behavior:
- Drop providerId, clientId, clientSecret from schema
- Add optional internalBaseUrl; use it for discovery URL when set
- Frontend now sends issuerUrl + internalBaseUrl (when populated)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-03 07:43:44 +00:00
groombook-engineer[bot] bdefb34059 fix(api): needsSetup guard ordering in setup auth endpoints (GRO-392 UAT fix)
* feat(oobe): add conditional auth provider bootstrap step (GRO-392)

Backend:
- GET /api/setup/status now returns showAuthProviderStep, authConfigExists,
  and authEnvVarsSet to inform the frontend whether to show the step
- POST /api/setup/auth-provider: unauthenticated endpoint for first-time
  auth provider configuration during OOBE; guarded by needsSetup check
  (returns 403 after setup completes); encrypts clientSecret before storing

Frontend:
- SetupWizard fetches /api/setup/status on mount to determine if the
  auth provider step is needed (fresh install with no DB config and no
  OIDC env vars)
- When needed, inserts the Auth Provider step after Welcome, before
  Business Name; includes full form with Test Connection button
- Endpoint is POST /api/admin/auth-provider/test for connection testing

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(oobe): add test connection endpoint and fix EOF newline (GRO-392)

- Add POST /api/setup/auth-provider/test endpoint for OOBE test connection
- Guard with same !superUser check as bootstrap endpoint
- Update SetupWizard to call /api/setup/auth-provider/test instead of
  /api/admin/auth-provider/test (which requires auth session)
- Add trailing newline at EOF in setup.ts

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(oobe): remove unused catch variable in setup.ts (GRO-392)

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* feat(api): auth provider CRUD endpoints + test-connection (GRO-388)

Implement admin API endpoints for managing auth provider configuration:

- GET  /api/admin/auth-provider         — get current config (secret redacted)
- PUT  /api/admin/auth-provider         — create or update provider config
- POST /api/admin/auth-provider/test    — validate via OIDC discovery endpoint
- DELETE /api/admin/auth-provider       — remove DB config (falls back to env vars)

All endpoints are gated by requireSuperUser(). The clientSecret is
AES-256-GCM encrypted before DB write and always redacted on return.
Test-connection fetches /.well-known/openid-configuration and returns
metadata on success or error detail on failure.

Includes 16 unit tests covering all endpoints and error paths.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(api): requireRoleOrSuperUser for /admin/* routes (GRO-412)

Fix bug where super users granted via Staff UI were blocked from
admin routes because requireRole("manager") checked role before
isSuperUser. Changed to requireRoleOrSuperUser("manager") so
super users bypass the manager-role check.

Also adds 7 unit tests for requireRoleOrSuperUser middleware
covering: manager access, super user bypass, non-super-user
blocking, and multi-role scenarios.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(api): remove unused decryptSecret import and eslint-disable directives

Fixes lint error exposed by merge with main (GRO-392 PR #214)

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(tests): use main's authProvider tests after rebase conflict resolution

The rebase introduced incompatible test code from the pre-merge GRO-388
commit. Replaced with the canonical test file from main to ensure tests
pass and reflect the actual router implementation.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(api): remove duplicate authProviderRouter import and route registration

Rebase introduced duplicate import from ./routes/admin/authProvider.js
and duplicate route registration. Removed duplicates since the correct
import is from ./routes/authProvider.js.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(e2e): use lean schema for OIDC test endpoint; add trailing newline

Fix CTO review comments on GRO-392:

- POST /api/setup/auth-provider/test now uses authProviderTestSchema
  (only issuerUrl + internalBaseUrl) instead of full
  authProviderBootstrapSchema — clientSecret is not needed for OIDC
  discovery and was not being sent by the frontend handler
- POST /api/admin/auth-provider/test already uses omit() correctly;
  no change needed
- apps/api/src/routes/admin/authProvider.ts: added trailing newline

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* feat(web): add auth provider section to settings page (GRO-391)

Add Authentication Provider section to /admin/settings for super users.
Implements: provider ID, display name, issuer URL, internal base URL
(optional, collapsed), client ID, client secret (masked, only sent on
change), scopes fields; Test Connection button; Save and Reset to
Environment Defaults with confirmation dialog; warning banner about
service restart; env config info banner when no DB config is set.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(api): move needsSetup guard before Zod parsing in setup endpoints

POST /api/setup/auth-provider and POST /api/setup/auth-provider/test
were returning 400 (Zod validation) instead of 403 when needsSetup
was false, because zValidator middleware ran before the route handler
body. Now manually parse the body after the needsSetup guard so 403
fires immediately for post-setup requests.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(api): replace c.req.valid("json") with await c.req.json()

Replace zValidator-orphaned c.req.valid("json") calls with await c.req.json()
in the auth provider bootstrap and test endpoints per CTO review.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

---------

Co-authored-by: groombook-engineer[bot] <3141748+groombook-engineer[bot]@users.noreply.github.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Barkley Trimsworth <noreply@groombook>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-03 07:17:12 +00:00
groombook-engineer[bot] 41491da254 feat(web): add auth provider section to settings page (GRO-391)
Add Authentication Provider section to /admin/settings for super users.
Implements: provider ID, display name, issuer URL, internal base URL
(optional, collapsed), client ID, client secret (masked, only sent on
change), scopes fields; Test Connection button; Save and Reset to
Environment Defaults with confirmation dialog; warning banner about
service restart; env config info banner when no DB config is set.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-03 02:20:02 +00:00
groombook-engineer[bot] fa5ddc0792 feat(settings): migrate logo storage from base64-in-DB to S3
- Add logoKey column to businessSettings schema
- Add Drizzle migration 0022_logo_key.sql
- Add POST /api/admin/settings/logo/upload-url (presigned PUT URL)
- Add POST /api/admin/settings/logo/confirm (record key, clear base64)
- Add GET /api/admin/settings/logo (presigned GET URL)
- Add DELETE /api/admin/settings/logo (remove S3 object, clear DB)
- Update PATCH /api/admin/settings to reject logoBase64/logoMimeType
- Update GET /api/branding to return logoUrl (presigned) with legacy base64 compat
- Update BrandingContext to include logoUrl field
- Update Settings page to use presigned upload flow (no base64 in PATCH body)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-02 12:17:57 +00:00
groombook-engineer[bot] 74571d9f2b feat(demo): expand demo pet images and seed data with diverse breed showcase
Generated 16 diverse pet images for demo site using MiniMax image generation:
- Multiple dog breeds (Golden Retriever, Poodle, Labrador, Shih Tzu, Cocker Spaniel, Schnauzer, Maltese, Dachshund, Pomeranian)
- Professional grooming styles and poses
- Studio lighting for quality showcase

Updated seed.ts to create 9 demo pets with image references:
- Expands from single demo pet to diverse pet portfolio
- Images deployed to apps/web/public/demo-pets/
- Each pet has breed-accurate styling and professional grooming

This completes GRO-395 demo assets expansion using allocated MiniMax credits.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-02 12:15:21 +00:00
groombook-paperclip[bot] f2501d9972 feat: customizable business branding (name, logo, colors) (#63)
* feat: add customizable business branding (name, logo, colors)

Add admin settings for business branding with name, logo upload, and
color scheme via CSS custom properties. Includes database migration,
API endpoints, admin settings page, and dynamic branding in both
admin nav and customer portal.

Closes #61

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address review feedback on branding PR

- Replace dynamic import with static import for @groombook/db in public branding endpoint
- Restore active nav item background highlight (bg-stone-100) in CustomerPortal
- Remove non-null assertion in settings route, add proper error handling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: trigger CI

* fix: resolve lint error and test failure for branding feature

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update E2E tests for branding changes

- Update navigation test to expect "GroomBook" (default branding) instead
  of hardcoded "Paws & Reflect" since CustomerPortal now uses dynamic branding
- Add /api/branding mock to shared E2E fixtures so BrandingProvider resolves
  immediately in all tests, preventing unhandled fetch interference

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: GroomBook CTO <cto@groombook.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: GroomBook CTO <cto@groombook.app>
2026-03-19 11:07:07 +00:00