143 lines
4.0 KiB
TypeScript
143 lines
4.0 KiB
TypeScript
import type { SupabaseClient } from "@supabase/supabase-js";
|
|||
|
|
import type { Database } from "@/types/supabase";
|
||
|
|
|
||
|
|
type TypedClient = SupabaseClient<Database>;
|
||
|
|
|
||
|
|
interface WorkspaceMembershipRow {
|
||
|
|
workspace_id: string;
|
||
|
|
is_default: boolean;
|
||
|
|
workspaces: {
|
||
|
|
id: string;
|
||
|
|
name: string;
|
||
|
|
type: "personal" | "team";
|
||
|
|
icon_url: string | null;
|
||
|
|
} | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
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}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
const rows: WorkspaceMembershipRow[] = memberRows ?? [];
|
||
|
|
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) => {
|
||
|
|
if (!row.workspaces) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
id: row.workspaces.id,
|
||
|
|
name: row.workspaces.name,
|
||
|
|
type: row.workspaces.type,
|
||
|
|
iconUrl: row.workspaces.icon_url,
|
||
|
|
memberCount: memberCountMap[row.workspaces.id] ?? 1,
|
||
|
|
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 ?? "";
|
||
|
|
}
|