iWish 工具平台接入标准化规范
*.iwishapp.cn(首选子域名 你的工具.iwishapp.cn,或子路径 tools.iwishapp.cn/你的工具)。<向台林索取> 就是占位,必须替换成真实值(见第 9 章)。1平台架构总览
用户访问 你的工具.iwishapp.cn(或 tools.iwishapp.cn/你的工具)→ Cloudflare 边缘提供静态 UI(public/)与轻后端(functions/)→ 后端读写台林维护的 Supabase(数据 + 统一登录)。
用户浏览器
│ https://你的工具.iwishapp.cn/ (或 tools.iwishapp.cn/你的工具/)
▼
Cloudflare(CDN + 边缘)
├─ Pages 静态资源 public/ ← 你的 UI
└─ Pages Functions functions/ ← 轻后端 / 调第三方 API / 校验登录
│
▼
Supabase(台林 · oddfmupuuokyfxzcidvl)
├─ Auth 统一登录(微信/Google)
└─ Postgres 数据 + RLS + 角色/组织
职责边界
| 你(工具作者)负责 | 平台 / 台林负责 |
|---|---|
| UI、交互、业务逻辑 | Cloudflare 账号与 Pages 接入审批 |
改造成 Pages 结构(public/+functions/) | *.iwishapp.cn 域名与子域名/子路径路由 |
| 套用统一设计令牌与外壳布局 | Supabase 项目、Auth、登录 provider |
| 在 Supabase 建自己的表 + 开 RLS | 组织/角色/权限主数据表 |
用 wrangler pages secret 配自己的密钥 | 平台级密钥与基础设施可用性 |
2接入前置 Checklist
- Cloudflare Pages 创建权限(或让管理员替你建并给 deploy 权限)。
- Supabase Project URL + anon public key(前端用)。
- 和管理员定好挂载方式:子域名
你的工具.iwishapp.cn(首选)/ 子路径 / 并入主 SPA。 - 需要登录?确认台林当前已开通哪些登录方式,以及登录后从哪张表读角色/组织。
- 第三方 API 密钥(如有),准备用 secret 注入,绝不写进代码或 wrangler.toml。
3第一步:改造成 Pages 结构 + 三种挂载
你的工具/
├── public/ # 构建产物 / 静态资源(Pages 部署根目录)
├── functions/ # (可选)Pages Functions = 边缘轻后端
│ └── api/xxx.js # 自动映射到 /api/xxx
├── wrangler.toml
├── package.json
└── README.md
3.1 wrangler.toml 模板
name = "yourtool" # 全小写短横线
compatibility_date = "2024-01-01"
pages_build_output_dir = "public" # 构建产物目录
[vars]
# 只放非敏感公开变量;密钥一律走 wrangler pages secret
SUPABASE_URL = "https://oddfmupuuokyfxzcidvl.supabase.co"
ai-promotion-generator / dtc-promo-cloudflare 的真实写法,照抄即可。3.2 三种挂载形态与 base(配错会白屏,先和管理员定)
| 形态 | 地址 | base 配置 |
|---|---|---|
| ① 子域名独立项目 首选 | 你的工具.iwishapp.cn | Vite base:'/'(默认);纯静态零配置 |
| ② 子路径独立项目 | tools.iwishapp.cn/你的工具/ | Vite 必须 base:'/你的工具/',否则 /assets/* 404 |
| ③ 并入主 SPA (现网 feed-workbench) | tools.iwishapp.cn/你的工具 客户端路由 | 资源走根 /assets/,base:'/';API 用 /api/你的工具/* |
// 形态② vite.config.ts
export default defineConfig({
base: '/你的工具/', // 子路径独立项目必须;子域名 / 并入主 SPA 用 '/'
build: { outDir: 'public' },
})
iwish-onboarding 即此)。4第二步:轻后端用 Pages Functions
functions/api/hello.js → 自动暴露为 /api/hello。除 onRequest,也可 onRequestGet / onRequestPost 分方法导出。
export async function onRequest({ request, env }) {
const cors = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
};
if (request.method === 'OPTIONS')
return new Response(null, { headers: { ...cors, 'Access-Control-Max-Age': '86400' } });
if (request.method !== 'POST') return json({ error: 'Method not allowed' }, 405, cors);
try {
const body = await request.json();
const API_KEY = env.SOME_API_KEY; // 密钥从 env 取,不硬编码
if (!API_KEY) return json({ error: 'SOME_API_KEY not configured' }, 500, cors);
// ...调第三方 API / 处理业务...
return json({ ok: true }, 200, cors);
} catch (e) { return json({ error: String(e) }, 500, cors); }
}
function json(obj, status, cors) {
return new Response(JSON.stringify(obj),
{ status, headers: { 'Content-Type': 'application/json', ...cors } });
}
npx wrangler pages secret put SOME_API_KEY --project-name yourtool 注入,只存在 Cloudflare。绝不写进 wrangler.toml、前端代码或 git。前端只能拿"公开 key"(如 Supabase anon key)。5第三步:数据接 Supabase
| 项 | 值 |
|---|---|
| Project Ref | oddfmupuuokyfxzcidvl(组织 Iwish System Pro) |
| Project URL | https://oddfmupuuokyfxzcidvl.supabase.co |
| anon public key | <向台林索取>(公开 key,可放前端) |
| service_role key | <向台林索取> 仅 Functions,绝不进前端 |
5.1 前端初始化
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';
const supabase = createClient(
'https://oddfmupuuokyfxzcidvl.supabase.co',
'<向台林索取:anon key>'
);
5.2 建表命名约定(多工具共用一库)
- 用工具前缀命名表:
yourtool_xxx,避免和别人打架;或与台林确认独立schema。改表结构前先同步台林,别动别人的表。
5.3 RLS 是安全红线(必须开)
alter table yourtool_records enable row level security;
create policy "owner can read" on yourtool_records
for select using (auth.uid() = user_id);
create policy "owner can write" on yourtool_records
for insert with check (auth.uid() = user_id);
6第四步:统一登录对接(Supabase Auth)
统一用 Supabase Auth(台林账号系统)登录,方式为微信/Google。你的工具不自建登录、不存密码,只对接。
6.1 前端发起登录 / 拿会话
await supabase.auth.signInWithOAuth({
provider: 'google',
options: { redirectTo: 'https://你的工具.iwishapp.cn/' }, // 子路径填到对应路径
});
const { data: { user } } = await supabase.auth.getUser();
if (!user) location.href = '/login.html'; // 未登录 → 登录页
6.2 Functions 里服务端校验 token
const token = (request.headers.get('Authorization') || '').replace('Bearer ', '');
const res = await fetch('https://oddfmupuuokyfxzcidvl.supabase.co/auth/v1/user', {
headers: { Authorization: `Bearer ${token}`, apikey: env.SUPABASE_ANON_KEY },
});
if (!res.ok) return new Response('Unauthorized', { status: 401 });
const user = await res.json(); // user.id / email → 继续业务
6.3 角色 / 组织(RBAC)
拿到 user.id 后,从台林维护的组织/角色表读角色、部门、权限点,据此控制菜单和按钮。具体表名第 9 章向台林确认;工具侧只做"按权限显示/隐藏"。
6.4 现网另一种实践(feed-workbench:Google OAuth via Functions)
feed-workbench 把 Google OAuth 放在 CF Functions 服务端:/api/auth/login → /api/auth/callback → /api/auth/session/<id>,前端不持有 client_secret,session 经 URL 回传后 history.replaceState 清掉。401 统一派发全局 app:auth-expired 事件一处重置登录态——这套 fetch 封装无论用哪种登录都值得抄。本规范标准仍是 Supabase Auth,二者择一,向台林确认平台口径。7第五步:样式规范(统一设计令牌)★
目标:平台每个工具看起来都是"iWish 一家人"。下面这套令牌(取自 iwishweb.com 官网主题色)直接复制进 CSS,颜色/圆角/阴影/字体全用变量,不要自调一套蓝。
7.1 调色板
7.2 令牌 :root(直接粘贴)
:root{
--logo-primary:#00367C; --logo-secondary:#0067FF; --logo-light:#E7F6FF; --logo-accent:#005EE9;
--bg-primary:#F5F7FA; --bg-card:#FFFFFF; --bg-hover:#EDF1F7;
--text-primary:#1E293B; --text-secondary:#64748B; --text-muted:#94A3B8;
--border-color:#E2E8F0;
--success:#10B981; --warning:#F59E0B; --danger:#EF4444; --info:#06B6D4;
--shadow:0 1px 3px rgba(0,0,0,.08); --shadow-lg:0 10px 25px rgba(0,54,124,.12);
--radius:12px; --radius-sm:8px; --sidebar-width:248px;
}
body{font-family:'Noto Sans SC',-apple-system,sans-serif;
background:var(--bg-primary);color:var(--text-primary);line-height:1.6}
7.3 字体
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;600;700&display=swap" rel="stylesheet">
7.4 平台外壳 + 组件规范
本页就是范例:顶栏品牌渐变(64px)+ 侧栏导航(248px)+ 主内容卡片。带后台的工具统一用这套骨架;单页工具可省侧栏,但顶栏渐变与配色要一致。
| 组件 | 规范 |
|---|---|
| 卡片 | bg-card + 1px border-color + radius:12px + shadow |
| 主按钮 | 背景 logo-primary,文字白,radius-sm,hover 抬阴影 |
| 次按钮 | 白底 + 1px border-color,文字 text-secondary |
| 状态徽章 | 成功绿 警告黄 危险红 信息青 |
| 表单 | 输入框 focus 边框转 logo-primary;label 用 text-secondary |
| 空/加载态 | 必须有:空数据给引导,加载给 skeleton/spinner,别白屏 |
五态徽章系统(借鉴现网 iwish-onboarding,凡"接入项/配置项/任务"状态统一用这套):
完成未完成/失败待处理警告重要
.badge{display:inline-block;padding:2px 9px;border-radius:11px;font-size:12px;font-weight:600}
.b-done{background:#E3F7E9;color:#2E9E51} /* 完成 */
.b-no {background:#FDE9EF;color:#C83E6B} /* 未完成 / 失败 */
.b-todo{background:#EEF1F6;color:#8A93A6} /* 待处理 */
.b-warn{background:#FFF3DF;color:#D9912A} /* 警告 */
.b-imp {background:#E7F0FF;color:#2F6BD8} /* 重要 */
7.5 Tailwind / Vue 项目令牌映射
theme:{ extend:{ colors:{
brand:{ primary:'#00367C', secondary:'#0067FF', light:'#E7F6FF', accent:'#005EE9' },
bg:{ app:'#F5F7FA', card:'#FFFFFF', hover:'#EDF1F7' },
ink:{1:'#1E293B',2:'#64748B',3:'#94A3B8'}, line:'#E2E8F0',
ok:'#10B981', warn:'#F59E0B', danger:'#EF4444', info:'#06B6D4',
}, borderRadius:{ DEFAULT:'12px', sm:'8px' } }}
7.6 响应式 + 文案
- 窄屏(<768px)侧栏折叠/抽屉化,主内容
margin-left归零;卡片单列;文本不溢出。桌面+移动各看一遍。
执行/生成/校验/Ingest/Query/Lint/Step 这类内部流程词当标题/按钮/标签,要展示就改写成业务语言(如"生成"→"创建报告")。8第六步:部署上线(挂到 *.iwishapp.cn)
- 代码进 git,
.gitignore排除.env/ 密钥 /node_modules。 - 创建 Cloudflare Pages 项目,连 GitHub;构建命令纯静态站留空、Vite 填
npm run build;输出目录public(或dist)。 - 配密钥:
npx wrangler pages secret put XXX --project-name yourtool。 - 配路由:子域名(
你的工具.iwishapp.cn加自定义域)或子路径由平台管理员在 Cloudflare 侧配规则——通常不是你自己点。 - 用 push 产生的预览 URL(
<项目>.pages.dev)验完再切正式。 - 打开
你的工具.iwishapp.cn,走一遍 登录 → 核心功能 → 数据读写。
9信息确认清单(向台林 / 平台管理员索取)
| 要的东西 | 找谁 | 用在哪 |
|---|---|---|
| Supabase anon public key | 台林 | 前端初始化 |
| service_role key(如需服务端写库) | 台林 | 仅 Functions |
| 当前已开通的登录方式 | 台林 | 第 6 章登录 |
| 读角色/组织的表名与字段 | 台林 | 第 6.3 RBAC |
| 表名前缀 / 是否独立 schema | 台林 | 第 5.2 建表 |
| 挂载方式:子域名 / 子路径 / 并入主 SPA | 平台管理员 | base + 路由(见 3.2) |
| Pages 项目创建/部署权限 | 平台管理员 | 第 8 章部署 |
10上线前验收清单
public/能独立打开,资源不 404(子路径独立项目确认 base,子域名/并入主 SPA 用/)。- 所有密钥走 secret / .env,git 里搜不到任何 key。
- 用到的每张 Supabase 表都开了 RLS 并有 policy。
- 需登录页未登录会跳登录页;敏感 Function 服务端校验了 token。
- 套用了统一设计令牌,没有自调杂色;顶栏/侧栏/卡片/按钮符合第 7 章;桌面+移动都看过。
- UI 文案没有流程词。
- 预览环境跑通:登录 → 核心功能 → 数据读写。
11红线汇总(别踩)
- 密钥不进前端、不进 git、不进 wrangler.toml —— 只走 secret / env。
- Supabase 表必须开 RLS —— 没开等于裸奔。
- 不自建用户体系 —— 统一 Supabase Auth,不存密码。
- 不动别人的表 / 不擅改共享 schema —— 改结构先同步台林。
- 挂载 base 配对 —— 子路径独立项目改
vite base,子域名/并入主 SPA 用/。 - UI 不暴露内部流程词 —— 给用户看的是业务语言。
12参考现网项目(可借鉴的真实模式)
12.1 feed-workbench(tools.iwishapp.cn/feed-workbench)— SPA 子路径 + 服务端鉴权
- 架构:React + React Router 单 SPA,
/feed-workbench是客户端路由,资源走根/assets/,base 保持/(对应 3.2 形态③)。 - API 命名空间:
/api/feed-workbench/xxx,工具名同时作 URL 前缀与 API 命名空间,便于functions/api/<工具名>/分目录。 - 统一登录(现网):Google OAuth via Functions(详见 6.4)。本规范标准仍是 Supabase Auth,二者择一向台林确认。
- 401 统一处理:统一
fetch封装 + 全局app:auth-expired事件,一处重置登录态。 - 数据层(现网):Google Sheets(OAuth scope 含
spreadsheets)+ Functions 中间件,无专属数据库;多人结构化数据仍建议走 Supabase。
12.2 iwish-onboarding(iwish-onboarding.pages.dev)— 单 HTML + 声明式配置矩阵
- 极简部署:逻辑全内联在单 HTML,无构建工具/无 npm,
/api/*由 Pages Functions 承接,第三方 API 由独立 Worker(gconfig-runner)代理。 - 声明式配置矩阵:每个配置项声明为
{平台, action, 是否只读, 是否可自动检测}数据对象,渲染/批量 dryrun/进度都消费同一份 map。 - 五态徽章系统:已纳入第 7.4 徽章规范。
- 状态持久化:单操作员用
localStorage按客户名隔离;多人协作必须升级 Supabase。
12.3 通用可借鉴清单
onRequestGet / onRequestPost 分方法导出 wrangler 加 D1 / KV / Workflows 绑定 Cloudflare Access 作第二条鉴权路(前端零 token) Next.js 用 @supabase/ssr(createBrowserClient + middleware 守卫) 版本锁 supabase-js@2.45.0 + ssr@0.5.0