diff --git a/.env.sample b/.env.sample index 1816f6915..371a875bf 100644 --- a/.env.sample +++ b/.env.sample @@ -32,8 +32,8 @@ AUTH_JWT_ALGORITHM=HS256 DATABASE_MULTITENANT_URL=postgresql://postgres:postgres@127.0.0.1:5433/postgres DATABASE_MULTITENANT_POOL_URL=postgresql://postgres:postgres@127.0.0.1:6454/postgres REQUEST_X_FORWARDED_HOST_REGEXP=^([a-z]{20}).local.(?:com|dev)$ -SERVER_ADMIN_API_KEYS=apikey -AUTH_ENCRYPTION_KEY=encryptionkey +SERVER_ADMIN_API_KEYS=this-value-should-be-changed-to-a-random-string +AUTH_ENCRYPTION_KEY=encryptionkey_Value:shouldBe_A_long_random_string ####################################### diff --git a/README.md b/README.md index 869e5ad54..7ff56bca4 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,8 @@ A scalable, lightweight object storage service. cp .env.sample .env && cp .env.test.sample .env.test ``` +The script at `scripts/init.sh` can be used to help generate strong values for important keys such as `SERVER_ADMIN_API_KEYS`. + **Your root directory should now have both `.env` and `.env.test` files.** - Then run the following: diff --git a/docker-compose-multi-tenant.yml b/docker-compose-multi-tenant.yml index 2f66e74c2..eba7523da 100644 --- a/docker-compose-multi-tenant.yml +++ b/docker-compose-multi-tenant.yml @@ -23,11 +23,13 @@ services: # Auth AUTH_JWT_SECRET: f023d3db-39dc-4ac9-87b2-b2be72e9162b AUTH_JWT_ALGORITHM: HS256 - AUTH_ENCRYPTION_KEY: encryptionkey + # change this value to a unique, strong value + AUTH_ENCRYPTION_KEY: encryptionkey_Value:shouldBe_A_long_random_string # Multi tenant Mode MULTI_TENANT: true DATABASE_MULTITENANT_URL: postgresql://postgres:postgres@multitenant_db:5432/postgres - SERVER_ADMIN_API_KEYS: apikey + # change this value to a strong API key + SERVER_ADMIN_API_KEYS: this-value-should-be-changed-to-a-random-string SERVER_ADMIN_PORT: 5001 REQUEST_X_FORWARDED_HOST_REGEXP: "^([a-z]{20}).local.(?:com|dev)$" # Migrations diff --git a/scripts/init.sh b/scripts/init.sh new file mode 100755 index 000000000..bd7a843f6 --- /dev/null +++ b/scripts/init.sh @@ -0,0 +1,108 @@ +#!/usr/bin/env bash + +# Help initialise self hosting to change environment variables +# +# Portions of this code are derived from Inder Singh's setup.sh shell script. +# Copyright 2025 Inder Singh. Licensed under Apache License 2.0. +# Original source: https://github.com/singh-inder/supabase-automated-self-host/blob/main/setup.sh +# + +set -e + +gen_hex() { + openssl rand -hex "$1" +} + +gen_base64() { + openssl rand -base64 "$1" +} + +base64_url_encode() { + openssl enc -base64 -A | tr '+/' '-_' | tr -d '=' +} + +prompt() { + local var=$1 msg=$2 default=$3 + read -p "$msg [$default]: " value + printf -v "$var" '%s' "${value:-$default}" +} + +promptYN() { + read -p "$1 y/N: " resp + resp=$(echo "$resp" | tr '[:upper:]' '[:lower:]') + [[ "$resp" == "y" ]] && echo true || echo false +} + +if ! command -v openssl >/dev/null 2>&1; then + echo "Error: openssl is required but not found." + exit 1 +fi + +multitenant=$(promptYN "Use multitenant?") +if $multitenant; then + prompt SERVER_ADMIN_API_KEYS "Enter SERVER_ADMIN_API_KEYS" $(gen_hex 16) + prompt DATABASE_MULTITENANT_URL "Enter DATABASE_MULTITENANT_URL" "" + prompt DATABASE_MULTITENANT_POOL_URL "Enter DATABASE_MULTITENANT_POOL_URL" "" +fi + +prompt AUTH_ENCRYPTION_KEY "Enter AUTH_ENCRYPTION_KEY" $(gen_hex 16) +prompt DATABASE_URL "Enter DATABASE_URL" "" +prompt DATABASE_POOL_URL "Enter DATABASE_URL" "" + +updateEnv=$(promptYN "Create .env and update?") +if $updateEnv; then + echo "Updating .env..." + cp .env.sample .env + if $multitenant; then + sed -i.old \ + -e "s|^# MULTI_TENANT=true$|MULTI_TENANT=true|" \ + -e "s|^SERVER_ADMIN_API_KEYS=.*$|SERVER_ADMIN_API_KEYS=${SERVER_ADMIN_API_KEYS}|" \ + .env + + if [[ -n "$DATABASE_MULTITENANT_URL" ]]; then + sed -i.old \ + -e "s|^DATABASE_MULTITENANT_URL=.*$|DATABASE_MULTITENANT_URL=${DATABASE_MULTITENANT_URL}|" \ + .env + fi + if [[ -n "$DATABASE_MULTITENANT_POOL_URL" ]]; then + sed -i.old \ + -e "s|^DATABASE_MULTITENANT_POOL_URL=.*$|DATABASE_MULTITENANT_POOL_URL=${DATABASE_MULTITENANT_POOL_URL}|" \ + .env + fi + fi + + sed -i.old \ + -e "s|^AUTH_ENCRYPTION_KEY=.*$|AUTH_ENCRYPTION_KEY=${AUTH_ENCRYPTION_KEY}|" \ + .env + + if [[ -n "$DATABASE_URL" ]]; then + sed -i.old \ + -e "s|^DATABASE_URL=.*$|DATABASE_URL=${DATABASE_URL}|" \ + .env + fi + if [[ -n "$DATABASE_POOL_URL" ]]; then + sed -i.old \ + -e "s|^DATABASE_POOL_URL=.*$|DATABASE_POOL_URL=${DATABASE_POOL_URL}|" \ + .env + fi +fi + +echo -e "\n\n#### Initialised values:\n" + +if $multitenant; then + echo "SERVER_ADMIN_API_KEYS: ${SERVER_ADMIN_API_KEYS}" + if [[ -n "$DATABASE_MULTITENANT_URL" ]]; then + echo "DATABASE_MULTITENANT_URL: ${DATABASE_MULTITENANT_URL}" + fi + if [[ -n "$DATABASE_MULTITENANT_POOL_URL" ]]; then + echo "DATABASE_MULTITENANT_POOL_URL: ${DATABASE_MULTITENANT_POOL_URL}" + fi +fi + +echo "AUTH_ENCRYPTION_KEY: ${AUTH_ENCRYPTION_KEY}" +if [[ -n "$DATABASE_URL" ]]; then + echo "DATABASE_URL: ${DATABASE_URL}" +fi +if [[ -n "$DATABASE_POOL_URL" ]]; then + echo "DATABASE_POOL_URL: ${DATABASE_POOL_URL}" +fi diff --git a/src/storage/events/pgboss/move-jobs.ts b/src/storage/events/pgboss/move-jobs.ts index d70695878..78ae40f80 100644 --- a/src/storage/events/pgboss/move-jobs.ts +++ b/src/storage/events/pgboss/move-jobs.ts @@ -59,7 +59,7 @@ export class MoveJobs extends BaseEvent { try { const sql = ` - INSERT INTO ${schema}.job ( + INSERT INTO :schema:.job ( id, name, priority, @@ -78,9 +78,9 @@ export class MoveJobs extends BaseEvent { policy, state ) - SELECT + SELECT id, - '${toQueue.name}' as name, + :to_queue_name as name, priority, data, retry_limit, @@ -94,23 +94,31 @@ export class MoveJobs extends BaseEvent { created_on, keep_until, output, - '${toQueue.policy}' as policy, + :to_queue_policy as policy, 'created' as state - FROM ${schema}.job - WHERE name = '${fromQueueName}' + FROM :schema:.job + WHERE name = :from_queue_name AND state IN ('created', 'active', 'retry') ON CONFLICT DO NOTHING ` - await tnx.raw(sql) + await tnx.raw(sql, { + schema: schema, + to_queue_name: toQueue.name, + to_queue_policy: toQueue.policy, + from_queue_name: fromQueueName + }) if (job.data.deleteJobsFromOriginalQueue) { const deleteSql = ` - DELETE FROM ${schema}.job - WHERE name = '${fromQueueName}' + DELETE FROM :schema:.job + WHERE name = :from_queue_name AND state IN ('created', 'active', 'retry') ` - await tnx.raw(deleteSql) + await tnx.raw(deleteSql, { + schema: schema, + from_queue_name: fromQueueName + }) } } catch (error) { logSchema.error(logger, '[PgBoss] Error while copying jobs', {