feat: scaffold frontend pnpm monorepo with apps (admin, web) and shared packages (types, api-client, ui)

This commit is contained in:
Bot
2026-08-30 23:55:17 +08:00
parent 874b421f39
commit 1e9b9bd0b2
11 changed files with 193 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
# WTFX Frontend Monorepo Architecture
WTFX 前端采用 **pnpm workspace Monorepo + Feature-Driven 领域划分** 架构。
---
## 1. 架构总览
```
wtf-frontend/
├── apps/
│ ├── admin/ # 超级管理员控制台 (合约部署向导、协议治理、风险裁决)
│ │ └── src/
│ │ ├── routes/ # 路由页面 (dashboard, markets, governance, deploy)
│ │ └── features/ # 独立业务领域 (contract-deploy, protocol-governance, risk-control)
│ │
│ └── web/ # 用户端交易主站 (高频预测交易、图表、持仓)
│ └── src/
│ ├── routes/ # 路由页面 (markets, trade, portfolio, profile)
│ └── features/ # 独立业务领域 (market, trading, wallet, prediction)
├── packages/
│ ├── types/ # 【核心】全栈共享 TypeScript 数据类型 (Market, Order, Position)
│ ├── api-client/ # 【核心】统一 API SDK 客户端 (供 Web/Admin/后续 TMA 复用)
│ ├── ui/ # 共享 Trading UI 基础原子组件库
│ └── config/ # 共享 ESLint / TS / Tailwind 配置
├── package.json # Monorepo 根管理
└── pnpm-workspace.yaml # Workspace 定义
```
---
## 2. 核心架构守则 (Frontend Architecture Rules)
1. **【Feature-Driven 组织业务】**
- 业务代码按领域归类在 `src/features/<feature-name>/` 下,严禁在全局 `components/` 堆积上百个业务大泥球组件。
2. **【SDK 与 Type 唯一维护源】**
- 所有前后端交互数据类型在 `packages/types` 统一定义;
- 所有 REST / GraphQL API 调用通过 `packages/api-client`,确保 Web、Admin 以及后续 TMA 共享同一套接口客户端。
3. **【按需抽象,拒绝过度设计】**
- 业务特定的复杂图表/组件(如 TradingView 集成)优先放在 `apps/web/features/trading/` 中;
- 仅当多个 App(如 Admin 与 Web)真实产生复用时,才提取到 `packages/ui/`
4. **【Admin 与 Web 严格独立部署】**
- Admin 运行在 `:3100` (`admin.wtfx.app`)Web 运行在 `:3000` (`wtfx.app`)
- 两者拥有独立的路由体系与鉴权逻辑,严禁在同一 App 内通过 `if (isAdmin)` 揉杂代码。
+19
View File
@@ -0,0 +1,19 @@
{
"name": "@wtfx/admin",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev -p 3100",
"build": "next build",
"start": "next start -p 3100"
},
"dependencies": {
"@wtfx/types": "workspace:*",
"@wtfx/api-client": "workspace:*",
"@wtfx/ui": "workspace:*",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"next": "^14.2.4",
"ethers": "^6.13.0"
}
}
+19
View File
@@ -0,0 +1,19 @@
{
"name": "@wtfx/web",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev -p 3000",
"build": "next build",
"start": "next start -p 3000"
},
"dependencies": {
"@wtfx/types": "workspace:*",
"@wtfx/api-client": "workspace:*",
"@wtfx/ui": "workspace:*",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"next": "^14.2.4",
"ethers": "^6.13.0"
}
}
+15
View File
@@ -0,0 +1,15 @@
{
"name": "wtf-frontend-monorepo",
"version": "1.0.0",
"private": true,
"scripts": {
"dev:admin": "pnpm --filter @wtfx/admin dev",
"dev:web": "pnpm --filter @wtfx/web dev",
"build:admin": "pnpm --filter @wtfx/admin build",
"build:web": "pnpm --filter @wtfx/web build",
"lint": "pnpm -r lint"
},
"devDependencies": {
"typescript": "^5.4.5"
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"name": "@wtfx/api-client",
"version": "1.0.0",
"main": "./src/index.ts",
"dependencies": {
"@wtfx/types": "workspace:*"
}
}
+29
View File
@@ -0,0 +1,29 @@
import { Market, Order, Position, GovernanceConfig } from '@wtfx/types';
export class WtfxApiClient {
private baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
async getMarkets(): Promise<Market[]> {
const res = await fetch(`${this.baseUrl}/api/v1/markets`);
return res.json();
}
async getMarket(marketAddress: string): Promise<Market> {
const res = await fetch(`${this.baseUrl}/api/v1/markets/${marketAddress}`);
return res.json();
}
async getPositions(userAddress: string): Promise<Position[]> {
const res = await fetch(`${this.baseUrl}/api/v1/positions?user=${userAddress}`);
return res.json();
}
async getGovernanceConfig(): Promise<GovernanceConfig> {
const res = await fetch(`${this.baseUrl}/api/v1/governance`);
return res.json();
}
}
+6
View File
@@ -0,0 +1,6 @@
{
"name": "@wtfx/types",
"version": "1.0.0",
"main": "./src/index.ts",
"types": "./src/index.ts"
}
+42
View File
@@ -0,0 +1,42 @@
export interface Market {
questionId: string;
marketAddress: string;
title: string;
imageUri?: string;
outcomeNames: string[];
status: 'Trading' | 'Graduated' | 'Finalised' | 'Refunded';
tierId: number;
totalMarketCap: string;
timestampEnd: number;
}
export interface Order {
id: string;
marketAddress: string;
outcomeIndex: number;
side: 'BUY' | 'SELL';
amount: string;
price: string;
status: 'OPEN' | 'FILLED' | 'CANCELLED';
createdAt: number;
}
export interface Position {
marketAddress: string;
tokenId: string;
outcomeName: string;
shares: string;
avgCost: string;
}
export interface GovernanceConfig {
creatorShare: string;
centralWallet: string;
defaultFeeRate: string;
disputeWindow: number;
tiers: Record<number, {
thresholdMcap: string;
thresholdMaxSupply: string;
disputeWindow: number;
}>;
}
+5
View File
@@ -0,0 +1,5 @@
{
"name": "@wtfx/ui",
"version": "1.0.0",
"main": "./src/index.ts"
}
+1
View File
@@ -0,0 +1 @@
export const UI_VERSION = "1.0.0";
+3
View File
@@ -0,0 +1,3 @@
packages:
- 'apps/*'
- 'packages/*'