Ctrl K
ComunidadGuías…

once/docker-entrypoint.sh

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/bin/bash
# We use /bin/bash instead of /bin/sh because we need process substitution.
# See https://www.shellcheck.net/wiki/SC3001

check_user_is_correct() {
	if [[ "$(id -u -n)" != "authgear" ]]; then
		printf 1>&2 "docker-entrypoint.sh is supposed to be run with the user authgear.\n"
		exit 1
	fi
}

generate_password() {
	# We are going to use base64url to encode the generated bytes.
	# Since base64url encodes every 3 bytes into 4 base64 characters without padding,
	# The length of the random bytes must be a multiple of 3.
	# 33 bytes = 264 bits.
	head -c 33 /dev/urandom | basenc --base64url -w 0
}

docker_authgearonce_check_immutable_environment_variables_are_set() {
	if [[ -z "$AUTHGEAR_ONCE_LICENSE_KEY" ]]; then
		printf 1>&2 "AUTHGEAR_ONCE_LICENSE_KEY must be set.\n"
		exit 1
	fi
	if [[ -z "$AUTHGEAR_ONCE_LICENSE_EXPIRE_AT" ]]; then
		printf 1>&2 "AUTHGEAR_ONCE_LICENSE_EXPIRE_AT must be set.\n"
		exit 1
	fi
	# AUTHGEAR_ONCE_LICENSEE_EMAIL is optional.
	if [[ -z "$AUTHGEAR_ONCE_MACHINE_FINGERPRINT" ]]; then
		printf 1>&2 "AUTHGEAR_ONCE_MACHINE_FINGERPRINT must be set.\n"
		exit 1
	fi
	if [[ -z "$AUTHGEAR_ONCE_ADMIN_USER_EMAIL" ]]; then
		printf 1>&2 "AUTHGEAR_ONCE_ADMIN_USER_EMAIL must be set.\n"
		exit 1
	fi
	if [[ -z "$AUTHGEAR_ONCE_ADMIN_USER_PASSWORD" ]]; then
		printf 1>&2 "AUTHGEAR_ONCE_ADMIN_USER_PASSWORD must be set.\n"
		exit 1
	fi
	if [[ "$(printf "%s" "$AUTHGEAR_ONCE_ADMIN_USER_PASSWORD" | wc -c)" -lt 8 ]]; then
		printf 1>&2 "AUTHGEAR_ONCE_ADMIN_USER_PASSWORD must be at least 8 characters long.\n"
		exit 1
	fi
}

check_http_origin_is_set() {
	name="AUTHGEAR_HTTP_ORIGIN_$1"
	value="${!name}"

	if [[ -z "$value" ]]; then
		printf 1>&2 "%s must be set. This will be used to configure nginx and certbot.\n" "$name"
		exit 1
	fi
}

check_http_origin_not_equal_to_each_other() {
	name1="AUTHGEAR_HTTP_ORIGIN_$1"
	name2="AUTHGEAR_HTTP_ORIGIN_$2"
	value1="${!name1}"
	value2="${!name2}"

	if [[ "$value1" == "$value2" ]]; then
		printf 1>&2 "%s must not equal %s.\n" "$name1" "$name2"
		exit 1
	fi
}

docker_authgearonce_create_directory() {
	dir="$(dirname "$AUTHGEARONCE_ENV_SHELL_SCRIPT")"
	sudo mkdir -p "$dir"
	sudo chmod 0755 "$dir"
	sudo find "$dir" \! -user "authgear" -exec chown "authgear":"authgear" '{}' +
}

docker_postgresql_create_database_directories() {
	sudo mkdir -p "$PGDATA"
	sudo chmod 0700 "$PGDATA"
	sudo mkdir -p /var/run/postgresql
	sudo chmod 0775 /var/run/postgresql

	sudo find "$PGDATA" \! -user "authgear" -exec chown "authgear":"authgear" '{}' +
	sudo find /var/run/postgresql \! -user "authgear" -exec chown "authgear":"authgear" '{}' +
}

docker_postgresql_initdb() {
	# This is the most secure method supported by PostgreSQL 16.
	# See https://www.postgresql.org/docs/16/auth-password.html
	auth_method="scram-sha-256"

	# Google Cloud SQL PostgreSQL uses libc as the locale provider.
	# So we just follow it.
	# Since --locale-provider=libc is the default, we need not specify it.
	# NOTE: --pwfile uses process substitution.
	initdb \
		--username="authgear" \
		--pwfile=<(printf "%s\n" "$POSTGRES_PASSWORD") \
		--encoding="UTF8" \
		--auth-host="$auth_method" \
		--auth-local="$auth_method"
}

docker_postgresql_temp_server_start() {
	pg_ctl start --wait
}

docker_postgresql_temp_server_stop() {
	pg_ctl stop \
		--mode fast \
		--wait
}

# docker_postgresql_psql is a wrapper of psql.
docker_postgresql_psql() {
	PGPASSWORD="$POSTGRES_PASSWORD" psql \
		--variable ON_ERROR_STOP=1 \
		--no-psqlrc "$@"
}

docker_postgresql_create_database() {
	docker_postgresql_temp_server_start

	# Connect to the database `postgres` that must exists.
	db_exists="$(docker_postgresql_psql \
		--dbname postgres \
		--set db="authgear" \
		--tuples-only <<-'EOSQL'
SELECT 1 FROM pg_database WHERE datname = :'db';
	EOSQL
	)"
	if [[ -z "$db_exists" ]]; then
		docker_postgresql_psql \
			--dbname postgres \
			--set db="authgear" <<-'EOSQL'
CREATE DATABASE :"db";
	EOSQL
	fi

	docker_postgresql_temp_server_stop
}

docker_redis_create_directories() {
	sudo mkdir -p "$AUTHGEARONCE_REDIS_DATA_DIRECTORY"
	sudo chmod 0700 "$AUTHGEARONCE_REDIS_DATA_DIRECTORY"
	sudo mkdir -p /var/run/redis
	sudo chmod 0700 /var/run/redis

	sudo find "$AUTHGEARONCE_REDIS_DATA_DIRECTORY" \! -user "authgear" -exec chown "authgear:authgear" '{}' +
	sudo find /var/run/redis \! -user "authgear" -exec chown "authgear:authgear" '{}' +
}

docker_redis_write_acl_file() {
	printf "user default on +@all ~* >%s\n" "$REDIS_PASSWORD" > /var/run/redis/users.acl
}

docker_nginx_create_directories() {
	# This is the default location of --prefix
	sudo mkdir -p /usr/share/nginx
	sudo chmod 0755 /usr/share/nginx
	# Ubuntu nginx-common installs files to here.
	sudo mkdir -p /etc/nginx
	sudo chmod 0755 /etc/nginx
	# When I run `nginx -t`, it tries to write files to /var/lib/nginx.
	sudo mkdir -p /var/lib/nginx
	sudo chmod 0755 /var/lib/nginx
	# The directory to contain the pid file.
	sudo mkdir -p /var/run/nginx
	sudo chmod 0755 /var/run/nginx
	# The directory to contain the log file.
	sudo mkdir -p /var/log/nginx
	sudo chmod 0755 /var/log/nginx
	# The directory to write temp files.
	sudo mkdir -p /tmp/nginx
	sudo chmod 0755 /tmp/nginx

	# There is a broken symlink /usr/share/nginx/modules
	# I do not know why.
	sudo rm -f /usr/share/nginx/modules

	sudo find /usr/share/nginx \! -user "authgear" -exec chown "authgear:authgear" '{}' +
	sudo find /etc/nginx \! -user "authgear" -exec chown "authgear:authgear" '{}' +
	sudo find /var/lib/nginx \! -user "authgear" -exec chown "authgear:authgear" '{}' +
	sudo find /var/run/nginx \! -user "authgear" -exec chown "authgear:authgear" '{}' +
	sudo find /var/log/nginx \! -user "authgear" -exec chown "authgear:authgear" '{}' +
	sudo find /tmp/nginx \! -user "authgear" -exec chown "authgear:authgear" '{}' +
}

docker_nginx_render_server_block() {
	name="AUTHGEAR_HTTP_ORIGIN_$1"
	value="${!name}"

	if [[ -z "$value" ]]; then
		printf 1>&2 "%s must be set. This will be used to configure nginx and certbot.\n" "$name"
		exit 1
	fi

	scheme=""
	without_scheme=""
	case "$value" in
	"http://"*)
		scheme="http"
		without_scheme="${value#http://}"
		;;
	"https://"*)
		scheme="https"
		without_scheme="${value#https://}"
		;;
	*)
		;;
	esac
	if [[ -z "$without_scheme" ]]; then
		printf 1>&2 "%s must start with http:// or https://.\n" "$name"
		exit 1
	fi

	contains_port="false"
	contains_path="false"
	contains_query="false"
	contains_fragment="false"
	case "$without_scheme" in
	*:*)
		contains_port="true"
		;;
	*/*)
		contains_path="true"
		;;
	*'?'*)
		contains_query="true"
		;;
	*'#'*)
		contains_fragment="true"
		;;
	*)
		;;
	esac
	if [[ "$contains_port" == "true" ]]; then
		printf 1>&2 "%s must not contain port.\n" "$name"
		exit 1
	fi
	if [[ "$contains_path" == "true" ]]; then
		printf 1>&2 "%s must not contain path.\n" "$name"
		exit 1
	fi
	if [[ "$contains_query" == "true" ]]; then
		printf 1>&2 "%s must not contain query.\n" "$name"
		exit 1
	fi
	if [[ "$contains_fragment" == "true" ]]; then
		printf 1>&2 "%s must not contain fragment.\n" "$name"
		exit 1
	fi

	sed -i -E "s,__AUTHGEAR_HTTP_HOST_${1}__,${without_scheme}," /etc/nginx/nginx.conf
	sed -i -E "s,__AUTHGEAR_HTTP_EXPECTED_SCHEME_${1}__,${scheme}," /etc/nginx/nginx.conf
}

docker_authgearonce_check_mutable_environment_variables_are_set() {
	if [[ -z "$AUTHGEAR_ONCE_IMAGE" ]]; then
		printf 1>&2 "AUTHGEAR_ONCE_IMAGE must be set.\n"
		exit 1
	fi

	check_http_origin_is_set "PORTAL"
	check_http_origin_is_set "ACCOUNTS"
	check_http_origin_is_set "PROJECT"

	check_http_origin_not_equal_to_each_other "PORTAL" "ACCOUNTS"
	check_http_origin_not_equal_to_each_other "PORTAL" "PROJECT"
	check_http_origin_not_equal_to_each_other "ACCOUNTS" "PROJECT"
}

docker_nginx_create_nginx_conf() {
	cp /etc/nginx/nginx.conf.sample /etc/nginx/nginx.conf
	docker_nginx_render_server_block "PORTAL"
	docker_nginx_render_server_block "ACCOUNTS"
	docker_nginx_render_server_block "PROJECT"
}

docker_nginx_create_fake_certificate() {
	openssl req \
		-batch \
		-x509 \
		-newkey rsa:4096 \
		-noenc \
		-keyout /etc/nginx/fake_certficate.key \
		-out /etc/nginx/fake_certficate.crt \
		-subj "/CN=invalid.invalid/OU=Fake certificate generated by docker-entrypoint.sh" \
		-days 365
}

docker_certbot_create_directories() {
	# --work-dir
	sudo mkdir -p /var/lib/letsencrypt
	# --logs-dir
	sudo mkdir -p /var/log/letsencrypt
	# --config-dir
	sudo mkdir -p "$AUTHGEARONCE_CERTBOT_DATA_DIRECTORY"

	sudo find /var/lib/letsencrypt \! -user "authgear" -exec chown "authgear:authgear" '{}' +
	sudo find /var/log/letsencrypt \! -user "authgear" -exec chown "authgear:authgear" '{}' +
	sudo find "$AUTHGEARONCE_CERTBOT_DATA_DIRECTORY" \! -user "authgear" -exec chown "authgear:authgear" '{}' +
}

docker_certbot_create_cli_ini() {
	# certbot stores its data (like certificates, accounts) in --config-dir
	# It also reads its config (cli.ini) in --config-dir.
	# If we naively volume mount --config-dir, --config-dir/cli.ini will be obscured by the mount.
	# Therefore, when we build the image, we copy the original /etc/letsencrypt/cli.ini to /home/authgear/certbot.ini,
	# and we always write a fresh --config-dir/cli.ini.
	cli_ini="$AUTHGEARONCE_CERTBOT_DATA_DIRECTORY/cli.ini"
	cp /home/authgear/certbot.ini.example "$cli_ini"
	sed -E -i 's,^#?\s*(max-log-backups)\s+.*,\1 = 10,' "$cli_ini"
	sed -E -i 's,^#?\s*(preconfigured-renewal)\s+.*,\1 = False,' "$cli_ini"

	if ! grep -E '^max-log-backups = 10' "$cli_ini" 1>/dev/null; then
		printf 1>&2 "failed to set max-log-backups in %s\n" "$cli_ini"
		exit 1
	fi

	if ! grep -E '^preconfigured-renewal = False' "$cli_ini" 1>/dev/null; then
		printf 1>&2 "failed to set preconfigured-renewal in %s\n" "$cli_ini"
		exit 1
	fi
}

docker_minio_create_directories() {
	sudo mkdir -p "$AUTHGEARONCE_MINIO_DATA_DIRECTORY"
	sudo chmod 0700 "$AUTHGEARONCE_MINIO_DATA_DIRECTORY"

	sudo find "$AUTHGEARONCE_MINIO_DATA_DIRECTORY" \! -user "authgear" -exec chown "authgear:authgear" '{}' +
}

docker_minio_create_buckets() {
	# We need to start the server temporarily.
	minio server "$AUTHGEARONCE_MINIO_DATA_DIRECTORY" &
	minio_pid=$!

	# 1 second should be enough. It starts very fast.
	sleep 1
	mcli alias set local http://localhost:9000 "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD"
	mcli mb --ignore-existing local/images
	mcli mb --ignore-existing local/userexport

	kill -SIGTERM "$minio_pid"
}

docker_tls_update_ca_certificates() {
	# Make the certificates in /usr/local/share/ca-certificates take effect.
	sudo update-ca-certificates
}

docker_dns_update_etc_hosts() {
	echo '127.0.0.1 accounts.projects.authgear' | sudo tee -a /etc/hosts >/dev/null
	echo '127.0.0.1 project.projects.authgear' | sudo tee -a /etc/hosts >/dev/null
}

docker_authgear_source_env() {
	# It is put at ~/.bashrc so that
	# `docker compose exec THE_SERVICE bash` will also source this file.
	source "/home/authgear/.bashrc"
}

docker_authgear_run_database_migrations() {
	docker_postgresql_temp_server_start

	authgear database migrate up
	authgear audit database migrate up
	authgear images database migrate up
	authgear search database migrate up
	authgear-portal database migrate up

	docker_postgresql_temp_server_stop
}

docker_authgear_create_deployment_runtime_directory() {
	mkdir -p /home/authgear/authgear_deployment_runtime

	cat > /home/authgear/authgear_deployment_runtime/authgear.secrets.yaml <<EOF
secrets:
- key: db
  data:
    database_schema: "$DATABASE_SCHEMA"
    database_url: "$DATABASE_URL"
- key: audit.db
  data:
    database_schema: "$AUDIT_DATABASE_SCHEMA"
    database_url: "$AUDIT_DATABASE_URL"
- key: search.db
  data:
    database_schema: "$SEARCH_DATABASE_SCHEMA"
    database_url: "$SEARCH_DATABASE_URL"
- key: redis
  data:
    redis_url: "$REDIS_URL"
- key: analytic.redis
  data:
    redis_url: "$ANALYTIC_REDIS_URL"
EOF
}

docker_authgear_first_time_setup() {
	docker_wrapper &
	wrapper_pid=$!
	# Wait 2 seconds for the server to start.
	sleep 2


	app_id_accounts="accounts"
	init_output_accounts="$(mktemp -d)"
	# The trailing . in src is important.
	cp -R "$HOME/resources/." "$init_output_accounts"
	authgear init --interactive=false \
		--purpose=portal \
		--for-helm-chart=true \
		--app-id="$app_id_accounts" \
		--public-origin="$AUTHGEAR_HTTP_ORIGIN_ACCOUNTS" \
		--portal-origin="$AUTHGEAR_HTTP_ORIGIN_PORTAL" \
		--portal-client-id=portal \
		--phone-otp-mode=sms \
		--disable-email-verification=false \
		--disable-public-signup=true \
		--smtp-host="$AUTHGEAR_SMTP_HOST" \
		--smtp-port="$AUTHGEAR_SMTP_PORT" \
		--smtp-username="$AUTHGEAR_SMTP_USERNAME" \
		--smtp-password="$AUTHGEAR_SMTP_PASSWORD" \
		--smtp-sender-address="$AUTHGEAR_SMTP_SENDER_ADDRESS" \
		--search-implementation=postgresql \
		-o "$init_output_accounts"

	authgear-portal internal configsource create "$init_output_accounts"
	rm -r "$init_output_accounts"

	app_id_project="project"
	init_output_project="$(mktemp -d)"
	authgear init --interactive=false \
		--purpose=project \
		--for-helm-chart=true \
		--app-id="$app_id_project" \
		--public-origin="$AUTHGEAR_HTTP_ORIGIN_PROJECT" \
		--phone-otp-mode=sms \
		--disable-email-verification=false \
		--smtp-host="$AUTHGEAR_SMTP_HOST" \
		--smtp-port="$AUTHGEAR_SMTP_PORT" \
		--smtp-username="$AUTHGEAR_SMTP_USERNAME" \
		--smtp-password="$AUTHGEAR_SMTP_PASSWORD" \
		--smtp-sender-address="$AUTHGEAR_SMTP_SENDER_ADDRESS" \
		--search-implementation=postgresql \
		-o "$init_output_project"
	cp -R "$HOME/project-resources/." "$init_output_project"
	authgear-portal internal configsource create "$init_output_project"
	rm -r "$init_output_project"

	# Set up domains first.
	# Otherwise, Admin API server cannot resolve which project.
	docker_authgear_setup_domains

	query_file="$(mktemp)"
	cat >"$query_file" <<'EOF'
mutation createUser($email: String!, $password: String!) {
  createUser(input: {
    definition: {
      loginID: {
        key: "email"
        value: $email
      }
    }
    password: $password
  }) {
    user {
      id
    }
  }
}
EOF

	query_output="$(mktemp)"
	authgear internal admin-api invoke \
		--app-id accounts \
		--endpoint "http://localhost:3002" \
		--host "accounts.projects.authgear" \
		--query-file "$query_file" \
		--operation-name "createUser" \
		--variables-json "$(jq -cn --arg email "$AUTHGEAR_ONCE_ADMIN_USER_EMAIL" --arg password "$AUTHGEAR_ONCE_ADMIN_USER_PASSWORD" '{email: $email, password: $password}')" | tee "$query_output"
	decoded_node_id="$(jq <"$query_output" --raw-output '.data.createUser.user.id' | basenc --base64url --decode)"
	raw_id="${decoded_node_id#User:}"

	# Add collaborator to both projects.
	authgear-portal internal collaborator add \
		--app-id "$app_id_accounts" \
		--user-id "$raw_id" \
		--role owner
	authgear-portal internal collaborator add \
		--app-id "$app_id_project" \
		--user-id "$raw_id" \
		--role owner

	kill -SIGTERM "$wrapper_pid"
	# Wait 2 seconds to let the process exits.
	sleep 2
}

docker_authgear_rerun_setup() {
	docker_wrapper &
	wrapper_pid=$!
	# Wait 2 seconds for the server to start.
	sleep 2


	docker_authgear_setup_domains


	kill -SIGTERM "$wrapper_pid"
	# Wait 2 seconds to let the process exits.
	sleep 2
}

docker_authgearonce_generate_passwords() {
	{
		printf "export POSTGRES_PASSWORD=%s\n" "$(generate_password)"
		printf "export REDIS_PASSWORD=%s\n" "$(generate_password)"
		printf "export MINIO_ROOT_PASSWORD=%s\n" "$(generate_password)"
	} >> "$1"
}

docker_authgearonce_persist_immutable_environment_variables() {
	# We persist the environment variables
	# NOTE(once): Possibly breaking change
	# These environment variables are part of the public API between the once image and the once command.
	# The once command provide these variables with `-e` in `docker run`.
	# If you introduce a new environment variable in the future,
	# you must think of the missing case of the environment variable,
	# as there is a running installation of the old version without this environment variable.
	{
		printf "export AUTHGEAR_ONCE_LICENSE_KEY=%s\n" "$AUTHGEAR_ONCE_LICENSE_KEY"
		printf "export AUTHGEAR_ONCE_LICENSE_EXPIRE_AT=%s\n" "$AUTHGEAR_ONCE_LICENSE_EXPIRE_AT"
		printf "export AUTHGEAR_ONCE_LICENSEE_EMAIL=%s\n" "$AUTHGEAR_ONCE_LICENSEE_EMAIL"
		printf "export AUTHGEAR_ONCE_MACHINE_FINGERPRINT=%s\n" "$AUTHGEAR_ONCE_MACHINE_FINGERPRINT"
		printf "export AUTHGEAR_ONCE_ADMIN_USER_EMAIL=%s\n" "$AUTHGEAR_ONCE_ADMIN_USER_EMAIL"
		printf "export AUTHGEAR_ONCE_ADMIN_USER_PASSWORD=%s\n" "$AUTHGEAR_ONCE_ADMIN_USER_PASSWORD"
	} >> "$1"
	if [[ -n "$AUTHGEAR_SMTP_HOST" ]]; then
		printf "export AUTHGEAR_SMTP_HOST=%s\n" "$AUTHGEAR_SMTP_HOST" >> "$1"
	fi
	if [[ -n "$AUTHGEAR_SMTP_PORT" ]]; then
		printf "export AUTHGEAR_SMTP_PORT=%s\n" "$AUTHGEAR_SMTP_PORT" >> "$1"
	fi
	if [[ -n "$AUTHGEAR_SMTP_USERNAME" ]]; then
		printf "export AUTHGEAR_SMTP_USERNAME=%s\n" "$AUTHGEAR_SMTP_USERNAME" >> "$1"
	fi
	if [[ -n "$AUTHGEAR_SMTP_PASSWORD" ]]; then
		printf "export AUTHGEAR_SMTP_PASSWORD=%s\n" "$AUTHGEAR_SMTP_PASSWORD" >> "$1"
	fi
	if [[ -n "$AUTHGEAR_SMTP_SENDER_ADDRESS" ]]; then
		printf "export AUTHGEAR_SMTP_SENDER_ADDRESS=%s\n" "$AUTHGEAR_SMTP_SENDER_ADDRESS" >> "$1"
	fi
}

docker_authgearonce_persist_mutable_environment_variables() {
	{
		printf "export AUTHGEAR_ONCE_IMAGE=%s\n" "$AUTHGEAR_ONCE_IMAGE"
		printf "export AUTHGEAR_HTTP_ORIGIN_PROJECT=%s\n" "$AUTHGEAR_HTTP_ORIGIN_PROJECT"
		printf "export AUTHGEAR_HTTP_ORIGIN_PORTAL=%s\n" "$AUTHGEAR_HTTP_ORIGIN_PORTAL"
		printf "export AUTHGEAR_HTTP_ORIGIN_ACCOUNTS=%s\n" "$AUTHGEAR_HTTP_ORIGIN_ACCOUNTS"
		printf "export AUTHGEAR_CERTBOT_ENABLED=%s\n" "$AUTHGEAR_CERTBOT_ENABLED"
		printf "export AUTHGEAR_CERTBOT_ENVIRONMENT=%s\n" "$AUTHGEAR_CERTBOT_ENVIRONMENT"
		printf "export AUTHGEAR_CERTBOT_RUN_INTERVAL=%s\n" "$AUTHGEAR_CERTBOT_RUN_INTERVAL"
	} >> "$1"
}

docker_authgearonce_create_env_sh() {
	docker_authgearonce_check_immutable_environment_variables_are_set
	docker_authgearonce_check_mutable_environment_variables_are_set

	printf 1>&2 "Creating %s\n" "$AUTHGEARONCE_ENV_SHELL_SCRIPT"
	docker_authgearonce_generate_passwords "$AUTHGEARONCE_ENV_SHELL_SCRIPT"

	docker_authgearonce_persist_immutable_environment_variables "$AUTHGEARONCE_ENV_SHELL_SCRIPT"
	docker_authgearonce_persist_mutable_environment_variables "$AUTHGEARONCE_ENV_SHELL_SCRIPT"
}

docker_authgearonce_update_env_sh() {
	docker_authgearonce_check_mutable_environment_variables_are_set

	printf 1>&2 "Updating %s\n" "$AUTHGEARONCE_ENV_SHELL_SCRIPT"

	original="$AUTHGEARONCE_ENV_SHELL_SCRIPT"
	updated="${AUTHGEARONCE_ENV_SHELL_SCRIPT}.tmp"

	sed <"$original" >"$updated" -E '/^export (AUTHGEAR_ONCE_IMAGE|AUTHGEAR_HTTP_ORIGIN_PROJECT|AUTHGEAR_HTTP_ORIGIN_PORTAL|AUTHGEAR_HTTP_ORIGIN_ACCOUNTS|AUTHGEAR_CERTBOT_ENABLED|AUTHGEAR_CERTBOT_ENVIRONMENT|AUTHGEAR_CERTBOT_RUN_INTERVAL)=/d'

	docker_authgearonce_persist_mutable_environment_variables "$updated"
	mv "$updated" "$original"
}

docker_authgear_setup_domains() {
	docker_postgresql_psql \
		--dbname authgear <<-'EOSQL'
DELETE FROM _portal_domain;
	EOSQL

	app_id_accounts="accounts"
	host_accounts="$(echo "$AUTHGEAR_HTTP_ORIGIN_ACCOUNTS" | awk -F '://' '{ print $2 }')"
	authgear-portal internal domain create-custom "$app_id_accounts" --domain "$host_accounts" --apex-domain "$host_accounts"

	app_id_project="project"
	host_project="$(echo "$AUTHGEAR_HTTP_ORIGIN_PROJECT" | awk -F '://' '{ print $2 }')"
	authgear-portal internal domain create-custom "$app_id_project" --domain "$host_project" --apex-domain "$host_project"

	authgear-portal internal domain create-default --default-domain-suffix '.projects.authgear'

	# We need to update the public_origin, as well as,
	# the redirect URIs of the portal.
	sql_output=$(docker_postgresql_psql \
		--dbname authgear \
		--tuples-only \
		--set app_id="$app_id_accounts" <<-'EOSQL'
SELECT data->>'authgear.yaml' FROM _portal_config_source where app_id = :'app_id';
		EOSQL
	)
	updated=$(echo -n "$sql_output" \
		| sed -E 's/[[:space:]]//' \
		| base64 -d \
		| yq --yaml-output --arg public_origin "$AUTHGEAR_HTTP_ORIGIN_ACCOUNTS" '.http.public_origin = $public_origin' \
		| yq --yaml-output --arg portal_origin "$AUTHGEAR_HTTP_ORIGIN_PORTAL" '.oauth.clients[0].post_logout_redirect_uris[0] = "\($portal_origin)/"' \
		| yq --yaml-output --arg portal_origin "$AUTHGEAR_HTTP_ORIGIN_PORTAL" '.oauth.clients[0].redirect_uris[0] = "\($portal_origin)/oauth-redirect"' \
		| base64 -w 0)
	docker_postgresql_psql \
		--dbname authgear \
		--set app_id="$app_id_accounts" \
		--set v="$updated" <<-'EOSQL'
UPDATE _portal_config_source
SET data = jsonb_set(data, '{authgear.yaml}', to_jsonb(:'v' ::text))
WHERE app_id = :'app_id';
		EOSQL

	# This is rather simple.
	# We just need to update the public_origin.
	sql_output=$(docker_postgresql_psql \
		--dbname authgear \
		--tuples-only \
		--set app_id="$app_id_project" <<-'EOSQL'
SELECT data->>'authgear.yaml' FROM _portal_config_source where app_id = :'app_id';
		EOSQL
	)
	updated=$(echo -n "$sql_output" \
		| sed -E 's/[[:space:]]//' \
		| base64 -d \
		| yq --yaml-output --arg public_origin "$AUTHGEAR_HTTP_ORIGIN_PROJECT" '.http.public_origin = $public_origin' \
		| base64 -w 0)
	docker_postgresql_psql \
		--dbname authgear \
		--set app_id="$app_id_project" \
		--set v="$updated" <<-'EOSQL'
UPDATE _portal_config_source
SET data = jsonb_set(data, '{authgear.yaml}', to_jsonb(:'v' ::text))
WHERE app_id = :'app_id';
		EOSQL
}

main() {
	check_user_is_correct

	docker_authgearonce_create_directory

	what_to_do=''
	if [[ ! -r "$AUTHGEARONCE_ENV_SHELL_SCRIPT" ]]; then
		# The env file does not exist.
		# This means the volume is new.
		# In this case, we require the environment variables to be set.
		what_to_do='first_time_setup'
		docker_authgearonce_create_env_sh
	elif [[ -n "$AUTHGEAR_ONCE_IMAGE" ]]; then
		# Just pick one of the required environment to check.
		# If it is non-empty, then the setup command is re-run.
		what_to_do='rerun_setup'
		docker_authgearonce_update_env_sh
	else
		# Otherwise, the start command is run.
		printf 1>&2 "Reading %s\n" "$AUTHGEARONCE_ENV_SHELL_SCRIPT"
	fi
	# Always source the env file.
	. "$AUTHGEARONCE_ENV_SHELL_SCRIPT"

	docker_nginx_create_directories
	docker_nginx_create_fake_certificate
	docker_nginx_create_nginx_conf

	docker_certbot_create_directories
	docker_certbot_create_cli_ini

	docker_postgresql_create_database_directories

	# If this file exists and its size is greater than zero,
	# then we consider the database has initialized.
	if [[ -s "$PGDATA/PG_VERSION" ]]; then
		printf 1>&2 "PostgreSQL database directory (%s) seems initialized. Skipping initialization.\n" "$PGDATA"
	else
		docker_postgresql_initdb

		# initdb will create the given database user (--username), and
		# the database `postgres`, `template1` and `template0`.
		# It does not provide an option to create more additional databases.
		# Thus we need to do that in a separate step.
		docker_postgresql_create_database
	fi

	docker_redis_create_directories
	docker_redis_write_acl_file

	docker_minio_create_directories
	docker_minio_create_buckets

	docker_tls_update_ca_certificates

	docker_dns_update_etc_hosts

	docker_authgear_source_env
	docker_authgear_run_database_migrations
	docker_authgear_create_deployment_runtime_directory

	if [[ "$what_to_do" == 'first_time_setup' ]]; then
		docker_authgear_first_time_setup
		exit_status="$?"
		if [[ "$exit_status" -ne 0 ]]; then
			exit "$exit_status"
		fi
	elif [[ "$what_to_do" == 'rerun_setup' ]]; then
		docker_authgear_rerun_setup
		exit_status="$?"
		if [[ "$exit_status" -ne 0 ]]; then
			exit "$exit_status"
		fi
	fi

	# Replace this process with the given arguments.
	exec "$@"
}

main "$@"