Files
mnote/wolai-frontend/src/lib/workspaces.ts
T

151 lines
4.1 KiB
TypeScript
Raw Normal View History

2025-11-23 10:55:04 +08:00
import type { SupabaseClient } from "@supabase/supabase-js";
2026-01-08 06:28:14 +08:00
type TypedClient = SupabaseClient<any>;
2025-11-23 10:55:04 +08:00
interface WorkspaceMembershipRow {
workspace_id: string;
is_default: boolean;
2026-01-08 06:28:14 +08:00
workspaces:
| {
id: string;
name: string;
type: "personal" | "team";
icon_url: string | null;
}
| Array<{
id: string;
name: string;
type: "personal" | "team";
icon_url: string | null;
}>
| null;
2025-11-23 10:55:04 +08:00
}
export interface WorkspaceSummary {
id: string;
name: string;
type: "personal" | "team";
iconUrl: string | null;
memberCount: number;
isDefault: boolean;
}
export async function ensureDefaultWorkspace(client: TypedClient, userId: string, fallbackName: string): Promise<void> {
const { data: memberships, error } = await client
.from("workspace_members")
.select("workspace_id")
.eq("user_id", userId)
.limit(1);
if (error) {
throw new Error(`获取工作空间成员信息失败:${error.message}`);
}
if (memberships && memberships.length > 0) {
return;
}
const workspaceName = fallbackName.trim() ? `${fallbackName.trim()} 的空间` : "我的空间";
const { data: workspace, error: workspaceError } = await client
.from("workspaces")
.insert({
name: workspaceName,
type: "personal",
created_by: userId,
})
.select("id")
.single();
if (workspaceError || !workspace) {
throw new Error(`创建默认工作空间失败:${workspaceError?.message ?? "未知错误"}`);
}
const { error: memberError } = await client.from("workspace_members").insert({
workspace_id: workspace.id,
user_id: userId,
role: "owner",
is_default: true,
});
if (memberError) {
throw new Error(`创建工作空间成员失败:${memberError.message}`);
}
}
export async function fetchWorkspaceSummaries(
client: TypedClient,
userId: string,
): Promise<{ workspaces: WorkspaceSummary[]; activeWorkspaceId: string }> {
const { data: memberRows, error } = await client
.from("workspace_members")
.select("workspace_id,is_default,workspaces(id,name,type,icon_url)")
.eq("user_id", userId)
.order("created_at", { ascending: true });
if (error) {
throw new Error(`拉取工作空间列表失败:${error.message}`);
}
2026-01-08 06:28:14 +08:00
const rows: WorkspaceMembershipRow[] = (memberRows ?? []) as any;
2025-11-23 10:55:04 +08:00
const workspaceIds = rows.map((row) => row.workspace_id);
const memberCountMap: Record<string, number> = {};
if (workspaceIds.length > 0) {
const { data: memberCounts, error: countError } = await client
.from("workspace_members")
.select("workspace_id")
.in("workspace_id", workspaceIds);
if (countError) {
throw new Error(`统计成员数失败:${countError.message}`);
}
(memberCounts ?? []).forEach((item) => {
const workspaceId = item.workspace_id as string;
memberCountMap[workspaceId] = (memberCountMap[workspaceId] ?? 0) + 1;
});
}
const summaries: WorkspaceSummary[] = rows
.map((row) => {
2026-01-08 06:28:14 +08:00
const workspace = Array.isArray(row.workspaces) ? row.workspaces[0] : row.workspaces;
if (!workspace) {
2025-11-23 10:55:04 +08:00
return null;
}
return {
2026-01-08 06:28:14 +08:00
id: workspace.id,
name: workspace.name,
type: workspace.type,
iconUrl: workspace.icon_url,
memberCount: memberCountMap[workspace.id] ?? 1,
2025-11-23 10:55:04 +08:00
isDefault: row.is_default,
} satisfies WorkspaceSummary;
})
.filter(Boolean) as WorkspaceSummary[];
const defaultWorkspace = summaries.find((workspace) => workspace.isDefault);
const activeWorkspaceId = defaultWorkspace?.id ?? summaries[0]?.id ?? "";
return {
workspaces: summaries,
activeWorkspaceId,
};
}
export async function resolveActiveWorkspaceId(client: TypedClient, userId: string): Promise<string> {
const { data, error } = await client
.from("workspace_members")
.select("workspace_id,is_default")
.eq("user_id", userId)
.order("is_default", { ascending: false })
.order("created_at", { ascending: true })
.limit(1);
if (error) {
throw new Error(`获取当前工作空间失败:${error.message}`);
}
return data?.[0]?.workspace_id ?? "";
}