dbcbdbb59e
- 处理: 74 个 .md 文件 - 跳过: 0 个(无已存在的 front matter) - 异常: 3 个(H1 缺失,用文件名兜底) - plans/PRISM/.research/readmes/3d-llm.md - plans/PRISM/.research/readmes/openmask3d.md - plans/PRISM/.research/readmes/openscene.md
1088 lines
67 KiB
Markdown
1088 lines
67 KiB
Markdown
---
|
||
title: "CrowdRoom · 基于 GitHub 成熟开源项目的工程执行方案"
|
||
date: 2026-05-20
|
||
draft: false
|
||
tags: ["CrowdRoom", "众包", "3D 重建", "隐私", "API", "iOS"]
|
||
categories: ["worldmodel"]
|
||
---
|
||
|
||
# CrowdRoom · 基于 GitHub 成熟开源项目的工程执行方案
|
||
|
||
> **文档版本**:v0.1 · 2026-05-19
|
||
> **承接设计**:[`README.md`](README.md) / [`00_overview.md`](00_overview.md) / [`01_data_schema.md`](01_data_schema.md) / [`02_api_contract.md`](02_api_contract.md) / [`03_ios_app_plan.md`](03_ios_app_plan.md) / [`04_web_app_plan.md`](04_web_app_plan.md) / [`09_privacy.md`](09_privacy.md) / [`10_governance.md`](10_governance.md) / [`ROADMAP.md`](ROADMAP.md)
|
||
> **文档目的**:把已锁定的 v0.2 设计「翻译」为一份**只用成熟开源项目**搭建的工程执行方案,新工程师拿到本文档可以从 W1 第一条命令开始落地直至 M1 MVP 上线。
|
||
|
||
---
|
||
|
||
## 1. 执行方案总览
|
||
|
||
### 1.1 一句话定义
|
||
|
||
> 本方案承诺:**全部使用 ≥ 1k star、Apache-2.0 / MIT / BSD 类宽松 license、近 6 个月有活跃 commit 的开源项目**构建 CrowdRoom——拒绝任何商业 SaaS 锁定(Matterport / Sketchfab 商业 API / Adobe Stock),所有云依赖均提供**自托管 / 多云迁移**的逃生通道,确保 8 周内可启动 M1 MVP 并在云厂商变脸时 2 周内迁移到自建栈。
|
||
|
||
### 1.2 五条工程原则
|
||
|
||
1. **开源优先 + 自托管兜底**:每个云服务必须存在「同名开源项目自托管版」(例如 Supabase Cloud ↔ [`supabase/supabase`](https://github.com/supabase/supabase) 自托管)。
|
||
2. **monorepo + workspace**:iOS / Web / Worker / Schemas 同仓共版本,避免「契约漂移」。
|
||
3. **TypeScript 一统服务端**:Edge Functions(Deno)+ Worker(Node)+ Web(Next.js)共享同一份 [`packages/shared-types/`](packages/shared-types/) 的 Zod schema。
|
||
4. **CDN-first**:所有公开资产走 Cloudflare R2 + CDN,CrowdRoom 服务端永不承担静态分发流量。
|
||
5. **可测试性即合规**:CI 不绿不许合并;端到端测试覆盖 [`02_api_contract.md`](02_api_contract.md) §8 的 11 条契约(X1-X5 + Y1-Y6)。
|
||
|
||
### 1.3 总体技术拓扑(每节点标注 GitHub repo)
|
||
|
||
```mermaid
|
||
graph TB
|
||
subgraph CLIENT[客户端层]
|
||
IOS[iOS App<br/>apple/swift + supabase-community/supabase-swift]
|
||
WEB[Web App<br/>vercel/next.js + pmndrs/react-three-fiber]
|
||
end
|
||
|
||
subgraph BAAS[Supabase BaaS]
|
||
AUTH[Auth<br/>supabase/gotrue]
|
||
DB[(Postgres 15<br/>supabase/postgres)]
|
||
STG[Storage<br/>supabase/storage-api]
|
||
EDGE[Edge Functions<br/>denoland/deno]
|
||
RT[Realtime<br/>supabase/realtime]
|
||
end
|
||
|
||
subgraph WORKER[Transcode Worker]
|
||
QUEUE[BullMQ<br/>taskforcesh/bullmq]
|
||
TRANS[gltf-transform<br/>donmccurdy/glTF-Transform]
|
||
USD[Apple usdzconvert<br/>via macOS runner]
|
||
SHOT[Headless render<br/>microsoft/playwright]
|
||
SHARP[Thumbnails<br/>lovell/sharp]
|
||
end
|
||
|
||
subgraph MOD[审核 / 治理]
|
||
NSFW[infinitered/nsfwjs]
|
||
SENS[houbb/sensitive-word]
|
||
end
|
||
|
||
subgraph CDN_DEPLOY[CDN + 部署]
|
||
R2[Cloudflare R2<br/>S3 兼容]
|
||
VERCEL[Vercel<br/>Next.js host]
|
||
ESCAPE[Escape: minio/minio + caddyserver/caddy]
|
||
end
|
||
|
||
subgraph OPS[DevOps / 监控]
|
||
CI[GitHub Actions]
|
||
SENTRY[getsentry/sentry]
|
||
POSTHOG[PostHog/posthog]
|
||
TURBO[vercel/turborepo]
|
||
end
|
||
|
||
IOS --> EDGE
|
||
IOS --> STG
|
||
WEB --> EDGE
|
||
WEB --> R2
|
||
EDGE --> QUEUE
|
||
QUEUE --> TRANS
|
||
TRANS --> USD
|
||
TRANS --> SHOT
|
||
TRANS --> SHARP
|
||
EDGE --> NSFW
|
||
EDGE --> SENS
|
||
STG --> R2
|
||
WEB -.deploy.- VERCEL
|
||
OPS -.- CI
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 开源依赖选型清单(核心章节,按层组织)
|
||
|
||
> **图例**:星数采用粗略量级(≥ 1k / ≥ 10k / ≥ 50k);license 列若标 `Apache-2.0/MIT` 表示主体 + 文档采用宽松双协议;「最近 commit」均确认 ≤ 6 个月内活跃。
|
||
|
||
### 2.1 iOS App 层(10 个 repo)
|
||
|
||
| 组件 | GitHub repo | Star | License | 最近 commit | 选型理由 | 备选方案 |
|
||
|---|---|---|---|---|---|---|
|
||
| Supabase Swift SDK | [`supabase-community/supabase-swift`](https://github.com/supabase-community/supabase-swift) | ≥ 1k | MIT | < 1 月 | 官方维护,覆盖 Auth / DB / Storage / Realtime / Functions 5 件套;与 Edge Function 鉴权同源 | 自己用 URLSession 包 PostgREST(不推荐) |
|
||
| HTTP 库(对比) | [`Alamofire/Alamofire`](https://github.com/Alamofire/Alamofire) | ≥ 40k | MIT | < 1 月 | 经典 HTTP 库;supabase-swift 已内置 URLSession,**最终决策不引入** | 系统 URLSession |
|
||
| 崩溃 / 性能监控 | [`getsentry/sentry-cocoa`](https://github.com/getsentry/sentry-cocoa) | ≥ 1k | MIT | < 1 月 | 与 Web 端、Worker 共用 Sentry 项目,跨端 traceId 联查 | Bugsnag(闭源化风险) |
|
||
| 产品分析 | [`PostHog/posthog-ios`](https://github.com/PostHog/posthog-ios) | ≥ 1k | MIT | < 1 月 | 与 Web 端共享 funnel 定义;自托管可选 | Mixpanel(SaaS 锁定,pass) |
|
||
| Async 工具 | [`apple/swift-async-algorithms`](https://github.com/apple/swift-async-algorithms) | ≥ 3k | Apache-2.0 | < 1 月 | `AsyncSequence` 组合子,处理 RoomPlan 增量回调流 | Combine |
|
||
| 代码规范 | [`realm/SwiftLint`](https://github.com/realm/SwiftLint) | ≥ 18k | MIT | < 1 月 | 行业事实标准;CI 拒绝非 lint-clean 提交 | swift-format(规则较少) |
|
||
| 包管理(采用) | Swift Package Manager / [`apple/swift-package-manager`](https://github.com/apple/swift-package-manager) | ≥ 9k | Apache-2.0 | < 1 月 | Xcode 原生支持;**不引入 Carthage / CocoaPods** | – |
|
||
| 包管理(对比) | [`Carthage/Carthage`](https://github.com/Carthage/Carthage) | ≥ 14k | MIT | < 6 月 | 仅作历史方案对比 | – |
|
||
| 持久化 | [`groue/GRDB.swift`](https://github.com/groue/GRDB.swift) | ≥ 6k | MIT | < 1 月 | iOS 本地草稿 / 上传队列 SQLite 封装;崩溃恢复 | Core Data(学习曲线陡) |
|
||
| USD 解析(关键空白) | [`PixarAnimationStudios/OpenUSD`](https://github.com/PixarAnimationStudios/OpenUSD) | ≥ 6k | Apache-2.0 / TOST | < 1 月 | 仅在 macOS CI runner 上用其 Python 绑定做 `.usdz` 解析;iPhone 端**只用 Apple 系统 ModelIO** | Apple `ModelIO`(系统框架) |
|
||
| Apple 系统框架(非 repo) | `RoomPlan` (iOS 16+) / `Vision` (iOS 11+) / `ModelIO` (iOS 9+) / `ARKit` (iOS 11+) | – | Apple SDK | iOS SDK 17.x | RoomPlan 仅 iOS、需 LiDAR;端侧脱敏走 Vision;USDZ 重打包走 ModelIO(仅读) | 无(RoomPlan 无替代) |
|
||
|
||
> **空白点结论**:`.usdz` 在 iPhone 端**只用 ModelIO 读不重打包**——端侧脱敏只对纹理图像做 `CIGaussianBlur`,最终 `.usdz` 重写交给服务端 Worker 的 macOS runner 跑 `usdzconvert`。这与 [`09_privacy.md`](09_privacy.md) §1 PR-1「端侧脱敏不可降级」对齐:**纹理(图像)端侧改,容器(USDZ 二进制)服务端重打**。
|
||
|
||
### 2.2 Web App 层(16 个 repo)
|
||
|
||
| 组件 | GitHub repo | Star | License | 最近 commit | 选型理由 | 备选方案 |
|
||
|---|---|---|---|---|---|---|
|
||
| Web 框架 | [`vercel/next.js`](https://github.com/vercel/next.js) | ≥ 100k | MIT | < 1 周 | App Router + SSR + OG endpoint + Edge runtime 一栈搞定 | [`remix-run/remix`](https://github.com/remix-run/remix) |
|
||
| React Three Fiber | [`pmndrs/react-three-fiber`](https://github.com/pmndrs/react-three-fiber) | ≥ 25k | MIT | < 1 月 | R3F 让「图层切换 / 换家具」用 React 思维表达 | 直接 [`mrdoob/three.js`](https://github.com/mrdoob/three.js) |
|
||
| Drei 工具集 | [`pmndrs/drei`](https://github.com/pmndrs/drei) | ≥ 8k | MIT | < 1 月 | `useGLTF` / `OrbitControls` / `Environment` / `Outline` 全套现成 | – |
|
||
| Three.js 底层 | [`mrdoob/three.js`](https://github.com/mrdoob/three.js) | ≥ 100k | MIT | < 1 周 | 3D 引擎事实标准;KTX2/Meshopt/Draco 内置 loader | [`BabylonJS/Babylon.js`](https://github.com/BabylonJS/Babylon.js) |
|
||
| 状态管理 | [`pmndrs/zustand`](https://github.com/pmndrs/zustand) | ≥ 45k | MIT | < 1 月 | 单 store + `subscribeWithSelector` 对 R3F 性能友好;无 Provider 树 | [`pmndrs/jotai`](https://github.com/pmndrs/jotai) |
|
||
| Tailwind CSS | [`tailwindlabs/tailwindcss`](https://github.com/tailwindlabs/tailwindcss) | ≥ 80k | MIT | < 1 周 | shadcn/ui 强绑定 | UnoCSS |
|
||
| shadcn/ui | [`shadcn-ui/ui`](https://github.com/shadcn-ui/ui) | ≥ 70k | MIT | < 1 周 | Radix UI 封装,A11y 已做掉;可直接 copy 进项目无包锁定 | Mantine |
|
||
| Supabase SSR | [`supabase/auth-helpers`](https://github.com/supabase/auth-helpers)(含 `@supabase/ssr` 子包) | ≥ 1k | MIT | < 1 月 | App Router 官方推荐;cookie 鉴权链路安全 | – |
|
||
| 服务端数据 | [`TanStack/query`](https://github.com/TanStack/query) v5 | ≥ 40k | MIT | < 1 周 | 评论/点赞乐观更新;infinite query 用于瀑布流 | SWR |
|
||
| 图标 | [`lucide-icons/lucide`](https://github.com/lucide-icons/lucide) | ≥ 11k | ISC | < 1 周 | shadcn/ui 默认同款;树摇彻底 | [`tabler/tabler-icons`](https://github.com/tabler/tabler-icons) |
|
||
| 国际化 | [`amannn/next-intl`](https://github.com/amannn/next-intl) | ≥ 3k | MIT | < 1 月 | App Router 友好,按路由段 `/[locale]/...` 切分 | next-i18next |
|
||
| 产品分析 | [`PostHog/posthog-js`](https://github.com/PostHog/posthog-js) | ≥ 1k | MIT | < 1 月 | 与 iOS 端共享同一份 funnel 定义 | Plausible |
|
||
| OG 卡片 | [`vercel/satori`](https://github.com/vercel/satori)(含 `@vercel/og`) | ≥ 10k | MPL-2.0 | < 1 月 | SSR 阶段在 Edge runtime 生成 OG 图(非 3D 截图) | Resvg |
|
||
| 表单 + 校验 | [`react-hook-form/react-hook-form`](https://github.com/react-hook-form/react-hook-form) + [`colinhacks/zod`](https://github.com/colinhacks/zod) | ≥ 39k / ≥ 30k | MIT | < 1 周 | Zod schema 可同时复用到 Edge Function 的入参校验 | yup / valibot |
|
||
| E2E 测试 | [`microsoft/playwright`](https://github.com/microsoft/playwright) | ≥ 60k | Apache-2.0 | < 1 周 | 既做 E2E,又用于 OG 截图 worker;可跑 WebGL | [`cypress-io/cypress`](https://github.com/cypress-io/cypress) |
|
||
| AI 备选位 | [`vercel/ai`](https://github.com/vercel/ai) | ≥ 8k | Apache-2.0 | < 1 月 | **MVP 不引入**;P2 做 AI 配色 / 风格推荐时启用 | – |
|
||
|
||
### 2.3 后端 / BaaS 层(8 个 repo)
|
||
|
||
| 组件 | GitHub repo | Star | License | 最近 commit | 选型理由 | 备选方案 |
|
||
|---|---|---|---|---|---|---|
|
||
| Supabase 主仓 | [`supabase/supabase`](https://github.com/supabase/supabase) | ≥ 65k | Apache-2.0 | < 1 周 | 云 + 自托管同一份代码,**这是云锁定逃生的根本保障** | [`appwrite/appwrite`](https://github.com/appwrite/appwrite) |
|
||
| Auth 服务 | [`supabase/gotrue`](https://github.com/supabase/gotrue) | ≥ 1k | MIT | < 1 月 | Apple / Google / Email OAuth | [`keycloak/keycloak`](https://github.com/keycloak/keycloak) |
|
||
| Storage 服务 | [`supabase/storage-api`](https://github.com/supabase/storage-api) | ≥ 1k | Apache-2.0 | < 1 月 | S3 兼容;可直接换底层为 R2 / MinIO | – |
|
||
| Realtime | [`supabase/realtime`](https://github.com/supabase/realtime) | ≥ 6k | Apache-2.0 | < 1 月 | Postgres LISTEN/NOTIFY + Phoenix Channels;转码进度推送 | socket.io |
|
||
| Postgres 元数据 | [`supabase/postgres-meta`](https://github.com/supabase/postgres-meta) | ≥ 1k | Apache-2.0 | < 1 月 | Studio 后台依赖;Schema 浏览 | – |
|
||
| Edge Functions runtime | [`denoland/deno`](https://github.com/denoland/deno) | ≥ 95k | MIT | < 1 周 | Supabase Edge Functions 底层;TypeScript 一等公民 | Cloudflare Workers |
|
||
| Supabase CLI | [`supabase/cli`](https://github.com/supabase/cli) | ≥ 1k | MIT | < 1 月 | `supabase init / start / db push / functions deploy` 完整链路 | – |
|
||
| ORM(备选) | [`prisma/prisma`](https://github.com/prisma/prisma) | ≥ 38k | Apache-2.0 | < 1 周 | **MVP 不引入**——PostgREST + `supabase-js` 已满足 | – |
|
||
|
||
> **Postgres 全文搜索**:使用原生 `tsvector` + GIN 索引即可([`01_data_schema.md`](01_data_schema.md) §1 D5),**不引入** [`zombodb/zombodb`](https://github.com/zombodb/zombodb)(依赖 ES,运维负担过重)。
|
||
|
||
### 2.4 转码 Worker 层(12 个 repo · 重点章节)
|
||
|
||
| 组件 | GitHub repo | Star | License | 最近 commit | 选型理由 | 备选方案 |
|
||
|---|---|---|---|---|---|---|
|
||
| **glb 操作核心** | [`donmccurdy/glTF-Transform`](https://github.com/donmccurdy/glTF-Transform) | ≥ 1k | MIT | < 1 月 | Worker 的**根基**:节点操作、材质提取、Draco / Meshopt 压缩 | 自写(劝退) |
|
||
| 几何压缩 | [`google/draco`](https://github.com/google/draco) | ≥ 6k | Apache-2.0 | < 1 月 | gltf-transform 内调;50-90% 几何体积压缩 | – |
|
||
| Meshopt | [`zeux/meshoptimizer`](https://github.com/zeux/meshoptimizer) | ≥ 5k | MIT | < 1 月 | 与 Draco 互补:顶点流压缩 + 三角形优化 | – |
|
||
| glTF 校验 | [`KhronosGroup/glTF-Validator`](https://github.com/KhronosGroup/glTF-Validator) | ≥ 1k | Apache-2.0 | < 1 月 | Worker 产出 `canonical.glb` 必须通过该 validator 才能 `transcode-done` | – |
|
||
| 测试样本 | [`KhronosGroup/glTF-Sample-Models`](https://github.com/KhronosGroup/glTF-Sample-Models) | ≥ 2k | 多 CC | < 6 月 | CI 端到端测试夹具 | – |
|
||
| Sample Assets | [`KhronosGroup/glTF-Sample-Assets`](https://github.com/KhronosGroup/glTF-Sample-Assets) | ≥ 1k | 多 CC | < 1 月 | 子集化的资产库 | – |
|
||
| USD 工具链 | [`PixarAnimationStudios/OpenUSD`](https://github.com/PixarAnimationStudios/OpenUSD) | ≥ 6k | Apache-2.0 / TOST | < 1 月 | Linux runner 上跑 USD-Python 解析;Apple `usdzconvert` 不可用时的逃生 | Apple `usdzconvert`(macOS only) |
|
||
| Three.js 服务端参考 | [`donmccurdy/three-gltf-viewer`](https://github.com/donmccurdy/three-gltf-viewer) | ≥ 1k | MIT | < 1 月 | 借鉴其 headless 渲染配置 | – |
|
||
| Headless 渲染 | [`microsoft/playwright`](https://github.com/microsoft/playwright) | ≥ 60k | Apache-2.0 | < 1 周 | 缩略图 / OG 截图;Docker 中跑 WebGL2 | [`puppeteer/puppeteer`](https://github.com/puppeteer/puppeteer) |
|
||
| 图像处理 | [`lovell/sharp`](https://github.com/lovell/sharp) | ≥ 28k | Apache-2.0 | < 1 周 | 缩略图 webp / avif 编码;libvips 底层最快 | imagemagick |
|
||
| 任务队列 | [`taskforcesh/bullmq`](https://github.com/taskforcesh/bullmq) | ≥ 5k | MIT | < 1 周 | Redis 队列 + 失败重试 + 优先级;Edge Function 跑不动时的后端 | [`OptimalBits/bull`](https://github.com/OptimalBits/bull) |
|
||
| 向量索引(P2) | [`pgvector/pgvector`](https://github.com/pgvector/pgvector) | ≥ 10k | PostgreSQL | < 1 月 | **MVP 不启用**;P2 做语义搜索时启用 | Qdrant |
|
||
|
||
### 2.5 内容审核 / 治理层(7 个 repo)
|
||
|
||
| 组件 | GitHub repo | Star | License | 最近 commit | 选型理由 | 备选方案 |
|
||
|---|---|---|---|---|---|---|
|
||
| NSFW 图像分类 | [`infinitered/nsfwjs`](https://github.com/infinitered/nsfwjs) | ≥ 7k | MIT | < 6 月 | TensorFlow.js 模型;MVP 放 Worker(见 §3.5) | OpenNSFW2 (Python only) |
|
||
| 中文敏感词 DFA | [`houbb/sensitive-word`](https://github.com/houbb/sensitive-word) | ≥ 1k | Apache-2.0 | < 1 月 | Java DFA + 完整词库,作为 Node 版的词库源 | – |
|
||
| 中文敏感词 Node 版 | [`qieguo2016/sensi`](https://github.com/qieguo2016/sensi)(或 [`sxei/mint-filter`](https://github.com/sxei/mint-filter)) | ≥ 1k | MIT | < 6 月 | 在 Edge Function (Deno) 中跑 DFA 过滤 | – |
|
||
| 英文脏词 | [`web-mech/badwords`](https://github.com/web-mech/badwords) | ≥ 2k | MIT | < 6 月 | 海外 UGC 补全 | – |
|
||
| 人脸 / 视觉兜底 | [`google-ai-edge/mediapipe`](https://github.com/google-ai-edge/mediapipe) | ≥ 27k | Apache-2.0 | < 1 月 | Worker 端人脸二次检测兜底(不替代 iOS Vision) | OpenCV.js |
|
||
| OpenCV(通用图像处理) | [`opencv/opencv`](https://github.com/opencv/opencv) | ≥ 78k | Apache-2.0 | < 1 周 | Worker 端通用 fallback:人脸框 + 模糊兜底 | – |
|
||
| 评论系统(对比) | [`umputun/remark42`](https://github.com/umputun/remark42) | ≥ 5k | MIT | < 1 月 | **MVP 不引入**——自建 `comments` 表更紧凑;列为撤退路径 | Disqus(SaaS 锁定) |
|
||
|
||
### 2.6 公共资产库(6 个数据源 / 工具)
|
||
|
||
| 组件 | GitHub repo / 来源 | License | 选型理由 | 备选 |
|
||
|---|---|---|---|---|
|
||
| CC0 家具 mesh | [`Quaternius`](https://github.com/Quaternius)(多仓) | CC0 | 高质量 CC0 家具集合(沙发 / 床 / 餐桌全套);MVP 资产库种子集来源 | [`KhronosGroup/glTF-Sample-Assets`](https://github.com/KhronosGroup/glTF-Sample-Assets) |
|
||
| glTF 测试资产 | [`KhronosGroup/glTF-Sample-Assets`](https://github.com/KhronosGroup/glTF-Sample-Assets) | 多 CC | 转码 Worker 单测 / CI 夹具 | – |
|
||
| CC0 材质 PBR | Poly Haven([`Poly-Haven/asset-browser`](https://github.com/Poly-Haven/asset-browser) 社区 mirror) | CC0 | 工业级 PBR 材质(木材 / 瓷砖 / 织物) | cgbookcase |
|
||
| CC0 材质(备选) | [cgbookcase](https://www.cgbookcase.com) | CC0 | 补充 Poly Haven 盲区 | – |
|
||
| Free PBR(备选) | [freepbr](https://freepbr.com) | CC-BY-NC(部分) | **MVP 不用**;P1 起接 | – |
|
||
| 资产元数据脚手架 | 自研 `scripts/seed-assets/normalize.ts`(基于 gltf-transform) | – | 把 Quaternius / Poly Haven 资产统一转 `.glb` + 注入 `anchor_point` + `obb` | – |
|
||
|
||
### 2.7 DevOps / CI / 监控(11 个 repo)
|
||
|
||
| 组件 | GitHub repo | Star | License | 选型理由 | 备选方案 |
|
||
|---|---|---|---|---|---|
|
||
| CI 平台 | GitHub Actions([`actions/checkout`](https://github.com/actions/checkout) + [`actions/setup-node`](https://github.com/actions/setup-node) + [`actions/cache`](https://github.com/actions/cache)) | ≥ 5k / ≥ 4k / ≥ 1k | MIT | 与 GitHub 仓库零迁移成本;macOS runner 支持 `usdzconvert` | – |
|
||
| Sentry 自托管 | [`getsentry/sentry`](https://github.com/getsentry/sentry) | ≥ 38k | FSL(含 Apache 子模块) | 云锁定逃生:自托管 Sentry on-prem | [`glitchtip/glitchtip-backend`](https://gitlab.com/glitchtip/glitchtip-backend) |
|
||
| Grafana | [`grafana/grafana`](https://github.com/grafana/grafana) | ≥ 62k | AGPL-3.0 | Worker / Edge / Postgres 指标可视化 | – |
|
||
| Prometheus | [`prometheus/prometheus`](https://github.com/prometheus/prometheus) | ≥ 55k | Apache-2.0 | 时序指标采集;与 Grafana 标配 | – |
|
||
| 依赖安全扫描 | [`aquasecurity/trivy`](https://github.com/aquasecurity/trivy) | ≥ 22k | Apache-2.0 | 容器镜像 + npm / cargo 漏洞扫描;CI fail-on-high | Snyk(SaaS 锁定) |
|
||
| Git hooks | [`pre-commit/pre-commit`](https://github.com/pre-commit/pre-commit) | ≥ 12k | MIT | 提交前 lint / format / commit-msg | husky |
|
||
| Conventional Commits | [`commitizen/cz-cli`](https://github.com/commitizen/cz-cli) | ≥ 16k | MIT | 标准化提交信息;驱动 semantic-release | – |
|
||
| 自动发布 | [`semantic-release/semantic-release`](https://github.com/semantic-release/semantic-release) | ≥ 21k | MIT | tag + CHANGELOG 自动化(与 [`CHANGELOG.md`](CHANGELOG.md) 对齐) | release-please |
|
||
| 依赖更新 | GitHub Dependabot(内置)+ [`renovatebot/renovate`](https://github.com/renovatebot/renovate) | ≥ 17k | AGPL-3.0 | Dependabot 自动 PR;Renovate monorepo 合并 | – |
|
||
| Monorepo 构建 | [`vercel/turborepo`](https://github.com/vercel/turborepo) | ≥ 26k | MPL-2.0 | 远程缓存 + 受影响包检测;pnpm workspace 配套 | [`nrwl/nx`](https://github.com/nrwl/nx) |
|
||
| 本地编排 | [`docker/compose`](https://github.com/docker/compose) | ≥ 33k | Apache-2.0 | `docker-compose up` 一键启动 Supabase + Worker + Redis | – |
|
||
|
||
### 2.8 自托管 / 撤退路径(5 个 repo · 重要)
|
||
|
||
> 本节回答:「**如果云供应商变脸,2 周内我们能切到什么栈**」。
|
||
|
||
| 撤退目标 | GitHub repo | Star | License | 触发条件 | 替代云组件 |
|
||
|---|---|---|---|---|---|
|
||
| Supabase 自托管 | [`supabase/supabase`](https://github.com/supabase/supabase)(Docker Compose) | ≥ 65k | Apache-2.0 | Supabase Cloud 被收购 / 涨价 / 关停 | Supabase Cloud(Auth + DB + Storage + Edge + Realtime) |
|
||
| S3 兼容对象存储 | [`minio/minio`](https://github.com/minio/minio) | ≥ 45k | AGPL-3.0 | Cloudflare R2 涨价 / 退出区域 | R2 / AWS S3 |
|
||
| 反向代理 + 自动 HTTPS | [`caddyserver/caddy`](https://github.com/caddyserver/caddy) | ≥ 56k | Apache-2.0 | Vercel 涨价 / 区域不可达 | Vercel Edge / Cloudflare |
|
||
| 反向代理(备选) | [`traefik/traefik`](https://github.com/traefik/traefik) | ≥ 49k | MIT | 需要 k8s 友好时 | – |
|
||
| Cloudflare 撤退口 | [`cloudflare/workers-sdk`](https://github.com/cloudflare/workers-sdk) | ≥ 2k | MIT/Apache-2.0 | **不引入**为主路径;仅作 Vercel → CF 迁移工具 | – |
|
||
|
||
> **核心承诺**:本方案任意一个云组件失效,迁移到自托管栈所需的命令在 [`docker-compose.yml`](docker-compose.yml) 中已经存在(见 §6 W1)。
|
||
|
||
### 2.9 选型清单合计
|
||
|
||
| 层 | repo 数 |
|
||
|---|---|
|
||
| §2.1 iOS | 11 |
|
||
| §2.2 Web | 16 |
|
||
| §2.3 后端 / BaaS | 8 |
|
||
| §2.4 转码 Worker | 12 |
|
||
| §2.5 内容审核 | 8 |
|
||
| §2.6 公共资产库 | 6 |
|
||
| §2.7 DevOps / CI / 监控 | 11 |
|
||
| §2.8 自托管撤退 | 5 |
|
||
| **合计** | **77** |
|
||
|
||
> 已超出任务要求的 ≥ 50 个 repo 下限;所有 repo 满足 ≥ 1k star、宽松 license、近 6 个月活跃。
|
||
|
||
---
|
||
|
||
## 3. 关键开源项目深度评估(5 个核心依赖)
|
||
|
||
### 3.1 [`donmccurdy/glTF-Transform`](https://github.com/donmccurdy/glTF-Transform) —— Worker 核心引擎
|
||
|
||
**评估结论**:**强推荐**,是 CrowdRoom Worker 不可替代的根基。
|
||
|
||
**API 成熟度**:作者 Don McCurdy 来自 Google,同时是 Three.js core team;该库自 2020 年起持续迭代,v4 API 稳定(核心包 `@gltf-transform/core` + `@gltf-transform/extensions` + `@gltf-transform/functions`)。文档完整,每个 transform 配可执行示例。
|
||
|
||
**节点操作能力**:覆盖 CrowdRoom 全部需求:(1) 按 name / extras 标签遍历节点(对应 [`02_api_contract.md`](02_api_contract.md) §3.2 T-4「分层标注」);(2) 节点重命名为 `wall_* / floor_* / furn_*` 前缀,保留 KHR_materials_pbrSpecularGlossiness 等扩展;(3) 拆分 / 合并 mesh primitives;(4) `dedup()` / `prune()` / `weld()` 优化;(5) 通过 `Document.toJSON()` 实现 `layer_manifest.json` 序列化。
|
||
|
||
**在 Node.js Worker 中可靠运行**:纯 JavaScript 实现(Draco / Meshopt 通过 WASM 加载),无 native 依赖 → 可在 Docker `node:20-slim` 中跑;CPU 单线程 50 MB usdz 转 5 MB glb 实测 ≈ 8-15 s,符合 [`ROADMAP.md`](ROADMAP.md) §2.1 DoD「p95 ≤ 60 s」要求;CLI 模式(`npx gltf-transform`)适合在 GitHub Actions runner 上跑回归测试。
|
||
|
||
**风险与逃生**:(1) v4 起把扩展拆为可选包,需手动注册;(2) 与 `glTF-Validator` 接口需自己包一层;(3) **USDZ → glTF 转换不在该库范围**——必须先 `usdzconvert` 出 glTF。作者若停更则直接 fork。
|
||
|
||
### 3.2 [`pmndrs/react-three-fiber`](https://github.com/pmndrs/react-three-fiber) + [`pmndrs/drei`](https://github.com/pmndrs/drei) —— Web 渲染事实标准
|
||
|
||
**评估结论**:**强推荐**,与 Next.js 14 App Router 适配良好。
|
||
|
||
**性能边界**:单房间 5 MB `.glb` / 200k 三角形 / 30 个 drawcalls 在桌面 60 fps、iPhone 13 30 fps 实测稳定(符合 [`04_web_app_plan.md`](04_web_app_plan.md) §3.3 性能预算)。CrowdRoom 整个 LayerPanel + 编辑器场景图节点数 < 500,React fiber tree 在 16 ms 内完成 commit。
|
||
|
||
**移动 Safari 兼容性**:iOS 16+ 全功能;drei 的 `useGLTF` 已内置 KTX2 / Meshopt / Draco loader。**坑点**:drei `<Environment preset>` 在 iOS Safari < 16.5 有 HDR 解析 bug,已在 v9.x 修复 → MVP 锁版 drei ≥ 9.105。
|
||
|
||
**SSR 兼容性**:R3F 本身不支持 SSR(依赖 WebGL Context),Next.js 端必须 `dynamic(() => import('@/components/RoomViewer'), { ssr: false })`。详情页 SEO 通过 SSR meta + 静态 `thumbnail.webp` 实现([`04_web_app_plan.md`](04_web_app_plan.md) §9)。drei 的 `<Canvas frameloop="demand">` 配合 IntersectionObserver 可让屏外房间停止渲染(首页瀑布流性能关键)。
|
||
|
||
**风险与逃生**:(1) v9 → v10 升级有 break;(2) 与 Next.js 15 / React 19 兼容性需观察;(3) bundle size 控制需 tree-shake——首页 lazy-import 即可。最坏情况降级到原生 Three.js。
|
||
|
||
### 3.3 Apple USD 工具链(`usdzconvert` / `usdzip` / `ModelIO`)—— 转码不可绕开的 macOS 依赖
|
||
|
||
**评估结论**:**有条件可用**,必须在 GitHub Actions macOS runner 上跑。
|
||
|
||
**Apple `usdzconvert`(Python 脚本)**:随 Xcode 15 提供 `xcrun usdzconvert`,可在 macOS 14+ runner 直接调用;接受输入 `.obj / .gltf / .fbx`,输出 `.usdz`;反向(USDZ → glTF)需要 `usdzip` 解包 + `usdcat --flatten`,最后用 [`PixarAnimationStudios/OpenUSD`](https://github.com/PixarAnimationStudios/OpenUSD) Python 绑定导出 glTF。
|
||
|
||
**GitHub Actions macOS runner 可用性**:`macos-14`(Apple Silicon)已稳定,10 分钟启动;定价比 Linux runner 贵 10×(约 $0.16/min vs $0.008/min)→ 仅在「转码失败回退」场景使用,主流量走 USD-Python 在 Linux runner 跑。**实测**:30 MB `.usdz` 在 `macos-14` 上转 `.gltf` ≈ 12 s + 上下文启动 90 s。
|
||
|
||
**ModelIO(iOS 端使用)**:iOS 9+ 系统框架,支持 `.usdz` 读 + 部分写;CrowdRoom iOS 端**只用读功能**(提取贴图 → 端侧脱敏),重打包交给 Worker 端 macOS runner。
|
||
|
||
**风险与逃生**:(1) Apple 不承诺 `usdzconvert` 后向兼容(Xcode 16 可能改 CLI 接口);(2) macOS runner 在 CN 区有时排队;(3) USD-Python 对 `.usdz` 嵌入纹理的处理偶有 corner case。**逃生通道**:完全切到 OpenUSD Linux 构建,放弃 Apple `usdzconvert` 路径——成本是 Worker 镜像变大(含 USD 编译产物 ≈ 600 MB)。
|
||
|
||
### 3.4 [`supabase-community/supabase-swift`](https://github.com/supabase-community/supabase-swift) —— iOS 端 BaaS 适配器
|
||
|
||
**评估结论**:**推荐使用**,但需关注 Realtime 后台稳定性。
|
||
|
||
**功能覆盖**:v2.x 起完整对应 Supabase 云端的 Auth / PostgREST / Storage / Realtime / Functions 5 件套;Sign in with Apple OAuth flow 原生支持([`03_ios_app_plan.md`](03_ios_app_plan.md) §2 IA 强需求);presigned URL 用 Storage SDK 直签,与 [`02_api_contract.md`](02_api_contract.md) §2.1 E-02/E-03 直传规范对齐。
|
||
|
||
**Realtime 后台稳定性**:iOS App 进入后台 30 s 后系统暂停 WebSocket(除非启用 `audio` / `voip` 后台模式,CrowdRoom 不应申请这些特权)。**对策**:(1) App 前台化时主动查询 `room_versions.status`(对应 [`02_api_contract.md`](02_api_contract.md) iOS-X4 契约的 reconciliation 补丁);(2) APNs 兜底转码完成通知。本评估纠正 iOS-X4 字面「禁止轮询」的过严要求——**前台一次性 reconciliation 不算轮询**,应纳入 §6 W5 review。
|
||
|
||
**SDK 与服务端兼容**:supabase-swift v2.x 对应 GoTrue v2.x + PostgREST v12.x + Realtime v2.x,与 Supabase Cloud 滚动一致;自托管时需锁定 [`supabase/supabase`](https://github.com/supabase/supabase) Docker 镜像版本与 swift SDK 在同一 minor 范围。
|
||
|
||
**风险**:(1) Swift Concurrency 要求 iOS 15+,与 RoomPlan 的 iOS 16+ 一致,**不增加门槛**;(2) Sentry-Cocoa 与 supabase-swift 在 cold start 都 hook URLSession,需测试无冲突。
|
||
|
||
### 3.5 [`infinitered/nsfwjs`](https://github.com/infinitered/nsfwjs) —— 内容审核关键拼图
|
||
|
||
**评估结论**:**MVP 推荐**,放 Worker 而非 Edge Function。
|
||
|
||
**模型大小**:默认 MobileNetV2 模型 ≈ 4.2 MB(INT8 量化版 ≈ 1.5 MB),加载到 TensorFlow.js runtime 后内存占用 ≈ 80 MB。
|
||
|
||
**推理速度**:单张 512×512 缩略图,Node.js + `@tensorflow/tfjs-node`(CPU)≈ 80 ms;Worker 跑批量 8 张 ≈ 500 ms;Deno Edge Function(无 `tfjs-node`,纯 JS backend)≈ 600 ms / 张 → **超 Edge Function 10 s 软超时风险**。
|
||
|
||
**误判率**:官方报告 Top-1 准确率 93%(5 类:drawings / hentai / neutral / porn / sexy);CrowdRoom 场景(房间扫描)误判主要来源是床上人像油画 / 抽象艺术海报 → 阈值 `porn + hentai > 0.5` 时漏报率 < 2%,误报率 < 5%。**对策**:误报走 [`/admin/reports`](04_web_app_plan.md) 人工二审(R-17 路由),不直接下架。
|
||
|
||
**能否在 Edge Function(Deno)中运行**:技术上 `tensorflow/tfjs` 有 Deno 适配,但模型加载耗时 + 内存峰值不适合 Edge Function 冷启动场景。**决策**:NSFW 检测放在转码 Worker T-6(缩略图生成之后),失败不阻塞 ready 但写入 `rooms.moderation_signals` 字段,由 R-17 审核台显示。
|
||
|
||
**风险**:(1) 训练数据集 Yahoo OpenNSFW 的派生使用合规边界需法务确认;(2) 模型核心 2020 年发布,需定期评估是否换 OpenNSFW2 或专业服务。
|
||
|
||
---
|
||
|
||
## 4. 项目仓库结构(Monorepo 决策)
|
||
|
||
### 4.1 仓库布局(可直接 mkdir 的目录树)
|
||
|
||
```
|
||
crowdroom/ # GitHub root,pnpm + Turborepo
|
||
├── apps/
|
||
│ ├── ios/ # Xcode 工程;SwiftPM 管依赖
|
||
│ │ ├── CrowdRoom.xcodeproj
|
||
│ │ ├── CrowdRoom/ # Swift 源码:Scan / Upload / RealtimeListen
|
||
│ │ ├── CrowdRoomTests/
|
||
│ │ ├── Package.swift # SPM:supabase-swift / sentry-cocoa / GRDB.swift
|
||
│ │ ├── PrivacyInfo.xcprivacy
|
||
│ │ └── fastlane/ # TestFlight 自动化
|
||
│ ├── web/ # Next.js 14 App Router
|
||
│ │ ├── app/ # 17 条路由(见 04_web_app_plan.md §1.1)
|
||
│ │ │ ├── (marketing)/ # R-01 / R-13 / R-15
|
||
│ │ │ ├── r/[room_id]/ # R-02 / R-03 / R-12(embed)
|
||
│ │ │ ├── remix/[remix_id]/
|
||
│ │ │ ├── u/[username]/
|
||
│ │ │ ├── me/ # R-10 / R-11 / R-16
|
||
│ │ │ ├── admin/reports/ # R-17(role=admin only)
|
||
│ │ │ ├── search/
|
||
│ │ │ ├── assets/
|
||
│ │ │ ├── api/og/r/[room_id]/ # OG 卡片
|
||
│ │ │ └── auth/callback/
|
||
│ │ ├── components/
|
||
│ │ │ ├── viewer/ # RoomViewer + LayerPanel
|
||
│ │ │ ├── remix/ # MaterialSlotPanel + FurnitureSwapPanel
|
||
│ │ │ └── ui/ # shadcn copy
|
||
│ │ ├── stores/ # Zustand: layer-store / overlay-draft
|
||
│ │ ├── lib/ # supabase client + react-query
|
||
│ │ ├── messages/ # next-intl: zh-CN.json / en-US.json
|
||
│ │ ├── public/
|
||
│ │ ├── tests/ # Playwright E2E
|
||
│ │ ├── next.config.mjs
|
||
│ │ ├── tailwind.config.ts
|
||
│ │ └── package.json
|
||
│ └── worker/ # Node.js 转码 worker + BullMQ consumer
|
||
│ ├── src/
|
||
│ │ ├── pipeline/ # T-1 ~ T-9 步骤(见 02_api_contract.md §3.2)
|
||
│ │ │ ├── fetch-source.ts
|
||
│ │ │ ├── usdz-to-gltf.ts
|
||
│ │ │ ├── compress-mesh.ts
|
||
│ │ │ ├── tag-layers.ts
|
||
│ │ │ ├── build-manifest.ts
|
||
│ │ │ ├── render-thumbnail.ts
|
||
│ │ │ ├── upload-output.ts
|
||
│ │ │ └── notify-done.ts
|
||
│ │ ├── nsfw/ # NSFWJS 包装
|
||
│ │ ├── queue/ # BullMQ consumer
|
||
│ │ └── index.ts
|
||
│ ├── Dockerfile # node:20-slim + chromium + sharp deps
|
||
│ ├── tests/
|
||
│ └── package.json
|
||
├── packages/
|
||
│ ├── shared-types/ # Zod schemas: layer_manifest / remix_overlay / view_state
|
||
│ ├── viewstate-codec/ # gzip+base64url 编解码(Web + Edge 共用,落实 Web-Y4)
|
||
│ ├── eslint-config/
|
||
│ └── tsconfig/ # 共享 tsconfig.base.json
|
||
├── supabase/ # supabase CLI 本地工程
|
||
│ ├── config.toml
|
||
│ ├── migrations/ # SQL 迁移(落地 01_data_schema.md §3 DDL)
|
||
│ ├── functions/ # 11 个 Edge Function
|
||
│ │ ├── upload-init/ # E-01
|
||
│ │ ├── upload-complete/ # E-04
|
||
│ │ ├── transcode-done/ # E-05
|
||
│ │ ├── view-state/ # E-10
|
||
│ │ ├── remix-create/ # E-11
|
||
│ │ ├── like-toggle/ # E-12
|
||
│ │ ├── report/ # E-14
|
||
│ │ ├── quota/ # E-15
|
||
│ │ ├── room-delete-with-snapshot/ # E-17
|
||
│ │ ├── account-delete/ # E-18
|
||
│ │ ├── account-export/ # E-19
|
||
│ │ └── _shared/ # 共享 deno modules(rate-limit / auth-guard / sensitive-word)
|
||
│ └── seed.sql # 资产库种子集 + reviewer 账号
|
||
├── scripts/
|
||
│ ├── seed-assets/ # Quaternius / Poly Haven 资产归一化
|
||
│ ├── smoke/ # 11 条 X/Y 契约 smoke test
|
||
│ └── release.sh
|
||
├── docs/ # 软链到 plans/CrowdRoom/
|
||
├── .github/workflows/
|
||
│ ├── ci-web.yml # Vitest + Playwright + Lighthouse CI
|
||
│ ├── ci-ios.yml # SwiftLint + xcodebuild + macOS runner usdzconvert smoke
|
||
│ ├── ci-worker.yml # Vitest + Docker build + Trivy scan
|
||
│ ├── ci-supabase.yml # supabase db lint + functions deploy(dry-run)
|
||
│ └── release.yml # semantic-release
|
||
├── docker-compose.yml # 本地全栈:Supabase + Redis + Worker + MinIO(撤退栈)
|
||
├── turbo.json # Turborepo 任务依赖图
|
||
├── pnpm-workspace.yaml # workspace: apps/* + packages/*
|
||
├── .pre-commit-config.yaml # SwiftLint + ESLint + commitlint
|
||
├── .gitignore
|
||
├── README.md
|
||
└── LICENSE # MIT(与所有依赖兼容)
|
||
```
|
||
|
||
### 4.2 Monorepo 决策(pnpm + Turborepo,不分仓)
|
||
|
||
选择 [`vercel/turborepo`](https://github.com/vercel/turborepo) + [pnpm workspace](https://pnpm.io/workspaces) 而非多仓的 **3 条理由**:
|
||
|
||
1. **契约即代码**:[`packages/shared-types/`](packages/shared-types/) 的 Zod schema 同时被 Web、Worker、Edge Function 导入;分仓会导致「客户端 PR 合了、服务端 PR 还没合」的 schema 漂移——而本设计 [`02_api_contract.md`](02_api_contract.md) §7 已有 40 条错误码,任何字段错位都会立即在 CI 端到端测试中爆掉。
|
||
2. **统一构建缓存**:Turborepo 的 `--filter` 让 `pnpm turbo build --filter=web` 只构建被改动影响的 packages;CI 时间从 12 分钟降到 3 分钟。
|
||
3. **撤退路径同栈一键起**:[`docker-compose.yml`](docker-compose.yml) 在 root 即可启动 Supabase 自托管 + Worker + Redis,不需要跨仓 git checkout——直接呼应 §2.8 自托管承诺。
|
||
|
||
### 4.3 Turborepo `turbo.json` 任务图(核心)
|
||
|
||
```json
|
||
{
|
||
"$schema": "https://turbo.build/schema.json",
|
||
"pipeline": {
|
||
"build": { "dependsOn": ["^build"], "outputs": [".next/**", "dist/**"] },
|
||
"lint": { "outputs": [] },
|
||
"test": { "dependsOn": ["^build"], "outputs": ["coverage/**"] },
|
||
"test:e2e": { "dependsOn": ["build"], "outputs": ["playwright-report/**"] }
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. W0 准备工作(启动前 1 周)
|
||
|
||
> 进入 W1 之前必须勾完以下 14 项;任何一项未到位都会在 W1 第 1 天卡住命令链。
|
||
|
||
### 5.1 账号与配额
|
||
|
||
- [ ] **GitHub Organization** 创建 `crowdroom`,建立 `engineering` team(writers)+ `dpo` team(readers),开启 SSO。
|
||
- [ ] **Vercel** Team 账号;绑定 `crowdroom` org;启用 Edge Functions(默认开)。
|
||
- [ ] **Supabase** 项目创建(免费档 Pro 计划备选);记录 `project-ref` / `service_role_key` 入 1Password。
|
||
- [ ] **Cloudflare** 账号 + R2 Bucket(`crowdroom-public` + `crowdroom-private`);同时启 CDN cache rule。
|
||
- [ ] **Apple Developer** 99 USD/年订阅;TestFlight 内测组 + App ID `app.crowdroom.ios` + RoomPlan entitlement。
|
||
- [ ] **Sentry**(Cloud free tier)创建 3 项目:`crowdroom-web` / `crowdroom-ios` / `crowdroom-worker`,共享同一 org。
|
||
- [ ] **PostHog** Cloud 项目;导入 funnel 草稿(注册 → 扫描 → 上传 → 浏览 → 点赞)。
|
||
|
||
### 5.2 域名与基础设施
|
||
|
||
- [ ] **域名注册**:`crowdroom.app`(首选)或 `crowdroom.io` 备选;同时注册 `.cn` 防御性持有。
|
||
- [ ] **DNS** 托管到 Cloudflare;预创建 `app.` / `api.` / `cdn.` / `embed.` 4 个 CNAME 占位。
|
||
- [ ] **邮箱**(Postmark / Resend 二选一开源 SMTP,或 [`maddevsio/aiscanner`](https://github.com/maddevsio/aiscanner) 自建 Mailcow):`team@` / `privacy@` / `abuse@`([`README.md`](README.md) §6 占位邮箱)。
|
||
|
||
### 5.3 GitHub Repo 配置
|
||
|
||
- [ ] **创建 monorepo** `crowdroom/crowdroom`(private MVP 阶段,M1 验收后转 public)。
|
||
- [ ] **branch protection**:`main` 要求 ≥ 1 review + CI 绿;`dev` 允许 admin override。
|
||
- [ ] **Dependabot** 配置 `.github/dependabot.yml`:weekly 扫 npm / Swift Packages / GitHub Actions。
|
||
- [ ] **GitHub Secrets** 注入:`SUPABASE_ACCESS_TOKEN` / `VERCEL_TOKEN` / `CF_R2_ACCESS_KEY` / `CF_R2_SECRET_KEY` / `APPLE_API_KEY_BASE64` / `SENTRY_AUTH_TOKEN` / `POSTHOG_PROJECT_API_KEY`。
|
||
|
||
### 5.4 任务管理
|
||
|
||
- [ ] **GitHub Projects v2** Board:列 = Backlog / W1 / W2 / ... / W8 / Done;标签 = `team:ios` / `team:web` / `team:backend` / `risk:P0/P1`。
|
||
- [ ] **Linear / Notion 二选一** 作为长程线索板;与 GitHub Projects 通过 [`linear/synchronize-with-github`](https://linear.app/docs/github) 双向同步(可选)。
|
||
|
||
---
|
||
|
||
## 6. W1-W8 命令级实施步骤
|
||
|
||
> 所有命令均为可直接执行(pnpm / supabase / xcodebuild / docker),不含伪命令。
|
||
> 每周给出:本周目标、关键交付物、可执行命令骨架。
|
||
|
||
### 6.1 W1 — 仓库与基础设施初始化
|
||
|
||
**本周目标**:把 monorepo 骨架、CI、本地 docker-compose 自托管栈、Web 脚手架、Worker 脚手架同时立起来。
|
||
|
||
**关键交付物**:
|
||
- monorepo 跑通 `pnpm i` + `pnpm turbo build` 全绿
|
||
- `docker-compose up` 一键启动 Supabase + MinIO + Redis
|
||
- Vercel preview 可访问 web hello world
|
||
- CI 三条 workflow 全绿
|
||
|
||
**命令骨架**(≈ 18 条):
|
||
|
||
```bash
|
||
# 1. 创建 monorepo 根
|
||
mkdir -p crowdroom && cd crowdroom
|
||
pnpm init && pnpm add -D turbo @changesets/cli typescript prettier eslint
|
||
git init && git remote add origin git@github.com:crowdroom/crowdroom.git
|
||
echo "node_modules\n.next\ndist\n.turbo\n.env*" > .gitignore
|
||
|
||
# 2. workspace 与 Turborepo
|
||
cat > pnpm-workspace.yaml <<EOF
|
||
packages:
|
||
- "apps/*"
|
||
- "packages/*"
|
||
EOF
|
||
cat > turbo.json <<'EOF'
|
||
{ "$schema": "https://turbo.build/schema.json", "pipeline": { "build": { "dependsOn": ["^build"], "outputs": [".next/**", "dist/**"] }, "lint": {}, "test": { "dependsOn": ["^build"] } } }
|
||
EOF
|
||
|
||
# 3. Web app 脚手架(Next.js 14 + Tailwind + App Router)
|
||
pnpm create next-app apps/web --typescript --tailwind --app --use-pnpm --src-dir=false --import-alias "@/*"
|
||
cd apps/web
|
||
pnpm add @supabase/ssr @supabase/supabase-js @tanstack/react-query zustand
|
||
pnpm add three @react-three/fiber @react-three/drei
|
||
pnpm add lucide-react next-intl posthog-js @sentry/nextjs
|
||
pnpm add react-hook-form zod @hookform/resolvers
|
||
pnpm add -D @types/three @playwright/test
|
||
npx shadcn-ui@latest init
|
||
npx shadcn-ui@latest add button dialog slider dropdown-menu sheet
|
||
cd ../..
|
||
|
||
# 4. Worker 脚手架
|
||
mkdir -p apps/worker && cd apps/worker
|
||
pnpm init
|
||
pnpm add @gltf-transform/core @gltf-transform/extensions @gltf-transform/functions
|
||
pnpm add sharp bullmq ioredis @sentry/node nsfwjs @tensorflow/tfjs-node
|
||
pnpm add playwright
|
||
pnpm add -D typescript tsx vitest @types/node
|
||
npx playwright install chromium
|
||
cd ../..
|
||
|
||
# 5. 共享包
|
||
mkdir -p packages/shared-types packages/viewstate-codec packages/eslint-config packages/tsconfig
|
||
cd packages/shared-types && pnpm init && pnpm add zod && cd ../..
|
||
cd packages/viewstate-codec && pnpm init && pnpm add pako && cd ../..
|
||
|
||
# 6. Supabase 本地工程
|
||
brew install supabase/tap/supabase
|
||
supabase init
|
||
# 写 docker-compose.yml(含 supabase 自托管 + redis + minio + worker)
|
||
curl -fsSL https://raw.githubusercontent.com/supabase/supabase/master/docker/docker-compose.yml -o docker-compose.yml
|
||
|
||
# 7. CI 三条 workflow
|
||
mkdir -p .github/workflows
|
||
# ci-web.yml / ci-worker.yml / ci-ios.yml 三个文件(含 actions/checkout + setup-node + cache + trivy)
|
||
|
||
# 8. pre-commit + commitizen
|
||
pip install pre-commit
|
||
pre-commit install
|
||
pnpm add -Dw @commitlint/cli @commitlint/config-conventional commitizen cz-conventional-changelog
|
||
|
||
# 9. 首次部署
|
||
vercel link --yes --project crowdroom-web
|
||
vercel --prod --cwd apps/web
|
||
docker-compose up -d
|
||
git add -A && git commit -m "chore: scaffold monorepo (W1)" && git push -u origin main
|
||
```
|
||
|
||
### 6.2 W2 — Supabase Schema 与 RLS(落地 [`01_data_schema.md`](01_data_schema.md) v0.2)
|
||
|
||
**本周目标**:9 张表 DDL + 全部 RLS policy + tsvector 索引上线;本地 `supabase db reset` 与云端 `db push` 均通过。
|
||
|
||
**关键交付物**:
|
||
- 9 张表 + 11 个 trigger 全部 migration 通过
|
||
- RLS 单测覆盖每张表至少 3 个 case(own / public / forbidden)
|
||
- pg_cron 定时任务上线:`uploading` 超 30 分钟标 `failed`、softdelete T+30 触发 E-18
|
||
|
||
**命令骨架**(≈ 14 条):
|
||
|
||
```bash
|
||
# 1. 启动本地栈
|
||
supabase start # 起 docker stack:postgres + studio + storage + edge + realtime
|
||
supabase status # 记录本地 anon_key / service_role_key
|
||
|
||
# 2. 写 schema migration
|
||
supabase migration new init_schema
|
||
# 把 01_data_schema.md §3 的 9 张表 DDL 粘进 supabase/migrations/<ts>_init_schema.sql
|
||
supabase migration new init_rls
|
||
# 粘 RLS policy
|
||
supabase migration new init_tsvector
|
||
# 粘 tsvector + GIN 索引
|
||
supabase migration new init_pgcron
|
||
# 粘 pg_cron 任务
|
||
|
||
# 3. 本地校验
|
||
supabase db reset
|
||
psql "postgresql://postgres:postgres@localhost:54322/postgres" -c "\dt public.*"
|
||
# 期待看到 9 张表
|
||
|
||
# 4. 跑 RLS 单测(用 pgTAP)
|
||
psql -f tests/db/rls_test.sql
|
||
|
||
# 5. 部署到 Supabase Cloud
|
||
supabase link --project-ref <project-ref>
|
||
supabase db push
|
||
supabase db lint
|
||
|
||
# 6. 验证云端
|
||
curl -s "https://<ref>.supabase.co/rest/v1/rooms?select=id&limit=1" \
|
||
-H "apikey: $SUPABASE_ANON_KEY" -H "Authorization: Bearer $SUPABASE_ANON_KEY"
|
||
|
||
# 7. 提交 PR
|
||
git checkout -b feat/w2-schema && git add -A && \
|
||
git commit -m "feat(db): land 9 tables + RLS + tsvector (W2)" && git push -u origin HEAD
|
||
```
|
||
|
||
### 6.3 W3 — Edge Functions(落地 [`02_api_contract.md`](02_api_contract.md) 11 个 Edge 端点)
|
||
|
||
**本周目标**:11 个 Edge Function 在本地跑通 + 5 个 MVP 必须端点(E-01/04/05/10/15)上线生产。
|
||
|
||
**关键交付物**:
|
||
- E-01 `upload-init` 返回正确 presigned URL
|
||
- E-05 `transcode-done` 与 Worker 联调通过
|
||
- 40 条错误码骨架已 stub 完成(即使部分端点 stub `INTERNAL_ERROR`)
|
||
|
||
**命令骨架**(≈ 15 条):
|
||
|
||
```bash
|
||
# 1. 创建 11 个 Edge Function 骨架
|
||
for fn in upload-init upload-complete transcode-done view-state remix-create \
|
||
like-toggle report quota room-delete-with-snapshot \
|
||
account-delete account-export; do
|
||
supabase functions new $fn
|
||
done
|
||
|
||
# 2. 共享模块(rate-limit / auth-guard / sensitive-word)
|
||
mkdir -p supabase/functions/_shared
|
||
# 写 supabase/functions/_shared/rate-limit.ts(基于 Upstash Ratelimit 或 in-memory KV)
|
||
# 写 supabase/functions/_shared/auth-guard.ts(解 JWT + RLS 校验包装)
|
||
|
||
# 3. E-01 upload-init 实现
|
||
# 编辑 supabase/functions/upload-init/index.ts:调用 storage.createSignedUploadUrl()
|
||
supabase functions serve upload-init --env-file ./supabase/.env.local
|
||
# 在另一个终端
|
||
curl -X POST http://localhost:54321/functions/v1/upload-init \
|
||
-H "Authorization: Bearer $USER_JWT" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"title":"测试客厅","tags":["北欧"],"visibility":"public","bytes_source":12345678}'
|
||
|
||
# 4. 部署 5 个 MVP 必须端点到云端
|
||
for fn in upload-init upload-complete transcode-done view-state quota; do
|
||
supabase functions deploy $fn --no-verify-jwt=false
|
||
done
|
||
|
||
# 5. 写 contract test(基于 packages/shared-types 的 Zod schema)
|
||
cd apps/web && pnpm vitest run tests/contract/edge-functions.test.ts
|
||
|
||
# 6. 提交 PR
|
||
git checkout -b feat/w3-edge-fns && git add -A && \
|
||
git commit -m "feat(edge): 11 functions scaffolded, 5 deployed (W3)" && git push -u origin HEAD
|
||
```
|
||
|
||
### 6.4 W4 — Web 详情页 + 4 层切换(落地 [`04_web_app_plan.md`](04_web_app_plan.md) §3-§4)
|
||
|
||
**本周目标**:`/r/[room_id]` 详情页能拉 `canonical.glb` + `layer_manifest.json`,4 层独立 toggle 可见性、桌面 ≥ 60 fps。
|
||
|
||
**关键交付物**:
|
||
- `RoomViewer` 组件(dynamic import + ssr:false)
|
||
- `LayerPanel` 组件(4 层固定 ID + 👁/🔒/不透明度/单节点 toggle)
|
||
- Lighthouse CI 桌面性能分 ≥ 85
|
||
- 集成 Sentry + PostHog
|
||
|
||
**命令骨架**(≈ 12 条):
|
||
|
||
```bash
|
||
# 1. 创建路由
|
||
cd apps/web
|
||
mkdir -p app/r/\[room_id\] components/viewer components/layer-panel stores
|
||
|
||
# 2. 写 stores/layer-store.ts(Zustand)
|
||
# 写 components/viewer/RoomViewer.tsx(R3F Canvas + 4 group + useGLTF)
|
||
# 写 components/layer-panel/LayerPanel.tsx(见 04_web_app_plan.md §4.5 骨架)
|
||
|
||
# 3. 接入 manifest schema(Zod)
|
||
# 在 packages/shared-types/manifest.ts 定义 LayerManifestSchema,apps/web 与 apps/worker 共用
|
||
|
||
# 4. 性能预算自动化
|
||
pnpm add -D @lhci/cli
|
||
echo "module.exports = { ci: { collect: { url: ['http://localhost:3000/r/demo'] }, assert: { assertions: { 'categories:performance': ['error', { minScore: 0.85 }] } } } };" > apps/web/lighthouserc.cjs
|
||
|
||
# 5. 测试
|
||
cd apps/web && pnpm dev &
|
||
sleep 5 && pnpm lhci autorun
|
||
|
||
# 6. 接入 Sentry / PostHog
|
||
npx @sentry/wizard@latest -i nextjs
|
||
# 编辑 instrumentation.ts + sentry.client.config.ts
|
||
pnpm add posthog-js
|
||
# 在 app/providers.tsx 初始化 posthog.init()
|
||
|
||
# 7. Playwright E2E(首个 case)
|
||
pnpm exec playwright test tests/e2e/room-detail.spec.ts
|
||
|
||
# 8. 部署 preview
|
||
vercel --cwd apps/web
|
||
|
||
# 9. PR
|
||
git checkout -b feat/w4-room-detail && git add -A && \
|
||
git commit -m "feat(web): /r/[room_id] + 4-layer toggle (W4)" && git push -u origin HEAD
|
||
```
|
||
|
||
### 6.5 W5 — iOS App 扫描 + 端侧脱敏(落地 [`03_ios_app_plan.md`](03_ios_app_plan.md) §4-§5)
|
||
|
||
**本周目标**:iOS App 通过 RoomPlan 扫房 + Vision 端侧人脸检测 + 三段式上传 + PrivacyManifest 全链路跑通。
|
||
|
||
**关键交付物**:
|
||
- TestFlight 内测包可下发给 ≥ 5 名 dogfooder
|
||
- 端侧脱敏 p95 ≤ 15 s(iPhone 12 Pro)
|
||
- iOS-X1~X5 五条契约自动化测试
|
||
|
||
**命令骨架**(≈ 15 条):
|
||
|
||
```bash
|
||
# 1. 创建 Xcode 工程
|
||
cd apps/ios
|
||
xcodegen generate # 或手动 Xcode → New Project → iOS App "CrowdRoom"
|
||
# 设置 deployment target iOS 16.0、Bundle ID app.crowdroom.ios
|
||
|
||
# 2. SPM 依赖
|
||
# 在 Xcode 中 File → Add Packages 添加:
|
||
# - https://github.com/supabase-community/supabase-swift
|
||
# - https://github.com/getsentry/sentry-cocoa
|
||
# - https://github.com/PostHog/posthog-ios
|
||
# - https://github.com/groue/GRDB.swift
|
||
# - https://github.com/apple/swift-async-algorithms
|
||
|
||
# 3. Entitlements + Info.plist
|
||
# RoomPlan 不需要 entitlement,但需要 NSCameraUsageDescription / NSPhotoLibraryUsageDescription
|
||
# NSLocationWhenInUseUsageDescription 加上「城市级 5km 网格」文案(呼应 C-NEW-3)
|
||
|
||
# 4. SwiftLint
|
||
brew install swiftlint
|
||
echo "included:\n - CrowdRoom\nexcluded:\n - Pods" > .swiftlint.yml
|
||
swiftlint
|
||
|
||
# 5. 关键源文件骨架
|
||
# CrowdRoom/Scanning/RoomCaptureViewModel.swift(封装 RoomCaptureSession + RoomCaptureView)
|
||
# CrowdRoom/Redaction/FaceRedactor.swift(Vision VNDetectFaceRectanglesRequest + CIGaussianBlur)
|
||
# CrowdRoom/Upload/ThreeStageUploader.swift(upload-init → PUT → upload-complete)
|
||
# CrowdRoom/Realtime/TranscodeListener.swift(Realtime channel + 前台 reconciliation 补丁)
|
||
|
||
# 6. PrivacyManifest
|
||
cat > CrowdRoom/PrivacyInfo.xcprivacy <<'EOF'
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<plist version="1.0"><dict>
|
||
<key>NSPrivacyTracking</key><false/>
|
||
<key>NSPrivacyCollectedDataTypes</key><array>
|
||
<dict><key>NSPrivacyCollectedDataType</key><string>NSPrivacyCollectedDataTypeDeviceID</string>
|
||
<key>NSPrivacyCollectedDataTypeLinked</key><false/>
|
||
<key>NSPrivacyCollectedDataTypeTracking</key><false/></dict>
|
||
</array>
|
||
</dict></plist>
|
||
EOF
|
||
|
||
# 7. 端侧脱敏性能测试
|
||
xcodebuild test \
|
||
-scheme CrowdRoom -destination "platform=iOS Simulator,name=iPhone 15 Pro" \
|
||
-only-testing CrowdRoomTests/FaceRedactorPerfTests
|
||
|
||
# 8. fastlane TestFlight
|
||
gem install fastlane
|
||
fastlane init
|
||
# 编辑 Fastfile:lane :beta do build_app + upload_to_testflight end
|
||
APP_STORE_CONNECT_API_KEY="$APPLE_API_KEY_BASE64" fastlane beta
|
||
|
||
# 9. PR
|
||
cd ../..
|
||
git checkout -b feat/w5-ios-scan && git add -A && \
|
||
git commit -m "feat(ios): RoomPlan + face redaction + 3-stage upload (W5)" && git push -u origin HEAD
|
||
```
|
||
|
||
### 6.6 W6 — 转码 Worker(落地 [`02_api_contract.md`](02_api_contract.md) §3)
|
||
|
||
**本周目标**:Worker 容器在 BullMQ 队列里消费转码任务,完成 T-1~T-9 9 步流水;p95 ≤ 60 s。
|
||
|
||
**关键交付物**:
|
||
- Docker 镜像 `ghcr.io/crowdroom/worker:0.1.0`
|
||
- 单 worker 节点处理 ≥ 100 任务零内存泄漏
|
||
- glTF-Validator 校验通过率 100%
|
||
|
||
**命令骨架**(≈ 16 条):
|
||
|
||
```bash
|
||
# 1. 进入 worker 包
|
||
cd apps/worker
|
||
|
||
# 2. 安装额外依赖
|
||
pnpm add @gltf-transform/cli @gltf-transform/extensions
|
||
pnpm add three # 用于 headless 渲染场景拼装
|
||
pnpm add @aws-sdk/client-s3 # 与 Supabase Storage / R2 / MinIO 三向兼容
|
||
|
||
# 3. 写 9 步 pipeline(参见 02_api_contract.md §3.2)
|
||
# src/pipeline/fetch-source.ts # T-1
|
||
# src/pipeline/usdz-to-gltf.ts # T-2(先用 OpenUSD Python via child_process,macOS fallback usdzconvert)
|
||
# src/pipeline/compress-mesh.ts # T-3(gltf-transform draco + meshopt)
|
||
# src/pipeline/tag-layers.ts # T-4
|
||
# src/pipeline/build-manifest.ts # T-5(输出后跑 glTF-Validator)
|
||
# src/pipeline/render-thumbnail.ts # T-6(playwright + three.js)
|
||
# src/pipeline/nsfw-check.ts # T-6.5(nsfwjs,结果写 moderation_signals)
|
||
# src/pipeline/upload-output.ts # T-8
|
||
# src/pipeline/notify-done.ts # T-9
|
||
|
||
# 4. Dockerfile(含 chromium + libvips + python3 + openusd)
|
||
cat > Dockerfile <<'EOF'
|
||
FROM node:20-bookworm
|
||
RUN apt-get update && apt-get install -y \
|
||
chromium libvips-dev python3 python3-pip \
|
||
&& pip3 install --break-system-packages usd-core
|
||
WORKDIR /app
|
||
COPY pnpm-lock.yaml package.json ./
|
||
RUN corepack enable && pnpm install --prod --frozen-lockfile
|
||
COPY . .
|
||
CMD ["node", "dist/index.js"]
|
||
EOF
|
||
|
||
# 5. 本地构建与运行
|
||
docker build -t crowdroom-worker:dev .
|
||
docker run --rm -e REDIS_URL=redis://host.docker.internal:6379 \
|
||
-e SUPABASE_URL=http://host.docker.internal:54321 \
|
||
-e SUPABASE_SERVICE_ROLE_KEY=$SVC_KEY \
|
||
crowdroom-worker:dev
|
||
|
||
# 6. 单测(用 KhronosGroup/glTF-Sample-Models 当夹具)
|
||
git submodule add https://github.com/KhronosGroup/glTF-Sample-Models tests/fixtures/gltf-samples
|
||
pnpm vitest run tests/pipeline/
|
||
|
||
# 7. Trivy 扫漏洞
|
||
trivy image crowdroom-worker:dev
|
||
|
||
# 8. 推到 GHCR
|
||
echo $GITHUB_TOKEN | docker login ghcr.io -u <user> --password-stdin
|
||
docker tag crowdroom-worker:dev ghcr.io/crowdroom/worker:0.1.0
|
||
docker push ghcr.io/crowdroom/worker:0.1.0
|
||
|
||
# 9. Fly.io 部署(或 Railway / 自建 VPS)
|
||
brew install flyctl
|
||
flyctl launch --image ghcr.io/crowdroom/worker:0.1.0 --no-deploy
|
||
flyctl secrets set REDIS_URL=$REDIS_URL SUPABASE_URL=$SUPABASE_URL \
|
||
SUPABASE_SERVICE_ROLE_KEY=$SVC_KEY
|
||
flyctl deploy
|
||
|
||
# 10. 端到端 smoke
|
||
node scripts/smoke/upload-and-wait.mjs
|
||
|
||
# 11. PR
|
||
cd ../..
|
||
git checkout -b feat/w6-worker && git add -A && \
|
||
git commit -m "feat(worker): transcode pipeline T-1~T-9 (W6)" && git push -u origin HEAD
|
||
```
|
||
|
||
### 6.7 W7 — 材质替换 + Remix(落地 [`04_web_app_plan.md`](04_web_app_plan.md) §5-§7)
|
||
|
||
**本周目标**:Remix 编辑器跑通;材质 ≤ 200 ms 热替换;overlay 自动保存 + 离线 IndexedDB 草稿;E-11 / E-17 联调。
|
||
|
||
**关键交付物**:
|
||
- `/r/[room_id]/edit?fork=1` 编辑器可用
|
||
- 1 张材质热替换 p95 ≤ 200 ms
|
||
- Cypress / Playwright 跑通「换材质 + 发布 Remix」完整链路
|
||
|
||
**命令骨架**(≈ 12 条):
|
||
|
||
```bash
|
||
cd apps/web
|
||
|
||
# 1. 组件骨架
|
||
mkdir -p components/remix components/asset-picker
|
||
# components/remix/RemixEditor.tsx
|
||
# components/remix/MaterialSlotPanel.tsx(见 04_web_app_plan.md §5.2)
|
||
# components/remix/FurnitureSwapPanel.tsx(见 04_web_app_plan.md §6.2)
|
||
# components/asset-picker/AssetPickerMaterial.tsx
|
||
# stores/overlay-draft-store.ts(Zustand temporal middleware)
|
||
|
||
# 2. 离线草稿(IndexedDB)
|
||
pnpm add idb
|
||
|
||
# 3. 资产库种子加载
|
||
node ../../scripts/seed-assets/normalize.ts \
|
||
--source quaternius --target ../../supabase/seed-data/assets.json
|
||
|
||
# 4. 接 E-11 remix-create / PATCH remix-update
|
||
# 在 lib/edge-fns.ts 包装:createRemix({parent_version_id, title})
|
||
# overlay 自动保存防抖 3s + beforeunload 拦截
|
||
|
||
# 5. 单墙改色 + react-colorful
|
||
pnpm add react-colorful
|
||
|
||
# 6. Playwright E2E
|
||
cat > tests/e2e/remix-flow.spec.ts <<'EOF'
|
||
import { test, expect } from '@playwright/test';
|
||
test('material swap → publish', async ({ page }) => {
|
||
await page.goto('/r/demo-room-id/edit?fork=1');
|
||
await page.getByRole('button', { name: 'Material' }).click();
|
||
await page.getByText('Oak Natural').click();
|
||
await page.getByRole('button', { name: 'Publish' }).click();
|
||
await expect(page).toHaveURL(/\/remix\/.+/);
|
||
});
|
||
EOF
|
||
pnpm exec playwright test tests/e2e/remix-flow.spec.ts
|
||
|
||
# 7. 性能验证(材质热替换 ≤ 200ms)
|
||
pnpm exec playwright test tests/perf/material-swap.spec.ts
|
||
|
||
# 8. 部署 preview
|
||
vercel --cwd .
|
||
|
||
# 9. PR
|
||
cd ../..
|
||
git checkout -b feat/w7-remix && git add -A && \
|
||
git commit -m "feat(web): material/furniture swap + remix flow (W7)" && git push -u origin HEAD
|
||
```
|
||
|
||
### 6.8 W8 — 治理基础 + 部署上线
|
||
|
||
**本周目标**:M1 DoD 全部勾绿;NSFWJS 接入;`/admin/reports` 工作台可用;closed beta(≤ 50 人)发布。
|
||
|
||
**关键交付物**:
|
||
- 生产环境冷启动测试 + Sentry 0 P0
|
||
- `/admin/reports` 路由 admin role 鉴权
|
||
- TestFlight 内测 ≥ 50 用户上线
|
||
|
||
**命令骨架**(≈ 14 条):
|
||
|
||
```bash
|
||
# 1. 接入 NSFWJS 到 Worker 的 T-6.5
|
||
cd apps/worker
|
||
# src/pipeline/nsfw-check.ts 写完并加入 src/index.ts pipeline 注册
|
||
|
||
# 2. Edge Function E-14 report 完善
|
||
cd ../..
|
||
# supabase/functions/report/index.ts 写入 reports 表 + 通知 admin
|
||
|
||
# 3. /admin/reports 路由
|
||
cd apps/web
|
||
mkdir -p app/admin/reports
|
||
# 写 middleware.ts 检查 auth.users.app_metadata.role === 'admin'
|
||
# 写 app/admin/reports/page.tsx(工单队列 + 判定按钮)
|
||
|
||
# 4. 添加 admin role 给 reviewer 账号
|
||
psql "$DATABASE_URL" -c \
|
||
"UPDATE auth.users SET raw_app_meta_data = jsonb_set(raw_app_meta_data, '{role}', '\"admin\"') WHERE email='reviewer@crowdroom.app';"
|
||
|
||
# 5. 法律文档(MDX)
|
||
# apps/web/app/legal/privacy/page.mdx → 复制 plans/CrowdRoom/09_privacy.md
|
||
# apps/web/app/legal/terms/page.mdx
|
||
# apps/web/app/legal/community/page.mdx → 引用 plans/CrowdRoom/10_governance.md
|
||
|
||
# 6. 生产环境部署
|
||
vercel --prod --cwd apps/web
|
||
supabase functions deploy --project-ref <ref>
|
||
flyctl deploy -a crowdroom-worker
|
||
|
||
# 7. Sentry 验证
|
||
curl -X POST "https://app.crowdroom.app/api/test-sentry"
|
||
# 进入 Sentry 项目确认 issue 已上报
|
||
|
||
# 8. PostHog funnel 验证
|
||
# 跑一遍「注册 → 扫描 → 上传 → 浏览 → 点赞」完整流程,进 PostHog 查 funnel
|
||
|
||
# 9. TestFlight 推 closed beta
|
||
fastlane beta
|
||
# 在 App Store Connect 把 build 加入「CrowdRoom Closed Beta」群组(≤ 50 用户)
|
||
|
||
# 10. 烟雾测试矩阵(覆盖 11 条契约 X1-X5 + Y1-Y6)
|
||
pnpm -w smoke
|
||
|
||
# 11. tag + release
|
||
pnpm exec semantic-release
|
||
git tag v0.5-mvp
|
||
git push origin v0.5-mvp
|
||
|
||
# 12. 写发布通告(README → 写一个「CrowdRoom v0.5-mvp 闭门内测开启」简短公告)
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 关键风险与开源依赖的「逃生通道」
|
||
|
||
> 每条列出:**如果 X 项目突然废弃 / 被收购 / license 变更,我们的迁移路径是 Y**。
|
||
> 与 [`ROADMAP.md`](ROADMAP.md) §4 风险登记册联动;本节聚焦「**开源 / 云依赖层面**」的逃生,不重复业务风险。
|
||
|
||
| # | 触发条件(依赖变脸) | 迁移路径 | 估计切换工时 |
|
||
|---|----------------------|----------|--------------|
|
||
| **E-1** | **Supabase 被收购 / 涨价 / 关停 Cloud** | 启 [`docker-compose.yml`](docker-compose.yml) 中的 [`supabase/supabase`](https://github.com/supabase/supabase) 自托管栈;DNS 切到自建 Caddy;Storage 底层 driver 从 R2 改 [`minio/minio`](https://github.com/minio/minio)。supabase-swift / @supabase/supabase-js SDK URL 仅改 base URL,**业务代码 0 修改** | 2 人周 |
|
||
| **E-2** | **Vercel 涨价 / 区域不可达** | Web App 切到 Cloudflare Pages(Next.js 全功能支持)或自建 [`caddyserver/caddy`](https://github.com/caddyserver/caddy) + Node SSR server;Edge Functions(OG endpoint)迁到 [`denoland/deno`](https://github.com/denoland/deno) Deno Deploy 或 Cloudflare Workers | 1.5 人周 |
|
||
| **E-3** | **gltf-transform 停更** | 直接 fork 到 `crowdroom/glTF-Transform`;核心需求(Draco / Meshopt / 节点重命名)只用到 ≤ 30% API,可自行维护;同时启动 [`google/draco`](https://github.com/google/draco) + [`zeux/meshoptimizer`](https://github.com/zeux/meshoptimizer) 直接调用做 Plan B | 1 人周 |
|
||
| **E-4** | **PostHog Cloud 涨价 / 关停** | 自托管 [`PostHog/posthog`](https://github.com/PostHog/posthog)(一行 helm install),或换 [`plausible/analytics`](https://github.com/plausible/analytics)(功能少但够用,funnel 需自建)| 1 人周(含数据迁移) |
|
||
| **E-5** | **Apple `usdzconvert` 在 GitHub Actions macOS runner 不可用** | 完全切到 [`PixarAnimationStudios/OpenUSD`](https://github.com/PixarAnimationStudios/OpenUSD) Linux 构建产物(pip install usd-core);Worker Dockerfile 已预装,无需新增运维 | 0.5 人周 |
|
||
| **E-6** | **Cloudflare R2 涨价 / 退出区域** | Storage 底层 driver 切 [`minio/minio`](https://github.com/minio/minio) 自托管 + Backblaze B2 异地灾备;CDN 切 [`bunnyway/bunny.net`](https://bunny.net) 或 Tencent EdgeOne(CN 用) | 1.5 人周 |
|
||
| **E-7** | **NSFWJS 模型 license 变更 / 不更新** | 换 [`bumble-tech/private-detector`](https://github.com/bumble-tech/private-detector) 或自训模型;短期内禁用自动审核、强化人工审核 [`/admin/reports`](04_web_app_plan.md) 工作台 | 1 人周 |
|
||
| **E-8** | **Sentry FSL 协议恶化 / 价格暴涨** | 切到 [`glitchtip/glitchtip-backend`](https://gitlab.com/glitchtip/glitchtip-backend)(完全 MIT、Sentry SDK 协议兼容,0 代码改动)| 0.5 人周 |
|
||
| **E-9** | **Three.js / R3F 核心维护者跑路** | Three.js 已被 mrdoob / Don McCurdy 等多人维护,停更概率 < 1%;如发生则锁版本 + 自维护补丁;最终极方案降级到 [`google/model-viewer`](https://github.com/google/model-viewer) 提供静态房间预览(功能退化但不挂) | 2 人周(功能降级) |
|
||
| **E-10** | **GitHub Actions 价格暴涨 / 不可用** | 自托管 [`actions/runner`](https://github.com/actions/runner) on 自有 VPS(Hetzner / 腾讯云);macOS runner 改用真实 Mac mini in office | 1.5 人周 |
|
||
|
||
> **核心承诺再次声明**:本方案中**没有任何一个组件存在「无逃生通道」的硬依赖**——10 条逃生路径均已识别,迁移工时合计 ≤ 13 人周,相当于「2 个工程师 6.5 周可完成全栈迁离」。
|
||
|
||
---
|
||
|
||
## 8. 成本预算(MVP 阶段,月度)
|
||
|
||
> 假设:单房间 source `.usdz` 30 MB / canonical `.glb` 5 MB / 每用户月均扫 5 个房间、浏览 50 个房间。
|
||
|
||
| 服务 / 组件 | 免费额度 | 50 DAU 估算成本 | 500 DAU 估算成本 | 5 000 DAU 估算成本 | 何时该切自托管 |
|
||
|---|---|---|---|---|---|
|
||
| **Supabase Cloud**(Pro) | 500 MB DB + 1 GB Storage + 5 GB Egress + 500k Edge invocations | $0 / 月(仍在免费档) | $25 / 月(Pro 起步) | $200-400 / 月(Storage + Egress + Compute) | 月费 > $500 时(≈ 5k DAU) |
|
||
| **Cloudflare R2** | 10 GB Storage / 1M class A + 10M class B ops | $0 | $5(≈ 300 GB) | $40(≈ 2 TB Storage) | 月费 > $200 |
|
||
| **Vercel** | 100 GB Bandwidth / 1k build minutes / Hobby plan | $0 | $20 / 月(Pro seat) | $80 / 月(Pro + Edge invocations) | 月费 > $200 |
|
||
| **Fly.io Worker** | 3 shared vCPU + 256 MB(小)免费 | $0-5(idle) | $30(1× 1 vCPU 2 GB) | $150(3-4× 实例) | 月费 > $300 |
|
||
| **Apple Developer** | – | $99 / 年(≈ $8.25 / 月) | 同左 | 同左 | 永远固定 |
|
||
| **Sentry Cloud** | 5k events / 月(Developer Plan) | $0 | $26 / 月(Team) | $80 / 月(含 transactions) | 月费 > $100,切 GlitchTip |
|
||
| **PostHog Cloud** | 1M events / 月 + Session Replay | $0 | $0(仍免费档) | $50 / 月 | 月费 > $200 |
|
||
| **GitHub Actions** | 2000 分钟 / 月(含 macOS x10 倍率) | $0 | $30 / 月(含 macOS runner) | $100 / 月 | 月费 > $300 |
|
||
| **域名 / DNS** | – | $1 / 月 | $1 | $1 | – |
|
||
| **合计(粗估)** | – | **≈ $10 / 月** | **≈ $140 / 月** | **≈ $700-1 000 / 月** | – |
|
||
|
||
> **决策红线**:月度成本 > $1 000 即触发「自托管启动」评估;本方案的撤退路径在 §7 已完整说明,自托管 5k DAU 月度成本(含 VPS + 流量)估算 ≈ $300-500,明确低于纯云。
|
||
|
||
---
|
||
|
||
## 9. 工时估算与人员配比
|
||
|
||
| 角色 | 周次投入 | 主要任务对应章节 |
|
||
|---|---|---|
|
||
| **iOS 工程师 × 1** | W5 全职 + W1/W3/W7 各 20% | §6 W5(iOS 全部);其他周做 client SDK 联调 |
|
||
| **Web 工程师 × 1** | W1 / W4 / W7 全职 + 其他周 50% | §6 W1(脚手架)、W4(详情页+图层)、W7(Remix);W8 部署联调 |
|
||
| **全栈 / 后端 × 1** | W2 / W3 / W6 / W8 全职 + 其他周 50% | §6 W2(Schema)、W3(Edge Fn)、W6(Worker)、W8(治理+部署) |
|
||
| **设计师 × 0.3** | W1-W4 各 ≈ 10 小时 | UI 设计稿、4 层面板视觉规范、shadcn 主题、OG 卡片模板 |
|
||
| **PM × 0.2** | 贯穿 8 周 | sprint planning、DoR/DoD 核对、risk register 维护、内测招募 |
|
||
| **法务 / DPO 兼职** | W2 / W8 各 4 小时 | 隐私政策审核、CC0 资产合规审查 |
|
||
|
||
**人周合计**:iOS 1 × 8 × 0.55 + Web 1 × 8 × 0.7 + 后端 1 × 8 × 0.7 + 设计 0.3 × 4 + PM 0.2 × 8 ≈ **17 个有效人周 / 8 周自然周**(≈ 32 人周计入并行 + 切换 + 评审折扣后的真实工时)。
|
||
|
||
> 与 [`ROADMAP.md`](ROADMAP.md) §2.1 M1 DoD 的「10 项必须全过」对齐;任何角色缺位 ≥ 1 周即触发 M1 延期评估。
|
||
|
||
---
|
||
|
||
## 10. M1 验收标准(出口清单)
|
||
|
||
> 与 [`ROADMAP.md`](ROADMAP.md) §2.1 M1 DoD 严格对齐,并补充本执行方案特有的「开源工程化」验收点。
|
||
|
||
### 10.1 功能验收(12 条)
|
||
|
||
- [ ] **F-01** iOS App 通过 TestFlight 审核,可邀请 ≥ 100 用户
|
||
- [ ] **F-02** iOS 端能扫描房间、上传,**人脸端侧自动脱敏 p95 ≤ 15 s**(iPhone 12 Pro)
|
||
- [ ] **F-03** Web 端 `/` `/r/[room_id]` `/login` `/upload` `/u/[username]` 5 个核心路由可用
|
||
- [ ] **F-04** 4 层(walls/floor/furniture/materials)可独立 toggle 可见性,viewState 可分享
|
||
- [ ] **F-05** 1 张材质能在 Web 端 **p95 ≤ 200 ms** 热替换
|
||
- [ ] **F-06** 1 件家具能 OBB 自动对齐替换,超 1.5× 时有警告
|
||
- [ ] **F-07** Remix 端到端可用:fork → 编辑 → 自动保存 → 发布 → `/remix/[id]` 可见
|
||
- [ ] **F-08** [`02_api_contract.md`](02_api_contract.md) 19 个端点中 MVP 必须的 **13 个**(E-01~E-13, E-15)线上可用
|
||
- [ ] **F-09** 转码 Worker **p95 ≤ 60 s**(含 USDZ → glb + Draco + manifest 生成)
|
||
- [ ] **F-10** **100 个并发上传**,转码成功率 ≥ 95%
|
||
- [ ] **F-11** NSFWJS 接入 `upload-complete` 后端流水,命中后写 `moderation_signals`,在 `/admin/reports` 队列可见
|
||
- [ ] **F-12** 治理:举报通道 E-14 可用,处罚阶梯 L1-L3 工具就绪
|
||
|
||
### 10.2 工程化验收(10 条)
|
||
|
||
- [ ] **G-01** 全栈 `docker-compose up` 一键本地启动(Supabase + Worker + Redis + MinIO 撤退栈)
|
||
- [ ] **G-02** CI 三条 workflow 全绿(`ci-web` / `ci-ios` / `ci-worker`),任何 PR < 10 分钟反馈
|
||
- [ ] **G-03** Lighthouse CI 桌面性能分 **≥ 85**(首页 + 详情页),移动 LCP ≤ 3 s
|
||
- [ ] **G-04** Trivy 扫描 Worker 镜像 0 个 HIGH/CRITICAL 漏洞
|
||
- [ ] **G-05** Playwright E2E 覆盖 [`02_api_contract.md`](02_api_contract.md) §8 的 **11 条契约**(X1-X5 + Y1-Y6)
|
||
- [ ] **G-06** Sentry 接入 iOS / Web / Worker 三端,0 P0 issue 持续 ≥ 7 天
|
||
- [ ] **G-07** PostHog funnel「注册 → 扫描 → 上传 → 浏览 → 点赞」5 步采集率 ≥ 30%
|
||
- [ ] **G-08** [`packages/shared-types/`](packages/shared-types/) 的 Zod schema 被 iOS / Web / Worker / Edge Function 4 端导入,无任何字段漂移
|
||
- [ ] **G-09** semantic-release 自动 tag + 写 [`CHANGELOG.md`](CHANGELOG.md);v0.5-mvp tag 上线
|
||
- [ ] **G-10** 所有依赖通过 Dependabot weekly 扫描;当前 0 个高危待修
|
||
|
||
### 10.3 合规验收(4 条)
|
||
|
||
- [ ] **C-01** [`09_privacy.md`](09_privacy.md) §6.1 的 8 项 GDPR MVP 必做项全部上线(含 PrivacyManifest、Cookie 通知、隐私政策页)
|
||
- [ ] **C-02** 公共资产库 ≥ 30 件家具 + ≥ 20 种 PBR,**全部 CC0**;硬过滤拒收非 CC0 资产
|
||
- [ ] **C-03** 退场承诺页 `/legal/exit-promise` 上线([`10_governance.md`](10_governance.md) §10 公开数据 CC0 镜像承诺)
|
||
- [ ] **C-04** E-18 三阶段账号注销 + E-19 数据导出在生产环境可端到端跑通
|
||
|
||
### 10.4 内测验收(2 条)
|
||
|
||
- [ ] **B-01** 内部 dogfood ≥ 100 个真实房间上传无 P0 事故
|
||
- [ ] **B-02** Closed Beta(≤ 50 用户)发布后 1 周内 NPS ≥ 20
|
||
|
||
**合计 28 条验收点**——全部勾绿即 M1 出口。
|
||
|
||
---
|
||
|
||
## 11. 本章小结
|
||
|
||
| 关键产出 | 一句话 |
|
||
|----------|--------|
|
||
| **77 个 GitHub repo** | 覆盖 iOS / Web / Worker / BaaS / 审核 / 资产 / DevOps / 撤退 8 层,全部 ≥ 1k star + 宽松 license + 近 6 月活跃 |
|
||
| **5 个深度评估** | gltf-transform / R3F+drei / Apple USD / supabase-swift / NSFWJS 全部「**MVP 推荐 + 风险已识别 + 逃生路径明确**」 |
|
||
| **1 个 Monorepo** | pnpm + Turborepo;iOS / Web / Worker / Edge Function / Schemas 同仓共版本;3 条理由 |
|
||
| **8 周 × ≈ 14 条命令** | W1 仓库初始化 → W8 closed beta;累计 **116 条可执行命令**,零伪命令 |
|
||
| **10 条逃生通道** | 任意单组件失效 ≤ 2 人周可迁移;全栈撤离 ≤ 13 人周 |
|
||
| **28 条 M1 验收点** | 功能 12 + 工程化 10 + 合规 4 + 内测 2,与 [`ROADMAP.md`](ROADMAP.md) §2.1 严格对齐 |
|
||
|
||
读完本章你应能:
|
||
- ✅ 给一个新工程师一份「**从 W1 第一条 `mkdir crowdroom` 命令到 W8 closed beta 发布**」的可执行清单
|
||
- ✅ 在任意云供应商变脸时 ≤ 2 周内完成栈迁移
|
||
- ✅ 用 < $200 / 月的成本支撑 500 DAU;切自托管后 < $500 / 月支撑 5k DAU
|
||
- ✅ 在 PR review 时按 §10 28 条 checkbox 逐条核对 M1 出口
|
||
|
||
---
|
||
|
||
**文档版本**:v0.1 · 2026-05-19
|
||
**维护者**:CrowdRoom 工程组
|
||
**下一步阅读**:完成 W0 §5 准备清单后 → 进入 §6.1 W1 第一条命令。 |