Omi Admin Dashboard 深度指南:基于 Next.js 的内部运营后台架构、鉴权与本地开发全解
2026/9/17 8:32:21 网站建设 项目流程

Omi Admin Dashboard 深度指南:基于 Next.js 的内部运营后台架构、鉴权与本地开发全解

【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend

导读

web/admin是 Omi 项目(AI 眼镜助手)的内部运营管理后台,运行在admin.omi.me,采用 Next.js 构建。它以浏览器端 Firebase 登录认证管理员身份,并在同源app/api/下暴露受保护的 Next.js Route Handler,由服务端统一读取 Firestore、调用 Omi 后端 API 与第三方厂商 API,浏览器代码永远不会拿到服务端密钥。本文基于仓库内 web/admin/README.md 展开,结合.env.examplelib/auth.tslib/dev-auth.tsDockerfile与 GitHub Actions 部署工作流等源码,系统讲解其本地启动流程、开发鉴权绕过机制、环境变量全量说明、权限模型以及 CI/部署链路,读者学完后即可独立完成本地开发、联调、自检与生产部署全流程。

一、整体架构:浏览器、Route Handler 与服务端的信任边界

1.1 三层职责划分

从 README 与目录结构可以提炼出后台的经典三层架构:

  • 客户端(app/(protected)/dashboard/**:负责渲染内部运营界面(analytics、reviews、announcements、fair-use、subscriptions 等数十个页面),通过 Firebase 登录获得身份,但不在浏览器里直接读取 Firestore,也不自行构造鉴权头。根据 web/admin/AGENTS.md 的硬性约定,所有网络请求必须经由hooks/useAuthToken.ts统一携带 token。
  • 服务端 Route Handler(app/api/omi/**app/api/stats/**等):每个受保护路由入口都调用verifyAdmin(request)(见 lib/auth.ts)完成鉴权,之后才代表管理员读取 Firestore 或调用 Omi API / 厂商 API。第三方凭证(Stripe、PostHog、Typesense、GoAffPro 等)只存在于服务端环境变量中,浏览器永远接触不到。
  • 上游服务:Omi 后端 API(backend/)、Firestore、LLM 网关(OMI_LLM_GATEWAY_URL)以及各类厂商系统。

1.2 为什么浏览器拿不到服务端凭证

README 明确指出:"Browser code never receives the server credentials"。实现上,服务端密钥只在 Route Handler 进程内使用,例如 LLM 网关路由只依赖OMI_LLM_GATEWAY_SERVICE_TOKEN(服务端令牌),而不会持有任何 LLM 提供商的 API Key。这层设计让后台可以安全地聚合管理 Stripe、Shopify、ShipBob、Mixpanel 等敏感系统,而不必担心把密钥泄露进 JS bundle。

二、本地开发环境搭建(UI 快速起步)

2.1 最小安装步骤

README 给出了标准的三步安装:

cd web/admin cp .env.example .env.local npm ci

注意使用npm ci(而非npm install),它会严格按package-lock.json锁定依赖版本,保证与 CI 环境一致。项目核心依赖见 package.json:Next.js 16、React 18、Firebase 客户端 SDKfirebase11 与 Admin SDKfirebase-admin13、SWR 数据请求、Radix UI 组件库、recharts 图表与 zod 校验。

2.2 跳过登录的开发鉴权绕过(DEV BYPASS)

纯 UI 开发不需要真实 Firebase 登录,只需在.env.local中设置:

NEXT_PUBLIC_DEV_BYPASS_AUTH=1

然后启动:

npm run dev

打开 http://localhost:3000/dashboard 即可。绕过的固定本地身份为dev-admin,它会同时提供给客户端和受保护路由处理器;对未配置对应服务依赖的 API 卡片,页面会显示错误(README 已提示:"API cards whose backing service is not configured will show errors"),这属于预期行为。

实现细节:绕过逻辑在 lib/dev-auth.ts 中非常明确:

export const DEV_BYPASS_ENABLED = process.env.NODE_ENV !== "production" && (process.env.NEXT_PUBLIC_DEV_BYPASS_AUTH === "1" || process.env.DEV_BYPASS_AUTH === "1"); export const DEV_BYPASS_UID = "dev-admin"; export const DEV_BYPASS_TOKEN = "dev-bypass-token";

两个关键事实值得注意:

  • 绕过同时受NODE_ENV !== "production"和显式开启标志的双重约束,在生产构建中被硬性禁用,无法通过误设环境变量绕开;
  • lib/auth.ts 中,服务端只接受Authorization: Bearer dev-bypass-token这一特定令牌并映射到dev-admin,其余情况一律走 Firebase ID Token 校验流程。

⚠️ README 特别警告:不要在.env.local中放入生产凭证

三、对接本地 Omi 后端进行真实联调

当需要验证真实的 Omi API 集成时,需要把backend/也跑起来(按 backend/AGENTS.md 开发者指南操作),并使用独立的开发或离线数据环境。在web/admin/.env.local中设置:

NEXT_PUBLIC_DEV_BYPASS_AUTH=1 NEXT_PUBLIC_OMI_API_URL=http://localhost:8080 OMI_API_SECRET_KEY=<the same value as backend ADMIN_KEY>

3.1 双重凭证的拼接逻辑

README 说明了后台的鉴权方式:向 Omi 后端发送基础密钥(base key)由该密钥 + 已认证 UID 拼接而成的 token两个值。绕过模式下 UID 恒为dev-admin,因此只要本地后端的ADMIN_KEYOMI_API_SECRET_KEY一致,即可通过认证。可对照 lib/services/omi-api/client.ts 中的客户端实现验证实际请求构造方式。

⚠️ 危险边界:管理员操作会真实修改后端及其数据存储,因此该模式严禁指向生产环境

3.2 数据类与外部系统类页面的额外配置

不同功能对凭证的需求分档如下:

功能类别所需配置说明
读写 Firestore 的路由FIREBASE_PROJECT_IDFIREBASE_CLIENT_EMAILFIREBASE_PRIVATE_KEY必须是非生产 Firebase 项目
使用外部系统的页面对应系统在.env.example中的凭证如 Stripe、PostHog、Typesense、GoAffPro
LLM 网关路由OMI_LLM_GATEWAY_URL+ 服务端专用OMI_LLM_GATEWAY_SERVICE_TOKEN服务端只持有网关服务令牌,从不接触 LLM 提供商凭证
真实登录流程测试NEXT_PUBLIC_FIREBASE_*客户端变量仅绕过模式关闭时才需要

四、环境变量全量参考(.env.example 逐项解析)

完整的模板见 web/admin/.env.example,按用途可划分为九组,这里给出每组的关键变量与使用场景:

4.1 开发与鉴权

  • NEXT_PUBLIC_DEV_BYPASS_AUTH:本地开发入口开关,置1免登录进入后台;NODE_ENV=production时被硬禁用。

4.2 Firebase 客户端 SDK(可公开暴露)

NEXT_PUBLIC_FIREBASE_API_KEYNEXT_PUBLIC_FIREBASE_AUTH_DOMAINNEXT_PUBLIC_FIREBASE_PROJECT_IDNEXT_PUBLIC_FIREBASE_STORAGE_BUCKETNEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_IDNEXT_PUBLIC_FIREBASE_APP_IDNEXT_PUBLIC_FIREBASE_VAPID_KEY——这些属于浏览器端安全公开的配置,用于真实登录与消息推送。

4.3 Firebase Admin SDK(服务端专用,绝不暴露)

  • FIREBASE_PROJECT_ID/FIREBASE_CLIENT_EMAIL/FIREBASE_PRIVATE_KEY:服务端校验 Firebase ID Token、读取 Firestore 的adminData/{uid}集合时使用。

4.4 Omi 后端 API

  • NEXT_PUBLIC_OMI_API_URL:Omi 后端地址(本地联调指向http://localhost:8080)。
  • OMI_API_SECRET_KEY:服务端专用,须与后端ADMIN_KEY一致。
  • NEXT_PUBLIC_PLUGINS_APP_ID:插件应用标识。

4.5 外部系统凭证(各页面按需使用)

  • Stripe:STRIPE_SECRET_KEY(对应 lib/stripe.ts 的订阅指标读取)。
  • Typesense:TYPESENSE_HOSTTYPESENSE_API_KEYTYPESENSE_CONVERSATION_COLLECTION(对话检索集合名)。
  • Redis:REDIS_HOSTREDIS_PORTREDIS_PASSWORD(缓存预热使用,见 lib/redis.ts)。
  • PostgreSQL:POSTGRES_URL
  • ShipBob / Shopify:SHIPBOB_API_KEYSHOPIFY_STORESHOPIFY_ACCESS_TOKEN(分销与订阅订单)。
  • Mixpanel:MIXPANEL_SECRETMIXPANEL_API_BASE
  • PostHog:POSTHOG_PERSONAL_API_KEYPOSTHOG_PROJECT_IDPOSTHOG_HOST(增长指标统计)。
  • LLM 网关:OMI_LLM_GATEWAY_URLOMI_LLM_GATEWAY_SERVICE_TOKEN
  • 其他:ENCRYPTION_SECRETGITHUB_TOKENGOAFFPRO_ACCESS_TOKEN

4.6 成本核算与定时任务(Stats/Infra 页)

  • CRON_SECRET:受保护的定时缓存预计算任务(对应app/api/internal/precompute/route.ts)。
  • ADMIN_INFRA_OVERHEAD_MONTHLYADMIN_SERVICE_COSTS_JSON:估算模式基础设施开销与服务成本表。
  • GCP_BILLING_SA_JSON:计费模式服务账号 JSON(BigQueryjobUser+dataViewer权限,留空则走 ambient ADC),对应 lib/services/gcp-billing.ts。
  • GCP_BILLING_TABLE:计费导出表覆盖项(project.dataset.table)。
  • ADMIN_ANTHROPIC_COST_API_KEYADMIN_OPENAI_COST_API_KEY:成本 API 专用密钥(仅限 ORG-ADMIN,推理密钥会收到 401/403)。模板注释特别说明:故意不命名为ANTHROPIC_API_KEY/OPENAI_API_KEY,因为部署契约会从 admin 服务中剥离这两个名字
  • ADMIN_PLATFORM_COST_SHARES_JSON:桌面/移动端成本分摊比例(来自omi-cost-analysis用量加权报告,结构为{"llm":{"desktop":..,"mobile":..},"core":{...},"asOf":"YYYY-MM-DD","method":".."})。

五、鉴权与权限模型

5.1 生产环境鉴权链路

正常运营中,浏览器使用 Firebase Google 或邮箱登录。鉴权遵循"双重校验"原则:

  1. 客户端:登录后,要求当前用户 UID 存在于 Firestore 的adminData/{uid}文档中,否则视为非管理员;
  2. 服务端(强校验):每个受保护 Route Handler 通过 Firebase Admin SDK 验证 ID Token,不完全信任客户端检查结果。核心逻辑在 lib/auth.ts:
const authorization = request.headers.get('Authorization'); if (DEV_BYPASS_ENABLED && authorization === `Bearer ${DEV_BYPASS_TOKEN}`) { return { uid: DEV_BYPASS_UID }; } // 正常路径:验证 Firebase ID Token + adminData/{uid} 存在性 const decodedToken = await verifyFirebaseToken(token); const adminDoc = await db.collection('adminData').doc(decodedToken.uid).get(); if (!adminDoc.exists) { return NextResponse.json({ error: 'Forbidden: Not an admin' }, { status: 403 }); }

失败时路由返回标准的 401(令牌缺失/无效)或 403(非管理员)响应。

5.2 本地开发的权限矩阵

README 按工作类型给出清晰的权限分档:

工作类型需要的前提条件
纯 UI 工作启用开发绕过即可,无需云端、Firebase 或厂商访问权限
真实登录测试非生产项目中的 Firebase Authentication 用户 + 匹配的adminData/{uid}文档
数据驱动页面能读取非生产 Firestore 项目的服务账号 + 被测页面对应的服务凭证
本地 Omi API 联调本地后端ADMIN_KEY禁止申请或使用生产密钥
通过 GitHub Actions 部署目标部署分支的合并/推送权限 + 目标 GitHub Environment 的审批;需要权限的是工作流的部署身份(而非开发者工作站),需具备 Artifact Registry、Cloud Run 与 Cloud Run 运行时密钥的访问权;若直接走 GCP 部署,还需等价的 Cloud Run、Artifact Registry、服务账号模拟与 Secret Manager 权限

六、提交前自检与 CI 校验

6.1 本地检查命令

README 要求开 PR 前执行:

npm run check npm run build

从 package.json 的 scripts 可以看到check实际是三个检查的串联:npm run lint(ESLint)+npm run typechecktsc --noEmit)+npm test(Vitest 单元测试)。编写或修改测试时用:

npm run test:watch

6.2 测试资产分布

仓库为后台沉淀了较完整的测试体系,可作为编写新测试的参照:

  • 路由测试:app/api/tests/route-param-encoding.test.ts、app/api/omi/team-members/tests/route.test.ts;
  • 页面测试:app/(protected)/dashboard/team/tests/page.test.tsx/dashboard/team/tests/page.test.tsx);
  • 核心逻辑测试web/admin/lib/__tests__/下覆盖鉴权、Stripe 订阅、统计指标诚实性(stats-honesty-routesstats-staleness-honestyprofitability-honesty)、PostHog 行数限制、平台范围路由(platform-scope)、TV 模式等数十个用例。其中多个*-honesty测试说明该项目对后台统计数据的真实性有自动化约束,很值得阅读学习。

6.3 GitHub 侧检查

GitHub 的 web check 会在web/admin/有改动时运行与本地相同的三项检查(ESLint、TypeScript、Vitest),随后执行生产构建。

七、交付路径:GitHub Actions → Docker → Cloud Run

7.1 部署工作流

.github/workflows/gcp_admin.yml 负责部署:推送至maindevelopment分支且包含web/admin/改动时触发。流程要点:

  1. 从本目录构建 Docker 镜像;
  2. 将公开的NEXT_PUBLIC_*值烘焙进镜像;
  3. 服务端密钥在 Cloud Run运行时注入(不写入镜像);
  4. 将 100% 流量切换到新版本 revision。

7.2 Dockerfile 中的密钥治理佐证

Dockerfile 从构建层面印证了 README 的密钥边界设计:

  • 多阶段构建:deps(安装依赖)→builder(构建)→runner(运行),最终阶段使用非 root 的nextjs用户;
  • 构建阶段通过 ARG 接收 9 个NEXT_PUBLIC_*变量(OMI_REQUIRED_PUBLIC_BUILD_INPUTS),并且构建前逐一校验这些变量非空,缺失即失败;
  • 服务端密钥明确不通过 build-arg 传递,注释写着 "Server-side secrets are injected at runtime via Cloud Run Secret Manager — no need to pass them as build-args",运行时阶段同样强调密钥不烘焙进镜像("NOT baked into the Docker image (more secure)");
  • 采用 Next.js standalone 输出(output: 'standalone')配合.next/standalone.next/static拷贝,缩小镜像体积。

7.3 已知缺口与后续方向

README 明确列出当前尚未具备的能力:没有 PR 预览部署、本地 Firebase emulator、预置的种子管理数据,也没有浏览器端到端测试。这些是本地 UI + 后端联调循环进入常规使用后值得优先投入的方向。对于想要贡献的开发者,这是一个明确的切入点。

八、开发注意事项速查

  • 客户端一律使用hooks/useAuthToken.ts发起网络请求,禁止直接调用getIdToken()、自行构造鉴权头或读取 Firestore;
  • 订阅类指标一律使用 lib/stripe-subscriptions.ts,禁止按 price ID 遍历订阅
  • 数据获取契约、SWR 部分失败与订阅范围等更细的约定见 docs/data-contracts.md;
  • /dashboard通过grafana/嵌入 UID 为omi-tv的看板,详见 grafana/README.md;
  • TV 轮播分享链接走/dashboard/tv-links(管理员)→/tv/view/<token>(免登录),约定见 docs/tv-mode.md。

结语

Omi Admin Dashboard 是一个"客户端轻、服务端重"的典型 Next.js 内部后台:浏览器只负责展示与登录,一切敏感读取与写操作都收敛在受verifyAdmin保护的 Route Handler 中。从NEXT_PUBLIC_DEV_BYPASS_AUTH的本地开发捷径,到 FirebaseadminData/{uid}双重校验,再到 Cloud Run 运行时注入密钥与构建期强制校验NEXT_PUBLIC_*非空的部署契约,整条链路的设计始终围绕"服务端密钥不落浏览器、生产凭证不落本地"这一安全基线。理解了这份 README 与配套源码,你就能在本地快速迭代 UI、安全地对联后端,并为后台补齐 PR 预览、Firebase emulator 与 E2E 测试等下一个里程碑。

【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询