Funkwhale 2.0.11: the API, a Celery worker with its beat and the front's nginx sharing one volume, on the shared Postgres and Redis, trusting the pasture's CA through REQUESTS_CA_BUNDLE; fwuser and its OAuth token made by its own tools. scenarios/funkwhale.sh passes its 16 checks: alice follows a channel, a track uploaded to it arrives as Audio with its file and duration, its deletion reaches PrivaPub, the unfollow, statistics. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
79 lines
4.0 KiB
Bash
79 lines
4.0 KiB
Bash
# Funkwhale 2.0.11: music and podcasts. A channel is an actor of its own that publishes Audio, its files in `url[]`;
|
|
# libraries follow libraries. The API (gunicorn), a Celery worker with its beat, and the front's nginx (which proxies the
|
|
# API and serves media) share the pasture-funkwhale-data volume, on the shared Postgres (database funkwhale) and Redis
|
|
# (dbs 8 and 9). Python's requests trusts the pasture's bundle through REQUESTS_CA_BUNDLE. Its admin is fwuser; a token
|
|
# is an OAuth access token made in Django's shell.
|
|
FUNKWHALE_IMAGE=${FUNKWHALE_IMAGE:-docker.io/funkwhale/api:2.0.11}
|
|
FUNKWHALE_FRONT_IMAGE=${FUNKWHALE_FRONT_IMAGE:-docker.io/funkwhale/front:2.0.11}
|
|
FUNKWHALE_PASSWORD=Funkwhale-Pasture-1
|
|
. "$here/peers/shared.sh"
|
|
|
|
funkwhale_env() {
|
|
local key="$here/.state/funkwhale/secret"
|
|
[ -s "$key" ] || { mkdir -p "$here/.state/funkwhale"; head -c 48 /dev/urandom | base64 -w0 > "$key"; }
|
|
cat <<ENV
|
|
FUNKWHALE_HOSTNAME=funkwhale.test
|
|
FUNKWHALE_PROTOCOL=https
|
|
FUNKWHALE_URL=https://funkwhale.test
|
|
FUNKWHALE_API_PORT=5000
|
|
FUNKWHALE_API_HOST=pasture-funkwhale-api
|
|
FUNKWHALE_WEB_WORKERS=2
|
|
DJANGO_SECRET_KEY=$(cat "$key")
|
|
DATABASE_URL=postgresql://pasture:pasture@postgres:5432/funkwhale
|
|
CACHE_URL=redis://redis:6379/8
|
|
CELERY_BROKER_URL=redis://redis:6379/9
|
|
MEDIA_ROOT=/srv/funkwhale/data/media
|
|
STATIC_ROOT=/srv/funkwhale/data/static
|
|
MUSIC_DIRECTORY_PATH=/srv/funkwhale/data/music
|
|
NGINX_MAX_BODY_SIZE=100M
|
|
REVERSE_PROXY_TYPE=nginx
|
|
REQUESTS_CA_BUNDLE=/ca/bundle.pem
|
|
SSL_CERT_FILE=/ca/bundle.pem
|
|
DISABLE_PASSWORD_VALIDATORS=true
|
|
EMAIL_CONFIG=consolemail://
|
|
LOGLEVEL=info
|
|
ENV
|
|
}
|
|
|
|
funkwhale_manage() { podman exec pasture-funkwhale-api funkwhale-manage "$@"; }
|
|
|
|
funkwhale_up() {
|
|
shared_postgres_up
|
|
shared_redis_up
|
|
pg_db funkwhale citext unaccent pg_trgm
|
|
mkdir -p "$here/.state/funkwhale"
|
|
funkwhale_env > "$here/.state/funkwhale/env"
|
|
podman volume exists pasture-funkwhale-data || podman volume create --label pasture=1 pasture-funkwhale-data >/dev/null
|
|
local common=(--network $net --label pasture=1 --env-file "$here/.state/funkwhale/env" -v pasture-funkwhale-data:/srv/funkwhale/data
|
|
-v "$ca/bundle.pem:/ca/bundle.pem:z,ro")
|
|
podman run --rm "${common[@]}" "$FUNKWHALE_IMAGE" funkwhale-manage migrate >/dev/null 2>&1
|
|
podman run --rm "${common[@]}" "$FUNKWHALE_IMAGE" funkwhale-manage collectstatic --noinput >/dev/null 2>&1
|
|
podman run -d --replace --name pasture-funkwhale-api "${common[@]}" "$FUNKWHALE_IMAGE" gunicorn >/dev/null
|
|
podman run -d --replace --name pasture-funkwhale-celery "${common[@]}" "$FUNKWHALE_IMAGE" \
|
|
celery -A funkwhale_api.taskapp worker -B -l info --concurrency=2 -s /tmp/celerybeat-schedule >/dev/null
|
|
podman run -d --replace --name pasture-funkwhale --network $net --label pasture=1 --env-file "$here/.state/funkwhale/env" \
|
|
-v pasture-funkwhale-data:/srv/funkwhale/data "$FUNKWHALE_FRONT_IMAGE" >/dev/null
|
|
for _ in $(seq 1 90); do
|
|
site funkwhale.test -s -o /dev/null -w '%{http_code}' https://funkwhale.test:6443/.well-known/nodeinfo 2>/dev/null | grep -q 200 && break
|
|
sleep 2
|
|
done
|
|
funkwhale_settle
|
|
echo "funkwhale: https://funkwhale.test:6443"
|
|
}
|
|
|
|
# fwuser, and an OAuth access token of its own
|
|
funkwhale_settle() {
|
|
funkwhale_manage fw users create --username fwuser --email fwuser@funkwhale.test --password "$FUNKWHALE_PASSWORD" --superuser >/dev/null 2>&1 || true
|
|
podman exec -i pasture-funkwhale-api funkwhale-manage shell >/dev/null 2>&1 <<'PY'
|
|
from datetime import timedelta
|
|
from django.utils import timezone
|
|
from funkwhale_api.users.models import User
|
|
from funkwhale_api.users.models import Application, AccessToken
|
|
user = User.objects.get(username="fwuser")
|
|
app, _ = Application.objects.get_or_create(name="pasture", defaults={"client_type": "confidential", "authorization_grant_type": "authorization-code", "redirect_uris": "urn:ietf:wg:oauth:2.0:oob", "scope": "read write", "user": user})
|
|
AccessToken.objects.filter(user=user, application=app).delete()
|
|
AccessToken.objects.create(user=user, application=app, token="pasture-fwuser-token", scope="read write", expires=timezone.now() + timedelta(days=365))
|
|
PY
|
|
echo "pasture-fwuser-token" > "$here/.state/funkwhale.token"
|
|
}
|