Skip to content

Commit 6abc81c

Browse files
committed
eng-1702-invite-new-member
1 parent 042ddb9 commit 6abc81c

4 files changed

Lines changed: 137 additions & 11 deletions

File tree

apps/website/app/utils/supabase/account.ts

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import type { DGSupabaseClient } from "@repo/database/lib/client";
33

44
type AgentType = Database["public"]["Enums"]["AgentType"] | "group";
55

6-
export const getSessionUserData = async (
6+
export const getSessionBaseUserData = async (
77
client: DGSupabaseClient,
88
): Promise<{
99
id: string;
10-
name: string;
10+
name?: string;
1111
type: AgentType;
1212
email?: string;
1313
} | null> => {
@@ -34,14 +34,75 @@ export const getSessionUserData = async (
3434
return { name, id, email, type: "group" };
3535
}
3636
}
37-
const accountReq = await client
38-
.from("PlatformAccount")
39-
.select("name")
40-
.eq("dg_account", session.data.session.user.id)
41-
.eq("agent_type", "person")
42-
.maybeSingle();
43-
if (accountReq.error || !accountReq.data) {
44-
return null;
37+
return { id, type: "person", email };
38+
};
39+
40+
export const getSessionUserData = async (
41+
client: DGSupabaseClient,
42+
): Promise<{
43+
id: string;
44+
name: string;
45+
type: AgentType;
46+
email?: string;
47+
} | null> => {
48+
const data = await getSessionBaseUserData(client);
49+
if (data === null) return null;
50+
if (!data.name && data.type === "person") {
51+
const accountReq = await client
52+
.from("PlatformAccount")
53+
.select("name")
54+
.eq("dg_account", data.id)
55+
.eq("agent_type", "person")
56+
.maybeSingle();
57+
if (accountReq.error || !accountReq.data?.name) {
58+
return null;
59+
}
60+
return { ...data, name: accountReq.data.name };
4561
}
46-
return { id, name: accountReq.data.name, type: "person", email };
62+
if (data.name === undefined) return null;
63+
return data as {
64+
id: string;
65+
name: string; // typescript is being dumb
66+
type: AgentType;
67+
email?: string;
68+
};
69+
};
70+
71+
export const createGroupInvitation = async ({
72+
client,
73+
groupId,
74+
admin = false,
75+
}: {
76+
client: DGSupabaseClient;
77+
groupId: string;
78+
admin: boolean;
79+
}): Promise<string | null> => {
80+
const userData = await getSessionBaseUserData(client);
81+
if (!userData) return null;
82+
const membershipReq = await client
83+
.from("group_membership")
84+
.select("admin")
85+
.eq("group_id", groupId)
86+
.eq("member_id", userData.id)
87+
.maybeSingle();
88+
if (membershipReq.data?.admin !== true) return null;
89+
const { data, error } = await client.rpc("create_secret_token", {
90+
v_payload: JSON.stringify({ groupId, type: "groupInvitation", admin }),
91+
expiry_interval: "60d",
92+
});
93+
if (error || !data) return null;
94+
return data;
95+
};
96+
97+
export const acceptGroupInvitation = async (
98+
client: DGSupabaseClient,
99+
token: string,
100+
): Promise<string | null> => {
101+
const userData = await getSessionBaseUserData(client);
102+
if (!userData) return "Not logged in";
103+
const { data, error } = await client.rpc("accept_group_invitation", {
104+
token,
105+
});
106+
if (error || !data) return "This token does not exist";
107+
return null;
47108
};

packages/database/src/dbTypes.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,7 @@ export type Database = {
13141314
isSetofReturn: false
13151315
}
13161316
}
1317+
accept_group_invitation: { Args: { token: string }; Returns: boolean }
13171318
account_in_shared_space: {
13181319
Args: {
13191320
access_level?: Database["public"]["Enums"]["SpaceAccessPermissions"]
@@ -1530,6 +1531,16 @@ export type Database = {
15301531
Returns: string
15311532
}
15321533
group_exists: { Args: { group_id_: string }; Returns: boolean }
1534+
group_members: {
1535+
Args: { p_group_id: string }
1536+
Returns: Database["public"]["CompositeTypes"]["group_member_info"][]
1537+
SetofOptions: {
1538+
from: "*"
1539+
to: "group_member_info"
1540+
isOneToOne: false
1541+
isSetofReturn: true
1542+
}
1543+
}
15331544
in_group: { Args: { group_id_: string }; Returns: boolean }
15341545
in_space: {
15351546
Args: {
@@ -1832,6 +1843,13 @@ export type Database = {
18321843
| Database["public"]["CompositeTypes"]["account_local_input"]
18331844
| null
18341845
}
1846+
group_member_info: {
1847+
id: number | null
1848+
name: string | null
1849+
platform: Database["public"]["Enums"]["Platform"] | null
1850+
agent_type: Database["public"]["Enums"]["AgentType"] | null
1851+
admin: boolean | null
1852+
}
18351853
inline_embedding_input: {
18361854
model: string | null
18371855
vector: number[] | null
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
CREATE OR REPLACE FUNCTION public.accept_group_invitation(token varchar) RETURNS boolean
2+
SET search_path = '' SECURITY DEFINER
3+
LANGUAGE plpgsql AS $$
4+
DECLARE
5+
v_creator UUID;
6+
token_info JSONB;
7+
v_group_id UUID;
8+
token_type varchar;
9+
as_admin BOOLEAN;
10+
is_admin BOOLEAN;
11+
BEGIN
12+
BEGIN
13+
SELECT creator INTO v_creator STRICT FROM public.secret_token WHERE id=token;
14+
SELECT public.get_secret_token(token) INTO token_info STRICT;
15+
SELECT (token_info->>'groupId')::UUID, token_info->>'type', (token_info->'admin')::boolean INTO v_group_id, token_type, as_admin STRICT;
16+
SELECT admin INTO is_admin FROM public.group_membership WHERE member_id = v_creator AND group_id = v_group_id;
17+
IF is_admin IS NOT true THEN RETURN false; END IF;
18+
INSERT INTO public.group_membership (group_id, member_id, admin) VALUES (v_group_id, auth.uid(), as_admin);
19+
RETURN true;
20+
EXCEPTION WHEN OTHERS THEN RETURN false;
21+
END;
22+
END;
23+
$$;

packages/database/supabase/schemas/account.sql

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,30 @@ WHERE id IN (
456456
WHERE permissions >= 'partial'
457457
);
458458

459+
CREATE OR REPLACE FUNCTION public.accept_group_invitation(token varchar) RETURNS boolean
460+
SET search_path = '' SECURITY DEFINER
461+
LANGUAGE plpgsql AS $$
462+
DECLARE
463+
v_creator UUID;
464+
token_info JSONB;
465+
v_group_id UUID;
466+
token_type varchar;
467+
as_admin BOOLEAN;
468+
is_admin BOOLEAN;
469+
BEGIN
470+
BEGIN
471+
SELECT creator INTO v_creator STRICT FROM public.secret_token WHERE id=token;
472+
SELECT public.get_secret_token(token) INTO token_info STRICT;
473+
SELECT (token_info->>'groupId')::UUID, token_info->>'type', (token_info->'admin')::boolean INTO v_group_id, token_type, as_admin STRICT;
474+
SELECT admin INTO is_admin FROM public.group_membership WHERE member_id = v_creator AND group_id = v_group_id;
475+
IF is_admin IS NOT true THEN RETURN false; END IF;
476+
INSERT INTO public.group_membership (group_id, member_id, admin) VALUES (v_group_id, auth.uid(), as_admin);
477+
RETURN true;
478+
EXCEPTION WHEN OTHERS THEN RETURN false;
479+
END;
480+
END;
481+
$$;
482+
459483
DROP POLICY IF EXISTS platform_account_policy ON public."PlatformAccount";
460484

461485
DROP POLICY IF EXISTS platform_account_select_policy ON public."PlatformAccount";

0 commit comments

Comments
 (0)