From 1d3a59e881e444cba658972e8c97497e63f6753a Mon Sep 17 00:00:00 2001 From: Duke Manh Date: Fri, 28 Jan 2022 17:10:04 +0000 Subject: [PATCH] Self host Supabase --- .gitpod.yml | 38 +++-- config/env.development | 55 +++++- docker/docker-compose.yml | 3 + docker/supabase/.gitignore | 5 + docker/supabase/dev/data.sql | 48 ++++++ docker/supabase/dev/docker-compose.dev.yml | 20 +++ docker/supabase/docker-compose.yml | 156 ++++++++++++++++++ docker/supabase/volumes/api/kong.yml | 148 +++++++++++++++++ .../volumes/db/init/00-initial-schema.sql | 48 ++++++ .../volumes/db/init/01-auth-schema.sql | 145 ++++++++++++++++ .../volumes/db/init/02-storage-schema.sql | 116 +++++++++++++ .../volumes/db/init/03-post-setup.sql | 68 ++++++++ pnpm-lock.yaml | 100 +++++++++-- src/api/sso/env.local | 5 + src/api/sso/package.json | 1 + src/api/sso/src/supabase.js | 5 + 16 files changed, 938 insertions(+), 23 deletions(-) create mode 100644 docker/supabase/.gitignore create mode 100644 docker/supabase/dev/data.sql create mode 100644 docker/supabase/dev/docker-compose.dev.yml create mode 100644 docker/supabase/docker-compose.yml create mode 100644 docker/supabase/volumes/api/kong.yml create mode 100644 docker/supabase/volumes/db/init/00-initial-schema.sql create mode 100644 docker/supabase/volumes/db/init/01-auth-schema.sql create mode 100644 docker/supabase/volumes/db/init/02-storage-schema.sql create mode 100644 docker/supabase/volumes/db/init/03-post-setup.sql create mode 100644 src/api/sso/src/supabase.js diff --git a/.gitpod.yml b/.gitpod.yml index 261dd4f014..a33fcf7ef9 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -10,12 +10,14 @@ tasks: eval $(gp env -e WEB_URL=$(gp url 8000)) eval $(gp env -e API_HOST=$(gp url 8443)) eval $(gp env -e SSO_LOGIN_URL=$(gp url 8081)) + eval $(gp env -e SUPABASE_API=$(gp url 8911)) eval $(gp env -e SERVICES="status image sso search posts feed-discovery users redis elasticsearch login firebase") sed -r \ -e "s@(.+=)http://localhost:8000(/[^ ]*)*@\1$WEB_URL\2@g" \ -e "s@(.+=)http://localhost:3000(/[^ ]*)*@\1$API_URL\2@g" \ -e "s@(.+=)http://localhost:8081(/[^ ]*)*@\1$SSO_LOGIN_URL\2@g" \ + -e "s@(.+=)http://kong:8000(/[^ ]*)*@\1$SUPABASE_API\2@g" \ -e "s@(.+=)http://localhost([^:]*)@\1$API_HOST\2@g" \ -e "s@development\.yml@gitpod\.yml@" \ config/env.development > .env @@ -45,38 +47,54 @@ tasks: # Exposing service ports # Overriding the default notification popup when a port is open ports: - # NextJS frontend - port: 8000 + name: frontend visibility: public onOpen: open-preview - # Web API - port: 3000 + name: web-api visibility: public onOpen: ignore - # Microservice server - port: 8443 + name: micro-services visibility: public onOpen: ignore - # Redis server - port: 6379 + name: redis onOpen: ignore - # Elasticsearch server - port: 9200 + name: elasticsearch onOpen: ignore - # Firebase emulator - port: 4000 + name: firebase onOpen: ignore - # Firestore emulator - port: 8088 + name: firestore onOpen: ignore - # Traefik UI - port: 8080 + name: traefik onOpen: ignore - # Login Page - port: 8081 + name: login onOpen: ignore - # Static Web Content test - port: 8888 + name: test-web-content + onOpen: ignore + - port: 7777 + name: auth + onOpen: ignore + # Supabase Services + - port: 8910 + name: supabase-studio + onOpen: notify + - port: 8911 + name: kong-http + onOpen: ignore + - port: 8912 + name: kong-https + onOpen: ignore + - port: 8913 + name: postgresql onOpen: ignore github: diff --git a/config/env.development b/config/env.development index 20cc27c0ed..1ea4cd7d43 100644 --- a/config/env.development +++ b/config/env.development @@ -10,7 +10,7 @@ COMPOSE_PROJECT_NAME=telescope_api # so it will work on Windows and Unix, see # https://docs.docker.com/compose/reference/envvars/#compose_file COMPOSE_PATH_SEPARATOR=; -COMPOSE_FILE=docker/docker-compose.yml;docker/development.yml +COMPOSE_FILE=docker/docker-compose.yml;docker/development.yml;docker/supabase/docker-compose.yml # The host where the Telescope 1.0 front-end and back-end are run. @@ -87,7 +87,6 @@ JWT_AUDIENCE=http://localhost # How long should a JWT work before it expires JWT_EXPIRES_IN=1h - ################################################################################ # Image Service ################################################################################ @@ -260,3 +259,55 @@ FEED_QUEUE_PARALLEL_WORKERS=1 # Max number of posts per page MAX_POSTS_PER_PAGE=5 + + +################################################################################ +# Supabase Services +################################################################################ + +SUPABASE_URL=http://kong:8000 + +# Secrets +# YOU MUST CHANGE THESE BEFORE GOING INTO PRODUCTION + +POSTGRES_PASSWORD=your-super-secret-and-long-postgres-password +JWT_SECRET=your-super-secret-jwt-token-with-at-least-32-characters-long +ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE +SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q + +# Auth + +## General + +### Telescope web URL, must match with WEB_URL +SITE_URL=http://localhost:8000 + +ADDITIONAL_REDIRECT_URLS= +JWT_EXPIRY=3600 +DISABLE_SIGNUP=false + +## Email auth +ENABLE_EMAIL_SIGNUP=true +ENABLE_EMAIL_AUTOCONFIRM=true +SMTP_ADMIN_EMAIL=admin@example.com +SMTP_HOST=mail +SMTP_PORT=2500 +SMTP_USER=fake_mail_user +SMTP_PASS=fake_mail_password +SMTP_SENDER_NAME=fake_sender + +## Phone auth +ENABLE_PHONE_SIGNUP=false +ENABLE_PHONE_AUTOCONFIRM=false + +# Ports + +## Studio port +STUDIO_PORT=8910 + +## API endpoint ports +KONG_HTTP_PORT=8911 +KONG_HTTPS_PORT=8912 + +## DB port +POSTGRES_PORT=8913 diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 25798a2aa5..9163807d6b 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -84,6 +84,9 @@ services: - ALLOWED_APP_ORIGINS - JWT_EXPIRES_IN - USERS_URL + # Supabase config + - SERVICE_ROLE_KEY + - SUPABASE_URL # Satellite authentication/authorization support - JWT_ISSUER - JWT_AUDIENCE diff --git a/docker/supabase/.gitignore b/docker/supabase/.gitignore new file mode 100644 index 0000000000..e5c6762c07 --- /dev/null +++ b/docker/supabase/.gitignore @@ -0,0 +1,5 @@ +volumes/db/data +volumes/db/init/data.sql +volumes/storage +.env +test.http diff --git a/docker/supabase/dev/data.sql b/docker/supabase/dev/data.sql new file mode 100644 index 0000000000..2328004184 --- /dev/null +++ b/docker/supabase/dev/data.sql @@ -0,0 +1,48 @@ +create table profiles ( + id uuid references auth.users not null, + updated_at timestamp with time zone, + username text unique, + avatar_url text, + website text, + + primary key (id), + unique(username), + constraint username_length check (char_length(username) >= 3) +); + +alter table profiles enable row level security; + +create policy "Public profiles are viewable by the owner." + on profiles for select + using ( auth.uid() = id ); + +create policy "Users can insert their own profile." + on profiles for insert + with check ( auth.uid() = id ); + +create policy "Users can update own profile." + on profiles for update + using ( auth.uid() = id ); + +-- Set up Realtime +begin; + drop publication if exists supabase_realtime; + create publication supabase_realtime; +commit; +alter publication supabase_realtime add table profiles; + +-- Set up Storage +insert into storage.buckets (id, name) +values ('avatars', 'avatars'); + +create policy "Avatar images are publicly accessible." + on storage.objects for select + using ( bucket_id = 'avatars' ); + +create policy "Anyone can upload an avatar." + on storage.objects for insert + with check ( bucket_id = 'avatars' ); + +create policy "Anyone can update an avatar." + on storage.objects for update + with check ( bucket_id = 'avatars' ); diff --git a/docker/supabase/dev/docker-compose.dev.yml b/docker/supabase/dev/docker-compose.dev.yml new file mode 100644 index 0000000000..8ea3772b4d --- /dev/null +++ b/docker/supabase/dev/docker-compose.dev.yml @@ -0,0 +1,20 @@ +version: '3.8' + +services: + mail: + container_name: supabase-mail + image: inbucket/inbucket:stable + ports: + - '2500:2500' # SMTP + - '9000:9000' # web interface + - '1100:1100' # POP3 + meta: + ports: + - 5555:8080 + db: + volumes: + - /var/lib/postgresql/data + - ./supabase/dev/data.sql:/docker-entrypoint-initdb.d/data.sql + storage: + volumes: + - /var/lib/storage diff --git a/docker/supabase/docker-compose.yml b/docker/supabase/docker-compose.yml new file mode 100644 index 0000000000..de05bcd2d3 --- /dev/null +++ b/docker/supabase/docker-compose.yml @@ -0,0 +1,156 @@ +# Usage +# Start: docker-compose up +# With helpers: docker-compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml up +# Stop: docker-compose down +# Destroy: docker-compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml down -v --remove-orphans + +version: '3.8' + +services: + studio: + container_name: supabase-studio + image: supabase/studio:latest + restart: unless-stopped + ports: + - ${STUDIO_PORT}:3000/tcp + environment: + SUPABASE_URL: http://kong:8000 + STUDIO_PG_META_URL: http://meta:8080 + SUPABASE_ANON_KEY: ${ANON_KEY} + SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY} + + kong: + container_name: supabase-kong + image: kong:2.1 + restart: unless-stopped + ports: + - ${KONG_HTTP_PORT}:8000/tcp + - ${KONG_HTTPS_PORT}:8443/tcp + environment: + KONG_DATABASE: 'off' + KONG_DECLARATIVE_CONFIG: /var/lib/kong/kong.yml + # https://github.com/supabase/cli/issues/14 + KONG_DNS_ORDER: LAST,A,CNAME + KONG_PLUGINS: request-transformer,cors,key-auth,acl + volumes: + - ./supabase/volumes/api/kong.yml:/var/lib/kong/kong.yml + + auth: + container_name: supabase-auth + image: supabase/gotrue:v2.2.12 + depends_on: + - db + restart: unless-stopped + environment: + GOTRUE_API_HOST: 0.0.0.0 + GOTRUE_API_PORT: 9999 + + GOTRUE_DB_DRIVER: postgres + GOTRUE_DB_DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@db:5432/postgres?search_path=auth + + GOTRUE_SITE_URL: ${SITE_URL} + GOTRUE_URI_ALLOW_LIST: ${ADDITIONAL_REDIRECT_URLS} + GOTRUE_DISABLE_SIGNUP: ${DISABLE_SIGNUP} + + GOTRUE_JWT_SECRET: ${JWT_SECRET} + GOTRUE_JWT_EXP: ${JWT_EXPIRY} + GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated + + GOTRUE_EXTERNAL_EMAIL_ENABLED: ${ENABLE_EMAIL_SIGNUP} + GOTRUE_MAILER_AUTOCONFIRM: ${ENABLE_EMAIL_AUTOCONFIRM} + GOTRUE_SMTP_ADMIN_EMAIL: ${SMTP_ADMIN_EMAIL} + GOTRUE_SMTP_HOST: ${SMTP_HOST} + GOTRUE_SMTP_PORT: ${SMTP_PORT} + GOTRUE_SMTP_USER: ${SMTP_USER} + GOTRUE_SMTP_PASS: ${SMTP_PASS} + GOTRUE_SMTP_SENDER_NAME: ${SMTP_SENDER_NAME} + GOTRUE_MAILER_URLPATHS_INVITE: /auth/v1/verify + GOTRUE_MAILER_URLPATHS_CONFIRMATION: /auth/v1/verify + GOTRUE_MAILER_URLPATHS_RECOVERY: /auth/v1/verify + GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: /auth/v1/verify + + GOTRUE_EXTERNAL_PHONE_ENABLED: ${ENABLE_PHONE_SIGNUP} + GOTRUE_SMS_AUTOCONFIRM: ${ENABLE_PHONE_AUTOCONFIRM} + + rest: + container_name: supabase-rest + image: postgrest/postgrest:v9.0.0 + depends_on: + - db + restart: unless-stopped + environment: + PGRST_DB_URI: postgres://postgres:${POSTGRES_PASSWORD}@db:5432/postgres + PGRST_DB_SCHEMAS: public,storage + PGRST_DB_ANON_ROLE: anon + PGRST_JWT_SECRET: ${JWT_SECRET} + PGRST_DB_USE_LEGACY_GUCS: 'false' + + realtime: + container_name: supabase-realtime + image: supabase/realtime:v0.19.3 + depends_on: + - db + restart: unless-stopped + environment: + DB_HOST: db + DB_PORT: 5432 + DB_NAME: postgres + DB_USER: postgres + DB_PASSWORD: ${POSTGRES_PASSWORD} + DB_SSL: 'false' + PORT: 4000 + JWT_SECRET: ${JWT_SECRET} + REPLICATION_MODE: RLS + REPLICATION_POLL_INTERVAL: 100 + SECURE_CHANNELS: 'true' + SLOT_NAME: supabase_realtime_rls + TEMPORARY_SLOT: 'true' + command: > + bash -c "./prod/rel/realtime/bin/realtime eval Realtime.Release.migrate + && ./prod/rel/realtime/bin/realtime start" + + storage: + container_name: supabase-storage + image: supabase/storage-api:v0.10.0 + depends_on: + - db + - rest + restart: unless-stopped + environment: + ANON_KEY: ${ANON_KEY} + SERVICE_KEY: ${SERVICE_ROLE_KEY} + POSTGREST_URL: http://rest:3000 + PGRST_JWT_SECRET: ${JWT_SECRET} + DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@db:5432/postgres + PGOPTIONS: -c search_path=storage,public + FILE_SIZE_LIMIT: 52428800 + STORAGE_BACKEND: file + FILE_STORAGE_BACKEND_PATH: /var/lib/storage + TENANT_ID: stub + # TODO: https://github.com/supabase/storage-api/issues/55 + REGION: stub + GLOBAL_S3_BUCKET: stub + volumes: + - ./supabase/volumes/storage:/var/lib/storage + + meta: + container_name: supabase-meta + image: supabase/postgres-meta:v0.29.0 + depends_on: + - db + restart: unless-stopped + environment: + PG_META_PORT: 8080 + PG_META_DB_HOST: db + PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD} + + db: + container_name: supabase-db + image: supabase/postgres:14.1.0 + command: postgres -c config_file=/etc/postgresql/postgresql.conf + restart: unless-stopped + environment: + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + # - ./supbase/volumes/db/data:/var/lib/postgresql/data + - ./supabase/volumes/db/init:/docker-entrypoint-initdb.d diff --git a/docker/supabase/volumes/api/kong.yml b/docker/supabase/volumes/api/kong.yml new file mode 100644 index 0000000000..8cf7f3ceda --- /dev/null +++ b/docker/supabase/volumes/api/kong.yml @@ -0,0 +1,148 @@ +_format_version: '1.1' + +### +### Consumers / Users +### +consumers: + - username: anon + keyauth_credentials: + - key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE + - username: service_role + keyauth_credentials: + - key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q + +### +### Access Control List +### +acls: + - consumer: anon + group: anon + - consumer: service_role + group: admin + +### +### API Routes +### +services: + ## Open Auth routes + - name: auth-v1-open + url: http://auth:9999/verify + routes: + - name: auth-v1-open + strip_path: true + paths: + - /auth/v1/verify + plugins: + - name: cors + - name: auth-v1-open-callback + url: http://auth:9999/callback + routes: + - name: auth-v1-open-callback + strip_path: true + paths: + - /auth/v1/callback + plugins: + - name: cors + - name: auth-v1-open-authorize + url: http://auth:9999/authorize + routes: + - name: auth-v1-open-authorize + strip_path: true + paths: + - /auth/v1/authorize + plugins: + - name: cors + + ## Secure Auth routes + - name: auth-v1 + _comment: 'GoTrue: /auth/v1/* -> http://auth:9999/*' + url: http://auth:9999/ + routes: + - name: auth-v1-all + strip_path: true + paths: + - /auth/v1/ + plugins: + - name: cors + - name: key-auth + config: + hide_credentials: false + - name: acl + config: + hide_groups_header: true + allow: + - admin + - anon + + ## Secure REST routes + - name: rest-v1 + _comment: 'PostgREST: /rest/v1/* -> http://rest:3000/*' + url: http://rest:3000/ + routes: + - name: rest-v1-all + strip_path: true + paths: + - /rest/v1/ + plugins: + - name: cors + - name: key-auth + config: + hide_credentials: true + - name: acl + config: + hide_groups_header: true + allow: + - admin + - anon + + ## Secure Realtime routes + - name: realtime-v1 + _comment: 'Realtime: /realtime/v1/* -> ws://realtime:4000/socket/*' + url: http://realtime:4000/socket/ + routes: + - name: realtime-v1-all + strip_path: true + paths: + - /realtime/v1/ + plugins: + - name: cors + - name: key-auth + config: + hide_credentials: false + - name: acl + config: + hide_groups_header: true + allow: + - admin + - anon + + ## Storage routes: the storage server manages its own auth + - name: storage-v1 + _comment: 'Storage: /storage/v1/* -> http://storage:5000/*' + url: http://storage:5000/ + routes: + - name: storage-v1-all + strip_path: true + paths: + - /storage/v1/ + plugins: + - name: cors + + ## Secure Database routes + - name: meta + _comment: 'pg-meta: /pg/* -> http://pg-meta:8080/*' + url: http://meta:8080/ + routes: + - name: meta-all + strip_path: true + paths: + - /pg/ + plugins: + - name: key-auth + config: + hide_credentials: false + - name: acl + config: + hide_groups_header: true + allow: + - admin diff --git a/docker/supabase/volumes/db/init/00-initial-schema.sql b/docker/supabase/volumes/db/init/00-initial-schema.sql new file mode 100644 index 0000000000..474b866773 --- /dev/null +++ b/docker/supabase/volumes/db/init/00-initial-schema.sql @@ -0,0 +1,48 @@ +-- Set up realtime +create schema if not exists realtime; +-- create publication supabase_realtime; -- defaults to empty publication +create publication supabase_realtime; + +-- Supabase super admin +create user supabase_admin; +alter user supabase_admin with superuser createdb createrole replication bypassrls; + +-- Extension namespacing +create schema if not exists extensions; +create extension if not exists "uuid-ossp" with schema extensions; +create extension if not exists pgcrypto with schema extensions; +create extension if not exists pgjwt with schema extensions; + +-- Set up auth roles for the developer +create role anon nologin noinherit; +create role authenticated nologin noinherit; -- "logged in" user: web_user, app_user, etc +create role service_role nologin noinherit bypassrls; -- allow developers to create JWT's that bypass their policies + +create user authenticator noinherit; +grant anon to authenticator; +grant authenticated to authenticator; +grant service_role to authenticator; +grant supabase_admin to authenticator; + +grant usage on schema public to postgres, anon, authenticated, service_role; +alter default privileges in schema public grant all on tables to postgres, anon, authenticated, service_role; +alter default privileges in schema public grant all on functions to postgres, anon, authenticated, service_role; +alter default privileges in schema public grant all on sequences to postgres, anon, authenticated, service_role; + +-- Allow Extensions to be used in the API +grant usage on schema extensions to postgres, anon, authenticated, service_role; + +-- Set up namespacing +alter user supabase_admin SET search_path TO public, extensions; -- don't include the "auth" schema + +-- These are required so that the users receive grants whenever "supabase_admin" creates tables/function +alter default privileges for user supabase_admin in schema public grant all + on sequences to postgres, anon, authenticated, service_role; +alter default privileges for user supabase_admin in schema public grant all + on tables to postgres, anon, authenticated, service_role; +alter default privileges for user supabase_admin in schema public grant all + on functions to postgres, anon, authenticated, service_role; + +-- Set short statement/query timeouts for API roles +alter role anon set statement_timeout = '3s'; +alter role authenticated set statement_timeout = '8s'; diff --git a/docker/supabase/volumes/db/init/01-auth-schema.sql b/docker/supabase/volumes/db/init/01-auth-schema.sql new file mode 100644 index 0000000000..3544f9afb0 --- /dev/null +++ b/docker/supabase/volumes/db/init/01-auth-schema.sql @@ -0,0 +1,145 @@ + +CREATE SCHEMA IF NOT EXISTS auth AUTHORIZATION supabase_admin; + +-- auth.users definition + +CREATE TABLE auth.users ( + instance_id uuid NULL, + id uuid NOT NULL UNIQUE, + aud varchar(255) NULL, + "role" varchar(255) NULL, + email varchar(255) NULL UNIQUE, + encrypted_password varchar(255) NULL, + confirmed_at timestamptz NULL, + invited_at timestamptz NULL, + confirmation_token varchar(255) NULL, + confirmation_sent_at timestamptz NULL, + recovery_token varchar(255) NULL, + recovery_sent_at timestamptz NULL, + email_change_token varchar(255) NULL, + email_change varchar(255) NULL, + email_change_sent_at timestamptz NULL, + last_sign_in_at timestamptz NULL, + raw_app_meta_data jsonb NULL, + raw_user_meta_data jsonb NULL, + is_super_admin bool NULL, + created_at timestamptz NULL, + updated_at timestamptz NULL, + CONSTRAINT users_pkey PRIMARY KEY (id) +); +CREATE INDEX users_instance_id_email_idx ON auth.users USING btree (instance_id, email); +CREATE INDEX users_instance_id_idx ON auth.users USING btree (instance_id); +comment on table auth.users is 'Auth: Stores user login data within a secure schema.'; + +-- auth.refresh_tokens definition + +CREATE TABLE auth.refresh_tokens ( + instance_id uuid NULL, + id bigserial NOT NULL, + "token" varchar(255) NULL, + user_id varchar(255) NULL, + revoked bool NULL, + created_at timestamptz NULL, + updated_at timestamptz NULL, + CONSTRAINT refresh_tokens_pkey PRIMARY KEY (id) +); +CREATE INDEX refresh_tokens_instance_id_idx ON auth.refresh_tokens USING btree (instance_id); +CREATE INDEX refresh_tokens_instance_id_user_id_idx ON auth.refresh_tokens USING btree (instance_id, user_id); +CREATE INDEX refresh_tokens_token_idx ON auth.refresh_tokens USING btree (token); +comment on table auth.refresh_tokens is 'Auth: Store of tokens used to refresh JWT tokens once they expire.'; + +-- auth.instances definition + +CREATE TABLE auth.instances ( + id uuid NOT NULL, + uuid uuid NULL, + raw_base_config text NULL, + created_at timestamptz NULL, + updated_at timestamptz NULL, + CONSTRAINT instances_pkey PRIMARY KEY (id) +); +comment on table auth.instances is 'Auth: Manages users across multiple sites.'; + +-- auth.audit_log_entries definition + +CREATE TABLE auth.audit_log_entries ( + instance_id uuid NULL, + id uuid NOT NULL, + payload json NULL, + created_at timestamptz NULL, + CONSTRAINT audit_log_entries_pkey PRIMARY KEY (id) +); +CREATE INDEX audit_logs_instance_id_idx ON auth.audit_log_entries USING btree (instance_id); +comment on table auth.audit_log_entries is 'Auth: Audit trail for user actions.'; + +-- auth.schema_migrations definition + +CREATE TABLE auth.schema_migrations ( + "version" varchar(255) NOT NULL, + CONSTRAINT schema_migrations_pkey PRIMARY KEY ("version") +); +comment on table auth.schema_migrations is 'Auth: Manages updates to the auth system.'; + +INSERT INTO auth.schema_migrations (version) +VALUES ('20171026211738'), + ('20171026211808'), + ('20171026211834'), + ('20180103212743'), + ('20180108183307'), + ('20180119214651'), + ('20180125194653'); + +create or replace function auth.uid() +returns uuid +language sql stable +as $$ + select + coalesce( + current_setting('request.jwt.claim.sub', true), + (current_setting('request.jwt.claims', true)::jsonb ->> 'sub') + )::uuid +$$; + +create or replace function auth.role() +returns text +language sql stable +as $$ + select + coalesce( + current_setting('request.jwt.claim.role', true), + (current_setting('request.jwt.claims', true)::jsonb ->> 'role') + )::text +$$; + +create or replace function auth.email() +returns text +language sql stable +as $$ + select + coalesce( + current_setting('request.jwt.claim.email', true), + (current_setting('request.jwt.claims', true)::jsonb ->> 'email') + )::text +$$; + +-- usage on auth functions to API roles +GRANT USAGE ON SCHEMA auth TO anon, authenticated, service_role; + +-- Supabase super admin +CREATE USER supabase_auth_admin NOINHERIT CREATEROLE LOGIN NOREPLICATION; +GRANT ALL PRIVILEGES ON SCHEMA auth TO supabase_auth_admin; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA auth TO supabase_auth_admin; +GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA auth TO supabase_auth_admin; +ALTER USER supabase_auth_admin SET search_path = "auth"; +ALTER table "auth".users OWNER TO supabase_auth_admin; +ALTER table "auth".refresh_tokens OWNER TO supabase_auth_admin; +ALTER table "auth".audit_log_entries OWNER TO supabase_auth_admin; +ALTER table "auth".instances OWNER TO supabase_auth_admin; +ALTER table "auth".schema_migrations OWNER TO supabase_auth_admin; + +ALTER FUNCTION "auth"."uid" OWNER TO supabase_auth_admin; +ALTER FUNCTION "auth"."role" OWNER TO supabase_auth_admin; +ALTER FUNCTION "auth"."email" OWNER TO supabase_auth_admin; +GRANT EXECUTE ON FUNCTION "auth"."uid"() TO PUBLIC; +GRANT EXECUTE ON FUNCTION "auth"."role"() TO PUBLIC; +GRANT EXECUTE ON FUNCTION "auth"."email"() TO PUBLIC; diff --git a/docker/supabase/volumes/db/init/02-storage-schema.sql b/docker/supabase/volumes/db/init/02-storage-schema.sql new file mode 100644 index 0000000000..ba891b018b --- /dev/null +++ b/docker/supabase/volumes/db/init/02-storage-schema.sql @@ -0,0 +1,116 @@ +CREATE SCHEMA IF NOT EXISTS storage AUTHORIZATION supabase_admin; + +grant usage on schema storage to postgres, anon, authenticated, service_role; +alter default privileges in schema storage grant all on tables to postgres, anon, authenticated, service_role; +alter default privileges in schema storage grant all on functions to postgres, anon, authenticated, service_role; +alter default privileges in schema storage grant all on sequences to postgres, anon, authenticated, service_role; + +CREATE TABLE "storage"."buckets" ( + "id" text not NULL, + "name" text NOT NULL, + "owner" uuid, + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now(), + CONSTRAINT "buckets_owner_fkey" FOREIGN KEY ("owner") REFERENCES "auth"."users"("id"), + PRIMARY KEY ("id") +); +CREATE UNIQUE INDEX "bname" ON "storage"."buckets" USING BTREE ("name"); + +CREATE TABLE "storage"."objects" ( + "id" uuid NOT NULL DEFAULT extensions.uuid_generate_v4(), + "bucket_id" text, + "name" text, + "owner" uuid, + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now(), + "last_accessed_at" timestamptz DEFAULT now(), + "metadata" jsonb, + CONSTRAINT "objects_bucketId_fkey" FOREIGN KEY ("bucket_id") REFERENCES "storage"."buckets"("id"), + CONSTRAINT "objects_owner_fkey" FOREIGN KEY ("owner") REFERENCES "auth"."users"("id"), + PRIMARY KEY ("id") +); +CREATE UNIQUE INDEX "bucketid_objname" ON "storage"."objects" USING BTREE ("bucket_id","name"); +CREATE INDEX name_prefix_search ON storage.objects(name text_pattern_ops); + +ALTER TABLE storage.objects ENABLE ROW LEVEL SECURITY; + +CREATE FUNCTION storage.foldername(name text) + RETURNS text[] + LANGUAGE plpgsql +AS $function$ +DECLARE +_parts text[]; +BEGIN + select string_to_array(name, '/') into _parts; + return _parts[1:array_length(_parts,1)-1]; +END +$function$; + +CREATE FUNCTION storage.filename(name text) + RETURNS text + LANGUAGE plpgsql +AS $function$ +DECLARE +_parts text[]; +BEGIN + select string_to_array(name, '/') into _parts; + return _parts[array_length(_parts,1)]; +END +$function$; + +CREATE FUNCTION storage.extension(name text) + RETURNS text + LANGUAGE plpgsql +AS $function$ +DECLARE +_parts text[]; +_filename text; +BEGIN + select string_to_array(name, '/') into _parts; + select _parts[array_length(_parts,1)] into _filename; + -- @todo return the last part instead of 2 + return split_part(_filename, '.', 2); +END +$function$; + +CREATE FUNCTION storage.search(prefix text, bucketname text, limits int DEFAULT 100, levels int DEFAULT 1, offsets int DEFAULT 0) + RETURNS TABLE ( + name text, + id uuid, + updated_at TIMESTAMPTZ, + created_at TIMESTAMPTZ, + last_accessed_at TIMESTAMPTZ, + metadata jsonb + ) + LANGUAGE plpgsql +AS $function$ +DECLARE +_bucketId text; +BEGIN + -- will be replaced by migrations when server starts + -- saving space for cloud-init +END +$function$; + +-- create migrations table +-- https://github.com/ThomWright/postgres-migrations/blob/master/src/migrations/0_create-migrations-table.sql +-- we add this table here and not let it be auto-created so that the permissions are properly applied to it +CREATE TABLE IF NOT EXISTS storage.migrations ( + id integer PRIMARY KEY, + name varchar(100) UNIQUE NOT NULL, + hash varchar(40) NOT NULL, -- sha1 hex encoded hash of the file name and contents, to ensure it hasn't been altered since applying the migration + executed_at timestamp DEFAULT current_timestamp +); + +CREATE USER supabase_storage_admin NOINHERIT CREATEROLE LOGIN NOREPLICATION; +GRANT ALL PRIVILEGES ON SCHEMA storage TO supabase_storage_admin; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA storage TO supabase_storage_admin; +GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA storage TO supabase_storage_admin; +ALTER USER supabase_storage_admin SET search_path = "storage"; +ALTER table "storage".objects owner to supabase_storage_admin; +ALTER table "storage".buckets owner to supabase_storage_admin; +ALTER table "storage".migrations OWNER TO supabase_storage_admin; +ALTER function "storage".foldername(text) owner to supabase_storage_admin; +ALTER function "storage".filename(text) owner to supabase_storage_admin; +ALTER function "storage".extension(text) owner to supabase_storage_admin; +ALTER function "storage".search(text,text,int,int,int) owner to supabase_storage_admin; diff --git a/docker/supabase/volumes/db/init/03-post-setup.sql b/docker/supabase/volumes/db/init/03-post-setup.sql new file mode 100644 index 0000000000..64dc7399fd --- /dev/null +++ b/docker/supabase/volumes/db/init/03-post-setup.sql @@ -0,0 +1,68 @@ +ALTER ROLE postgres SET search_path TO "\$user",public,extensions; +CREATE OR REPLACE FUNCTION extensions.notify_api_restart() +RETURNS event_trigger +LANGUAGE plpgsql +AS $$ +BEGIN + NOTIFY pgrst, 'reload schema'; +END; +$$; +CREATE EVENT TRIGGER api_restart ON ddl_command_end +EXECUTE PROCEDURE extensions.notify_api_restart(); +COMMENT ON FUNCTION extensions.notify_api_restart IS 'Sends a notification to the API to restart. If your database schema has changed, this is required so that Supabase can rebuild the relationships.'; + +-- Trigger for pg_cron +CREATE OR REPLACE FUNCTION extensions.grant_pg_cron_access() +RETURNS event_trigger +LANGUAGE plpgsql +AS $$ +DECLARE + schema_is_cron bool; +BEGIN + schema_is_cron = ( + SELECT n.nspname = 'cron' + FROM pg_event_trigger_ddl_commands() AS ev + LEFT JOIN pg_catalog.pg_namespace AS n + ON ev.objid = n.oid + ); + + IF schema_is_cron + THEN + grant usage on schema cron to postgres with grant option; + + alter default privileges in schema cron grant all on tables to postgres with grant option; + alter default privileges in schema cron grant all on functions to postgres with grant option; + alter default privileges in schema cron grant all on sequences to postgres with grant option; + + alter default privileges for user supabase_admin in schema cron grant all + on sequences to postgres with grant option; + alter default privileges for user supabase_admin in schema cron grant all + on tables to postgres with grant option; + alter default privileges for user supabase_admin in schema cron grant all + on functions to postgres with grant option; + + grant all privileges on all tables in schema cron to postgres with grant option; + + END IF; + +END; +$$; +CREATE EVENT TRIGGER issue_pg_cron_access ON ddl_command_end WHEN TAG in ('CREATE SCHEMA') +EXECUTE PROCEDURE extensions.grant_pg_cron_access(); +COMMENT ON FUNCTION extensions.grant_pg_cron_access IS 'Grants access to pg_cron'; + +-- Supabase dashboard user +CREATE ROLE dashboard_user NOSUPERUSER CREATEDB CREATEROLE REPLICATION; +GRANT ALL ON DATABASE postgres TO dashboard_user; +GRANT ALL ON SCHEMA auth TO dashboard_user; +GRANT ALL ON SCHEMA extensions TO dashboard_user; +GRANT ALL ON SCHEMA storage TO dashboard_user; +GRANT ALL ON ALL TABLES IN SCHEMA auth TO dashboard_user; +GRANT ALL ON ALL TABLES IN SCHEMA extensions TO dashboard_user; +-- GRANT ALL ON ALL TABLES IN SCHEMA storage TO dashboard_user; +GRANT ALL ON ALL SEQUENCES IN SCHEMA auth TO dashboard_user; +GRANT ALL ON ALL SEQUENCES IN SCHEMA storage TO dashboard_user; +GRANT ALL ON ALL SEQUENCES IN SCHEMA extensions TO dashboard_user; +GRANT ALL ON ALL ROUTINES IN SCHEMA auth TO dashboard_user; +GRANT ALL ON ALL ROUTINES IN SCHEMA storage TO dashboard_user; +GRANT ALL ON ALL ROUTINES IN SCHEMA extensions TO dashboard_user; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1c2da9e1cb..e0997fda73 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -265,6 +265,7 @@ importers: src/api/sso: specifiers: '@senecacdot/satellite': ^1.x + '@supabase/supabase-js': 1.29.4 celebrate: 15.0.0 connect-redis: 6.0.0 env-cmd: 10.1.0 @@ -276,6 +277,7 @@ importers: passport-saml: 3.2.0 dependencies: '@senecacdot/satellite': 1.17.0 + '@supabase/supabase-js': 1.29.4 celebrate: 15.0.0 connect-redis: 6.0.0 express-session: 1.17.2 @@ -3844,6 +3846,48 @@ packages: '@sinonjs/commons': 1.8.3 dev: true + /@supabase/gotrue-js/1.22.0: + resolution: {integrity: sha512-6r8YJvl8+mEBO6a5xoFr9K0kVw7C0FmcudFFAop+2FNSo6BXR/eEsPx/yAgVgv+1GowG4n2kOm0TYB7EvcH2QQ==} + dependencies: + cross-fetch: 3.1.5 + transitivePeerDependencies: + - encoding + dev: false + + /@supabase/postgrest-js/0.36.0: + resolution: {integrity: sha512-KOnhVy8tEr/qNnvOLpFqwOkt7ilRDFMXY+JJfmLjS3+eZuna1G57w4zb3L0SdY6BL7AKnfzP5BG3yHTAuJPSww==} + dependencies: + cross-fetch: 3.1.5 + transitivePeerDependencies: + - encoding + dev: false + + /@supabase/realtime-js/1.3.5: + resolution: {integrity: sha512-If+C0A6eT1BflFOOZNlnGw/91gNuj7f/M19/WutsPM0pjhx++8Dj0YH8z+tbgxX0nf/2UGVUMMgTBckzpk9R3g==} + dependencies: + '@types/websocket': 1.0.5 + websocket: 1.0.34 + dev: false + + /@supabase/storage-js/1.5.1: + resolution: {integrity: sha512-W82st1RvkChVJ/FTCcPXFXfS3V0Z4rZuMnoDnB9/NI5i9r9zspZS40tHpUQ+vbN6R6k0pfr/Waa1jcEd3YAtrQ==} + dependencies: + cross-fetch: 3.1.5 + transitivePeerDependencies: + - encoding + dev: false + + /@supabase/supabase-js/1.29.4: + resolution: {integrity: sha512-RR3/Ji6o6GPO1/pXbG9sHoNynWIc3MnBnwgbC0UFc10kQfYcPaRgQCfI66V7O4Gesa0qGGYpcYq6/IUg8Qmh0A==} + dependencies: + '@supabase/gotrue-js': 1.22.0 + '@supabase/postgrest-js': 0.36.0 + '@supabase/realtime-js': 1.3.5 + '@supabase/storage-js': 1.5.1 + transitivePeerDependencies: + - encoding + dev: false + /@surma/rollup-plugin-off-main-thread/2.2.3: resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} dependencies: @@ -4176,6 +4220,12 @@ packages: '@types/node': 17.0.12 dev: true + /@types/websocket/1.0.5: + resolution: {integrity: sha512-NbsqiNX9CnEfC1Z0Vf4mE1SgAJ07JnRYcNex7AJ9zAVzmiGHmjKFEk7O4TJIsgv2B1sLEb6owKFZrACwdYngsQ==} + dependencies: + '@types/node': 17.0.12 + dev: false + /@types/yargs-parser/20.2.1: resolution: {integrity: sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==} @@ -5291,6 +5341,14 @@ packages: engines: {node: '>=0.2.0'} dev: true + /bufferutil/4.0.6: + resolution: {integrity: sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw==} + engines: {node: '>=6.14.2'} + requiresBuild: true + dependencies: + node-gyp-build: 4.3.0 + dev: false + /builtin-modules/3.2.0: resolution: {integrity: sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==} engines: {node: '>=6'} @@ -6031,7 +6089,6 @@ packages: node-fetch: 2.6.7 transitivePeerDependencies: - encoding - dev: true /cross-spawn/6.0.5: resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} @@ -6142,7 +6199,6 @@ packages: dependencies: es5-ext: 0.10.53 type: 1.2.0 - dev: true /damerau-levenshtein/1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} @@ -6655,7 +6711,6 @@ packages: /encoding/0.1.13: resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} - requiresBuild: true dependencies: iconv-lite: 0.6.3 dev: true @@ -6761,7 +6816,6 @@ packages: es6-iterator: 2.0.3 es6-symbol: 3.1.3 next-tick: 1.0.0 - dev: true /es6-error/4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} @@ -6773,14 +6827,12 @@ packages: d: 1.0.1 es5-ext: 0.10.53 es6-symbol: 3.1.3 - dev: true /es6-symbol/3.1.3: resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==} dependencies: d: 1.0.1 ext: 1.6.0 - dev: true /es6-weak-map/2.0.3: resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==} @@ -7613,7 +7665,6 @@ packages: resolution: {integrity: sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==} dependencies: type: 2.5.0 - dev: true /extend/3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -11393,7 +11444,6 @@ packages: /next-tick/1.0.0: resolution: {integrity: sha1-yobR/ogoFpsBICCOPchCS524NCw=} - dev: true /next-tick/1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} @@ -11488,6 +11538,11 @@ packages: resolution: {integrity: sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==} engines: {node: '>= 6.13.0'} + /node-gyp-build/4.3.0: + resolution: {integrity: sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==} + hasBin: true + dev: false + /node-gyp/8.4.1: resolution: {integrity: sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==} engines: {node: '>= 10.12.0'} @@ -13309,7 +13364,7 @@ packages: dependencies: debug: 4.3.3 module-details-from-path: 1.0.3 - resolve: 1.22.0 + resolve: 1.21.0 transitivePeerDependencies: - supports-color dev: false @@ -15078,11 +15133,9 @@ packages: /type/1.2.0: resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==} - dev: true /type/2.5.0: resolution: {integrity: sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==} - dev: true /typedarray-to-buffer/3.1.5: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} @@ -15352,6 +15405,14 @@ packages: react: 17.0.2 dev: false + /utf-8-validate/5.0.8: + resolution: {integrity: sha512-k4dW/Qja1BYDl2qD4tOMB9PFVha/UJtxTc1cXYOe3WwA/2m0Yn4qB7wLMpJyLJ/7DR0XnTut3HsCSzDT4ZvKgA==} + engines: {node: '>=6.14.2'} + requiresBuild: true + dependencies: + node-gyp-build: 4.3.0 + dev: false + /util-deprecate/1.0.2: resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=} @@ -15582,6 +15643,18 @@ packages: engines: {node: '>=0.8.0'} dev: false + /websocket/1.0.34: + resolution: {integrity: sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==} + engines: {node: '>=4.0.0'} + dependencies: + bufferutil: 4.0.6 + debug: 2.6.9 + es5-ext: 0.10.53 + typedarray-to-buffer: 3.1.5 + utf-8-validate: 5.0.8 + yaeti: 0.0.6 + dev: false + /whatwg-encoding/1.0.5: resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==} dependencies: @@ -16022,6 +16095,11 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + /yaeti/0.0.6: + resolution: {integrity: sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=} + engines: {node: '>=0.10.32'} + dev: false + /yallist/2.1.2: resolution: {integrity: sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=} dev: false diff --git a/src/api/sso/env.local b/src/api/sso/env.local index 176029442c..ea2492f96e 100644 --- a/src/api/sso/env.local +++ b/src/api/sso/env.local @@ -47,3 +47,8 @@ JWT_AUDIENCE=http://localhost # How long should a JWT work before it expires JWT_EXPIRES_IN=1h + +# Supabase connection https://supabase.com/docs/guides/database/connecting-to-postgres +SUPABASE_URL=http://kong:8000 +# Admin key, able to bypass security rules +SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q diff --git a/src/api/sso/package.json b/src/api/sso/package.json index dd9966a700..7035b9863f 100644 --- a/src/api/sso/package.json +++ b/src/api/sso/package.json @@ -15,6 +15,7 @@ "homepage": "https://github.com/Seneca-CDOT/telescope#readme", "dependencies": { "@senecacdot/satellite": "^1.x", + "@supabase/supabase-js": "1.29.4", "celebrate": "15.0.0", "connect-redis": "6.0.0", "express-session": "1.17.2", diff --git a/src/api/sso/src/supabase.js b/src/api/sso/src/supabase.js new file mode 100644 index 0000000000..74f8ac1bd3 --- /dev/null +++ b/src/api/sso/src/supabase.js @@ -0,0 +1,5 @@ +const { createClient } = require('@supabase/supabase-js'); + +const { SERVICE_ROLE_KEY, SUPABASE_URL } = process.env; + +module.exports = createClient(SUPABASE_URL, SERVICE_ROLE_KEY);