Official-image wrapper of inventree/inventree 1.5.2 (digest-pinned). allauth OIDC provider JSON built from the platform env; invoke update on boot; RQ worker behind the web-port gate; admin seeded with a generated password. grind-stack: lowercase image names. 19th package. Verified: health 200, SSO route 302s to issuer, migrations clean. Detail: https://projects.knownelement.com/issues/658#note-5086
98 lines
4.2 KiB
Bash
Executable File
98 lines
4.2 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
# InvenTree runtime setup for Cloudron:
|
|
# 1. map the Cloudron postgresql + redis addons onto INVENTREE_* env
|
|
# and pin the data dir into localstorage (/app/data)
|
|
# 2. wait for both addons
|
|
# 3. drive `invoke update` (migrations + static collection) before
|
|
# serving; the image ENTRYPOINT is bypassed so we control ordering
|
|
# 4. background the RQ worker (gated on the web port = migrations
|
|
# done), then exec gunicorn on :8000
|
|
#
|
|
# Authentication: OIDC via the Cloudron platform provider, mapped into
|
|
# INVENTREE_SOCIAL_PROVIDERS (django-allauth openid_connect). The first
|
|
# admin is seeded via INVENTREE_ADMIN_* with a generated password,
|
|
# persisted at /app/data/.admin_password (see README).
|
|
|
|
DATA_DIR="/app/data"
|
|
ADMIN_PASS_FILE="${DATA_DIR}/.admin_password"
|
|
|
|
mkdir -p "${DATA_DIR}"
|
|
|
|
# --- 1. Cloudron -> INVENTREE_* environment ---------------------------------------
|
|
export INVENTREE_DATA_DIR="${DATA_DIR}"
|
|
export INVENTREE_CONFIG_FILE="${DATA_DIR}/config.yaml"
|
|
|
|
export INVENTREE_DB_ENGINE=postgresql
|
|
export INVENTREE_DB_HOST="${CLOUDRON_POSTGRESQL_HOST:-127.0.0.1}"
|
|
export INVENTREE_DB_PORT="${CLOUDRON_POSTGRESQL_PORT:-5432}"
|
|
export INVENTREE_DB_NAME="${CLOUDRON_POSTGRESQL_DATABASE:-inventree}"
|
|
export INVENTREE_DB_USER="${CLOUDRON_POSTGRESQL_USERNAME:-inventree}"
|
|
export INVENTREE_DB_PASSWORD="${CLOUDRON_POSTGRESQL_PASSWORD:-}"
|
|
|
|
export INVENTREE_CACHE_ENABLED=true
|
|
export INVENTREE_CACHE_HOST="${CLOUDRON_REDIS_HOST:-127.0.0.1}"
|
|
export INVENTREE_CACHE_PORT="${CLOUDRON_REDIS_PORT:-6379}"
|
|
export INVENTREE_CACHE_PASSWORD="${CLOUDRON_REDIS_PASSWORD:-}"
|
|
export INVENTREE_CACHE_DB=1
|
|
|
|
export INVENTREE_SITE_URL="${CLOUDRON_APP_ORIGIN:-http://localhost}"
|
|
export INVENTREE_SECRET_KEY="$(cat "${DATA_DIR}/.secret_key" 2>/dev/null || true)"
|
|
if [[ -z "${INVENTREE_SECRET_KEY}" ]]; then
|
|
( umask 077; head -c 48 /dev/urandom | od -An -tx1 | tr -d ' \n' > "${DATA_DIR}/.secret_key" )
|
|
export INVENTREE_SECRET_KEY="$(cat "${DATA_DIR}/.secret_key")"
|
|
echo "Generated new InvenTree secret key"
|
|
fi
|
|
|
|
# first admin: generated password, stored under /app/data
|
|
if [[ ! -s "${ADMIN_PASS_FILE}" ]]; then
|
|
( umask 077; head -c 12 /dev/urandom | od -An -tx1 | tr -d ' \n' > "${ADMIN_PASS_FILE}" )
|
|
echo "Generated admin password (stored at ${ADMIN_PASS_FILE} - see README)"
|
|
fi
|
|
export INVENTREE_ADMIN_USER="${INVENTREE_ADMIN_USER:-admin}"
|
|
export INVENTREE_ADMIN_PASSWORD="${INVENTREE_ADMIN_PASSWORD:-$(cat "${ADMIN_PASS_FILE}")}"
|
|
export INVENTREE_ADMIN_EMAIL="${INVENTREE_ADMIN_EMAIL:-admin@${CLOUDRON_APP_DOMAIN:-localhost}}"
|
|
|
|
# platform OIDC -> django-allauth openid_connect (server_url drives
|
|
# .well-known discovery; PKCE on)
|
|
if [[ -n "${CLOUDRON_OIDC_CLIENT_ID:-}" ]]; then
|
|
export INVENTREE_SOCIAL_PROVIDERS="$(jq -nc \
|
|
--arg cid "${CLOUDRON_OIDC_CLIENT_ID}" \
|
|
--arg csec "${CLOUDRON_OIDC_CLIENT_SECRET:-}" \
|
|
--arg issuer "${CLOUDRON_OIDC_ISSUER:-}" \
|
|
'{openid_connect: {OAUTH_PKCE_ENABLED: true, APPS: [{provider_id: "cloudron", name: "Cloudron SSO", server_url: $issuer, client_id: $cid, secret: $csec}]}}')"
|
|
fi
|
|
|
|
# --- 2. wait for the addons ---------------------------------------------------------
|
|
wait_tcp() {
|
|
local host="$1" port="$2" name="$3"
|
|
echo "Waiting for ${name} at ${host}:${port} ..."
|
|
until (exec 3<>"/dev/tcp/${host}/${port}") 2>/dev/null; do
|
|
echo "${name} is unavailable - sleeping"
|
|
sleep 2
|
|
done
|
|
echo "${name} is up"
|
|
}
|
|
|
|
wait_tcp "${INVENTREE_DB_HOST}" "${INVENTREE_DB_PORT}" "PostgreSQL"
|
|
wait_tcp "${INVENTREE_CACHE_HOST}" "${INVENTREE_CACHE_PORT}" "Redis"
|
|
|
|
# --- 3. schema + static + plugin setup ----------------------------------------------
|
|
# image layout: INVENTREE_HOME=/home/inventree (tasks.py + gunicorn.conf.py
|
|
# + init.sh live here), INVENTREE_BACKEND_DIR=/home/inventree/src/backend
|
|
cd "${INVENTREE_HOME:-/home/inventree}"
|
|
echo "Running invoke update (migrations + static files) ..."
|
|
invoke update
|
|
|
|
# --- 4. worker + web server ----------------------------------------------------------
|
|
(
|
|
until (exec 3<>/dev/tcp/127.0.0.1/8000) 2>/dev/null; do sleep 2; done
|
|
echo "web port is up - starting InvenTree worker"
|
|
exec invoke worker
|
|
) &
|
|
|
|
echo "Starting InvenTree web server on :8000 ..."
|
|
exec gunicorn -c ./gunicorn.conf.py InvenTree.wsgi -b 0.0.0.0:8000 \
|
|
--chdir "${INVENTREE_BACKEND_DIR:-/home/inventree/src/backend}/InvenTree"
|