From 3111fafaf28661fcc526906dc53a3a431ee5fda5 Mon Sep 17 00:00:00 2001 From: Filipe Cabaco Date: Fri, 7 Apr 2023 10:53:51 +0100 Subject: [PATCH 1/3] feat: Add Functions to Self Hosted Adds Functions to Supabase Self Hosted --- docker/docker-compose-logging.yml | 9 +++++++++ docker/docker-compose.yml | 16 ++++++++++++++++ docker/volumes/logs/vector.yml | 11 +++++++++++ 3 files changed, 36 insertions(+) diff --git a/docker/docker-compose-logging.yml b/docker/docker-compose-logging.yml index 3aed11c1d5f..2a1cb32010c 100644 --- a/docker/docker-compose-logging.yml +++ b/docker/docker-compose-logging.yml @@ -80,6 +80,15 @@ services: options: syslog-address: "tcp://localhost:${VECTOR_PORT}" tag: meta + function: + depends_on: + analytics: + condition: service_healthy + logging: + driver: syslog + options: + syslog-address: "tcp://localhost:${VECTOR_PORT}" + tag: functions analytics: container_name: supabase-analytics diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 067633f138e..599497604a6 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -241,6 +241,22 @@ services: PG_META_DB_USER: supabase_admin PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD} + function: + container_name: supabase-function + image: supabase/edge-runtime:v1.1.7 + depends_on: + db: + # Disable this if you are using an external Postgres database + condition: service_healthy + restart: unless-stopped + environment: + JWT_SECRET: ${JWT_SECRET} + SUPABASE_URL: http://kong:8000" + SUPABASE_ANON_KEY: ${ANON_KEY} + SUPABASE_SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY} + SUPABASE_DB_URL: postgresql://postgres:${POSTGRES_PASSWORD}@{POSTGRES_DB}:${POSTGRES_PORT}/${POSTGRES_DB}" + VERIFY_JWT: false + # Comment out everything below this point if you are using an external Postgres database db: container_name: supabase-db diff --git a/docker/volumes/logs/vector.yml b/docker/volumes/logs/vector.yml index 2d7bd81764e..52394f8fc0f 100644 --- a/docker/volumes/logs/vector.yml +++ b/docker/volumes/logs/vector.yml @@ -33,6 +33,7 @@ transforms: rest: '.appname == "rest"' realtime: '.appname == "realtime"' storage: '.appname == "storage"' + functions: '.appname == "functions"' db: '.appname == "db"' # Kong logs only include api requests kong_logs: @@ -179,6 +180,16 @@ sinks: # lead to broken queries from studio. This works by the assumption that containers are started in the # following order: vector > db > logflare > kong uri: 'http://kong:8000/analytics/v1/api/logs?source_name=postgres.logs&api_key=your-super-secret-and-long-logflare-key' + logflare_functions: + type: 'http' + inputs: + - router.functions + encoding: + codec: 'json' + method: 'post' + request: + retry_max_duration_secs: 10 + uri: 'http://analytics:4000/api/logs?source_name=deno-relay-logs&api_key=your-super-secret-and-long-logflare-key' logflare_storage: type: 'http' inputs: From 2efa3095892e8f18b12074ceeeb1cb1932243295 Mon Sep 17 00:00:00 2001 From: Lakshan Perera Date: Tue, 11 Apr 2023 20:27:29 +1000 Subject: [PATCH 2/3] chore: add volumes and command for egdge functions --- docker/docker-compose-logging.yml | 3 +- docker/docker-compose.yml | 16 +++-- docker/volumes/functions/main/index.ts | 93 ++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 docker/volumes/functions/main/index.ts diff --git a/docker/docker-compose-logging.yml b/docker/docker-compose-logging.yml index 2a1cb32010c..b65d190d52e 100644 --- a/docker/docker-compose-logging.yml +++ b/docker/docker-compose-logging.yml @@ -80,7 +80,8 @@ services: options: syslog-address: "tcp://localhost:${VECTOR_PORT}" tag: meta - function: + + functions: depends_on: analytics: condition: service_healthy diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 599497604a6..0152bb6e109 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -241,13 +241,9 @@ services: PG_META_DB_USER: supabase_admin PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD} - function: - container_name: supabase-function - image: supabase/edge-runtime:v1.1.7 - depends_on: - db: - # Disable this if you are using an external Postgres database - condition: service_healthy + functions: + container_name: supabase-edge-functions + image: supabase/edge-runtime:v1.2.12 restart: unless-stopped environment: JWT_SECRET: ${JWT_SECRET} @@ -256,6 +252,12 @@ services: SUPABASE_SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY} SUPABASE_DB_URL: postgresql://postgres:${POSTGRES_PASSWORD}@{POSTGRES_DB}:${POSTGRES_PORT}/${POSTGRES_DB}" VERIFY_JWT: false + volumes: + - ./volumes/functions:/home/deno/functions:Z + command: + - start + - --main-service + - /home/deno/functions/main # Comment out everything below this point if you are using an external Postgres database db: diff --git a/docker/volumes/functions/main/index.ts b/docker/volumes/functions/main/index.ts new file mode 100644 index 00000000000..2408aee0ff7 --- /dev/null +++ b/docker/volumes/functions/main/index.ts @@ -0,0 +1,93 @@ +import { serve } from 'https://deno.land/std@0.131.0/http/server.ts' + +console.log('main function started') + +const JWT_SECRET = Deno.env.get('JWT_SECRET') +const VERIFY_JWT = Deno.env.get('VERIFY_JWT') === 'true' + +function getAuthToken(req: Request) { + const authHeader = req.headers.get('authorization') + if (!authHeader) { + throw new Error('Missing authorization header') + } + const [bearer, token] = authHeader.split(' ') + if (bearer !== 'Bearer') { + throw new Error(`Auth header is not 'Bearer {token}'`) + } + return token +} + +async function verifyJWT(jwt: string): Promise { + const encoder = new TextEncoder() + const secretKey = encoder.encode(JWT_SECRET) + try { + await jose.jwtVerify(jwt, secretKey) + } catch (err) { + console.error(err) + return false + } + return true +} + +serve(async (req: Request) => { + if (req.method !== 'OPTIONS' && VERIFY_JWT) { + try { + const token = getAuthToken(req) + const isValidJWT = await verifyJWT(token) + + if (!isValidJWT) { + return new Response(JSON.stringify({ msg: 'Invalid JWT' }), { + status: 401, + headers: { 'Content-Type': 'application/json' }, + }) + } + } catch (e) { + console.error(e) + return new Response(JSON.stringify({ msg: e.toString() }), { + status: 401, + headers: { 'Content-Type': 'application/json' }, + }) + } + } + + const url = new URL(req.url) + const { pathname } = url + const path_parts = pathname.split('/') + const service_name = path_parts[1] + + if (!service_name || service_name === '') { + const error = { msg: 'missing function name in request' } + return new Response(JSON.stringify(error), { + status: 400, + headers: { 'Content-Type': 'application/json' }, + }) + } + + const servicePath = `/home/deno/functions/${service_name}` + console.error(`serving the request with ${servicePath}`) + + const memoryLimitMb = 150 + const workerTimeoutMs = 1 * 60 * 1000 + const noModuleCache = false + const importMapPath = null + const envVarsObj = Deno.env.toObject() + const envVars = Object.keys(envVarsObj).map((k) => [k, envVarsObj[k]]) + + try { + const worker = await EdgeRuntime.userWorkers.create({ + servicePath, + memoryLimitMb, + workerTimeoutMs, + noModuleCache, + importMapPath, + envVars, + }) + return await worker.fetch(req) + } catch (e) { + const error = { msg: e.toString() } + return new Response(JSON.stringify(error), { + status: 500, + headers: { 'Content-Type': 'application/json' }, + }) + } +}) From 3140860be8bc55f998291c09d41cec333a19c741 Mon Sep 17 00:00:00 2001 From: Filipe Cabaco Date: Fri, 14 Apr 2023 22:14:31 +0100 Subject: [PATCH 3/3] Expose port to run functions from localhost --- docker/.env.example | 4 ++++ docker/docker-compose.yml | 2 ++ 2 files changed, 6 insertions(+) diff --git a/docker/.env.example b/docker/.env.example index e39eb8f4f0b..17bb463705f 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -79,6 +79,10 @@ SUPABASE_PUBLIC_URL=http://localhost:8000 # Enable webp support IMGPROXY_ENABLE_WEBP_DETECTION=true +############ +# Functions - Configuration for Functions +############ +FUNCTIONS_HTTP_PORT=9002 ############ # Logs - Configuration for Logflare diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 0152bb6e109..c2a5205856d 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -252,6 +252,8 @@ services: SUPABASE_SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY} SUPABASE_DB_URL: postgresql://postgres:${POSTGRES_PASSWORD}@{POSTGRES_DB}:${POSTGRES_PORT}/${POSTGRES_DB}" VERIFY_JWT: false + ports: + - ${FUNCTIONS_HTTP_PORT}:9000/tcp volumes: - ./volumes/functions:/home/deno/functions:Z command: