299 lines
8.8 KiB
Bash
Executable File
299 lines
8.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
repo_root="$(cd "${script_dir}/.." && pwd)"
|
|
|
|
mode="nossl"
|
|
env_file="${repo_root}/.env.devprod"
|
|
restart_services="0"
|
|
env_changed="0"
|
|
app_config_changed="0"
|
|
|
|
usage() {
|
|
cat <<USAGE
|
|
Usage: ./scripts/sync-bundled-runtime-env-from-nextcloud.sh [options]
|
|
|
|
Reads bundled runtime credentials from qortal_integration app config and, when
|
|
they differ, writes them back into the env file used by docker recreate flows.
|
|
|
|
Options:
|
|
--mode ssl|nossl Compose mode (default: nossl)
|
|
--env-file <path> Env file to update (default: .env.devprod)
|
|
--restart-services Recreate broker + external_auth if env values change
|
|
-h, --help Show this help
|
|
USAGE
|
|
}
|
|
|
|
read_kv() {
|
|
local key="$1"
|
|
local line
|
|
line="$(grep -m1 -E "^${key}=" "${env_file}" || true)"
|
|
if [[ -z "${line}" ]]; then
|
|
return 1
|
|
fi
|
|
echo "${line#*=}"
|
|
}
|
|
|
|
set_kv() {
|
|
local key="$1"
|
|
local value="$2"
|
|
local esc
|
|
esc="${value//\\/\\\\}"
|
|
esc="${esc//&/\\&}"
|
|
esc="${esc//|/\\|}"
|
|
if grep -q -E "^${key}=" "${env_file}"; then
|
|
sed -i -E "s|^${key}=.*|${key}=${esc}|" "${env_file}"
|
|
else
|
|
echo "${key}=${value}" >> "${env_file}"
|
|
fi
|
|
}
|
|
|
|
json_escape() {
|
|
local input="$1"
|
|
input="${input//\\/\\\\}"
|
|
input="${input//\"/\\\"}"
|
|
input="${input//$'\n'/ }"
|
|
input="${input//$'\r'/ }"
|
|
echo "${input}"
|
|
}
|
|
|
|
extract_json_value() {
|
|
local json="$1"
|
|
local key="$2"
|
|
if command -v jq >/dev/null 2>&1; then
|
|
echo "${json}" | jq -r --arg key "${key}" '.[$key] // .data[$key] // empty'
|
|
return 0
|
|
fi
|
|
echo "${json}" | sed -n "s/.*\"${key}\"[[:space:]]*:[[:space:]]*\"\\([^\"]*\\)\".*/\\1/p" | head -n1
|
|
}
|
|
|
|
ensure_docker_access() {
|
|
local docker_path=""
|
|
docker_path="$(type -P docker || true)"
|
|
if [[ -z "${docker_path}" ]]; then
|
|
echo "docker is required"
|
|
exit 1
|
|
fi
|
|
|
|
if "${docker_path}" info >/dev/null 2>&1; then
|
|
DOCKER_CMD=("${docker_path}")
|
|
return 0
|
|
fi
|
|
|
|
if ! command -v sudo >/dev/null 2>&1; then
|
|
echo "docker is installed but this user cannot access it, and sudo is unavailable."
|
|
exit 1
|
|
fi
|
|
|
|
if sudo -n "${docker_path}" info >/dev/null 2>&1; then
|
|
DOCKER_CMD=(sudo "${docker_path}")
|
|
return 0
|
|
fi
|
|
|
|
echo "docker requires elevated privileges on this machine."
|
|
echo "You may be prompted for your sudo password."
|
|
if sudo "${docker_path}" info >/dev/null 2>&1; then
|
|
DOCKER_CMD=(sudo "${docker_path}")
|
|
return 0
|
|
fi
|
|
|
|
echo "Unable to access docker, even with sudo."
|
|
exit 1
|
|
}
|
|
|
|
docker() {
|
|
"${DOCKER_CMD[@]}" "$@"
|
|
}
|
|
|
|
occ_app_get() {
|
|
local key="$1"
|
|
docker compose -f "${compose_file}" --env-file "${env_file}" exec -T app \
|
|
php occ config:app:get qortal_integration "${key}" 2>/dev/null | tr -d '\r' | xargs || true
|
|
}
|
|
|
|
occ_app_set() {
|
|
local key="$1"
|
|
local value="$2"
|
|
docker compose -f "${compose_file}" --env-file "${env_file}" exec -T app \
|
|
php occ config:app:set qortal_integration "${key}" --value="${value}" >/dev/null 2>&1
|
|
}
|
|
|
|
test_external_auth_session() {
|
|
local app_id="$1"
|
|
local app_secret="$2"
|
|
local response token
|
|
if [[ -z "${app_id}" || -z "${app_secret}" ]]; then
|
|
return 1
|
|
fi
|
|
response="$(
|
|
curl -sS --max-time 15 -X POST "http://127.0.0.1:${external_auth_host_port}/sessions" \
|
|
-H 'content-type: application/json' \
|
|
-d "{\"appId\":\"$(json_escape "${app_id}")\",\"appSecret\":\"$(json_escape "${app_secret}")\"}" || true
|
|
)"
|
|
token="$(extract_json_value "${response}" "token")"
|
|
[[ -n "${token}" ]]
|
|
}
|
|
|
|
mark_env_external_auth_credentials() {
|
|
local app_id="$1"
|
|
local app_secret="$2"
|
|
set_kv "QORTAL_EXTERNAL_AUTH_APP_ID" "${app_id}"
|
|
set_kv "QORTAL_EXTERNAL_AUTH_APP_SECRET" "${app_secret}"
|
|
env_changed="1"
|
|
}
|
|
|
|
mark_app_config_external_auth_credentials() {
|
|
local app_id="$1"
|
|
local app_secret="$2"
|
|
occ_app_set "external_auth_app_id" "${app_id}"
|
|
occ_app_set "external_auth_app_secret" "${app_secret}"
|
|
app_config_changed="1"
|
|
}
|
|
|
|
mark_env_node_api_key() {
|
|
local node_api_key="$1"
|
|
set_kv "QORTAL_AUTH_NODE_API_KEY" "${node_api_key}"
|
|
set_kv "QORTAL_AUTH_NODE_API_KEY_MODE" "paths"
|
|
set_kv "QORTAL_AUTH_NODE_API_KEY_PATHS" "/"
|
|
env_changed="1"
|
|
}
|
|
|
|
mark_app_config_node_api_key() {
|
|
local node_api_key="$1"
|
|
occ_app_set "external_auth_node_api_key" "${node_api_key}"
|
|
occ_app_set "qortal_node_api_key" "${node_api_key}"
|
|
app_config_changed="1"
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--mode)
|
|
mode="${2:-}"
|
|
shift 2
|
|
;;
|
|
--mode=*)
|
|
mode="${1#*=}"
|
|
shift
|
|
;;
|
|
--env-file)
|
|
env_file="${2:-}"
|
|
shift 2
|
|
;;
|
|
--env-file=*)
|
|
env_file="${1#*=}"
|
|
shift
|
|
;;
|
|
--restart-services)
|
|
restart_services="1"
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1"
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ "${mode}" != "ssl" && "${mode}" != "nossl" ]]; then
|
|
echo "Invalid mode: ${mode}"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "${env_file}" != /* ]]; then
|
|
env_file="${repo_root}/${env_file}"
|
|
fi
|
|
if [[ ! -f "${env_file}" ]]; then
|
|
echo "Missing env file: ${env_file}"
|
|
exit 1
|
|
fi
|
|
|
|
compose_file="${repo_root}/docker-compose.devprod.nossl.yml"
|
|
if [[ "${mode}" == "ssl" ]]; then
|
|
compose_file="${repo_root}/docker-compose.devprod.yml"
|
|
fi
|
|
|
|
external_auth_host_port="$(read_kv "EXTERNAL_AUTH_PORT" || true)"
|
|
external_auth_host_port="${external_auth_host_port:-3191}"
|
|
|
|
ensure_docker_access
|
|
|
|
if ! docker compose -f "${compose_file}" --env-file "${env_file}" exec -T app php occ status >/dev/null 2>&1; then
|
|
echo "Nextcloud OCC is not ready; skipping bundled runtime env sync."
|
|
exit 0
|
|
fi
|
|
|
|
stored_app_id="$(occ_app_get "external_auth_app_id")"
|
|
stored_app_secret="$(occ_app_get "external_auth_app_secret")"
|
|
stored_node_api_key="$(occ_app_get "external_auth_node_api_key")"
|
|
if [[ -z "${stored_node_api_key}" ]]; then
|
|
stored_node_api_key="$(occ_app_get "qortal_node_api_key")"
|
|
fi
|
|
|
|
current_app_id="$(read_kv "QORTAL_EXTERNAL_AUTH_APP_ID" || true)"
|
|
current_app_secret="$(read_kv "QORTAL_EXTERNAL_AUTH_APP_SECRET" || true)"
|
|
current_node_api_key="$(read_kv "QORTAL_AUTH_NODE_API_KEY" || true)"
|
|
|
|
stored_creds_valid="0"
|
|
env_creds_valid="0"
|
|
if test_external_auth_session "${stored_app_id}" "${stored_app_secret}"; then
|
|
stored_creds_valid="1"
|
|
fi
|
|
if test_external_auth_session "${current_app_id}" "${current_app_secret}"; then
|
|
env_creds_valid="1"
|
|
fi
|
|
|
|
if [[ -n "${stored_app_id}" && -n "${stored_app_secret}" ]]; then
|
|
if [[ "${stored_app_id}" != "${current_app_id}" || "${stored_app_secret}" != "${current_app_secret}" ]]; then
|
|
if [[ "${stored_creds_valid}" == "1" && "${env_creds_valid}" != "1" ]]; then
|
|
mark_env_external_auth_credentials "${stored_app_id}" "${stored_app_secret}"
|
|
elif [[ "${env_creds_valid}" == "1" && "${stored_creds_valid}" != "1" ]]; then
|
|
mark_app_config_external_auth_credentials "${current_app_id}" "${current_app_secret}"
|
|
elif [[ "${stored_creds_valid}" == "1" && "${env_creds_valid}" == "1" ]]; then
|
|
mark_env_external_auth_credentials "${stored_app_id}" "${stored_app_secret}"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [[ -n "${stored_node_api_key}" && "${stored_node_api_key}" != "${current_node_api_key}" ]]; then
|
|
if [[ -z "${current_node_api_key}" ]]; then
|
|
mark_env_node_api_key "${stored_node_api_key}"
|
|
else
|
|
mark_app_config_node_api_key "${current_node_api_key}"
|
|
fi
|
|
elif [[ -n "${current_node_api_key}" && -z "${stored_node_api_key}" ]]; then
|
|
mark_app_config_node_api_key "${current_node_api_key}"
|
|
fi
|
|
|
|
if [[ "${stored_creds_valid}" != "1" && "${env_creds_valid}" == "1" ]]; then
|
|
echo "Bundled runtime note: Nextcloud app config external-auth credentials were invalid; synchronized from env."
|
|
elif [[ "${env_creds_valid}" != "1" && "${stored_creds_valid}" == "1" ]]; then
|
|
echo "Bundled runtime note: env external-auth credentials were invalid; synchronized from Nextcloud app config."
|
|
elif [[ "${stored_creds_valid}" != "1" && "${env_creds_valid}" != "1" ]]; then
|
|
echo "Bundled runtime credentials match their sources, but external_auth rejected both credential sets."
|
|
echo "Current App ID source values are not valid for /sessions."
|
|
if [[ -z "${current_node_api_key}" && -z "${stored_node_api_key}" ]]; then
|
|
echo "QORTAL_AUTH_NODE_API_KEY is also missing in both env and Nextcloud app config."
|
|
fi
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -z "${current_node_api_key}" && -z "${stored_node_api_key}" ]]; then
|
|
echo "Warning: QORTAL_AUTH_NODE_API_KEY is missing in both env and Nextcloud app config."
|
|
fi
|
|
|
|
if [[ "${env_changed}" == "1" || "${app_config_changed}" == "1" ]]; then
|
|
echo "Synchronized bundled runtime credentials from Nextcloud app config into ${env_file}."
|
|
if [[ "${restart_services}" == "1" ]]; then
|
|
echo "Recreating broker + external_auth to apply synchronized env values..."
|
|
docker compose -f "${compose_file}" --env-file "${env_file}" up -d --force-recreate --no-deps broker external_auth
|
|
fi
|
|
else
|
|
echo "Bundled runtime credentials are aligned and valid against external_auth."
|
|
fi
|