Files
nick-doc/02 - Data Models/User.md
Siavash Sameni 7a616744f4 docs: complete code-reality alignment for remaining docs + reconcile issue set
Remaining docs updated to match code (the docs that the first pass had not covered):
- Flows: Chat, Referral, Rating, Registration, Google OAuth, Negotiation, Payout,
  Trezor Safekeeping — corrected endpoints, socket events, status enums, auth gaps
- API Reference: User API, Trezor API — admin route prefix/verb/status corrections,
  added undocumented endpoints (ton-proof challenge, profile email verify,
  GET /trezor/account, POST /trezor/verify-operation)
- Data Models: Chat, Notification, Payment, PointTransaction, User — corrected
  enums (PaymentProvider, escrowState, PointTransaction.type, User.status),
  90-day notification TTL, soft-delete semantics, wallet fields

Trezor "zero frontend" finding (audit C31/C32) corrected as STALE:
- Verified current code HAS a full frontend Trezor implementation (admin/trezor
  page, TrezorSettingsView, trezorConnector via @trezor/connect-web,
  TrezorSignDialog, actions/trezor.ts building the {message,signature} object)
- Fixed Trezor Safekeeping Flow doc (removed false "no frontend" warnings)
- Reclassified ISSUE-012 as invalid/superseded with explanation

Issue set reconciled to a single canonical numbering (ISSUE-001..054):
- Adopted the comprehensive 51-issue set (long-slug, fully indexed)
- Removed 35 superseded short-slug duplicates from the first pass
- Removed a duplicate ISSUE-046 file
- Added 3 issues the 51-set lacked: ISSUE-052 (completed-not-counted-in-stats),
  ISSUE-053 (axios 401-only interceptor), ISSUE-054 (rate limiter counts all attempts)
- Regenerated Issues Index: 53 open (14 critical, 39 major) + 1 invalid

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 15:15:02 +04:00

10 KiB

title, tags, aliases
title tags aliases
User
data-model
mongoose
User Model
IUser
Account

User

Last updated: 2026-05-29 — aligned with code (see Doc vs Code Audit Report)

The core identity document for every actor in the marketplace: buyers, sellers, and admins. Stores credentials (password + WebAuthn passkeys), profile/preference data, referral bookkeeping, point balances, and a soft-delete status flag. Almost every other model carries an ObjectId reference back to User, so this collection is the relational hub of the system.

[!note] Source backend/src/models/User.ts:70 — schema definition backend/src/models/User.ts:257 — model export

[!note] Email change re-verification When a profile update (PUT /api/user/profile, userController.updateUserProfile) changes email to a new value, the controller sets isEmailVerified = false, generates a 6-digit emailVerificationCode (valid 15 minutes), stores it on emailVerificationCode / emailVerificationCodeExpires, and emails the code to the new address. The user must then confirm via POST /api/user/profile/email/verify (or request a new code with POST /api/user/profile/email/resend-verification).

[!note] Wallet ownership proof PATCH /api/user/wallet-address accepts both EVM and TON wallets. EVM addresses require an EIP-191 signature (ethers.verifyMessage); TON addresses are format-validated and may include an optional TonProof. A successful proof sets profile.walletProofVerified = true and profile.walletProofTimestamp.

Schema

Field Type Required Default Validation Index Description
email String no lowercase, trim unique, sparse Primary email login identifier. Nullable for Telegram-only accounts.
password String no minlength 6 Hashed password. Optional to support passkey-only, Google, and Telegram accounts.
firstName String no "کاربر" trim Persian default ("user").
lastName String no "جدید" trim Persian default ("new").
role String yes "buyer" enum: admin / buyer / seller yes Authorisation tier.
isEmailVerified Boolean no false Set to true after the email verification code is consumed. ⚠️ Changing the email via PUT /api/user/profile resets this to false and dispatches a fresh 6-digit verification code to the new address (see Email verification note below).
authProvider String yes "email" enum: email / google / telegram yes Provider used to create the account. Existing email/password accounts remain email; Telegram-only users are telegram.
telegramVerified Boolean no false Set when Telegram identity has been signature-verified and linked through TelegramLink.
emailVerificationToken String no Legacy token-based email verification.
emailVerificationCode String no OTP code for email verification.
emailVerificationCodeExpires Date no Expiry for emailVerificationCode.
passwordResetToken String no Token for reset link flow.
passwordResetExpires Date no Expiry of passwordResetToken.
passwordResetCode String no OTP reset code.
passwordResetCodeExpires Date no Expiry for OTP reset code.
passkeys[] Subdocument array no [] WebAuthn credentials (see below).
passkeys[].id String yes Credential ID.
passkeys[].publicKey String yes Stored public key.
passkeys[].counter Number yes 0 Signature counter.
passkeys[].deviceType String yes enum: platform / cross-platform Authenticator class.
passkeys[].deviceName String no Optional human label.
passkeys[].createdAt Date no Date.now Registration timestamp.
profile.avatar String no Avatar URL.
profile.photoURL String no Alternative photo URL.
profile.phone String no Contact phone.
profile.address.street String no Inline address (separate from Address book).
profile.address.city String no
profile.address.state String no
profile.address.zipCode String no
profile.address.country String no
profile.bio String no Free-form bio.
profile.website String no Personal website URL.
profile.walletAddress String no On-chain wallet address (EVM 0x… or TON). Set via PATCH /api/user/wallet-address.
profile.walletType String no enum: evm / ton Which chain family the stored walletAddress belongs to.
profile.walletProvider String no Wallet provider label (e.g. evm, telegram-wallet). Defaults to telegram-wallet for TON, evm otherwise.
profile.walletProofVerified Boolean no True when ownership was proven — EIP-191 signature for EVM, or a verified TonProof for TON.
profile.walletProofTimestamp Date no When the wallet proof was last verified (only set when walletProofVerified is true).
profile.isPublic Boolean no false Whether the profile is publicly visible.
preferences.language String no "en" UI language.
preferences.currency String no "USD" Display currency.
preferences.notifications.email Boolean no true Opt-in for email notifications.
preferences.notifications.sms Boolean no false Opt-in for SMS notifications.
preferences.notifications.push Boolean no true Opt-in for push notifications.
status String no "active" enum: active / suspended / deleted yes Soft-delete and moderation flag.
lastLoginAt Date no Updated by auth middleware.
refreshTokens[] String[] no [] Array of currently active JWT refresh tokens. ⚠️ Reset to [] on password change and on password reset, which invalidates every outstanding session and forces re-login everywhere.
referralCode String no unique, sparse Not yet implemented in User.ts — planned for referral programme.
referredBy ObjectId → User no yes Not yet implemented in User.ts — planned for referral programme.
points.total Number no 0 Not yet implemented in User.ts — planned for loyalty system.
points.available Number no 0 Not yet implemented in User.ts.
points.used Number no 0 Not yet implemented in User.ts.
points.level Number no 1 yes (points.level) Not yet implemented in User.ts — planned for LevelConfig lookup.
referralStats.totalReferrals Number no 0 Not yet implemented in User.ts.
referralStats.activeReferrals Number no 0 Not yet implemented in User.ts.
referralStats.totalEarned Number no 0 Not yet implemented in User.ts.
createdAt Date auto Mongoose timestamp.
updatedAt Date auto Mongoose timestamp.

Virtuals

Virtual Returns Definition
fullName ${firstName} ${lastName} backend/src/models/User.ts:238

Indexes

Defined explicitly:

  • { email: 1 } unique sparse — allows multiple Telegram-only users without email while preserving uniqueness for email-bearing users.
  • { role: 1 }backend/src/models/User.ts:178
  • { status: 1 }backend/src/models/User.ts:179
  • { authProvider: 1 } — supports provider-level account reporting and cleanup.

[!warning] Missing indexes The schema currently defines only role and status indexes. The referralCode, referredBy, and points.level indexes documented below are not yet present in User.ts:

Pre/Post Hooks

None declared at the schema level.

Instance Methods

Signature Purpose
toJSON(): object Strips password, refreshTokens, all emailVerification* and passwordReset* fields before serialisation. Defined at backend/src/models/User.ts:243.

Static Methods

None defined on the schema.

Relationships

State Transitions

stateDiagram-v2
    [*] --> active : signup verified
    active --> suspended : admin action
    suspended --> active : admin restore
    active --> deleted : self-delete
    suspended --> deleted : admin purge
    deleted --> [*]

Common Queries

// Find by email (login)
User.findOne({ email: email.toLowerCase() });

// Active sellers
User.find({ role: 'seller', status: 'active' });

// Validate referral
User.findOne({ referralCode: code });

// Leaderboard by points
User.find({ status: 'active' }).sort({ 'points.total': -1 }).limit(10);

// Promote level
User.updateOne({ _id: id }, { $set: { 'points.level': newLevel } });

Related: TempVerification, LevelConfig, PointTransaction, ShopSettings.