Files
SocialPub/tools/pasture/peers/ghost.sh
T

76 lines
5.1 KiB
Bash
Raw Normal View History

# Ghost 6.67 with its ActivityPub service 1.2.14 (Fedify): a publication whose posts go out as Articles from its
# @index actor, and which follows, likes, reposts and replies from its "Network" screen. Ghost and the service share the
# pasture's MySQL (databases ghost and activitypub); Caddy sends /.ghost/activitypub, WebFinger and NodeInfo to the
# service and everything else to Ghost, as Ghost's own Caddyfile does. Both trust the CA through NODE_EXTRA_CA_CERTS.
# The owner is made through Ghost's setup API, staff device verification is off (the pasture sends no mail), and the
# service learns of the site the first time Ghost's identity token asks it.
GHOST_IMAGE=${GHOST_IMAGE:-docker.io/library/ghost:6.67.0-alpine}
GHOST_AP_IMAGE=${GHOST_AP_IMAGE:-ghcr.io/tryghost/activitypub:1.2.14}
GHOST_AP_MIGRATIONS_IMAGE=${GHOST_AP_MIGRATIONS_IMAGE:-ghcr.io/tryghost/activitypub-migrations:1.2.14}
# (Ghost refuses a password that repeats the site's or the owner's name)
GHOST_PASSWORD=Lantern-Meadow-Quartz-71
. "$here/peers/shared.sh"
ghost_up() {
shared_mysql_up
mysql_db ghost
mysql_db activitypub
podman run --rm --network $net -e MYSQL_DB='mysql://pasture:pasture@tcp(mysql:3306)/activitypub' $GHOST_AP_MIGRATIONS_IMAGE >/dev/null 2>&1
podman volume exists pasture-ghost || podman volume create --label pasture=1 pasture-ghost >/dev/null
podman run -d --replace --name pasture-ghost-ap --label pasture=1 --network $net -v pasture-ghost:/opt/activitypub/content \
-e NODE_ENV=production -e MYSQL_HOST=mysql -e MYSQL_USER=pasture -e MYSQL_PASSWORD=pasture -e MYSQL_DATABASE=activitypub \
-e LOCAL_STORAGE_PATH=/opt/activitypub/content/images/activitypub -e LOCAL_STORAGE_HOSTING_URL=https://ghost.test/content/images/activitypub \
-e ALLOW_PRIVATE_ADDRESS=true -e NODE_EXTRA_CA_CERTS=/pasture/ca/root.crt -v "$ca:/pasture/ca:z,ro" $GHOST_AP_IMAGE >/dev/null
podman run -d --replace --name pasture-ghost --label pasture=1 --network $net -v pasture-ghost:/var/lib/ghost/content \
-e NODE_ENV=production -e url=https://ghost.test -e database__client=mysql -e database__connection__host=mysql \
-e database__connection__user=pasture -e database__connection__password=pasture -e database__connection__database=ghost \
-e security__staffDeviceVerification=false -e NODE_EXTRA_CA_CERTS=/pasture/ca/root.crt -v "$ca:/pasture/ca:z,ro" \
$GHOST_IMAGE >/dev/null
for _ in $(seq 1 120); do
site ghost.test -s -o /dev/null -w '%{http_code}' https://ghost.test:6443/ghost/api/admin/site/ 2>/dev/null | grep -q 200 && break
sleep 2
done
ghost_settle
echo "ghost: https://ghost.test:6443"
}
# ghost_api <method> <path> [json]: Ghost's admin API as the owner, with the session cookie ghost_settle keeps
ghost_api() {
local method=$1 path=$2 body=${3:-}
site ghost.test -s -X "$method" "https://ghost.test:6443/ghost/api/admin/$path" -b "$here/.state/ghost/cookies" -c "$here/.state/ghost/cookies" \
-H 'Origin: https://ghost.test' -H 'Content-Type: application/json' ${body:+-d "$body"}
}
# the owner (ghostuser@ghost.test), its session, and the ActivityPub service told of the site
ghost_settle() {
local st="$here/.state/ghost"
mkdir -p "$st"
site ghost.test -s -o /dev/null -X POST https://ghost.test:6443/ghost/api/admin/authentication/setup/ -H 'Origin: https://ghost.test' \
-H 'Content-Type: application/json' \
-d "{\"setup\":[{\"name\":\"Ghost User\",\"email\":\"ghostuser@ghost.test\",\"password\":\"$GHOST_PASSWORD\",\"blogTitle\":\"Ghost pasture\"}]}"
rm -f "$st/cookies"
site ghost.test -s -o /dev/null -X POST https://ghost.test:6443/ghost/api/admin/session/ -c "$st/cookies" -H 'Origin: https://ghost.test' \
-H 'Content-Type: application/json' -d "{\"username\":\"ghostuser@ghost.test\",\"password\":\"$GHOST_PASSWORD\"}"
ghost_ap GET v1/site >/dev/null
# Ghost sets up the webhooks that publish its posts when it starts, and only once it has an owner and the service
# knows the site: the first time, it starts again
if [ "$(podman exec pasture-mysql mysql -uroot -ppasture -N ghost -e 'select count(*) from webhooks' 2>/dev/null)" != "4" ]; then
podman restart pasture-ghost >/dev/null
for _ in $(seq 1 60); do
site ghost.test -s -o /dev/null -w '%{http_code}' https://ghost.test:6443/ghost/api/admin/site/ 2>/dev/null | grep -q 200 && break
sleep 2
done
rm -f "$st/cookies"
site ghost.test -s -o /dev/null -X POST https://ghost.test:6443/ghost/api/admin/session/ -c "$st/cookies" -H 'Origin: https://ghost.test' \
-H 'Content-Type: application/json' -d "{\"username\":\"ghostuser@ghost.test\",\"password\":\"$GHOST_PASSWORD\"}"
fi
}
# ghost_ap <method> <path> [json]: the ActivityPub service's own API (/.ghost/activitypub/<path>), with Ghost's identity token
ghost_ap() {
local method=$1 path=$2 body=${3:-} token
token=$(ghost_api GET identities/ | python3 -c 'import json, sys; print(json.load(sys.stdin)["identities"][0]["token"])')
# (the service names the site after the request's host, so the call says ghost.test without the workstation's port)
site ghost.test -s -X "$method" "https://ghost.test:6443/.ghost/activitypub/$path" -H 'Host: ghost.test' -H "Authorization: Bearer $token" \
-H 'Content-Type: application/json' ${body:+-d "$body"}
}