diff --git a/apps/docs/content/guides/functions/websockets.mdx b/apps/docs/content/guides/functions/websockets.mdx index d606fec4a8f..48fd6fff028 100644 --- a/apps/docs/content/guides/functions/websockets.mdx +++ b/apps/docs/content/guides/functions/websockets.mdx @@ -7,7 +7,7 @@ subtitle: 'How to handle WebSocket connections in Edge Functions' Edge Functions supports hosting WebSocket servers that can facilitate bi-directional communications with browser clients. -You can also establish outgoing WebSocket client connections to another server from Edge Functions (e.g., [OpenAI Realtime API](https://platform.openai.com/docs/guides/realtime/overview)). +You can also establish outgoing WebSocket client connections to another server from Edge Functions (e.g., [OpenAI Realtime API](https://platform.openai.com/docs/guides/realtime/overview)). You can find an example OpenAI Realtime Relay Server implementation on the [supabase-community GitHub account](https://github.com/supabase-community/openai-realtime-console?tab=readme-ov-file#using-supabase-edge-functions-as-a-relay-server). ### Writing a WebSocket server @@ -92,94 +92,17 @@ server.listen(8080); You can also establish an outbound WebSocket connection to another server from an Edge Function. -Combining it with incoming WebSocket servers, it's possible to use Edge Functions as a WebSocket proxy. +Combining it with incoming WebSocket servers, it's possible to use Edge Functions as a WebSocket proxy, for example as a [relay server](https://github.com/supabase-community/openai-realtime-console?tab=readme-ov-file#using-supabase-edge-functions-as-a-relay-server) for the [OpenAI Realtime API](https://platform.openai.com/docs/guides/realtime/overview). -Here is an example of proxying messages to OpenAI Realtime API. - -We use [Supabase Auth](/docs/guides/functions/auth#fetching-the-user) to authenticate the user who is sending the messages. - -```ts -import { createClient } from 'jsr:@supabase/supabase-js@2' - -const supabase = createClient( - Deno.env.get('SUPABASE_URL'), - Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') -) -const OPENAI_API_KEY = Deno.env.get('OPENAI_API_KEY') - -Deno.serve(async (req) => { - const upgrade = req.headers.get('upgrade') || '' - - if (upgrade.toLowerCase() != 'websocket') { - return new Response("request isn't trying to upgrade to websocket.") - } - - // WebSocket browser clients does not support sending custom headers. - // We have to use the URL query params to provide user's JWT. - // Please be aware query params may be logged in some logging systems. - const url = new URL(req.url) - const jwt = url.searchParams.get('jwt') - if (!jwt) { - console.error('Auth token not provided') - return new Response('Auth token not provided', { status: 403 }) - } - const { error, data } = await supabase.auth.getUser(jwt) - if (error) { - console.error(error) - return new Response('Invalid token provided', { status: 403 }) - } - if (!data.user) { - console.error('user is not authenticated') - return new Response('User is not authenticated', { status: 403 }) - } - - const { socket, response } = Deno.upgradeWebSocket(req) - - socket.onopen = () => { - // initiate an outbound WS connection with OpenAI - const url = 'wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2024-10-01' - - // openai-insecure-api-key isn't a problem since this code runs in an Edge Function (not client browser) - const openaiWS = new WebSocket(url, [ - 'realtime', - `openai-insecure-api-key.${OPENAI_API_KEY}`, - 'openai-beta.realtime-v1', - ]) - - openaiWS.onopen = () => { - console.log('Connected to OpenAI server.') - - socket.onmessage = (e) => { - console.log('socket message:', e.data) - // only send the message if openAI ws is open - if (openaiWS.readyState === 1) { - openaiWS.send(e.data) - } else { - socket.send( - JSON.stringify({ - type: 'error', - msg: 'openAI connection not ready', - }) - ) - } - } - } - - openaiWS.onmessage = (e) => { - console.log(e.data) - socket.send(e.data) - } - - openaiWS.onerror = (e) => console.log('OpenAI error: ', e.message) - openaiWS.onclose = (e) => console.log('OpenAI session closed') - } - - socket.onerror = (e) => console.log('socket errored:', e.message) - socket.onclose = () => console.log('socket closed') - - return response // 101 (Switching Protocols) -}) -``` +<$CodeSample +external={true} +org="supabase-community" +repo="openai-realtime-console" +commit="0f93657a71670704fbf77c48cf54d6c9eb956698" +path="/supabase/functions/relay/index.ts" +meta="supabase/functions/relay/index.ts" +lines={[[1, 3], [5, -1]]} +/> ### Authentication diff --git a/apps/docs/features/directives/CodeSample.ts b/apps/docs/features/directives/CodeSample.ts index ab297b45e6b..47d74391b59 100644 --- a/apps/docs/features/directives/CodeSample.ts +++ b/apps/docs/features/directives/CodeSample.ts @@ -48,7 +48,7 @@ import { z, type SafeParseError } from 'zod' import { fetchWithNextOptions } from '~/features/helpers.fetch' import { EXAMPLES_DIRECTORY } from '~/lib/docs' -const ALLOW_LISTED_GITHUB_ORGS = ['supabase'] as [string, ...string[]] +const ALLOW_LISTED_GITHUB_ORGS = ['supabase', 'supabase-community'] as [string, ...string[]] const linesSchema = z.array(z.tuple([z.coerce.number(), z.coerce.number()])) const linesValidator = z