docs: sync from backend e8cb64c — DB audit bounded high cleanup

This commit is contained in:
Siavash Sameni
2026-06-07 15:40:42 +04:00
parent c5aecb4130
commit 4879046f44
3 changed files with 39 additions and 20 deletions

View File

@@ -12,6 +12,16 @@ entries on top. Maintained by agents per the rule in `../AGENTS.md`.
---
### 2026-06-07 — backend@e8cb64c, frontend@05e9bcd — DB audit bounded high cleanup and blog status enum
**Commits:** `e8cb64c` `05e9bcd`
**Touched:** backend `src/services/blog/blogPostgresSchema.ts`, `src/db/migrations/0027_blog_post_status_enum.sql`, `src/db/backfill/backfill-users.ts`, `src/db/repositories/drizzle/DrizzleUserRepo.ts`, `src/db/repositories/drizzle/DrizzleMarketplaceRepo.ts`, targeted regression tests, `package.json`, `package-lock.json`; frontend `package.json`, `Dockerfile`; docs `09 - Audits/DB Query & Schema Audit - 2026-06-06.md`, `09 - Audits/Activity Log.md`, `RTK.md`
**Why:** Normalize the audit fixed table so early C/H closures are keyed by issue ID, close H38 by enforcing `blog_post_status` in runtime schema and migration, and keep residual H1/H30-H33-style paths bounded with regression coverage. Added the CI/build freeze rule to the canonical docs RTK so agents diagnose CI before changing build procedure.
**Verification:** backend `npm test -- --runInBand __tests__/blog-postgres-schema.test.ts __tests__/drizzle-payment-repo-export.test.ts __tests__/drizzle-user-repo.test.ts __tests__/drizzle-marketplace-repo-batch.test.ts __tests__/payment-migration.service.test.ts __tests__/auth-store-pg-query.test.ts __tests__/backfill-guard.test.ts` (7 suites / 52 tests), backend `npm run typecheck`, backend/frontend scoped `git diff --check`; frontend/backend version metadata confirmed at v2.9.43.
**Linked docs updated:** [[09 - Audits/DB Query & Schema Audit - 2026-06-06]]
---
### 2026-06-07 — backend@55321c0, frontend@525d50a — restore last-green Woodpecker build procedure
**Commits:** `55321c0` `525d50a`

View File

@@ -17,25 +17,25 @@ updated: 2026-06-07
| Stats endpoint: 10 serial `findPurchaseRequests` queries → 1 `GROUP BY status` | `2484150` v2.9.13 |
| Offer filter: per-offer `sameUser()` calls (N×2 queries) → resolve-once + in-memory filter | `2484150` v2.9.13 |
| Seller notification fan-out: in-JS filter + 100-row hard cap → SQL `watchedCategory` filter, no limit | `2484150` v2.9.13 |
| Auth user hydration: `rowToUser` child lookups per row → batched token/passkey/referrer hydration per result set | `4aa6ccb` v2.9.14 |
| Auth user save: users/tokens/passkeys independent writes → single `BEGIN`/`COMMIT` transaction with rollback | `4aa6ccb` v2.9.14 |
| Auth token/passkey save loops: one INSERT per child row → bulk token/passkey inserts inside the transaction | `4aa6ccb` v2.9.14 |
| Bulk notification canonicalization: one UUID→legacy lookup per notification → one batched lookup per bulk call | `2a56f98` v2.9.15 |
| Offer acceptance loser notifications: sequential per-offer INSERTs → one `createNotificationsBulk` call | `2a56f98` v2.9.15 |
| Template batch conversion: first-seller templates fetched twice → first-pass template cache reused | `2a56f98` v2.9.15 |
| Dispute statistics: four repeated status count scans → one status `GROUP BY` query | `2a56f98` v2.9.15 |
| C1: Auth user hydration `rowToUser` child lookups per row → batched token/passkey/referrer hydration per result set | `4aa6ccb` v2.9.14 |
| C8: Auth user save users/tokens/passkeys independent writes → single `BEGIN`/`COMMIT` transaction with rollback | `4aa6ccb` v2.9.14 |
| H1: Auth token/passkey save loops one INSERT per child row → bulk token/passkey inserts inside the transaction | `4aa6ccb` v2.9.14 |
| H2: Bulk notification canonicalization one UUID→legacy lookup per notification → one batched lookup per bulk call | `2a56f98` v2.9.15 |
| H6: Offer acceptance loser notifications sequential per-offer INSERTs → one `createNotificationsBulk` call | `2a56f98` v2.9.15 |
| H5: Template batch conversion first-seller templates fetched twice → first-pass template cache reused | `2a56f98` v2.9.15 |
| H9: Dispute statistics four repeated status count scans → one status `GROUP BY` query | `2a56f98` v2.9.15 |
| Chat query reads: full-table `findRows()` scan → SQL pushdown for participant/archive/unread filters with in-memory fallback only for unsupported predicates | `3ad3bbe` v2.9.16 |
| Chat JSONB filters: missing `participants`/`unread_counts` GIN indexes → added schema indexes and migration `0020_chat_jsonb_indexes.sql` | `3ad3bbe` v2.9.16 |
| Buyer purchase request list: per-request latest-payment lookup → one `DISTINCT ON (purchase_request_id)` payment query per page | `3ad3bbe` v2.9.16 |
| Notification bulk mark/delete: serial per-id writes with no cap → set-based repo operations plus 100-id API cap | `3ad3bbe` v2.9.16 |
| Payments admin list: unbounded `findAllPayments` plus per-row buyer/seller lookups → single joined payment/user query | `0835be9` v2.9.17 |
| Seller template list: per-template seller/category lookups → batched seller/category page lookups | `0835be9` v2.9.17 |
| Payment coordinator rejected-seller notifications: per-seller notification loop → one `createNotificationsBulk` call | `0835be9` v2.9.17 |
| Category path lookup: one query per ancestor level → one recursive CTE returning root-to-leaf path | `0835be9` v2.9.17 |
| Payment export: `listForExport` fetched all matching payments → bounded export query with default/max limits | `5ff0013` v2.9.18 |
| Seller lookup: `findSellers` without input limit loaded all sellers → safe default cap while preserving explicit limits | `5ff0013` v2.9.18 |
| Active template seller list/detail: full seller/template table scans → capped list query and scoped single-seller detail query | `5ff0013` v2.9.18 |
| SHKeeper migration report: loaded all SHKeeper payments → bounded sorted scan with explicit `maxRecords` cap | `5ff0013` v2.9.18 |
| C3: Chat JSONB filters missing `participants`/`unread_counts` GIN indexes → added schema indexes and migration `0020_chat_jsonb_indexes.sql` | `3ad3bbe` v2.9.16 |
| C5: Buyer purchase request list per-request latest-payment lookup → one `DISTINCT ON (purchase_request_id)` payment query per page | `3ad3bbe` v2.9.16 |
| H3: Notification bulk mark/delete serial per-id writes with no cap → set-based repo operations plus 100-id API cap | `3ad3bbe` v2.9.16 |
| C4: Payments admin list unbounded `findAllPayments` plus per-row buyer/seller lookups → single joined payment/user query | `0835be9` v2.9.17 |
| H4: Seller template list per-template seller/category lookups → batched seller/category page lookups | `0835be9` v2.9.17 |
| H7: Payment coordinator rejected-seller notifications per-seller notification loop → one `createNotificationsBulk` call | `0835be9` v2.9.17 |
| H8: Category path lookup one query per ancestor level → one recursive CTE returning root-to-leaf path | `0835be9` v2.9.17 |
| H30: Payment export `listForExport` fetched all matching payments → bounded export query with default/max limits | `5ff0013` v2.9.18 |
| H31: Seller lookup `findSellers` without input limit loaded all sellers → safe default cap while preserving explicit limits | `5ff0013` v2.9.18 |
| H32: Active template seller list/detail full seller/template table scans → capped list query and scoped single-seller detail query | `5ff0013` v2.9.18 |
| H33: SHKeeper migration report loaded all SHKeeper payments → bounded sorted scan with explicit `maxRecords` cap | `5ff0013` v2.9.18 |
| M2: `updatePurchaseRequestStatus` 3rd redundant read → reuse first `currentRequest` with status override | `2abba67` v2.9.19 |
| M5: `createOffer` fetched PR twice → cached first `requestForOffer` reused for notification path | `2abba67` v2.9.19 |
| M7: `createReviewRecord` sequential `resolveUserUuid``Promise.all` parallel | `2abba67` v2.9.19 |
@@ -90,6 +90,7 @@ updated: 2026-06-07
| H16: batch template conversion request/offer/status writes split across service calls → proposal conversions use one serializable vital transaction; template usage remains explicitly non-vital | `957c356` v2.9.38 |
| H17: template payment completion per-request status loop → one serializable bulk status update with an all-rows-updated guard | `957c356` v2.9.38 |
| H18: email-code registration split referrer/user/temp writes → one serializable auth-store registration transaction, with referral bonus left post-commit best-effort | `957c356` v2.9.38 |
| H38: `blog_posts.status` stored as text instead of pgEnum → runtime schema and migration `0027_blog_post_status_enum.sql` enforce `blog_post_status` | `e8cb64c` v2.9.43 |
---
@@ -559,13 +560,13 @@ status, priority, and category are `text` with TypeScript-only `$type<>` casts.
---
### 38. blogPosts.status stored as text instead of pgEnum
### 38. blogPosts.status stored as text instead of pgEnum | **FIXED** `e8cb64c` v2.9.43
> **Category:** Wrong Schema | **File:** `src/db/schema/blogPost.ts:50`
`status` is declared as `text` with a `$type<BlogPostStatus>` cast only. Postgres accepts any string. The composite indexes `blog_posts_status_published_idx`, `blog_posts_category_status_idx`, and `blog_posts_featured_status_idx` all rely on this column and return incorrect results for mistyped values.
**Fix:** Create a `blog_post_status` pgEnum with values `('draft', 'published', 'archived')` and use it as the column type.
**Fix:** `e8cb64c` adds `blog_post_status`, updates the runtime schema initializer so new tables use the enum, and adds migration `0027_blog_post_status_enum.sql` to normalize existing rows and convert `blog_posts.status` from text to the enum.
---