217 lines
13 KiB
Markdown
217 lines
13 KiB
Markdown
---
|
|
title: System Overview
|
|
tags: [overview, architecture, system-map, mermaid]
|
|
created: 2026-05-23
|
|
---
|
|
|
|
# System Overview
|
|
|
|
> [!info] Scope
|
|
> This document gives you a single-page map of Amn from browser to blockchain. It is intentionally broad — the goal is for a new developer to walk away knowing what each major box does and how the boxes talk to each other. For deep dives into any subsystem, follow the wikilinks into [[01 - Architecture]].
|
|
|
|
## The 10,000-foot view
|
|
|
|
Amn is a **two-repo system**:
|
|
|
|
- **Frontend** (`/Users/mojtabaheidari/code/frontend`) — a Next.js 16 App Router application that serves the marketplace UI, the admin dashboard, the public blog, and the user-facing Web3 wallet flow.
|
|
- **Backend** (`/Users/mojtabaheidari/code/backend`) — an Express 5 + TypeScript API server that owns all business logic, persists to MongoDB, caches in Redis, and brokers all external integrations.
|
|
|
|
The two repos are deployable independently. They communicate over **HTTPS (REST)** for stateful actions and over **WebSocket (Socket.IO)** for live updates. The frontend never talks directly to MongoDB, Redis, SHKeeper, or OpenAI — every external interaction is mediated by the backend so that secrets stay on the server.
|
|
|
|
## System map
|
|
|
|
```mermaid
|
|
flowchart TB
|
|
subgraph Client["Client tier"]
|
|
Browser["Browser<br/>(Chrome / Safari / mobile)"]
|
|
Wallet["Wallet extension<br/>(MetaMask / WalletConnect)"]
|
|
end
|
|
|
|
subgraph FE["Frontend tier — Next.js 16"]
|
|
SSR["Next.js SSR / RSC<br/>App Router"]
|
|
ClientJS["Client JS<br/>MUI v7 + React 19"]
|
|
Wagmi["Wagmi + Viem<br/>Web3 client"]
|
|
SocketC["socket.io-client"]
|
|
I18n["i18next<br/>6 locales + RTL"]
|
|
end
|
|
|
|
subgraph BE["Backend tier — Node.js / Express 5"]
|
|
REST["REST API<br/>/api/*"]
|
|
SocketS["Socket.IO server<br/>rooms per user / chat / request"]
|
|
Auth["Auth service<br/>JWT + Passkey + Google"]
|
|
Market["Marketplace service<br/>Requests, Offers, Templates"]
|
|
ChatSvc["Chat service"]
|
|
PaySvc["Payment service<br/>+ PaymentCoordinator"]
|
|
Disp["Dispute service"]
|
|
Points["Points / Referrals"]
|
|
BlogSvc["Blog service"]
|
|
AISvc["AI service"]
|
|
Notif["Notification service"]
|
|
Files["File upload<br/>(multer + sharp)"]
|
|
end
|
|
|
|
subgraph Data["Data tier"]
|
|
Mongo[("MongoDB<br/>via Mongoose")]
|
|
RedisDB[("Redis<br/>cache + locks")]
|
|
Disk[("Local disk<br/>/uploads")]
|
|
end
|
|
|
|
subgraph External["External services"]
|
|
SHK["SHKeeper<br/>crypto invoicing"]
|
|
DePay["DePay widget"]
|
|
Chain["EVM chains<br/>BSC / ETH / Polygon"]
|
|
SMTP["SMTP<br/>(nodemailer)"]
|
|
OpenAI["OpenAI API"]
|
|
Google["Google OAuth"]
|
|
Sentry["Sentry"]
|
|
Alchemy["Alchemy RPC"]
|
|
end
|
|
|
|
Browser --> SSR
|
|
Browser <--> ClientJS
|
|
ClientJS <--> SocketC
|
|
ClientJS --> Wagmi
|
|
Wagmi <--> Wallet
|
|
Wallet <--> Chain
|
|
|
|
SSR --> REST
|
|
ClientJS --> REST
|
|
SocketC <--> SocketS
|
|
|
|
REST --> Auth & Market & ChatSvc & PaySvc & Disp & Points & BlogSvc & AISvc & Notif & Files
|
|
SocketS --> ChatSvc & Notif & Market
|
|
|
|
Auth & Market & ChatSvc & PaySvc & Disp & Points & BlogSvc --> Mongo
|
|
Auth & PaySvc & Notif --> RedisDB
|
|
Files --> Disk
|
|
|
|
PaySvc <--> SHK
|
|
SHK -.webhook.-> PaySvc
|
|
PaySvc --> Chain
|
|
Wagmi --> DePay
|
|
DePay --> Chain
|
|
PaySvc -.tx fetch.-> Alchemy
|
|
|
|
Notif --> SMTP
|
|
Auth --> Google
|
|
AISvc --> OpenAI
|
|
BE --> Sentry
|
|
FE --> Sentry
|
|
```
|
|
|
|
## Walk-through of each subsystem
|
|
|
|
### Authentication & identity — [[Authentication Flow]]
|
|
|
|
Auth is the gate to every authenticated route. Amn supports three login methods in parallel:
|
|
|
|
- **Email + password (JWT).** Standard `bcrypt`-hashed credentials, access + refresh token pair, six-digit email verification codes, and password reset codes. Source: `backend/src/services/auth/authService.ts`.
|
|
- **Passkey (WebAuthn).** Platform and cross-platform authenticators are registered against the user account; multiple devices per user are stored in `User.passkeys[]` (`backend/src/models/User.ts:125`).
|
|
- **Google OAuth.** Server-side verification via `google-auth-library`. See `backend/src/services/auth/googleOAuthService.ts`.
|
|
|
|
Roles are `admin | buyer | seller` (`backend/src/models/User.ts:94`). Role checks happen in route middleware. **Refresh tokens** are stored on the `User` document and rotated.
|
|
|
|
### Marketplace — [[Marketplace Domain]]
|
|
|
|
The heart of the platform. Three first-class models drive it:
|
|
|
|
- **PurchaseRequest** (`backend/src/models/PurchaseRequest.ts`) — the buyer's brief, with productType (`physical_product | digital_product | service | consultation`), budget range, urgency, delivery info (physical or online), and a long status enum that walks the entire deal: `pending_payment → pending → active → received_offers → in_negotiation → payment → processing → delivery → delivered → confirming → completed`.
|
|
- **SellerOffer** (`backend/src/models/SellerOffer.ts`) — a single bid attached to a request with `price`, `deliveryTime`, `status`, `validUntil`, and free-form `notes`.
|
|
- **RequestTemplate** (`backend/src/models/RequestTemplate.ts`) — a reusable "express checkout" version of a purchase request that can spawn real `PurchaseRequest` instances at click time.
|
|
|
|
Services live in `backend/src/services/marketplace/` and are exposed through `/api/marketplace/*`. The frontend uses a mix of React Query (`@tanstack/react-query`) and SWR for data fetching, with mutations gated through the actions layer in `frontend/src/actions/`.
|
|
|
|
### Payments — [[Payments Overview]] / [[SHKeeper Integration]]
|
|
|
|
Payments are where Amn is most distinctive. The backend supports **three payment surfaces** routed through a common `Payment` model (`backend/src/models/Payment.ts`):
|
|
|
|
- **SHKeeper** — `/api/payment/shkeeper`. Mounted at `backend/src/app.ts:327`. Issues a fresh wallet address per invoice, polls / webhooks for payment confirmation, and runs through `PaymentCoordinator` to avoid race conditions where a payment status is updated twice. Health is monitored in the background (`shkeeperHealthCheck.ts`, started in `app.ts:433`).
|
|
- **Decentralized (Wagmi + DePay)** — `/api/payment/decentralized`. The user signs and sends the transfer from their own wallet; the backend then verifies the transaction on-chain via `blockchainTxFetcher.ts` and the Alchemy SDK.
|
|
- **Payout** — `/api/payment/shkeeper/payout`. Admin-triggered release of escrow funds to the seller's wallet once delivery is confirmed.
|
|
|
|
All three surfaces converge on the same `Payment` record (with `direction: 'in' | 'out' | 'refund'`) and trigger the same downstream events: order status update, notification, points award. **Pending payments are auto-cleaned** by a background timer started in `app.ts:374`.
|
|
|
|
### Real-time chat — [[Chat System]]
|
|
|
|
Chat is built on Socket.IO rooms. Every entity that needs live updates gets its own room (see `backend/src/app.ts:79-178`):
|
|
|
|
- `user-<id>` — personal notifications
|
|
- `chat-<id>` — chat room messages, typing indicators, presence
|
|
- `request-<id>` — purchase request lifecycle events
|
|
- `buyer-<id>` / `seller-<id>` — marketplace-wide updates
|
|
- `sellers` / `buyers` — global broadcast pools
|
|
|
|
Messages persist to MongoDB through the `Chat` model and are rate-limited per chat (`chatRateLimiter.ts`). The frontend's `socket/` directory wraps `socket.io-client` and exposes typed event hooks to React components.
|
|
|
|
### Notifications — [[Notifications]]
|
|
|
|
Two notification channels:
|
|
|
|
- **In-app** — `Notification` documents pushed over Socket.IO to `user-<id>` rooms; rendered in the frontend's bell-icon drawer.
|
|
- **Email** — `nodemailer` + SMTP for verification codes, password resets, and high-importance events. See `backend/src/services/email/`.
|
|
|
|
Push and SMS are tracked as **planned** in `backend/TODO.md`.
|
|
|
|
### Disputes — [[Dispute System]]
|
|
|
|
When a deal goes wrong (see [[Glossary#Dispute]]), either party can open a dispute. The backend (`backend/src/services/dispute/DisputeService.ts`) creates a **three-way chat** between buyer, seller, and admin, opens a `Dispute` document with a structured `timeline[]` and `evidence[]`, and assigns the dispute to an admin via `assignAdmin()`. Resolution can be `refund | replacement | compensation | warning_seller | ban_seller | no_action` and is recorded on the dispute itself.
|
|
|
|
### Points & referrals — [[Points System]]
|
|
|
|
Each user has an embedded `points` object (`total | available | used | level`) and `referralStats` (`backend/src/models/User.ts`). Every grant or spend writes a `PointTransaction` record for auditability. The points module supplies referral codes (the `/r/:code` short URL in `app.ts:274` redirects to signup), tracks level progression against `LevelConfig`, and exposes the user-facing dashboard through `frontend/src/sections/points/`.
|
|
|
|
### Blog — [[Blog System]]
|
|
|
|
A simple admin-authored CMS. `BlogPost` documents support categories, tags, and embedded video via TipTap's rich-text editor on the frontend. Public reads are unauthenticated (`/api/blog/posts`); writes require admin role. Seed data lives in `backend/src/seeds/seedBlogPosts.ts` and runs on dev startup.
|
|
|
|
### AI — [[AI Assist]]
|
|
|
|
OpenAI (model configurable per call) is exposed through `/api/ai/*`. The current surface includes purchase-request drafting, chat summarisation, and admin-facing intent classification. Requests are queued from the frontend's `actions/ai*` modules and use streaming where appropriate.
|
|
|
|
### File uploads
|
|
|
|
`multer` accepts multipart uploads (max 10 MB body, `app.ts:232`), `sharp` resizes images on the fly, and files land in `/uploads` (mounted as a static directory at `app.ts:260`). In production the path is configurable via `UPLOAD_PATH`.
|
|
|
|
### Caching, locks & background jobs
|
|
|
|
**Redis** (`backend/src/services/redis/`) is used for:
|
|
- short-lived caches (sessions, marketplace listings)
|
|
- locks used by `PaymentCoordinator` to serialise status transitions
|
|
- rate-limit counters (currently disabled in code but plumbed in)
|
|
|
|
**Background workers** run inside the Express process for now — no separate worker tier. Notable timers:
|
|
- `startPendingPaymentsCleanup()` — sweeps stale unpaid invoices
|
|
- `startShkeeperHealthMonitor()` — pings the SHKeeper instance and surfaces alerts
|
|
- Auto-seed logic on startup (gated by `NODE_ENV` and `AUTO_SEED_ON_START`)
|
|
|
|
## Request lifecycle (the happy path)
|
|
|
|
> [!example] End-to-end deal walk-through
|
|
> 1. Buyer signs in (JWT). UI joins `user-<buyerId>` over Socket.IO.
|
|
> 2. Buyer creates a [[Purchase Request]] → `POST /api/marketplace/requests`. The request lands in `pending`/`active`. Sellers in the matching category receive a Socket.IO notification.
|
|
> 3. Seller views the request, opens [[Seller Offer]] modal, submits price + delivery time → `POST /api/marketplace/offers`. Buyer sees the offer arrive live.
|
|
> 4. Buyer accepts an offer → request moves to `payment`. UI opens the payment selector.
|
|
> 5. Buyer picks **SHKeeper** → backend creates a SHKeeper invoice, returns a wallet address + QR code. Buyer pays. SHKeeper webhook hits `/api/payment/shkeeper/webhook`; `PaymentCoordinator` flips `Payment.status = paid` and `PurchaseRequest.status = processing`.
|
|
> 6. Seller ships. Buyer confirms delivery (or it auto-confirms after the SLA window). Admin triggers (or schedules) a **payout** → SHKeeper releases USDT to the seller's wallet.
|
|
> 7. Both parties leave reviews. Points are awarded. The deal is closed.
|
|
>
|
|
> If the buyer disputes the delivery, jump to step 7 of the [[Dispute Flow]] instead.
|
|
|
|
## Cross-cutting concerns
|
|
|
|
- **Observability** — Sentry is initialised at the very top of `app.ts` (line 2-3) and on the frontend in `sentry.{client,edge,server}.config.ts`. Logs flow through `backend/src/utils/logger.ts`.
|
|
- **Security** — `helmet`, CORS scoped to `FRONTEND_URL`, JWT with bcrypt-hashed passwords, role-gated middleware. Rate limiting is plumbed but currently disabled (see `app.ts:227`).
|
|
- **Internationalisation** — Six locales (en, fr, vi, cn, ar, fa), with `fa` as default. RTL via `stylis-plugin-rtl`. See `frontend/src/locales/`.
|
|
- **Theming** — MUI v7 with a custom theme in `frontend/src/theme/`. Dark mode is on the roadmap.
|
|
- **Containerisation** — Docker Compose stacks for dev and prod live in both repos (`docker-compose.dev.yml`, `docker-compose.production.yml`).
|
|
|
|
## Where to go next
|
|
|
|
- [[Tech Stack]] — exact versions of every dependency.
|
|
- [[Roles & Personas]] — who does what in the system.
|
|
- [[Glossary]] — a domain dictionary you will want open in another pane.
|
|
- [[01 - Architecture]] — service boundaries, module layout, and deployment topology.
|
|
- [[02 - Data Models]] — MongoDB collections and field-by-field schemas.
|
|
- [[03 - API Reference]] — every endpoint, its payload, and its auth requirements.
|
|
- [[04 - Flows]] — diagrammed user journeys for every major use case.
|