# NomadCNA — Cursor 项目上下文报告 > 更新日期:2026-06-08 > 用途:为 Cursor AI 助手提供项目全貌、架构约定与开发指引。 > 生产站点:https://nomadro.cn --- ## 1. 项目简介 **NomadCNA(游牧中国)** 是面向数字游民的生活操作系统,单仓 Monorepo 包含: - **主站**:城市探索、下一站决策、活动、社区匹配、支付、统计、访谈视频等 - **Digital 子站**(`/digital`):指南、课程、电子书、远程工作、个人资料 - **FastAPI 后端**:认证、业务 API、支付回调、PocketBase 集成 - **VPS 自建开源服务**:MiroTalk P2P(视频)、The Lounge + Ergo IRC(Web 群聊) | 模块 | 说明 | |------|------| | 城市探索 | 18 城静态数据 + API,生活成本/网速/安全等指标 | | 下一站决策 | `/dashboard`,预算/气候/标签推荐 | | 活动 | 线上/线下/混合;**统一活动间** `/meetups/[id]/live` 深度融合 MiroTalk + Lounge | | 访谈视频 | `/videos` 列表 + `/videos/[slug]` 详情 | | 社区匹配 | `/dating` 滑动匹配、意图/城市筛选、互赞、额度、撤销 | | 会员与支付 | ZPay / XorPay,加入表单 + VIP 状态 | | Digital 子站 | 课程章节、电子书 Markdown、资源导航 | | 媒体 | MinIO 上传 + `app/lib/media.ts` + `/api/media/proxy` | --- ## 2. 技术栈 | 层级 | 技术 | 版本 | |------|------|------| | 前端框架 | Next.js App Router | 16.1.6 | | UI | React + TypeScript | 19.2.3 / TS 5 | | 样式 | Tailwind CSS | 4 | | 图表 | ECharts | 6 | | Markdown | marked, react-markdown, highlight.js | — | | 后端 | FastAPI + uvicorn | ≥0.115 | | 数据库 | PocketBase | SDK 0.27 | | 对象存储 | MinIO | Node + Python SDK | | 视频会议 | MiroTalk P2P | VPS :9300 | | Web 群聊 | The Lounge 4.5 + Ergo IRC 2.18 | VPS :9000 / :6667 | | 支付 | ZPay / XorPay 适配层 | — | | 包管理 | pnpm | 10.25.0 | | Node 生产版本 | — | v22.13.1 | 路径别名:`@/*` → 项目根目录(见 `tsconfig.json`)。 --- ## 3. 架构总览 ```mermaid flowchart TB subgraph Client["浏览器"] Next["Next.js 16
:3001 dev / :4002 prod"] end subgraph APIs["API 层"] NextAPI["Next Route Handlers
/api/digital/*
/api/media/*"] FastAPI["FastAPI
:8000
/api/*"] end subgraph Data["数据与服务"] PB["PocketBase :8090"] MinIO["MinIO :9100"] MiroTalk["MiroTalk P2P :9300"] Lounge["The Lounge :9000"] Ergo["Ergo IRC :6667"] end Client --> Next Next -->|"rewrite 排除 digital/media"| FastAPI Next --> NextAPI NextAPI --> PB NextAPI --> MinIO FastAPI --> PB FastAPI --> MinIO Next -->|"活动间 iframe"| MiroTalk Next -->|"活动间 iframe"| Lounge Lounge --> Ergo ``` ### API 路由分流(关键) `next.config.ts` 将 **除 `digital` 和 `media` 外** 的所有 `/api/*` 转发到 FastAPI: ```ts source: "/api/:path((?!(?:digital|media)(?:$|/.*)).*)" destination: "http://127.0.0.1:8000/api/:path*" ``` | 路径前缀 | 处理方 | |----------|--------| | `/api/digital/*` | Next.js Route Handlers | | `/api/media/*` | Next.js Route Handlers | | 其他 `/api/*` | FastAPI(经 rewrite 代理) | 前端统一通过 `app/lib/api-client.ts` 的 `apiFetch()` / `apiUrl()` 调用后端。 --- ## 4. 目录结构 ```text . ├── app/ │ ├── [locale]/ # 主站多语言页面(~54 个 page.tsx) │ │ ├── dating/ # 匹配页 + components/ │ │ ├── meetups/ # 活动列表、host、[id]/live 活动间 │ │ ├── videos/ # 访谈列表 + [slug] 详情 │ │ └── digital/ # Digital 子站页面路由 │ ├── api/ │ │ ├── digital/ # Digital 认证、支付、上传、VIP │ │ └── media/proxy/ # 远程媒体代理 │ ├── components/ │ │ └── meetups/EventLiveRoom.tsx # 活动间分屏 UI │ ├── context/ # LocaleContext、ThemeContext │ ├── data/ # cities.ts、avatars.ts、mock.ts │ ├── digital/ # Digital 子站组件、lib、context │ └── lib/ # api-client、meetups、media、payment、pocketbase ├── backend/ │ ├── main.py # FastAPI 入口(~70 个端点) │ ├── meetup_live.py # 活动间 URL / 权限 │ ├── avatar_assets.py # 头像池(与前端 avatars.ts 同步) │ ├── settings.py # 环境变量 │ ├── pb_client.py # PocketBase HTTP 客户端 │ ├── payment.py # ZPay / XorPay │ ├── minio_storage.py # MinIO 上传 │ ├── init_db.py # 集合 schema + seed │ └── seed_data.py # 种子数据 ├── config/ │ ├── remote-services.ts # 线上服务地址、Windows 远程开发 │ ├── site.config.ts # 主站 SEO │ ├── nav.config.ts # 顶部导航 │ └── digital/ # Digital 子站专属配置 ├── scripts/ │ ├── dev-tunnel.py # SSH 隧道:MinIO → localhost:9100 │ └── dev-windows.ps1 # Windows 一键开发 ├── i18n/ # routing.ts、navigation.ts ├── messages/ # zh.json、en.json 主站文案 └── next.config.ts ``` ### 主站 vs Digital 子站代码边界 | 区域 | 路径 | 说明 | |------|------|------| | 主站页面 | `app/[locale]/*` | 城市、活动、匹配、视频等 | | 主站组件 | `app/components/` | Navbar、EventLiveRoom 等 | | 主站工具 | `app/lib/` | api-client、meetups、media、payment | | Digital 页面 | `app/[locale]/digital/*` | 课程、电子书、工作等 | | Digital 组件 | `app/digital/components/` | 独立 Header、Hero、课程区块 | | Digital 工具 | `app/digital/lib/` | 平行的 payment、pocketbase、learning | | Digital 配置 | `config/digital/*` | 课程、域名、视觉主题 | 两套代码 **有意平行**(非完全共享),修改时注意对应目录。 --- ## 5. 路由与国际化 - 语言:`zh`(默认)、`en`,路由 **始终带前缀**(`/zh/...`、`/en/...`) - 配置:`i18n/routing.ts` - 文案:`messages/zh.json`、`messages/en.json` - 语言感知导航:**必须**使用 `i18n/navigation.ts` 的 `Link`、`useRouter`、`useTranslation` ```tsx import { Link, useRouter, useTranslation } from "@/i18n/navigation"; ``` 新页面:在 `app/[locale]/` 建 `page.tsx` → 补 `messages/*.json` → 必要时改 `config/nav.config.ts`。 --- ## 6. API 端点速查 ### 6.1 Next.js Route Handlers | 路径 | 作用 | |------|------| | `app/api/digital/auth/*` | Digital 认证 | | `app/api/digital/join`、`pay/*`、`vip/check` | 加入、支付、VIP | | `app/api/digital/upload-media` | 媒体上传 MinIO | | `app/api/media/proxy` | 远程媒体抓取/缓存 | ### 6.2 FastAPI(`backend/main.py`) | 分类 | 端点 | |------|------| | 健康/认证 | `GET /api/health`,`POST /api/auth/email|google`,`GET /api/auth/me` | | 加入/支付 | `POST /api/join`,`POST /api/pay`,`GET /api/pay/status` | | 城市/内容 | `GET /api/cities`,`GET /api/content/home`,`GET /api/content/{slug}` | | **视频** | `GET /api/videos` | | **活动** | `GET/POST /api/meetups`,`GET /api/meetups/{id}`,**`GET /api/meetups/{id}/session`** | | **匹配** | `GET /api/matches/candidates|likes|mutual|quota|profile/me`,`POST /api/matches/swipes`,`POST /api/matches/swipes/undo` | | 社区 | `GET/POST /api/discussions`,`/api/gigs`,`GET /api/profiles` | | 通知/统计 | `/api/notifications/*`,`/api/stats/*` | | 其他 | `POST /api/feedback`,`POST /api/recommendations/next-stop` | | 支付回调 | `/cnomadcna/zpay_order_status`,`/cnomadcna/xorpay_notify` | **注意**:`/api/videos` 必须独立路由,不能走 `/api/content/{slug}`(slug=`videos` 会 404)。 --- ## 7. 核心业务模块指引 ### 7.1 城市探索 - 静态:`app/data/cities.ts`(18 城) - 首页:`app/[locale]/page.tsx` + FilterBar、CityCard、CityModal - API:`GET /api/cities`、`/api/cities/{slug}` ### 7.2 活动 / 视频 / 群聊(深度融合) | 层级 | 文件 | |------|------| | 列表/弹窗 | `app/[locale]/meetups/page.tsx` | | **活动间** | `app/[locale]/meetups/[id]/live/page.tsx` | | UI 组件 | `app/components/meetups/EventLiveRoom.tsx` | | 前端逻辑 | `app/lib/meetups.ts` | | 后端逻辑 | `backend/meetup_live.py` | | 会话 API | `GET /api/meetups/{id}/session` | 流程: 1. 用户打开活动详情 → **「进入活动间」** 2. Live 页请求 `/api/meetups/{id}/session`(带 Cookie) 3. 返回 `videoUrl`(MiroTalk + `name=`)、`chatUrl`(Lounge channel deep link)、`canJoin` 4. `EventLiveRoom` 分屏 iframe 嵌入;支持只看视频/只看群聊 PocketBase `meetups` 关键字段:`mode`、`accessLevel`、`mirotalkRoom`、`loungeChannel`、`features`。 ### 7.3 访谈视频 - 列表:`app/[locale]/videos/page.tsx` → `GET /api/videos` - 详情:`app/[locale]/videos/[slug]/page.tsx` - 首页 video 卡片 `targetUrl` 固定 `/videos`(见 `app/[locale]/page.tsx` 的 `normalizeHomeContentItem`) ### 7.4 社区匹配(Dating) - 页面:`app/[locale]/dating/page.tsx` - 组件目录:`app/[locale]/dating/components/` - `IntentTabs`、`CityFilter`、`SwipeStack`、`SwipeCard` - `LikesPanel`、`MatchModal`、`AdvancedFilters`、`SwipeQuota` - 常量/类型:`constants.ts`、`types.ts`、`utils.ts` - 头像:`app/data/avatars.ts` ↔ `backend/avatar_assets.py` - Schema:`profiles`(lookingFor、gender、citySlug)、`swipes`(intent)、`match_connections` ### 7.5 支付 - 前端:`app/lib/payment/` - Digital:`app/digital/lib/payment/` - 后端:`backend/payment.py` - 配置:`config/services.config.ts`、`config/digital/services.config.ts` ### 7.6 媒体 - `app/lib/media.ts`:URL 规范化、Unsplash 直连、MinIO 公开路径 - `app/api/media/proxy/route.ts`:代理失败时 passthrough 原图 ### 7.7 认证 - Cookie:`app/lib/auth-cookie.ts` - PocketBase:`app/lib/pocketbase/auth.ts` - FastAPI 会话:`backend/pb_client.py` + `current_user()` --- ## 8. 配置系统 | 文件 | 用途 | |------|------| | `config/site.config.ts` | 主站 SEO | | `config/nav.config.ts` | 顶部导航 | | `config/home.config.ts` | 首页静态块 | | `config/remote-services.ts` | nomadro.cn 各服务 URL、MinIO/PB 解析、`isRemoteDevMode()` | | `config/services.config.ts` | 主站 PocketBase/支付/MinIO | | `config/runtime-env.ts` | `APP_ENV` 判断 | | `config/digital/*` | Digital 子站 | --- ## 9. 开发命令 ```bash pnpm install pnpm dev # http://localhost:3001 # 后端(Windows) python -m venv backend/.venv backend\.venv\Scripts\pip install -r backend/requirements.txt backend\.venv\Scripts\uvicorn backend.main:app --host 127.0.0.1 --port 8000 --reload # Windows 连线上 MinIO python scripts/dev-tunnel.py # 初始化 PocketBase 集合 + 种子 python -m backend.init_db # 质量检查(上线前) pnpm lint pnpm exec tsc --noEmit --pretty false ``` **PowerShell 不支持 `&&`**,命令分步执行。 本地需 **Next.js (:3001) + FastAPI (:8000)** 同时运行。8000 端口若被多个旧 uvicorn 占用,新路由不会生效——先杀干净再启一个实例。 --- ## 10. 环境变量要点 > ⚠️ 勿提交 `.env.local`、`backend/.env` 或真实密钥。 ### 前端(`.env.local`) | 变量 | 说明 | |------|------| | `NEXT_PUBLIC_SITE_URL` | 本地 `http://localhost:3001` | | `NEXT_PUBLIC_API_BASE_URL` | 生产 `/api`;本地可留空走 rewrite | | `FASTAPI_PROXY_TARGET` | rewrite 目标,默认 `http://127.0.0.1:8000` | | `NEXT_PUBLIC_POCKETBASE_URL` | PocketBase | | `NEXT_PUBLIC_MIROTALK_URL` | MiroTalk | | `NEXT_PUBLIC_LOUNGE_URL` | The Lounge | | `NEXT_PUBLIC_MINIO_PUBLIC_URL` | MinIO 公开读 | | `DEV_SSH_*` | dev-tunnel 用(勿提交密码) | ### 后端(`backend/.env`) | 变量 | 说明 | |------|------| | `POCKETBASE_URL` | 内网或公网 PB | | `MIROTALK_URL` / `LOUNGE_URL` | 活动间 URL 生成 | | `MINIO_*` | 对象存储 | | `DEV_AUTO_PAY` | 开发自动完成支付 | 完整列表见 `.env.example` 与 `README.md`。 --- ## 11. AI 编码约定 ### 必须遵守 1. **最小改动**:只改任务相关文件 2. **匹配现有风格**:`@/` import、组件结构一致 3. **国际化**:UI 文案走 `messages/*.json` + `t()` 4. **路由**:`app/[locale]/` + `@/i18n/navigation` 的 `Link` 5. **API**:主站用 `app/lib/api-client.ts` 的 `apiFetch` 6. **活动**:复用 `app/lib/meetups.ts` + `backend/meetup_live.py` 7. **媒体**:复用 `app/lib/media.ts` 8. **支付**:复用 `app/lib/payment` + `backend/payment.py` 9. **不提交密钥** ### 文件选择决策树 ``` 新功能属于 Digital? ├── 是 → app/[locale]/digital/ + app/digital/ + config/digital/ └── 否 → app/[locale]/ + app/components/ + config/ 需要新 API? ├── Digital → app/api/digital/ ├── 媒体代理 → app/api/media/ └── 主站业务 → backend/main.py 线上活动? ├── 列表/弹窗 → meetups/page.tsx ├── 活动间 → meetups/[id]/live + EventLiveRoom.tsx └── URL/权限 → meetups.ts + meetup_live.py + /session API ``` ### Python 约定 - 请求体用 Pydantic `BaseModel` - PocketBase 走 `pb_client.py` - 新端点加 `main.py`,配置加 `settings.py` --- ## 12. 生产部署架构 | 域名 | 上游 | 服务 | |------|------|------| | nomadro.cn | :4002 | Next.js(cnomadcna.service) | | api.nomadro.cn | :8000 | FastAPI | | pocketbase.nomadro.cn | :8090 | PocketBase | | minio.nomadro.cn | :9100/9101 | MinIO | | mirotalk.nomadro.cn | :9300 | MiroTalk P2P | | lounge.nomadro.cn | :9000 | The Lounge | | ergo.nomadro.cn | HTTPS 重定向 | → lounge(IRC 仅本机 :6667) | VPS:`82.157.112.245`,代码目录 `/home/ubuntu/gitlab-instance-0a899031_cnomadcna`。 上线前端: ```bash APP_ENV=production NEXT_PUBLIC_APP_ENV=production NODE_ENV=production pnpm build systemctl restart cnomadcna.service ``` 活动间相关改动需同步部署 **Next.js + FastAPI**,VPS 上 MiroTalk/Lounge 配置变更需 `systemctl restart`。 --- ## 13. 常见陷阱 | 问题 | 说明 | |------|------| | dev 端口 | `pnpm dev` → **3001**,不是 3000 | | API 分流 | `/api/digital/*`、`/api/media/*` 不走 FastAPI | | `/api/videos` | 独立路由;`/api/content/videos` 会 404 | | 8000 多进程 | 多个 uvicorn 占端口时 session 等新路由可能不生效 | | PowerShell | 不支持 `&&`,分步执行 | | 双套 lib | `app/lib/` vs `app/digital/lib/` 各自独立 | | MiroTalk embed | `ALLOWED_EMBED_ORIGINS` 需含 nomadro.cn 与 localhost:3001 | | Ergo 公网 | 不开放 IRC 6697;仅 The Lounge 内网连 Ergo | | 密钥 | `.env.example` 仅示例,生产用独立密钥 | --- ## 14. 关键文件索引 | 场景 | 文件 | |------|------| | 新增主站页面 | `app/[locale]/`、`messages/*.json` | | 活动间 | `meetups/[id]/live/page.tsx`、`EventLiveRoom.tsx`、`meetup_live.py` | | 活动 URL/权限 | `app/lib/meetups.ts` | | 访谈视频 | `videos/page.tsx`、`GET /api/videos` | | 社区匹配 | `dating/page.tsx`、`dating/components/*` | | 调用后端 | `app/lib/api-client.ts` | | 封面/媒体 URL | `app/lib/media.ts` | | 头像池 | `app/data/avatars.ts`、`backend/avatar_assets.py` | | 远程开发 | `config/remote-services.ts`、`scripts/dev-tunnel.py` | | API 代理 | `next.config.ts` | | 后端入口 | `backend/main.py` | | DB 初始化 | `backend/init_db.py`、`seed_data.py` | | 完整运维 | `README.md` | --- ## 15. 相关文档 - `README.md` — 业务介绍、部署、MiroTalk/Lounge 运维、Windows 本地开发 - `.env.example` — 环境变量模板 - `app/lib/charts/README.md` — 图表组件 --- *本文件供 Cursor AI 编辑 NomadCNA 时快速建立上下文。重大架构变更后请同步更新。*