一个开源的嵌入式分析后台:把 Tableau Cloud / Tableau Server 的工作簿与视图嵌进你自己的产品,同时把多团队、页面级权限、站点用户与计划管理这些「后台该有的东西」一并给你。它是纯前端、可自托管的源码起点,不是带后端的企业平台。
MIT 许可 · v0.13.0 · 纯前端(无后端 / 无数据库) · React 19 + TypeScript 7 + shadcn/ui + TanStack Router
一、为什么要做它
把 Tableau 嵌进自己的产品,难点从来不在"放一个 iframe":认证要签 Connected App JWT、REST 接口不给你 CORS、
站点角色和计划任务散在一堆端点里、再加上团队隔离与权限控制——这些东西每个项目都要重写一遍,而且每一条都有坑。
1.1 认证这一步最容易翻车
Connected App 的 JWT 要在浏览器里签发、按分钟级过期刷新、还要按能力域申请最小 scope。
1.2 REST 不支持 CORS
Tableau Cloud 的 REST 没有 ACAO 头、预检直接 405,必须同源反代 —— 但嵌入用的 iframe 又直连 Tableau,两条链路要分清。
1.3 后台该有的都得有
多团队、页面级权限、系统配置、帮助页、组件规范……嵌入式分析只是其中一块。
这个项目就是把这些一次性做掉,并且把"为什么这么做"写在代码注释和文档里。它不是别人的模板 fork,是从零搭的。
二、它是什么样的应用
一个纯前端的 SPA(pnpm build 出来就是静态资源),按"跨团队的管理面"和"团队内的工作区"分两层:
| 页面 | 路由 | 说明 |
|---|---|---|
| 工作区 | /t/{slug}/… | Dashboard / Favorites / Recents / Workbooks / Views,数据全部来自你连接的 Tableau 站点 |
| Tableau 站点用户与角色 | /t/{slug}/tableau/users | 读站点用户(自动翻页)、按关键词/角色筛选、改站点角色(本应用唯一的写操作,二次确认) |
| 定时计划运行情况 | /t/{slug}/tableau/schedules | 提取刷新任务、后台作业、订阅计划,只读 |
| 用户 / 团队 / 权限 | /users · /teams · /permissions | 角色 × 路由的页面级权限矩阵,一处登记全站生效 |
| 系统配置 | /config/smtp · /config/login | SMTP 预检、登录页样式与第三方入口的公开参数 |
| 组件总览 | /components | 公共件清单 + 实时预览,新页面照它搭就不会走样 |
| 帮助 / 关于 | /help | 环境自检(当前连的是谁的站点)、版本、第三方版权与商标声明 |
三、核心能力
3.1 Tableau 嵌入
官方 Embedding API v3,Connected App JWT 在浏览器内签发并定时热更新,不重建视图、不闪烁。
3.2 按能力域拆 scope
内容 / 站点用户 / 计划任务各自一条认证链路,而不是一张全权限令牌;某域 401 或限流不拖累其它页面。
3.3 多团队工作区
团队身份进 URL(/t/{slug}/…),收藏与最近浏览按团队分区,两个标签页可以同时停在两个团队。
3.4 页面级权限
一份路由目录驱动侧边栏、路由守卫与权限矩阵;新页面默认 fail-closed,未登记直接报错拦住。
3.5 站点用户与角色
直接读站点用户表;改角色走同一套 REST,失败按 Tableau 错误码给专属文案(席位不足 / 组内最低角色 / 无效角色)。
3.6 计划运行监控
提取刷新频率说人话、连续失败次数成徽章、后台作业按需展开取对象名(避免逐行 N+1)。
3.7 部署即配
站点绑定全部走 .env,附 nginx 反代样例与 /help 的环境自检卡。
3.8 AI 起点
/ai 是能跑的对话页,provider 一处可换;零配置走内置演示 provider,不配 Key 也不空白。
3.9 i18n 与主题
i18next 词典(当前 en-US)+ 亮/暗/跟随系统三种主题,都在页头可切。
3.10 行为有脚本钉住
类型检查 + 一批真实 Chrome 用例,把类型系统证明不了的行为(路由、权限、落盘)钉死。
四、看看真实界面
五、技术栈
| React 19 · TypeScript 7(原生编译器,next 通道) |
| Vite 8 · pnpm 12 · Node 24 |
| Tailwind CSS v4 + shadcn/ui(radix-nova 预设)+ Radix UI |
| TanStack Router(文件路由)· TanStack Query 5 · zustand |
| i18next 26(当前仅 en-US,词典已为多语言准备好) |
| 官方 Embedding API v3 + REST API 3.23 + Connected App JWT |
六、快速开始
git clone https://github.com/tableau-China/Tableau-Embed-Hub.git
cd Tableau-Embed-Hub
pnpm install
pnpm dev 什么都不配也能跑:应用内置演示站点与演示凭据,每个页面都是活的。换自己的站点时只改 .env,不用改代码。
6.1 接上你自己的 Tableau 站点
cp .env.example .env
# 填「站点绑定」三项 + 「Connected App 凭据」三项,然后重新构建 | 你想改 | 改哪里 |
|---|---|
| 站点(地址 / Site name / 站点内容 URL / 嵌入用户) | VITE_TABLEAU_SERVER_URL · _SITE_NAME · _SITE_CONTENT_URL · _EMBED_USER |
| Connected App 凭据 | VITE_TABLEAU_CLIENT_ID · _SECRET_ID · _SECRET_VALUE |
| 只显示某个项目 | VITE_TABLEAU_PROJECT(留空 = 不过滤) |
| REST 反代路径(要和生产网关一致) | VITE_TABLEAU_API_BASE(默认 /tableau-proxy) |
6.2 部署三步
- pnpm build,把 dist/ 丢给任意静态托管(CDN / nginx / S3+CDN 均可);
- 按仓库里的 nginx 样例,把 /tableau-proxy 反代到你的 Tableau 站点(路径必须和 VITE_TABLEAU_API_BASE 一致);
- 在 Connected App 里把生产域名加进 Trusted Sites。
前端是 history 路由,静态托管要配"未命中回退到 index.html"。嵌入用的 iframe 直连 Tableau,不经过反代。
七、工程化:靠脚本而不是靠自觉
类型系统证明不了的行为,这个项目用真实 Chrome 用例钉住——改坏了会当场报错,而不是等上线后被人发现:
| 命令 | 它保证什么 |
|---|---|
| pnpm typecheck | 类型检查(TS 7 原生编译器) |
| pnpm test | 纯函数用例:权限语义、目录不变量、SMTP 规则、存储迁移 |
| pnpm check:routes | 路由文件 ↔ 权限目录一致性(路由守卫是 fail-open 的,漏登记 = 权限盲区) |
| pnpm check:i18n · check:i18n:keys | 词典 key 对齐 + 代码里用到的 key 无遗漏 |
| pnpm check:team-routes | 团队 slug 路由与作用域数据分区(16 项浏览器用例) |
| pnpm check:permissions | 权限矩阵与侧边栏顺序(15 项浏览器用例) |
| pnpm check:smtp · check:login | 配置页的校验、落盘与"密码不落盘"(6 + 7 项浏览器用例) |
八、许可、合规与边界
这不是官方项目。 与 Salesforce, Inc. 没有隶属、授权或赞助关系;Tableau、Tableau Cloud、Tableau Server 是 Salesforce, Inc. 的商标。 项目名里的 "Tableau" 只是用来说明它集成的是什么产品。
- 许可:本项目自身代码是 MIT。
- 第三方:@tableau/embedding-api 适用 Salesforce 的二进制代码许可(不是开源许可);使用者需要自行接受并遵守其条款。其余依赖均为 MIT / ISC / Apache-2.0 一类宽松许可。仓库里的 THIRD-PARTY.md 与 /help 页都列了完整说明。
- 凭据边界:这是纯前端应用,.env 与内置演示凭据都会被打进构建产物,混淆不构成加密。真正的访问控制依赖 Tableau 后台的 Connected App 域名白名单、访问级别与密钥轮换;要彻底隐藏密钥,必须把 JWT 签发迁到后端。
- 权限不是安全边界:页面权限控制的是可见性与直达,接入后端后必须由接口再校验一次。
九、和 Vizwise Hub 是什么关系
同一个作者还在 vizwise.cn 上维护着一个商业产品
Vizwise Hub(企业级内容管理与 AI 分析门户)。两者不是「开源版 / 付费版」的关系,
也不是彼此的简化版——架构与交付形态本就不同,只是恰好都在解决"让 Tableau 的内容被更多人真正用起来"这一件事。
| 维度 | Tableau Embed Plus(本仓库) | Vizwise Hub(商业产品) |
|---|---|---|
| 性质与许可 | 开源项目,MIT,社区支持(GitHub Issues) | 商业产品,付费,官方支持 |
| 交付形态 | 源码 + 静态产物,自己构建、部署与运维 | 部署即可用的成品平台 |
| 架构 | 纯前端 SPA:无后端、无数据库 | 前后端 + PostgreSQL 数据仓库 + 服务端任务 |
| 安全模型 | 凭据会进构建产物;页面权限只控制可见性,不作安全边界 | 接口双重校验;三级角色,数据按团队隔离 |
| 能力终点 | 浏览、嵌入、站点用户与计划只读运行情况 | 订阅推送、数据仓库元数据与数据字典、AI 自然语言操作、平台运营分析 |
| 国际化 | 词典已就绪,当前仅 en-US | 简体 / 繁体 / English 完整翻译 |
| 适合谁 | 单个或少量 Tableau 站点、有前端能力、想自己做外壳或二次开发 | 中大型企业、多团队治理、要让非 Tableau 用户也能看数与问数 |
| 入口 | 本页 · GitHub 仓库 | vizwise.cn/pages/vizwise-hub-overview |
十、路线图
- Phase 0–4 已完成:脚手架与工具链、多团队工作区、Tableau 嵌入与站点管理页、页面级权限、系统配置、AI 起点、部署样例。
- 进行中:把嵌入 API 改为运行时从使用者自己的 Tableau 站点加载,让仓库与构建产物不再分发 Salesforce 代码。
- 计划中:TypeScript 7.1 正式版发布后切到稳定版;等上游支持 TS 7 后合并掉现在为 lint 保留的第二份 TypeScript。
- 可选:SQLite 后端(Drizzle)+ JWT 签发下沉。
Tableau Embed Plus · MIT 许可 ·
仓库:github.com/tableau-China/Tableau-Embed-Hub ·
作者:xilejun
本文配图取自项目实际运行界面(v0.13.0)。其中站点用户页的账号信息已作模糊处理。