552 Commits
Author SHA1 Message Date
bermudalambandClaude Opus 5 9222e97deb feat(api): categories, tags, and storefront item filters (#23)
Adds a self-referencing categories tree, a tag registry with
deterministic colours, and item_tags, plus admin CRUD for both.

GET /api/items now accepts category, tags, min_price and max_price.
Category matching walks the subtree with a recursive CTE so selecting a
parent includes everything filed beneath it; tags match with AND via a
count check, since ANY() alone would return items carrying only one of
them. Malformed filter params return 400 rather than being ignored, so a
broken link doesn't quietly list the whole catalogue.

GET /api/filters serves the drawer its tree, tags, and price bounds in
one request.

Item image/tag aggregation moves from LEFT JOIN + GROUP BY to scalar
subqueries. Joining two one-to-many relations multiplies their rows, so
an item with 2 images and 3 tags would have repeated every image three
times once tags were added.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 09:13:51 -05:00
bermudalambandClaude Opus 5 766358a9fe docs: design spec for categories and tags (#23)
Records the resolved requirements for issue #23: manual category tree
(no rule engine), single category per item with descendant matching,
central tag registry with hashed-then-overridable colours, AND semantics
for multi-tag filtering, and a drawer-plus-chips storefront filter UI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 09:04:33 -05:00
bermudalamb cd07f9a1c5 Merge pull request 'test: disambiguate the Email locator in auth.spec' (#22) from fix/auth-spec-email-locator into main
SonarQube Analysis / sonarqube (push) Successful in 5m36s
Tests / backend-unit (push) Successful in 1m31s
Tests / backend-integration (push) Failing after 3h10m32s
Tests / frontend-e2e (push) Failing after 16m17s
Reviewed-on: #22
2026-08-15 18:37:38 -05:00
bermudalambandClaude Opus 5 a240d86762 test: disambiguate the Email locator in auth.spec
SonarQube Analysis / sonarqube (pull_request) Successful in 5m18s
Tests / backend-unit (pull_request) Successful in 51s
Tests / backend-integration (pull_request) Successful in 3m25s
Tests / frontend-e2e (pull_request) Successful in 12m21s
getByLabel('Email') matched two elements on /register -- the email input and
the marketing consent checkbox, whose label "Send me occasional emails"
contains the substring. Playwright's strict mode failed both tests that
filled it.

getByRole('textbox', ...) narrows by role, so the checkbox no longer collides.
Applied to the /login usage too, so the same concept reads the same way
throughout the file.

These failures were latent: the frontend-e2e job died at the build step
before it ever reached the tests, so CI never reported them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 18:35:43 -05:00
bermudalamb 02c2c74013 Merge pull request 'fix: add the missing /verify-email page' (#20) from fix/verify-email-page into main
SonarQube Analysis / sonarqube (push) Successful in 4m48s
Tests / backend-unit (push) Successful in 52s
Tests / backend-integration (push) Successful in 1m28s
Tests / frontend-e2e (push) Failing after 7m22s
Reviewed-on: #20
2026-08-15 18:23:11 -05:00
bermudalambandClaude Opus 5 b7c8c37447 fix: add the missing /verify-email page
SonarQube Analysis / sonarqube (pull_request) Successful in 3m42s
Tests / backend-unit (pull_request) Successful in 59s
Tests / backend-integration (pull_request) Successful in 1m30s
Tests / frontend-e2e (pull_request) Failing after 5m21s
Verification emails linked to /verify-email?token=..., but no such route
existed in main.tsx and nothing in the frontend ever called
POST /api/customers/verify-email. The SPA catch-all served index.html, no
route matched, and the page rendered blank -- so the token was never
redeemed and accounts stayed unverified forever.

The gap was invisible until SMTP was configured, because no verification
email had ever actually been delivered.

Adds VerifyEmail.tsx (verifying / verified / failed states), a verifyEmail
call in customerApi, and the route. The request is pinned to a single
firing via a ref: the endpoint deletes the token on success, so StrictMode's
double effect invocation in dev would otherwise overwrite the success state
with "invalid or expired token".

Verified end to end against a local stack: a real token flips
customers.email_verified to true and is consumed from customer_tokens.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 18:16:32 -05:00
bermudalamb e7fb7271f0 Merge pull request 'fix: unset NODE_ENV in e2e job so devDependencies install' (#19) from fix/e2e-devdeps-and-summarize into main
SonarQube Analysis / sonarqube (push) Failing after 5s
Tests / backend-unit (push) Successful in 1m11s
Tests / backend-integration (push) Successful in 1m32s
Tests / frontend-e2e (push) Failing after 4m42s
Reviewed-on: #19
2026-08-15 08:56:25 -05:00
bermudalambandClaude Opus 5 ec63b9cdb0 fix: unset NODE_ENV in e2e job so devDependencies install
npm treats NODE_ENV=production as --omit=dev, so `npm install` in the
frontend-e2e job skipped typescript and the build died on `tsc: not found`.
The same env var would have stripped vite and @playwright/test from the
frontend install, and flipped the session cookie to Secure on a run served
over plain http.

The reported summarize crash was a symptom: the job aborted before Playwright
ran, but Summarize is `if: always()` and threw ENOENT on the missing JSON,
burying the real failure. Both summarize scripts now report the missing file
and exit 0 -- the job still fails via its own step.

Also split build from start, replaced `sleep 3` with a readiness poll against
/api/config, and dump the backend log when e2e fails.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 08:56:25 -05:00
bermudalamb 96edce5159 Merge pull request 'refactor: delete superseded checkout routes and dedupe cart checkout' (#17) from refactor/remove-dead-checkout-routes into main
SonarQube Analysis / sonarqube (push) Successful in 5m12s
Tests / backend-unit (push) Successful in 57s
Tests / backend-integration (push) Successful in 1m47s
Tests / frontend-e2e (push) Failing after 1m31s
Reviewed-on: #17
2026-08-14 17:59:42 -05:00
bermudalambandClaude Opus 5 977489cbc0 refactor: delete superseded checkout routes and dedupe cart checkout
routes/paypal.ts and routes/demo.ts were the pre-cart single-item
checkout flow. Nothing has imported them since the cart flow landed:
app.ts mounts only cartCheckout, the frontend calls /api/checkout/cart/*,
and no test touches them. They duplicated PAYPAL_BASE, getAccessToken,
and a second handler for the /webhooks/paypal mount.

Also extract openCheckout() from /paypal/create and /demo/purchase in
cartCheckout.ts, which repeated the same address-ownership check, cart
lock, and checkouts/checkout_items inserts. It returns a discriminated
union so callers keep control of the transaction and the response. Add
CartItem/LockedCart interfaces, dropping the (it: any) casts.

Note: paypal.ts was the only writer of items.reserved_until and
items.paypal_order_id. Those columns are now write-dead; the schema is
left alone for a separate migration.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 17:59:42 -05:00
bermudalamb 0935bc14f1 Merge pull request 'chore: sync package-lock with declared dependencies' (#18) from chore/sync-package-lock into main
SonarQube Analysis / sonarqube (push) Successful in 6m27s
Tests / backend-unit (push) Successful in 2m10s
Tests / backend-integration (push) Successful in 2m3s
Tests / frontend-e2e (push) Failing after 46s
Reviewed-on: #18
2026-08-14 17:58:14 -05:00
bermudalambandClaude Opus 5 b0a38d0ac6 chore: sync package-lock with declared dependencies
node-cron, node-pg-migrate, and @types/node-cron are declared in
package.json but were missing from the lockfile, so npm ci fails and a
plain npm install silently rewrites the lock. Regenerated to match.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 17:58:14 -05:00
bermudalamb ecaef5dc6d Merge pull request 'fix: resolve SonarQube hotspots for upload size and X-Powered-By' (#16) from fix/sonarqube-hotspots into main
SonarQube Analysis / sonarqube (push) Successful in 5m51s
Tests / backend-unit (push) Successful in 1m5s
Tests / backend-integration (push) Successful in 3m7s
Tests / frontend-e2e (push) Failing after 1m4s
Reviewed-on: #16
2026-08-14 17:24:06 -05:00
bermudalambandClaude Opus 5 ab1024a1b6 fix: resolve SonarQube hotspots for upload size and X-Powered-By
SonarQube Analysis / sonarqube (pull_request) Successful in 7m11s
Tests / backend-unit (pull_request) Successful in 2m18s
Tests / backend-integration (pull_request) Successful in 2m28s
Tests / frontend-e2e (pull_request) Failing after 59s
- S5693: MAX_IMAGE_BYTES was 8 * 1024 * 1024 (8,388,608), just over the
  8,000,000-byte ceiling the rule treats as safe, so the hotspot on the
  multer storage config never cleared. Use 8_000_000.
- S5689: Express advertises its stack in X-Powered-By by default, which
  tells an attacker what to aim exploits at. Disable the header.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 17:18:42 -05:00
bermudalamb 9e4adb0586 Merge pull request 'docs: update project context with deployment lessons and cart architecture' (#15) from docs/update-project-context into main
SonarQube Analysis / sonarqube (push) Successful in 6m3s
Tests / backend-unit (push) Successful in 1m15s
Tests / backend-integration (push) Successful in 3m4s
Tests / frontend-e2e (push) Failing after 51s
Reviewed-on: #15
2026-08-14 17:02:16 -05:00
bermudalamb 05441f78ad docs: update project context with deployment lessons and cart architecture
SonarQube Analysis / sonarqube (pull_request) Successful in 6m2s
Tests / backend-unit (pull_request) Successful in 1m5s
Tests / backend-integration (pull_request) Successful in 3m29s
Tests / frontend-e2e (pull_request) Failing after 44s
2026-08-14 16:58:59 -05:00
bermudalamb 9d5e2daf6e Merge pull request 'fix: use node-pg-migrate instead of deleted init.sql in e2e workflow' (#14) from fix/e2e-workflow-migrations into main
SonarQube Analysis / sonarqube (push) Successful in 5m17s
Tests / backend-unit (push) Successful in 1m12s
Tests / backend-integration (push) Successful in 2m37s
Tests / frontend-e2e (push) Failing after 53s
Reviewed-on: #14
2026-08-14 16:21:40 -05:00
bermudalamb 483b1391a5 fix: use node-pg-migrate instead of deleted init.sql in e2e workflow
SonarQube Analysis / sonarqube (pull_request) Successful in 6m39s
Tests / backend-unit (pull_request) Successful in 1m24s
Tests / backend-integration (pull_request) Successful in 2m2s
Tests / frontend-e2e (pull_request) Failing after 56s
2026-08-14 16:18:20 -05:00
bermudalamb 1355b3076a Merge pull request 'fix: remove stale single-item demo-checkout tests, add cart integration tests' (#13) from fix/stale-demo-checkout-tests into main
SonarQube Analysis / sonarqube (push) Successful in 22m36s
Tests / backend-unit (push) Successful in 1m45s
Tests / backend-integration (push) Successful in 2m6s
Tests / frontend-e2e (push) Failing after 1m1s
Reviewed-on: #13
2026-08-14 16:12:29 -05:00
bermudalamb 1c46632e8f fix: remove stale single-item demo-checkout tests, add cart integration tests
SonarQube Analysis / sonarqube (pull_request) Successful in 13m27s
Tests / backend-integration (pull_request) Successful in 6m32s
Tests / frontend-e2e (pull_request) Failing after 2m57s
Tests / backend-unit (pull_request) Successful in 1m19s
2026-08-14 16:11:20 -05:00
bermudalamb 9de3e6a2fa Merge pull request 'Fix/cart back navigation' (#12) from fix/cart-back-navigation into main
SonarQube Analysis / sonarqube (push) Successful in 2m38s
Tests / backend-unit (push) Successful in 30s
Tests / backend-integration (push) Failing after 45s
Tests / frontend-e2e (push) Failing after 26s
Reviewed-on: #12
2026-08-14 15:37:21 -05:00
bermudalamb 1421694a0c fix: add back-to-shop navigation on cart page
SonarQube Analysis / sonarqube (pull_request) Successful in 3m55s
Tests / backend-unit (pull_request) Successful in 39s
Tests / backend-integration (pull_request) Failing after 1m6s
Tests / frontend-e2e (pull_request) Failing after 1m5s
2026-08-14 15:36:30 -05:00
bermudalamb 86f48ed3f8 fix: add back-to-shop navigation on cart page 2026-08-14 15:20:11 -05:00
bermudalamb cc3798a7f8 Merge pull request 'fix: cart badge not updating after account creation due to stale closure' (#11) from fix/cart-badge-stale-closure into main
SonarQube Analysis / sonarqube (push) Successful in 3m4s
Tests / backend-unit (push) Successful in 50s
Tests / backend-integration (push) Failing after 52s
Tests / frontend-e2e (push) Failing after 45s
Reviewed-on: #11
2026-08-14 15:15:12 -05:00
bermudalamb b85cdb9c44 fix: cart badge not updating after account creation due to stale closure
SonarQube Analysis / sonarqube (pull_request) Successful in 7m25s
Tests / backend-unit (pull_request) Successful in 1m46s
Tests / backend-integration (pull_request) Failing after 3m6s
Tests / frontend-e2e (pull_request) Failing after 1m16s
2026-08-14 15:13:55 -05:00
bermudalamb 853a962eb5 Merge pull request 'fix: TypeScript type mismatch between UspsValidationResult and local fallback object' (#10) from fix/usps-type-mismatch into main
Tests / frontend-e2e (push) Failing after 49s
SonarQube Analysis / sonarqube (push) Successful in 3m24s
Tests / backend-unit (push) Successful in 40s
Tests / backend-integration (push) Failing after 1m2s
Reviewed-on: #10
2026-08-14 14:49:05 -05:00
bermudalamb b73b472d80 fix: TypeScript type mismatch between UspsValidationResult and local fallback object
SonarQube Analysis / sonarqube (pull_request) Successful in 3m12s
Tests / backend-unit (pull_request) Successful in 57s
Tests / backend-integration (pull_request) Failing after 1m7s
Tests / frontend-e2e (pull_request) Failing after 38s
2026-08-14 14:47:52 -05:00
bermudalamb 4f055a2fcd Merge pull request 'feat: add customer cart with expiry, shipping addresses with USPS validation, and multi-item PayPal checkout' (#9) from feature/cart-shipping-checkout into main
SonarQube Analysis / sonarqube (push) Failing after 1m13s
Tests / backend-unit (push) Successful in 41s
Tests / backend-integration (push) Failing after 1m4s
Tests / frontend-e2e (push) Failing after 1m12s
Reviewed-on: #9
2026-08-14 14:05:09 -05:00
bermudalamb 9ab689e624 feat: add customer cart with expiry, shipping addresses with USPS validation, and multi-item PayPal checkout
SonarQube Analysis / sonarqube (pull_request) Failing after 59s
Tests / backend-unit (pull_request) Successful in 34s
Tests / backend-integration (pull_request) Failing after 1m33s
Tests / frontend-e2e (pull_request) Failing after 1m5s
2026-08-14 14:02:25 -05:00
bermudalamb 433d7e1db5 Merge pull request 'fix: Dockerfile' (#8) from fix/Dockerfile into main
SonarQube Analysis / sonarqube (push) Successful in 4m25s
Tests / backend-unit (push) Successful in 55s
Tests / backend-integration (push) Successful in 1m4s
Tests / frontend-e2e (push) Failing after 55s
Reviewed-on: #8
2026-08-14 10:58:33 -05:00
bermudalamb 845b1f29b9 fix: Dockerfile
SonarQube Analysis / sonarqube (pull_request) Successful in 3m3s
Tests / backend-unit (pull_request) Successful in 2m0s
Tests / backend-integration (pull_request) Successful in 1m50s
Tests / frontend-e2e (pull_request) Failing after 1m0s
2026-08-14 10:58:07 -05:00
bermudalamb b3835c1e9d Merge pull request 'chore: replace manual SQL migrations with node-pg-migrate' (#7) from feat/node-pg-migrate into main
SonarQube Analysis / sonarqube (push) Successful in 4m47s
Tests / backend-unit (push) Successful in 54s
Tests / backend-integration (push) Successful in 1m40s
Tests / frontend-e2e (push) Failing after 1m2s
Reviewed-on: #7
2026-08-14 10:05:59 -05:00
bermudalamb 7f4479605a chore: replace manual SQL migrations with node-pg-migrate
SonarQube Analysis / sonarqube (pull_request) Successful in 2m42s
Tests / backend-unit (pull_request) Successful in 34s
Tests / backend-integration (pull_request) Successful in 50s
Tests / frontend-e2e (pull_request) Failing after 38s
2026-08-14 10:05:09 -05:00
bermudalamb 6ca3366821 Merge pull request 'fix: markdown editor not accepting input due to Form.Item prop injection conflict' (#6) from fix/markdown-editor-not-editable into main
SonarQube Analysis / sonarqube (push) Successful in 4m9s
Tests / backend-unit (push) Successful in 44s
Tests / backend-integration (push) Successful in 54s
Tests / frontend-e2e (push) Failing after 45s
Reviewed-on: #6
2026-08-14 09:45:22 -05:00
bermudalamb 9325610517 fix: markdown editor not accepting input due to Form.Item prop injection conflict
SonarQube Analysis / sonarqube (pull_request) Successful in 3m17s
Tests / backend-unit (pull_request) Successful in 50s
Tests / backend-integration (pull_request) Successful in 1m26s
Tests / frontend-e2e (pull_request) Failing after 40s
2026-08-14 09:43:59 -05:00
bermudalamb 83232d6ac2 Merge pull request 'fix: add frontend package-lock.json' (#5) from fix/package-lock into main
SonarQube Analysis / sonarqube (push) Successful in 2m55s
Tests / backend-unit (push) Successful in 1m11s
Tests / backend-integration (push) Successful in 1m1s
Tests / frontend-e2e (push) Failing after 45s
Reviewed-on: #5
2026-08-14 09:27:22 -05:00
bermudalamb 336ad3d5ab fix: add frontend package-lock.json
Introduces the `package-lock.json` file to the frontend directory to ensure consistent dependency versions across development environments and deployments. This prevents dependency drift and improves build reliability.
2026-08-14 09:27:22 -05:00
bermudalamb 827f065bb2 Merge pull request 'fix: bound admin image uploads and use a CSPRNG for stored filenames' (#4) from fix/sonarqube-upload-limits-and-weak-rng into main
SonarQube Analysis / sonarqube (push) Successful in 3m28s
Tests / backend-unit (push) Successful in 41s
Tests / backend-integration (push) Successful in 1m19s
Tests / frontend-e2e (push) Failing after 46s
Reviewed-on: #4
2026-08-14 09:25:58 -05:00
bermudalambandClaude Opus 5 aba47c81e3 fix: bound admin image uploads and use a CSPRNG for stored filenames
SonarQube Analysis / sonarqube (pull_request) Successful in 3m49s
Tests / backend-unit (pull_request) Successful in 33s
Tests / backend-integration (pull_request) Successful in 1m6s
Tests / frontend-e2e (pull_request) Failing after 1m7s
SonarQube flagged three hotspots in the admin router: multer was
configured with no content length limits, and stored filenames were
derived from Date.now() plus Math.random().

- Cap the multipart body on every dimension: 6 files, 8 MiB per image,
  8 fields, 64 KiB per field. Without limits a single request could
  fill the uploads volume.
- Generate stored filenames with crypto.randomUUID() so paths are not
  predictable. Image ordering is unaffected; sort_order already drives it.
- Wrap the upload middleware to translate MulterError into 413/400 JSON.
  The app mounts no error handler, so a limit rejection would otherwise
  surface as an HTML 500.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 09:22:45 -05:00
bermudalamb da240621db docs: fix local-dev commands for PowerShell, document branching/commit conventions
SonarQube Analysis / sonarqube (push) Successful in 4m42s
Tests / backend-unit (push) Successful in 49s
Tests / backend-integration (push) Successful in 51s
Tests / frontend-e2e (push) Failing after 40s
2026-08-14 08:50:46 -05:00
bermudalamb 01cf325018 Merge pull request 'fix: sonarqube denial of service issue' (#2) from fix/sonarqube-denial-of-service-issue into main
SonarQube Analysis / sonarqube (push) Successful in 3m21s
Tests / backend-unit (push) Successful in 1m7s
Tests / backend-integration (push) Successful in 1m28s
Tests / frontend-e2e (push) Failing after 45s
Reviewed-on: #2
2026-08-14 08:43:37 -05:00
bermudalamb 81118245ce fix: sonarqube denial of service issue 2026-08-14 08:43:37 -05:00
bermudalamb 7a026b7e82 Merge pull request 'ci: fix Postgres service port conflict in CI' (#3) from fix/fix-workflow-test-scenario-ports into main
SonarQube Analysis / sonarqube (push) Successful in 4m37s
Tests / backend-unit (push) Successful in 1m16s
Tests / backend-integration (push) Successful in 1m6s
Tests / frontend-e2e (push) Failing after 43s
Reviewed-on: #3
2026-08-14 08:42:50 -05:00
bermudalamb 2bff3a231f ci: fix Postgres service port conflict in CI
SonarQube Analysis / sonarqube (pull_request) Successful in 4m43s
Tests / backend-unit (pull_request) Successful in 1m1s
Tests / backend-integration (pull_request) Successful in 1m32s
Tests / frontend-e2e (pull_request) Failing after 59s
2026-08-14 08:41:40 -05:00
bermudalamb 8ffd05f73d Merge pull request 'fix: move scripts folder to correct location' (#1) from fix/failing-workflows into main
SonarQube Analysis / sonarqube (push) Successful in 4m8s
Tests / backend-unit (push) Successful in 35s
Tests / backend-integration (push) Failing after 2s
Tests / frontend-e2e (push) Failing after 2s
Reviewed-on: #1
2026-08-14 08:16:26 -05:00
bermudalamb 4b524e0da3 fix: move scripts folder to correct location
SonarQube Analysis / sonarqube (pull_request) Successful in 4m31s
Tests / backend-unit (pull_request) Successful in 59s
Tests / backend-integration (pull_request) Failing after 4s
Tests / frontend-e2e (pull_request) Failing after 1s
2026-08-14 08:15:52 -05:00
bermudalamb 234330e482 Add CI test workflow with job summaries
SonarQube Analysis / sonarqube (push) Successful in 3m55s
Tests / backend-unit (push) Failing after 55s
Tests / backend-integration (push) Failing after 2s
Tests / frontend-e2e (push) Failing after 1s
2026-08-13 19:10:30 -05:00
bermudalamb f5218782e9 Fix header overflow on mobile viewports
SonarQube Analysis / sonarqube (push) Successful in 6m55s
2026-08-13 17:51:15 -05:00
bermudalamb 921022c658 Add backend unit/integration tests, Playwright e2e tests, README, cookie Secure fix
SonarQube Analysis / sonarqube (push) Successful in 4m20s
2026-08-13 22:35:38 +00:00
bermudalamb 2adbf24b23 Add SonarQube CI workflow
SonarQube Analysis / sonarqube (push) Successful in 11m16s
2026-08-13 21:53:31 +00:00