@autolabz/z-supabase-sdk
v0.1.2
Published
Client SDK for z-supabase-service shadow sessions
Downloads
9
Readme
@autolabz/z-supabase-sdk
面向前端应用的 Supabase 工具集,聚焦自托管 z-supabase-service 的三件事:会话托管、Shadow Session 同步、Storage 限流。
功能总览
- Supabase Client:
createSupabaseClient默认关闭autoRefreshToken/persistSession,将 session 生命周期交给业务决定。 - Shadow Session 管理:
syncSupabaseSession/refreshShadowSession通过服务端 API 下发 shadow 用户的 access/refresh token,并支持内存或 Web Storage 缓存。 - Storage 工具:
createSignedUploadUrl通过服务端统一校验配额;getStorageQuota独立查询配额;listStorageObjects复用 shadow session 查询resources表;deleteStorageResource触发服务端删除并具备幂等自愈能力。所有方法在异常时都会抛出可枚举的StorageUploadError。
1. Supabase 客户端创建策略
SDK 会在创建客户端时合并默认 auth 选项,避免 Supabase JS 自动持久化 session,从而确保 shadow token 只来自受控流程。
import { createSupabaseClient } from '@autolabz/z-supabase-sdk';
const supabase = createSupabaseClient({
supabaseUrl: import.meta.env.VITE_SUPABASE_URL!,
supabaseAnonKey: import.meta.env.VITE_SUPABASE_ANON_KEY!,
clientOptions: {
global: { headers: { 'X-App-Version': '[email protected]' } },
},
});如需特殊场景(例如 SSR)可继续覆写 clientOptions.auth,SDK 仅负责提供安全的默认值。
2. Shadow Session 生命周期
syncSupabaseSession 调用 /auth/sync 生成 shadow 用户并向 Supabase client 写入 access / refresh token;refreshShadowSession 命中 /session/refresh,在 token 即将过期时续期。两个方法都会在失败时抛出 ShadowSessionError,并支持自定义 fetcher、clientId 以及各类回调。
import {
syncSupabaseSession,
refreshShadowSession,
WebStorageSessionStorage,
} from '@autolabz/z-supabase-sdk';
const storage = new WebStorageSessionStorage('zsupa_session');
await syncSupabaseSession({
serviceBaseUrl: import.meta.env.VITE_ZSUPA_SERVICE_URL!,
authToken: oauthAccessToken,
supabaseClient: supabase,
storage,
clientId: 'paper-admin',
onBeforeSync: () => startLoading(),
onSyncSuccess: (payload) => console.log('shadow user:', payload.shadowUserId),
});
await refreshShadowSession({
serviceBaseUrl: import.meta.env.VITE_ZSUPA_SERVICE_URL!,
supabaseClient: supabase,
storage,
clientId: 'paper-admin',
});默认提供三种存储策略:
MemorySessionStorage:测试或短生命周期页面;WebStorageSessionStorage:使用localStorage,支持自定义 key;createDefaultStorage():在浏览器中落地到localStorage,否则回退内存。
3. Storage 工具
所有 Storage API 都会先进行参数校验(大小、路径、分页等)再请求服务端,当配额不足 / 参数非法时抛出带有 code 与 meta 的 StorageUploadError,便于将 ZS501 之类的错误码直接映射到 UI。
上传流程(Signed URL 代上传)
- 前端调用
createSignedUploadUrl。SDK 会校验authToken、fileName与文件大小后,向z-supabase-service的/storage/uploads/sign发起请求。 - 服务端收到请求后,会:
- 根据 OAuth 用户 ID 找到对应 shadow user;
- 创建一条
resources记录并生成resourceId,将真实存储文件名固定为<resourceId>(.ext),并只允许写入<shadowUserId>/<resourceId>.ext; - 通过 Supabase Service Role 调用
createSignedUploadUrl,把存储配额(userQuotaBytes、maxFileBytes)和允许的 bucket 统一限制住。
- 服务端返回包含
resourceId的 signed URL 响应;客户端可把resourceId用于后续轮询处理状态,并直接对该 URL 发起PUT,整个过程无需接触 Service Role Key。
这样即使用户在浏览器 F12 中看到请求,也只能把 signedUrl 用于当前文件上传,无法在桶内任意写入其他路径,实现“系统代用户上传”的受控链路。
资源列表(Supabase resources 表)
listStorageObjects 需要一个 shadow session 已经连线的 supabaseClient,它会直接查询 resources 表并返回规范化字段(fileName, storagePath, size, status, createdAt 等),默认按 created_at desc 排序,同时返回 count 和 hasMore 方便分页。如果当前 Supabase Client 里没有 session,方法会尝试从提供的 storage(或默认的 createDefaultStorage())恢复 shadow access/refresh token,确保真正发送的 Authorization 为 Bearer eyJ...;若缓存缺失则抛出 SHADOW_SESSION_MISSING 提醒先执行 syncSupabaseSession。
const listed = await listStorageObjects({
supabaseClient,
shadowUserId: session.shadowUserId,
storage, // 传入与 sync 阶段相同的 storage,便于页面刷新后自动恢复 JWT
limit: 20,
orderBy: { column: 'created_at', order: 'desc' },
});
listed.objects.forEach((resource) => {
console.log(resource.fileName, resource.size, resource.storagePath);
});配额查询(Quota API)
当页面需要展示“已用/剩余空间”而不想触发签名流程时,可直接调用 getStorageQuota。SDK 仅需 serviceBaseUrl 与 authToken,会向 /storage/quota 发送 GET 请求,并返回 shadowUserId, usedBytes, remainingBytes, quotaBytes, maxFileBytes。
import { getStorageQuota } from '@autolabz/z-supabase-sdk';
const quota = await getStorageQuota({
serviceBaseUrl: import.meta.env.VITE_ZSUPA_SERVICE_URL!,
authToken: shadowAccessToken,
clientId: 'paper-admin',
});
console.log(`已用 ${(quota.usedBytes / 1024 / 1024).toFixed(1)} MB / 总额度 ${(quota.quotaBytes / 1024 / 1024).toFixed(1)} MB`);资源删除(Delete API)
deleteStorageResource 会向 /storage/resources/:resourceId 发送 DELETE 请求。服务端会验证影子账号归属、删除 Supabase Storage 对象,并在对象缺失时自动忽略错误、仅清理数据库记录,实现幂等、“自愈”的删除逻辑。SDK 只需提供 authToken, serviceBaseUrl, resourceId:
import { deleteStorageResource } from '@autolabz/z-supabase-sdk';
await deleteStorageResource({
serviceBaseUrl: import.meta.env.VITE_ZSUPA_SERVICE_URL!,
authToken: shadowAccessToken,
resourceId: selectedResourceId,
clientId: 'paper-admin',
});如返回 success: true 即表示顺利清理,无论 Supabase Storage 中对象是否已经不存在。
查看 / 下载方式
- 生成可访问链接:拿到资源记录后,可用 shadow session 驱动的 Supabase JS 客户端直接调用
supabase.storage.from(bucket).createSignedUrl(resource.storagePath!, ttl)或download(resource.storagePath!)获取一次性访问链接 / Blob 数据。RLS 只允许访问<shadowUserId>/**,因此天然满足“只看自己的文件”。 - 对外分享(可选):需要向第三方暴露文件时,可在自建后端使用 Service Role Key 生成一次性签名,再下发给终端;SDK 专注在“用户查看自己的资源”场景,推荐优先走 shadow session。
import { createSignedUploadUrl, getStorageQuota, listStorageObjects } from '@autolabz/z-supabase-sdk';
const signed = await createSignedUploadUrl({
serviceBaseUrl: import.meta.env.VITE_ZSUPA_SERVICE_URL!,
authToken: supabase.auth.session()?.access_token ?? '',
fileName: file.name,
bytes: file.size,
mimeType: file.type,
clientId: 'paper-admin',
});
console.log('resourceId for follow-up polling:', signed.resourceId);
await fetch(signed.uploadUrl, { method: 'PUT', body: file, headers: signed.headers });
const quota = await getStorageQuota({
serviceBaseUrl: import.meta.env.VITE_ZSUPA_SERVICE_URL!,
authToken: supabase.auth.session()?.access_token ?? '',
});
const listed = await listStorageObjects({
supabaseClient,
shadowUserId: session.shadowUserId,
storage,
limit: 20,
});安装
npm install @autolabz/z-supabase-sdk快速上手
import { createSupabaseClient, syncSupabaseSession, createSignedUploadUrl } from '@autolabz/z-supabase-sdk';
const supabase = createSupabaseClient({
supabaseUrl: import.meta.env.VITE_SUPABASE_URL!,
supabaseAnonKey: import.meta.env.VITE_SUPABASE_ANON_KEY!,
});
await syncSupabaseSession({
serviceBaseUrl: import.meta.env.VITE_ZSUPA_SERVICE_URL!,
authToken: oauthAccessToken,
supabaseClient: supabase,
clientId: 'paper-admin',
});
const signed = await createSignedUploadUrl({
serviceBaseUrl: import.meta.env.VITE_ZSUPA_SERVICE_URL!,
authToken: supabase.auth.session()?.access_token ?? '',
fileName: file.name,
bytes: file.size,
mimeType: file.type,
});
await fetch(signed.uploadUrl, {
method: 'PUT',
body: file,
headers: signed.headers,
});API 速览
| 方法 / 类型 | 描述 |
| --- | --- |
| createSupabaseClient | 生成禁用自动 refresh/persist 的 Supabase client。 |
| syncSupabaseSession | 请求 /auth/sync,拉取 shadow session 并写入 Supabase client。 |
| refreshShadowSession | 请求 /session/refresh,在 access token 即将过期时续期。 |
| createSignedUploadUrl | 请求 /storage/uploads/sign,校验配额并拿到 PUT 签名链接。 |
| getStorageQuota | 请求 /storage/quota,查询影子用户的已用/剩余额度。 |
| deleteStorageResource | 请求 /storage/resources/:resourceId,触发服务端删除并自愈缺失对象。 |
| listStorageObjects | 使用 shadow session 的 Supabase client 查询 resources 表并返回分页结果。 |
| MemorySessionStorage / WebStorageSessionStorage | shadow session 的可插拔持久化策略。 |
| ShadowSessionError / StorageUploadError | 携带 code、meta 的错误类型,便于 UI 映射。 |
createSignedUploadUrl 会在参数阶段阻止 authToken 缺失、bytes <= 0 等问题;服务端错误会透出 StorageUploadError,可直接针对 ZS501(容量用尽)、ZS401(鉴权失败)等错误码给出提示。
