Full-codebase-audit 2026-05-30 outputs: - Audit report: 09 - Audits/Full Codebase Audit - 2026-05-30.md - 81 issue files ISSUE-055..135 (decisions + 1 skipped no-brainer). - Scanner docs from scratch (was zero): architecture, data model, API ref, payment flow, operations runbook + repo README. - Doc-sync updates across API reference, data models, flows, design system. - Secret Rotation Runbook (08 - Operations) for the exposed credentials. - Reusable workflow guide (07 - Development) + .claude/workflows/full-codebase-audit.js. Issues remain status:open intentionally — the code fixes are uncommitted-then-committed working-tree changes per repo and aren't "resolved" until merged/deployed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9.9 KiB
title, tags, aliases
| title | tags | aliases | |||||
|---|---|---|---|---|---|---|---|
| Data Model Overview |
|
|
Data Model Overview
This section documents every Mongoose model that backs the marketplace. The persistence layer lives in backend/src/models/ and is exported through a single barrel file at backend/src/models/index.ts. All models target MongoDB via Mongoose, lean on timestamps: true for createdAt / updatedAt, and follow a consistent pattern: one schema per file, an exported I<Name> TypeScript interface, and named exports for the compiled model.
[!note] Scope Twenty-two models are present in
backend/src/models/. The "File" concept exists only at the service layer (backend/src/services/file/) and is not persisted as its own Mongoose collection, so it is not listed below.[!note] Documentation freshness The 2026-05-24 audit note that marked
Dispute,BlogPost,Review,PointTransaction,LevelConfig, andShopSettingsas missing is now stale: schema files exist for those models. Newer operational models such as ConfigSetting, DerivedDestination, FundsLedgerEntry, and TrezorAccount should be expanded into dedicated model pages when the docs are next deepened.
Index of Models
- User — Core identity. Stores credentials, profile, preferences, referral data, points, and WebAuthn passkeys. Every other model that records "who did what" points back at a
User._id. Buyers, sellers, and admins all live in this collection, differentiated by aroleenum. - PurchaseRequest — The buyer-side document at the heart of the marketplace. Captures what a buyer wants, the budget, urgency, delivery preferences, and the full lifecycle status (
pending_payment→seller_paid). Aggregates SellerOffer references and tracks delivery codes. - SellerOffer — A seller's bid against a PurchaseRequest. Holds price, delivery ETA, attachments, and a small status machine (
pending/accepted/rejected/withdrawn). - Payment — Records monetary movement intent and state: buyer pay-in, seller release, and refund. The current primary provider path is Request Network plus in-house checkout, derived destinations, funds ledger entries, and Transaction Safety Provider metadata.
- Chat — Conversation container with embedded messages, participants, unread counters, and reactions. Used for direct buyer-seller chats, group chats, and support tickets. Can be linked to a PurchaseRequest or SellerOffer.
- Notification — Per-user notification with category, type, and 90-day TTL for automatic cleanup. References any related entity by stringified id.
- RequestTemplate — A seller-authored, sharable template that pre-fills a PurchaseRequest. Carries a public shareable link, usage counter, and an optional default proposal.
- Dispute — Buyer-raised complaint tied to a PurchaseRequest. Captures evidence uploads, a timeline of admin actions, deadlines, and a structured resolution.
- BlogPost — Editorial content: title, slug, rich content, media, SEO metadata, view/like counters, and a draft/published/archived workflow.
- Address — User shipping address book entry. Enforces a single primary address per user via a pre-save hook.
- Category — Hierarchical product/service taxonomy referenced by PurchaseRequest and RequestTemplate. Supports parent/child via
parentIdand bilingualname/nameEn. - Review — Polymorphic 1-5 star review against either a seller or a RequestTemplate (
subjectTypediscriminator). One review per reviewer per subject (compound unique index). - PointTransaction — Ledger of point grants and spends per user. Sources include purchase, referral, bonus, admin grant, and redemption.
- LevelConfig — Static configuration of loyalty tiers (level number, point thresholds, benefits, icon, color). Driven by admins; consumed by the User.points.level field.
- ShopSettings — One-to-one storefront branding for a seller: name, description, avatar, cover image, review toggles, and social links.
- TempVerification — Short-lived signup record that holds candidate user data and a verification code. Auto-purges via TTL when
emailVerificationCodeExpirespasses. - TelegramLink — Permanent auditable association between a Telegram user ID and an Amanat User. Stores Telegram profile metadata, link source (
miniapp/bot/login_widget), status (active/blocked), and last-seen timestamp. One per Telegram user (unique on bothuserIdandtelegramUserId). - TelegramSession — Short-lived Telegram Mini App session token issued when
initDatais verified. Carries theinitDataFingerprintfor replay protection and auto-expires via a MongoDB TTL index onexpiresAt. - ConfigSetting — Runtime configuration persisted in MongoDB for operational knobs that need an admin surface rather than a deploy.
- DerivedDestination — Per-payment derived wallet destination records used to reduce address reuse and reconcile on-chain pay-ins.
- FundsLedgerEntry — Immutable accounting ledger rows for pay-in, hold, release, refund, fee, adjustment, and reversal events.
- TrezorAccount — Hardware-wallet/safekeeping account metadata for custody operations and staged signer hardening.
- ConfigSettingHistory — Immutable audit trail of numeric runtime-config changes. Currently used for per-chain confirmation threshold change events, keyed as
confirmation_threshold:<chainId>. Added in commit27fb15a.
Relationship Diagram
erDiagram
USER ||--o{ PURCHASE_REQUEST : "creates as buyer"
USER ||--o{ SELLER_OFFER : "submits as seller"
USER ||--o{ ADDRESS : "owns"
USER ||--o{ NOTIFICATION : "receives"
USER ||--o{ POINT_TRANSACTION : "earns/spends"
USER ||--o{ REQUEST_TEMPLATE : "authors as seller"
USER ||--o| SHOP_SETTINGS : "configures"
USER ||--o{ BLOG_POST : "publishes"
USER ||--o{ REVIEW : "writes as reviewer"
USER ||--o{ DISPUTE : "raises as buyer"
USER ||--o{ USER : "referred by"
USER ||--o{ TREZOR_ACCOUNT : "controls custody account"
PURCHASE_REQUEST }o--|| CATEGORY : "belongs to"
PURCHASE_REQUEST ||--o{ SELLER_OFFER : "receives"
PURCHASE_REQUEST ||--o{ PAYMENT : "settled by"
PURCHASE_REQUEST ||--o| CHAT : "discussed in"
PURCHASE_REQUEST ||--o{ DISPUTE : "may trigger"
PURCHASE_REQUEST ||--o| REVIEW : "rated by buyer"
SELLER_OFFER ||--o| PAYMENT : "funds"
SELLER_OFFER }o--|| PURCHASE_REQUEST : "responds to"
PAYMENT }o--|| USER : "buyer"
PAYMENT }o--|| USER : "seller"
PAYMENT ||--o{ FUNDS_LEDGER_ENTRY : "accounted by"
PAYMENT ||--o| DERIVED_DESTINATION : "collects into"
CHAT }o--o{ USER : "participants"
CHAT ||--o{ DISPUTE : "support channel"
REQUEST_TEMPLATE }o--|| CATEGORY : "belongs to"
REQUEST_TEMPLATE ||--o{ REVIEW : "rated as subject"
CATEGORY ||--o{ CATEGORY : "parent of"
POINT_TRANSACTION }o--|| USER : "owner"
LEVEL_CONFIG ||..|| USER : "level lookup"
TEMP_VERIFICATION ||..|| USER : "promoted to"
TELEGRAM_LINK }o--|| USER : "links identity"
TELEGRAM_SESSION }o--o| USER : "session for"
TELEGRAM_SESSION }o--|| TELEGRAM_LINK : "matches"
Conventions Across All Models
[!note] Shared schema patterns
- Timestamps: every model declares
{ timestamps: true }, socreatedAtandupdatedAtare always present.- ObjectId references: foreign keys use
Schema.Types.ObjectIdwith an explicitref(e.g.ref: 'User'). The two exceptions are Notification and Payment which use string-typed orMixedidentifiers in places to support template-flow payments.- Soft delete: deletion is modelled as a
statusflag (e.g.User.status = 'deleted',BlogPost.status = 'archived') rather than physical removal.- TTL indexes: short-lived collections (Notification, TempVerification) use
{ expireAfterSeconds: ... }so MongoDB does the cleanup.- toJSON sanitisation: User overrides
toJSONto strip credentials, refresh tokens, and verification codes before serialisation.
[!warning] Index discipline Several schemas leave a comment noting that
unique: truealready creates an index — addingschema.index({ field: 1 })on top would produce a duplicate-index warning at startup. When introducing new indexes, search forunique: truefirst.
Lifecycle View
The dominant happy-path flow exercises five collections in order:
- A buyer (
User) creates aPurchaseRequestwithstatus: 'pending'. - Sellers (other
Users) attachSellerOfferdocuments; the request transitions throughreceived_offers→in_negotiationas the parties chat in aChat. - The buyer accepts an offer; a
Paymentis opened against the Request Network provider and, once verified by webhook/reconciliation and safety checks, advances to a funded escrow state. - The seller marks the request
delivery→delivered; the buyer confirms with the 6-digitdeliveryCodeand the request becomescompleted. - The escrow
Paymentflips toreleasedafter a ledger-gated custody transfer instruction. Optionally the buyer writes aReviewand earns aPointTransaction.
If anything goes sideways, the buyer can open a Dispute, which freezes release until an admin resolves it (refund, replacement, compensation, or no-action).
How to Navigate
Each model has its own note in this folder. Cross-references use [[wikilinks]] so backlinks work in Obsidian's graph view. Schemas are documented at field-level granularity — every field is listed with its type, default, validation, and indexing decisions. Where a model carries a meaningful state machine, a Mermaid stateDiagram-v2 accompanies the schema table.
[!note] Source of truth The information below is mirrored from the TypeScript schema definitions. If a field listed here disagrees with the code, the code wins — please update the note. All source citations use the form
backend/src/models/<File>.ts:<line>.