- Update backend, frontend, scanner, deployment, amanat-assist service docs - Update System Overview, Scanner Architecture, Telegram Mini App flow - Update 10 - Services/README.md - Add Tenant data model, Tenant API reference, Tenant Storefront Flow - Add Multi-Shop Branch Project Scan (2026-06-10) - Add tenant.md service doc - Append activity log entry - Reflects archived/search/stats route fix and new E2E test suite Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
20 KiB
title, tags, created, updated
| title | tags | created | updated | ||||||
|---|---|---|---|---|---|---|---|---|---|
| Frontend Service — amn-frontend |
|
2026-06-08 | 2026-06-12 |
Frontend Service — amn-frontend
1. Overview
amn-frontend is the primary user-facing application for the Amanat (AMN) escrow marketplace. It serves buyers, sellers, and admins through a unified Next.js 16 App Router application with a Persian-first (RTL) UI.
| Field | Value |
|---|---|
| Package name | amn-frontend |
| Version | 2.11.89 |
| Status | Active — main deployed to dev.amn.gg; feature/white-label-shops deployed to multi.amn.gg |
| Framework | Next.js 16 (App Router + Turbopack), React 19, TypeScript strict |
| Dev port | 8083 (both local and Docker) |
| Package manager | yarn@1.22.22 |
| Node requirement | >=20 |
| Repo | git@git.tbs.amn.gg:escrow/frontend.git |
The app covers the full escrow lifecycle: request creation, multi-seller offer collection, negotiation, on-chain payment (BSC/ETH/Base/TON), delivery confirmation, dispute handling, loyalty points, tenant admin for white-label shops, public seller-shop browsing, and a Telegram Mini App shell for mobile-native access.
[!note] Multi-shop branch
feature/white-label-shopsaddsTenantProvider,/dashboard/admin/tenants, custom-domain controls, bot activation links, and theWEBAPP_ENABLEDmiddleware gate that keepsmulti.amn.ggMini App-first while leaving dashboard/auth routes reachable.
2. Tech Stack
| Layer | Library / Version | Notes |
|---|---|---|
| Framework | next@^16.1.1 |
App Router, Turbopack dev server |
| UI runtime | react@^19.1.0, react-dom@^19.1.0 |
Server + Client Components |
| Component library | @mui/material@^9.0.1 |
MUI v9 with Emotion; @mui/lab, @mui/x-data-grid, @mui/x-date-pickers, @mui/x-tree-view |
| Styling engine | @emotion/react, @emotion/styled, stylis-plugin-rtl |
RTL support via stylis |
| State / data fetching | @tanstack/react-query@^5.83.0, swr@^2.3.3 |
TanStack Query is primary; SWR used in some legacy paths |
| Real-time | socket.io-client@^4.8.1 |
Bidirectional events; custom SocketContext |
| Forms | react-hook-form@^7.77.0, @hookform/resolvers@^5.0.1, zod@^4.0.10 |
Schema validation via Zod v4 |
| i18n | i18next@^26.3.0, react-i18next@^17.0.8 |
6 locales (en, fa, ar, fr, cn, vi); RTL for fa/ar |
| Web3 — EVM | wagmi@^2.19.5, viem@^2.31.7, ethers@^6.15.0 |
WalletConnect + MetaMask + Trezor |
| Web3 — TON | @tonconnect/ui-react@^2.4.4, @ton/core@^0.63.1 |
TON wallet payments |
| Hardware wallet | @trezor/connect-web@^9.7.3 |
Trezor signing flow |
| Chain indexing | alchemy-sdk@^3.6.1 |
Alchemy for multi-chain queries |
| Rich text editor | @tiptap/react@^3.23.6 + extensions |
Used in post/blog editor |
| Charts | apexcharts@^5.10.1, react-apexcharts@^2.1.0 |
Dashboard KPI charts |
| Animation | framer-motion@^12.13.0 |
Page transitions and UI motion |
| Carousel | embla-carousel-react@8.6.0 |
Product / shop carousels |
| Maps | mapbox-gl@^3.12.0, react-map-gl@^8.0.4 |
Address / location pickers |
| HTTP client | axios@^1.11.0 |
Centralised instance with auth interceptors in src/lib/axios.ts |
| Notifications | notistack@^3.0.2, sonner@^2.0.3 |
Snackbar + toast |
| Error monitoring | @sentry/nextjs@^10.22.0 |
SDK wraps Next.js build + runtime |
| CAPTCHA | @marsidev/react-turnstile@^1.5.2 |
Cloudflare Turnstile |
| Dates | dayjs@^1.11.13, date-fns-jalali@^4.1.0-0 |
Jalali (Persian) calendar support |
| QR code | qrcode@^1.5.4 |
Wallet payment QR generation |
| Fonts | DM Sans, Inter, Nunito Sans, Public Sans, Barlow | Variable fonts via @fontsource-variable |
3. App Router Page Structure
All routes live under frontend/src/app/. The dev server and Docker container both bind port 8083.
Top-level route segments
| Route | Type | Purpose |
|---|---|---|
/ |
Public | Landing / marketing home |
/api/health |
API Route | Container health-check endpoint |
/api/llm |
API Route | LLM proxy for amanat-assist features |
/auth/jwt/* |
Public | Sign-in, sign-up, OTP verify, password reset, update |
/checkout/request-network/* |
Public | Request Network payment checkout shell |
/dashboard/* |
Protected | Main authenticated app (see below) |
/design-preview |
Internal | Theme/component sandbox |
/error |
Public | Global error display |
/payment/callback, /payment/cancel |
Public | Payment gateway redirect landing |
/post/[slug] |
Public | Blog post reader |
/shop/[seller]/[id] |
Public | Public seller shop / item view |
/store/items, /store/checkout |
Public | Storefront browsing and checkout |
/telegram |
Mini App | Telegram Mini App shell (see §7) |
Dashboard sub-routes (AuthGuard + EmailVerificationGuard)
| Route | Purpose |
|---|---|
/dashboard → /dashboard/overview |
KPI home tiles, recent activity |
/dashboard/chat |
Real-time escrow chat |
/dashboard/account/* |
Profile, address, notifications, wallet, passkey |
/dashboard/request/* |
Buyer purchase requests |
/dashboard/request-template/* |
Seller request templates |
/dashboard/payment/* |
Payment history and detail |
/dashboard/points/* |
Loyalty hub — transactions, referrals, levels |
/dashboard/disputes/* |
Dispute creation and management |
/dashboard/seller/* |
Seller-side offer management |
/dashboard/shop-settings/* |
Seller shop configuration (incl. Telegram config) |
/dashboard/shops/* |
Browse / checkout from within dashboard |
/dashboard/user/* |
Admin user management |
/dashboard/post/* |
Admin blog editor (Tiptap) |
/dashboard/admin/tenants/* |
Tenant admin (white-label shops; feature/white-label-shops only) |
/dashboard/assist/* |
AI assistant (amanat-assist) |
4. Key Sections / Features
Marketplace and escrow flow
The primary buyer journey:
- Buyer submits a purchase request (
/dashboard/request/new) — product description, budget, chain preference. - Sellers see the request and submit offers via request templates (
/dashboard/request-template). - Buyer selects an offer; both sides enter the escrow chat (
/dashboard/chat). - Buyer initiates payment — on-chain via Wagmi/Trezor or off-chain via Request Network.
- After delivery, buyer releases escrow funds; on dispute, both parties access
/dashboard/disputes.
Dashboard
Multi-role dashboard accessible post-login. Guards:
AuthGuard— redirects unauthenticated users to/auth/jwt/sign-in.EmailVerificationGuard— blocks unverified accounts on key routes.
Sidebar nav adapts to role: buyer, seller, admin, or multi-tenant operator.
Admin
/dashboard/user and /dashboard/post are admin-only sections gated by role check in the layout. Tenant admin (/dashboard/admin/tenants) is only visible on the feature/white-label-shops build.
Telegram Mini App
Full Telegram Mini App (TMA) at /telegram. See §7 for integration details.
White-label shops
feature/white-label-shops branch adds multi-tenancy: TenantProvider (Context), /dashboard/admin/tenants CRUD, custom domain config per tenant, and a WEBAPP_ENABLED middleware flag to route Mini App-first for multi.amn.gg.
Blog / content
Public blog at /post/[slug] rendered server-side. Admin writes posts via Tiptap rich-text editor at /dashboard/post.
AI assistant (amanat-assist)
/api/llm proxies requests to the amanat-assist backend service. The dashboard /assist section provides the in-app chat interface.
5. State Management
| Layer | Mechanism | Usage |
|---|---|---|
| Server cache / async state | @tanstack/react-query |
All API data fetching, mutation, background refetch |
| Legacy async state | swr |
Some older sections not yet migrated to TQ |
| Real-time events | SocketContext (src/contexts/) |
Socket.io connection; exposes socket via useSocket hook |
| Global UI state | React Context (multiple providers) | Auth, Settings (theme/direction/language), Web3 |
| Form state | react-hook-form + Zod |
All forms; validation on client |
| Zustand | Not in use | No Zustand dependency in package.json |
The root layout stacks providers in order: ThemeProvider → SettingsProvider → AuthProvider → QueryClientProvider → SocketContextProvider → Web3Provider.
The Telegram layout uses a minimal provider stack: TonConnectUIProvider + QueryClientProvider only — no dashboard providers.
6. Internationalization
| Detail | Value |
|---|---|
| Library | i18next@^26.3.0 + react-i18next@^17.0.8 |
| Language detection | i18next-browser-languagedetector + accept-language (server hint) |
| Locales shipped | English (en), Persian/Farsi (fa), Arabic (ar), French (fr), Chinese (cn), Vietnamese (vi) |
| RTL locales | fa, ar — direction: rtl applied at theme level; stylis-plugin-rtl transforms MUI Emotion styles |
| Jalali calendar | date-fns-jalali@^4.1.0-0 — date pickers switch to Jalali for fa locale |
| Translation files | src/locales/langs/{en,fa,ar,fr,cn,vi}/*.json (lazy-loaded via i18next-resources-to-backend) |
| Telegram locales | src/sections/telegram/locales/{en,fa}.ts — standalone namespace for TMA strings |
| Default locale | Determined by browser; Persian is the primary product locale |
RTL layout direction is set in SettingsProvider and passed to the MUI theme. The stylis-plugin-rtl plugin auto-mirrors margin/padding/float/border-radius CSS properties.
7. Telegram Mini App Integration
Loading mechanism
/app/telegram/layout.tsx injects the Telegram SDK via next/script with strategy="beforeInteractive":
<Script src="https://telegram.org/js/telegram-web-app.js" strategy="beforeInteractive" />
No @telegram-apps/sdk npm package is used. The native CDN script approach is chosen for compatibility with Telegram's own versioned releases.
WebApp wrapper
src/utils/telegram-webapp.ts provides a typed wrapper around window.Telegram.WebApp, exposing:
initData/initDataUnsafe— raw launch parameterscolorScheme,themeParams— Telegram UI themeMainButton,BackButton— native Telegram controlsclose(),expand(),ready()helper calls
Auth flow
- On TMA load, the app extracts
initDatafromwindow.Telegram.WebApp. - The frontend calls
POST /api/auth/telegramwith the signedinitDatastring. - Backend verifies the HMAC signature against
TELEGRAM_BOT_TOKENand issues a JWT. - The JWT is stored in memory / cookie, and subsequent API calls use the standard auth header.
Replay protection on this path is intentionally absent — Telegram may reuse initData across reloads. The backend relies on HMAC verification + auth_date freshness only.
TMA route structure
| Route | Purpose |
|---|---|
/telegram |
Entry point — reads initData, authenticates, redirects |
/telegram/shop |
Seller list and product browsing |
/telegram/cart |
In-app cart and checkout handoff |
/telegram/account |
Account tab (mirrors dashboard/account) |
Components
src/sections/telegram/components/ contains TMA-native components:
telegram-header.tsx— top navigation bar styled to Telegram themetelegram-chat-row.tsx,telegram-chat-bubble.tsx,telegram-chat-composer.tsx— inline chat UItelegram-request-stepper.tsx— step-through purchase request wizardtelegram-cart-fab.tsx— floating cart icontelegram-onboarding-sheet.tsx— first-run onboarding bottom sheettelegram-filter-drawer.tsx,telegram-list-row.tsx,telegram-list-controls.tsx— marketplace list viewstelegram-theme-toggle.tsx,telegram-language-toggle.tsx— in-app settingstelegram-unlinked-state.tsx— shown when no shop is linked to the bot
Shop-settings Telegram config
/dashboard/shop-settings includes a Telegram configuration UI where sellers can:
- Link their Telegram bot to their shop
- Set the Mini App URL
- Preview the bot launch button (
TelegramAppButtoncomponent)
8. Web3 Integration
EVM (Ethereum / BSC / Base / Polygon / Arbitrum)
| Component | Detail |
|---|---|
| Wagmi | wagmi@^2.19.5 — React hooks for wallet connection, transaction signing, contract reads |
| Viem | viem@^2.31.7 — low-level EVM client used by wagmi internally |
| Ethers | ethers@^6.15.0 — used in src/web3/web3Service.ts for legacy contract interaction |
| WalletConnect | Project ID via NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID |
| Alchemy | Per-chain API keys (NEXT_PUBLIC_ALCHEMY_API_KEY_{MAINNET,ARBITRUM,BASE,POLYGON,SEPOLIA}) for RPC and indexing |
| Escrow wallet | NEXT_PUBLIC_ESCROW_WALLET_ADDRESS — the platform escrow contract / EOA |
Chains supported: Ethereum Mainnet, BSC (BNB Chain), Base, Polygon, Arbitrum, Sepolia (testnet).
Provider hierarchy in src/web3/context/:
wagmi-provider.tsx— wraps app inWagmiProvider+QueryClientProviderweb3-provider.tsx— DePay + custom payment orchestration layerweb3-context.tsx+use-web3-context.ts— React Context for payment state
Trezor
@trezor/connect-web@^9.7.3 handles hardware wallet signing. src/web3/components/web3-signing-card.tsx surfaces the Trezor confirmation UI within the payment flow.
TON
@tonconnect/ui-react@^2.4.4 and @ton/core@^0.63.1 enable TON wallet connections and payments. The Telegram layout wraps the Mini App in TonConnectUIProvider so TON payments work natively within Telegram.
Payment UI components
| Component | Location |
|---|---|
web3-connect-card.tsx |
Wallet selection / connection modal |
web3-payment.tsx |
Payment execution with status tracking |
web3-signing-card.tsx |
Trezor/hardware wallet signing prompt |
Request Network
/app/checkout/request-network/ is the payment shell for Request Network-based payments. The checkout flow is server-side rendered and uses a dedicated layout outside the dashboard guard.
9. CI/CD
Two Woodpecker pipelines run on the CI host at 89.58.32.32 (linux/arm64).
production.yml — main branch → dev.amn.gg
Trigger: push to main or master.
| Step | Action |
|---|---|
get-version |
Reads package.json version → writes dev-<version> to .tags |
build-and-deploy |
docker build -t git.tbs.amn.gg/escrow/frontend:dev . then docker compose up -d --no-deps --pull never frontend against /opt/escrow-dev/docker-compose.yml |
notify |
node scripts/ci/tg-notify.cjs → Telegram notification (success/failure) |
Image tag: git.tbs.amn.gg/escrow/frontend:dev. No registry push — image is built locally on the CI host. pull_policy: never in the compose override prevents watchtower from pulling a stale remote image.
multi.yml — feature/white-label-shops → multi.amn.gg
Trigger: push to feature/white-label-shops.
| Step | Action |
|---|---|
build-local |
docker build with hardcoded NEXT_PUBLIC_* build-args for multi.amn.gg → tags as escrow-multi-frontend:local |
deploy |
docker compose up -d --force-recreate --no-deps frontend against /opt/arcane/data/projects/escrow-multi |
notify |
node scripts/ci/tg-notify.cjs → Telegram notification |
[!important] Version bump required Every push that triggers a build must increment the patch version in
package.json. Container images are tagged by version — an unchanged version overwrites the previous image and loses history. See RTK.md version policy.
Telegram CI notifications
scripts/ci/tg-notify.cjs is the CI notification script. It reads TG_TOKEN and TG_USERS from Woodpecker secrets. Messages must not use parse_mode (HTML/Markdown) to avoid Telegram API 400 errors from unescaped characters in commit messages.
10. Local Development Quick-Start
# Prerequisites: Node >=20, yarn 1.22.22
cd frontend/
# Install dependencies
yarn install
# Copy and populate env vars
cp .env.local.example .env.local
# Edit .env.local with backend URL, API keys, etc.
# Start dev server (Turbopack, port 8083)
yarn dev
# Alternative: webpack (for debugging Turbopack-specific issues)
yarn dev:webpack
# Type-check
npx tsc --noEmit
# Lint
yarn lint
# Unit tests
yarn test
# E2E tests (requires running backend)
yarn playwright:install # once
yarn test:e2e
# Production build (outputs standalone server)
yarn build
yarn start
The standalone server output is at .next/standalone/server.js. The build script copies static assets and public folder into the standalone bundle automatically.
11. Environment Variables
All NEXT_PUBLIC_* variables are baked into the client bundle at build time. Server-side and secret variables are runtime-only.
Client-side (build-time baked)
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_API_URL |
Backend API base URL (e.g. https://dev.amn.gg/api) |
NEXT_PUBLIC_BACKEND_URL |
Backend root URL (used for non-API paths) |
NEXT_PUBLIC_APP_URL |
Canonical frontend URL |
NEXT_PUBLIC_SOCKET_URL |
Socket.io server URL |
NEXT_PUBLIC_APP_NAME |
Display name of the application |
NEXT_PUBLIC_APP_VERSION |
App version string (mirrors package.json version) |
NEXT_PUBLIC_ASSETS_DIR |
Public assets base path |
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID |
WalletConnect Cloud project ID |
NEXT_PUBLIC_ALCHEMY_API_KEY_MAINNET |
Alchemy RPC key — Ethereum Mainnet |
NEXT_PUBLIC_ALCHEMY_API_KEY_BASE |
Alchemy RPC key — Base |
NEXT_PUBLIC_ALCHEMY_API_KEY_ARBITRUM |
Alchemy RPC key — Arbitrum |
NEXT_PUBLIC_ALCHEMY_API_KEY_POLYGON |
Alchemy RPC key — Polygon |
NEXT_PUBLIC_ALCHEMY_API_KEY_SEPOLIA |
Alchemy RPC key — Sepolia testnet |
NEXT_PUBLIC_ESCROW_WALLET_ADDRESS |
Platform escrow wallet / contract address |
NEXT_PUBLIC_TELEGRAM_BOT_ID |
Telegram Bot ID for Mini App auth |
NEXT_PUBLIC_TELEGRAM_MINI_APP_URL |
Deep link URL for TMA launches |
NEXT_PUBLIC_TURNSTILE_SITE_KEY |
Cloudflare Turnstile site key |
NEXT_PUBLIC_GOOGLE_CLIENT_ID |
Google OAuth client ID |
NEXT_PUBLIC_MAPBOX_API_KEY |
Mapbox GL access token |
NEXT_PUBLIC_ENABLE_TEST_PAYMENT |
"true" to show test payment UI |
NEXT_PUBLIC_PASSKEY_RP_ID |
WebAuthn relying party ID |
NEXT_PUBLIC_PASSKEY_ORIGIN |
WebAuthn origin |
Multi-stack build-arg overrides (Woodpecker multi.yml)
The multi pipeline passes NEXT_PUBLIC_APP_URL, NEXT_PUBLIC_API_URL, NEXT_PUBLIC_BACKEND_URL, NEXT_PUBLIC_SERVER_URL, NEXT_PUBLIC_SOCKET_URL, NEXT_PUBLIC_PASSKEY_RP_ID, and NEXT_PUBLIC_PASSKEY_ORIGIN as --build-arg to target multi.amn.gg.
[!warning] Two stacks, two bot tokens
NEXT_PUBLIC_TELEGRAM_BOT_IDmust differ between theescrow-devandescrow-multistacks. Sharing a bot token causes Telegram webhook delivery to break for one of the stacks.
12. Known Issues / Open Items
| ID | Area | Description |
|---|---|---|
| FE-01 | Performance | Measured 300–800ms API response times on dev.amn.gg are WAN RTT-bound (~235ms), not DB-bound. Server-side requests from loopback :8083 show 3–12ms. Fix: CDN/edge delivery or server-side rendering that avoids client roundtrips. |
| FE-02 | Cart isolation | Global payment socket broadcasts previously wiped every user's cart. Provider gate added in v2.8.4; backend room-scoping remains an open follow-up. |
| FE-03 | Real-time | Socket.io rooms are not yet fully scoped by provider on the backend — global broadcast events can still leak cross-user in some edge cases. |
| FE-04 | TMA auth | Telegram initData replay protection is intentionally absent. If the threat model changes, a server-side session deduplication layer would be needed at /api/auth/telegram. |
| FE-05 | State migration | Some data-fetching paths still use swr rather than @tanstack/react-query. Incremental migration to TQ is ongoing. |
| FE-06 | Multi-chain scanner | The AMN scanner payment rail watches specific chains. Cross-chain support (multi-seller + multi-chain) is not fully wired. |
| FE-07 | Trezor | @trezor/connect-web requires a popup; CSP and popup-blocker edge cases exist in some Telegram In-App Browser contexts. |
| FE-08 | Pre-push hook | The backend repo has a pre-push TSC hook that blocks on full-tree errors; a parallel agent's mid-refactor tree can block clean frontend commits. Always use explicit git add <paths>, never git add -A. |
| FE-09 | CI version policy | Every CI-triggering push must bump the patch version. An unchanged version silently overwrites the previous image tag. |
| FE-10 | TON payments | TON wallet integration is present but not fully tested end-to-end on mainnet. Verify TON mainnet contract addresses before enabling for buyers. |
Last updated: 2026-06-12 — reflects v2.11.89