This commit is contained in:
root
2026-06-07 23:18:53 +08:00
parent ebd3d6a317
commit 934b9a4a23
36 changed files with 2272 additions and 296 deletions

642
README.md
View File

@@ -1,110 +1,618 @@
# 游牧中国 NomadCNA
数字游民生活操作系统 (Nomad OS) - 你的下一站,由数据决定
NomadCNA 是面向数字游民生活操作系统,用一个站点承载城市决策、会员加入、线上/线下活动、视频会议、Web 群聊、内容资源、任务赏金、支付和媒体存储等能力。
## 项目简介
生产站点:
NomadCNA 是一个面向数字游民的站式生活平台,提供城市决策、税务签证、智能匹配、赏金任务等功能。从 Free 到 Pro开启你的游牧人生。
- 主站https://nomadro.cn
- APIhttps://api.nomadro.cn
- PocketBasehttps://pocketbase.nomadro.cn
- MinIOhttps://minio.nomadro.cn
- MiroTalk P2P 视频房https://mirotalk.nomadro.cn
- Web 群聊https://lounge.nomadro.cn
- IRC 服务名https://ergo.nomadro.cn
## 业务介绍
NomadCNA 的目标用户是正在中国或亚洲范围内长期旅居、远程工作、寻找社群和机会的数字游民。业务上分为以下几条主线:
| 模块 | 说明 |
| --- | --- |
| 城市探索 | 展示中国数字游民城市数据,包括生活成本、网络、温度、社群密度、安全、文化等指标。 |
| 下一站决策 | 根据预算、网络、气候、标签偏好生成城市推荐。 |
| 线上/线下活动 | 支持公开、登录成员、VIP、主持人等权限层级线上和混合活动可进入 MiroTalk 视频房。 |
| Web 群聊 | 使用 The Lounge + Ergo IRC Server 提供数字游民 Web 群组聊天,活动参与人可进入群聊。 |
| 会员加入与支付 | 支持加入表单、支付订单、支付状态轮询、VIP/会员写入。 |
| Digital 子站 | `/digital` 是内容专题站,包含数字游民指南、课程、电子书、资源、工作机会、个人资料等。 |
| 媒体与内容 | 支持图片/视频上传、远程媒体代理缓存、内容投稿、博客、电子书、视频等内容页面。 |
| 社区与增长 | 包含讨论、交友匹配、任务赏金、反馈、通知、站内消息、服务线索。 |
## 技术栈
- **前端框架**: Next.js 16 (App Router)
- **UI 框架**: React 19 + Tailwind CSS 4
- **样式**: Tailwind CSS
- **字体**: Geist (Vercel)
- **数据库**: PocketBase
- **对象存储**: MinIO
- **语言**: TypeScript
| 层级 | 技术 |
| --- | --- |
| Web 前端 | Next.js 16 App Router、React 19、TypeScript、Tailwind CSS 4 |
| API | Next.js Route Handlers、FastAPI、PocketBase SDK/HTTP |
| 数据 | PocketBase、静态数据、MinIO 对象存储 |
| 支付 | ZPay / XorPay 适配层 |
| 视频会议 | MiroTalk P2PNginx HTTPS 反代 |
| Web 群聊 | The Lounge 4.5.0 + Ergo IRC Server 2.18.0 |
| 部署 | systemd、Nginx、Certbot、Docker |
| 包管理 | pnpm 10.25.0 |
## 功能特性
生产服务器当前 Node.js 版本为 `v22.13.1`
- 🌏 **城市探索** - 发现最适合数字游民的中国城市
- 📍 **城市数据** - 成本、生活质量、网络速度等数据对比
- 🎯 **智能匹配** - 根据个人偏好推荐适合的城市
- 🤝 **社区聚会** - 与各地游民建立联系
- 💬 **社区互动** - 论坛、话题讨论
- 🛠️ **实用工具** - 汇率、天气等实用工具
- 🌙 **暗色模式** - 支持亮色/暗色主题切换
- 🌐 **国际化** - 支持中文/英文
## 源码解析
## 快速开始
### 顶层结构
```text
.
├── app/ # Next.js App Router 主目录
│ ├── [locale]/ # 中文/英文路由,默认 zh
│ ├── api/ # Next.js Route Handlers
│ ├── components/ # 主站通用组件
│ ├── context/ # Locale、Theme 等上下文
│ ├── data/ # 城市、地图、mock 数据
│ ├── digital/ # Digital 子站组件、配置、数据和工具
│ └── lib/ # API、支付、媒体、活动、PocketBase 等工具
├── backend/ # FastAPI 后端
├── config/ # 主站和 Digital 子站配置
├── i18n/ # 路由和国际化导航封装
├── messages/ # zh/en 文案
├── public/ # 静态资源
├── cnomadcna.sh # 生产 Next.js 启动脚本
├── next.config.ts # Next.js 配置与 API rewrites
├── package.json # 前端依赖与脚本
└── tsconfig.json # TypeScript 配置
```
### Next.js 页面结构
主站所有面向用户的页面位于 `app/[locale]`,路由强制带语言前缀:
- `/zh``/en`:首页与城市探索
- `/zh/dashboard`:下一站决策仪表盘
- `/zh/meetups`:线上/线下活动,含 MiroTalk 视频房和 Web 群聊入口
- `/zh/meetups/host`:活动发布页
- `/zh/dating`:社区交友/匹配
- `/zh/discuss`:讨论
- `/zh/gigs`:任务赏金
- `/zh/tools`:工具箱
- `/zh/report``/zh/report/data`:统计与年报
- `/zh/digital`Digital 子站入口
通用布局与组件:
- `app/layout.tsx`:根布局
- `app/[locale]/layout.tsx`:语言布局
- `app/components/Navbar.tsx``Footer.tsx``Sidebar.tsx`:主站导航与外壳
- `app/context/LocaleContext.tsx`:运行时文案读取
- `app/context/ThemeContext.tsx`:亮色/暗色主题
### Digital 子站
Digital 子站代码集中在 `app/digital``app/[locale]/digital`
- `app/[locale]/digital/page.tsx`:专题首页
- `app/[locale]/digital/course`:课程页
- `app/[locale]/digital/course/[moduleIndex]/[lessonIndex]`:课程章节
- `app/[locale]/digital/ebook`:电子书
- `app/[locale]/digital/jobs`:远程工作
- `app/[locale]/digital/join`:加入/申请
- `app/[locale]/digital/profile`:用户资料
配置在 `config/digital`
- `site.config.ts`:专题主题、功能开关、课程/电子书/加入等能力
- `services.config.ts`PocketBase、支付、MinIO 配置读取
- `domain.config.ts`:域名和服务地址推导
- `course.ts``lessons.ts`:课程数据
### API 分层
本项目有两层 API
1. Next.js 内置 API`app/api/*`
2. FastAPI 后端:`backend/main.py`
`next.config.ts` 将大多数 `/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
### Next.js API
| 路径 | 作用 |
| --- | --- |
| `app/api/digital/auth/*` | Digital 子站登录、退出、当前用户、注册、跨站同步 |
| `app/api/digital/join` | Digital 加入/申请表单写入 PocketBase |
| `app/api/digital/pay/*` | Digital 支付创建、确认、状态查询 |
| `app/api/digital/upload-media` | 媒体上传到 MinIO |
| `app/api/digital/vip/check` | VIP 状态检查 |
| `app/api/media/proxy` | 远程媒体抓取、校验、缓存到 MinIO 后跳转公开 URL |
### FastAPI 后端
FastAPI 入口为 `backend/main.py`,配置读取在 `backend/settings.py`。核心能力:
| API | 作用 |
| --- | --- |
| `/api/health` | 健康检查 |
| `/api/auth/email``/api/auth/google``/api/auth/me` | 主站认证 |
| `/api/join``/api/join/status` | 加入表单与状态 |
| `/api/upload-media` | 后端媒体上传 |
| `/api/pay``/api/pay/status` | 支付创建与查询 |
| `/cnomadcna/*_notify` | 支付网关回调 |
| `/api/cities``/api/cities/{slug}` | 城市数据 |
| `/api/content/*` | 首页内容、内容投稿、内容详情 |
| `/api/notifications/*` | 通知、回执、偏好 |
| `/api/recommendations/next-stop` | 下一站推荐 |
| `/api/meetups` | 活动列表与活动创建 |
| `/api/discussions` | 讨论列表与创建 |
| `/api/gigs` | 任务赏金列表、创建、申请 |
| `/api/profiles``/api/matches/swipes` | 资料与匹配 |
| `/api/feedback` | 用户反馈 |
| `/api/stats/*` | 成员地图、概览、城市排行 |
| `/api/services``/api/services/leads` | 增值服务与线索 |
| `/api/ai/assistant` | AI 助手占位接口 |
后端辅助模块:
- `backend/pb_client.py`PocketBase HTTP 客户端
- `backend/payment.py`ZPay / XorPay 支付适配
- `backend/minio_storage.py`MinIO 上传
- `backend/init_db.py``seed_data.py``enhanced_seed.py`:初始化与种子数据
- `backend/settings.py`:环境变量读取
### 活动、视频与群聊源码
活动相关逻辑主要在:
- `app/[locale]/meetups/page.tsx`
- `app/[locale]/meetups/host/page.tsx`
- `app/lib/meetups.ts`
`app/lib/meetups.ts` 负责:
- MiroTalk 默认地址:`https://mirotalk.nomadro.cn`
- MiroTalk 房间名规范化
- MiroTalk 入会链接生成
- Lounge 默认地址:`https://lounge.nomadro.cn`
- Web 群聊服务标签:`The Lounge + Ergo IRC`
线上/混合活动详情页会展示:
- MiroTalk P2P 视频入口
- 页面内 iframe 打开视频房
- The Lounge Web 群聊入口
- 信令/中继服务器和群聊入口域名
### 配置与国际化
| 文件 | 作用 |
| --- | --- |
| `config/site.config.ts` | 主站 SEO 与站点基础信息 |
| `config/nav.config.ts` | 顶部导航 |
| `config/footer.config.ts` | 页脚 |
| `config/services.config.ts` | 主站支付配置 |
| `config/runtime-env.ts` | 运行环境判断 |
| `config/digital/*` | Digital 子站主题、服务、课程、域名配置 |
| `messages/zh.json``messages/en.json` | 中英文文案 |
| `i18n/routing.ts` | 语言列表与默认语言 |
| `i18n/navigation.ts` | 语言路由 Link / Router 封装 |
## 本地开发
### 环境要求
- Node.js 18+
- npm / yarn / pnpm / bun
- Node.js 20+,生产当前使用 Node.js 22.13.1
- pnpm 10+
- Python 3.12+
- PocketBase
- 可选MinIO、ZPay/XorPay 测试配置
### 安装依赖
```bash
npm install
pnpm install
```
### 开发模式
### 前端开发
```bash
npm run dev
pnpm dev
```
打开 [http://localhost:3000](http://localhost:3000) 查看项目。
默认启动在:
### 生产构建
```text
http://localhost:3001
```
### FastAPI 后端开发
```bash
npm run build
npm start
python3 -m venv backend/.venv
source backend/.venv/bin/activate
pip install -r backend/requirements.txt
uvicorn backend.main:app --host 127.0.0.1 --port 8000 --reload
```
## 项目结构
### 构建与启动
```
├── app/ # Next.js App Router
│ ├── components/ # React 组件
│ ├── context/ # React Context (主题等)
│ ├── lib/ # 工具库
│ └── [locale]/ # 国际化路由
├── config/ # 配置文件
│ ├── site.config.ts # 站点配置
│ ├── nav.config.ts # 导航配置
│ ├── footer.config.ts # 页脚配置
│ └── ...
├── public/ # 静态资源
└── i18n/ # 国际化配置
```bash
pnpm build
pnpm start
```
## 主要页面
### 常用检查
- `/` - 首页 (城市探索)
- `/about` - 关于我们
- `/meetups` - 聚会活动
- `/dating` - 社区交友
- `/tools` - 实用工具
- `/ai` - AI 助手
- `/gigs` - 赏金任务
- `/pricing` - 会员价格
- `/join` - 加入社区
```bash
pnpm lint
pnpm exec tsc --noEmit --pretty false
APP_ENV=production NEXT_PUBLIC_APP_ENV=production NODE_ENV=production pnpm build
```
## 配置
## 环境变量
### 主题配置
不要把真实密钥提交到 Git。`.env.example` 只放示例值,生产值放 `.env.local``backend/.env` 或 systemd Environment。
主题相关逻辑位于 `app/context/ThemeContext.tsx`,支持:
- 手动切换亮色/暗色模式
- 记住用户偏好 (localStorage)
- 跟随系统设置
### 前端公开变量
### 导航配置
| 变量 | 说明 |
| --- | --- |
| `APP_ENV` | `development` / `production` |
| `NEXT_PUBLIC_APP_ENV` | 浏览器可见运行环境 |
| `NEXT_PUBLIC_SITE_URL` | 站点根地址 |
| `NEXT_PUBLIC_SITE_ID` | 站点 ID用于会员/VIP 隔离 |
| `NEXT_PUBLIC_API_BASE_URL` | API 基础地址,生产通常为 `/api` |
| `NEXT_PUBLIC_POCKETBASE_URL` | 浏览器可访问 PocketBase 地址 |
| `NEXT_PUBLIC_MIROTALK_URL` | MiroTalk P2P 服务地址 |
| `NEXT_PUBLIC_LOUNGE_URL` | The Lounge Web 群聊地址 |
| `NEXT_PUBLIC_MINIO_PUBLIC_URL` | MinIO 文件公开地址 |
导航链接位于 `config/nav.config.ts`,可自定义修改。
### 后端和服务端变量
### 页脚配置
| 变量 | 说明 |
| --- | --- |
| `POCKETBASE_URL` | 服务端访问 PocketBase 的内网地址 |
| `POCKETBASE_EMAIL` | PocketBase 管理员邮箱 |
| `POCKETBASE_PASSWORD` | PocketBase 管理员密码 |
| `PAYMENT_API_URL` | 支付 API 地址 |
| `PAYMENT_PROVIDER` | `zpay``xorpay` |
| `PAYMENT_DEFAULT_AMOUNT` | 默认支付金额 |
| `PAYMENT_JOIN_AMOUNT` | 加入场景金额 |
| `PAYMENT_JOIN_TYPE` | 加入场景订单类型 |
| `ZPAY_PID``ZPAY_KEY` | ZPay 商户配置 |
| `XORPAY_AID``XORPAY_SECRET` | XorPay 商户配置 |
| `MINIO_ENDPOINT` | MinIO 内网端点 |
| `MINIO_PORT` | MinIO 端口 |
| `MINIO_USE_SSL` | 是否对 MinIO 内网连接启用 SSL |
| `MINIO_BUCKET` | 存储桶 |
| `MINIO_ACCESS_KEY` | MinIO Access Key |
| `MINIO_SECRET_KEY` | MinIO Secret Key |
| `MINIO_REGION` | MinIO 区域 |
| `MINIO_UPLOAD_PREFIX` | 上传路径前缀 |
| `FRONTEND_ORIGINS` | FastAPI CORS 白名单 |
| `UPLOAD_DIR` | 后端本地上传目录 |
页脚链接位于 `config/footer.config.ts`,可自定义修改。
## 生产服务器支持
## 贡献指南
生产代码目录:
欢迎提交 Pull Request 和 Issue
```text
/home/ubuntu/gitlab-instance-0a899031_cnomadcna
```
## 许可证
### 域名与服务映射
MIT License
| 域名 | 上游 | 说明 |
| --- | --- | --- |
| `nomadro.cn` / `www.nomadro.cn` | `127.0.0.1:4002` | Next.js 主站,`cnomadcna.service` |
| `api.nomadro.cn` | `127.0.0.1:8000` | FastAPI |
| `pocketbase.nomadro.cn` | `127.0.0.1:8090` | PocketBase |
| `minio.nomadro.cn` | `127.0.0.1:9100` / `9101` | MinIO API / Console |
| `mirotalk.nomadro.cn` | `127.0.0.1:9300` | MiroTalk P2P |
| `lounge.nomadro.cn` | `127.0.0.1:9000` | The Lounge Web IRC |
| `ergo.nomadro.cn` | HTTPS redirect | 跳转到 `lounge.nomadro.cn`IRC 后端只内网监听 |
| `ntfy.nomadro.cn` | `127.0.0.1:9180` | ntfy 通知服务 |
### systemd 服务
| 服务 | 作用 | 当前策略 |
| --- | --- | --- |
| `cnomadcna.service` | Next.js 生产服务 | enabled监听 `0.0.0.0:4002` |
| `mirotalk-p2p.service` | MiroTalk P2P | enabled监听 `127.0.0.1:9300` |
| `ergo.service` | Ergo IRC Server | enabled监听 `127.0.0.1:6667``[::1]:6667` |
| `thelounge.service` | The Lounge Web IRC | enabled监听 `127.0.0.1:9000` |
常用命令:
```bash
systemctl status cnomadcna.service
systemctl status mirotalk-p2p.service
systemctl status ergo.service
systemctl status thelounge.service
journalctl -u cnomadcna.service -n 100 --no-pager
journalctl -u mirotalk-p2p.service -n 100 --no-pager
journalctl -u ergo.service -n 100 --no-pager
journalctl -u thelounge.service -n 100 --no-pager
```
### Docker 服务
当前生产容器:
| 容器 | 镜像 | 端口 |
| --- | --- | --- |
| `nomadro-minio` | `minio/minio:latest` | `127.0.0.1:9100->9000``127.0.0.1:9101->9001` |
| `nomadro-ntfy` | `binwiederhier/ntfy:latest` | `127.0.0.1:9180->80` |
检查:
```bash
docker ps
docker logs nomadro-minio --tail 100
docker logs nomadro-ntfy --tail 100
```
### Nginx
Nginx vhost 目录:
```text
/www/server/panel/vhost/nginx
```
关键配置:
- `www.nomadro.cn.conf`:主站
- `api.nomadro.cn.conf`FastAPI
- `mirotalk.nomadro.cn.conf`MiroTalk
- `lounge.nomadro.cn.conf`The Lounge + Ergo redirect
- `minio.nomadro.cn.conf`MinIO
- `pocketbase.nomadro.cn.conf`PocketBase
常用命令:
```bash
nginx -t
nginx -s reload
```
### HTTPS 证书
证书由 Certbot 管理。`lounge.nomadro.cn` 证书包含两个域名:
```text
lounge.nomadro.cn
ergo.nomadro.cn
```
当前证书路径:
```text
/etc/letsencrypt/live/lounge.nomadro.cn/fullchain.pem
/etc/letsencrypt/live/lounge.nomadro.cn/privkey.pem
```
检查:
```bash
certbot certificates
```
### MiroTalk P2P
安装目录:
```text
/opt/nomadro-services/mirotalk
```
运行方式:
- systemd`mirotalk-p2p.service`
- 内网端口:`127.0.0.1:9300`
- 公网入口:`https://mirotalk.nomadro.cn`
- 活动页自动生成链接:`https://mirotalk.nomadro.cn/join?room=...&name=...`
健康检查:
```bash
curl -I https://mirotalk.nomadro.cn/join?room=nomadcna-healthcheck
```
### Ergo IRC Server
安装目录:
```text
/opt/ergo
```
配置:
```text
/etc/ergo/ircd.yaml
/etc/ergo/ergo.motd
/var/lib/ergo/ircd.db
```
当前策略:
- 网络名:`NomadCNA`
- 服务名:`ergo.nomadro.cn`
- 只监听本机 `127.0.0.1:6667``[::1]:6667`
- 不开放公网 IRC TLS `6697`
- `ergo.nomadro.cn` 的 HTTPS 请求跳转到 The Lounge
本机 IRC 握手检查:
```bash
{ printf 'NICK health\r\nUSER health 0 * :NomadCNA Health\r\nJOIN #nomadcna\r\nQUIT :healthcheck\r\n'; sleep 1; } \
| timeout 5s nc 127.0.0.1 6667
```
### The Lounge Web 群聊
安装目录:
```text
/opt/nomadro-services/thelounge
```
配置:
```text
/var/lib/thelounge/config.js
```
当前策略:
- public mode
- 只监听 `127.0.0.1:9000`
- Nginx 通过 `https://lounge.nomadro.cn` 反代
- 默认网络锁定到本机 Ergo`127.0.0.1:6667`
- 默认加入频道:`#nomadcna,#online-events`
- 文件上传关闭
- The Lounge 自身消息持久化关闭Ergo 保留内存历史
健康检查:
```bash
curl -I https://lounge.nomadro.cn/
curl 'https://lounge.nomadro.cn/socket.io/?EIO=4&transport=polling&t=health'
```
## 上线流程
### 前端上线
```bash
cd /home/ubuntu/gitlab-instance-0a899031_cnomadcna
APP_ENV=production NEXT_PUBLIC_APP_ENV=production NODE_ENV=production pnpm build
systemctl restart cnomadcna.service
systemctl status cnomadcna.service --no-pager
```
### 后端上线
FastAPI 当前运行在 `127.0.0.1:8000`。更新后需重启对应 uvicorn 进程或其 systemd 服务。如果后续为 FastAPI 增加 systemd 单元,建议命名为:
```text
cnomadcna-api.service
```
### Nginx 配置变更
```bash
nginx -t
nginx -s reload
```
### 服务健康检查
```bash
curl -I https://nomadro.cn/zh/meetups
curl -I https://api.nomadro.cn/api/health
curl -I https://mirotalk.nomadro.cn/join?room=nomadcna-healthcheck
curl -I https://lounge.nomadro.cn/
curl -I https://ergo.nomadro.cn/
```
## 数据与备份
建议纳入备份的内容:
| 路径/服务 | 说明 |
| --- | --- |
| `.env.local``backend/.env` | 生产环境变量和密钥 |
| PocketBase 数据目录 | 用户、会员、订单、内容、活动等业务数据 |
| MinIO 数据卷 | 上传媒体、远程媒体缓存 |
| `/var/lib/ergo` | Ergo IRC 数据库 |
| `/var/lib/thelounge` | The Lounge 配置、VAPID key、包配置 |
| `/opt/nomadro-services/mirotalk` | MiroTalk 源码部署目录 |
| `/www/server/panel/vhost/nginx/*.conf` | Nginx vhost 配置 |
| `/etc/systemd/system/*.service` | systemd 单元 |
## 安全注意事项
- README 不应包含真实密钥、管理员密码、支付密钥或 MinIO Secret。
- `.env.local``backend/.env` 不应提交到 Git。
- The Lounge 当前为 public mode适合活动公开群聊如需长期身份和历史可切换 private mode 并创建用户账号。
- Ergo 原生 IRC 公网端口当前不开放,这是有意设计;需要原生 IRC 客户端时再开放 `6697` 并配置 TLS。
- 内部服务优先绑定 `127.0.0.1`,统一通过 Nginx HTTPS 暴露。
- 修改 Nginx 前必须先 `nginx -t`
- 更新证书后确认 SAN 包含对应域名。
## 开发约定
- 新页面优先放入 `app/[locale]`,并补充 `messages/zh.json``messages/en.json`
- 需要语言感知链接时使用 `i18n/navigation.ts` 导出的 `Link`
- 调用后端 API 使用 `app/lib/api-client.ts`
- 主站通用配置放 `config/*`Digital 子站配置放 `config/digital/*`
- 媒体上传和远程媒体代理优先走 MinIO。
- 支付相关逻辑优先复用 `app/lib/payment``backend/payment.py``config/*/services.config.ts`
- 线上活动相关逻辑优先复用 `app/lib/meetups.ts`
## 常见问题
### 为什么 `ergo.nomadro.cn` 不直接打开 IRC
`ergo.nomadro.cn` 是 IRC 服务名,但原生 IRC 端口没有对公网开放。用户从浏览器进入 `lounge.nomadro.cn`The Lounge 在服务器本机连接 Ergo。这样能减少公网攻击面同时满足 Web 群聊需求。
### 为什么 `/api/digital/*` 没有转发到 FastAPI
`next.config.ts` 明确排除了 `digital``media` 两类 API它们由 Next.js Route Handlers 直接处理。其他 `/api/*` 才会转发到 FastAPI。
### 活动视频房如何生成?
`app/lib/meetups.ts` 会根据活动城市、日期、时间和 ID 生成 MiroTalk room slug。如果活动有自定义 `meetingUrl`,则优先使用自定义链接。
### 群聊频道如何进入?
The Lounge public mode 的默认网络已锁定到 Ergo并默认加入 `#nomadcna``#online-events`。活动详情页的“进入群聊”按钮指向 `https://lounge.nomadro.cn`
## 维护命令速查
```bash
# 构建并重启主站
APP_ENV=production NEXT_PUBLIC_APP_ENV=production NODE_ENV=production pnpm build
systemctl restart cnomadcna.service
# 查看服务
systemctl status cnomadcna.service --no-pager
systemctl status mirotalk-p2p.service --no-pager
systemctl status ergo.service --no-pager
systemctl status thelounge.service --no-pager
# 查看日志
journalctl -u cnomadcna.service -n 100 --no-pager
journalctl -u mirotalk-p2p.service -n 100 --no-pager
journalctl -u ergo.service -n 100 --no-pager
journalctl -u thelounge.service -n 100 --no-pager
# Nginx
nginx -t
nginx -s reload
# 端口
ss -ltnp | rg ':4002|:8000|:8090|:9000|:9300|:9100|:9101|:6667|:6697'
# Docker
docker ps
# HTTPS
certbot certificates
```