diff --git a/09 - Audits/Activity Log.md b/09 - Audits/Activity Log.md index 4b75d56..b5c3f94 100644 --- a/09 - Audits/Activity Log.md +++ b/09 - Audits/Activity Log.md @@ -11,6 +11,123 @@ entries on top. Maintained by agents per the rule in `../AGENTS.md`. --- +### 2026-06-05 — backend@v2.8.84, frontend@v2.8.101 — Notifications wired end-to-end (id-seam normalize + Mini App joins user room) + +**Commits:** backend v2.8.84, frontend v2.8.101 +**Touched:** backend `services/notification/NotificationService.ts`; frontend `sections/telegram/hooks/use-telegram-notifications.ts` +**Why:** No notifications reached the Mini App (in-app, real-time, or Telegram push). Two root causes. (1) **Id seam:** notifications were created with whatever id the event carried — usually a Postgres uuid (`sellerId`/`buyerId`) — but everything that *consumes* a notification keys on the user's session legacy ObjectId: the in-app fetch (`req.user.id`), the `user-` socket room, and `TelegramLink.userId`. So a notification stored under a uuid was invisible to fetch, never reached the room, and `sendTelegramNotificationToUser(uuid)` found `no_link`. Fix: `NotificationService.createNotification`/`createNotificationsBulk` now normalise `userId` via `toCanonicalUserId` (uuid → `users.legacy_object_id`) before persist + real-time emit + Telegram forward, so all three line up. (2) **Mini App never joined its room:** the socket connected but `use-telegram-notifications` only registered listeners — it never emitted `join-user-room`, so the backend's targeted `new-notification` emits had no subscriber. Added a `joinUserRoom(userId)` effect that (re)joins on every connect. `selfId` is the session ObjectId, matching the socket's authed id and the now-normalised emit target. +**Verification:** backend `npx tsc --noEmit`; frontend `npx tsc --noEmit --ignoreDeprecations 6.0` + eslint — clean. After deploy: a marketplace event (offer received/accepted, payment, delivery) creates a notification that (a) shows in the Mini App bell list, (b) bumps the unread badge live, and (c) arrives as a Telegram bot message for linked users. + +--- + +### 2026-06-05 — backend@v2.8.83, frontend@v2.8.100 — Select-offer 403 (id seam) + offer delivery-time `[object Object]` + +**Commits:** backend v2.8.83, frontend v2.8.100 +**Touched:** backend `services/marketplace/marketplaceController.ts` (`selectOffer`); frontend `sections/telegram/view/telegram-request-detail-view.tsx` +**Why:** Testing the new Mini App «انتخاب و پرداخت» surfaced two bugs. (1) **403 on select-offer**: `selectOffer` gated buyer ownership with `toIdString(purchaseRequest.buyerId) !== buyerId` — the session legacy ObjectId vs the PG-uuid buyerId (the recurring seam) → the real buyer got 403. Switched to `!(await sameUser(buyerId, toIdString(purchaseRequest.buyerId)))`. (2) **Delivery time rendered `[object Object]`**: the offer card assumed `offer.deliveryTime` was a number, but it can be an object (`{amount, unit}`). Now extracts `dt.amount ?? dt.value ?? offer.deliveryTimeAmount` when it isn't a plain number. +**Verification:** backend `npx tsc --noEmit`; frontend `npx tsc --noEmit --ignoreDeprecations 6.0` + eslint — clean. After deploy: buyer taps «انتخاب و پرداخت» → offer accepted (no 403) → in-shell payment opens; delivery time shows a number of days, not `[object Object]`. + +--- + +### 2026-06-05 — frontend@v2.8.99 — Mini App buyer: see received offers + select & pay in-shell + +**Commits:** frontend only (v2.8.99) +**Touched:** `sections/telegram/view/telegram-request-detail-view.tsx`, `sections/telegram/locales/{fa,en,types}.ts` (`offers_title`, `offer_select_pay`, `offer_delivery_days`, `offer_accepting`) +**Why:** When the buyer's stepper reached «انتخاب و پرداخت» (step 3, status `received_offers`) the Mini App had no way to act — the pay CTA only showed for `pending_payment`. Now, for a buyer on an offer-selection status, the detail view fetches and lists the received offers (seller, price, delivery days, description) and each has a «انتخاب و پرداخت» button → `acceptOffer(requestId, offerId)` (sets `selectedOfferId`, moves to `pending_payment`) → `refresh()` → opens the in-shell direct-transfer payment screen via `onPay`. Reused the existing offers SWR (broadened from review-only to also fire on `received_offers`/`offers_received`/`in_negotiation`). +**Verification:** frontend `npx tsc --noEmit --ignoreDeprecations 6.0` + eslint clean. After deploy: buyer opens a request with an offer → sees the offer card → «انتخاب و پرداخت» → in-shell payment opens; paying advances the stepper. + +--- + +### 2026-06-05 — frontend@v2.8.98 — Mini App seller shop: product-type filter + show controls for small shops + +**Commits:** frontend only (v2.8.98) +**Touched:** `sections/telegram/view/telegram-seller-shop-view.tsx` +**Why:** The seller shop (per-seller template list) already had search + sort but (1) no filter, and (2) the controls bar was gated on `allTemplates.length > 3`, so a typical 3-template shop showed no controls at all. Added a product-type filter (chips built only for the types the seller actually sells — physical/digital/service/consultation, plus «همه») wired through `TelegramListControls`, and lowered the controls threshold to `> 1` so shops with 2–3 templates still get search/filter/sort. +**Verification:** frontend `npx tsc --noEmit --ignoreDeprecations 6.0` + eslint clean. After deploy: opening a seller shop with ≥2 templates shows the controls bar; the filter chips reflect the seller's product types and narrow the list. + +--- + +### 2026-06-04 — backend@v2.8.82, frontend@v2.8.97 — Stepper advances: offer→step, seller offer detection, real-time on code-verify + +**Commits:** backend v2.8.82, frontend v2.8.97 +**Touched:** backend `services/marketplace/marketplaceController.ts` (`getOffersForRequest` sellerId filter, `verifyDeliveryCode` socket emit); frontend `sections/request/request-config.tsx` (buyer `received_offers`→3), `actions/marketplace.ts` (`getSellerOfferForRequest` fallback) +**Why:** Three related stepper bugs surfaced while testing an offer end-to-end. (1) **Buyer stepper stuck at "awaiting offers" (2)** after an offer arrived: `determineBuyerStep('received_offers')` returned `offersCount>0 ? 3 : 2`, but callers that don't thread `offersCount` (the Mini App) got 2. The status itself means an offer arrived on the buyer's own request, so it now returns 3 unconditionally. (2) **Seller stuck on "send offer" (1)** after submitting: `getSellerOfferForRequest` matched offers by `o.sellerId === user._id`, but `o.sellerId` is a PG uuid and `user._id` is the legacy ObjectId (the seam) → no match → `sellerOfferStatus` undefined → step 1. Backend `getOffersForRequest` now filters to the seller's own offers via `sameUser` when `?sellerId=` is passed, and the client returns `data[0]` as a fallback. (3) **Seller needed a manual refresh** to go from "awaiting buyer" (4) to "receive funds" (5): the seller's code entry (`verifyDeliveryCode`) replaced the buyer's `confirmDelivery` in v2.8.95 but never emitted a socket event, so no live update. Added the `purchase-request-update` / `status-changed` emit on successful verify (matching what `confirmDelivery` did). +**Verification:** backend `npx tsc --noEmit`; frontend `npx tsc --noEmit --ignoreDeprecations 6.0` + eslint — all clean. After deploy: buyer sees step 3 once an offer arrives; seller sees step 2 right after sending an offer; seller's stepper advances to "receive funds" without a manual refresh after entering the code. + +--- + +### 2026-06-04 — frontend@v2.8.96 — Mini App account: don't show "email verified" when the user has no email + +**Commits:** frontend only (v2.8.96) +**Touched:** `sections/telegram/view/telegram-account-view.tsx`, `sections/telegram/locales/{fa,en,types}.ts` (`email_not_set`) +**Why:** A Telegram sign-up has `isEmailVerified=true` but no email address, so the account header showed a green «ایمیل تأیید شده» badge even though the email field was empty. Gated the verified badge on `user?.email && user?.isEmailVerified`. Added a third state for the no-email case: a «ایمیل ثبت نشده» badge that opens the in-shell settings (`onOpenSettings`) to add an email. The not-verified (has-email-but-unverified) branch is unchanged. +**Verification:** frontend `npx tsc --noEmit --ignoreDeprecations 6.0` + eslint clean. After deploy: a Telegram user with no email sees «ایمیل ثبت نشده» (→ settings), not a false «ایمیل تأیید شده». + +--- + +### 2026-06-04 — frontend@v2.8.95 — Delivery confirmation: code is entered by the seller, buyer's «تایید دریافت کالا» removed + +**Commits:** frontend only (v2.8.95) +**Touched:** `sections/telegram/view/telegram-request-detail-view.tsx` (Mini App buyer: drop confirm button), `sections/request/components/buyer-steps/step-5-receive-goods.tsx` (web buyer: drop confirm button), `sections/request/components/seller-steps/step-4-waiting-for-confirmation.tsx` (web seller: code display → code input) +**Why:** The handover flow was wrong/duplicated: the buyer had a «تایید دریافت کالا» button (`confirmDelivery`) AND the seller was shown the expected code. Per the user, the single correct mechanism is: the **buyer gives the 6-digit code, the seller enters it** to confirm — and that 403'd anyway (`confirmDelivery` compared session ObjectId `!==` PG-uuid buyerId — same id seam). Changes: (1) Mini App buyer (`status delivery`) now only displays the code to hand over — the `confirmDelivery` button/handler/import removed. (2) Web buyer step-5: removed the «تایید دریافت کالا» button (kept the code display + an explanatory note). (3) Web seller step-4: replaced the code **display** (`getDeliveryCode`, which leaked the code to the seller and 403s post-v2.8.79) with a 6-digit **input** + «تایید تحویل کالا» calling `verifyDeliveryCode(id, code)`. Both `confirmDelivery` and `verifyDeliveryCode` reach the same `delivered` state, so no escrow-release regression. (Mini App seller already had the input from v2.8.94.) +**Verification:** frontend `npx tsc --noEmit --ignoreDeprecations 6.0` + eslint clean. After deploy: buyer sees only the code (no confirm button); seller enters the buyer's code → status → delivered; no 403. + +--- + +### 2026-06-04 — backend@v2.8.81 — Public shop settings: resolve uuid→legacy ObjectId (name/avatar were blank publicly) + +**Commits:** backend v2.8.81 +**Touched:** `services/marketplace/shopSettingsStore.ts` (`getSellerShopSettings` + new `resolveSellerLegacyId`) +**Why:** Found while setting shop name/avatar/cover for the three seed sellers. After PUT-ing settings, the seller's OWN `GET /marketplace/shop/settings` returned them correctly, but the PUBLIC `GET /marketplace/shop/settings/:sellerId` (what the shop page shows buyers) returned `data:null` — blank name, no avatar/cover. Root cause: the recurring Mongo↔PG id seam. `shop_settings` rows are keyed by `seller_legacy_object_id` (the seller's legacy Mongo ObjectId, which is what `req.user.id` is on the own-settings path), but the public sellers list returns each seller by their PG **uuid**, so `getSellerShopSettings(uuid)` queried `where seller_legacy_object_id = ` and matched nothing. Fix: when the direct lookup misses, resolve the incoming id via `select legacy_object_id from users where id=$1` (a 24-hex id is treated as already-legacy) and retry. Mirrors the existing reverse `resolveSellerUuid` helper. +**Verification:** backend `npx tsc --noEmit` clean. After deploy: `GET /marketplace/shop/settings/` returns the saved name/avatar/cover for all three sellers (was null). Seeded: seller/seller1/seller2 @amn.gg each have a shop name, description, avatar, cover image, social links, and 3 active products. + +--- + +### 2026-06-04 — backend@v2.8.80 — Shop sellers list cache: invalidate global `templates:list` on per-seller change + +**Commits:** backend v2.8.80 +**Touched:** `services/redis/cacheService.ts` (`invalidateTemplatesCache`) +**Why:** Found while seeding shops for three sellers via API: after creating templates for `seller1@amn.gg`/`seller2@amn.gg`, the public shop endpoint (`GET /marketplace/request-templates/sellers`) still returned only the first seller. Root cause — the global sellers list is cached under Redis key `templates:list`, but `RequestTemplateService` create/update/delete call `invalidateTemplatesCache(sellerId)` which only deleted `templates:seller:`, never `templates:list`. So a newly-onboarded seller didn't appear in `/shop` until the 5-minute TTL elapsed. Fix: when invalidating with a `sellerId`, also `del('templates:list')` since the aggregated list changes whenever any seller adds/edits/removes a template. +**Verification:** backend `npx tsc --noEmit` clean. After deploy: a brand-new seller's first template appears in `GET …/request-templates/sellers` (and dev.amn.gg/shop) immediately, not after TTL. (Seeded data: seller/seller1/seller2 @amn.gg each have 3 active templates — two at 0.01 USDT, one at 0.05 — with images, BSC USDT/USDC payment config.) + +--- + +### 2026-06-04 — frontend@v2.8.94 — Telegram Mini App: seller delivery-code entry field + +**Commits:** frontend only (v2.8.94); backend stays v2.8.79 +**Touched:** `telegram-request-detail-view.tsx` (seller code-entry card), `locales/{fa,en,types}.ts` (`seller_code_*` strings) +**Why:** The escrow handover is two-sided: the buyer generates a 6-digit delivery code (shown in v2.8.92) and the **seller enters it** to confirm delivery. The Mini App seller had no field for this — at status `delivery` the seller only saw «منتظر تایید خریدار» with nowhere to type the code. Added a code-entry card for `role === 'seller' && status === 'delivery'`: a numeric 6-digit input (digit-filtered, monospace, centred) + «تایید تحویل کالا» button calling `verifyDeliveryCode(requestId, code)` then `refresh()` so the status advances to `delivered`. Backend `verifyDeliveryCode` already uses the `sameUser` seller gate (v2.8.77), so this composes with the id-seam fix. +**Verification:** frontend `npx tsc --noEmit --ignoreDeprecations 6.0` + eslint clean. Admin verifies after deploy: buyer opens `delivery` request → sees code; seller opens same request in Mini App → enters that code → «تایید تحویل کالا» → status → delivered. + +--- + +### 2026-06-04 — backend@v2.8.79 — Delivery code: apply `sameUser` to buyer auth gates (Mini App showed "—") + +**Commits:** backend v2.8.79 (frontend stays v2.8.93) +**Touched:** `services/marketplace/marketplaceController.ts` (`generateDeliveryCode`, `getDeliveryCode`) +**Why:** In the Mini App receive-goods step the delivery code rendered as «—»: both `getDeliveryCode` and `generateDeliveryCode` returned 403. Root cause is the recurring Mongo↔PG id seam — these two handlers still used the strict `request.buyerId !== userId` comparison (session legacy ObjectId vs PG-uuid `buyerId`), unlike `verifyDeliveryCode` which was already moved to the async `sameUser` helper in v2.8.77. Replaced both buyer checks with `!(await sameUser(userId, toIdString(request.buyerId)))`. Added an explicit `!userId` unauthorized guard in `getDeliveryCode` to restore the `string` narrowing the removed `!==` previously provided (so `getDeliveryCode(id, userId)` still type-checks). The repo layer (`getDeliveryCodeForUser`) already resolves the seam via `resolveEntityId`, so once the code exists it loads without regenerating. +**Verification:** backend `npx tsc --noEmit` clean. Admin verifies after deploy: buyer opens a `delivery`-status request in the Mini App → a 6-digit delivery code appears (no «—», no 403); seller can verify it. + +--- + +### 2026-06-04 — backend@v2.8.78, frontend@v2.8.93 — Gate shop orders on payment: abandon-cancel + 30-min TTL sweep + +**Commits:** backend v2.8.78, frontend v2.8.93 +**Touched:** backend `services/admin/dataCleanupService.ts` (new `cancelStaleUnpaidRequests`), `services/admin/ttlCleanupJob.ts` (schedule it every 5 min); frontend `telegram-payment-view.tsx` (abandon-cancel on unmount) +**Why:** Shop checkout creates the purchase request in `pending_payment` *before* the buyer pays — unavoidable because the direct-balance deposit address is derived per-request, so the request must exist for the AMN scanner to credit it (the payment-intent route requires `purchaseRequestId`/`sellerOfferId`/`sellerId`). That left unpaid orders lingering. Now, per the user's choice (both mechanisms, 30-min window, only cancel orders with **no** deposit): (1) **Frontend abandon-cancel** — when the buyer leaves the in-shell checkout payment screen without completing, the just-created order is set to `cancelled` via `updateRequestStatus`; two refs (`paidRef`, `depositRef`) suppress the cancel if the deposit was confirmed *or* a partial deposit was detected, so funds are never orphaned. (2) **Backend TTL sweep** — `cancelStaleUnpaidRequests` runs every 5 min, cancels `pending_payment` requests older than 30 minutes that have **no** active payment (`findActivePaymentForRequest === null`); any active/slow on-chain payment is left untouched. The sweep is the backstop for app-closed cases the frontend unmount can't catch. +**Verification:** backend `npx tsc --noEmit` clean; frontend `npx tsc --noEmit --ignoreDeprecations 6.0` + eslint clean. Admin verifies after deploy: shop checkout → reach payment step → press back without paying → order shows `cancelled` (not lingering pending_payment); a paid/underpaid order is NOT cancelled. + +--- + +### 2026-06-04 — frontend@v2.8.92 — Telegram Mini App: buyer receive-goods/confirm + checkout stepper line fix + +**Commits:** frontend only (v2.8.92); backend stays at v2.8.77 +**Touched:** `telegram-request-detail-view.tsx` (buyer receive-goods flow), `components/telegram-request-stepper.tsx` (row width cap/centre), `locales/{fa,en,types}.ts` (delivery-code + confirm-receipt strings) +**Why:** (1) The buyer had no way to confirm receipt inside the Mini App — once the seller shipped (status `delivery`, fixed in backend v2.8.77's `sameUser` patch), the buyer was stuck at step 5 «دریافت کالا» with no action. Mirrored the web `step-5-receive-goods`: on `status === 'delivery'` for a buyer, auto-load/generate the delivery code (`getDeliveryCode` → fallback `generateDeliveryCode`) shown dashed/monospace for the buyer to hand the seller, plus a «تایید دریافت کالا» button calling `confirmDelivery(requestId)` then `refresh()` so the stepper advances to «تایید نهایی». (2) The 3-step checkout stepper stretched each cell across the full Telegram width, leaving a long connector stub past the last dot — capped the steps row at `steps.length * 96` and centred it (`margin: 0 auto`), so both the 3-step checkout and 6-step request steppers render tight, even connectors. +**Verification:** frontend `npx tsc --noEmit --ignoreDeprecations 6.0` clean. Admin verifies after deploy: buyer opens a `delivery`-status request → sees delivery code + «تایید دریافت کالا» → tap → request advances to تایید نهایی; checkout stepper line is clean (no stub). + +--- + ### 2026-06-03 — scanner@ccd96e8, backend@22ae0bd, frontend@10c4292 — direct scanner balance checks and balance watches **Commits:** scanner `ccd96e8` (tag `v0.1.8`), backend `22ae0bd` (tag `v2.8.60`), frontend `10c4292` (tag `v2.8.60`) @@ -699,6 +816,19 @@ TON-only, no EVM; backend already has tonProofService). --- +### 2026-06-03 — frontend@HEAD — Mini App floating cart FAB (v2.8.60) + +**Why:** User wants the web shop's small edge-docked basket (count badge) in the Mini App too. +New `TelegramCartFab` component pinned above the tab bar, shown on shop tab + in-shell seller +shop, opens the in-shell cart. Replaced the header cart chip + bottom cart bar. +**Pending next (user requests):** (1) Mini App «تنظیمات» — dark/light mode for the Telegram shell +(needs a dark palette variant of telegram-shell-css + toggle; should follow webApp.colorScheme); +(2) «درخواست جدید» button font — it is Telegram's NATIVE MainButton, font cannot be customized; +option is replacing it with an in-shell button using the project font. +**Verification:** tsc + eslint clean. + +--- + ### 2026-06-02 — backend@7c4dedf — complete dual-write repos, migrations pipeline, TTL scheduler, address reconciliation **Commits:** `7c4dedf` (backend v2.8.44), frontend v2.8.44 @@ -709,83 +839,612 @@ TON-only, no EVM; backend already has tonProofService). --- -### 2026-06-04 — backend@41087c7, frontend@ab18334, deployment@8764fdf — CI/CD infrastructure updates and Telegram Mini App delivery flow +### 2026-06-03 — frontend@HEAD — Mini App floating support bubble (v2.8.61) -**Commits:** backend `41087c7`, `ed7b960`, `aaa7a04`, `f380f92`, `64792b1`; frontend `ab18334`, `4bc038e`, `81caab5`, `0f3b7c0`, `482d64a`, `f6b1b0e`, `d37a0c0`, `abc5e73`, `0001f22`, `3d7df1a`, `e90659f`, `88fa4da`, `9f90995`, `e8fff89`, `715e143`, `57f8067`, `57bd04a`; deployment `8764fdf` -**Touched:** Backend CI/CD pipeline files, frontend Telegram Mini App components, Docker configurations, payment flow, chat archive, marketplace delivery -**Why:** Major CI/CD updates: mount /opt/escrow-dev for compose, build locally with registry push skip, pin pipelines to linux/arm64 agent with native builder. Telegram Mini App: seller delivery-code entry, abandon-cancel unpaid shop orders, buyer receive-goods/confirm, checkout stepper fixes, in-shell direct-transfer payment, search in archived chats, WhatsApp-style archive view with unarchive. Frontend Docker: raise Node.js heap to 4GB for Next.js build. -**Verification:** Backend and frontend builds passing, Telegram Mini App features verified in development. -**Linked docs updated:** [[Telegram Mini App]], [[CI-CD Pipeline]], [[Payment Flow - DePay & Web3]] +**Why:** Parity with the web dashboard's green floating chat widget. New `TelegramSupportFab` +(edge-docked, green bubble) on home/shop/requests tabs + in-shell seller shop; opens/creates the +support chat. Stacks above the cart FAB in shop contexts. +**Verification:** tsc + eslint clean. +**In progress / queued:** Points & Referral audit (background agent — bugs observed: values render +as "·", Bronze level claims "highest level"); Mini App dark mode (تنظیمات); buyer-parity phase 2 +(offers view/accept). --- -### 2026-06-04 — backend@9bb73e2, frontend@35ed72d — security hardening and feature enhancements +### 2026-06-03 — backend@c5d6490 — Points & Referral audit fixes (v2.8.62) -**Commits:** backend `9bb73e2`, `0283e73`, `fb85b38`, `94e2dbe`, `118466d`, `fde1c04`, `c2ae239`, `7810d6b`, `e2aad59`, `64c5d5c`, `62f0af8`; frontend `35ed72d`, `0358af5`, `e25b6b8`, `406810d`, `d64e194`, `131700b`, `0bdb5b3`, `7b3e9ca`, `9bafbbb`, `6dc3918`, `a8ae1e3`, `6adb2e0`, `a18e870`, `583d55a`, `7b949bf` -**Touched:** Delivery code auth gates, TTL sweep for stale purchase requests, marketplace uuid/ObjectId seam, chat archive/unarchive, participant canonicalization, stepper navigation, floating cart/support buttons, product-style template cards -**Why:** Security: use sameUser for buyer auth gates, cancel stale unpaid purchase requests via TTL sweep. Chat: boolean query filter fixes, support unarchive and archived list fetch, participant id mapping. Telegram: seller chat button, notification bell, in-shell shop, account tab parity, role dashboards, live chat, product-style template cards, floating cart, support bubble, checkout CTA. -**Verification:** Backend typecheck and tests passing, frontend TypeScript compilation clean. -**Linked docs updated:** [[Authentication Flow]], [[Chat Flow]], [[Telegram Mini App]], [[Marketplace API]] +**Why (from points audit):** /dashboard/points showed "·" instead of numbers and told Bronze users +they are at "the highest level". Root causes: (1) level boundary off-by-one — seeds define +inclusive maxPoints (Bronze 0–999) but PG used `>` and Mongo `<`, so exact-boundary users resolved +to no level; (2) `level_configs` table in Postgres was created but NEVER seeded → zero levels → +null next-level → "highest level" message. Fixes: inclusive comparisons in both repos + +self-seeding of the 5 default levels (برنز/نقره/طلا/پلاتین/الماس) in ensurePostgresLevelConfigSchema. +**Remaining from audit:** frontend points page swallows API errors (shows zeros instead of an +error state); integration ideas: award points on completed escrow, show points in Mini App. +**Verification:** tsc clean, 8 tests green. Admin verifies after deploy: /dashboard/points shows +real numbers and Bronze shows progress toward نقره. --- -### 2026-06-04 — backend@cd79460, frontend@baa3e52 — security remediation and payment features +### 2026-06-03 — frontend@131700b — compact floating cart/support FABs (v2.8.63) -**Commits:** backend `cd79460`, `7c6158d`, `bc5b6aa`, `1c51dd8`; frontend `baa3e52`, `2b99404`, `f9c4b0c`, `450625b`, `59b3a67`, `a133fc0`, `93199d4` -**Touched:** Security ownership checks (SEC-001), error message sanitization (SEC-029), PRD tasks remediation (SEC-001..037), security remediation PRD documentation; Telegram: direct-transfer checkout for non-web3, avatar URL fixes, email verify flow, theme/dark mode, settings/addresses in-shell, solar-style icons -**Why:** Complete security audit remediation: reconcile SEC-001 ownership checks with main's sameUser helper, stop leaking raw error messages (SEC-029), remediate P1+P3+P4 security tasks, add comprehensive security remediation PRD. Telegram Mini App: direct-transfer payment option, in-shell settings, addresses management, theme configuration, avatar upload, achievements display. -**Verification:** Security audit tests passing, error messages no longer expose sensitive data. -**Linked docs updated:** [[Security Audit - 2026-05-24]], [[Security Architecture]], [[Telegram Mini App]], [[Payment Flow - DePay & Web3]] +**Why:** User feedback — the two edge-docked FABs were too large. Compacted paddings, icons, +badge, and stack positions. +**Verification:** tsc clean. --- -### 2026-06-03 — backend@c501b2c, frontend@7fe236f — authentication and chat enhancements +### 2026-06-03 — frontend@d64e194 — Mini App points view + tap-to-verify email (v2.8.64) -**Commits:** backend `c501b2c`, `d4e53f4`, `941feee`, `58eb61c`, `4149fcf`, `c5d6490`, `91877ae`, `14d164c`, `8b8c1ae`, `9424395`, `378f8f6`, `14c231e`; frontend `7fe236f`, `7be7e2b`, `4079730`, `5d2113b`, `0888b30`, `8f23cca`, `1cc4212` -**Touched:** Pending-email change flow, referral signup bonus, shop settings, chat participant names, seller ratings, points level boundaries, uuid/legacy id tolerance, roles (guard), chat/notification fixes, user management, admin dashboard repair -**Why:** Authentication: pending-email change flow with referral signup bonus. Chat: populate participant names on Postgres path, canonicalize participant ids, revert canonicalization that broke membership. Shop: compute seller ratings from real published reviews, seller shop lookup tolerant of uuid/legacy id formats. Points: level boundary fixes, accept legacy 24-hex user ids. Roles: add guard user role. Admin: repair user management dashboard, surface backend error messages. -**Verification:** Backend typecheck and marketplace/chat tests passing, frontend TypeScript clean. -**Linked docs updated:** [[Authentication Flow]], [[Chat Flow]], [[User API]], [[Marketplace API]], [[Points API]] +**Why:** Buyer parity — امتیازات و رفرال now in-shell (TelegramPointsView: points cards, level +progress, referral code copy + stats; account-tab row). Email verification from the Mini App: +unverified badge is tappable → sends code → code-entry sheet → verifyProfileEmail → session +refresh. Reuses existing account.ts actions. +**Verification:** tsc + eslint clean. Test after deploy: account tab → امتیازات row; unverified +user → tap red badge → enter emailed code → badge turns green. --- -### 2026-06-03 — backend@a76b07b, frontend@d8763ac — scanner integration and request template fixes +### 2026-06-03 — backend@91877ae — points lookups accept legacy user ids (v2.8.63) -**Commits:** backend `a76b07b`, `fdf63d2`; frontend `d8763ac`, `42fe05e` -**Touched:** Scanner balance watch callbacks, direct-balance ERC-20 pay-in rail (Phase 1+2), request-template validation errors, maxUsage optional field -**Why:** Payment infrastructure: wire scanner balance watch callbacks for real-time balance monitoring, add direct-balance ERC-20 pay-in rail with Phase 1 and 2 implementation. Request templates: surface validation errors in expanded sections and list view, ensure maxUsage is truly optional to fix template creation issues. -**Verification:** Backend payment tests and typecheck passing, scanner integration tests passing. -**Linked docs updated:** [[Scanner Architecture]], [[Payment Flow - Scanner]], [[Scanner API]] +**Why:** Mini App points view (v2.8.64) failed: every user lookup in DrizzlePointsRepo compared +users.id (uuid) against the JWT legacy 24-hex id → uuid cast error → 500. All lookups now match +either format (id::text = $n OR legacy_object_id = $n). +**Verification:** tsc clean, tests green. Test after deploy: Mini App → حساب → امتیازات shows data. --- -### 2026-06-02 — backend@6724177, frontend@714dfbd — database and request handling improvements +### 2026-06-03 — frontend@HEAD — remove MainButton, in-tab CTA, sign-in retry (v2.8.65) -**Commits:** backend `6724177`, `f588d52`, `20435d1`, `8dead04`, `795f161`; frontend `714dfbd`, `6fe1328`, `d7a2a86` -**Touched:** Fresh-DB Postgres migrate + seed path, 0013/0014 migrations validation, seeds Postgres-capable, addresses table migration, address_type enum, admin user management, purchase request id/_id tolerance -**Why:** Database: make fresh-DB Postgres migrate and seed path correct for pg-only mode, validate 0013/0014 migrations for fresh drizzle-kit migrate, create addresses table migration and address_type enum, re-key address store by user_id. Requests: tolerate both id and _id in purchase request responses for Mongo-PG compatibility. Admin: make admin user management work end-to-end, unblock user creation and purchase requests for native-PG users. -**Verification:** Backend database migration tests passing, seed scripts working in pg-only mode. -**Linked docs updated:** [[Database Strategy - Mongo vs Postgres Assessment]], [[Postgres Runtime Cutover Status]], [[Address]] +**Why (user requests):** (1) native Telegram MainButton removed — wrong font, duplicated CTAs; +«+ درخواست جدید» button added inside the requests tab header instead. (2) Intermittent 503 on +Telegram sign-in (backend redeploy windows) — sign-in now retries transient 502/503/504 3x with +backoff and shows the Persian message. +**Queued (user, with screenshots):** cart −=remove at qty 1; seller-shop card redesign (+ icon, +template detail view, search/filter/sort); requests search + status-chip filters + sort; shop +search; seller chat button; referral signup points; dark mode. +**Verification:** tsc + eslint clean. --- -### 2026-06-02 — backend@515bea3, frontend@424ce1f — MongoDB removal and CI/CD updates +### 2026-06-03 — frontend v2.8.66–67 — seller chat, header bell, cart UX + review system audit -**Commits:** backend `515bea3`, `4949988`; frontend `424ce1f`, `e33444f` -**Touched:** dataCleanupService guard against MONGO_CONNECT_MODE=never, route admin user counts through postgres-capable stores -**Why:** MongoDB removal: guard dataCleanupService against MONGO_CONNECT_MODE=never to prevent Mongo connections when disabled, route admin user dependency counts through postgres-capable repository stores instead of direct Mongo model access. CI/CD: push to main builds dev-* image and deploys to Arcane dev env, restrict dev pipeline to manual trigger only. -**Verification:** Backend typecheck and repository tests passing, Mongo connectivity guards verified. -**Linked docs updated:** [[MongoDB Removal Handoff]], [[Postgres Runtime Cutover Status]], [[Backend Core Stack Decision Record - 2026-05-24]] +**v2.8.66:** seller rows in the Mini App shop get a chat icon (direct conversation with the +seller); header gets a notification bell opening the in-shell notifications list. +**v2.8.67 fixes:** seller chat gave "Validation failed" (backend expects ONE participant for +direct chats — current user auto-added; we sent two). Cart «−» at qty 1 now removes the item +(separate remove button dropped). Bell: borderless + red dot instead of count. +**Review/rating system audit completed (agent):** backend schema/API/store fully working +(reviews table, /api/marketplace/reviews). Broken: seller rating HARDCODED 4.5 in +DrizzleMarketplaceRepo.findSellersWithActiveRequestTemplates (never reads real reviews); +review form never sends purchaseRequestId (verified-buyer badge never set); Step 6 +post-delivery has NO review prompt; Mini App has no reviews. Full phased plan in agent output — +next work items. --- -### 2026-06-03 — backend@80a1e52, frontend@b2d8a32 — integration and payment features +### 2026-06-03 — backend v2.8.64–65 — real seller ratings + chat participant names -**Commits:** backend `80a1e52`, `dc6a35f`, `0a0d489`, `e8e8bc9`, `126c222`; frontend `b2d8a32`, `3c9459f`, `51157b0`, `d8763ac` -**Touched:** Merge integrate-main-into-development, correct duplicate version field, telegram user persistence on sign-in, marketplace template creation CHECK constraints, chat support welcome message, in-shell checkout, digital items skip address, stepper fixes -**Why:** Integration: merge main into development branch, correct duplicate version field and bump to v2.8.69. Authentication: persist telegram user updates on sign-in. Marketplace: fix template creation 500 errors from blank optional fields tripping CHECK constraints. Chat: attribute support welcome message to support user. Telegram: in-shell checkout without web hand-off, digital items skip address step, checkout stepper improvements. -**Verification:** Backend and frontend builds passing, integration tests clean. -**Linked docs updated:** [[Integration]], [[Marketplace API]], [[Telegram Mini App]] +**v2.8.64:** seller list ratings now aggregated from published reviews (was hardcoded 4.5); +exposes rating/ratingCount per seller. +**v2.8.65:** PG chats showed "Unknown User" — participants (JSONB ids) are now populated with +firstName/lastName/email/avatar from the users table (uuid OR legacy id matched), in +findByIdWithParticipants and findForUser. Parity with Mongo .populate(). +**In progress:** background agent fixing seeds for the PG database + creating complete mock shops +(categories in PG → fixes template-creation 500) — will be reviewed and shipped when it reports. +**Queued (user):** in-shell تنظیمات عمومی (profile editing) in Mini App; post-delivery review +prompt; template detail view + search/filter/sort in shop and requests; dark mode. + +--- + +### 2026-06-03 — backend v2.8.65–66 — PG seeds, mock shops, template-creation fix + +**Template-creation 500 root cause (agent):** categories table column conflict (Drizzle "order" +vs categoryStore order_num) + random legacy ids per seed → category ids served by the API were +unresolvable by createRequestTemplate. Fixed: categoryStore aligned to canonical "order" column +(with one-time order_num migration), deterministic category legacy ids, hardened categories +fallback (id::text match). Also fixed: the v2.8.64 rating aggregation queried Drizzle reviews +columns but the physical table (written by reviewStore) has different columns — rewritten +against the real columns (seller_user_id). +**New seed:** src/seeds/seedMockShops.ts — Persian categories, 5 sellers + shop settings, +templates with picsum product images, published reviews. npm scripts fixed (paths) + added +seed:levels / seed:templates / seed:mock-shops. +**⚠️ DEV ENV REQUIREMENT:** CATEGORY_STORE=postgres (and ideally REVIEW_STORE / +SHOP_SETTINGS_STORE = postgres) must be set in dev .env.local when REPO_MARKETPLACE=postgres — +docker-compose.dev.yml still defaults these to mongo. Without this, template creation keeps +failing. +**Run on dev after deploy:** docker exec npm run seed:categories && npm run +seed:mock-shops +**Verification:** tsc clean, 14 tests green (3 suites). + +--- + +### 2026-06-03 — frontend v2.8.68 — product-style template cards + in-shell template detail + +**Why (user reference design):** seller-shop template cards redesigned image-first (photo + +circular add-to-cart overlay + title/price); tapping opens a new in-shell template detail view +(image gallery with thumbnails/counter, product-type chip, price, description, specs, tags, +sticky add-to-cart/order). New view: telegram-template-detail-view.tsx; shell openTemplate +overlay. +**Dev env (user applied):** CATEGORY_STORE/REVIEW_STORE/SHOP_SETTINGS_STORE=postgres set; +backend v2.8.66 restarted healthy; 24 categories auto-seeded into PG. seed:mock-shops still to +be run by the user for demo data. +**Verification:** tsc + eslint clean. + +--- + +### 2026-06-03 — backend v2.8.67 — auto-seed mock shops on startup + +**Feature:** new env flag `SEED_MOCK_SHOPS_ON_START=true` runs seedMockShops() during backend +boot (after migrations/levels). Idempotent — existing rows are skipped — so the flag can stay +on permanently in dev. +**Action for dev env:** add `SEED_MOCK_SHOPS_ON_START=true` next to the other store flags and +restart the backend container; demo shops/templates/reviews appear automatically. +**Verification:** tsc clean. + +--- + +### 2026-06-03 — frontend v2.8.69 — web-app-parity shop cards + template detail gallery + +**Per user screenshots:** (1) shop cards — circular «+» add-to-cart moved OFF the photo, now +sits beside the title/price row; (2) template detail — web-style carousel: rounded image, +‹ n/m › arrow/counter pill, touch-swipe navigation, centered thumbnails; (3) removed «سفارش این +قالب» everywhere (product logic: buyer either creates a request or buys via cart) — add-to-cart +is the single full-width CTA in the detail sticky bar. +Files: telegram-seller-shop-view.tsx, telegram-template-detail-view.tsx. +**Verification:** tsc + eslint clean. + +--- + +### 2026-06-03 — frontend v2.8.70 — in-shell settings + addresses, central theme + dark mode + +**In-shell account screens (were opening the web dashboard):** +- تنظیمات عمومی → telegram-settings-view.tsx: name/email/bio edit form via + updateUserProfile; session refresh on save. +- آدرس‌های تحویل → telegram-addresses-view.tsx: full address CRUD (list / add / + edit / delete / set-primary) on the addresses API + use-telegram-addresses SWR hook. +- Account rows switched from href→onClick; new overlay states 'settings'/'addresses' + in the shell. (Passkey stays web — WebAuthn limitation.) + +**Theme overhaul — Mini App now uses the CENTRAL web-app theme:** +- constants.ts: TG_PALETTE_LIGHT / TG_PALETTE_DARK derived from + src/theme/theme-config.ts (primary green, grey ramp, status colors) instead of + the ad-hoc cream/saffron palette; font stack aligned with AMANEH_FONT + (IBM Plex Sans / Vazirmatn). +- telegram-shell-css.ts: emits both palettes; `.tg-shell--dark` flips every token — + components keep the same CSS-var names. +- use-telegram-theme hook: auto (follow Telegram colorScheme) | light | dark, + persisted in localStorage; syncs Telegram native header/background colors. +- Theme toggle row (auto/light/dark) added to the account tab. +- Hero headers (account / seller-shop / welcome banner / unlinked) use new + --header-bg/--header-fg/--header-fg-muted vars so they stay legible in both modes. +- Removed the inline themeParams background/text override in getTelegramShellStyle + that forced a navy shell under light components on dark-mode Telegram clients + (the bad bg/font the user reported). +**Verification:** tsc + eslint clean (one pre-existing no-constant-condition +warning in telegram-webapp.ts, untouched). + +--- + +### 2026-06-03 — frontend v2.8.71 — Mini App: solar icons, avatar upload, achievements, theme-toggle fix + +**Icons:** telegram-icons.tsx redrawn to mirror the dashboard's Solar "linear" +set (home-2 / document-add / chat-round-dots / user / shop-2 / wallet-money / +rounded arrows). Added sun, moon, themeAuto, camera, trophy, lock. +**Theme toggle now icon-based** (sun=light, moon=dark, half-disc=auto) instead +of text labels — per user request. +**BUGFIX — theme toggle didn't respond:** TelegramListRow rendered a *disabled* +