Skip to content

Commit 6b5fbb3

Browse files
authored
ENG-1437 Use the supabase CORS headers as a basis. Extend to all responses.
1 parent e9feba0 commit 6b5fbb3

6 files changed

Lines changed: 110 additions & 105 deletions

File tree

packages/database/supabase/functions/create-group/deno.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"imports": {
33
"@supabase/functions-js/edge-runtime": "jsr:@supabase/functions-js/edge-runtime.d.ts",
4-
"@supabase/supabase-js": "jsr:@supabase/supabase-js@2",
4+
"@supabase/supabase-js": "jsr:@supabase/supabase-js@2.95",
5+
"@supabase/supabase-js/cors": "jsr:@supabase/supabase-js@2.95/cors",
56
"@supabase/functions-js": "jsr:@supabase/functions-js@2",
67
"@repo/database/lib/client": "../../../src/lib/client.ts"
78
}

packages/database/supabase/functions/create-group/index.ts

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// This enables autocomplete, go to definition, etc.
44

55
import "@supabase/functions-js/edge-runtime";
6+
import { corsHeaders } from '@supabase/supabase-js/cors'
67
import { createClient, type UserResponse } from "@supabase/supabase-js";
78
import type { DGSupabaseClient } from "@repo/database/lib/client";
89

@@ -20,31 +21,37 @@ const isAllowedOrigin = (origin: string): boolean =>
2021
Deno.serve(async (req) => {
2122
const origin = req.headers.get("origin");
2223
const originIsAllowed = origin && isAllowedOrigin(origin);
24+
const myCorsHeaders = {...corsHeaders, "Access-Control-Allow-Origin": originIsAllowed? origin:''};
2325
if (req.method === "OPTIONS") {
2426
return new Response(null, {
2527
status: 204,
26-
headers: {
27-
...(originIsAllowed ? { "Access-Control-Allow-Origin": origin } : {}),
28-
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
29-
"Access-Control-Allow-Headers":
30-
"Content-Type, Authorization, x-vercel-protection-bypass, x-client-info, apikey",
31-
"Access-Control-Max-Age": "86400",
32-
},
28+
headers: myCorsHeaders,
3329
});
3430
}
3531
if (req.method !== "POST") {
3632
return Response.json(
3733
{ msg: 'Method not allowed' },
38-
{ status: 405 }
34+
{ status: 405,
35+
headers: myCorsHeaders,
36+
}
3937
);
4038
}
41-
42-
const input: {name?: string} = await req.json();
39+
let input: {name?: string} = {}
40+
try {
41+
input = await req.json();
42+
} catch (error) {
43+
return Response.json({
44+
msg: 'Invalid JSON in request body', error: String(error?.message ?? error)
45+
}, {
46+
status: 400,
47+
headers: myCorsHeaders,
48+
});
49+
}
4350
const groupName = input.name;
4451
if (groupName === undefined) {
4552
return new Response("Missing group name", {
4653
status: 400,
47-
headers: { "Content-Type": "application/json" },
54+
headers: myCorsHeaders,
4855
});
4956
}
5057
// @ts-ignore Deno is not visible to the IDE
@@ -57,7 +64,7 @@ Deno.serve(async (req) => {
5764
if (!url || !anon_key || !service_key) {
5865
return new Response("Missing SUPABASE_URL or SB_SECRET_KEY or SB_PUBLISHABLE_KEY", {
5966
status: 500,
60-
headers: { "Content-Type": "application/json" },
67+
headers: myCorsHeaders,
6168
});
6269
}
6370
const supabase = createClient(url, anon_key)
@@ -67,6 +74,7 @@ Deno.serve(async (req) => {
6774
{ msg: 'Missing authorization headers' },
6875
{
6976
status: 401,
77+
headers: myCorsHeaders,
7078
}
7179
)
7280
}
@@ -79,6 +87,7 @@ Deno.serve(async (req) => {
7987
{ msg: 'Invalid JWT' },
8088
{
8189
status: 401,
90+
headers: myCorsHeaders,
8291
}
8392
)
8493
}
@@ -105,27 +114,21 @@ Deno.serve(async (req) => {
105114
{ msg: 'A group by this name exists' },
106115
{
107116
status: 400,
117+
headers: myCorsHeaders,
108118
});
109119
}
110-
return Response.json({ msg: 'Failed to create group user', error: error.message }, { status: 500 });
120+
return Response.json({ msg: 'Failed to create group user', error: error.message }, { status: 500, headers: myCorsHeaders });
111121
}
112122
// eslint-disable-next-line @typescript-eslint/naming-convention
113123
const group_id = userResponse.data.user.id;
114124
// eslint-disable-next-line @typescript-eslint/naming-convention
115125
const membershipResponse = await supabaseAdmin.from("group_membership").insert({group_id, member_id:data.claims.sub, admin:true});
116126
if (membershipResponse.error)
117-
return Response.json({ msg: `Failed to create membership for group ${group_id}`, error: membershipResponse.error.message }, { status: 500 });
118-
119-
const res = Response.json({group_id});
120-
121-
if (originIsAllowed) {
122-
res.headers.set("Access-Control-Allow-Origin", origin as string);
123-
res.headers.set("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
124-
res.headers.set(
125-
"Access-Control-Allow-Headers",
126-
"Content-Type, Authorization, x-vercel-protection-bypass, x-client-info, apikey",
127-
);
128-
}
127+
return Response.json({
128+
msg: `Failed to create membership for group ${group_id}`,
129+
error: membershipResponse.error.message
130+
},
131+
{ status: 500, headers: myCorsHeaders, });
129132

130-
return res;
133+
return Response.json({group_id}, {headers: myCorsHeaders });
131134
});

packages/database/supabase/functions/create-space/deno.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"imports": {
33
"@supabase/functions-js/edge-runtime": "jsr:@supabase/functions-js/edge-runtime.d.ts",
4-
"@supabase/supabase-js": "jsr:@supabase/supabase-js@2",
4+
"@supabase/supabase-js": "jsr:@supabase/supabase-js@2.95",
5+
"@supabase/supabase-js/cors": "jsr:@supabase/supabase-js@2.95/cors",
56
"@supabase/functions-js": "jsr:@supabase/functions-js@2",
67
"@repo/database/dbTypes": "../../../src/dbTypes.ts",
78
"@repo/database/lib/contextFunctions": "../../../src/lib/contextFunctions.ts",

packages/database/supabase/functions/create-space/index.ts

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// https://deno.land/manual/getting_started/setup_your_environment
33
// This enables autocomplete, go to definition, etc.
44
import "@supabase/functions-js/edge-runtime";
5+
import { corsHeaders } from '@supabase/supabase-js/cors'
56
import {
67
createClient,
78
type User,
@@ -195,16 +196,11 @@ const isAllowedOrigin = (origin: string): boolean =>
195196
Deno.serve(async (req) => {
196197
const origin = req.headers.get("origin");
197198
const originIsAllowed = origin && isAllowedOrigin(origin);
199+
const myCorsHeaders = {...corsHeaders, "Access-Control-Allow-Origin": originIsAllowed? origin:''};
198200
if (req.method === "OPTIONS") {
199201
return new Response(null, {
200202
status: 204,
201-
headers: {
202-
...(originIsAllowed ? { "Access-Control-Allow-Origin": origin } : {}),
203-
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
204-
"Access-Control-Allow-Headers":
205-
"Content-Type, Authorization, x-vercel-protection-bypass, x-client-info, apikey",
206-
"Access-Control-Max-Age": "86400",
207-
},
203+
headers: myCorsHeaders,
208204
});
209205
}
210206

@@ -215,7 +211,7 @@ Deno.serve(async (req) => {
215211
if (!url || !key) {
216212
return new Response("Missing SUPABASE_URL or SB_SECRET_KEY", {
217213
status: 500,
218-
headers: { "Content-Type": "application/json" },
214+
headers: myCorsHeaders,
219215
});
220216
}
221217

@@ -227,6 +223,7 @@ Deno.serve(async (req) => {
227223
{ msg: 'Missing authorization headers' },
228224
{
229225
status: 401,
226+
headers: myCorsHeaders,
230227
}
231228
)
232229
}
@@ -235,42 +232,45 @@ Deno.serve(async (req) => {
235232
url, token, { global: { headers: { Authorization: authHeader } } });
236233
{
237234
const { error } = await supabaseAnonClient.from("Space").select("id").limit(1);
238-
if (error?.code) return new Response(JSON.stringify(error), {
239-
status: 401,
240-
headers: { "Content-Type": "application/json" },
241-
});
235+
if (error?.code) {
236+
const {code, message, name} = error;
237+
return Response.json({code, message, name}, {
238+
status: 401,
239+
headers: myCorsHeaders,
240+
});
241+
}
242242
}
243243

244244
// note: If we wanted this to be bound by permissions, we'd set the following options:
245245
// { global: { headers: { Authorization: authHeader } } }
246246
// But the point here is to bypass RLS
247247
const supabase: DGSupabaseClient = createClient(url, key);
248248

249-
const input = await req.json();
249+
let input: SpaceCreationInput | undefined = undefined;
250+
try {
251+
input = await req.json();
252+
// TODO: Validate input
253+
// For now, errors will be caught downstream
254+
} catch (error) {
255+
return Response.json({
256+
msg: 'Invalid JSON in request body', error: String(error?.message ?? error)
257+
}, {
258+
status: 400,
259+
headers: myCorsHeaders,
260+
});
261+
}
250262

251-
const { data, error } = await processAndGetOrCreateSpace(supabase, input);
263+
const { data, error } = await processAndGetOrCreateSpace(supabase, input!);
252264
if (error) {
253-
const status = error.code === "invalid space" ? 400 : 500;
254-
return new Response(JSON.stringify(error), {
265+
const {code, message, name} = error;
266+
const status = code === "invalid space" ? 400 : 500;
267+
return Response.json({code, message, name}, {
255268
status,
256-
headers: { "Content-Type": "application/json" },
269+
headers: myCorsHeaders,
257270
});
258271
}
259272

260-
const res = new Response(JSON.stringify(data), {
261-
headers: { "Content-Type": "application/json" },
262-
});
263-
264-
if (originIsAllowed) {
265-
res.headers.set("Access-Control-Allow-Origin", origin as string);
266-
res.headers.set("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
267-
res.headers.set(
268-
"Access-Control-Allow-Headers",
269-
"Content-Type, Authorization, x-vercel-protection-bypass, x-client-info, apikey",
270-
);
271-
}
272-
273-
return res;
273+
return Response.json(data, {headers: myCorsHeaders });
274274
});
275275

276276
/* To invoke locally:

0 commit comments

Comments
 (0)