Merge branch 'master' into develop
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
DOCKER_MACHINE=${DOCKER_MACHINE}
|
||||||
|
DOCKER_SOCKET_LOCATION=${DOCKER_SOCKET_LOCATION}
|
||||||
|
DOCKER_SYSTEM=${DOCKER_SYSTEM}
|
||||||
|
DOMAIN=${DOMAIN}
|
||||||
|
STACK=${STACK}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
ARG SUPABASE_EDGE_RUNTIME_VERSION=v1.65.3
|
||||||
|
FROM supabase/edge-runtime:${SUPABASE_EDGE_RUNTIME_VERSION}
|
||||||
|
ARG DOCKER_BUILD_DIR=.
|
||||||
|
|
||||||
|
RUN mkdir -p /home/deno/functions
|
||||||
|
COPY ${DOCKER_BUILD_DIR}/hello/ /home/deno/functions/hello/
|
||||||
|
COPY ${DOCKER_BUILD_DIR}/main/ /home/deno/functions/main/
|
||||||
|
|
||||||
|
VOLUME /home/deno/functions
|
||||||
|
VOLUME /root/.cache
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// Follow this setup guide to integrate the Deno language server with your editor:
|
||||||
|
// https://deno.land/manual/getting_started/setup_your_environment
|
||||||
|
// This enables autocomplete, go to definition, etc.
|
||||||
|
|
||||||
|
import { serve } from "https://deno.land/std@0.177.1/http/server.ts"
|
||||||
|
|
||||||
|
serve(async () => {
|
||||||
|
return new Response(
|
||||||
|
`"Hello from Edge Functions!"`,
|
||||||
|
{ headers: { "Content-Type": "application/json" } },
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
// To invoke:
|
||||||
|
// curl 'http://localhost:<KONG_HTTP_PORT>/functions/v1/hello' \
|
||||||
|
// --header 'Authorization: Bearer <anon/service_role API key>'
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import { serve } from 'https://deno.land/std@0.131.0/http/server.ts'
|
||||||
|
import * as jose from 'https://deno.land/x/jose@v4.14.4/index.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<boolean> {
|
||||||
|
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' },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
ARG SUPABASE_KONG_VERSION=3.9.0
|
||||||
|
FROM kong:${SUPABASE_KONG_VERSION}
|
||||||
|
ARG DOCKER_BUILD_DIR=.
|
||||||
|
ARG DOCKER_NETWORK=supabase_default
|
||||||
|
ARG REALTIME_TENANT=realtime-dev
|
||||||
|
|
||||||
|
RUN mkdir -p /home/kong
|
||||||
|
COPY ${DOCKER_BUILD_DIR}/kong.yml /home/kong/temp.yml
|
||||||
|
# realtime tenant is taken from hostname in url
|
||||||
|
RUN sed -i 's/realtime-dev.supabase-realtime/'"${REALTIME_TENANT}"'.'"${DOCKER_NETWORK}"'/' /home/kong/temp.yml
|
||||||
|
|
||||||
|
VOLUME /home/kong
|
||||||
@@ -0,0 +1,241 @@
|
|||||||
|
_format_version: '2.1'
|
||||||
|
_transform: true
|
||||||
|
|
||||||
|
###
|
||||||
|
### Consumers / Users
|
||||||
|
###
|
||||||
|
consumers:
|
||||||
|
- username: DASHBOARD
|
||||||
|
- username: anon
|
||||||
|
keyauth_credentials:
|
||||||
|
- key: $SUPABASE_ANON_KEY
|
||||||
|
- username: service_role
|
||||||
|
keyauth_credentials:
|
||||||
|
- key: $SUPABASE_SERVICE_KEY
|
||||||
|
|
||||||
|
###
|
||||||
|
### Access Control List
|
||||||
|
###
|
||||||
|
acls:
|
||||||
|
- consumer: anon
|
||||||
|
group: anon
|
||||||
|
- consumer: service_role
|
||||||
|
group: admin
|
||||||
|
|
||||||
|
###
|
||||||
|
### Dashboard credentials
|
||||||
|
###
|
||||||
|
basicauth_credentials:
|
||||||
|
- consumer: DASHBOARD
|
||||||
|
username: $DASHBOARD_USERNAME
|
||||||
|
password: $DASHBOARD_PASSWORD
|
||||||
|
|
||||||
|
###
|
||||||
|
### 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 GraphQL routes
|
||||||
|
- name: graphql-v1
|
||||||
|
_comment: 'PostgREST: /graphql/v1/* -> http://rest:3000/rpc/graphql'
|
||||||
|
url: http://rest:3000/rpc/graphql
|
||||||
|
routes:
|
||||||
|
- name: graphql-v1-all
|
||||||
|
strip_path: true
|
||||||
|
paths:
|
||||||
|
- /graphql/v1
|
||||||
|
plugins:
|
||||||
|
- name: cors
|
||||||
|
- name: key-auth
|
||||||
|
config:
|
||||||
|
hide_credentials: true
|
||||||
|
- name: request-transformer
|
||||||
|
config:
|
||||||
|
add:
|
||||||
|
headers:
|
||||||
|
- Content-Profile:graphql_public
|
||||||
|
- name: acl
|
||||||
|
config:
|
||||||
|
hide_groups_header: true
|
||||||
|
allow:
|
||||||
|
- admin
|
||||||
|
- anon
|
||||||
|
|
||||||
|
## Secure Realtime routes
|
||||||
|
- name: realtime-v1-ws
|
||||||
|
_comment: 'Realtime: /realtime/v1/* -> ws://realtime:4000/socket/*'
|
||||||
|
url: http://realtime-dev.supabase-realtime:4000/socket
|
||||||
|
protocol: ws
|
||||||
|
routes:
|
||||||
|
- name: realtime-v1-ws
|
||||||
|
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
|
||||||
|
- name: realtime-v1-rest
|
||||||
|
_comment: 'Realtime: /realtime/v1/* -> ws://realtime:4000/socket/*'
|
||||||
|
url: http://realtime-dev.supabase-realtime:4000/api
|
||||||
|
protocol: http
|
||||||
|
routes:
|
||||||
|
- name: realtime-v1-rest
|
||||||
|
strip_path: true
|
||||||
|
paths:
|
||||||
|
- /realtime/v1/api
|
||||||
|
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
|
||||||
|
|
||||||
|
## Edge Functions routes
|
||||||
|
- name: functions-v1
|
||||||
|
_comment: 'Edge Functions: /functions/v1/* -> http://functions:9000/*'
|
||||||
|
url: http://functions:9000/
|
||||||
|
routes:
|
||||||
|
- name: functions-v1-all
|
||||||
|
strip_path: true
|
||||||
|
paths:
|
||||||
|
- /functions/v1/
|
||||||
|
plugins:
|
||||||
|
- name: cors
|
||||||
|
|
||||||
|
## Analytics routes
|
||||||
|
- name: analytics-v1
|
||||||
|
_comment: 'Analytics: /analytics/v1/* -> http://logflare:4000/*'
|
||||||
|
url: http://analytics:4000/
|
||||||
|
routes:
|
||||||
|
- name: analytics-v1-all
|
||||||
|
strip_path: true
|
||||||
|
paths:
|
||||||
|
- /analytics/v1/
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
## Protected Dashboard - catch all remaining routes
|
||||||
|
- name: dashboard
|
||||||
|
_comment: 'Studio: /* -> http://studio:3000/*'
|
||||||
|
url: http://studio:3000/
|
||||||
|
routes:
|
||||||
|
- name: dashboard-all
|
||||||
|
strip_path: true
|
||||||
|
paths:
|
||||||
|
- /
|
||||||
|
plugins:
|
||||||
|
- name: cors
|
||||||
|
- name: basic-auth
|
||||||
|
config:
|
||||||
|
hide_credentials: true
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
ARG SUPABASE_POSTGRES_VERSION=15.6.1.146
|
||||||
|
FROM supabase/postgres:${SUPABASE_POSTGRES_VERSION}
|
||||||
|
ARG DOCKER_BUILD_DIR=.
|
||||||
|
|
||||||
|
COPY ${DOCKER_BUILD_DIR}/realtime.sql /docker-entrypoint-initdb.d/migrations/99-realtime.sql
|
||||||
|
COPY ${DOCKER_BUILD_DIR}/webhooks.sql /docker-entrypoint-initdb.d/init-scripts/98-webhooks.sql
|
||||||
|
COPY ${DOCKER_BUILD_DIR}/roles.sql /docker-entrypoint-initdb.d/init-scripts/99-roles.sql
|
||||||
|
COPY ${DOCKER_BUILD_DIR}/jwt.sql /docker-entrypoint-initdb.d/init-scripts/99-jwt.sql
|
||||||
|
COPY ${DOCKER_BUILD_DIR}/_supabase.sql /docker-entrypoint-initdb.d/migrations/97-_supabase.sql
|
||||||
|
COPY ${DOCKER_BUILD_DIR}/logs.sql /docker-entrypoint-initdb.d/migrations/99-logs.sql
|
||||||
|
COPY ${DOCKER_BUILD_DIR}/pooler.sql /docker-entrypoint-initdb.d/migrations/99-pooler.sql
|
||||||
|
|
||||||
|
VOLUME /etc/postgresql-custom
|
||||||
|
VOLUME /var/lib/postgres
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
\set pguser `echo "$POSTGRES_USER"`
|
||||||
|
|
||||||
|
CREATE DATABASE _supabase WITH OWNER :pguser;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
\set jwt_secret `echo "$JWT_SECRET"`
|
||||||
|
\set jwt_exp `echo "$JWT_EXP"`
|
||||||
|
|
||||||
|
ALTER DATABASE postgres SET "app.settings.jwt_secret" TO :'jwt_secret';
|
||||||
|
ALTER DATABASE postgres SET "app.settings.jwt_exp" TO :'jwt_exp';
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
\set pguser `echo "$POSTGRES_USER"`
|
||||||
|
|
||||||
|
\c _supabase
|
||||||
|
create schema if not exists _analytics;
|
||||||
|
alter schema _analytics owner to :pguser;
|
||||||
|
\c postgres
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
\set pguser `echo "$POSTGRES_USER"`
|
||||||
|
|
||||||
|
\c _supabase
|
||||||
|
create schema if not exists _supavisor;
|
||||||
|
alter schema _supavisor owner to :pguser;
|
||||||
|
\c postgres
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
\set pguser `echo "$POSTGRES_USER"`
|
||||||
|
|
||||||
|
create schema if not exists _realtime;
|
||||||
|
alter schema _realtime owner to :pguser;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
-- NOTE: change to your own passwords for production environments
|
||||||
|
\set pgpass `echo "$POSTGRES_PASSWORD"`
|
||||||
|
|
||||||
|
ALTER USER authenticator WITH PASSWORD :'pgpass';
|
||||||
|
ALTER USER pgbouncer WITH PASSWORD :'pgpass';
|
||||||
|
ALTER USER supabase_auth_admin WITH PASSWORD :'pgpass';
|
||||||
|
ALTER USER supabase_functions_admin WITH PASSWORD :'pgpass';
|
||||||
|
ALTER USER supabase_storage_admin WITH PASSWORD :'pgpass';
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
BEGIN;
|
||||||
|
-- Create pg_net extension
|
||||||
|
CREATE EXTENSION IF NOT EXISTS pg_net SCHEMA extensions;
|
||||||
|
-- Create supabase_functions schema
|
||||||
|
CREATE SCHEMA supabase_functions AUTHORIZATION supabase_admin;
|
||||||
|
GRANT USAGE ON SCHEMA supabase_functions TO postgres, anon, authenticated, service_role;
|
||||||
|
ALTER DEFAULT PRIVILEGES IN SCHEMA supabase_functions GRANT ALL ON TABLES TO postgres, anon, authenticated, service_role;
|
||||||
|
ALTER DEFAULT PRIVILEGES IN SCHEMA supabase_functions GRANT ALL ON FUNCTIONS TO postgres, anon, authenticated, service_role;
|
||||||
|
ALTER DEFAULT PRIVILEGES IN SCHEMA supabase_functions GRANT ALL ON SEQUENCES TO postgres, anon, authenticated, service_role;
|
||||||
|
-- supabase_functions.migrations definition
|
||||||
|
CREATE TABLE supabase_functions.migrations (
|
||||||
|
version text PRIMARY KEY,
|
||||||
|
inserted_at timestamptz NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
-- Initial supabase_functions migration
|
||||||
|
INSERT INTO supabase_functions.migrations (version) VALUES ('initial');
|
||||||
|
-- supabase_functions.hooks definition
|
||||||
|
CREATE TABLE supabase_functions.hooks (
|
||||||
|
id bigserial PRIMARY KEY,
|
||||||
|
hook_table_id integer NOT NULL,
|
||||||
|
hook_name text NOT NULL,
|
||||||
|
created_at timestamptz NOT NULL DEFAULT NOW(),
|
||||||
|
request_id bigint
|
||||||
|
);
|
||||||
|
CREATE INDEX supabase_functions_hooks_request_id_idx ON supabase_functions.hooks USING btree (request_id);
|
||||||
|
CREATE INDEX supabase_functions_hooks_h_table_id_h_name_idx ON supabase_functions.hooks USING btree (hook_table_id, hook_name);
|
||||||
|
COMMENT ON TABLE supabase_functions.hooks IS 'Supabase Functions Hooks: Audit trail for triggered hooks.';
|
||||||
|
CREATE FUNCTION supabase_functions.http_request()
|
||||||
|
RETURNS trigger
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $function$
|
||||||
|
DECLARE
|
||||||
|
request_id bigint;
|
||||||
|
payload jsonb;
|
||||||
|
url text := TG_ARGV[0]::text;
|
||||||
|
method text := TG_ARGV[1]::text;
|
||||||
|
headers jsonb DEFAULT '{}'::jsonb;
|
||||||
|
params jsonb DEFAULT '{}'::jsonb;
|
||||||
|
timeout_ms integer DEFAULT 1000;
|
||||||
|
BEGIN
|
||||||
|
IF url IS NULL OR url = 'null' THEN
|
||||||
|
RAISE EXCEPTION 'url argument is missing';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF method IS NULL OR method = 'null' THEN
|
||||||
|
RAISE EXCEPTION 'method argument is missing';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF TG_ARGV[2] IS NULL OR TG_ARGV[2] = 'null' THEN
|
||||||
|
headers = '{"Content-Type": "application/json"}'::jsonb;
|
||||||
|
ELSE
|
||||||
|
headers = TG_ARGV[2]::jsonb;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF TG_ARGV[3] IS NULL OR TG_ARGV[3] = 'null' THEN
|
||||||
|
params = '{}'::jsonb;
|
||||||
|
ELSE
|
||||||
|
params = TG_ARGV[3]::jsonb;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF TG_ARGV[4] IS NULL OR TG_ARGV[4] = 'null' THEN
|
||||||
|
timeout_ms = 1000;
|
||||||
|
ELSE
|
||||||
|
timeout_ms = TG_ARGV[4]::integer;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
CASE
|
||||||
|
WHEN method = 'GET' THEN
|
||||||
|
SELECT http_get INTO request_id FROM net.http_get(
|
||||||
|
url,
|
||||||
|
params,
|
||||||
|
headers,
|
||||||
|
timeout_ms
|
||||||
|
);
|
||||||
|
WHEN method = 'POST' THEN
|
||||||
|
payload = jsonb_build_object(
|
||||||
|
'old_record', OLD,
|
||||||
|
'record', NEW,
|
||||||
|
'type', TG_OP,
|
||||||
|
'table', TG_TABLE_NAME,
|
||||||
|
'schema', TG_TABLE_SCHEMA
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT http_post INTO request_id FROM net.http_post(
|
||||||
|
url,
|
||||||
|
payload,
|
||||||
|
params,
|
||||||
|
headers,
|
||||||
|
timeout_ms
|
||||||
|
);
|
||||||
|
ELSE
|
||||||
|
RAISE EXCEPTION 'method argument % is invalid', method;
|
||||||
|
END CASE;
|
||||||
|
|
||||||
|
INSERT INTO supabase_functions.hooks
|
||||||
|
(hook_table_id, hook_name, request_id)
|
||||||
|
VALUES
|
||||||
|
(TG_RELID, TG_NAME, request_id);
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END
|
||||||
|
$function$;
|
||||||
|
-- Supabase super admin
|
||||||
|
DO
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_roles
|
||||||
|
WHERE rolname = 'supabase_functions_admin'
|
||||||
|
)
|
||||||
|
THEN
|
||||||
|
CREATE USER supabase_functions_admin NOINHERIT CREATEROLE LOGIN NOREPLICATION;
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
GRANT ALL PRIVILEGES ON SCHEMA supabase_functions TO supabase_functions_admin;
|
||||||
|
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA supabase_functions TO supabase_functions_admin;
|
||||||
|
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA supabase_functions TO supabase_functions_admin;
|
||||||
|
ALTER USER supabase_functions_admin SET search_path = "supabase_functions";
|
||||||
|
ALTER table "supabase_functions".migrations OWNER TO supabase_functions_admin;
|
||||||
|
ALTER table "supabase_functions".hooks OWNER TO supabase_functions_admin;
|
||||||
|
ALTER function "supabase_functions".http_request() OWNER TO supabase_functions_admin;
|
||||||
|
GRANT supabase_functions_admin TO postgres;
|
||||||
|
-- Remove unused supabase_pg_net_admin role
|
||||||
|
DO
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_roles
|
||||||
|
WHERE rolname = 'supabase_pg_net_admin'
|
||||||
|
)
|
||||||
|
THEN
|
||||||
|
REASSIGN OWNED BY supabase_pg_net_admin TO supabase_admin;
|
||||||
|
DROP OWNED BY supabase_pg_net_admin;
|
||||||
|
DROP ROLE supabase_pg_net_admin;
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
-- pg_net grants when extension is already enabled
|
||||||
|
DO
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_extension
|
||||||
|
WHERE extname = 'pg_net'
|
||||||
|
)
|
||||||
|
THEN
|
||||||
|
GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role;
|
||||||
|
ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER;
|
||||||
|
ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER;
|
||||||
|
ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net;
|
||||||
|
ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net;
|
||||||
|
REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC;
|
||||||
|
REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC;
|
||||||
|
GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role;
|
||||||
|
GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role;
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
-- Event trigger for pg_net
|
||||||
|
CREATE OR REPLACE FUNCTION extensions.grant_pg_net_access()
|
||||||
|
RETURNS event_trigger
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_event_trigger_ddl_commands() AS ev
|
||||||
|
JOIN pg_extension AS ext
|
||||||
|
ON ev.objid = ext.oid
|
||||||
|
WHERE ext.extname = 'pg_net'
|
||||||
|
)
|
||||||
|
THEN
|
||||||
|
GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role;
|
||||||
|
ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER;
|
||||||
|
ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER;
|
||||||
|
ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net;
|
||||||
|
ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net;
|
||||||
|
REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC;
|
||||||
|
REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC;
|
||||||
|
GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role;
|
||||||
|
GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role;
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
|
COMMENT ON FUNCTION extensions.grant_pg_net_access IS 'Grants access to pg_net';
|
||||||
|
DO
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_event_trigger
|
||||||
|
WHERE evtname = 'issue_pg_net_access'
|
||||||
|
) THEN
|
||||||
|
CREATE EVENT TRIGGER issue_pg_net_access ON ddl_command_end WHEN TAG IN ('CREATE EXTENSION')
|
||||||
|
EXECUTE PROCEDURE extensions.grant_pg_net_access();
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
INSERT INTO supabase_functions.migrations (version) VALUES ('20210809183423_update_grants');
|
||||||
|
ALTER function supabase_functions.http_request() SECURITY DEFINER;
|
||||||
|
ALTER function supabase_functions.http_request() SET search_path = supabase_functions;
|
||||||
|
REVOKE ALL ON FUNCTION supabase_functions.http_request() FROM PUBLIC;
|
||||||
|
GRANT EXECUTE ON FUNCTION supabase_functions.http_request() TO postgres, anon, authenticated, service_role;
|
||||||
|
COMMIT;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
ARG SUPABASE_VECTOR_VERSION=0.28.1-alpine
|
||||||
|
FROM timberio/vector:${SUPABASE_VECTOR_VERSION}
|
||||||
|
ARG DOCKER_BUILD_DIR=.
|
||||||
|
|
||||||
|
COPY ${DOCKER_BUILD_DIR}/vector.yml /etc/vector/vector.yml
|
||||||
|
|
||||||
|
VOLUME /etc/vector
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
api:
|
||||||
|
enabled: true
|
||||||
|
address: 0.0.0.0:9001
|
||||||
|
|
||||||
|
sources:
|
||||||
|
docker_host:
|
||||||
|
type: docker_logs
|
||||||
|
exclude_containers:
|
||||||
|
- supabase-vector
|
||||||
|
|
||||||
|
transforms:
|
||||||
|
project_logs:
|
||||||
|
type: remap
|
||||||
|
inputs:
|
||||||
|
- docker_host
|
||||||
|
source: |-
|
||||||
|
.project = "default"
|
||||||
|
.event_message = del(.message)
|
||||||
|
.appname = del(.container_name)
|
||||||
|
del(.container_created_at)
|
||||||
|
del(.container_id)
|
||||||
|
del(.source_type)
|
||||||
|
del(.stream)
|
||||||
|
del(.label)
|
||||||
|
del(.image)
|
||||||
|
del(.host)
|
||||||
|
del(.stream)
|
||||||
|
router:
|
||||||
|
type: route
|
||||||
|
inputs:
|
||||||
|
- project_logs
|
||||||
|
route:
|
||||||
|
kong: '.appname == "supabase-kong"'
|
||||||
|
auth: '.appname == "supabase-auth"'
|
||||||
|
rest: '.appname == "supabase-rest"'
|
||||||
|
realtime: '.appname == "supabase-realtime"'
|
||||||
|
storage: '.appname == "supabase-storage"'
|
||||||
|
functions: '.appname == "supabase-functions"'
|
||||||
|
db: '.appname == "supabase-db"'
|
||||||
|
# Ignores non nginx errors since they are related with kong booting up
|
||||||
|
kong_logs:
|
||||||
|
type: remap
|
||||||
|
inputs:
|
||||||
|
- router.kong
|
||||||
|
source: |-
|
||||||
|
req, err = parse_nginx_log(.event_message, "combined")
|
||||||
|
if err == null {
|
||||||
|
.timestamp = req.timestamp
|
||||||
|
.metadata.request.headers.referer = req.referer
|
||||||
|
.metadata.request.headers.user_agent = req.agent
|
||||||
|
.metadata.request.headers.cf_connecting_ip = req.client
|
||||||
|
.metadata.request.method = req.method
|
||||||
|
.metadata.request.path = req.path
|
||||||
|
.metadata.request.protocol = req.protocol
|
||||||
|
.metadata.response.status_code = req.status
|
||||||
|
}
|
||||||
|
if err != null {
|
||||||
|
abort
|
||||||
|
}
|
||||||
|
# Ignores non nginx errors since they are related with kong booting up
|
||||||
|
kong_err:
|
||||||
|
type: remap
|
||||||
|
inputs:
|
||||||
|
- router.kong
|
||||||
|
source: |-
|
||||||
|
.metadata.request.method = "GET"
|
||||||
|
.metadata.response.status_code = 200
|
||||||
|
parsed, err = parse_nginx_log(.event_message, "error")
|
||||||
|
if err == null {
|
||||||
|
.timestamp = parsed.timestamp
|
||||||
|
.severity = parsed.severity
|
||||||
|
.metadata.request.host = parsed.host
|
||||||
|
.metadata.request.headers.cf_connecting_ip = parsed.client
|
||||||
|
url, err = split(parsed.request, " ")
|
||||||
|
if err == null {
|
||||||
|
.metadata.request.method = url[0]
|
||||||
|
.metadata.request.path = url[1]
|
||||||
|
.metadata.request.protocol = url[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != null {
|
||||||
|
abort
|
||||||
|
}
|
||||||
|
# Gotrue logs are structured json strings which frontend parses directly. But we keep metadata for consistency.
|
||||||
|
auth_logs:
|
||||||
|
type: remap
|
||||||
|
inputs:
|
||||||
|
- router.auth
|
||||||
|
source: |-
|
||||||
|
parsed, err = parse_json(.event_message)
|
||||||
|
if err == null {
|
||||||
|
.metadata.timestamp = parsed.time
|
||||||
|
.metadata = merge!(.metadata, parsed)
|
||||||
|
}
|
||||||
|
# PostgREST logs are structured so we separate timestamp from message using regex
|
||||||
|
rest_logs:
|
||||||
|
type: remap
|
||||||
|
inputs:
|
||||||
|
- router.rest
|
||||||
|
source: |-
|
||||||
|
parsed, err = parse_regex(.event_message, r'^(?P<time>.*): (?P<msg>.*)$')
|
||||||
|
if err == null {
|
||||||
|
.event_message = parsed.msg
|
||||||
|
.timestamp = to_timestamp!(parsed.time)
|
||||||
|
.metadata.host = .project
|
||||||
|
}
|
||||||
|
# Realtime logs are structured so we parse the severity level using regex (ignore time because it has no date)
|
||||||
|
realtime_logs:
|
||||||
|
type: remap
|
||||||
|
inputs:
|
||||||
|
- router.realtime
|
||||||
|
source: |-
|
||||||
|
.metadata.project = del(.project)
|
||||||
|
.metadata.external_id = .metadata.project
|
||||||
|
parsed, err = parse_regex(.event_message, r'^(?P<time>\d+:\d+:\d+\.\d+) \[(?P<level>\w+)\] (?P<msg>.*)$')
|
||||||
|
if err == null {
|
||||||
|
.event_message = parsed.msg
|
||||||
|
.metadata.level = parsed.level
|
||||||
|
}
|
||||||
|
# Storage logs may contain json objects so we parse them for completeness
|
||||||
|
storage_logs:
|
||||||
|
type: remap
|
||||||
|
inputs:
|
||||||
|
- router.storage
|
||||||
|
source: |-
|
||||||
|
.metadata.project = del(.project)
|
||||||
|
.metadata.tenantId = .metadata.project
|
||||||
|
parsed, err = parse_json(.event_message)
|
||||||
|
if err == null {
|
||||||
|
.event_message = parsed.msg
|
||||||
|
.metadata.level = parsed.level
|
||||||
|
.metadata.timestamp = parsed.time
|
||||||
|
.metadata.context[0].host = parsed.hostname
|
||||||
|
.metadata.context[0].pid = parsed.pid
|
||||||
|
}
|
||||||
|
# Postgres logs some messages to stderr which we map to warning severity level
|
||||||
|
db_logs:
|
||||||
|
type: remap
|
||||||
|
inputs:
|
||||||
|
- router.db
|
||||||
|
source: |-
|
||||||
|
.metadata.host = "db-default"
|
||||||
|
.metadata.parsed.timestamp = .timestamp
|
||||||
|
|
||||||
|
parsed, err = parse_regex(.event_message, r'.*(?P<level>INFO|NOTICE|WARNING|ERROR|LOG|FATAL|PANIC?):.*', numeric_groups: true)
|
||||||
|
|
||||||
|
if err != null || parsed == null {
|
||||||
|
.metadata.parsed.error_severity = "info"
|
||||||
|
}
|
||||||
|
if parsed != null {
|
||||||
|
.metadata.parsed.error_severity = parsed.level
|
||||||
|
}
|
||||||
|
if .metadata.parsed.error_severity == "info" {
|
||||||
|
.metadata.parsed.error_severity = "log"
|
||||||
|
}
|
||||||
|
.metadata.parsed.error_severity = upcase!(.metadata.parsed.error_severity)
|
||||||
|
|
||||||
|
sinks:
|
||||||
|
logflare_auth:
|
||||||
|
type: 'http'
|
||||||
|
inputs:
|
||||||
|
- auth_logs
|
||||||
|
encoding:
|
||||||
|
codec: 'json'
|
||||||
|
method: 'post'
|
||||||
|
request:
|
||||||
|
retry_max_duration_secs: 10
|
||||||
|
uri: 'http://analytics:4000/api/logs?source_name=gotrue.logs.prod&api_key=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
|
||||||
|
logflare_realtime:
|
||||||
|
type: 'http'
|
||||||
|
inputs:
|
||||||
|
- realtime_logs
|
||||||
|
encoding:
|
||||||
|
codec: 'json'
|
||||||
|
method: 'post'
|
||||||
|
request:
|
||||||
|
retry_max_duration_secs: 10
|
||||||
|
uri: 'http://analytics:4000/api/logs?source_name=realtime.logs.prod&api_key=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
|
||||||
|
logflare_rest:
|
||||||
|
type: 'http'
|
||||||
|
inputs:
|
||||||
|
- rest_logs
|
||||||
|
encoding:
|
||||||
|
codec: 'json'
|
||||||
|
method: 'post'
|
||||||
|
request:
|
||||||
|
retry_max_duration_secs: 10
|
||||||
|
uri: 'http://analytics:4000/api/logs?source_name=postgREST.logs.prod&api_key=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
|
||||||
|
logflare_db:
|
||||||
|
type: 'http'
|
||||||
|
inputs:
|
||||||
|
- db_logs
|
||||||
|
encoding:
|
||||||
|
codec: 'json'
|
||||||
|
method: 'post'
|
||||||
|
request:
|
||||||
|
retry_max_duration_secs: 10
|
||||||
|
# We must route the sink through kong because ingesting logs before logflare is fully initialised will
|
||||||
|
# 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=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
|
||||||
|
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=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
|
||||||
|
logflare_storage:
|
||||||
|
type: 'http'
|
||||||
|
inputs:
|
||||||
|
- storage_logs
|
||||||
|
encoding:
|
||||||
|
codec: 'json'
|
||||||
|
method: 'post'
|
||||||
|
request:
|
||||||
|
retry_max_duration_secs: 10
|
||||||
|
uri: 'http://analytics:4000/api/logs?source_name=storage.logs.prod.2&api_key=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
|
||||||
|
logflare_kong:
|
||||||
|
type: 'http'
|
||||||
|
inputs:
|
||||||
|
- kong_logs
|
||||||
|
- kong_err
|
||||||
|
encoding:
|
||||||
|
codec: 'json'
|
||||||
|
method: 'post'
|
||||||
|
request:
|
||||||
|
retry_max_duration_secs: 10
|
||||||
|
uri: 'http://analytics:4000/api/logs?source_name=cloudflare.logs.prod&api_key=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
|
||||||
+4
-2
@@ -5,7 +5,8 @@
|
|||||||
# on local host
|
# on local host
|
||||||
.PHONY: build-env
|
.PHONY: build-env
|
||||||
build-env: SERVICE ?= $(DOCKER_SERVICE)
|
build-env: SERVICE ?= $(DOCKER_SERVICE)
|
||||||
build-env: bootstrap
|
build-env: APP_NAME := $(subst _,,$(subst -,,$(subst .,,$(call LOWERCASE,$(firstword $(subst /, ,$(STACK)))))))
|
||||||
|
build-env: bootstrap stack
|
||||||
$(call docker-compose-exec-sh,$(SERVICE), \
|
$(call docker-compose-exec-sh,$(SERVICE), \
|
||||||
rm -f .env \
|
rm -f .env \
|
||||||
&& make .env ENV=$(ENV) \
|
&& make .env ENV=$(ENV) \
|
||||||
@@ -25,7 +26,8 @@ build-init:
|
|||||||
# on local host
|
# on local host
|
||||||
.PHONY: build-shared
|
.PHONY: build-shared
|
||||||
build-shared: SERVICE ?= $(DOCKER_SERVICE)
|
build-shared: SERVICE ?= $(DOCKER_SERVICE)
|
||||||
build-shared: bootstrap
|
build-shared: APP_NAME := $(subst _,,$(subst -,,$(subst .,,$(call LOWERCASE,$(firstword $(subst /, ,$(STACK)))))))
|
||||||
|
build-shared: bootstrap stack
|
||||||
$(call docker-compose-exec-sh,$(SERVICE), \
|
$(call docker-compose-exec-sh,$(SERVICE), \
|
||||||
mkdir -p /$(notdir $(SHARED)) \
|
mkdir -p /$(notdir $(SHARED)) \
|
||||||
&& $(foreach folder,$(SHARED_FOLDERS), \
|
&& $(foreach folder,$(SHARED_FOLDERS), \
|
||||||
|
|||||||
+25
-20
@@ -32,7 +32,7 @@ bootstrap-stack: docker-network debug-STACK $(foreach stack,$(STACK),bootstrap-s
|
|||||||
# target build: Build application docker images to run
|
# target build: Build application docker images to run
|
||||||
# on local host
|
# on local host
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build: docker-compose-build ## Build application docker images
|
build: docker-stack-build ## Build application docker images
|
||||||
|
|
||||||
# target build@%: Build application docker images of % ENV
|
# target build@%: Build application docker images of % ENV
|
||||||
# on local host
|
# on local host
|
||||||
@@ -50,22 +50,24 @@ build@%: myos-user
|
|||||||
# target clean: Clean application and docker images
|
# target clean: Clean application and docker images
|
||||||
# on local host
|
# on local host
|
||||||
.PHONY: clean app-clean
|
.PHONY: clean app-clean
|
||||||
clean: app-clean docker-rm docker-images-rm docker-volume-rm .env-clean ## Clean application and docker stuffs
|
clean: DOCKER_COMPOSE_DOWN_OPTIONS += --rmi all --volumes
|
||||||
|
clean: app-clean docker-stack-down $(foreach stack,$(STACK),docker-image-rm-$(STACK)) .env-clean ## Clean application and docker stuffs
|
||||||
|
|
||||||
# target clean@%: Clean deployed application and docker images of % ENV
|
# target clean@%: Clean deployed application and docker images of % ENV
|
||||||
# on local host
|
# on local host
|
||||||
.PHONY: clean@%
|
.PHONY: clean@%
|
||||||
clean@%: docker-rm docker-image-rm docker-volume-rm;
|
clean@%: DOCKER_COMPOSE_DOWN_OPTIONS += --rmi all --volumes
|
||||||
|
clean@%: docker-stack-down;
|
||||||
|
|
||||||
# target config: View application docker compose file
|
# target config: View application docker compose file
|
||||||
# on local host
|
# on local host
|
||||||
.PHONY: config
|
.PHONY: config
|
||||||
config: docker-compose-config ## View application docker compose file
|
config: docker-stack-config ## View application docker compose file
|
||||||
|
|
||||||
# target connect: Connect to docker SERVICE
|
# target connect: Connect to docker SERVICE
|
||||||
# on local host
|
# on local host
|
||||||
.PHONY: connect
|
.PHONY: connect
|
||||||
connect: docker-compose-connect ## Connect to docker SERVICE
|
connect: docker-stack-connect ## Connect to docker SERVICE
|
||||||
|
|
||||||
# target connect@%: Connect to docker SERVICE of % ENV
|
# target connect@%: Connect to docker SERVICE of % ENV
|
||||||
# on first remote host
|
# on first remote host
|
||||||
@@ -81,13 +83,14 @@ deploy: $(if $(filter $(ENV),$(ENV_DEPLOY)),deploy-localhost,deploy@$(ENV)) ## D
|
|||||||
# target down: Remove application dockers
|
# target down: Remove application dockers
|
||||||
# on local host
|
# on local host
|
||||||
.PHONY: down
|
.PHONY: down
|
||||||
down: docker-compose-down ufw-delete ## Remove application dockers
|
down: docker-stack-down ufw-delete ## Remove application dockers
|
||||||
|
|
||||||
# target exec: Exec ARGS in docker SERVICE
|
# target exec: Exec ARGS in docker SERVICE
|
||||||
# on local host
|
# on local host
|
||||||
.PHONY: exec
|
.PHONY: exec
|
||||||
exec: SERVICE ?= $(DOCKER_SERVICE)
|
exec: SERVICE ?= $(DOCKER_SERVICE)
|
||||||
exec: ## Exec command in docker SERVICE
|
exec: APP_NAME := $(subst _,,$(subst -,,$(subst .,,$(call LOWERCASE,$(firstword $(subst /, ,$(STACK)))))))
|
||||||
|
exec: stack ## Exec command in docker SERVICE
|
||||||
#ifneq (,$(filter $(ENV),$(ENV_DEPLOY)))
|
#ifneq (,$(filter $(ENV),$(ENV_DEPLOY)))
|
||||||
# $(RUN) $(call exec,$(ARGS))
|
# $(RUN) $(call exec,$(ARGS))
|
||||||
#else
|
#else
|
||||||
@@ -98,7 +101,7 @@ exec: ## Exec command in docker SERVICE
|
|||||||
# on all remote hosts
|
# on all remote hosts
|
||||||
.PHONY: exec@%
|
.PHONY: exec@%
|
||||||
exec@%: SERVICE ?= $(DOCKER_SERVICE)
|
exec@%: SERVICE ?= $(DOCKER_SERVICE)
|
||||||
exec@%:
|
exec@%: stack
|
||||||
$(call make,ssh-exec,$(MYOS),APP ARGS SERVICE)
|
$(call make,ssh-exec,$(MYOS),APP ARGS SERVICE)
|
||||||
|
|
||||||
# target force-%: Fire targets %, stack-user-% and stack-host-%
|
# target force-%: Fire targets %, stack-user-% and stack-host-%
|
||||||
@@ -114,17 +117,17 @@ install: bootstrap app-install ## Install application
|
|||||||
# target logs: Display application dockers logs
|
# target logs: Display application dockers logs
|
||||||
# on local host
|
# on local host
|
||||||
.PHONY: logs
|
.PHONY: logs
|
||||||
logs: docker-compose-logs ## Display application dockers logs
|
logs: docker-stack-logs ## Display application dockers logs
|
||||||
|
|
||||||
# target ps: List application dockers
|
# target ps: List application dockers
|
||||||
# on local host
|
# on local host
|
||||||
.PHONY: ps
|
.PHONY: ps
|
||||||
ps: docker-compose-ps ## List application dockers
|
ps: docker-stack-ps ## List application dockers
|
||||||
|
|
||||||
# target rebuild: Rebuild application docker images
|
# target rebuild: Rebuild application docker images
|
||||||
# on local host
|
# on local host
|
||||||
.PHONY: rebuild
|
.PHONY: rebuild
|
||||||
rebuild: docker-compose-rebuild ## Rebuild application dockers images
|
rebuild: docker-stack-rebuild ## Rebuild application dockers images
|
||||||
|
|
||||||
# target rebuild@%: Rebuild application docker images
|
# target rebuild@%: Rebuild application docker images
|
||||||
# on local host
|
# on local host
|
||||||
@@ -135,7 +138,7 @@ rebuild@%:
|
|||||||
# target recreate: Recreate application dockers
|
# target recreate: Recreate application dockers
|
||||||
# on local host
|
# on local host
|
||||||
.PHONY: recreate
|
.PHONY: recreate
|
||||||
recreate: docker-compose-recreate app-start ## Recreate application dockers
|
recreate: docker-stack-recreate app-start ## Recreate application dockers
|
||||||
|
|
||||||
# target reinstall: Fire clean, Call .env target, Call install target
|
# target reinstall: Fire clean, Call .env target, Call install target
|
||||||
# on local host
|
# on local host
|
||||||
@@ -151,13 +154,14 @@ release: release-create ## Create release VERSION
|
|||||||
# target restart: Restart application dockers
|
# target restart: Restart application dockers
|
||||||
# on local host
|
# on local host
|
||||||
.PHONY: restart
|
.PHONY: restart
|
||||||
restart: docker-compose-restart app-start ## Restart application
|
restart: docker-stack-restart app-start ## Restart application
|
||||||
|
|
||||||
# target run: Run command ARGS in a new docker SERVICE
|
# target run: Run command ARGS in a new docker SERVICE
|
||||||
# on local host
|
# on local host
|
||||||
.PHONY: run
|
.PHONY: run
|
||||||
run: SERVICE ?= $(or $(DOCKER_COMPOSE_SERVICE),$(DOCKER_SERVICE))
|
run: SERVICE ?= $(or $(DOCKER_COMPOSE_SERVICE),$(DOCKER_SERVICE))
|
||||||
run: ## Run a command in a new docker
|
run: APP_NAME := $(subst _,,$(subst -,,$(subst .,,$(call LOWERCASE,$(firstword $(subst /, ,$(STACK)))))))
|
||||||
|
run: stack ## Run a command in a new docker
|
||||||
#ifneq (,$(filter $(ENV),$(ENV_DEPLOY)))
|
#ifneq (,$(filter $(ENV),$(ENV_DEPLOY)))
|
||||||
# $(call run,$(ARGS))
|
# $(call run,$(ARGS))
|
||||||
#else
|
#else
|
||||||
@@ -175,7 +179,7 @@ run@%:
|
|||||||
# target scale: Scale SERVICE application to NUM dockers
|
# target scale: Scale SERVICE application to NUM dockers
|
||||||
# on local host
|
# on local host
|
||||||
.PHONY: scale
|
.PHONY: scale
|
||||||
scale: docker-compose-scale ## Scale SERVICE application to NUM dockers
|
scale: docker-stack-scale ## Scale SERVICE application to NUM dockers
|
||||||
|
|
||||||
# target shutdown: remove application, host and user dockers
|
# target shutdown: remove application, host and user dockers
|
||||||
# on local host
|
# on local host
|
||||||
@@ -192,8 +196,9 @@ ssh@%:
|
|||||||
## it updates COMPOSE_FILE with all .yml files of the current stack
|
## it updates COMPOSE_FILE with all .yml files of the current stack
|
||||||
.PHONY: stack
|
.PHONY: stack
|
||||||
stack:
|
stack:
|
||||||
$(call compose-file,docker)
|
$(if $(filter-out . myos,$(MYOS)), \
|
||||||
$(foreach stackz,$(STACK),$(call docker-stack,$(stackz)))
|
$(call compose-file,. docker), \
|
||||||
|
$(foreach stackz,$(STACK),$(call docker-stack,$(stackz))))
|
||||||
$(call compose-file,$(MYOS_STACK),$(MYOS_STACK_FILE))
|
$(call compose-file,$(MYOS_STACK),$(MYOS_STACK_FILE))
|
||||||
|
|
||||||
# target stack-%: Call docker-compose-% target on STACK
|
# target stack-%: Call docker-compose-% target on STACK
|
||||||
@@ -211,12 +216,12 @@ stack-%:
|
|||||||
# target start app-start: Start application dockers
|
# target start app-start: Start application dockers
|
||||||
# on local host
|
# on local host
|
||||||
.PHONY: start app-start
|
.PHONY: start app-start
|
||||||
start: docker-compose-start ## Start application dockers
|
start: docker-stack-start ## Start application dockers
|
||||||
|
|
||||||
# target stop: Stop application dockers
|
# target stop: Stop application dockers
|
||||||
# on local host
|
# on local host
|
||||||
.PHONY: stop
|
.PHONY: stop
|
||||||
stop: docker-compose-stop ## Stop application dockers
|
stop: docker-stack-stop ## Stop application dockers
|
||||||
|
|
||||||
# target tests app-tests: Test application
|
# target tests app-tests: Test application
|
||||||
# on local host
|
# on local host
|
||||||
@@ -226,7 +231,7 @@ tests: app-tests ## Test application
|
|||||||
# target up: Create and start application dockers
|
# target up: Create and start application dockers
|
||||||
# on local host
|
# on local host
|
||||||
.PHONY: up
|
.PHONY: up
|
||||||
up: docker-compose-up ufw-update app-start ## Create application dockers
|
up: docker-stack-up ufw-update app-start ## Create application dockers
|
||||||
|
|
||||||
# target update app-update: Update application files
|
# target update app-update: Update application files
|
||||||
# on local host
|
# on local host
|
||||||
|
|||||||
+11
-6
@@ -1,6 +1,8 @@
|
|||||||
COMPOSE_FILE_DEBUG ?= $(if $(DEBUG),true)
|
COMPOSE_FILE_DEBUG ?= $(if $(DEBUG),true)
|
||||||
COMPOSE_FILE_DNS ?= false
|
COMPOSE_FILE_DNS ?= false
|
||||||
COMPOSE_FILE_HOME ?= false
|
COMPOSE_FILE_HOME ?= false
|
||||||
|
COMPOSE_FILE_LABELS ?= true
|
||||||
|
COMPOSE_FILE_NETWORKS ?= true
|
||||||
COMPOSE_FILE_NFS ?= $(MOUNT_NFS)
|
COMPOSE_FILE_NFS ?= $(MOUNT_NFS)
|
||||||
COMPOSE_FILE_SSH ?= true
|
COMPOSE_FILE_SSH ?= true
|
||||||
ifneq ($(SUBREPO),)
|
ifneq ($(SUBREPO),)
|
||||||
@@ -8,14 +10,16 @@ COMPOSE_FILE_SUBREPO ?= true
|
|||||||
else
|
else
|
||||||
COMPOSE_FILE_APP ?= true
|
COMPOSE_FILE_APP ?= true
|
||||||
endif
|
endif
|
||||||
COMPOSE_FILE_SUFFIX ?= $(foreach suffix,$(call LOWERCASE,$(filter-out SUFFIX,$(patsubst COMPOSE_FILE_%,%,$(filter COMPOSE_FILE_%,$(MAKE_FILE_VARS))))),$(if $(filter-out false False FALSE,$(COMPOSE_FILE_$(call UPPERCASE,$(suffix)))),$(suffix)))
|
COMPOSE_FILE_SUFFIX ?= $(foreach suffix,$(call LOWERCASE,$(filter-out SUFFIX,$(patsubst COMPOSE_FILE_%,%,$(filter COMPOSE_FILE_%,$(MAKE_CMD_VARS) $(MAKE_ENV_VARS) $(MAKE_FILE_VARS))))),$(if $(filter-out false False FALSE,$(COMPOSE_FILE_$(call UPPERCASE,$(suffix)))),$(suffix) $(foreach service,$(filter-out true True TRUE,$(COMPOSE_FILE_$(call UPPERCASE,$(suffix)))),$(suffix).$(service))))
|
||||||
|
COMPOSE_FILE_VOLUMES ?= true
|
||||||
COMPOSE_FILE_WWW ?= false
|
COMPOSE_FILE_WWW ?= false
|
||||||
COMPOSE_IGNORE_ORPHANS ?= false
|
COMPOSE_IGNORE_ORPHANS ?= false
|
||||||
COMPOSE_PROJECT_NAME ?= $(if $(DOCKER_COMPOSE_PROJECT_NAME),$(DOCKER_COMPOSE_PROJECT_NAME),$(subst .,,$(call LOWERCASE,$(USER)-$(APP_NAME)-$(ENV)$(addprefix -,$(subst /,,$(subst -,,$(APP_PATH)))))))
|
COMPOSE_PROJECT_NAME ?= $(if $(DOCKER_COMPOSE_PROJECT_NAME),$(DOCKER_COMPOSE_PROJECT_NAME),$(subst .,,$(call LOWERCASE,$(USER)-$(APP_NAME)-$(ENV)$(addprefix -,$(subst /,,$(subst -,,$(APP_PATH)))))))
|
||||||
COMPOSE_SERVICE_NAME ?= $(if $(DOCKER_COMPOSE_SERVICE_NAME),$(DOCKER_COMPOSE_SERVICE_NAME),$(subst _,-,$(COMPOSE_PROJECT_NAME)))
|
COMPOSE_SERVICE_NAME ?= $(if $(DOCKER_COMPOSE_SERVICE_NAME),$(DOCKER_COMPOSE_SERVICE_NAME),$(subst _,-,$(COMPOSE_PROJECT_NAME)))
|
||||||
COMPOSE_VERSION ?= 2.5.0
|
COMPOSE_VERSION ?= 2.24.4
|
||||||
CONTEXT += COMPOSE_FILE DOCKER_REPOSITORY
|
CONTEXT += COMPOSE_FILE DOCKER_REPOSITORY
|
||||||
CONTEXT_DEBUG += DOCKER_BUILD_TARGET DOCKER_COMPOSE_PROJECT_NAME DOCKER_IMAGE_TAG DOCKER_REGISTRY DOCKER_SERVICE DOCKER_SERVICES
|
CONTEXT_DEBUG += DOCKER_BUILD_TARGET DOCKER_COMPOSE_PROJECT_NAME DOCKER_IMAGE_TAG DOCKER_REGISTRY DOCKER_SERVICE DOCKER_SERVICES
|
||||||
|
DOCKER_ARGS ?= --log-level=$(LOG_LEVEL)
|
||||||
DOCKER_AUTHOR ?= $(DOCKER_AUTHOR_NAME) <$(DOCKER_AUTHOR_EMAIL)>
|
DOCKER_AUTHOR ?= $(DOCKER_AUTHOR_NAME) <$(DOCKER_AUTHOR_EMAIL)>
|
||||||
DOCKER_AUTHOR_EMAIL ?= $(subst +git,+docker,$(GIT_AUTHOR_EMAIL))
|
DOCKER_AUTHOR_EMAIL ?= $(subst +git,+docker,$(GIT_AUTHOR_EMAIL))
|
||||||
DOCKER_AUTHOR_NAME ?= $(GIT_AUTHOR_NAME)
|
DOCKER_AUTHOR_NAME ?= $(GIT_AUTHOR_NAME)
|
||||||
@@ -27,7 +31,7 @@ DOCKER_BUILD_TARGET ?= $(if $(filter $(ENV),$(DOCKER_BUILD_TARGETS))
|
|||||||
DOCKER_BUILD_TARGET_DEFAULT ?= master
|
DOCKER_BUILD_TARGET_DEFAULT ?= master
|
||||||
DOCKER_BUILD_TARGETS ?= $(ENV_DEPLOY)
|
DOCKER_BUILD_TARGETS ?= $(ENV_DEPLOY)
|
||||||
DOCKER_BUILD_VARS ?= APP BRANCH COMPOSE_VERSION DOCKER_GID DOCKER_MACHINE DOCKER_REPOSITORY DOCKER_SYSTEM GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME SSH_REMOTE_HOSTS USER VERSION
|
DOCKER_BUILD_VARS ?= APP BRANCH COMPOSE_VERSION DOCKER_GID DOCKER_MACHINE DOCKER_REPOSITORY DOCKER_SYSTEM GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME SSH_REMOTE_HOSTS USER VERSION
|
||||||
DOCKER_COMPOSE := $(or $(shell docker-compose --version 2>/dev/null |awk '$$4 != "v'"$(COMPOSE_VERSION)"'" {exit 1} END {if (NR == 0) exit 1}' && printf 'docker-compose\n'),$(shell docker compose >/dev/null 2>&1 && printf 'docker compose\n'))
|
DOCKER_COMPOSE ?= $(or $(shell $(call verle,$(COMPOSE_VERSION),$(shell docker compose version --short 2>/dev/null)) && printf 'docker $(DOCKER_ARGS) compose\n'),$(shell $(call verle,$(COMPOSE_VERSION),$(shell docker-compose version --short 2>/dev/null)) && printf 'docker-compose\n'))
|
||||||
DOCKER_COMPOSE_ARGS ?= --ansi=auto
|
DOCKER_COMPOSE_ARGS ?= --ansi=auto
|
||||||
DOCKER_COMPOSE_DOWN_OPTIONS ?=
|
DOCKER_COMPOSE_DOWN_OPTIONS ?=
|
||||||
DOCKER_COMPOSE_FILE ?= docker-compose
|
DOCKER_COMPOSE_FILE ?= docker-compose
|
||||||
@@ -38,7 +42,7 @@ DOCKER_COMPOSE_RUN_OPTIONS ?= --rm $(DOCKER_COMPOSE_RUN_ENTRYPOINT) $(DOCKE
|
|||||||
DOCKER_COMPOSE_RUN_WORKDIR ?= $(if $(DOCKER_COMPOSE_WORKDIR),-w $(DOCKER_COMPOSE_WORKDIR))
|
DOCKER_COMPOSE_RUN_WORKDIR ?= $(if $(DOCKER_COMPOSE_WORKDIR),-w $(DOCKER_COMPOSE_WORKDIR))
|
||||||
DOCKER_COMPOSE_SERVICE_NAME ?= $(subst _,-,$(DOCKER_COMPOSE_PROJECT_NAME))
|
DOCKER_COMPOSE_SERVICE_NAME ?= $(subst _,-,$(DOCKER_COMPOSE_PROJECT_NAME))
|
||||||
DOCKER_COMPOSE_UP_OPTIONS ?= -d
|
DOCKER_COMPOSE_UP_OPTIONS ?= -d
|
||||||
DOCKER_IMAGE_TAG ?= $(if $(filter true,$(DEPLOY)),$(if $(filter $(ENV),$(ENV_DEPLOY)),$(VERSION)),$(if $(DRONE_BUILD_NUMBER),$(DRONE_BUILD_NUMBER),latest))
|
DOCKER_IMAGE_TAG ?= $(if $(filter true,$(DEPLOY)),$(if $(filter $(ENV),$(ENV_DEPLOY)),$(VERSION)),$(if $(DRONE_BUILD_NUMBER),$(DRONE_BUILD_NUMBER),$(ENV)))
|
||||||
DOCKER_IMAGES ?= $(patsubst %/,%,$(patsubst docker/%,%,$(dir $(wildcard docker/*/Dockerfile))))
|
DOCKER_IMAGES ?= $(patsubst %/,%,$(patsubst docker/%,%,$(dir $(wildcard docker/*/Dockerfile))))
|
||||||
DOCKER_PLUGIN ?= rexray/s3fs:latest
|
DOCKER_PLUGIN ?= rexray/s3fs:latest
|
||||||
DOCKER_PLUGIN_ARGS ?= $(foreach var,$(DOCKER_PLUGIN_VARS),$(if $(DOCKER_PLUGIN_$(var)),$(var)='$(DOCKER_PLUGIN_$(var))'))
|
DOCKER_PLUGIN_ARGS ?= $(foreach var,$(DOCKER_PLUGIN_VARS),$(if $(DOCKER_PLUGIN_$(var)),$(var)='$(DOCKER_PLUGIN_$(var))'))
|
||||||
@@ -72,7 +76,7 @@ define compose-file
|
|||||||
$(eval name := $(or $(2),$(DOCKER_COMPOSE_FILE)))
|
$(eval name := $(or $(2),$(DOCKER_COMPOSE_FILE)))
|
||||||
$(eval suffix := $(or $(3),$(COMPOSE_FILE_SUFFIX)))
|
$(eval suffix := $(or $(3),$(COMPOSE_FILE_SUFFIX)))
|
||||||
$(eval extension := $(or $(4),yml yaml))
|
$(eval extension := $(or $(4),yml yaml))
|
||||||
$(eval COMPOSE_FILE += $(wildcard $(foreach e,$(extension),$(foreach n,$(name),$(foreach p,$(path),$(p)/$(n).$(e) $(p)/$(n).$(ENV).$(e) $(foreach s,$(suffix),$(p)/$(n).$(s).$(e) $(p)/$(n).$(s).$(ENV).$(e)))))))
|
$(eval COMPOSE_FILE += $(wildcard $(foreach e,$(extension),$(foreach n,$(name),$(foreach p,$(path),$(p)/$(n).$(e) $(p)/$(n).$(ENV).$(e) $(p)/$(ENV)/$(n).$(e) $(p)/$(ENV)/$(n).$(ENV).$(e) $(foreach s,$(suffix),$(p)/$(n).$(s).$(e) $(p)/$(n).$(s).$(ENV).$(e)))))))
|
||||||
endef
|
endef
|
||||||
# function docker-build: Build docker image
|
# function docker-build: Build docker image
|
||||||
define docker-build
|
define docker-build
|
||||||
@@ -95,6 +99,7 @@ define docker-commit
|
|||||||
$(RUN) docker commit $(container) $(repository):$(tag)
|
$(RUN) docker commit $(container) $(repository):$(tag)
|
||||||
endef
|
endef
|
||||||
# function docker-compose: Run docker-compose with arg 1
|
# function docker-compose: Run docker-compose with arg 1
|
||||||
|
# TODO: accept $2 COMPOSE_FILE and remove stack from prereq
|
||||||
define docker-compose
|
define docker-compose
|
||||||
$(call INFO,docker-compose,$(1))
|
$(call INFO,docker-compose,$(1))
|
||||||
$(if $(COMPOSE_FILE),
|
$(if $(COMPOSE_FILE),
|
||||||
@@ -148,7 +153,7 @@ define docker-stack-update
|
|||||||
$(eval name := $(firstword $(subst :, ,$(stack))))
|
$(eval name := $(firstword $(subst :, ,$(stack))))
|
||||||
$(eval version := $(or $(2),$(if $(findstring :,$(stack)),$(lastword $(subst :, ,$(stack))),latest)))
|
$(eval version := $(or $(2),$(if $(findstring :,$(stack)),$(lastword $(subst :, ,$(stack))),latest)))
|
||||||
$(eval path := $(patsubst %/,%,$(or $(3),$(if $(findstring /,$(1)),$(if $(wildcard stack/$(1) stack/$(1).yml),stack/$(if $(findstring .yml,$(1)),$(dir $(1)),$(if $(wildcard stack/$(1).yml),$(dir $(1)),$(1))),$(if $(wildcard stack/$(stackz)/$(1) stack/$(stackz)/$(1).yml),stack/$(stackz)/$(if $(findstring .yml,$(1)),$(dir $(1)),$(if $(wildcard stack/$(stackz)/$(1).yml),$(dir $(1)),$(1))),$(dir $(1))))),$(firstword $(wildcard stack/$(stackz)/$(name) stack/$(stackz) stack/$(name))))))
|
$(eval path := $(patsubst %/,%,$(or $(3),$(if $(findstring /,$(1)),$(if $(wildcard stack/$(1) stack/$(1).yml),stack/$(if $(findstring .yml,$(1)),$(dir $(1)),$(if $(wildcard stack/$(1).yml),$(dir $(1)),$(1))),$(if $(wildcard stack/$(stackz)/$(1) stack/$(stackz)/$(1).yml),stack/$(stackz)/$(if $(findstring .yml,$(1)),$(dir $(1)),$(if $(wildcard stack/$(stackz)/$(1).yml),$(dir $(1)),$(1))),$(dir $(1))))),$(firstword $(wildcard stack/$(stackz)/$(name) stack/$(stackz) stack/$(name))))))
|
||||||
$(call compose-file,$(path),$(name),$(COMPOSE_FILE_SUFFIX) $(version))
|
$(call compose-file,$(path),docker-compose $(name),$(COMPOSE_FILE_SUFFIX) $(version))
|
||||||
$(if $(wildcard $(path)/.env.dist),$(call .env,,$(path)/.env.dist,$(wildcard $(CONFIG)/$(ENV)/$(APP)/.env $(path)/.env.$(ENV) .env)))
|
$(if $(wildcard $(path)/.env.dist),$(call .env,,$(path)/.env.dist,$(wildcard $(CONFIG)/$(ENV)/$(APP)/.env $(path)/.env.$(ENV) .env)))
|
||||||
endef
|
endef
|
||||||
# function docker-tag: Tag docker image
|
# function docker-tag: Tag docker image
|
||||||
|
|||||||
+5
-5
@@ -18,10 +18,9 @@ APP_REPOSITORY_SCHEME ?= $(shell printf '$(APP_REPOSITORY_URL)\n' |sed
|
|||||||
APP_REPOSITORY_URI ?= $(shell printf '$(APP_REPOSITORY_URL)\n' |sed 's|.*://||;')
|
APP_REPOSITORY_URI ?= $(shell printf '$(APP_REPOSITORY_URL)\n' |sed 's|.*://||;')
|
||||||
APP_REPOSITORY_URL ?= $(GIT_REPOSITORY)
|
APP_REPOSITORY_URL ?= $(GIT_REPOSITORY)
|
||||||
APP_REQUIRED ?= $(APP_REPOSITORY)
|
APP_REQUIRED ?= $(APP_REPOSITORY)
|
||||||
APP_SCHEME ?= https
|
APP_SCHEME ?= http
|
||||||
APP_UPSTREAM_REPOSITORY ?= $(or $(shell git config --get remote.upstream.url 2>/dev/null),$(GIT_UPSTREAM_REPOSITORY))
|
APP_UPSTREAM_REPOSITORY ?= $(or $(shell git config --get remote.upstream.url 2>/dev/null),$(GIT_UPSTREAM_REPOSITORY))
|
||||||
APP_URI ?= $(patsubst %,%/$(APP_PATH),$(APP_HOST))
|
APP_URI ?= $(patsubst %,%/$(APP_PATH),$(APP_HOST))
|
||||||
APP_URIS ?= $(APP_URI)
|
|
||||||
APP_URL ?= $(patsubst %,$(APP_SCHEME)://%,$(APP_URI))
|
APP_URL ?= $(patsubst %,$(APP_SCHEME)://%,$(APP_URI))
|
||||||
APP_VERSION ?= $(VERSION)
|
APP_VERSION ?= $(VERSION)
|
||||||
CONTEXT += APP APPS BRANCH DOMAIN VERSION RELEASE
|
CONTEXT += APP APPS BRANCH DOMAIN VERSION RELEASE
|
||||||
@@ -37,10 +36,11 @@ SERVICES ?= $(DOCKER_SERVICES)
|
|||||||
envprefix = $(foreach env,$(3),$(if $($(call UPPERCASE,$(1)_SERVICE_$(2)_$(env))),$(env)=$($(call UPPERCASE,$(1)_SERVICE_$(2)_$(env)))))
|
envprefix = $(foreach env,$(3),$(if $($(call UPPERCASE,$(1)_SERVICE_$(2)_$(env))),$(env)=$($(call UPPERCASE,$(1)_SERVICE_$(2)_$(env)))))
|
||||||
patsublist = $(patsubst $(1),$(2),$(firstword $(3)))$(foreach pattern,$(wordlist 2,255,$(3)),$(comma)$(patsubst $(1),$(2),$(pattern)))
|
patsublist = $(patsubst $(1),$(2),$(firstword $(3)))$(foreach pattern,$(wordlist 2,255,$(3)),$(comma)$(patsubst $(1),$(2),$(pattern)))
|
||||||
servicenvs = $(foreach env,$(call UPPERCASE,$($(1)_SERVICE_$(2)_ENVS)),$(if $(3),$($(1)_SERVICE_$(env)_$(3)),$($(1)_SERVICE_$(2)_$(env))))
|
servicenvs = $(foreach env,$(call UPPERCASE,$($(1)_SERVICE_$(2)_ENVS)),$(if $(3),$($(1)_SERVICE_$(env)_$(3)),$($(1)_SERVICE_$(2)_$(env))))
|
||||||
tagprefix = $(call urlprefix,$(or $($(call UPPERCASE,$(1)_SERVICE_$(2)_PATH)),$($(call UPPERCASE,$(1)_SERVICE_PATH))),$(or $($(call UPPERCASE,$(1)_SERVICE_$(2)_OPTS)),$($(call UPPERCASE,$(1)_SERVICE_OPTS)),$(call envprefix,$(1),$(2),allow auth deny preprend proto register strip)),$(or $(foreach env,$(3),$($(call UPPERCASE,$(1)_SERVICE_$(2)_$(env)))),$($(call UPPERCASE,$(1)_SERVICE_$(2)_URIS)),$(call uriprefix,$(1),$(2))))
|
tagprefix = $(call urlprefix,$(or $($(call UPPERCASE,$(1)_SERVICE_$(2)_PATH)),$($(call UPPERCASE,$(1)_SERVICE_PATH))),$(or $($(call UPPERCASE,$(1)_SERVICE_$(2)_OPTS)),$($(call UPPERCASE,$(1)_SERVICE_OPTS)),$(call envprefix,$(1),$(2),allow auth deny preprend proto register strip)),$(or $(foreach env,$(3),$($(call UPPERCASE,$(1)_SERVICE_$(2)_$(env)))),$($(call UPPERCASE,$(1)_SERVICE_$(2)_URIS)),$(call uri,$(1),$(2))))
|
||||||
uriprefix = $(foreach svc,$(1),$(patsubst %,$(addsuffix .,$(or $($(call UPPERCASE,$(svc)_SERVICE_$(2)_NAME)),$($(call UPPERCASE,$(svc)_SERVICE_NAME)),$(svc)))%,$(or $(3),$(APP_URIS))))
|
uri = $(foreach svc,$(1),$(patsubst %,$(addsuffix .,$(or $($(call UPPERCASE,$(svc)_SERVICE_$(2)_NAME)),$($(call UPPERCASE,$(svc)_SERVICE_NAME)),$(svc)))%,$(or $(3),$(APP_URI))))
|
||||||
|
url = $(patsubst %,$(APP_SCHEME)://%,$(call uri,$(1),$(2),$(or $(3),$(APP_URI))))
|
||||||
url_suffix = *
|
url_suffix = *
|
||||||
urlprefix = $(strip $(call patsublist,%,urlprefix-%$(1)$(url_suffix) $(2),$(or $(3),$(APP_URIS))))
|
urlprefix = $(strip $(call patsublist,%,urlprefix-%$(1)$(url_suffix) $(2),$(or $(3),$(APP_URI))))
|
||||||
urlprefixs = $(strip $(call urlprefix,$(firstword $(1)),$(wordlist 2,16,$(1)))$(foreach prefix,$(subst $(space),$(dollar),$(2)) $(subst $(space),$(dollar),$(3)) $(subst $(space),$(dollar),$(4)),$(comma)$(call subst,$(dollar),$(space),$(call urlprefix,$(firstword $(prefix)),$(wordlist 2,16,$(prefix))))))
|
urlprefixs = $(strip $(call urlprefix,$(firstword $(1)),$(wordlist 2,16,$(1)))$(foreach prefix,$(subst $(space),$(dollar),$(2)) $(subst $(space),$(dollar),$(3)) $(subst $(space),$(dollar),$(4)),$(comma)$(call subst,$(dollar),$(space),$(call urlprefix,$(firstword $(prefix)),$(wordlist 2,16,$(prefix))))))
|
||||||
## urlprefix tests (x APP_URI)
|
## urlprefix tests (x APP_URI)
|
||||||
# $(call urlprefix)
|
# $(call urlprefix)
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
MAKECMDARGS += ufw ufw-docker
|
MAKECMDARGS += ufw ufw-docker
|
||||||
UFW_UPDATE ?= $(or $(SERVICE),$(DOCKER_SERVICES))
|
UFW_UPDATE ?= $(or $(SERVICE),$(DOCKER_SERVICES))
|
||||||
|
|
||||||
ifeq ($(SETUP_UFW),true)
|
|
||||||
|
|
||||||
# function ufw: Exec command ufw with args 1
|
# function ufw: Exec command ufw with args 1
|
||||||
define ufw
|
define ufw
|
||||||
$(call INFO,ufw,$(1)$(comma))
|
$(call INFO,ufw,$(1)$(comma))
|
||||||
@@ -18,5 +16,3 @@ define ufw-docker
|
|||||||
$(eval DOCKER_COMPOSE_PROJECT_NAME := $(HOST_COMPOSE_PROJECT_NAME))
|
$(eval DOCKER_COMPOSE_PROJECT_NAME := $(HOST_COMPOSE_PROJECT_NAME))
|
||||||
$(call app-exec,,$(if $(DOCKER_RUN),,$(SUDO)) ufw-docker $(1))
|
$(call app-exec,,$(if $(DOCKER_RUN),,$(SUDO)) ufw-docker $(1))
|
||||||
endef
|
endef
|
||||||
|
|
||||||
endif
|
|
||||||
|
|||||||
+32
-25
@@ -1,9 +1,9 @@
|
|||||||
##
|
##
|
||||||
# DOCKER
|
# DOCKER
|
||||||
|
|
||||||
# target docker-build: Fire docker-images-myos, Call docker-build-% target for each DOCKER_IMAGES
|
# target docker-build: Fire docker-image-myos, Call docker-build-% target for each DOCKER_IMAGES
|
||||||
.PHONY: docker-build
|
.PHONY: docker-build
|
||||||
docker-build: docker-images-myos
|
docker-build: docker-image-myos
|
||||||
$(foreach image,$(or $(SERVICE),$(DOCKER_IMAGES)),$(call make,docker-build-$(image)))
|
$(foreach image,$(or $(SERVICE),$(DOCKER_IMAGES)),$(call make,docker-build-$(image)))
|
||||||
|
|
||||||
# target docker-build-%: Call docker-build for each Dockerfile in docker/% folder
|
# target docker-build-%: Call docker-build for each Dockerfile in docker/% folder
|
||||||
@@ -23,10 +23,10 @@ docker-commit: stack
|
|||||||
docker-commit-%: stack
|
docker-commit-%: stack
|
||||||
$(foreach service,$(or $(SERVICE),$(SERVICES)),$(call docker-commit,$(service),,,$*))
|
$(foreach service,$(or $(SERVICE),$(SERVICES)),$(call docker-commit,$(service),,,$*))
|
||||||
|
|
||||||
# target docker-compose-build: Fire docker-images-myos, Call docker-compose build SERVICE
|
# target docker-compose-build: Fire docker-image-myos, Call docker-compose build SERVICE
|
||||||
.PHONY: docker-compose-build
|
.PHONY: docker-compose-build
|
||||||
docker-compose-build: DOCKER_RUN_OPTIONS += -it
|
docker-compose-build: DOCKER_RUN_OPTIONS += -it
|
||||||
docker-compose-build: docker-images-myos stack
|
docker-compose-build: docker-image-myos stack
|
||||||
$(call docker-compose,build $(DOCKER_BUILD_ARGS) $(if $(filter $(SERVICE),$(SERVICES)),$(SERVICE)))
|
$(call docker-compose,build $(DOCKER_BUILD_ARGS) $(if $(filter $(SERVICE),$(SERVICES)),$(SERVICE)))
|
||||||
|
|
||||||
# target docker-compose-config: Call docker-compose config
|
# target docker-compose-config: Call docker-compose config
|
||||||
@@ -109,37 +109,38 @@ docker-compose-stop: stack
|
|||||||
# target docker-compose-up: Fire docker-image-myos, Call docker-compose up SERVICE
|
# target docker-compose-up: Fire docker-image-myos, Call docker-compose up SERVICE
|
||||||
.PHONY: docker-compose-up
|
.PHONY: docker-compose-up
|
||||||
docker-compose-up: DOCKER_RUN_OPTIONS += -it
|
docker-compose-up: DOCKER_RUN_OPTIONS += -it
|
||||||
docker-compose-up: docker-images-myos bootstrap-stack stack
|
docker-compose-up: docker-image-myos bootstrap-stack stack
|
||||||
$(call docker-compose,up $(DOCKER_COMPOSE_UP_OPTIONS) $(if $(filter $(SERVICE),$(SERVICES)),$(SERVICE)))
|
$(call docker-compose,up $(DOCKER_COMPOSE_UP_OPTIONS) $(if $(filter $(SERVICE),$(SERVICES)),$(SERVICE)))
|
||||||
|
|
||||||
# target docker-images-myos: Call myos-docker-build-% target for each DOCKER_IMAGES_MYOS
|
# target docker-image-myos: Call myos-docker-build-% target for each MYOS_DOCKER_IMAGES
|
||||||
.PHONY: docker-images-myos
|
.PHONY: docker-image-myos
|
||||||
docker-images-myos: MAKE_VARS += DOCKER_REPOSITORY STACK
|
docker-image-myos: MAKE_VARS += DOCKER_REPOSITORY STACK
|
||||||
docker-images-myos:
|
docker-image-myos:
|
||||||
$(foreach image,$(subst $(quote),,$(DOCKER_IMAGES_MYOS)),$(call make,docker-build-$(image),$(MYOS)))
|
$(foreach image,$(subst $(quote),,$(MYOS_DOCKER_IMAGES)),$(call make,docker-build-$(image),$(MYOS)))
|
||||||
|
|
||||||
# target docker-images-rm: Remove docker images matching DOCKER_REPOSITORY
|
# target docker-image-rm: Remove docker images matching DOCKER_REPOSITORY
|
||||||
.PHONY: docker-images-rm
|
.PHONY: docker-image-rm
|
||||||
docker-images-rm:
|
docker-image-rm:
|
||||||
docker images |awk '$$1 ~ /^$(subst /,\/,$(DOCKER_REPOSITORY)/)/ {print $$3}' |sort -u |while read image; do $(RUN) docker rmi -f $$image; done
|
docker image ls |awk '$$1 ~ /^$(subst /,\/,$(DOCKER_REPOSITORY)/)/ {print $$3}' |sort -u |while read image; do $(RUN) docker rmi -f $$image; done
|
||||||
|
|
||||||
# target docker-images-rm-%: Remove docker images matching %
|
# target docker-image-rm-%: Remove docker images matching %
|
||||||
.PHONY: docker-images-rm-%
|
.PHONY: docker-image-rm-%
|
||||||
docker-images-rm-%:
|
docker-image-rm-%:
|
||||||
docker images |awk '$$1 ~ /^$(subst /,\/,$*)/ {print $$3}' |sort -u |while read image; do $(RUN) docker rmi -f $$image; done
|
$(foreach image,$(or $($(call UPPERCASE,$*)_DOCKER_IMAGES),$*), \
|
||||||
|
docker image ls |awk '$$1 ~ /^$(subst /,\/,$(firstword $(subst :, ,$(image))))/ {print $$3}' |sort -u |while read image; do $(RUN) docker rmi -f $$image; done;)
|
||||||
|
|
||||||
# target docker-login: Run 'docker login'
|
# target docker-login: Run 'docker login'
|
||||||
.PHONY: docker-login
|
.PHONY: docker-login
|
||||||
docker-login: myos-user
|
docker-login: myos-user
|
||||||
$(RUN) docker login
|
$(RUN) docker login
|
||||||
|
|
||||||
# target docker-network: Fire docker-network-create-% for DOCKER_NETWORK and DOCKER_NETWORK_PUBLIC
|
# target docker-network: Fire docker-network-create
|
||||||
.PHONY: docker-network
|
.PHONY: docker-network
|
||||||
docker-network: docker-network-create docker-network-create-$(DOCKER_NETWORK_PUBLIC)
|
docker-network: docker-network-create
|
||||||
|
|
||||||
# target docker-network-create: Fire docker-network-create-% for DOCKER_NETWORK
|
# target docker-network-create: Fire docker-network-create-% for DOCKER_NETWORK_PRIVATE and DOCKER_NETWORK_PUBLIC
|
||||||
.PHONY: docker-network-create
|
.PHONY: docker-network-create
|
||||||
docker-network-create: docker-network-create-$(DOCKER_NETWORK)
|
docker-network-create: docker-network-create-$(DOCKER_NETWORK_PRIVATE) docker-network-create-$(DOCKER_NETWORK_PUBLIC)
|
||||||
|
|
||||||
# target docker-network-create-%: Run 'docker network create %'
|
# target docker-network-create-%: Run 'docker network create %'
|
||||||
.PHONY: docker-network-create-%
|
.PHONY: docker-network-create-%
|
||||||
@@ -150,9 +151,9 @@ docker-network-create-%:
|
|||||||
||: ; \
|
||: ; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# target docker-network-rm: Fire docker-network-rm-% for DOCKER_NETWORK
|
# target docker-network-rm: Fire docker-network-rm-% for DOCKER_NETWORK_PRIVATE and DOCKER_NETWORK_PUBLIC
|
||||||
.PHONY: docker-network-rm
|
.PHONY: docker-network-rm
|
||||||
docker-network-rm: docker-network-rm-$(DOCKER_NETWORK)
|
docker-network-rm: docker-network-rm-$(DOCKER_NETWORK_PRIVATE) docker-network-rm-$(DOCKER_NETWORK_PUBLIC)
|
||||||
|
|
||||||
# target docker-network-rm-%: Run 'docker network rm %'
|
# target docker-network-rm-%: Run 'docker network rm %'
|
||||||
.PHONY: docker-network-rm-%
|
.PHONY: docker-network-rm-%
|
||||||
@@ -226,9 +227,15 @@ docker-run-%: docker-build-%
|
|||||||
$(eval command := $(ARGS))
|
$(eval command := $(ARGS))
|
||||||
$(eval path := $(patsubst %/,%,$*))
|
$(eval path := $(patsubst %/,%,$*))
|
||||||
$(eval image := $(DOCKER_REPOSITORY)/$(lastword $(subst /, ,$(path)))$(if $(findstring :,$*),,:$(DOCKER_IMAGE_TAG)))
|
$(eval image := $(DOCKER_REPOSITORY)/$(lastword $(subst /, ,$(path)))$(if $(findstring :,$*),,:$(DOCKER_IMAGE_TAG)))
|
||||||
$(eval image_id := $(shell docker images -q $(image) 2>/dev/null))
|
$(eval image_id := $(shell docker image ls -q $(image) 2>/dev/null))
|
||||||
$(call docker-run,$(if $(image_id),$(image),$(path)),$(command))
|
$(call docker-run,$(if $(image_id),$(image),$(path)),$(command))
|
||||||
|
|
||||||
|
# target docker-stack-%: Call docker-compose-% target for each STACK
|
||||||
|
.PHONY: docker-stack-%
|
||||||
|
docker-stack-%: MAKE_VARS += DOCKER_COMPOSE_DOWN_OPTIONS
|
||||||
|
docker-stack-%:
|
||||||
|
$(foreach stack, $(STACK), $(call make,docker-compose-$* STACK=$(stack) APP_NAME=$(subst _,,$(subst -,,$(subst .,,$(call LOWERCASE,$(firstword $(subst /, ,$(stack)))))))))
|
||||||
|
|
||||||
# target docker-tag: Call docker-tag for each SERVICES
|
# target docker-tag: Call docker-tag for each SERVICES
|
||||||
.PHONY: docker-tag
|
.PHONY: docker-tag
|
||||||
docker-tag: stack
|
docker-tag: stack
|
||||||
|
|||||||
+6
-1
@@ -1,8 +1,11 @@
|
|||||||
# target ufw: Call ufw ARGS
|
# target ufw: Call ufw ARGS
|
||||||
|
|
||||||
.PHONY: ufw
|
.PHONY: ufw
|
||||||
ufw:
|
ufw:
|
||||||
$(call ufw,$(ARGS))
|
$(call ufw,$(ARGS))
|
||||||
|
|
||||||
|
ifeq ($(SETUP_UFW),true)
|
||||||
|
|
||||||
# target ufw-bootstrap: Eval ufw-docker app variables
|
# target ufw-bootstrap: Eval ufw-docker app variables
|
||||||
ufw-bootstrap:
|
ufw-bootstrap:
|
||||||
$(eval APP_COMPOSE_FILE :=)
|
$(eval APP_COMPOSE_FILE :=)
|
||||||
@@ -33,7 +36,7 @@ ufw-install:
|
|||||||
|
|
||||||
# target ufw-up: Start ufw-docker docker
|
# target ufw-up: Start ufw-docker docker
|
||||||
ufw-up: DOCKER_RUN_NETWORK :=
|
ufw-up: DOCKER_RUN_NETWORK :=
|
||||||
ufw-up: DOCKER_RUN_OPTIONS := --restart always -d --cap-add NET_ADMIN -v /etc/ufw:/etc/ufw $(if wildcard /etc/default/ufw,-v /etc/default/ufw:/etc/default/ufw) --network host
|
ufw-up: DOCKER_RUN_OPTIONS := --restart always -d --cap-add NET_ADMIN -v /etc/ufw:/etc/ufw $(if $(wildcard /etc/default/ufw),-v /etc/default/ufw:/etc/default/ufw) --network host
|
||||||
ufw-up:
|
ufw-up:
|
||||||
$(call app-up)
|
$(call app-up)
|
||||||
|
|
||||||
@@ -65,3 +68,5 @@ ufw-%:
|
|||||||
$(call make,ufw-$(command) STACK="$(stack)") \
|
$(call make,ufw-$(command) STACK="$(stack)") \
|
||||||
) \
|
) \
|
||||||
)
|
)
|
||||||
|
|
||||||
|
endif
|
||||||
|
|||||||
+9
-3
@@ -6,6 +6,11 @@ DOCKER_IMAGE ?= $(USER_DOCKER_IMAGE)
|
|||||||
DOCKER_MACHINE ?= $(shell docker run --rm alpine uname -m 2>/dev/null)
|
DOCKER_MACHINE ?= $(shell docker run --rm alpine uname -m 2>/dev/null)
|
||||||
DOCKER_NAME ?= $(USER_DOCKER_NAME)
|
DOCKER_NAME ?= $(USER_DOCKER_NAME)
|
||||||
DOCKER_NETWORK ?= $(if $(USER_STACK),$(USER),$(DOCKER_NETWORK_PRIVATE))
|
DOCKER_NETWORK ?= $(if $(USER_STACK),$(USER),$(DOCKER_NETWORK_PRIVATE))
|
||||||
|
# https://github.com/moby/libnetwork/issues/2093
|
||||||
|
# network interface are attached ordered by name
|
||||||
|
# default should be first to prevent cross stack
|
||||||
|
# connections to other services with same name
|
||||||
|
DOCKER_NETWORK_DEFAULT ?= _$(COMPOSE_PROJECT_NAME)
|
||||||
DOCKER_NETWORK_PRIVATE ?= $(USER)-$(ENV)
|
DOCKER_NETWORK_PRIVATE ?= $(USER)-$(ENV)
|
||||||
DOCKER_NETWORK_PUBLIC ?= $(HOSTNAME)
|
DOCKER_NETWORK_PUBLIC ?= $(HOSTNAME)
|
||||||
# DOCKER_RUN: if empty, run system command, else run it in a docker
|
# DOCKER_RUN: if empty, run system command, else run it in a docker
|
||||||
@@ -16,15 +21,16 @@ DOCKER_RUN_NETWORK += --network $(DOCKER_NETWORK)
|
|||||||
DOCKER_RUN_OPTIONS += --rm
|
DOCKER_RUN_OPTIONS += --rm
|
||||||
DOCKER_RUN_VOLUME ?= $(patsubst %,-v %,$(DOCKER_VOLUME))
|
DOCKER_RUN_VOLUME ?= $(patsubst %,-v %,$(DOCKER_VOLUME))
|
||||||
DOCKER_RUN_WORKDIR ?= $(if $(DOCKER_WORKDIR),-w $(DOCKER_WORKDIR))
|
DOCKER_RUN_WORKDIR ?= $(if $(DOCKER_WORKDIR),-w $(DOCKER_WORKDIR))
|
||||||
|
DOCKER_SOCKET_LOCATION ?= /var/run/docker.sock
|
||||||
DOCKER_SYSTEM ?= $(shell docker run --rm alpine uname -s 2>/dev/null)
|
DOCKER_SYSTEM ?= $(shell docker run --rm alpine uname -s 2>/dev/null)
|
||||||
DOCKER_VOLUME ?= /var/run/docker.sock:/var/run/docker.sock
|
DOCKER_VOLUME ?= $(DOCKER_SOCKET_LOCATION):/var/run/docker.sock
|
||||||
DOCKER_WORKDIR ?= $(PWD)
|
DOCKER_WORKDIR ?= $(PWD)
|
||||||
ENV_VARS += DOCKER_MACHINE DOCKER_NETWORK DOCKER_NETWORK_PRIVATE DOCKER_NETWORK_PUBLIC DOCKER_SYSTEM HOST_COMPOSE_PROJECT_NAME HOST_COMPOSE_SERVICE_NAME HOST_DOCKER_REPOSITORY HOST_DOCKER_VOLUME HOST_GID HOST_UID USER_COMPOSE_PROJECT_NAME USER_COMPOSE_SERVICE_NAME USER_DOCKER_IMAGE USER_DOCKER_NAME USER_DOCKER_REPOSITORY USER_DOCKER_VOLUME
|
ENV_VARS += DOCKER_MACHINE DOCKER_NETWORK DOCKER_NETWORK_DEFAULT DOCKER_NETWORK_PRIVATE DOCKER_NETWORK_PUBLIC DOCKER_SOCKET_LOCATION DOCKER_SYSTEM HOST_COMPOSE_PROJECT_NAME HOST_COMPOSE_SERVICE_NAME HOST_DOCKER_REPOSITORY HOST_DOCKER_VOLUME HOST_GID HOST_UID USER_COMPOSE_PROJECT_NAME USER_COMPOSE_SERVICE_NAME USER_DOCKER_IMAGE USER_DOCKER_NAME USER_DOCKER_REPOSITORY USER_DOCKER_VOLUME
|
||||||
HOST_COMPOSE_PROJECT_NAME ?= $(HOSTNAME)
|
HOST_COMPOSE_PROJECT_NAME ?= $(HOSTNAME)
|
||||||
HOST_COMPOSE_SERVICE_NAME ?= $(subst _,-,$(HOST_COMPOSE_PROJECT_NAME))
|
HOST_COMPOSE_SERVICE_NAME ?= $(subst _,-,$(HOST_COMPOSE_PROJECT_NAME))
|
||||||
HOST_DOCKER_REPOSITORY ?= $(subst -,/,$(subst _,/,$(HOST_COMPOSE_PROJECT_NAME)))
|
HOST_DOCKER_REPOSITORY ?= $(subst -,/,$(subst _,/,$(HOST_COMPOSE_PROJECT_NAME)))
|
||||||
HOST_DOCKER_VOLUME ?= $(HOST_COMPOSE_PROJECT_NAME)
|
HOST_DOCKER_VOLUME ?= $(HOST_COMPOSE_PROJECT_NAME)
|
||||||
HOST_GID ?= 100
|
HOST_GID ?= $(HOST_UID)
|
||||||
HOST_UID ?= 123
|
HOST_UID ?= 123
|
||||||
HOST_STACK ?= $(filter host,$(firstword $(subst /, ,$(STACK))))
|
HOST_STACK ?= $(filter host,$(firstword $(subst /, ,$(STACK))))
|
||||||
MYOS_STACK ?= $(MYOS)/stack/myos
|
MYOS_STACK ?= $(MYOS)/stack/myos
|
||||||
|
|||||||
+43
-5
@@ -76,7 +76,7 @@ IGNORE_VERBOSE ?= false
|
|||||||
INSTALL ?= $(RUN) $(SUDO) $(subst &&,&& $(RUN) $(SUDO),$(INSTALL_CMD))
|
INSTALL ?= $(RUN) $(SUDO) $(subst &&,&& $(RUN) $(SUDO),$(INSTALL_CMD))
|
||||||
INSTALL_CMDS ?= APK_INSTALL APT_INSTALL
|
INSTALL_CMDS ?= APK_INSTALL APT_INSTALL
|
||||||
$(foreach cmd,$(INSTALL_CMDS),$(if $(CMD_$(cmd)),$(eval INSTALL_CMD ?= $(CMD_$(cmd)))))
|
$(foreach cmd,$(INSTALL_CMDS),$(if $(CMD_$(cmd)),$(eval INSTALL_CMD ?= $(CMD_$(cmd)))))
|
||||||
LOG_LEVEL ?= $(if $(DEBUG),debug,$(if $(VERBOSE),info,error))
|
LOG_LEVEL := $(or $(if $(DEBUG),debug),$(if $(VERBOSE),info),error)
|
||||||
MAIL ?= $(GIT_AUTHOR_EMAIL)
|
MAIL ?= $(GIT_AUTHOR_EMAIL)
|
||||||
MAKE_ARGS = $(foreach var,$(MAKE_VARS),$(if $($(var)),$(var)='$($(var))'))
|
MAKE_ARGS = $(foreach var,$(MAKE_VARS),$(if $($(var)),$(var)='$($(var))'))
|
||||||
MAKE_SUBDIRS ?= $(if $(filter myos,$(MYOS)),monorepo,$(if $(APP),apps $(foreach type,$(APP_LOAD),$(if $(wildcard $(MAKE_DIR)/apps/$(type)),apps/$(type)))))
|
MAKE_SUBDIRS ?= $(if $(filter myos,$(MYOS)),monorepo,$(if $(APP),apps $(foreach type,$(APP_LOAD),$(if $(wildcard $(MAKE_DIR)/apps/$(type)),apps/$(type)))))
|
||||||
@@ -90,7 +90,7 @@ MAKE_OLDFILE ?= $@
|
|||||||
MAKE_TARGETS ?= $(filter-out $(.VARIABLES),$(shell $(MAKE) -qp 2>/dev/null |awk -F':' '/^[a-zA-Z0-9][^$$\#\/\t=]*:([^=]|$$)/ {print $$1}' 2>/dev/null |sort -u))
|
MAKE_TARGETS ?= $(filter-out $(.VARIABLES),$(shell $(MAKE) -qp 2>/dev/null |awk -F':' '/^[a-zA-Z0-9][^$$\#\/\t=]*:([^=]|$$)/ {print $$1}' 2>/dev/null |sort -u))
|
||||||
MAKE_UNIXTIME_START := $(shell date -u +'%s' 2>/dev/null)
|
MAKE_UNIXTIME_START := $(shell date -u +'%s' 2>/dev/null)
|
||||||
MAKE_UNIXTIME_CURRENT = $(shell date -u "+%s" 2>/dev/null)
|
MAKE_UNIXTIME_CURRENT = $(shell date -u "+%s" 2>/dev/null)
|
||||||
MAKE_VARS := ENV COMPOSE_FILE DOCKER_COMPOSE DOCKER_MACHINE DOCKER_SERVICES DOCKER_SYSTEM
|
MAKE_VARS := ENV COMPOSE_FILE DOCKER_COMPOSE DOCKER_SERVICES
|
||||||
MAKECMDARGS ?= apps-install install-app
|
MAKECMDARGS ?= apps-install install-app
|
||||||
MONOREPO ?= $(if $(filter myos,$(MYOS)),$(notdir $(CURDIR)),$(if $(APP),$(notdir $(realpath $(CURDIR)/..))))
|
MONOREPO ?= $(if $(filter myos,$(MYOS)),$(notdir $(CURDIR)),$(if $(APP),$(notdir $(realpath $(CURDIR)/..))))
|
||||||
MONOREPO_DIR ?= $(if $(MONOREPO),$(if $(filter myos,$(MYOS)),$(realpath $(CURDIR)),$(if $(APP),$(realpath $(CURDIR)/..))))
|
MONOREPO_DIR ?= $(if $(MONOREPO),$(if $(filter myos,$(MYOS)),$(realpath $(CURDIR)),$(if $(APP),$(realpath $(CURDIR)/..))))
|
||||||
@@ -136,6 +136,11 @@ MACHINE ?= $(shell uname -m 2>/dev/null)
|
|||||||
|
|
||||||
ifeq ($(SYSTEM),Darwin)
|
ifeq ($(SYSTEM),Darwin)
|
||||||
SED_SUFFIX := ''
|
SED_SUFFIX := ''
|
||||||
|
STAT_FORMAT_ARG := -f
|
||||||
|
STAT_FORMAT_FILE := '%a %N'
|
||||||
|
else
|
||||||
|
STAT_FORMAT_ARG := -c
|
||||||
|
STAT_FORMAT_FILE := '%Y %n'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# include .env files
|
# include .env files
|
||||||
@@ -169,6 +174,30 @@ INFO = $(if $(VERBOSE),$(if $(filter-out true,$(IGNORE_VERBOSE)), \
|
|||||||
&& printf '\n' >&$(INFO_FD) \
|
&& printf '\n' >&$(INFO_FD) \
|
||||||
))
|
))
|
||||||
|
|
||||||
|
# macro base64t: Print trimed base64 encoded string from $1
|
||||||
|
base64t = $(shell echo -n '$(1)' |openssl enc -A -base64 |sed 's/+/-/g;s|/|_|g;s/=*$$//;')
|
||||||
|
|
||||||
|
# macro hs256: Print hmac sha256 digest from string $1 with key $2
|
||||||
|
hs256 = $(shell echo -n '$(1)' |openssl dgst -sha256 -binary -hmac '$(2)')
|
||||||
|
b64_hs256 = $(shell echo -n '$(1)' |openssl dgst -sha256 -binary -hmac '$(2)' |base64 |sed 's/+/-/g;s|/|_|g;s/=*$$//;')
|
||||||
|
|
||||||
|
# macro rs256: Print sha256 digest from string $1 signed by key $2
|
||||||
|
rs256 = $(shell echo -n '$(1)' |openssl dgst -sha256 -binary -sign '$(2)')
|
||||||
|
|
||||||
|
JWT_HEADER = {"alg":"HS256","typ":"JWT"}
|
||||||
|
|
||||||
|
# macro JWT: Print Json Web Token for header $1 payload $2 and key $3
|
||||||
|
JWT = $(strip \
|
||||||
|
$(eval header := $(or $(1),$(JWT_HEADER))) \
|
||||||
|
$(eval payload := $(or $(2),$(JWT_PAYLOAD))) \
|
||||||
|
$(eval secret := $(or $(3),$(JWT_SECRET))) \
|
||||||
|
$(eval b64_header := $(call base64t,$(header))) \
|
||||||
|
$(eval b64_payload := $(call base64t,$(payload))) \
|
||||||
|
$(eval b64_signature := $(call base64t,$(call hs256,$(b64_header).$(b64_payload),$(secret)))) \
|
||||||
|
$(eval b64_signature := $(call b64_hs256,$(b64_header).$(b64_payload),$(secret))) \
|
||||||
|
$(if $(DEBUG),$(header) - $(payload) - $(secret) --- ) \
|
||||||
|
$(b64_header).$(b64_payload).$(b64_signature))
|
||||||
|
|
||||||
# macro RESU: Print USER associated to MAIL
|
# macro RESU: Print USER associated to MAIL
|
||||||
RESU = $(strip \
|
RESU = $(strip \
|
||||||
$(if $(findstring @,$(MAIL)), \
|
$(if $(findstring @,$(MAIL)), \
|
||||||
@@ -222,14 +251,17 @@ force = $$(while true; do \
|
|||||||
&& $(RUN) $(1) || sleep 1; done \
|
&& $(RUN) $(1) || sleep 1; done \
|
||||||
)
|
)
|
||||||
|
|
||||||
# macro gid: Return GID of group 1
|
# macro gid: Return GID of group $1
|
||||||
gid = $(shell awk -F':' '$$1 == "$(1)" {print $$3}' /etc/group 2>/dev/null)
|
gid = $(shell awk -F':' '$$1 == "$(1)" {print $$3}' /etc/group 2>/dev/null)
|
||||||
|
|
||||||
|
# macro newenv: Return VAR from file $2 that does not exist in file $1, matching on VAR= part of lines
|
||||||
|
newenv = $(shell awk -F'=' 'NR == FNR {if($$1 !~ /^(\#|$$)/) { A[$$1]; next }} !($$1 in A) {if($$1 !~ /^(\#|$$)/) { print $$1 }}' $(1) $(2) 2>/dev/null)
|
||||||
|
|
||||||
# macro newer: Return the newest file
|
# macro newer: Return the newest file
|
||||||
newer = $(shell stat -c '%Y %n' $(1) $(2) $(if $(DEBUG),,2>/dev/null) |sort -n |tail -n1 |awk '{print $$2}')
|
newer = $(shell stat $(STAT_FORMAT_ARG) $(STAT_FORMAT_FILE) $(1) $(2) $(if $(DEBUG),,2>/dev/null) |sort -n |tail -n1 |awk '{print $$2}')
|
||||||
|
|
||||||
# older newer: Return the oldest file
|
# older newer: Return the oldest file
|
||||||
older = $(shell stat -c '%Y %n' $(1) $(2) $(if $(DEBUG),,2>/dev/null) |sort -n |head -n1 |awk '{print $$2}')
|
older = $(shell stat $(STAT_FORMAT_ARG) $(STAT_FORMAT_FILE) $(1) $(2) $(if $(DEBUG),,2>/dev/null) |sort -n |head -n1 |awk '{print $$2}')
|
||||||
|
|
||||||
# macro pop: Return last word of string 1 according to separator 2
|
# macro pop: Return last word of string 1 according to separator 2
|
||||||
pop = $(patsubst %$(or $(2),/)$(lastword $(subst $(or $(2),/), ,$(1))),%,$(1))
|
pop = $(patsubst %$(or $(2),/)$(lastword $(subst $(or $(2),/), ,$(1))),%,$(1))
|
||||||
@@ -237,6 +269,12 @@ pop = $(patsubst %$(or $(2),/)$(lastword $(subst $(or $(2),/), ,$(1))),%,$(1))
|
|||||||
# macro sed: Run sed script 1 on file 2
|
# macro sed: Run sed script 1 on file 2
|
||||||
sed = $(RUN) sed -i $(SED_SUFFIX) '$(1)' $(2)
|
sed = $(RUN) sed -i $(SED_SUFFIX) '$(1)' $(2)
|
||||||
|
|
||||||
|
# macro verle: Return true when version 1 <= 2
|
||||||
|
verle = [ -n "$(1)" ] && [ "$(1)" = "$(shell echo -e "$(1)\n$(2)" |sort -V |head -n1)" ]
|
||||||
|
|
||||||
|
# macro verlt: Return true when version 1 < 2
|
||||||
|
verlt = [ "$(1)" = "$(2)" ] && return 1 || $(call verlte,$(1),$(2))
|
||||||
|
|
||||||
# function conf: Extract variable=value line from configuration files
|
# function conf: Extract variable=value line from configuration files
|
||||||
## it prints the line with variable 3 definition from block 2 in file 1
|
## it prints the line with variable 3 definition from block 2 in file 1
|
||||||
define conf
|
define conf
|
||||||
|
|||||||
+8
-6
@@ -11,7 +11,7 @@
|
|||||||
## it removes file .env
|
## it removes file .env
|
||||||
.PHONY: .env-clean
|
.PHONY: .env-clean
|
||||||
.env-clean:
|
.env-clean:
|
||||||
$(RUN) rm -$(if $(FORCE),f,i) .env || true
|
$(RUN) rm -$(if $(FORCE),f,i) $(ENV_FILE) || true
|
||||||
|
|
||||||
# target .env-update: Update file ENV_FILE
|
# target .env-update: Update file ENV_FILE
|
||||||
## it updates file ENV_FILE with missing values from file ENV_DIST
|
## it updates file ENV_FILE with missing values from file ENV_DIST
|
||||||
@@ -51,8 +51,8 @@ define .env
|
|||||||
$(eval env_file:=$(or $(1),.env))
|
$(eval env_file:=$(or $(1),.env))
|
||||||
$(eval env_dists:=$(wildcard $(or $(2),$(env_file).dist)))
|
$(eval env_dists:=$(wildcard $(or $(2),$(env_file).dist)))
|
||||||
$(eval env_over:=$(wildcard $(or $(3),$(env_file).$(ENV))))
|
$(eval env_over:=$(wildcard $(or $(3),$(env_file).$(ENV))))
|
||||||
$(if $(FORCE)$(filter $(env_file),$(call newer,$(env_file) $(env_dists) $(env_over))),
|
$(if $(FORCE)$(call newenv,$(env_file),$(env_dists) $(env_over))$(if $(wildcard $(env_file)),,FORCE),
|
||||||
,$(foreach env_dist,$(env_dists),$(call .env_update)))
|
$(foreach env_dist,$(env_dists),$(call .env_update)))
|
||||||
endef
|
endef
|
||||||
|
|
||||||
# function .env_update: Update .env file with values from .env.dist
|
# function .env_update: Update .env file with values from .env.dist
|
||||||
@@ -74,10 +74,11 @@ endef
|
|||||||
# create a new (empty if ENV_RESET is true) environment with env.args
|
# create a new (empty if ENV_RESET is true) environment with env.args
|
||||||
# read environment variables and keep only those existing in .env.dist
|
# read environment variables and keep only those existing in .env.dist
|
||||||
# add .env overrides variables definition
|
# add .env overrides variables definition
|
||||||
# add .env.dist variables definition
|
# add .env.dist variables definition when variable value is not the variable name itself
|
||||||
# remove empty lines or comments
|
# remove empty lines or comments
|
||||||
# remove duplicate variables
|
# remove duplicate variables
|
||||||
# replace variables in stdin with their value from the new environment
|
# replace variables in stdin with their value from the new environment
|
||||||
|
# eval and replace commands in stdin with their result
|
||||||
# remove residual empty lines or comments
|
# remove residual empty lines or comments
|
||||||
# sort alphabetically
|
# sort alphabetically
|
||||||
# add variables definition to the .env file
|
# add variables definition to the .env file
|
||||||
@@ -95,10 +96,11 @@ define .env_update
|
|||||||
env $(env_reset) $(env.args) \
|
env $(env_reset) $(env.args) \
|
||||||
$$(env |awk -F '=' 'NR == FNR { if($$1 !~ /^(#|$$)/) { A[$$1]; next } } ($$1 in A)' $(env_dist) - \
|
$$(env |awk -F '=' 'NR == FNR { if($$1 !~ /^(#|$$)/) { A[$$1]; next } } ($$1 in A)' $(env_dist) - \
|
||||||
|cat - $(env_over) \
|
|cat - $(env_over) \
|
||||||
|cat - $(env_dist) \
|
|cat - $(env_dist) |awk -F '=' '{ if(match($$0,"[$$]{[^}]*}")) {var=substr($$0,RSTART+2,RLENGTH-3);if(var!=$$1) print} else print}' \
|
||||||
|sed -e /^$$/d -e /^#/d \
|
|sed -e /^$$/d -e /^#/d \
|
||||||
|awk -F '=' '!seen[$$1]++') \
|
|awk -F '=' '!seen[$$1]++') \
|
||||||
awk '{while(match($$0,"[$$]{[^}]*}")) {var=substr($$0,RSTART+2,RLENGTH-3);gsub("[$$]{"var"}",ENVIRON[var])} print}') \
|
awk '{while(match($$0,"[$$]{[^}]*}")) {var=substr($$0,RSTART+2,RLENGTH-3);if("$${"var"}"!=ENVIRON[var]){gsub("[$$]{"var"}",ENVIRON[var])}else{gsub("[$$]{"var"}","")}}; \
|
||||||
|
while(match($$0,"[$$]\\([^\\)]*\\)")) {cmd=substr($$0,RSTART+2,RLENGTH-3);cmd|getline var;gsub("[$$]\\([^\\)]*\\)",var);close(cmd)} print}') \
|
||||||
|sed -e /^$$/d -e /^#/d \
|
|sed -e /^$$/d -e /^#/d \
|
||||||
|sort \
|
|sort \
|
||||||
>> $(env_file) $(if $(VERBOSE)$(DEBUG),,2> /dev/null) ||:;
|
>> $(env_file) $(if $(VERBOSE)$(DEBUG),,2> /dev/null) ||:;
|
||||||
|
|||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
ENV_VARS += USER_IPFS_API_HTTPHEADERS_ACA_ORIGIN USER_IPFS_LETSENCRYPT_HOST USER_IPFS_PATH USER_IPFS_SERVICE_5001_TAGS USER_IPFS_SERVICE_8080_TAGS
|
ENV_VARS += USER_IPFS_API_HTTPHEADERS_ACA_ORIGIN USER_IPFS_LETSENCRYPT_HOST USER_IPFS_PATH USER_IPFS_SERVICE_5001_TAGS USER_IPFS_SERVICE_8080_TAGS
|
||||||
USER_IPFS_API_HTTPHEADERS_ACA_ORIGIN ?= [$(call patsublist,%,"https://%",$(patsubst %/,%,$(USER_IPFS_SERVICE_8080_URIS)))]
|
USER_IPFS_API_HTTPHEADERS_ACA_ORIGIN ?= [$(call patsublist,%,"https://%",$(patsubst %/,%,$(USER_IPFS_SERVICE_8080_URIS)))]
|
||||||
USER_IPFS_LETSENCRYPT_HOST ?= $(subst $(space),$(comma),$(call uriprefix,USER_IPFS,,$(USER_HOST)))
|
USER_IPFS_LETSENCRYPT_HOST ?= $(subst $(space),$(comma),$(call uri,USER_IPFS,,$(USER_HOST)))
|
||||||
USER_IPFS_PATH ?= $(USER_HOME)/.ipfs
|
USER_IPFS_PATH ?= $(USER_HOME)/.ipfs
|
||||||
USER_IPFS_SERVICE_NAME ?= ipfs
|
USER_IPFS_SERVICE_NAME ?= ipfs
|
||||||
USER_IPFS_SERVICE_5001_PATH ?= api/
|
USER_IPFS_SERVICE_5001_PATH ?= api/
|
||||||
@@ -8,8 +8,8 @@ USER_IPFS_SERVICE_5001_TAGS ?= $(or $(USER_IPFS_SERVICE_5001_TAGS_
|
|||||||
USER_IPFS_SERVICE_5001_TAGS_LOCALHOST ?= $(filter %.localhost/$(USER_PATH)$(USER_IPFS_SERVICE_5001_PATH)$(url_suffix),$(call tagprefix,USER_IPFS,5001))
|
USER_IPFS_SERVICE_5001_TAGS_LOCALHOST ?= $(filter %.localhost/$(USER_PATH)$(USER_IPFS_SERVICE_5001_PATH)$(url_suffix),$(call tagprefix,USER_IPFS,5001))
|
||||||
USER_IPFS_SERVICE_5001_TAGS_PROXY_TCP ?= $(call patsublist,%,urlprefix-% proxy=tcp,$(USER_IPFS_SERVICE_PROXY_TCP))
|
USER_IPFS_SERVICE_5001_TAGS_PROXY_TCP ?= $(call patsublist,%,urlprefix-% proxy=tcp,$(USER_IPFS_SERVICE_PROXY_TCP))
|
||||||
USER_IPFS_SERVICE_5001_TAGS_URIS ?= $(strip $(if $(call servicenvs,USER_IPFS,5001,URIS),$(call urlprefix,$(USER_IPFS_SERVICE_5001_PATH),,$(call servicenvs,USER_IPFS,5001,URIS))))
|
USER_IPFS_SERVICE_5001_TAGS_URIS ?= $(strip $(if $(call servicenvs,USER_IPFS,5001,URIS),$(call urlprefix,$(USER_IPFS_SERVICE_5001_PATH),,$(call servicenvs,USER_IPFS,5001,URIS))))
|
||||||
USER_IPFS_SERVICE_5001_URIS ?= $(call uriprefix,USER_IPFS,5001,$(USER_URIS))
|
USER_IPFS_SERVICE_5001_URIS ?= $(call uri,USER_IPFS,5001,$(USER_URIS))
|
||||||
USER_IPFS_SERVICE_8080_OPTS ?= $(patsubst %/,%,$(if $(USER_PATH),strip=/$(USER_PATH)))
|
USER_IPFS_SERVICE_8080_OPTS ?= $(patsubst %/,%,$(if $(USER_PATH),strip=/$(USER_PATH)))
|
||||||
USER_IPFS_SERVICE_8080_PATH ?= ipfs/
|
USER_IPFS_SERVICE_8080_PATH ?= ipfs/
|
||||||
USER_IPFS_SERVICE_8080_TAGS ?= $(call tagprefix,USER_IPFS,8080)
|
USER_IPFS_SERVICE_8080_TAGS ?= $(call tagprefix,USER_IPFS,8080)
|
||||||
USER_IPFS_SERVICE_8080_URIS ?= $(call uriprefix,USER_IPFS,8080,$(USER_URIS))
|
USER_IPFS_SERVICE_8080_URIS ?= $(call uri,USER_IPFS,8080,$(USER_URIS))
|
||||||
|
|||||||
@@ -16,4 +16,4 @@ services:
|
|||||||
- private
|
- private
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- ${DOCKER_SOCKET_LOCATION}:/var/run/docker.sock
|
||||||
|
|||||||
+1
-1
@@ -10,4 +10,4 @@ services:
|
|||||||
- private
|
- private
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- ${DOCKER_SOCKET_LOCATION}:/var/run/docker.sock
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ services:
|
|||||||
volumes_from:
|
volumes_from:
|
||||||
- nginx
|
- nginx
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- ${DOCKER_SOCKET_LOCATION}:/var/run/docker.sock:ro
|
||||||
- acme:/etc/acme.sh
|
- acme:/etc/acme.sh
|
||||||
- certs:/etc/nginx/certs
|
- certs:/etc/nginx/certs
|
||||||
- html:/usr/share/nginx/html
|
- html:/usr/share/nginx/html
|
||||||
|
|||||||
@@ -8,4 +8,4 @@ services:
|
|||||||
image: willfarrell/autoheal:latest
|
image: willfarrell/autoheal:latest
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- ${DOCKER_SOCKET_LOCATION}:/var/run/docker.sock
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
- consul:/consul/data:delegated
|
- consul:/consul/data:delegated
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- ${DOCKER_SOCKET_LOCATION}:/var/run/docker.sock
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
consul:
|
consul:
|
||||||
|
|||||||
+1
-1
@@ -6,5 +6,5 @@ HOST_IPFS_SERVICE_5001_PATH ?= api/
|
|||||||
HOST_IPFS_SERVICE_5001_TAGS ?= $(call tagprefix,HOST_IPFS,5001)
|
HOST_IPFS_SERVICE_5001_TAGS ?= $(call tagprefix,HOST_IPFS,5001)
|
||||||
HOST_IPFS_SERVICE_8080_ENVS ?= host
|
HOST_IPFS_SERVICE_8080_ENVS ?= host
|
||||||
HOST_IPFS_SERVICE_8080_TAGS ?= $(call urlprefix,,,$(HOST_IPFS_SERVICE_8080_URIS) $(call servicenvs,HOST_IPFS,8080,URIS))
|
HOST_IPFS_SERVICE_8080_TAGS ?= $(call urlprefix,,,$(HOST_IPFS_SERVICE_8080_URIS) $(call servicenvs,HOST_IPFS,8080,URIS))
|
||||||
HOST_IPFS_SERVICE_8080_URIS ?= $(call uriprefix,ipfs *.ipns *.ipfs)
|
HOST_IPFS_SERVICE_8080_URIS ?= $(call uri,ipfs *.ipns *.ipfs)
|
||||||
HOST_IPFS_UFW_UPDATE ?= 4001/tcp 4001/udp 8080
|
HOST_IPFS_UFW_UPDATE ?= 4001/tcp 4001/udp 8080
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ services:
|
|||||||
- 443
|
- 443
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
- ${DOCKER_SOCKET_LOCATION}:/tmp/docker.sock:ro
|
||||||
- certs:/etc/nginx/certs
|
- certs:/etc/nginx/certs
|
||||||
- html:/usr/share/nginx/html
|
- html:/usr/share/nginx/html
|
||||||
- htpasswd:/etc/nginx/htpasswd
|
- htpasswd:/etc/nginx/htpasswd
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ services:
|
|||||||
- 9000
|
- 9000
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- ${DOCKER_SOCKET_LOCATION}:/var/run/docker.sock
|
||||||
- portainer:/data
|
- portainer:/data
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -25,4 +25,4 @@ services:
|
|||||||
network_mode: host
|
network_mode: host
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/tmp/docker.sock
|
- ${DOCKER_SOCKET_LOCATION}:/tmp/docker.sock
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ IPFS_SERVICE_5001_PATH ?= api/
|
|||||||
IPFS_SERVICE_5001_TAGS ?= $(call tagprefix,ipfs,5001)
|
IPFS_SERVICE_5001_TAGS ?= $(call tagprefix,ipfs,5001)
|
||||||
IPFS_SERVICE_8080_CHECK_HTTP ?= /ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn
|
IPFS_SERVICE_8080_CHECK_HTTP ?= /ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn
|
||||||
IPFS_SERVICE_8080_TAGS ?= $(call urlprefix,,,$(IPFS_SERVICE_8080_URIS))
|
IPFS_SERVICE_8080_TAGS ?= $(call urlprefix,,,$(IPFS_SERVICE_8080_URIS))
|
||||||
IPFS_SERVICE_8080_URIS ?= $(call uriprefix,ipfs *.ipns)
|
IPFS_SERVICE_8080_URIS ?= $(call uri,ipfs *.ipns *.ipfs)
|
||||||
IPFS_VERSION ?= 0.32.1
|
IPFS_VERSION ?= 0.32.1
|
||||||
|
|
||||||
.PHONY: bootstrap-stack-ipfs
|
.PHONY: bootstrap-stack-ipfs
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
networks:
|
networks:
|
||||||
|
default:
|
||||||
|
name: ${DOCKER_NETWORK_DEFAULT:-_default}
|
||||||
private:
|
private:
|
||||||
external: true
|
external: true
|
||||||
name: ${DOCKER_NETWORK_PRIVATE:-docker}
|
name: ${DOCKER_NETWORK_PRIVATE:-docker}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ services:
|
|||||||
- 443
|
- 443
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
- ${DOCKER_SOCKET_LOCATION}:/tmp/docker.sock:ro
|
||||||
- certs:/etc/nginx/certs
|
- certs:/etc/nginx/certs
|
||||||
- html:/usr/share/nginx/html
|
- html:/usr/share/nginx/html
|
||||||
- log:/var/log/nginx
|
- log:/var/log/nginx
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ services:
|
|||||||
- 9000
|
- 9000
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- ${DOCKER_SOCKET_LOCATION}:/var/run/docker.sock
|
||||||
- portainer:/data
|
- portainer:/data
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
ENV_VARS += PROMETHEUS_BLACKBOX_PRIMARY_TARGETS PROMETHEUS_BLACKBOX_SECONDARY_TARGETS PROMETHEUS_SERVICE_9090_TAGS
|
ENV_VARS += PROMETHEUS_BLACKBOX_PRIMARY_TARGETS PROMETHEUS_BLACKBOX_SECONDARY_TARGETS PROMETHEUS_SERVICE_9090_TAGS
|
||||||
PROMETHEUS_BLACKBOX_PRIMARY_TARGETS ?= $(patsubst %,https://%,$(DOMAIN))
|
PROMETHEUS_BLACKBOX_PRIMARY_TARGETS ?= $(patsubst %,https://%,$(DOMAIN))
|
||||||
PROMETHEUS_BLACKBOX_SECONDARY_TARGETS ?= $(patsubst %,https://%,$(APP_URIS))
|
PROMETHEUS_BLACKBOX_SECONDARY_TARGETS ?= $(patsubst %,https://%,$(APP_URI))
|
||||||
PROMETHEUS_SERVICE_9090_NAME ?= prometheus
|
PROMETHEUS_SERVICE_9090_NAME ?= prometheus
|
||||||
PROMETHEUS_SERVICE_9090_TAGS ?= $(call tagprefix,prometheus,9090)
|
PROMETHEUS_SERVICE_9090_TAGS ?= $(call tagprefix,prometheus,9090)
|
||||||
|
|||||||
@@ -20,4 +20,4 @@ services:
|
|||||||
- 514
|
- 514
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/tmp/docker.sock
|
- ${DOCKER_SOCKET_LOCATION}:/tmp/docker.sock
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
DOCKER_SOCKET_LOCATION=/var/run/docker.sock
|
||||||
|
SUPABASE_DASHBOARD_PASSWORD=$(head -c11 /dev/random |base64 |sed 's/+/-/g;s/\//_/g;s/=*$//;')
|
||||||
|
SUPABASE_JWT_IAT=${SUPABASE_JWT_IAT}
|
||||||
|
# JWT_SECRET: 40 chars
|
||||||
|
SUPABASE_JWT_SECRET=$(head -c30 /dev/random |base64 |sed 's/+/-/g;s/\//_/g;s/=*$//;')
|
||||||
|
SUPABASE_LOGFLARE_API_KEY=$(head -c42 /dev/random |base64 |sed 's/+/-/g;s/\//_/g;s/=*$//;')
|
||||||
|
# DB_ENC_KEY: 16 chars
|
||||||
|
SUPABASE_POSTGRES_DB_ENC_KEY=$(head -c12 /dev/random |base64 |sed 's/+/-/g;s/\//_/g;s/=*$//;')
|
||||||
|
SUPABASE_POSTGRES_PASSWORD=$(head -c42 /dev/random |base64 |sed 's/+/-/g;s/\//_/g;s/=*$//;')
|
||||||
|
# SECRET_KEY_BASE: 64 chars
|
||||||
|
SUPABASE_SECRET_KEY_BASE=$(head -c48 /dev/random |base64 |sed 's/+/-/g;s/\//_/g;s/=*$//;')
|
||||||
|
# VAULT_ENC_KEY: 32 chars
|
||||||
|
SUPABASE_VAULT_ENC_KEY=$(head -c24 /dev/random |base64 |sed 's/+/-/g;s/\//_/g;s/=*$//;')
|
||||||
|
|
||||||
|
## General
|
||||||
|
# SUPABASE_SITE_URL=http://localhost:3000
|
||||||
|
# SUPABASE_ADDITIONAL_REDIRECT_URLS=
|
||||||
|
# SUPABASE_JWT_EXPIRY=3600
|
||||||
|
# SUPABASE_DISABLE_SIGNUP=false
|
||||||
|
# SUPABASE_API_EXTERNAL_URL=http://kong:8000
|
||||||
|
# SUPABASE_POSTGRES_HOST=supabase-db
|
||||||
|
# SUPABASE_POSTGRES_DB=postgres
|
||||||
|
# SUPABASE_POSTGRES_PORT=5432
|
||||||
|
# SUPABASE_POSTGRES_USER=postgres
|
||||||
|
# SUPABASE_POSTGRES_PORT=5432
|
||||||
|
# SUPABASE_POOLER_PROXY_PORT_TRANSACTION=6543
|
||||||
|
# SUPABASE_POOLER_DEFAULT_POOL_SIZE=20
|
||||||
|
# SUPABASE_POOLER_MAX_CLIENT_CONN=100
|
||||||
|
# SUPABASE_POOLER_TENANT_ID=your-tenant-id
|
||||||
|
# SUPABASE_KONG_HTTP_PORT=8000
|
||||||
|
# SUPABASE_KONG_HTTPS_PORT=8443
|
||||||
|
## API - Configuration for PostgREST.
|
||||||
|
# SUPABASE_PGRST_DB_SCHEMAS=public,storage,graphql_public
|
||||||
|
## Auth - Configuration for the GoTrue authentication server.
|
||||||
|
# SUPABASE_GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_URI=pg-functions://postgres/public/mfa_verification_attempt
|
||||||
|
# SUPABASE_GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_SECRETS=<standard-base64-secret>
|
||||||
|
# SUPABASE_GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_URI=pg-functions://postgres/public/mfa_verification_attempt
|
||||||
|
# SUPABASE_GOTRUE_HOOK_SEND_SMS_URI=pg-functions://postgres/public/custom_access_token_hook
|
||||||
|
# SUPABASE_GOTRUE_HOOK_SEND_SMS_SECRETS=v1,whsec_VGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgc2hvcnRlciBCYXNlNjQgc3RyaW5n
|
||||||
|
# SUPABASE_GOTRUE_HOOK_SEND_EMAIL_URI=http://host.docker.internal:54321/functions/v1/email_sender
|
||||||
|
# SUPABASE_GOTRUE_HOOK_SEND_EMAIL_SECRETS=v1,whsec_VGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgc2hvcnRlciBCYXNlNjQgc3RyaW5n
|
||||||
|
## Mailer Config
|
||||||
|
# SUPABASE_MAILER_URLPATHS_CONFIRMATION="/auth/v1/verify"
|
||||||
|
# SUPABASE_MAILER_URLPATHS_INVITE="/auth/v1/verify"
|
||||||
|
# SUPABASE_MAILER_URLPATHS_RECOVERY="/auth/v1/verify"
|
||||||
|
# SUPABASE_MAILER_URLPATHS_EMAIL_CHANGE="/auth/v1/verify"
|
||||||
|
## Email auth
|
||||||
|
# SUPABASE_ENABLE_EMAIL_SIGNUP=true
|
||||||
|
# SUPABASE_ENABLE_EMAIL_AUTOCONFIRM=false
|
||||||
|
# SUPABASE_SMTP_ADMIN_EMAIL=admin@${DOMAINNAME}
|
||||||
|
# SUPABASE_SMTP_HOST=mail
|
||||||
|
# SUPABASE_SMTP_PORT=25
|
||||||
|
# SUPABASE_SMTP_USER=
|
||||||
|
# SUPABASE_SMTP_PASS=
|
||||||
|
# SUPABASE_SMTP_SENDER_NAME=supabase@${DOMAINNAME}
|
||||||
|
# SUPABASE_ENABLE_ANONYMOUS_USERS=false
|
||||||
|
## Phone auth
|
||||||
|
# SUPABASE_ENABLE_PHONE_SIGNUP=true
|
||||||
|
# SUPABASE_ENABLE_PHONE_AUTOCONFIRM=true
|
||||||
|
## Studio - Configuration for the Dashboard
|
||||||
|
# SUPABASE_STUDIO_DEFAULT_ORGANIZATION=Default Organization
|
||||||
|
# SUPABASE_STUDIO_DEFAULT_PROJECT=Default Project
|
||||||
|
## replace if you intend to use Studio outside of localhost
|
||||||
|
# SUPABASE_PUBLIC_URL=http://localhost:8000
|
||||||
|
## Enable webp support
|
||||||
|
# SUPABASE_IMGPROXY_ENABLE_WEBP_DETECTION=true
|
||||||
|
## Add your OpenAI API key to enable SQL Editor Assistant
|
||||||
|
# SUPABASE_OPENAI_API_KEY=
|
||||||
|
## Functions - Configuration for Functions
|
||||||
|
# SUPABASE_FUNCTIONS_VERIFY_JWT=false
|
||||||
|
# SUPABASE_GOOGLE_PROJECT_ID=
|
||||||
|
# SUPABASE_GOOGLE_PROJECT_NUMBER=
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
############
|
||||||
|
# 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
|
||||||
|
DASHBOARD_USERNAME=supabase
|
||||||
|
DASHBOARD_PASSWORD=this_password_is_insecure_and_should_be_updated
|
||||||
|
SECRET_KEY_BASE=UpNVntn3cDxHJpq99YMc1T1AQgQpc8kfYTuRgBiYa15BLrx8etQoXz3gZv1/u2oq
|
||||||
|
VAULT_ENC_KEY=your-encryption-key-32-chars-min
|
||||||
|
|
||||||
|
|
||||||
|
############
|
||||||
|
# Database - You can change these to any PostgreSQL database that has logical replication enabled.
|
||||||
|
############
|
||||||
|
|
||||||
|
POSTGRES_HOST=db
|
||||||
|
POSTGRES_DB=postgres
|
||||||
|
POSTGRES_PORT=5432
|
||||||
|
# default user is postgres
|
||||||
|
|
||||||
|
|
||||||
|
############
|
||||||
|
# Supavisor -- Database pooler
|
||||||
|
############
|
||||||
|
POOLER_PROXY_PORT_TRANSACTION=6543
|
||||||
|
POOLER_DEFAULT_POOL_SIZE=20
|
||||||
|
POOLER_MAX_CLIENT_CONN=100
|
||||||
|
POOLER_TENANT_ID=your-tenant-id
|
||||||
|
|
||||||
|
|
||||||
|
############
|
||||||
|
# API Proxy - Configuration for the Kong Reverse proxy.
|
||||||
|
############
|
||||||
|
|
||||||
|
KONG_HTTP_PORT=8000
|
||||||
|
KONG_HTTPS_PORT=8443
|
||||||
|
|
||||||
|
|
||||||
|
############
|
||||||
|
# API - Configuration for PostgREST.
|
||||||
|
############
|
||||||
|
|
||||||
|
PGRST_DB_SCHEMAS=public,storage,graphql_public
|
||||||
|
|
||||||
|
|
||||||
|
############
|
||||||
|
# Auth - Configuration for the GoTrue authentication server.
|
||||||
|
############
|
||||||
|
|
||||||
|
## General
|
||||||
|
SITE_URL=http://localhost:3000
|
||||||
|
ADDITIONAL_REDIRECT_URLS=
|
||||||
|
JWT_EXPIRY=3600
|
||||||
|
DISABLE_SIGNUP=false
|
||||||
|
API_EXTERNAL_URL=http://localhost:8000
|
||||||
|
|
||||||
|
## Mailer Config
|
||||||
|
MAILER_URLPATHS_CONFIRMATION="/auth/v1/verify"
|
||||||
|
MAILER_URLPATHS_INVITE="/auth/v1/verify"
|
||||||
|
MAILER_URLPATHS_RECOVERY="/auth/v1/verify"
|
||||||
|
MAILER_URLPATHS_EMAIL_CHANGE="/auth/v1/verify"
|
||||||
|
|
||||||
|
## Email auth
|
||||||
|
ENABLE_EMAIL_SIGNUP=true
|
||||||
|
ENABLE_EMAIL_AUTOCONFIRM=false
|
||||||
|
SMTP_ADMIN_EMAIL=admin@example.com
|
||||||
|
SMTP_HOST=supabase-mail
|
||||||
|
SMTP_PORT=2500
|
||||||
|
SMTP_USER=fake_mail_user
|
||||||
|
SMTP_PASS=fake_mail_password
|
||||||
|
SMTP_SENDER_NAME=fake_sender
|
||||||
|
ENABLE_ANONYMOUS_USERS=false
|
||||||
|
|
||||||
|
## Phone auth
|
||||||
|
ENABLE_PHONE_SIGNUP=true
|
||||||
|
ENABLE_PHONE_AUTOCONFIRM=true
|
||||||
|
|
||||||
|
|
||||||
|
############
|
||||||
|
# Studio - Configuration for the Dashboard
|
||||||
|
############
|
||||||
|
|
||||||
|
STUDIO_DEFAULT_ORGANIZATION=Default Organization
|
||||||
|
STUDIO_DEFAULT_PROJECT=Default Project
|
||||||
|
|
||||||
|
STUDIO_PORT=3000
|
||||||
|
# replace if you intend to use Studio outside of localhost
|
||||||
|
SUPABASE_PUBLIC_URL=http://localhost:8000
|
||||||
|
|
||||||
|
# Enable webp support
|
||||||
|
IMGPROXY_ENABLE_WEBP_DETECTION=true
|
||||||
|
|
||||||
|
# Add your OpenAI API key to enable SQL Editor Assistant
|
||||||
|
OPENAI_API_KEY=
|
||||||
|
|
||||||
|
|
||||||
|
############
|
||||||
|
# Functions - Configuration for Functions
|
||||||
|
############
|
||||||
|
# NOTE: VERIFY_JWT applies to all functions. Per-function VERIFY_JWT is not supported yet.
|
||||||
|
FUNCTIONS_VERIFY_JWT=false
|
||||||
|
|
||||||
|
|
||||||
|
############
|
||||||
|
# Logs - Configuration for Logflare
|
||||||
|
# Please refer to https://supabase.com/docs/reference/self-hosting-analytics/introduction
|
||||||
|
############
|
||||||
|
|
||||||
|
LOGFLARE_LOGGER_BACKEND_API_KEY=your-super-secret-and-long-logflare-key
|
||||||
|
|
||||||
|
# Change vector.toml sinks to reflect this change
|
||||||
|
LOGFLARE_API_KEY=your-super-secret-and-long-logflare-key
|
||||||
|
|
||||||
|
# Docker socket location - this value will differ depending on your OS
|
||||||
|
DOCKER_SOCKET_LOCATION=/var/run/docker.sock
|
||||||
|
|
||||||
|
# Google Cloud Project details
|
||||||
|
GOOGLE_PROJECT_ID=GOOGLE_PROJECT_ID
|
||||||
|
GOOGLE_PROJECT_NUMBER=GOOGLE_PROJECT_NUMBER
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
volumes/db/data
|
||||||
|
volumes/storage
|
||||||
|
.env
|
||||||
|
test.http
|
||||||
|
docker-compose.override.yml
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Supabase Docker
|
||||||
|
|
||||||
|
This is a minimal Docker Compose setup for self-hosting Supabase. Follow the steps [here](https://supabase.com/docs/guides/hosting/docker) to get started.
|
||||||
@@ -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' );
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
studio:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: studio/Dockerfile
|
||||||
|
target: dev
|
||||||
|
ports:
|
||||||
|
- 8082:8082
|
||||||
|
mail:
|
||||||
|
container_name: supabase-mail
|
||||||
|
image: inbucket/inbucket:3.0.3
|
||||||
|
ports:
|
||||||
|
- '2500:2500' # SMTP
|
||||||
|
- '9000:9000' # web interface
|
||||||
|
- '1100:1100' # POP3
|
||||||
|
auth:
|
||||||
|
environment:
|
||||||
|
- GOTRUE_SMTP_USER=
|
||||||
|
- GOTRUE_SMTP_PASS=
|
||||||
|
meta:
|
||||||
|
ports:
|
||||||
|
- 5555:8080
|
||||||
|
db:
|
||||||
|
restart: 'no'
|
||||||
|
volumes:
|
||||||
|
# Always use a fresh database when developing
|
||||||
|
- /var/lib/postgresql/data
|
||||||
|
# Seed data should be inserted last (alphabetical order)
|
||||||
|
- ./dev/data.sql:/docker-entrypoint-initdb.d/seed.sql
|
||||||
|
storage:
|
||||||
|
volumes:
|
||||||
|
- /var/lib/storage
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
services:
|
||||||
|
|
||||||
|
minio:
|
||||||
|
image: minio/minio
|
||||||
|
ports:
|
||||||
|
- '9000:9000'
|
||||||
|
- '9001:9001'
|
||||||
|
environment:
|
||||||
|
MINIO_ROOT_USER: supa-storage
|
||||||
|
MINIO_ROOT_PASSWORD: secret1234
|
||||||
|
command: server --console-address ":9001" /data
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "curl", "-f", "http://minio:9000/minio/health/live" ]
|
||||||
|
interval: 2s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 5
|
||||||
|
volumes:
|
||||||
|
- ./volumes/storage:/data:z
|
||||||
|
|
||||||
|
minio-createbucket:
|
||||||
|
image: minio/mc
|
||||||
|
depends_on:
|
||||||
|
minio:
|
||||||
|
condition: service_healthy
|
||||||
|
entrypoint: >
|
||||||
|
/bin/sh -c "
|
||||||
|
/usr/bin/mc alias set supa-minio http://minio:9000 supa-storage secret1234;
|
||||||
|
/usr/bin/mc mb supa-minio/stub;
|
||||||
|
exit 0;
|
||||||
|
"
|
||||||
|
|
||||||
|
storage:
|
||||||
|
container_name: supabase-storage
|
||||||
|
image: supabase/storage-api:v1.11.13
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
# Disable this if you are using an external Postgres database
|
||||||
|
condition: service_healthy
|
||||||
|
rest:
|
||||||
|
condition: service_started
|
||||||
|
imgproxy:
|
||||||
|
condition: service_started
|
||||||
|
minio:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"wget",
|
||||||
|
"--no-verbose",
|
||||||
|
"--tries=1",
|
||||||
|
"--spider",
|
||||||
|
"http://localhost:5000/status"
|
||||||
|
]
|
||||||
|
timeout: 5s
|
||||||
|
interval: 5s
|
||||||
|
retries: 3
|
||||||
|
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://supabase_storage_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
|
||||||
|
FILE_SIZE_LIMIT: 52428800
|
||||||
|
STORAGE_BACKEND: s3
|
||||||
|
GLOBAL_S3_BUCKET: stub
|
||||||
|
GLOBAL_S3_ENDPOINT: http://minio:9000
|
||||||
|
GLOBAL_S3_PROTOCOL: http
|
||||||
|
GLOBAL_S3_FORCE_PATH_STYLE: true
|
||||||
|
AWS_ACCESS_KEY_ID: supa-storage
|
||||||
|
AWS_SECRET_ACCESS_KEY: secret1234
|
||||||
|
AWS_DEFAULT_REGION: stub
|
||||||
|
FILE_STORAGE_BACKEND_PATH: /var/lib/storage
|
||||||
|
TENANT_ID: stub
|
||||||
|
# TODO: https://github.com/supabase/storage-api/issues/55
|
||||||
|
REGION: stub
|
||||||
|
ENABLE_IMAGE_TRANSFORMATION: "true"
|
||||||
|
IMGPROXY_URL: http://imgproxy:5001
|
||||||
|
volumes:
|
||||||
|
- ./volumes/storage:/var/lib/storage:z
|
||||||
|
|
||||||
|
imgproxy:
|
||||||
|
container_name: supabase-imgproxy
|
||||||
|
image: darthsim/imgproxy:v3.8.0
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "imgproxy", "health" ]
|
||||||
|
timeout: 5s
|
||||||
|
interval: 5s
|
||||||
|
retries: 3
|
||||||
|
environment:
|
||||||
|
IMGPROXY_BIND: ":5001"
|
||||||
|
IMGPROXY_USE_ETAG: "true"
|
||||||
|
IMGPROXY_ENABLE_WEBP_DETECTION: ${IMGPROXY_ENABLE_WEBP_DETECTION}
|
||||||
@@ -0,0 +1,526 @@
|
|||||||
|
# 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
|
||||||
|
# Reset everything: ./reset.sh
|
||||||
|
|
||||||
|
name: supabase
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
studio:
|
||||||
|
container_name: supabase-studio
|
||||||
|
image: supabase/studio:20250113-83c9420
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"node",
|
||||||
|
"-e",
|
||||||
|
"fetch('http://studio:3000/api/profile').then((r) => {if (r.status !== 200) throw new Error(r.status)})"
|
||||||
|
]
|
||||||
|
timeout: 10s
|
||||||
|
interval: 5s
|
||||||
|
retries: 3
|
||||||
|
depends_on:
|
||||||
|
analytics:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
STUDIO_PG_META_URL: http://meta:8080
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
|
||||||
|
DEFAULT_ORGANIZATION_NAME: ${STUDIO_DEFAULT_ORGANIZATION}
|
||||||
|
DEFAULT_PROJECT_NAME: ${STUDIO_DEFAULT_PROJECT}
|
||||||
|
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
|
||||||
|
|
||||||
|
SUPABASE_URL: http://kong:8000
|
||||||
|
SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL}
|
||||||
|
SUPABASE_ANON_KEY: ${ANON_KEY}
|
||||||
|
SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
|
||||||
|
AUTH_JWT_SECRET: ${JWT_SECRET}
|
||||||
|
|
||||||
|
LOGFLARE_API_KEY: ${LOGFLARE_API_KEY}
|
||||||
|
LOGFLARE_URL: http://analytics:4000
|
||||||
|
NEXT_PUBLIC_ENABLE_LOGS: true
|
||||||
|
# Comment to use Big Query backend for analytics
|
||||||
|
NEXT_ANALYTICS_BACKEND_PROVIDER: postgres
|
||||||
|
# Uncomment to use Big Query backend for analytics
|
||||||
|
# NEXT_ANALYTICS_BACKEND_PROVIDER: bigquery
|
||||||
|
|
||||||
|
kong:
|
||||||
|
container_name: supabase-kong
|
||||||
|
image: kong:2.8.1
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- ${KONG_HTTP_PORT}:8000/tcp
|
||||||
|
- ${KONG_HTTPS_PORT}:8443/tcp
|
||||||
|
volumes:
|
||||||
|
# https://github.com/supabase/supabase/issues/12661
|
||||||
|
- ./volumes/api/kong.yml:/home/kong/temp.yml:ro
|
||||||
|
depends_on:
|
||||||
|
analytics:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
KONG_DATABASE: "off"
|
||||||
|
KONG_DECLARATIVE_CONFIG: /home/kong/kong.yml
|
||||||
|
# https://github.com/supabase/cli/issues/14
|
||||||
|
KONG_DNS_ORDER: LAST,A,CNAME
|
||||||
|
KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth
|
||||||
|
KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k
|
||||||
|
KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k
|
||||||
|
SUPABASE_ANON_KEY: ${ANON_KEY}
|
||||||
|
SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
|
||||||
|
DASHBOARD_USERNAME: ${DASHBOARD_USERNAME}
|
||||||
|
DASHBOARD_PASSWORD: ${DASHBOARD_PASSWORD}
|
||||||
|
# https://unix.stackexchange.com/a/294837
|
||||||
|
entrypoint: bash -c 'eval "echo \"$$(cat ~/temp.yml)\"" > ~/kong.yml && /docker-entrypoint.sh kong docker-start'
|
||||||
|
|
||||||
|
auth:
|
||||||
|
container_name: supabase-auth
|
||||||
|
image: supabase/gotrue:v2.167.0
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"wget",
|
||||||
|
"--no-verbose",
|
||||||
|
"--tries=1",
|
||||||
|
"--spider",
|
||||||
|
"http://localhost:9999/health"
|
||||||
|
]
|
||||||
|
timeout: 5s
|
||||||
|
interval: 5s
|
||||||
|
retries: 3
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
# Disable this if you are using an external Postgres database
|
||||||
|
condition: service_healthy
|
||||||
|
analytics:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
GOTRUE_API_HOST: 0.0.0.0
|
||||||
|
GOTRUE_API_PORT: 9999
|
||||||
|
API_EXTERNAL_URL: ${API_EXTERNAL_URL}
|
||||||
|
|
||||||
|
GOTRUE_DB_DRIVER: postgres
|
||||||
|
GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
|
||||||
|
|
||||||
|
GOTRUE_SITE_URL: ${SITE_URL}
|
||||||
|
GOTRUE_URI_ALLOW_LIST: ${ADDITIONAL_REDIRECT_URLS}
|
||||||
|
GOTRUE_DISABLE_SIGNUP: ${DISABLE_SIGNUP}
|
||||||
|
|
||||||
|
GOTRUE_JWT_ADMIN_ROLES: service_role
|
||||||
|
GOTRUE_JWT_AUD: authenticated
|
||||||
|
GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated
|
||||||
|
GOTRUE_JWT_EXP: ${JWT_EXPIRY}
|
||||||
|
GOTRUE_JWT_SECRET: ${JWT_SECRET}
|
||||||
|
|
||||||
|
GOTRUE_EXTERNAL_EMAIL_ENABLED: ${ENABLE_EMAIL_SIGNUP}
|
||||||
|
GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED: ${ENABLE_ANONYMOUS_USERS}
|
||||||
|
GOTRUE_MAILER_AUTOCONFIRM: ${ENABLE_EMAIL_AUTOCONFIRM}
|
||||||
|
|
||||||
|
# Uncomment to bypass nonce check in ID Token flow. Commonly set to true when using Google Sign In on mobile.
|
||||||
|
# GOTRUE_EXTERNAL_SKIP_NONCE_CHECK: true
|
||||||
|
|
||||||
|
# GOTRUE_MAILER_SECURE_EMAIL_CHANGE_ENABLED: true
|
||||||
|
# GOTRUE_SMTP_MAX_FREQUENCY: 1s
|
||||||
|
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: ${MAILER_URLPATHS_INVITE}
|
||||||
|
GOTRUE_MAILER_URLPATHS_CONFIRMATION: ${MAILER_URLPATHS_CONFIRMATION}
|
||||||
|
GOTRUE_MAILER_URLPATHS_RECOVERY: ${MAILER_URLPATHS_RECOVERY}
|
||||||
|
GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: ${MAILER_URLPATHS_EMAIL_CHANGE}
|
||||||
|
|
||||||
|
GOTRUE_EXTERNAL_PHONE_ENABLED: ${ENABLE_PHONE_SIGNUP}
|
||||||
|
GOTRUE_SMS_AUTOCONFIRM: ${ENABLE_PHONE_AUTOCONFIRM}
|
||||||
|
# Uncomment to enable custom access token hook. Please see: https://supabase.com/docs/guides/auth/auth-hooks for full list of hooks and additional details about custom_access_token_hook
|
||||||
|
|
||||||
|
# GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_ENABLED: "true"
|
||||||
|
# GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_URI: "pg-functions://postgres/public/custom_access_token_hook"
|
||||||
|
# GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_SECRETS: "<standard-base64-secret>"
|
||||||
|
|
||||||
|
# GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_ENABLED: "true"
|
||||||
|
# GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_URI: "pg-functions://postgres/public/mfa_verification_attempt"
|
||||||
|
|
||||||
|
# GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_ENABLED: "true"
|
||||||
|
# GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_URI: "pg-functions://postgres/public/password_verification_attempt"
|
||||||
|
|
||||||
|
# GOTRUE_HOOK_SEND_SMS_ENABLED: "false"
|
||||||
|
# GOTRUE_HOOK_SEND_SMS_URI: "pg-functions://postgres/public/custom_access_token_hook"
|
||||||
|
# GOTRUE_HOOK_SEND_SMS_SECRETS: "v1,whsec_VGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgc2hvcnRlciBCYXNlNjQgc3RyaW5n"
|
||||||
|
|
||||||
|
# GOTRUE_HOOK_SEND_EMAIL_ENABLED: "false"
|
||||||
|
# GOTRUE_HOOK_SEND_EMAIL_URI: "http://host.docker.internal:54321/functions/v1/email_sender"
|
||||||
|
# GOTRUE_HOOK_SEND_EMAIL_SECRETS: "v1,whsec_VGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgc2hvcnRlciBCYXNlNjQgc3RyaW5n"
|
||||||
|
|
||||||
|
rest:
|
||||||
|
container_name: supabase-rest
|
||||||
|
image: postgrest/postgrest:v12.2.0
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
# Disable this if you are using an external Postgres database
|
||||||
|
condition: service_healthy
|
||||||
|
analytics:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
|
||||||
|
PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS}
|
||||||
|
PGRST_DB_ANON_ROLE: anon
|
||||||
|
PGRST_JWT_SECRET: ${JWT_SECRET}
|
||||||
|
PGRST_DB_USE_LEGACY_GUCS: "false"
|
||||||
|
PGRST_APP_SETTINGS_JWT_SECRET: ${JWT_SECRET}
|
||||||
|
PGRST_APP_SETTINGS_JWT_EXP: ${JWT_EXPIRY}
|
||||||
|
command:
|
||||||
|
[
|
||||||
|
"postgrest"
|
||||||
|
]
|
||||||
|
|
||||||
|
realtime:
|
||||||
|
# This container name looks inconsistent but is correct because realtime constructs tenant id by parsing the subdomain
|
||||||
|
container_name: realtime-dev.supabase-realtime
|
||||||
|
image: supabase/realtime:v2.34.7
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
# Disable this if you are using an external Postgres database
|
||||||
|
condition: service_healthy
|
||||||
|
analytics:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"curl",
|
||||||
|
"-sSfL",
|
||||||
|
"--head",
|
||||||
|
"-o",
|
||||||
|
"/dev/null",
|
||||||
|
"-H",
|
||||||
|
"Authorization: Bearer ${ANON_KEY}",
|
||||||
|
"http://localhost:4000/api/tenants/realtime-dev/health"
|
||||||
|
]
|
||||||
|
timeout: 5s
|
||||||
|
interval: 5s
|
||||||
|
retries: 3
|
||||||
|
environment:
|
||||||
|
PORT: 4000
|
||||||
|
DB_HOST: ${POSTGRES_HOST}
|
||||||
|
DB_PORT: ${POSTGRES_PORT}
|
||||||
|
DB_USER: supabase_admin
|
||||||
|
DB_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
DB_NAME: ${POSTGRES_DB}
|
||||||
|
DB_AFTER_CONNECT_QUERY: 'SET search_path TO _realtime'
|
||||||
|
DB_ENC_KEY: supabaserealtime
|
||||||
|
API_JWT_SECRET: ${JWT_SECRET}
|
||||||
|
SECRET_KEY_BASE: ${SECRET_KEY_BASE}
|
||||||
|
ERL_AFLAGS: -proto_dist inet_tcp
|
||||||
|
DNS_NODES: "''"
|
||||||
|
RLIMIT_NOFILE: "10000"
|
||||||
|
APP_NAME: realtime
|
||||||
|
SEED_SELF_HOST: true
|
||||||
|
RUN_JANITOR: true
|
||||||
|
|
||||||
|
# To use S3 backed storage: docker compose -f docker-compose.yml -f docker-compose.s3.yml up
|
||||||
|
storage:
|
||||||
|
container_name: supabase-storage
|
||||||
|
image: supabase/storage-api:v1.14.5
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./volumes/storage:/var/lib/storage:z
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"wget",
|
||||||
|
"--no-verbose",
|
||||||
|
"--tries=1",
|
||||||
|
"--spider",
|
||||||
|
"http://storage:5000/status"
|
||||||
|
]
|
||||||
|
timeout: 5s
|
||||||
|
interval: 5s
|
||||||
|
retries: 3
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
# Disable this if you are using an external Postgres database
|
||||||
|
condition: service_healthy
|
||||||
|
rest:
|
||||||
|
condition: service_started
|
||||||
|
imgproxy:
|
||||||
|
condition: service_started
|
||||||
|
environment:
|
||||||
|
ANON_KEY: ${ANON_KEY}
|
||||||
|
SERVICE_KEY: ${SERVICE_ROLE_KEY}
|
||||||
|
POSTGREST_URL: http://rest:3000
|
||||||
|
PGRST_JWT_SECRET: ${JWT_SECRET}
|
||||||
|
DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
|
||||||
|
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
|
||||||
|
ENABLE_IMAGE_TRANSFORMATION: "true"
|
||||||
|
IMGPROXY_URL: http://imgproxy:5001
|
||||||
|
|
||||||
|
imgproxy:
|
||||||
|
container_name: supabase-imgproxy
|
||||||
|
image: darthsim/imgproxy:v3.8.0
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./volumes/storage:/var/lib/storage:z
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"imgproxy",
|
||||||
|
"health"
|
||||||
|
]
|
||||||
|
timeout: 5s
|
||||||
|
interval: 5s
|
||||||
|
retries: 3
|
||||||
|
environment:
|
||||||
|
IMGPROXY_BIND: ":5001"
|
||||||
|
IMGPROXY_LOCAL_FILESYSTEM_ROOT: /
|
||||||
|
IMGPROXY_USE_ETAG: "true"
|
||||||
|
IMGPROXY_ENABLE_WEBP_DETECTION: ${IMGPROXY_ENABLE_WEBP_DETECTION}
|
||||||
|
|
||||||
|
meta:
|
||||||
|
container_name: supabase-meta
|
||||||
|
image: supabase/postgres-meta:v0.84.2
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
# Disable this if you are using an external Postgres database
|
||||||
|
condition: service_healthy
|
||||||
|
analytics:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
PG_META_PORT: 8080
|
||||||
|
PG_META_DB_HOST: ${POSTGRES_HOST}
|
||||||
|
PG_META_DB_PORT: ${POSTGRES_PORT}
|
||||||
|
PG_META_DB_NAME: ${POSTGRES_DB}
|
||||||
|
PG_META_DB_USER: supabase_admin
|
||||||
|
PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
|
||||||
|
functions:
|
||||||
|
container_name: supabase-edge-functions
|
||||||
|
image: supabase/edge-runtime:v1.66.5
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./volumes/functions:/home/deno/functions:Z
|
||||||
|
depends_on:
|
||||||
|
analytics:
|
||||||
|
condition: service_healthy
|
||||||
|
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_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
|
||||||
|
# TODO: Allow configuring VERIFY_JWT per function. This PR might help: https://github.com/supabase/cli/pull/786
|
||||||
|
VERIFY_JWT: "${FUNCTIONS_VERIFY_JWT}"
|
||||||
|
command:
|
||||||
|
[
|
||||||
|
"start",
|
||||||
|
"--main-service",
|
||||||
|
"/home/deno/functions/main"
|
||||||
|
]
|
||||||
|
|
||||||
|
analytics:
|
||||||
|
container_name: supabase-analytics
|
||||||
|
image: supabase/logflare:1.4.0
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- 4000:4000
|
||||||
|
# Uncomment to use Big Query backend for analytics
|
||||||
|
# volumes:
|
||||||
|
# - type: bind
|
||||||
|
# source: ${PWD}/gcloud.json
|
||||||
|
# target: /opt/app/rel/logflare/bin/gcloud.json
|
||||||
|
# read_only: true
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"curl",
|
||||||
|
"http://localhost:4000/health"
|
||||||
|
]
|
||||||
|
timeout: 5s
|
||||||
|
interval: 5s
|
||||||
|
retries: 10
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
# Disable this if you are using an external Postgres database
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
LOGFLARE_NODE_HOST: 127.0.0.1
|
||||||
|
DB_USERNAME: supabase_admin
|
||||||
|
DB_DATABASE: _supabase
|
||||||
|
DB_HOSTNAME: ${POSTGRES_HOST}
|
||||||
|
DB_PORT: ${POSTGRES_PORT}
|
||||||
|
DB_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
DB_SCHEMA: _analytics
|
||||||
|
LOGFLARE_API_KEY: ${LOGFLARE_API_KEY}
|
||||||
|
LOGFLARE_SINGLE_TENANT: true
|
||||||
|
LOGFLARE_SUPABASE_MODE: true
|
||||||
|
LOGFLARE_MIN_CLUSTER_SIZE: 1
|
||||||
|
|
||||||
|
# Comment variables to use Big Query backend for analytics
|
||||||
|
POSTGRES_BACKEND_URL: postgresql://supabase_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/_supabase
|
||||||
|
POSTGRES_BACKEND_SCHEMA: _analytics
|
||||||
|
LOGFLARE_FEATURE_FLAG_OVERRIDE: multibackend=true
|
||||||
|
# Uncomment to use Big Query backend for analytics
|
||||||
|
# GOOGLE_PROJECT_ID: ${GOOGLE_PROJECT_ID}
|
||||||
|
# GOOGLE_PROJECT_NUMBER: ${GOOGLE_PROJECT_NUMBER}
|
||||||
|
|
||||||
|
# Comment out everything below this point if you are using an external Postgres database
|
||||||
|
db:
|
||||||
|
container_name: supabase-db
|
||||||
|
image: supabase/postgres:15.8.1.020
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./volumes/db/realtime.sql:/docker-entrypoint-initdb.d/migrations/99-realtime.sql:Z
|
||||||
|
# Must be superuser to create event trigger
|
||||||
|
- ./volumes/db/webhooks.sql:/docker-entrypoint-initdb.d/init-scripts/98-webhooks.sql:Z
|
||||||
|
# Must be superuser to alter reserved role
|
||||||
|
- ./volumes/db/roles.sql:/docker-entrypoint-initdb.d/init-scripts/99-roles.sql:Z
|
||||||
|
# Initialize the database settings with JWT_SECRET and JWT_EXP
|
||||||
|
- ./volumes/db/jwt.sql:/docker-entrypoint-initdb.d/init-scripts/99-jwt.sql:Z
|
||||||
|
# PGDATA directory is persisted between restarts
|
||||||
|
- ./volumes/db/data:/var/lib/postgresql/data:Z
|
||||||
|
# Changes required for internal supabase data such as _analytics
|
||||||
|
- ./volumes/db/_supabase.sql:/docker-entrypoint-initdb.d/migrations/97-_supabase.sql:Z
|
||||||
|
# Changes required for Analytics support
|
||||||
|
- ./volumes/db/logs.sql:/docker-entrypoint-initdb.d/migrations/99-logs.sql:Z
|
||||||
|
# Changes required for Pooler support
|
||||||
|
- ./volumes/db/pooler.sql:/docker-entrypoint-initdb.d/migrations/99-pooler.sql:Z
|
||||||
|
# Use named volume to persist pgsodium decryption key between restarts
|
||||||
|
- db-config:/etc/postgresql-custom
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"pg_isready",
|
||||||
|
"-U",
|
||||||
|
"postgres",
|
||||||
|
"-h",
|
||||||
|
"localhost"
|
||||||
|
]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
depends_on:
|
||||||
|
vector:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
POSTGRES_HOST: /var/run/postgresql
|
||||||
|
PGPORT: ${POSTGRES_PORT}
|
||||||
|
POSTGRES_PORT: ${POSTGRES_PORT}
|
||||||
|
PGPASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
PGDATABASE: ${POSTGRES_DB}
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB}
|
||||||
|
JWT_SECRET: ${JWT_SECRET}
|
||||||
|
JWT_EXP: ${JWT_EXPIRY}
|
||||||
|
command:
|
||||||
|
[
|
||||||
|
"postgres",
|
||||||
|
"-c",
|
||||||
|
"config_file=/etc/postgresql/postgresql.conf",
|
||||||
|
"-c",
|
||||||
|
"log_min_messages=fatal" # prevents Realtime polling queries from appearing in logs
|
||||||
|
]
|
||||||
|
|
||||||
|
vector:
|
||||||
|
container_name: supabase-vector
|
||||||
|
image: timberio/vector:0.28.1-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./volumes/logs/vector.yml:/etc/vector/vector.yml:ro
|
||||||
|
- ${DOCKER_SOCKET_LOCATION}:/var/run/docker.sock:ro
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"wget",
|
||||||
|
"--no-verbose",
|
||||||
|
"--tries=1",
|
||||||
|
"--spider",
|
||||||
|
"http://vector:9001/health"
|
||||||
|
]
|
||||||
|
timeout: 5s
|
||||||
|
interval: 5s
|
||||||
|
retries: 3
|
||||||
|
environment:
|
||||||
|
LOGFLARE_API_KEY: ${LOGFLARE_API_KEY}
|
||||||
|
command:
|
||||||
|
[
|
||||||
|
"--config",
|
||||||
|
"/etc/vector/vector.yml"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Update the DATABASE_URL if you are using an external Postgres database
|
||||||
|
supavisor:
|
||||||
|
container_name: supabase-pooler
|
||||||
|
image: supabase/supavisor:1.1.56
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- ${POSTGRES_PORT}:5432
|
||||||
|
- ${POOLER_PROXY_PORT_TRANSACTION}:6543
|
||||||
|
volumes:
|
||||||
|
- ./volumes/pooler/pooler.exs:/etc/pooler/pooler.exs:ro
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"curl",
|
||||||
|
"-sSfL",
|
||||||
|
"--head",
|
||||||
|
"-o",
|
||||||
|
"/dev/null",
|
||||||
|
"http://127.0.0.1:4000/api/health"
|
||||||
|
]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
analytics:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
PORT: 4000
|
||||||
|
POSTGRES_PORT: ${POSTGRES_PORT}
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
DATABASE_URL: ecto://supabase_admin:${POSTGRES_PASSWORD}@db:${POSTGRES_PORT}/_supabase
|
||||||
|
CLUSTER_POSTGRES: true
|
||||||
|
SECRET_KEY_BASE: ${SECRET_KEY_BASE}
|
||||||
|
VAULT_ENC_KEY: ${VAULT_ENC_KEY}
|
||||||
|
API_JWT_SECRET: ${JWT_SECRET}
|
||||||
|
METRICS_JWT_SECRET: ${JWT_SECRET}
|
||||||
|
REGION: local
|
||||||
|
ERL_AFLAGS: -proto_dist inet_tcp
|
||||||
|
POOLER_TENANT_ID: ${POOLER_TENANT_ID}
|
||||||
|
POOLER_DEFAULT_POOL_SIZE: ${POOLER_DEFAULT_POOL_SIZE}
|
||||||
|
POOLER_MAX_CLIENT_CONN: ${POOLER_MAX_CLIENT_CONN}
|
||||||
|
POOLER_POOL_MODE: transaction
|
||||||
|
command:
|
||||||
|
[
|
||||||
|
"/bin/sh",
|
||||||
|
"-c",
|
||||||
|
"/app/bin/migrate && /app/bin/supavisor eval \"$$(cat /etc/pooler/pooler.exs)\" && /app/bin/server"
|
||||||
|
]
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db-config:
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
services:
|
||||||
|
analytics:
|
||||||
|
labels:
|
||||||
|
- SERVICE_4000_CHECK_HTTP=/health
|
||||||
|
- SERVICE_4000_NAME=${COMPOSE_SERVICE_NAME}-analytics-4000
|
||||||
|
auth:
|
||||||
|
labels:
|
||||||
|
- SERVICE_9999_CHECK_HTTP=/health
|
||||||
|
- SERVICE_9999_NAME=${COMPOSE_SERVICE_NAME}-auth-9999
|
||||||
|
db:
|
||||||
|
labels:
|
||||||
|
- SERVICE_5432_CHECK_TCP=true
|
||||||
|
- SERVICE_5432_NAME=${COMPOSE_SERVICE_NAME}-db-5432
|
||||||
|
functions:
|
||||||
|
labels:
|
||||||
|
- SERVICE_9000_CHECK_HTTP=/functions/v1/hello
|
||||||
|
- SERVICE_9000_NAME=${COMPOSE_SERVICE_NAME}-functions-9000
|
||||||
|
imgproxy:
|
||||||
|
labels:
|
||||||
|
- SERVICE_5001_CHECK_HTTP=/health
|
||||||
|
- SERVICE_5001_NAME=${COMPOSE_SERVICE_NAME}-imgproxy-5001
|
||||||
|
- SERVICE_8080_IGNORE=true
|
||||||
|
kong:
|
||||||
|
labels:
|
||||||
|
- SERVICE_8000_CHECK_TCP=true
|
||||||
|
- SERVICE_8000_NAME=${COMPOSE_SERVICE_NAME}-kong-8000
|
||||||
|
- SERVICE_8000_TAGS=${SUPABASE_KONG_SERVICE_8000_TAGS:-urlprefix-supabase.localhost/*}
|
||||||
|
- SERVICE_8001_IGNORE=true
|
||||||
|
- SERVICE_8443_IGNORE=true
|
||||||
|
- SERVICE_8444_IGNORE=true
|
||||||
|
meta:
|
||||||
|
labels:
|
||||||
|
- SERVICE_8080_CHECK_HTTP=/
|
||||||
|
- SERVICE_8080_NAME=${COMPOSE_SERVICE_NAME}-meta-8080
|
||||||
|
rest:
|
||||||
|
labels:
|
||||||
|
- SERVICE_3000_CHECK_HTTP=/
|
||||||
|
- SERVICE_3000_NAME=${COMPOSE_SERVICE_NAME}-rest-3000
|
||||||
|
realtime:
|
||||||
|
labels:
|
||||||
|
- SERVICE_4000_CHECK_HTTP=/
|
||||||
|
- SERVICE_4000_NAME=${COMPOSE_SERVICE_NAME}-realtime-4000
|
||||||
|
storage:
|
||||||
|
labels:
|
||||||
|
- SERVICE_5000_CHECK_HTTP=/status
|
||||||
|
- SERVICE_5000_NAME=${COMPOSE_SERVICE_NAME}-storage-5000
|
||||||
|
studio:
|
||||||
|
labels:
|
||||||
|
- SERVICE_3000_CHECK_HTTP=/api/profile
|
||||||
|
- SERVICE_3000_NAME=${COMPOSE_SERVICE_NAME}-studio-3000
|
||||||
|
supavisor:
|
||||||
|
labels:
|
||||||
|
- SERVICE_4000_CHECK_HTTP=/api/health
|
||||||
|
- SERVICE_4000_NAME=${COMPOSE_SERVICE_NAME}-supavisor-4000
|
||||||
|
- SERVICE_5432_IGNORE=true
|
||||||
|
- SERVICE_6543_IGNORE=true
|
||||||
|
vector:
|
||||||
|
labels:
|
||||||
|
- SERVICE_9001_CHECK_HTTP=/health
|
||||||
|
- SERVICE_9001_NAME=${COMPOSE_SERVICE_NAME}-vector-9001
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
services:
|
||||||
|
supavisor:
|
||||||
|
ports:
|
||||||
|
- ${SUPABASE_POSTGRES_PORT:-5432}:5432/tcp
|
||||||
|
- ${SUPABASE_POOLER_PROXY_PORT_TRANSACTION:-6543}:6543/tcp
|
||||||
|
kong:
|
||||||
|
ports:
|
||||||
|
- ${SUPABASE_KONG_HTTP_PORT:-8000}:8000/tcp
|
||||||
|
- ${SUPABASE_KONG_HTTPS_PORT:-8443}:8443/tcp
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
ENV_VARS += SUPABASE_ADDITIONAL_REDIRECT_URLS SUPABASE_ANON_KEY SUPABASE_JWT_IAT SUPABASE_KONG_SERVICE_8000_TAGS SUPABASE_PUBLIC_URL SUPABASE_REALTIME_TENANT SUPABASE_SERVICE_ROLE_KEY SUPABASE_EDGE_RUNTIME_VERSION SUPABASE_KONG_VERSION SUPABASE_POSTGRES_VERSION SUPABASE_VECTOR_VERSION
|
||||||
|
SUPABASE_ADDITIONAL_REDIRECT_URLS ?= $(subst $(space),$(comma),$(call url,SUPABASE))
|
||||||
|
SUPABASE_ANON_KEY ?= $(call JWT,,$(SUPABASE_JWT_PAYLOAD_ANON),$(SUPABASE_JWT_SECRET))
|
||||||
|
SUPABASE_DOCKER_IMAGES ?= supabase/edge-runtime:$(SUPABASE_EDGE_RUNTIME_VERSION) kong:$(SUPABASE_KONG_VERSION) supabase/postgres:$(SUPABASE_POSTGRES_VERSION) timberio/vector:$(SUPABASE_VECTOR_VERSION)
|
||||||
|
SUPABASE_JWT_IAT ?= $(MAKE_UNIXTIME_START)
|
||||||
|
SUPABASE_JWT_EXP ?= $(shell echo $$(($(SUPABASE_JWT_IAT)+94608000)))
|
||||||
|
SUPABASE_JWT_PAYLOAD_ANON ?= {\n "role": "anon",\n "iss": "supabase",\n "iat": $(SUPABASE_JWT_IAT),\n "exp": $(SUPABASE_JWT_EXP)\n}
|
||||||
|
SUPABASE_JWT_PAYLOAD_SERVICE ?= {\n "role": "service_role",\n "iss": "supabase",\n "iat": $(SUPABASE_JWT_IAT),\n "exp": $(SUPABASE_JWT_EXP)\n}
|
||||||
|
SUPABASE_KONG_SERVICE_8000_TAGS ?= $(call tagprefix,SUPABASE,8000)
|
||||||
|
SUPABASE_PUBLIC_URL ?= $(SUPABASE_SERVICE_URL)
|
||||||
|
SUPABASE_REALTIME_TENANT ?= realtime-dev
|
||||||
|
SUPABASE_SERVICE_NAME ?= supabase
|
||||||
|
SUPABASE_SERVICE_ROLE_KEY ?= $(call JWT,,$(SUPABASE_JWT_PAYLOAD_SERVICE),$(SUPABASE_JWT_SECRET))
|
||||||
|
SUPABASE_SERVICE_URIS ?= $(call uri,SUPABASE)
|
||||||
|
SUPABASE_SERVICE_URL ?= $(call firstword,$(call url,SUPABASE))
|
||||||
|
SUPABASE_EDGE_RUNTIME_VERSION ?= $(shell awk -F: '/image: supabase\/edge-runtime:/ {print $$3}' stack/supabase/docker-compose.yml)
|
||||||
|
SUPABASE_KONG_VERSION ?= $(shell awk -F: '/image: kong:/ {print $$3}' stack/supabase/docker-compose.yml)
|
||||||
|
SUPABASE_POSTGRES_VERSION ?= $(shell awk -F: '/image: supabase\/postgres:/ {print $$3}' stack/supabase/docker-compose.yml)
|
||||||
|
SUPABASE_VECTOR_VERSION ?= $(shell awk -F: '/image: timberio\/vector:/ {print $$3}' stack/supabase/docker-compose.yml)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
services:
|
||||||
|
db:
|
||||||
|
volumes: !override
|
||||||
|
- db-data:/var/lib/postgresql/data
|
||||||
|
- db-config:/etc/postgres-custom
|
||||||
|
functions:
|
||||||
|
volumes: !override
|
||||||
|
- functions-cache:/root/.cache
|
||||||
|
- functions-home:/home/deno/functions
|
||||||
|
imgproxy:
|
||||||
|
volumes: !override
|
||||||
|
- storage-data:/var/lib/storage
|
||||||
|
kong:
|
||||||
|
volumes: !override
|
||||||
|
- kong-home:/home/kong
|
||||||
|
storage:
|
||||||
|
volumes: !override
|
||||||
|
- storage-data:/var/lib/storage
|
||||||
|
vector:
|
||||||
|
volumes: !override
|
||||||
|
- vector-config:/etc/vector:ro
|
||||||
|
- ${DOCKER_SOCKET_LOCATION:-/var/run/docker.sock}:/var/run/docker.sock:ro
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db-config:
|
||||||
|
db-data:
|
||||||
|
functions-cache:
|
||||||
|
functions-home:
|
||||||
|
kong-home:
|
||||||
|
storage-data:
|
||||||
|
vector-config:
|
||||||
@@ -0,0 +1,272 @@
|
|||||||
|
services:
|
||||||
|
studio:
|
||||||
|
container_name: !reset null
|
||||||
|
environment: !override
|
||||||
|
STUDIO_PG_META_URL: ${SUPABASE_STUDIO_PG_META_URL:-http://meta:8080}
|
||||||
|
POSTGRES_PASSWORD: ${SUPABASE_POSTGRES_PASSWORD}
|
||||||
|
DEFAULT_ORGANIZATION_NAME: ${SUPABASE_STUDIO_DEFAULT_ORGANIZATION:-Default Organization}
|
||||||
|
DEFAULT_PROJECT_NAME: ${SUPABASE_STUDIO_DEFAULT_PROJECT:-Default Project}
|
||||||
|
OPENAI_API_KEY: ${SUPABASE_OPENAI_API_KEY:-}
|
||||||
|
SUPABASE_URL: ${SUPABASE_URL:-http://kong:8000}
|
||||||
|
SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL:-http://localhost:8000}
|
||||||
|
SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}
|
||||||
|
SUPABASE_SERVICE_KEY: ${SUPABASE_SERVICE_ROLE_KEY}
|
||||||
|
AUTH_JWT_SECRET: ${SUPABASE_JWT_SECRET}
|
||||||
|
LOGFLARE_API_KEY: ${SUPABASE_LOGFLARE_API_KEY}
|
||||||
|
LOGFLARE_URL: ${SUPABASE_LOGFLARE_URL:-http://analytics:4000}
|
||||||
|
NEXT_PUBLIC_ENABLE_LOGS: ${SUPABASE_NEXT_PUBLIC_ENABLE_LOGS:-true}
|
||||||
|
NEXT_ANALYTICS_BACKEND_PROVIDER: ${SUPABASE_NEXT_ANALYTICS_BACKEND_PROVIDER:-postgres}
|
||||||
|
|
||||||
|
kong:
|
||||||
|
build:
|
||||||
|
args:
|
||||||
|
- DOCKER_BUILD_DIR=docker/supabase/kong
|
||||||
|
- DOCKER_NETWORK=${DOCKER_NETWORK_DEFAULT:-_default}
|
||||||
|
- REALTIME_TENANT=${SUPABASE_REALTIME_TENANT:-realtime-dev}
|
||||||
|
- SUPABASE_KONG_VERSION=${SUPABASE_KONG_VERSION:-2.8.1}
|
||||||
|
context: ../..
|
||||||
|
dockerfile: docker/supabase/kong/Dockerfile
|
||||||
|
container_name: !reset null
|
||||||
|
image: ${DOCKER_REPOSITORY}/kong:${DOCKER_IMAGE_TAG}
|
||||||
|
environment: !override
|
||||||
|
KONG_DATABASE: ${SUPABASE_KONG_DATABASE:-off}
|
||||||
|
KONG_DECLARATIVE_CONFIG: ${SUPABASE_KONG_DECLARATIVE_CONFIG:-/home/kong/kong.yml}
|
||||||
|
KONG_DNS_ORDER: ${SUPABASE_KONG_DNS_ORDER:-LAST,A,CNAME}
|
||||||
|
KONG_PLUGINS: ${SUPABASE_KONG_PLUGINS:-request-transformer,cors,key-auth,acl,basic-auth}
|
||||||
|
KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: ${SUPABASE_KONG_NGINX_PROXY_PROXY_BUFFER_SIZE:-160k}
|
||||||
|
KONG_NGINX_PROXY_PROXY_BUFFERS: ${SUPABASE_KONG_NGINX_PROXY_PROXY_BUFFERS:-64 160k}
|
||||||
|
SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}
|
||||||
|
SUPABASE_SERVICE_KEY: ${SUPABASE_SERVICE_ROLE_KEY}
|
||||||
|
DASHBOARD_USERNAME: ${SUPABASE_DASHBOARD_USERNAME:-supabase}
|
||||||
|
DASHBOARD_PASSWORD: ${SUPABASE_DASHBOARD_PASSWORD}
|
||||||
|
ports: !override
|
||||||
|
- 8000/tcp
|
||||||
|
- 8443/tcp
|
||||||
|
|
||||||
|
auth:
|
||||||
|
container_name: !reset null
|
||||||
|
environment: !override
|
||||||
|
GOTRUE_API_HOST: ${SUPABASE_GOTRUE_API_HOST:-0.0.0.0}
|
||||||
|
GOTRUE_API_PORT: ${SUPABASE_GOTRUE_API_PORT:-9999}
|
||||||
|
API_EXTERNAL_URL: ${SUPABASE_API_EXTERNAL_URL:-${SUPABASE_PUBLIC_URL:-http://localhost:8000}}
|
||||||
|
GOTRUE_DB_DRIVER: ${SUPABASE_GOTRUE_DB_DRIVER:-postgres}
|
||||||
|
GOTRUE_DB_DATABASE_URL: postgres://${SUPABASE_POSTGRES_USER_AUTH:-supabase_auth_admin}:${SUPABASE_POSTGRES_PASSWORD_AUTH:-${SUPABASE_POSTGRES_PASSWORD}}@${SUPABASE_POSTGRES_HOST:-db}:${SUPABASE_POSTGRES_PORT:-5432}/${SUPABASE_POSTGRES_DB:-postgres}
|
||||||
|
GOTRUE_SITE_URL: ${SUPABASE_SITE_URL:-${SUPABASE_PUBLIC_URL:-http://localhost:3000}}
|
||||||
|
GOTRUE_URI_ALLOW_LIST: ${SUPABASE_ADDITIONAL_REDIRECT_URLS:-}
|
||||||
|
GOTRUE_DISABLE_SIGNUP: ${SUPABASE_DISABLE_SIGNUP:-false}
|
||||||
|
GOTRUE_JWT_ADMIN_ROLES: ${SUPABASE_JWT_ADMIN_ROLES:-service_role}
|
||||||
|
GOTRUE_JWT_AUD: ${SUPABASE_JWT_AUD:-authenticated}
|
||||||
|
GOTRUE_JWT_DEFAULT_GROUP_NAME: ${SUPABASE_JWT_DEFAULT_GROUP_NAME:-authenticated}
|
||||||
|
GOTRUE_JWT_EXP: ${SUPABASE_JWT_EXPIRY:-3600}
|
||||||
|
GOTRUE_JWT_SECRET: ${SUPABASE_JWT_SECRET}
|
||||||
|
GOTRUE_EXTERNAL_EMAIL_ENABLED: ${SUPABASE_ENABLE_EMAIL_SIGNUP:-true}
|
||||||
|
GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED: ${SUPABASE_ENABLE_ANONYMOUS_USERS:-false}
|
||||||
|
GOTRUE_MAILER_AUTOCONFIRM: ${SUPABASE_ENABLE_EMAIL_AUTOCONFIRM:-false}
|
||||||
|
GOTRUE_EXTERNAL_SKIP_NONCE_CHECK: ${SUPABASE_EXTERNAL_SKIP_NONCE_CHECK:-false}
|
||||||
|
GOTRUE_MAILER_SECURE_EMAIL_CHANGE_ENABLED: ${SUPABASE_MAILER_SECURE_EMAIL_CHANGE_ENABLED:-true}
|
||||||
|
GOTRUE_SMTP_MAX_FREQUENCY: ${SUPABASE_SMTP_MAX_FREQUENCY:-1s}
|
||||||
|
GOTRUE_SMTP_ADMIN_EMAIL: ${SUPABASE_SMTP_ADMIN_EMAIL:-}
|
||||||
|
GOTRUE_SMTP_HOST: ${SUPABASE_SMTP_HOST:-}
|
||||||
|
GOTRUE_SMTP_PORT: ${SUPABASE_SMTP_PORT:-25}
|
||||||
|
GOTRUE_SMTP_USER: ${SUPABASE_SMTP_USER:-}
|
||||||
|
GOTRUE_SMTP_PASS: ${SUPABASE_SMTP_PASS:-}
|
||||||
|
GOTRUE_SMTP_SENDER_NAME: ${SUPABASE_SMTP_SENDER_NAME:-}
|
||||||
|
GOTRUE_MAILER_URLPATHS_INVITE: ${SUPABASE_MAILER_URLPATHS_INVITE:-/auth/v1/verify}
|
||||||
|
GOTRUE_MAILER_URLPATHS_CONFIRMATION: ${SUPABASE_MAILER_URLPATHS_CONFIRMATION:-/auth/v1/verify}
|
||||||
|
GOTRUE_MAILER_URLPATHS_RECOVERY: ${SUPABASE_MAILER_URLPATHS_RECOVERY:-/auth/v1/verify}
|
||||||
|
GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: ${SUPABASE_MAILER_URLPATHS_EMAIL_CHANGE:-/auth/v1/verify}
|
||||||
|
GOTRUE_EXTERNAL_PHONE_ENABLED: ${SUPABASE_ENABLE_PHONE_SIGNUP:-true}
|
||||||
|
GOTRUE_SMS_AUTOCONFIRM: ${SUPABASE_ENABLE_PHONE_AUTOCONFIRM:-true}
|
||||||
|
GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_ENABLED: ${SUPABASE_GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_ENABLED:-false}
|
||||||
|
GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_URI: ${SUPABASE_GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_URI:-}
|
||||||
|
GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_SECRETS: ${SUPABASE_GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_SECRETS:-}
|
||||||
|
GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_ENABLED: ${SUPABASE_GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_ENABLED:-false}
|
||||||
|
GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_URI: ${SUPABASE_GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_URI:-}
|
||||||
|
GOTRUE_HOOK_SEND_SMS_ENABLED: ${SUPABASE_GOTRUE_HOOK_SEND_SMS_ENABLED:-false}
|
||||||
|
GOTRUE_HOOK_SEND_SMS_URI: ${SUPABASE_GOTRUE_HOOK_SEND_SMS_URI:-}
|
||||||
|
GOTRUE_HOOK_SEND_SMS_SECRETS: ${SUPABASE_GOTRUE_HOOK_SEND_SMS_SECRETS:-}
|
||||||
|
GOTRUE_HOOK_SEND_EMAIL_ENABLED: ${SUPABASE_GOTRUE_HOOK_SEND_EMAIL_ENABLED:-false}
|
||||||
|
GOTRUE_HOOK_SEND_EMAIL_URI: ${SUPABASE_GOTRUE_HOOK_SEND_EMAIL_URI:-}
|
||||||
|
GOTRUE_HOOK_SEND_EMAIL_SECRETS: ${SUPABASE_GOTRUE_HOOK_SEND_EMAIL_SECRETS:-}
|
||||||
|
|
||||||
|
rest:
|
||||||
|
container_name: !reset null
|
||||||
|
environment: !override
|
||||||
|
PGRST_DB_URI: postgres://${SUPABASE_POSTGRES_USER_REST:-authenticator}:${SUPABASE_POSTGRES_PASSWORD_REST:-${SUPABASE_POSTGRES_PASSWORD}}@${SUPABASE_POSTGRES_HOST:-db}:${SUPABASE_POSTGRES_PORT:-5432}/${SUPABASE_POSTGRES_DB:-postgres}
|
||||||
|
PGRST_DB_SCHEMAS: ${SUPABASE_PGRST_DB_SCHEMAS:-public,storage,graphql_public}
|
||||||
|
PGRST_DB_ANON_ROLE: ${SUPABASE_PGRST_DB_ANON_ROLE:-anon}
|
||||||
|
PGRST_JWT_SECRET: ${SUPABASE_JWT_SECRET}
|
||||||
|
PGRST_DB_USE_LEGACY_GUCS: ${SUPABASE_PGRST_DB_USE_LEGACY_GUCS:-false}
|
||||||
|
PGRST_APP_SETTINGS_JWT_SECRET: ${SUPABASE_JWT_SECRET}
|
||||||
|
PGRST_APP_SETTINGS_JWT_EXP: ${SUPABASE_JWT_EXPIRY:-3600}
|
||||||
|
|
||||||
|
realtime:
|
||||||
|
container_name: !reset null
|
||||||
|
environment: !override
|
||||||
|
PORT: ${SUPABASE_REALTIME_PORT:-4000}
|
||||||
|
DB_HOST: ${SUPABASE_POSTGRES_HOST:-db}
|
||||||
|
DB_PORT: ${SUPABASE_POSTGRES_PORT:-5432}
|
||||||
|
DB_USER: ${SUPABASE_POSTGRES_USER_SUPABASE:-supabase_admin}
|
||||||
|
DB_PASSWORD: ${SUPABASE_POSTGRES_PASSWORD_SUPABASE:-${SUPABASE_POSTGRES_PASSWORD}}
|
||||||
|
DB_NAME: ${SUPABASE_POSTGRES_DB:-postgres}
|
||||||
|
DB_AFTER_CONNECT_QUERY: ${SUPABASE_POSTGRES_DB_AFTER_CONNECT_QUERY:-SET search_path TO _realtime}
|
||||||
|
DB_ENC_KEY: ${SUPABASE_POSTGRES_DB_ENC_KEY:-supabaserealtime}
|
||||||
|
API_JWT_SECRET: ${SUPABASE_JWT_SECRET}
|
||||||
|
SECRET_KEY_BASE: ${SUPABASE_SECRET_KEY_BASE}
|
||||||
|
ERL_AFLAGS: ${SUPABASE_REALTIME_ERL_AFLAGS:--proto_dist inet_tcp}
|
||||||
|
DNS_NODES: ${SUPABASE_REALTIME_DNS_NODES:-''}
|
||||||
|
RLIMIT_NOFILE: ${SUPABASE_REALTIME_RLIMIT_NOFILE:-10000}
|
||||||
|
APP_NAME: ${SUPABE_REALTIME_APP_NAME:-realtime}
|
||||||
|
SEED_SELF_HOST: ${SUPABASE_REALTIME_SEED_SELF_HOST:-true}
|
||||||
|
RUN_JANITOR: ${SUPABASE_REALTIME_RUN_JANITOR:-true}
|
||||||
|
SELF_HOST_TENANT_NAME: ${SUPABASE_REALTIME_TENANT:-realtime-dev}
|
||||||
|
MIX_ENV: ${SUPABASE_REALTIME_MIX_ENV:-prod}
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"curl",
|
||||||
|
"-sSfL",
|
||||||
|
"--head",
|
||||||
|
"-o",
|
||||||
|
"/dev/null",
|
||||||
|
"-H",
|
||||||
|
"Authorization: Bearer ${SUPABASE_ANON_KEY}",
|
||||||
|
"http://localhost:4000/api/tenants/${SUPABASE_REALTIME_TENANT:-realtime-dev}/health"
|
||||||
|
]
|
||||||
|
timeout: 5s
|
||||||
|
interval: 5s
|
||||||
|
retries: 3
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
aliases:
|
||||||
|
- ${SUPABASE_REALTIME_TENANT:-realtime-dev}
|
||||||
|
|
||||||
|
storage:
|
||||||
|
container_name: !reset null
|
||||||
|
environment: !override
|
||||||
|
ANON_KEY: ${SUPABASE_ANON_KEY}
|
||||||
|
SERVICE_KEY: ${SUPABASE_SERVICE_ROLE_KEY}
|
||||||
|
POSTGREST_URL: ${SUPABASE_POSTGREST_URL:-http://rest:3000}
|
||||||
|
PGRST_JWT_SECRET: ${SUPABASE_JWT_SECRET}
|
||||||
|
DATABASE_URL: postgres://${SUPABASE_POSTGRES_USER_STORAGE:-supabase_storage_admin}:${SUPABASE_POSTGRES_PASSWORD_STORAGE:-${SUPABASE_POSTGRES_PASSWORD}}@${SUPABASE_POSTGRES_HOST:-db}:${SUPABASE_POSTGRES_PORT:-5432}/${SUPABASE_POSTGRES_DB:-postgres}
|
||||||
|
FILE_SIZE_LIMIT: ${SUPABASE_STORAGE_FILE_SIZE_LIMIT:-52428800}
|
||||||
|
STORAGE_BACKEND: ${SUPABASE_STORAGE_BACKEND:-file}
|
||||||
|
FILE_STORAGE_BACKEND_PATH: ${SUPABASE_FILE_STORAGE_BACKEND_PATH:-/var/lib/storage}
|
||||||
|
TENANT_ID: ${SUPABASE_STORAGE_TENANT_ID:-stub}
|
||||||
|
REGION: ${SUPABASE_STORAGE_REGION:-stub}
|
||||||
|
GLOBAL_S3_BUCKET: ${SUPABASE_STORAGE_GLOBAL_S3_BUCKET:-stub}
|
||||||
|
ENABLE_IMAGE_TRANSFORMATION: ${SUPABASE_STORAGE_ENABLE_IMAGE_TRANSFORMATION:-true}
|
||||||
|
IMGPROXY_URL: ${SUPABASE_IMGPROXY_URL:-http://imgproxy:5001}
|
||||||
|
|
||||||
|
imgproxy:
|
||||||
|
container_name: !reset null
|
||||||
|
environment: !override
|
||||||
|
IMGPROXY_BIND: ${SUPABASE_IMGPROXY_BIND:-:5001}
|
||||||
|
IMGPROXY_LOCAL_FILESYSTEM_ROOT: ${SUPABASE_IMGPROXY_LOCAL_FILESYSTEM_ROOT:-/}
|
||||||
|
IMGPROXY_USE_ETAG: ${SUPABASE_IMGPROXY_USE_ETAG:-true}
|
||||||
|
IMGPROXY_ENABLE_WEBP_DETECTION: ${SUPABASE_IMGPROXY_ENABLE_WEBP_DETECTION:-true}
|
||||||
|
|
||||||
|
meta:
|
||||||
|
container_name: !reset null
|
||||||
|
environment: !override
|
||||||
|
PG_META_PORT: ${SUPABASE_META_PORT:-8080}
|
||||||
|
PG_META_DB_HOST: ${SUPABASE_POSTGRES_HOST:-db}
|
||||||
|
PG_META_DB_PORT: ${SUPABASE_POSTGRES_PORT:-5432}
|
||||||
|
PG_META_DB_NAME: ${SUPABASE_POSTGRES_DB:-postgres}
|
||||||
|
PG_META_DB_USER: ${SUPABASE_POSTGRES_USER_SUPABASE:-supabase_admin}
|
||||||
|
PG_META_DB_PASSWORD: ${SUPABASE_POSTGRES_PASSWORD_SUPABASE:-${SUPABASE_POSTGRES_PASSWORD}}
|
||||||
|
|
||||||
|
functions:
|
||||||
|
build:
|
||||||
|
args:
|
||||||
|
- DOCKER_BUILD_DIR=docker/supabase/edge-runtime
|
||||||
|
- SUPABASE_EDGE_RUNTIME_VERSION=${SUPABASE_EDGE_RUNTIME_VERSION:-v1.65.3}
|
||||||
|
context: ../..
|
||||||
|
dockerfile: docker/supabase/edge-runtime/Dockerfile
|
||||||
|
container_name: !reset null
|
||||||
|
image: ${DOCKER_REPOSITORY}/edge-runtime:${DOCKER_IMAGE_TAG}
|
||||||
|
environment: !override
|
||||||
|
JWT_SECRET: ${SUPABASE_JWT_SECRET}
|
||||||
|
SUPABASE_URL: ${SUPABASE_URL:-http://kong:8000}
|
||||||
|
SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}
|
||||||
|
SUPABASE_SERVICE_ROLE_KEY: ${SUPABASE_SERVICE_ROLE_KEY}
|
||||||
|
SUPABASE_DB_URL: postgresql://${SUPABASE_POSTGRES_USER:-postgres}:${SUPABASE_POSTGRES_PASSWORD}@${SUPABASE_POSTGRES_HOST:-db}:${SUPABASE_POSTGRES_PORT:-5432}/${SUPABASE_POSTGRES_DB:-postgres}
|
||||||
|
VERIFY_JWT: "${SUPABASE_FUNCTIONS_VERIFY_JWT}"
|
||||||
|
|
||||||
|
analytics:
|
||||||
|
container_name: !reset null
|
||||||
|
environment: !override
|
||||||
|
LOGFLARE_NODE_HOST: ${SUPABASE_LOGFLARE_NODE_HOST:-127.0.0.1}
|
||||||
|
DB_USERNAME: ${SUPABASE_POSTGRES_USER_SUPABASE:-supabase_admin}
|
||||||
|
DB_DATABASE: ${SUPABASE_POSTGRES_DB_SUPABASE:-_supabase}
|
||||||
|
DB_HOSTNAME: ${SUPABASE_POSTGRES_HOST:-db}
|
||||||
|
DB_PORT: ${SUPABASE_POSTGRES_PORT:-5432}
|
||||||
|
DB_PASSWORD: ${SUPABASE_POSTGRES_PASSWORD_SUPABASE:-${SUPABASE_POSTGRES_PASSWORD}}
|
||||||
|
DB_SCHEMA: ${SUPABASE_POSTGRES_SCHEMA_SUPABASE:-_analytics}
|
||||||
|
LOGFLARE_API_KEY: ${SUPABASE_LOGFLARE_API_KEY}
|
||||||
|
LOGFLARE_SINGLE_TENANT: ${SUPABASE_LOGFLARE_SINGLE_TENANT:-true}
|
||||||
|
LOGFLARE_SUPABASE_MODE: ${SUPABASE_LOGFLARE_SUPABASE_MODE:-true}
|
||||||
|
LOGFLARE_MIN_CLUSTER_SIZE: ${SUPABASE_LOGFLARE_MIN_CLUSTER_SIZE:-1}
|
||||||
|
POSTGRES_BACKEND_URL: postgresql://${SUPABASE_POSTGRES_USER_SUPABASE:-supabase_admin}:${SUPABASE_POSTGRES_PASSWORD_SUPABASE:-${SUPABASE_POSTGRES_PASSWORD}}@${SUPABASE_POSTGRES_HOST:-db}:${SUPABASE_POSTGRES_PORT:-5432}/${SUPABASE_POSTGRES_DB_SUPABASE:-_supabase}
|
||||||
|
POSTGRES_BACKEND_SCHEMA: ${SUPABASE_POSTGRES_SCHEMA_SUPABASE:-_analytics}
|
||||||
|
LOGFLARE_FEATURE_FLAG_OVERRIDE: ${SUPABASE_LOGFLARE_FEATURE_FLAG_OVERRIDE:-multibackend=true}
|
||||||
|
GOOGLE_PROJECT_ID: ${SUPABASE_GOOGLE_PROJECT_ID:-}
|
||||||
|
GOOGLE_PROJECT_NUMBER: ${SUPABASE_GOOGLE_PROJECT_NUMBER:-}
|
||||||
|
ports: !override
|
||||||
|
- 4000
|
||||||
|
|
||||||
|
db:
|
||||||
|
build:
|
||||||
|
args:
|
||||||
|
- DOCKER_BUILD_DIR=docker/supabase/postgres
|
||||||
|
- SUPABASE_POSTGRES_VERSION=${SUPABASE_POSTGRES_VERSION:-15.6.1.146}
|
||||||
|
context: ../..
|
||||||
|
dockerfile: docker/supabase/postgres/Dockerfile
|
||||||
|
container_name: !reset null
|
||||||
|
image: ${DOCKER_REPOSITORY}/postgres:${DOCKER_IMAGE_TAG}
|
||||||
|
environment: !override
|
||||||
|
POSTGRES_HOST: /var/run/postgresql
|
||||||
|
PGPORT: ${SUPABASE_POSTGRES_PORT:-5432}
|
||||||
|
POSTGRES_PORT: ${SUPABASE_POSTGRES_PORT:-5432}
|
||||||
|
PGPASSWORD: ${SUPABASE_POSTGRES_PASSWORD}
|
||||||
|
POSTGRES_PASSWORD: ${SUPABASE_POSTGRES_PASSWORD}
|
||||||
|
PGDATABASE: ${SUPABASE_POSTGRES_DB:-postgres}
|
||||||
|
POSTGRES_DB: ${SUPABASE_POSTGRES_DB:-postgres}
|
||||||
|
JWT_SECRET: ${SUPABASE_JWT_SECRET}
|
||||||
|
JWT_EXP: ${SUPABASE_JWT_EXPIRY:-3600}
|
||||||
|
|
||||||
|
vector:
|
||||||
|
build:
|
||||||
|
args:
|
||||||
|
- DOCKER_BUILD_DIR=docker/supabase/vector
|
||||||
|
- SUPABASE_VECTOR_VERSION=${SUPABASE_VECTOR_VERSION:-0.28.1-alpine}
|
||||||
|
context: ../..
|
||||||
|
dockerfile: docker/supabase/vector/Dockerfile
|
||||||
|
container_name: !reset null
|
||||||
|
image: ${DOCKER_REPOSITORY}/vector:${DOCKER_IMAGE_TAG}
|
||||||
|
environment: !override
|
||||||
|
LOGFLARE_API_KEY: ${SUPABASE_LOGFLARE_API_KEY}
|
||||||
|
|
||||||
|
supavisor:
|
||||||
|
container_name: !reset null
|
||||||
|
ports: !override
|
||||||
|
- 5432
|
||||||
|
- 6543
|
||||||
|
environment: !override
|
||||||
|
- PORT=${SUPABASE_SUPAVISOR_PORT:-4000}
|
||||||
|
- POSTGRES_PORT=${SUPABASE_POSTGRES_PORT:-5432}
|
||||||
|
- POSTGRES_DB=${SUPABASE_POSTGRES_DB:-postgres}
|
||||||
|
- POSTGRES_PASSWORD=${SUPABASE_POSTGRES_PASSWORD}
|
||||||
|
- DATABASE_URL=ecto://${SUPABASE_POSTGRES_USER_SUPABASE:-supabase_admin}:${SUPABASE_POSTGRES_PASSWORD_SUPABASE:-${SUPABASE_POSTGRES_PASSWORD}}@${SUPABASE_POSTGRES_HOST:-db}:${SUPABASE_POSTGRES_PORT:-5432}/${SUPABASE_POSTGRES_DB_SUPABASE:-_supabase}
|
||||||
|
- CLUSTER_POSTGRES=true
|
||||||
|
- SECRET_KEY_BASE=${SUPABASE_SECRET_KEY_BASE}
|
||||||
|
- VAULT_ENC_KEY=${SUPABASE_VAULT_ENC_KEY}
|
||||||
|
- API_JWT_SECRET=${SUPABASE_JWT_SECRET}
|
||||||
|
- METRICS_JWT_SECRET=${SUPABASE_JWT_SECRET}
|
||||||
|
- REGION=${SUPABASE_SUPAVISOR_REGION:-local}
|
||||||
|
- ERL_AFLAGS=${SUPABASE_SUPAVISOR_ERL_AFLAGS:--proto_dist inet_tcp}
|
||||||
|
- POOLER_TENANT_ID=${SUPABASE_POOLER_TENANT_ID:-local}
|
||||||
|
- POOLER_DEFAULT_POOL_SIZE=${SUPABASE_POOLER_DEFAULT_POOL_SIZE:-20}
|
||||||
|
- POOLER_MAX_CLIENT_CONN=${SUPABASE_POOLER_MAX_CLIENT_CONN:-100}
|
||||||
|
- POOLER_POOL_MODE=${SUPABASE_POOLER_POOL_MODE:-transaction}
|
||||||
@@ -0,0 +1,241 @@
|
|||||||
|
_format_version: '2.1'
|
||||||
|
_transform: true
|
||||||
|
|
||||||
|
###
|
||||||
|
### Consumers / Users
|
||||||
|
###
|
||||||
|
consumers:
|
||||||
|
- username: DASHBOARD
|
||||||
|
- username: anon
|
||||||
|
keyauth_credentials:
|
||||||
|
- key: $SUPABASE_ANON_KEY
|
||||||
|
- username: service_role
|
||||||
|
keyauth_credentials:
|
||||||
|
- key: $SUPABASE_SERVICE_KEY
|
||||||
|
|
||||||
|
###
|
||||||
|
### Access Control List
|
||||||
|
###
|
||||||
|
acls:
|
||||||
|
- consumer: anon
|
||||||
|
group: anon
|
||||||
|
- consumer: service_role
|
||||||
|
group: admin
|
||||||
|
|
||||||
|
###
|
||||||
|
### Dashboard credentials
|
||||||
|
###
|
||||||
|
basicauth_credentials:
|
||||||
|
- consumer: DASHBOARD
|
||||||
|
username: $DASHBOARD_USERNAME
|
||||||
|
password: $DASHBOARD_PASSWORD
|
||||||
|
|
||||||
|
###
|
||||||
|
### 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 GraphQL routes
|
||||||
|
- name: graphql-v1
|
||||||
|
_comment: 'PostgREST: /graphql/v1/* -> http://rest:3000/rpc/graphql'
|
||||||
|
url: http://rest:3000/rpc/graphql
|
||||||
|
routes:
|
||||||
|
- name: graphql-v1-all
|
||||||
|
strip_path: true
|
||||||
|
paths:
|
||||||
|
- /graphql/v1
|
||||||
|
plugins:
|
||||||
|
- name: cors
|
||||||
|
- name: key-auth
|
||||||
|
config:
|
||||||
|
hide_credentials: true
|
||||||
|
- name: request-transformer
|
||||||
|
config:
|
||||||
|
add:
|
||||||
|
headers:
|
||||||
|
- Content-Profile:graphql_public
|
||||||
|
- name: acl
|
||||||
|
config:
|
||||||
|
hide_groups_header: true
|
||||||
|
allow:
|
||||||
|
- admin
|
||||||
|
- anon
|
||||||
|
|
||||||
|
## Secure Realtime routes
|
||||||
|
- name: realtime-v1-ws
|
||||||
|
_comment: 'Realtime: /realtime/v1/* -> ws://realtime:4000/socket/*'
|
||||||
|
url: http://realtime-dev.supabase-realtime:4000/socket
|
||||||
|
protocol: ws
|
||||||
|
routes:
|
||||||
|
- name: realtime-v1-ws
|
||||||
|
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
|
||||||
|
- name: realtime-v1-rest
|
||||||
|
_comment: 'Realtime: /realtime/v1/* -> ws://realtime:4000/socket/*'
|
||||||
|
url: http://realtime-dev.supabase-realtime:4000/api
|
||||||
|
protocol: http
|
||||||
|
routes:
|
||||||
|
- name: realtime-v1-rest
|
||||||
|
strip_path: true
|
||||||
|
paths:
|
||||||
|
- /realtime/v1/api
|
||||||
|
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
|
||||||
|
|
||||||
|
## Edge Functions routes
|
||||||
|
- name: functions-v1
|
||||||
|
_comment: 'Edge Functions: /functions/v1/* -> http://functions:9000/*'
|
||||||
|
url: http://functions:9000/
|
||||||
|
routes:
|
||||||
|
- name: functions-v1-all
|
||||||
|
strip_path: true
|
||||||
|
paths:
|
||||||
|
- /functions/v1/
|
||||||
|
plugins:
|
||||||
|
- name: cors
|
||||||
|
|
||||||
|
## Analytics routes
|
||||||
|
- name: analytics-v1
|
||||||
|
_comment: 'Analytics: /analytics/v1/* -> http://logflare:4000/*'
|
||||||
|
url: http://analytics:4000/
|
||||||
|
routes:
|
||||||
|
- name: analytics-v1-all
|
||||||
|
strip_path: true
|
||||||
|
paths:
|
||||||
|
- /analytics/v1/
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
## Protected Dashboard - catch all remaining routes
|
||||||
|
- name: dashboard
|
||||||
|
_comment: 'Studio: /* -> http://studio:3000/*'
|
||||||
|
url: http://studio:3000/
|
||||||
|
routes:
|
||||||
|
- name: dashboard-all
|
||||||
|
strip_path: true
|
||||||
|
paths:
|
||||||
|
- /
|
||||||
|
plugins:
|
||||||
|
- name: cors
|
||||||
|
- name: basic-auth
|
||||||
|
config:
|
||||||
|
hide_credentials: true
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
\set pguser `echo "$POSTGRES_USER"`
|
||||||
|
|
||||||
|
CREATE DATABASE _supabase WITH OWNER :pguser;
|
||||||
Executable
@@ -0,0 +1,5 @@
|
|||||||
|
\set jwt_secret `echo "$JWT_SECRET"`
|
||||||
|
\set jwt_exp `echo "$JWT_EXP"`
|
||||||
|
|
||||||
|
ALTER DATABASE postgres SET "app.settings.jwt_secret" TO :'jwt_secret';
|
||||||
|
ALTER DATABASE postgres SET "app.settings.jwt_exp" TO :'jwt_exp';
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
\set pguser `echo "$POSTGRES_USER"`
|
||||||
|
|
||||||
|
\c _supabase
|
||||||
|
create schema if not exists _analytics;
|
||||||
|
alter schema _analytics owner to :pguser;
|
||||||
|
\c postgres
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
\set pguser `echo "$POSTGRES_USER"`
|
||||||
|
|
||||||
|
\c _supabase
|
||||||
|
create schema if not exists _supavisor;
|
||||||
|
alter schema _supavisor owner to :pguser;
|
||||||
|
\c postgres
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
\set pguser `echo "$POSTGRES_USER"`
|
||||||
|
|
||||||
|
create schema if not exists _realtime;
|
||||||
|
alter schema _realtime owner to :pguser;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
-- NOTE: change to your own passwords for production environments
|
||||||
|
\set pgpass `echo "$POSTGRES_PASSWORD"`
|
||||||
|
|
||||||
|
ALTER USER authenticator WITH PASSWORD :'pgpass';
|
||||||
|
ALTER USER pgbouncer WITH PASSWORD :'pgpass';
|
||||||
|
ALTER USER supabase_auth_admin WITH PASSWORD :'pgpass';
|
||||||
|
ALTER USER supabase_functions_admin WITH PASSWORD :'pgpass';
|
||||||
|
ALTER USER supabase_storage_admin WITH PASSWORD :'pgpass';
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
BEGIN;
|
||||||
|
-- Create pg_net extension
|
||||||
|
CREATE EXTENSION IF NOT EXISTS pg_net SCHEMA extensions;
|
||||||
|
-- Create supabase_functions schema
|
||||||
|
CREATE SCHEMA supabase_functions AUTHORIZATION supabase_admin;
|
||||||
|
GRANT USAGE ON SCHEMA supabase_functions TO postgres, anon, authenticated, service_role;
|
||||||
|
ALTER DEFAULT PRIVILEGES IN SCHEMA supabase_functions GRANT ALL ON TABLES TO postgres, anon, authenticated, service_role;
|
||||||
|
ALTER DEFAULT PRIVILEGES IN SCHEMA supabase_functions GRANT ALL ON FUNCTIONS TO postgres, anon, authenticated, service_role;
|
||||||
|
ALTER DEFAULT PRIVILEGES IN SCHEMA supabase_functions GRANT ALL ON SEQUENCES TO postgres, anon, authenticated, service_role;
|
||||||
|
-- supabase_functions.migrations definition
|
||||||
|
CREATE TABLE supabase_functions.migrations (
|
||||||
|
version text PRIMARY KEY,
|
||||||
|
inserted_at timestamptz NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
-- Initial supabase_functions migration
|
||||||
|
INSERT INTO supabase_functions.migrations (version) VALUES ('initial');
|
||||||
|
-- supabase_functions.hooks definition
|
||||||
|
CREATE TABLE supabase_functions.hooks (
|
||||||
|
id bigserial PRIMARY KEY,
|
||||||
|
hook_table_id integer NOT NULL,
|
||||||
|
hook_name text NOT NULL,
|
||||||
|
created_at timestamptz NOT NULL DEFAULT NOW(),
|
||||||
|
request_id bigint
|
||||||
|
);
|
||||||
|
CREATE INDEX supabase_functions_hooks_request_id_idx ON supabase_functions.hooks USING btree (request_id);
|
||||||
|
CREATE INDEX supabase_functions_hooks_h_table_id_h_name_idx ON supabase_functions.hooks USING btree (hook_table_id, hook_name);
|
||||||
|
COMMENT ON TABLE supabase_functions.hooks IS 'Supabase Functions Hooks: Audit trail for triggered hooks.';
|
||||||
|
CREATE FUNCTION supabase_functions.http_request()
|
||||||
|
RETURNS trigger
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $function$
|
||||||
|
DECLARE
|
||||||
|
request_id bigint;
|
||||||
|
payload jsonb;
|
||||||
|
url text := TG_ARGV[0]::text;
|
||||||
|
method text := TG_ARGV[1]::text;
|
||||||
|
headers jsonb DEFAULT '{}'::jsonb;
|
||||||
|
params jsonb DEFAULT '{}'::jsonb;
|
||||||
|
timeout_ms integer DEFAULT 1000;
|
||||||
|
BEGIN
|
||||||
|
IF url IS NULL OR url = 'null' THEN
|
||||||
|
RAISE EXCEPTION 'url argument is missing';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF method IS NULL OR method = 'null' THEN
|
||||||
|
RAISE EXCEPTION 'method argument is missing';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF TG_ARGV[2] IS NULL OR TG_ARGV[2] = 'null' THEN
|
||||||
|
headers = '{"Content-Type": "application/json"}'::jsonb;
|
||||||
|
ELSE
|
||||||
|
headers = TG_ARGV[2]::jsonb;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF TG_ARGV[3] IS NULL OR TG_ARGV[3] = 'null' THEN
|
||||||
|
params = '{}'::jsonb;
|
||||||
|
ELSE
|
||||||
|
params = TG_ARGV[3]::jsonb;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF TG_ARGV[4] IS NULL OR TG_ARGV[4] = 'null' THEN
|
||||||
|
timeout_ms = 1000;
|
||||||
|
ELSE
|
||||||
|
timeout_ms = TG_ARGV[4]::integer;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
CASE
|
||||||
|
WHEN method = 'GET' THEN
|
||||||
|
SELECT http_get INTO request_id FROM net.http_get(
|
||||||
|
url,
|
||||||
|
params,
|
||||||
|
headers,
|
||||||
|
timeout_ms
|
||||||
|
);
|
||||||
|
WHEN method = 'POST' THEN
|
||||||
|
payload = jsonb_build_object(
|
||||||
|
'old_record', OLD,
|
||||||
|
'record', NEW,
|
||||||
|
'type', TG_OP,
|
||||||
|
'table', TG_TABLE_NAME,
|
||||||
|
'schema', TG_TABLE_SCHEMA
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT http_post INTO request_id FROM net.http_post(
|
||||||
|
url,
|
||||||
|
payload,
|
||||||
|
params,
|
||||||
|
headers,
|
||||||
|
timeout_ms
|
||||||
|
);
|
||||||
|
ELSE
|
||||||
|
RAISE EXCEPTION 'method argument % is invalid', method;
|
||||||
|
END CASE;
|
||||||
|
|
||||||
|
INSERT INTO supabase_functions.hooks
|
||||||
|
(hook_table_id, hook_name, request_id)
|
||||||
|
VALUES
|
||||||
|
(TG_RELID, TG_NAME, request_id);
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END
|
||||||
|
$function$;
|
||||||
|
-- Supabase super admin
|
||||||
|
DO
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_roles
|
||||||
|
WHERE rolname = 'supabase_functions_admin'
|
||||||
|
)
|
||||||
|
THEN
|
||||||
|
CREATE USER supabase_functions_admin NOINHERIT CREATEROLE LOGIN NOREPLICATION;
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
GRANT ALL PRIVILEGES ON SCHEMA supabase_functions TO supabase_functions_admin;
|
||||||
|
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA supabase_functions TO supabase_functions_admin;
|
||||||
|
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA supabase_functions TO supabase_functions_admin;
|
||||||
|
ALTER USER supabase_functions_admin SET search_path = "supabase_functions";
|
||||||
|
ALTER table "supabase_functions".migrations OWNER TO supabase_functions_admin;
|
||||||
|
ALTER table "supabase_functions".hooks OWNER TO supabase_functions_admin;
|
||||||
|
ALTER function "supabase_functions".http_request() OWNER TO supabase_functions_admin;
|
||||||
|
GRANT supabase_functions_admin TO postgres;
|
||||||
|
-- Remove unused supabase_pg_net_admin role
|
||||||
|
DO
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_roles
|
||||||
|
WHERE rolname = 'supabase_pg_net_admin'
|
||||||
|
)
|
||||||
|
THEN
|
||||||
|
REASSIGN OWNED BY supabase_pg_net_admin TO supabase_admin;
|
||||||
|
DROP OWNED BY supabase_pg_net_admin;
|
||||||
|
DROP ROLE supabase_pg_net_admin;
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
-- pg_net grants when extension is already enabled
|
||||||
|
DO
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_extension
|
||||||
|
WHERE extname = 'pg_net'
|
||||||
|
)
|
||||||
|
THEN
|
||||||
|
GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role;
|
||||||
|
ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER;
|
||||||
|
ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER;
|
||||||
|
ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net;
|
||||||
|
ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net;
|
||||||
|
REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC;
|
||||||
|
REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC;
|
||||||
|
GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role;
|
||||||
|
GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role;
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
-- Event trigger for pg_net
|
||||||
|
CREATE OR REPLACE FUNCTION extensions.grant_pg_net_access()
|
||||||
|
RETURNS event_trigger
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_event_trigger_ddl_commands() AS ev
|
||||||
|
JOIN pg_extension AS ext
|
||||||
|
ON ev.objid = ext.oid
|
||||||
|
WHERE ext.extname = 'pg_net'
|
||||||
|
)
|
||||||
|
THEN
|
||||||
|
GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role;
|
||||||
|
ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER;
|
||||||
|
ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER;
|
||||||
|
ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net;
|
||||||
|
ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net;
|
||||||
|
REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC;
|
||||||
|
REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC;
|
||||||
|
GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role;
|
||||||
|
GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role;
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
|
COMMENT ON FUNCTION extensions.grant_pg_net_access IS 'Grants access to pg_net';
|
||||||
|
DO
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_event_trigger
|
||||||
|
WHERE evtname = 'issue_pg_net_access'
|
||||||
|
) THEN
|
||||||
|
CREATE EVENT TRIGGER issue_pg_net_access ON ddl_command_end WHEN TAG IN ('CREATE EXTENSION')
|
||||||
|
EXECUTE PROCEDURE extensions.grant_pg_net_access();
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
INSERT INTO supabase_functions.migrations (version) VALUES ('20210809183423_update_grants');
|
||||||
|
ALTER function supabase_functions.http_request() SECURITY DEFINER;
|
||||||
|
ALTER function supabase_functions.http_request() SET search_path = supabase_functions;
|
||||||
|
REVOKE ALL ON FUNCTION supabase_functions.http_request() FROM PUBLIC;
|
||||||
|
GRANT EXECUTE ON FUNCTION supabase_functions.http_request() TO postgres, anon, authenticated, service_role;
|
||||||
|
COMMIT;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// Follow this setup guide to integrate the Deno language server with your editor:
|
||||||
|
// https://deno.land/manual/getting_started/setup_your_environment
|
||||||
|
// This enables autocomplete, go to definition, etc.
|
||||||
|
|
||||||
|
import { serve } from "https://deno.land/std@0.177.1/http/server.ts"
|
||||||
|
|
||||||
|
serve(async () => {
|
||||||
|
return new Response(
|
||||||
|
`"Hello from Edge Functions!"`,
|
||||||
|
{ headers: { "Content-Type": "application/json" } },
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
// To invoke:
|
||||||
|
// curl 'http://localhost:<KONG_HTTP_PORT>/functions/v1/hello' \
|
||||||
|
// --header 'Authorization: Bearer <anon/service_role API key>'
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import { serve } from 'https://deno.land/std@0.131.0/http/server.ts'
|
||||||
|
import * as jose from 'https://deno.land/x/jose@v4.14.4/index.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<boolean> {
|
||||||
|
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' },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
api:
|
||||||
|
enabled: true
|
||||||
|
address: 0.0.0.0:9001
|
||||||
|
|
||||||
|
sources:
|
||||||
|
docker_host:
|
||||||
|
type: docker_logs
|
||||||
|
exclude_containers:
|
||||||
|
- supabase-vector
|
||||||
|
|
||||||
|
transforms:
|
||||||
|
project_logs:
|
||||||
|
type: remap
|
||||||
|
inputs:
|
||||||
|
- docker_host
|
||||||
|
source: |-
|
||||||
|
.project = "default"
|
||||||
|
.event_message = del(.message)
|
||||||
|
.appname = del(.container_name)
|
||||||
|
del(.container_created_at)
|
||||||
|
del(.container_id)
|
||||||
|
del(.source_type)
|
||||||
|
del(.stream)
|
||||||
|
del(.label)
|
||||||
|
del(.image)
|
||||||
|
del(.host)
|
||||||
|
del(.stream)
|
||||||
|
router:
|
||||||
|
type: route
|
||||||
|
inputs:
|
||||||
|
- project_logs
|
||||||
|
route:
|
||||||
|
kong: '.appname == "supabase-kong"'
|
||||||
|
auth: '.appname == "supabase-auth"'
|
||||||
|
rest: '.appname == "supabase-rest"'
|
||||||
|
realtime: '.appname == "supabase-realtime"'
|
||||||
|
storage: '.appname == "supabase-storage"'
|
||||||
|
functions: '.appname == "supabase-functions"'
|
||||||
|
db: '.appname == "supabase-db"'
|
||||||
|
# Ignores non nginx errors since they are related with kong booting up
|
||||||
|
kong_logs:
|
||||||
|
type: remap
|
||||||
|
inputs:
|
||||||
|
- router.kong
|
||||||
|
source: |-
|
||||||
|
req, err = parse_nginx_log(.event_message, "combined")
|
||||||
|
if err == null {
|
||||||
|
.timestamp = req.timestamp
|
||||||
|
.metadata.request.headers.referer = req.referer
|
||||||
|
.metadata.request.headers.user_agent = req.agent
|
||||||
|
.metadata.request.headers.cf_connecting_ip = req.client
|
||||||
|
.metadata.request.method = req.method
|
||||||
|
.metadata.request.path = req.path
|
||||||
|
.metadata.request.protocol = req.protocol
|
||||||
|
.metadata.response.status_code = req.status
|
||||||
|
}
|
||||||
|
if err != null {
|
||||||
|
abort
|
||||||
|
}
|
||||||
|
# Ignores non nginx errors since they are related with kong booting up
|
||||||
|
kong_err:
|
||||||
|
type: remap
|
||||||
|
inputs:
|
||||||
|
- router.kong
|
||||||
|
source: |-
|
||||||
|
.metadata.request.method = "GET"
|
||||||
|
.metadata.response.status_code = 200
|
||||||
|
parsed, err = parse_nginx_log(.event_message, "error")
|
||||||
|
if err == null {
|
||||||
|
.timestamp = parsed.timestamp
|
||||||
|
.severity = parsed.severity
|
||||||
|
.metadata.request.host = parsed.host
|
||||||
|
.metadata.request.headers.cf_connecting_ip = parsed.client
|
||||||
|
url, err = split(parsed.request, " ")
|
||||||
|
if err == null {
|
||||||
|
.metadata.request.method = url[0]
|
||||||
|
.metadata.request.path = url[1]
|
||||||
|
.metadata.request.protocol = url[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != null {
|
||||||
|
abort
|
||||||
|
}
|
||||||
|
# Gotrue logs are structured json strings which frontend parses directly. But we keep metadata for consistency.
|
||||||
|
auth_logs:
|
||||||
|
type: remap
|
||||||
|
inputs:
|
||||||
|
- router.auth
|
||||||
|
source: |-
|
||||||
|
parsed, err = parse_json(.event_message)
|
||||||
|
if err == null {
|
||||||
|
.metadata.timestamp = parsed.time
|
||||||
|
.metadata = merge!(.metadata, parsed)
|
||||||
|
}
|
||||||
|
# PostgREST logs are structured so we separate timestamp from message using regex
|
||||||
|
rest_logs:
|
||||||
|
type: remap
|
||||||
|
inputs:
|
||||||
|
- router.rest
|
||||||
|
source: |-
|
||||||
|
parsed, err = parse_regex(.event_message, r'^(?P<time>.*): (?P<msg>.*)$')
|
||||||
|
if err == null {
|
||||||
|
.event_message = parsed.msg
|
||||||
|
.timestamp = to_timestamp!(parsed.time)
|
||||||
|
.metadata.host = .project
|
||||||
|
}
|
||||||
|
# Realtime logs are structured so we parse the severity level using regex (ignore time because it has no date)
|
||||||
|
realtime_logs:
|
||||||
|
type: remap
|
||||||
|
inputs:
|
||||||
|
- router.realtime
|
||||||
|
source: |-
|
||||||
|
.metadata.project = del(.project)
|
||||||
|
.metadata.external_id = .metadata.project
|
||||||
|
parsed, err = parse_regex(.event_message, r'^(?P<time>\d+:\d+:\d+\.\d+) \[(?P<level>\w+)\] (?P<msg>.*)$')
|
||||||
|
if err == null {
|
||||||
|
.event_message = parsed.msg
|
||||||
|
.metadata.level = parsed.level
|
||||||
|
}
|
||||||
|
# Storage logs may contain json objects so we parse them for completeness
|
||||||
|
storage_logs:
|
||||||
|
type: remap
|
||||||
|
inputs:
|
||||||
|
- router.storage
|
||||||
|
source: |-
|
||||||
|
.metadata.project = del(.project)
|
||||||
|
.metadata.tenantId = .metadata.project
|
||||||
|
parsed, err = parse_json(.event_message)
|
||||||
|
if err == null {
|
||||||
|
.event_message = parsed.msg
|
||||||
|
.metadata.level = parsed.level
|
||||||
|
.metadata.timestamp = parsed.time
|
||||||
|
.metadata.context[0].host = parsed.hostname
|
||||||
|
.metadata.context[0].pid = parsed.pid
|
||||||
|
}
|
||||||
|
# Postgres logs some messages to stderr which we map to warning severity level
|
||||||
|
db_logs:
|
||||||
|
type: remap
|
||||||
|
inputs:
|
||||||
|
- router.db
|
||||||
|
source: |-
|
||||||
|
.metadata.host = "db-default"
|
||||||
|
.metadata.parsed.timestamp = .timestamp
|
||||||
|
|
||||||
|
parsed, err = parse_regex(.event_message, r'.*(?P<level>INFO|NOTICE|WARNING|ERROR|LOG|FATAL|PANIC?):.*', numeric_groups: true)
|
||||||
|
|
||||||
|
if err != null || parsed == null {
|
||||||
|
.metadata.parsed.error_severity = "info"
|
||||||
|
}
|
||||||
|
if parsed != null {
|
||||||
|
.metadata.parsed.error_severity = parsed.level
|
||||||
|
}
|
||||||
|
if .metadata.parsed.error_severity == "info" {
|
||||||
|
.metadata.parsed.error_severity = "log"
|
||||||
|
}
|
||||||
|
.metadata.parsed.error_severity = upcase!(.metadata.parsed.error_severity)
|
||||||
|
|
||||||
|
sinks:
|
||||||
|
logflare_auth:
|
||||||
|
type: 'http'
|
||||||
|
inputs:
|
||||||
|
- auth_logs
|
||||||
|
encoding:
|
||||||
|
codec: 'json'
|
||||||
|
method: 'post'
|
||||||
|
request:
|
||||||
|
retry_max_duration_secs: 10
|
||||||
|
uri: 'http://analytics:4000/api/logs?source_name=gotrue.logs.prod&api_key=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
|
||||||
|
logflare_realtime:
|
||||||
|
type: 'http'
|
||||||
|
inputs:
|
||||||
|
- realtime_logs
|
||||||
|
encoding:
|
||||||
|
codec: 'json'
|
||||||
|
method: 'post'
|
||||||
|
request:
|
||||||
|
retry_max_duration_secs: 10
|
||||||
|
uri: 'http://analytics:4000/api/logs?source_name=realtime.logs.prod&api_key=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
|
||||||
|
logflare_rest:
|
||||||
|
type: 'http'
|
||||||
|
inputs:
|
||||||
|
- rest_logs
|
||||||
|
encoding:
|
||||||
|
codec: 'json'
|
||||||
|
method: 'post'
|
||||||
|
request:
|
||||||
|
retry_max_duration_secs: 10
|
||||||
|
uri: 'http://analytics:4000/api/logs?source_name=postgREST.logs.prod&api_key=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
|
||||||
|
logflare_db:
|
||||||
|
type: 'http'
|
||||||
|
inputs:
|
||||||
|
- db_logs
|
||||||
|
encoding:
|
||||||
|
codec: 'json'
|
||||||
|
method: 'post'
|
||||||
|
request:
|
||||||
|
retry_max_duration_secs: 10
|
||||||
|
# We must route the sink through kong because ingesting logs before logflare is fully initialised will
|
||||||
|
# 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=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
|
||||||
|
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=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
|
||||||
|
logflare_storage:
|
||||||
|
type: 'http'
|
||||||
|
inputs:
|
||||||
|
- storage_logs
|
||||||
|
encoding:
|
||||||
|
codec: 'json'
|
||||||
|
method: 'post'
|
||||||
|
request:
|
||||||
|
retry_max_duration_secs: 10
|
||||||
|
uri: 'http://analytics:4000/api/logs?source_name=storage.logs.prod.2&api_key=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
|
||||||
|
logflare_kong:
|
||||||
|
type: 'http'
|
||||||
|
inputs:
|
||||||
|
- kong_logs
|
||||||
|
- kong_err
|
||||||
|
encoding:
|
||||||
|
codec: 'json'
|
||||||
|
method: 'post'
|
||||||
|
request:
|
||||||
|
retry_max_duration_secs: 10
|
||||||
|
uri: 'http://analytics:4000/api/logs?source_name=cloudflare.logs.prod&api_key=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{:ok, _} = Application.ensure_all_started(:supavisor)
|
||||||
|
|
||||||
|
{:ok, version} =
|
||||||
|
case Supavisor.Repo.query!("select version()") do
|
||||||
|
%{rows: [[ver]]} -> Supavisor.Helpers.parse_pg_version(ver)
|
||||||
|
_ -> nil
|
||||||
|
end
|
||||||
|
|
||||||
|
params = %{
|
||||||
|
"external_id" => System.get_env("POOLER_TENANT_ID"),
|
||||||
|
"db_host" => "db",
|
||||||
|
"db_port" => System.get_env("POSTGRES_PORT"),
|
||||||
|
"db_database" => System.get_env("POSTGRES_DB"),
|
||||||
|
"require_user" => false,
|
||||||
|
"auth_query" => "SELECT * FROM pgbouncer.get_auth($1)",
|
||||||
|
"default_max_clients" => System.get_env("POOLER_MAX_CLIENT_CONN"),
|
||||||
|
"default_pool_size" => System.get_env("POOLER_DEFAULT_POOL_SIZE"),
|
||||||
|
"default_parameter_status" => %{"server_version" => version},
|
||||||
|
"users" => [%{
|
||||||
|
"db_user" => "pgbouncer",
|
||||||
|
"db_password" => System.get_env("POSTGRES_PASSWORD"),
|
||||||
|
"mode_type" => System.get_env("POOLER_POOL_MODE"),
|
||||||
|
"pool_size" => System.get_env("POOLER_DEFAULT_POOL_SIZE"),
|
||||||
|
"is_manager" => true
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Supavisor.Tenants.get_tenant_by_external_id(params["external_id"]) do
|
||||||
|
{:ok, _} = Supavisor.Tenants.create_tenant(params)
|
||||||
|
end
|
||||||
@@ -33,7 +33,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- sources:/Sources:cached
|
- sources:/Sources:cached
|
||||||
- user:/tmp/ssh-agent:ro
|
- user:/tmp/ssh-agent:ro
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- ${DOCKER_SOCKET_LOCATION}:/var/run/docker.sock
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
sources:
|
sources:
|
||||||
|
|||||||
Reference in New Issue
Block a user