70 lines
5.0 KiB
Bash
70 lines
5.0 KiB
Bash
# A persona's archive (owner decision 2026-10-07), with a real one: mastouser's own account archive, made by Mastodon's
|
|||
|
|
# backup service, is imported into mover_archive, a PrivaPub persona mastouser follows. Its posts arrive, with their
|
||
|
|
# media, on the persona's profile, and nobody is told: Mastodon, which would get anything delivered to the persona's
|
||
|
|
# followers, holds none of the copies. Importing it again changes nothing. Then the persona's own archive is exported and
|
||
|
|
# downloaded through its link, in Mastodon's layout. Needs the mastodon peer.
|
||
|
|
M=https://mastodon.test:6443
|
||
|
|
mcurl() { curl -sk --resolve mastodon.test:6443:127.0.0.1 "$@"; }
|
||
|
|
. "$here/peers/mastodon.sh"
|
||
|
|
m_rails() { podman exec pasture-mastodon bin/rails runner "$1" 2>/dev/null | tail -1; }
|
||
|
|
|
||
|
|
echo "persona-archive"
|
||
|
|
JWT=$(privapub_root)
|
||
|
|
RH="Authorization: Bearer $JWT"
|
||
|
|
AT=$(privapub_token mover_archive)
|
||
|
|
AH="Authorization: Bearer $AT"
|
||
|
|
[ -n "$AT" ] && ok "PrivaPub token for mover_archive" || { ko "PrivaPub token for mover_archive"; return 1; }
|
||
|
|
me=$(curl -s -H "$AH" "$P/api/v1/accounts/verify_credentials")
|
||
|
|
me_id=$(echo "$me" | j "print(d['id'])")
|
||
|
|
me_uri=$(echo "$me" | j "print(d['url'])" | sed 's|/@|/peasants/|')
|
||
|
|
A="$P/clientapi/persona/$me_id/archive"
|
||
|
|
# each run starts from a persona that has imported nothing
|
||
|
|
podman exec pasture-mongo mongosh --quiet PrivaPub --eval "db.Post.deleteMany({GroupUserId:'$me_id', ImportedFromURI:{\$exists:true, \$ne:null}}); db.MediaAttachment.deleteMany({OwnerAvatarId:'$me_id'})" >/dev/null
|
||
|
|
|
||
|
|
m_follows() { m_rails "puts Follow.exists?(account: Account.find_local(\"mastouser\"), target_account: Account.find_by(uri: \"$me_uri\")) ? \"True\" : \"False\""; }
|
||
|
|
if [ "$(m_follows)" != "True" ]; then
|
||
|
|
on_m=$(mcurl -H "Authorization: Bearer $(mastodon_token)" "$M/api/v2/search?q=@mover_archive@privapub.test&resolve=true&type=accounts" | j "print(d['accounts'][0]['id'])")
|
||
|
|
mcurl -o /dev/null -X POST -H "Authorization: Bearer $(mastodon_token)" "$M/api/v1/accounts/$on_m/follow"
|
||
|
|
fi
|
||
|
|
until_true 45 '[ "$(m_follows)" = "True" ]' && ok "mastouser follows mover_archive" || ko "mastouser never followed mover_archive"
|
||
|
|
|
||
|
|
dump=$(m_rails 'u = Account.find_local("mastouser").user; b = u.backups.create!; BackupService.new.call(b); puts b.reload.dump.path')
|
||
|
|
podman cp "pasture-mastodon:$dump" "$work/mastodon.zip" 2>/dev/null
|
||
|
|
[ -s "$work/mastodon.zip" ] && ok "Mastodon made mastouser's archive" || { ko "Mastodon made no archive"; return 1; }
|
||
|
|
|
||
|
|
import() {
|
||
|
|
local upload
|
||
|
|
upload=$(curl -s -X POST -H "$RH" "$P/clientapi/persona/archive/uploads" | j "print(d['id'])")
|
||
|
|
curl -s -o /dev/null -X PUT -H "$RH" -H "Content-Type: application/octet-stream" --data-binary "@$work/mastodon.zip" "$P/clientapi/persona/archive/uploads/$upload?offset=0"
|
||
|
|
curl -s -o /dev/null -w '%{http_code}' -X POST -H "$RH" -H 'Content-Type: application/json' "$A/import" -d "{\"uploadId\":\"$upload\",\"parts\":[\"posts\"]}"
|
||
|
|
}
|
||
|
|
state() { curl -s -H "$RH" "$A" | j "print(d['$1'])"; }
|
||
|
|
count() { curl -s -H "$RH" "$A" | j "print(d['importCounts'].get('$1', 0))"; }
|
||
|
|
|
||
|
|
[ "$(import)" = "202" ] && ok "the import is asked for" || { ko "the import was refused"; return 1; }
|
||
|
|
until_true 150 '[ "$(state importState)" = "done" ]' && ok "the import is done" || { ko "the import is $(state importState): $(state importError)"; return 1; }
|
||
|
|
posts=$(count posts)
|
||
|
|
[ "$posts" -gt 0 ] && ok "$posts of mastouser's posts imported ($(count 'boosts skipped') boosts and $(count 'direct or circle posts skipped') private posts left out)" \
|
||
|
|
|| ko "no post was imported"
|
||
|
|
[ "$(count media)" -gt 0 ] && ok "$(count media) pictures came with them" || ko "no picture came with them"
|
||
|
|
copies=$(curl -s -H "$AH" "$P/api/v1/accounts/$me_id/statuses?limit=40")
|
||
|
|
[ "$(echo "$copies" | j "print(len(d))")" -gt 0 ] && ok "they are on mover_archive's profile" || ko "the profile shows none of them"
|
||
|
|
|
||
|
|
# nobody told: what PrivaPub would have delivered would be at Mastodon by now
|
||
|
|
sleep 20
|
||
|
|
uris=$(echo "$copies" | j "print(' '.join(s['uri'] for s in d[:5]))")
|
||
|
|
held=$("$here/town.sh" stored mastodon $uris | j "print(sum(1 for v in d.values() if v['exists']))")
|
||
|
|
[ "$held" = "0" ] && ok "Mastodon holds none of the copies" || ko "Mastodon holds $held of the copies"
|
||
|
|
|
||
|
|
[ "$(import)" = "202" ] && until_true 150 '[ "$(state importState)" = "done" ]' \
|
||
|
|
&& [ "$(count posts)" = "0" ] && [ "$(count 'posts already here')" = "$posts" ] \
|
||
|
|
&& ok "importing it again changes nothing" || ko "the second import: $(count posts) posts, $(count 'posts already here') already here"
|
||
|
|
|
||
|
|
echo " exported"
|
||
|
|
[ "$(curl -s -o /dev/null -w '%{http_code}' -X POST -H "$RH" "$A")" = "202" ] && ok "the persona's archive is asked for" || ko "the archive was refused"
|
||
|
|
until_true 90 '[ "$(state exportState)" = "ready" ]' && ok "the archive is ready" || ko "the archive is $(state exportState): $(state exportError)"
|
||
|
|
link=$(curl -s -X POST -H "$RH" "$A/ticket" | j "print(d['url'])")
|
||
|
|
curl -s -o "$work/mover.zip" "$P$link"
|
||
|
|
names=$(unzip -Z1 "$work/mover.zip" 2>/dev/null)
|
||
|
|
grep -qx outbox.json <<< "$names" && grep -qx actor.json <<< "$names" \
|
||
|
|
&& ok "it downloads, in Mastodon's layout" || ko "the download is not an archive"
|