Go to file
noelorin ffc69d691c feat(web): acid redesign + hero mouse parallax
- 纯黑/冷白双主题 + 100% 饱和电光酸色(lime/cyan/magenta),强对比冲击
- 氛围层:网格 + 双色光晕 + 扫描线 + 噪点;HUD 边角 / 跑马灯 / 信号条 / 滚动入场
- hero 鼠标视差:ghost 水印反向漂移 + 标题/deck/统计分层位移,rAF+lerp 平滑,reduced-motion 降级
- 修复整页刷新时房间列表鉴权竞态、推流页右栏拉伸空白
- vite dev 根路径服务 + /api /rtc /live 代理(build 仍落 /static/)
2026-08-20 16:15:54 +08:00
api feat(auth): 基于 casbin 的登录与 RBAC 权限系统 2026-08-19 16:42:17 +08:00
cmd/server feat(auth): 基于 casbin 的登录与 RBAC 权限系统 2026-08-19 16:42:17 +08:00
data feat(auth): 基于 casbin 的登录与 RBAC 权限系统 2026-08-19 16:42:17 +08:00
deploy feat(auth): 基于 casbin 的登录与 RBAC 权限系统 2026-08-19 16:42:17 +08:00
docs feat(auth): 基于 casbin 的登录与 RBAC 权限系统 2026-08-19 16:42:17 +08:00
gen feat(auth): 基于 casbin 的登录与 RBAC 权限系统 2026-08-19 16:42:17 +08:00
internal feat(web): acid redesign + hero mouse parallax 2026-08-20 16:15:54 +08:00
web feat(web): acid redesign + hero mouse parallax 2026-08-20 16:15:54 +08:00
.env.example feat(auth): 基于 casbin 的登录与 RBAC 权限系统 2026-08-19 16:42:17 +08:00
.gitignore feat(web): rewrite frontend as SolidJS + Tailwind v4 SPA with chat UI 2026-08-20 15:03:03 +08:00
README.md docs: document chat/danmaku and broadcaster external push API; add design spec 2026-08-20 15:03:44 +08:00
buf.gen.yaml feat: live SFU fan-out demo (Cloudflare Realtime + SRS) 2026-08-17 15:14:51 +08:00
design.md docs: document chat/danmaku and broadcaster external push API; add design spec 2026-08-20 15:03:44 +08:00
go.mod feat(auth): 基于 casbin 的登录与 RBAC 权限系统 2026-08-19 16:42:17 +08:00
go.sum feat(auth): 基于 casbin 的登录与 RBAC 权限系统 2026-08-19 16:42:17 +08:00

README.md

直播 SFU 分流 Demo(Cloudflare Realtime · SRS)

参考 GOSpeak 技术栈的直播 SFU 分流演示。控制面用 protobuf / gRPC 定义,媒体面走 Cloudflare Realtime(主 SFU)与 SRS(本地对照)的 WebRTC 扇出。

  • 一路推流 → SFU 扇出 → 多路拉流。
  • 同一路发布可同时落到 Cloudflare Realtime 与 SRS 两条分发链路(分流)。
  • 浏览器经服务端反向代理直连 SFU,凭证不出服务端。
  • 默认 DB 使用 Turso 嵌入式 (libSQL file):房间分发拓扑持久化到本地文件。
  • 前端为 SolidJS + TanStack Router/Query + Ark UI + Tailwind v4 单页应用,构建产物嵌入 Go 二进制。

目录结构

live-sfu-demo/
  api/live_sfu.proto       # protobuf 控制面契约
  gen/                     # buf generate 产出(Go)
  internal/
    config/                # 运行参数
    db/                    # Turso 嵌入式 (libSQL file) 持久化
    sfu/cloudflare/        # Cloudflare Realtime REST 客户端 + Provider
    sfu/srs/               # SRS Provider
    server/                # gRPC 服务 + JSON 网关 + SRS/CF 媒体反代 + 房间扇出状态
      static/              # 前端构建产物(由 web/ 构建生成,Go embed)
    auth/                  # Casbin RBAC + JWT + bcrypt 用户存储
  cmd/server/main.go
  deploy/                  # docker-compose.yml + srs.conf
  web/                     # 前端工程(Solid + TanStack + Ark UI + Tailwind v4)
    src/                   # 页面 / 组件 / 库
    vite.config.ts         # 构建输出到 ../internal/server/static

运行(SRS 链路,开箱即跑)

# 1) 起本地 SRS(WHIP/WHEP/HLS 三协议后端)
cd deploy && SRS_CANDIDATE=127.0.0.1 docker compose up -d srs

# 2) 起 Demo 控制面(默认 Turso 嵌入式,无需额外配置)
cd live-sfu-demo
cp .env.example .env
go run ./cmd/server

# 3) 打开浏览器(发布/订阅需先登录,见下方鉴权说明)
#    首页:   http://localhost:8088/
#    发布:   http://localhost:8088/publish?room=demo
#    观看:   http://localhost:8088/watch?room=demo

两个标签页用同一房间名即可配对。发布页勾选的分发后端会在「分发状态」中实时显示; SRS 链路同时产出 HLS/FLV,分发状态面板可直接获取 /live/<stream>.m3u8 / .flv 链接, 经 Go 网关 (/live/*) 反代,无需浏览器直连 8080。

鉴权(Casbin RBAC + JWT)

控制面已启用登录与 RBAC:

  • 发布 / 订阅 / 房间管理需要登录;guest 仅可读取配置与 SRS 切片流(HLS/FLV)。
  • 默认账号(bcrypt,落盘 data/users.json):
    • admin / Admin123!(admin)
    • publisher / Publisher123!(publisher)
    • viewer / Viewer123!(viewer)
  • 登录后服务端下发 HttpOnly Cookie,前端同源请求自动携带;在 /login 登录。

默认 DB:Turso 嵌入式

房间“分发目标”拓扑(stream_targets)默认持久化到 Turso 嵌入式 (github.com/tursodatabase/go-libsql 的 SQLite 兼容 file 模式),而非纯内存。

  • DSN:TURSO_DATABASE_URL=file:./data/live-sfu.db?cache=shared&_journal_mode=WAL(默认)
    • 兼容 DATABASE_URL 覆盖
    • 仅支持嵌入 file: / :memory:,拒绝 libsql:// 远程(嵌入适配专注单机持久化)
    • 自动建表 (stream_targets, rooms),SetMaxOpenConns(1) 适配 SQLite 单写模型
    • 重启后自动 LoadAll 恢复房间
# 默认即嵌入文件
TURSO_DATABASE_URL=file:./data/live-sfu.db?cache=shared&_journal_mode=WAL

# 验证持久化:发布后重启,/api/rooms 仍在
curl -b cookie.txt http://localhost:8088/api/rooms | jq
sqlite3 data/live-sfu.db "select room, backend, stream from stream_targets;"

运行(Cloudflare Realtime 链路,主 SFU)

在 .env 填入 Cloudflare Realtime 凭证后,go run ./cmd/server 即启用主 SFU:

CF_APP_ID=xxxxxxxxxxxx
CF_APP_SECRET=xxxxxxxxxxxx
  • 发布:服务端用 CF_APP_SECRET 创建 Cloudflare session,浏览器只交换 SDP(/api/cf/... 反代注入 Bearer)。
  • 观看:服务端为观众创建 viewer session,并订阅发布者 session 的轨道(location=remote)。

Cloudflare 未配置时,UI 会标注「未配置」,SRS 链路不受影响。

protobuf / gRPC

# 修改 api/live_sfu.proto 后重新生成
export PATH="$HOME/go/bin:$PATH"
buf generate api

契约(LiveSFU 服务):GetConfig / ListRooms / Publish / Subscribe / StopStream / WatchRoom(服务端流式推送房间分发拓扑)以及 Login / Register / GetMe / ListUsers / UpdateUserRole。 gRPC 监听 GRPC_PORT(默认 9090),浏览器走同端口的 JSON 网关(protojson)。

前端开发(web/)

cd web
pnpm install
pnpm dev      # http://localhost:5173 热更新
pnpm build    # 构建进 ../internal/server/static(被 Go embed)

技术栈:SolidJS(vite-plugin-solid)、TanStack Router + Query、Ark UI(无样式原语)、 Tailwind v4(CSS-first @theme 做 design token 体系化驱动)。design token 集中在 web/src/index.css 的 @theme,组件语义类在 web/src/components/ui.tsx 中以 token 派生。

分流拓扑

 publisher ──WHIP/tracks.new──▶ Cloudflare Realtime SFU ──▶ viewer(s)
          └────WHIP────────────▶ SRS SFU ─┬─▶ WHEP  viewer(s)  (低延时 0.2-0.5s)
                                         ├─▶ HLS   viewer(s)  (全端兼容,/live/*.m3u8)
                                         └─▶ FLV   viewer(s)
                一路 WHIP 推流,SRS 自动 remux 三协议同出;房间分发目标由 WatchRoom 实时广播

SRS 推流链路详解见 docs/streaming-pipeline.md。

主播外部推送 API(弹幕广播)

除观众可发送的 POST /api/room/{room}/chat 外,额外提供仅主播可用的独立推送端点,供 OBS 脚本 / 机器人 / 管理工具以主播身份推送弹幕:

POST /api/room/{room}/broadcast
Authorization: Bearer <publisher-or-admin jwt>
Content-Type: application/json

{ "message": "欢迎来到直播间", "color": "#4ea8ff" }
  • 权限:需 room:publish(仅 publisher / admin),未登录返回 401,越权返回 403。
  • 推送的弹幕自动标记 host: true,经同一 chatHub 广播,主播端与观众端实时可见;前端在消息列表与弹幕叠加层以「主播」标识区分。
  • 同样受滑动窗口限流(房间 + 发送者 + IP,3s 内 5 条)保护。
  • 外部工具用 Authorization: Bearer <token> 调用;浏览器端同源请求自动携带登录 Cookie。