创建与部署规范
Edge Functions 的创建审批流程、开发规范和部署要求
创建新函数的前置要求
创建新的 Edge Function 之前,必须先联系 Galentwww 进行评估。未经评估不提供创建和部署新函数。
新建 Edge Function 需要同时满足以下条件:
- 现有函数无法满足需求 — 已确认当前已部署的 Edge Functions 均不能覆盖目标功能
- RPC 无法替代 — 已评估过使用 Postgres RPC 函数实现的可行性,确认 RPC 无法满足(例如需要调用外部 API、需要访问非数据库资源等)
- Galentwww 审批通过 — 向 Galentwww 说明需求背景、为什么现有方案不可行,获得明确同意
评估流程
开发规范
禁止硬编码
所有配置值、URL、密钥等禁止硬编码在函数代码中。应通过环境变量或 Supabase Secrets 注入:
// ✅ 正确:通过环境变量获取
const cdnDomain = Deno.env.get("CDN_DOMAIN") ?? "";
const webhookUrl = Deno.env.get("WEBHOOK_URL") ?? "";
// ❌ 错误:硬编码
const cdnDomain = "https://bucket.xfmanga.top";
const webhookUrl = "https://hooks.slack.com/xxx";敏感信息使用 Secrets
对于 API 密钥、第三方服务凭证等敏感内容,必须使用 Supabase 的 Secret 功能管理,不得明文写入代码或普通环境变量:
# 设置 Secret
supabase secrets set MY_API_KEY=sk_live_xxxxxxxxxxxx
# 在函数中读取
Deno.env.get("MY_API_KEY")
# 查看已有 Secrets(不会显示值)
supabase secrets listSecrets 不会出现在代码仓库、部署日志和 Dashboard 中。已设置的 Secret 只能被覆盖或删除,无法查看原始值。
避免使用 SERVICE_ROLE_KEY
SUPABASE_SERVICE_ROLE_KEY 拥有绕过所有 RLS 策略的最高权限。如非必要,严禁使用。
使用 SUPABASE_ANON_KEY 创建客户端,配合 RLS 策略控制权限:
const supabase = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_ANON_KEY")!,
);如果需要以请求用户的身份操作数据库,从请求头中提取用户 JWT 并透传:
const authHeader = req.headers.get("Authorization") ?? "";
const supabase = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_ANON_KEY")!,
{ global: { headers: { Authorization: authHeader } } }
);以下情况可以使用 SERVICE_ROLE_KEY,但需要在代码注释中说明原因:
- 需要跨用户操作数据(如定时任务批量更新)
- 需要访问不对外暴露的管理表
- 对接外部 Webhook 回调(无用户上下文)
// ⚠️ 使用 SERVICE_ROLE_KEY:此函数为系统级定时任务,
// 需要更新所有用户的 popularity 字段,无用户上下文
const supabase = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!,
);身份与权限校验
需要确认当前用户是否具备管理员或编辑者身份时,优先调用数据库 RPC 函数,而不是在 Edge Function 中自行查表判断:
// ✅ 推荐:调用 RPC 函数判断身份
const { data: isAdmin } = await supabase.rpc("is_admin");
const { data: isAdminOrEditor } = await supabase.rpc("is_admin_or_editor");
if (!isAdmin) {
return new Response(
JSON.stringify({ error: "权限不足" }),
{ status: 403, headers: { "Content-Type": "application/json" } }
);
}// ❌ 避免:自己查 profiles 表判断角色
const { data: user } = await supabase
.from("profiles")
.select("role")
.eq("id", userId)
.single();
if (user?.role !== "admin") { ... }is_admin() 和 is_admin_or_editor() 是已在数据库中定义好的 RPC 函数,它们基于 auth.uid() 自动获取当前用户身份,无需传参。这两个函数同时也被 RLS 策略复用,保证权限判断逻辑的一致性。
部署流程
确认函数开发完成并在本地测试通过后,执行部署:
# 部署单个函数
supabase functions deploy my-function
# 部署全部函数
supabase functions deploy部署后验证
# 确认函数状态为 ACTIVE
supabase functions list
# 查看实时日志
supabase functions logs my-function
# 手动调用测试
curl -X POST \
'https://<project-ref>.supabase.co/functions/v1/my-function' \
-H 'Authorization: Bearer <ANON_KEY>' \
-H 'Content-Type: application/json' \
-d '{"test": true}'规范核对清单
部署前请对照以下清单自检:
- 已获得 Galentwww 的创建审批
- 代码中无硬编码的 URL、密钥、配置值
- 敏感信息通过
supabase secrets set管理 - 未使用
SERVICE_ROLE_KEY(如使用了需注释说明原因) - 身份校验通过
is_admin()/is_admin_or_editor()RPC 完成 - 本地测试通过
- 错误情况有合理的 HTTP 状态码和错误信息返回