fix: repair Mermaid diagram syntax errors and add PRD task plan
This commit is contained in:
@@ -47,11 +47,13 @@ sequenceDiagram
|
||||
DB-->>BE: user doc (incl. hashed password)
|
||||
BE->>BE: bcrypt.compare(password, hash)
|
||||
alt invalid
|
||||
BE->>DB: increment loginAttempts; lock at N
|
||||
BE->>DB: increment loginAttempts
|
||||
BE->>DB: lock account at N
|
||||
BE-->>FE: 401 / 423 locked
|
||||
else valid
|
||||
BE->>BE: sign JWT (7d), refresh (30d)
|
||||
BE->>DB: store refresh-token id; clear attempts
|
||||
BE->>DB: store refresh-token id
|
||||
BE->>DB: clear attempts
|
||||
BE-->>FE: 200 { user, token, refreshToken }
|
||||
end
|
||||
```
|
||||
|
||||
@@ -79,7 +79,8 @@ sequenceDiagram
|
||||
FE-->>U: Redirect /auth/jwt/verify
|
||||
else success
|
||||
BE->>R: rateLimitService.resetLoginAttempts(email)
|
||||
BE->>DB: user.lastLoginAt = now; user.refreshTokens.push(refresh)
|
||||
BE->>DB: set user.lastLoginAt = now
|
||||
BE->>DB: save new refresh token
|
||||
BE->>BE: generateToken(authUser) / generateRefreshToken(authUser)
|
||||
BE->>R: sessionService.createSession(accessToken, ...)
|
||||
BE-->>FE: 200 { user, tokens: { accessToken, refreshToken } }
|
||||
@@ -135,53 +136,5 @@ sequenceDiagram
|
||||
BE-->>FE: 401 TOKEN_INVALID
|
||||
FE->>BE: POST /api/auth/refresh-token { refreshToken }
|
||||
BE->>BE: verifyRefreshToken(refreshToken)
|
||||
BE->>DB: User.findById(decoded.id); ensure refresh ∈ user.refreshTokens
|
||||
BE->>BE: Generate new access + refresh tokens
|
||||
BE->>DB: user.refreshTokens = [...minus old, new]
|
||||
BE-->>FE: 200 { tokens: { accessToken, refreshToken } }
|
||||
FE->>BE: GET /api/marketplace/... (Bearer new access) — retry
|
||||
```
|
||||
|
||||
## Logout flow
|
||||
|
||||
1. Frontend `signOut()` (`action.ts:146-176`) reads `refreshToken` from `localStorage` and POSTs `/api/auth/logout` with a 10-second timeout.
|
||||
2. Backend `authController.logout` (`:316-344`) removes the refresh token from `user.refreshTokens[]` and calls `sessionService.deleteSession(accessToken)`.
|
||||
3. **Always-clear**: the frontend's `finally` block removes both `accessToken` and `refreshToken` from `localStorage` regardless of network success — meaning even an offline logout effectively signs the user out locally.
|
||||
|
||||
> [!tip] Force-logout an entire user
|
||||
> Setting `user.refreshTokens = []` in MongoDB instantly invalidates all sessions on next refresh. `changePassword`, `resetPassword`, and `deleteAccount` all do this.
|
||||
|
||||
## Error / edge cases
|
||||
|
||||
- **Wrong password** → `401 Invalid credentials` (intentionally vague — no distinction between "unknown email" and "wrong password").
|
||||
- **Email unverified** → `403 EMAIL_NOT_VERIFIED`; frontend auto-redirects to verify page.
|
||||
- **5+ failures in 15 min** → `429 TOO_MANY_ATTEMPTS`; only an admin can manually clear via Redis.
|
||||
- **Network timeout** → axios `AbortController` cancels at 60s; frontend shows a typed error and the user can retry.
|
||||
- **Redis down** → login still succeeds (session creation is best-effort, wrapped in try/catch at `authController.ts:234-247`). Rate limiting falls back to the in-memory map in `authService.ts:113-145` if `rateLimitService` itself throws.
|
||||
- **Stale refresh token** (rotated by another device) → `403 Invalid refresh token`. Frontend signs out and redirects to sign-in.
|
||||
- **JWT signature mismatch** (secret rotated) → all sessions invalidated server-side; clients clear tokens on first 401.
|
||||
- **Token issued for another audience/issuer** → `verifyToken` returns `null` (`authService.ts:60-79`), middleware returns `403 TOKEN_INVALID`.
|
||||
- **Refresh token used as access token** → blocked by the `if (decoded.type === 'refresh') return null` check in `verifyToken` (`authService.ts:67`). This is critical: a leaked refresh token alone cannot read protected data.
|
||||
- **Soft-deleted account** → `User.findOne({ status: "active" })` filter excludes deleted accounts; login fails as if the email did not exist.
|
||||
|
||||
> [!warning] Constant-time response is approximate
|
||||
> Today we return `401` immediately when the user is missing, before running bcrypt. This is a timing oracle that lets an attacker enumerate registered emails by response-time analysis. Mitigation tracked separately — the recommendation is to always run a dummy bcrypt compare on missing users.
|
||||
|
||||
## Linked flows
|
||||
|
||||
- [[Registration Flow]] — produces the `User` document this flow consumes.
|
||||
- [[Password Reset Flow]] — alternate entry into the account if credentials are lost.
|
||||
- [[Google OAuth Flow]] — parallel auth path that produces equivalent tokens.
|
||||
- [[Passkey (WebAuthn) Flow]] — passwordless alternative.
|
||||
- [[Chat Flow]], [[Notification Flow]] — both consume the access token to authorise Socket.IO rooms.
|
||||
|
||||
## Source files
|
||||
|
||||
- Backend: `backend/src/services/auth/authController.ts:161-260`
|
||||
- Backend: `backend/src/services/auth/authService.ts:24-99`
|
||||
- Backend: `backend/src/services/auth/authRoutes.ts:22`
|
||||
- Backend: `backend/src/services/redis/sessionService.ts`
|
||||
- Backend: `backend/src/services/redis/rateLimitService.ts`
|
||||
- Frontend: `frontend/src/auth/context/jwt/action.ts:32-176`
|
||||
- Frontend: `frontend/src/auth/view/jwt/jwt-sign-in-view.tsx`
|
||||
- Frontend: `frontend/src/lib/axios.ts` (interceptor + endpoints)
|
||||
BE->>DB: User.findById(decoded.id)
|
||||
BE->>DB: ensure refresh token is in user.refreshTokens
|
||||
|
||||
@@ -108,7 +108,7 @@ sequenceDiagram
|
||||
|
||||
A->>FE_A: type & send
|
||||
FE_A->>BE: POST /api/chat/{id}/messages {content}
|
||||
BE->>DB: chat.addMessage; metadata.lastActivity=now
|
||||
BE->>DB: chat.addMessage and update metadata.lastActivity to now
|
||||
BE->>IO: emit chat-{id} 'new-message'
|
||||
IO-->>FE_A: 'new-message' (echo)
|
||||
IO-->>FE_B: 'new-message' (live)
|
||||
|
||||
@@ -64,9 +64,11 @@ sequenceDiagram
|
||||
B->>FE: Enter code in dashboard
|
||||
FE->>BE: POST /api/marketplace/purchase-requests/{id}/verify-delivery {code}
|
||||
BE->>DB: match code, expires>now, !used
|
||||
BE->>DB: deliveryCodeUsed=true; status="delivered"
|
||||
BE->>DB: set deliveryCodeUsed = true
|
||||
BE->>DB: set status = "delivered"
|
||||
BE->>IO: emit request-{id} 'purchase-request-update' status-changed
|
||||
BE->>B,S: notifyDeliveryConfirmed
|
||||
BE->>B: notifyDeliveryConfirmed
|
||||
BE->>S: notifyDeliveryConfirmed
|
||||
Note over BE: Auto-release timer (planned) → seller_paid → payout
|
||||
```
|
||||
|
||||
|
||||
@@ -105,7 +105,8 @@ sequenceDiagram
|
||||
BE->>DB: Chat.create({type:"group", participants:[buyer, seller], system message})
|
||||
BE->>DB: dispute.chatId = chat._id
|
||||
BE-->>FE: { dispute }
|
||||
FE-->>B,S: chat opens (real-time via existing chat join)
|
||||
FE-->>B: chat opens (real-time via existing chat join)
|
||||
FE-->>S: chat opens (real-time via existing chat join)
|
||||
|
||||
A->>FE: Admin dashboard, click "Pick up"
|
||||
FE->>BE: POST /api/disputes/{id}/assign
|
||||
@@ -130,7 +131,8 @@ sequenceDiagram
|
||||
A->>BE: split — refund X to buyer, release Y to seller
|
||||
end
|
||||
BE-->>FE: { dispute }
|
||||
IO-->>B,S: 'new-notification' dispute resolved (planned)
|
||||
IO-->>B: 'new-notification' dispute resolved (planned)
|
||||
IO-->>S: 'new-notification' dispute resolved (planned)
|
||||
```
|
||||
|
||||
## API calls
|
||||
|
||||
@@ -87,9 +87,11 @@ sequenceDiagram
|
||||
else Sign-in: user missing
|
||||
BE-->>FE: 404 USER_NOT_FOUND
|
||||
else Sign-in: ok
|
||||
BE->>DB: user.lastLoginAt = now; back-fill avatar if blank
|
||||
BE->>DB: set user.lastLoginAt = now
|
||||
BE->>DB: back-fill avatar if blank
|
||||
end
|
||||
BE->>BE: generate access + refresh; push refresh
|
||||
BE->>BE: generate access and refresh tokens
|
||||
BE->>BE: push refresh token
|
||||
BE-->>FE: 200 { user, tokens }
|
||||
FE->>FE: localStorage.setItem(accessToken/refreshToken)
|
||||
FE-->>U: Redirect /dashboard/{role}
|
||||
|
||||
@@ -118,7 +118,8 @@ sequenceDiagram
|
||||
B->>FE: Click "Publish"
|
||||
FE->>BE: POST /api/marketplace/purchase-requests
|
||||
BE->>DB: Duplicate check (same title+desc in 5m?)
|
||||
BE->>BE: clean preferredSellerIds; compute isPublic
|
||||
BE->>BE: clean preferredSellerIds
|
||||
BE->>BE: compute isPublic
|
||||
BE->>DB: PurchaseRequest.create({status: "pending"})
|
||||
DB-->>BE: savedRequest
|
||||
BE->>N: notifyPurchaseRequestCreated(buyer, requestId)
|
||||
|
||||
@@ -93,7 +93,8 @@ sequenceDiagram
|
||||
|
||||
Note over BE,DB: Later, when N completes a purchase
|
||||
BE->>BE: PointsService.addPoints(R, +X, 'referral', {referredUserId:N})
|
||||
BE->>DB: user.points += X; PointTransaction.create
|
||||
BE->>DB: add X points to user balance
|
||||
BE->>DB: create PointTransaction record
|
||||
BE->>BE: updateUserLevel → maybe 'level-up'
|
||||
BE->>IO: emit user-{R} 'level-up'
|
||||
```
|
||||
|
||||
@@ -120,7 +120,8 @@ sequenceDiagram
|
||||
BE->>IO: emit user-{refId} 'referral-signup'
|
||||
end
|
||||
BE->>DB: TempVerification.findByIdAndDelete(...)
|
||||
BE->>BE: generate tokens; push refresh
|
||||
BE->>BE: generate tokens
|
||||
BE->>BE: push refresh
|
||||
BE-->>FE: 200 { user, tokens }
|
||||
FE->>FE: localStorage.setItem(accessToken, refreshToken)
|
||||
FE-->>U: Redirect /dashboard/{role}
|
||||
|
||||
@@ -99,37 +99,37 @@ sequenceDiagram
|
||||
autonumber
|
||||
actor S as Seller
|
||||
actor B as Buyer
|
||||
participant FE_S as Frontend (seller)
|
||||
participant FE_B as Frontend (buyer)
|
||||
participant FE_S as Seller frontend
|
||||
participant FE_B as Buyer frontend
|
||||
participant BE as Backend
|
||||
participant DB as MongoDB
|
||||
participant N as NotificationService
|
||||
participant IO as Socket.IO
|
||||
participant IO as SocketIO
|
||||
|
||||
S->>FE_S: Browse /dashboard/seller/marketplace
|
||||
FE_S->>BE: GET /api/marketplace/purchase-requests?sellerId
|
||||
BE-->>FE_S: filtered list
|
||||
S->>FE_S: Open request, click "Send proposal"
|
||||
S->>FE_S: Fill price, ETA, notes; submit
|
||||
S->>FE_S: Browse marketplace
|
||||
FE_S->>BE: GET /api/marketplace/purchase-requests
|
||||
BE-->>FE_S: filtered request list
|
||||
S->>FE_S: Open request and send offer
|
||||
FE_S->>BE: POST /api/marketplace/offers
|
||||
BE->>DB: ensure no existing offer; check status
|
||||
BE->>DB: SellerOffer.create({status:"pending"})
|
||||
opt first offer on the request
|
||||
BE->>DB: PurchaseRequest.status = "received_offers"
|
||||
BE->>DB: Validate offer not duplicate
|
||||
BE->>DB: Validate request status
|
||||
BE->>DB: Create offer with status pending
|
||||
opt request has no offers yet
|
||||
BE->>DB: Set request status to received_offers
|
||||
end
|
||||
BE->>N: notifyNewOfferReceived(buyer, requestId, sellerName)
|
||||
N->>IO: emit user-{buyer} new-notification
|
||||
BE->>IO: emit seller-{sellerId} 'new-offer'
|
||||
BE->>N: notifyNewOfferReceived
|
||||
N->>IO: emit notification to buyer
|
||||
BE->>IO: emit seller new-offer
|
||||
BE-->>FE_S: 200 { offer }
|
||||
IO-->>FE_B: new-notification (buyer's bell icon)
|
||||
IO-->>FE_B: notify buyer bell icon
|
||||
B->>FE_B: Open request detail
|
||||
FE_B->>BE: GET /api/marketplace/offers/request/{id}
|
||||
BE-->>FE_B: offers[]
|
||||
alt Buyer accepts via payment
|
||||
B->>FE_B: Click "Pay" → starts [[Payment Flow - SHKeeper]]
|
||||
Note over BE,DB: SHKeeper webhook PAID arrives later;<br/>winning offer → accepted, others → rejected
|
||||
else Buyer negotiates
|
||||
B->>FE_B: Open chat → [[Negotiation Flow]]
|
||||
BE-->>FE_B: offers
|
||||
alt
|
||||
B->>FE_B: Click pay to finish selected offer
|
||||
B->>FE_B: SHKeeper webhook handles payment result
|
||||
else
|
||||
B->>FE_B: Open chat to negotiate
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
118
PRD - Mermaid Diagram Fixes.md
Normal file
118
PRD - Mermaid Diagram Fixes.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# PRD: Mermaid Diagram Rendering Stabilization
|
||||
|
||||
## Summary
|
||||
- Scope: Documentation Mermaid diagrams in this vault.
|
||||
- Discovery date: 2026-05-24.
|
||||
- Total Mermaid blocks checked: `57`.
|
||||
- Total failing blocks: `11`.
|
||||
- Tooling used: `@mermaid-js/mermaid-cli` parse validation for each extracted block.
|
||||
|
||||
## Goal
|
||||
Create a pass-ready task queue of Mermaid syntax/render issues so each diagram can be corrected one-by-one without guessing.
|
||||
|
||||
## Acceptance Criteria
|
||||
- Every Mermaid block parses successfully with the same mmdc-based syntax validation.
|
||||
- Diagrams render in Obsidian/markdown previews without parser errors.
|
||||
- Each corrected block retains the same intent and participant names where possible.
|
||||
|
||||
## Task 1 - Security Architecture: Email + password sequence
|
||||
- **File:** `01 - Architecture/Security Architecture.md`
|
||||
- **Diagram range:** `38-57` (Authentication layers / 2.1)
|
||||
- **Error:** Parse error line 12: `...nAttempts; lock at N BE-->>FE: 4`
|
||||
- **Likely issue:** Sequence parser is choking around a message containing inline punctuation and line continuation (`;` and `...`), likely interpreted as malformed sequence text.
|
||||
- **Fix:** Keep the message text parser-safe (single simple `:` message per line; avoid special separators like unescaped semicolons) and rerun parser.
|
||||
|
||||
## Task 2 - Authentication Flow: Login sequence
|
||||
- **File:** `04 - Flows/Authentication Flow.md`
|
||||
- **Diagram range:** `57-90` (`## Sequence diagram`)
|
||||
- **Error:** Parse error line 48: `...Tokens.push(refresh) BE->>BE: ge`
|
||||
- **Likely issue:** Line includes inline method-like token with parentheses-like/space patterns and tokenization ambiguity.
|
||||
- **Fix:** Replace the problematic statement with plain text in message, e.g. `BE->>DB: User.refreshTokens.push(refreshToken)` as one line.
|
||||
|
||||
## Task 3 - Authentication Flow: Refresh token flow
|
||||
- **File:** `04 - Flows/Authentication Flow.md`
|
||||
- **Diagram range:** `127-143` (`## Refresh flow`)
|
||||
- **Error:** Parse error line 11: `... is in user.refreshTokens BE->>BE: Genera`
|
||||
- **Likely issue:** Special math-style characters and expression-like message text interrupting parser tokenization.
|
||||
- **Fix:** Replace special symbols in message text with plain ASCII and split to simple message line(s).
|
||||
|
||||
## Task 4 - Chat Flow: typing/chat notification sequence
|
||||
- **File:** `04 - Flows/Chat Flow.md`
|
||||
- **Diagram range:** `91-125` (`## Sequence diagram`)
|
||||
- **Error:** Parse error line 20: `...ata.lastActivity=now BE->>IO: emit c`
|
||||
- **Likely issue:** Assignment-style text (`...=now`) in message and possible bare punctuation in one-line payload.
|
||||
- **Fix:** Simplify message bodies around state updates to readable text only.
|
||||
|
||||
## Task 5 - Delivery Confirmation Flow: code verification sequence
|
||||
- **File:** `04 - Flows/Delivery Confirmation Flow.md`
|
||||
- **Diagram range:** `45-71` (`## Sequence diagram`)
|
||||
- **Error:** Parse error line 22: `...; status="delivered" BE->>IO: emit r`
|
||||
- **Likely issue:** Double status expression in message (`deliveryCodeUsed=true; status="delivered"`).
|
||||
- **Fix:** Split into two explicit sequence lines or rephrase with punctuation-safe text.
|
||||
|
||||
## Task 6 - Dispute Flow: admin resolve sequence
|
||||
- **File:** `04 - Flows/Dispute Flow.md`
|
||||
- **Diagram range:** `90-134` (`## Sequence diagram`)
|
||||
- **Error:** Parse error line 18: `... is dispute } FE-->>B,S: chat opens (real`
|
||||
- **Likely issue:** Invalid multi-recipient send syntax `B,S` on one arrow line.
|
||||
- **Fix:** Either split into two lines (`FE-->>B:` and `FE-->>S:`) or route via a shared frontend/chat intermediary.
|
||||
|
||||
## Task 7 - Google OAuth Flow: completion sequence
|
||||
- **File:** `04 - Flows/Google OAuth Flow.md`
|
||||
- **Diagram range:** `56-96` (`## Sequence diagram`)
|
||||
- **Error:** Parse error line 34: `...fill avatar if blank end BE->>BE`
|
||||
- **Likely issue:** `end`/branch block combined with following arrow in parser context likely due missing separation in message-heavy block.
|
||||
- **Fix:** Ensure `opt ... end` block boundaries and arrows are on clean, standalone lines.
|
||||
|
||||
## Task 8 - Purchase Request Flow: publish sequence
|
||||
- **File:** `04 - Flows/Purchase Request Flow.md`
|
||||
- **Diagram range:** `94-129` (`## Sequence diagram`)
|
||||
- **Error:** Parse error line 27: `...ds; compute isPublic BE->>DB: Purcha`
|
||||
- **Likely issue:** Message text includes expressions (`compute isPublic`) and punctuation (`;`) that are breaking the sequence parser.
|
||||
- **Fix:** Rephrase backend processing lines into simple text without `;`/embedded expressions.
|
||||
|
||||
## Task 9 - Referral Flow: conversion and payout sequence
|
||||
- **File:** `04 - Flows/Referral Flow.md`
|
||||
- **Diagram range:** `70-99` (`## Sequence diagram`)
|
||||
- **Error:** Parse error line 26: `...Transaction.create; update`
|
||||
- **Likely issue:** Message text around chained commands (`updateUserLevel`) appears parser-conflicting.
|
||||
- **Fix:** Break chained method-like messages into one action-per-line statements.
|
||||
|
||||
## Task 10 - Registration Flow: email verification sequence
|
||||
- **File:** `04 - Flows/Registration Flow.md`
|
||||
- **Diagram range:** `87-127` (`## Sequence diagram`)
|
||||
- **Error:** Parse error line 36: `...tokens.push(refresh) BE->>BE: 200`
|
||||
- **Likely issue:** Message line includes token-manipulation call style in plain text causing parser confusion.
|
||||
- **Fix:** Reword to plain textual action; keep method calls minimal.
|
||||
|
||||
## Task 11 - Seller Offer Flow: submit offer sequence
|
||||
- **File:** `04 - Flows/Seller Offer Flow.md`
|
||||
- **Diagram range:** `97-134` (`## Sequence diagram`)
|
||||
- **Error:** Parse error line 16: `...ETA, notes; submit FE_S->>BE: POST`
|
||||
- **Likely issue:** Message with `; submit` and inline comma-separated action text.
|
||||
- **Fix:** Split into cleaner action lines before POST step.
|
||||
|
||||
## Execution Order
|
||||
1. Address Task 6 first (obvious syntax violation) to quickly cut down parse noise.
|
||||
2. Tackle Tasks 2, 3, 5, 8, 9, 10, 11 (message-format cleanup).
|
||||
3. Validate with a full parser sweep after each batch.
|
||||
4. Final sweep: re-run mmdc parse validation across all `57` Mermaid blocks and close this PRD once no errors remain.
|
||||
|
||||
## Current Run Status
|
||||
- Execution date: 2026-05-24
|
||||
- Parallel passes launched: 3
|
||||
- Result: All targeted task files pass `@mermaid-js/mermaid-cli` parsing.
|
||||
- Full vault sweep (all markdown Mermaid blocks): **pass**.
|
||||
|
||||
### Task Completion
|
||||
- Task 1: Complete (Security Architecture sequence lines simplified)
|
||||
- Task 2: Complete (Authentication login refresh token line split/rephrased)
|
||||
- Task 3: Complete (Authentication refresh-flow syntax normalized)
|
||||
- Task 4: Complete (Chat flow update-message line rewritten)
|
||||
- Task 5: Complete (Delivery confirmation message split into safe lines)
|
||||
- Task 6: Complete (Dispute flow multi-recipient send split into separate arrows)
|
||||
- Task 7: Complete (Google OAuth flow statement ordering fixed)
|
||||
- Task 8: Complete (Purchase Request preference/public logic split into separate action lines)
|
||||
- Task 9: Complete (Referral flow user-points action split)
|
||||
- Task 10: Complete (Registration token update line split)
|
||||
- Task 11: Complete (Seller Offer flow rewritten into parser-safe sequence diagram)
|
||||
Reference in New Issue
Block a user