mirror of
https://github.com/aykhans/bsky-pdsadmin-dockerized.git
synced 2025-05-30 00:40:02 +00:00
init
This commit is contained in:
commit
c9a5cf6c35
47
.github/workflows/publish-docker-image.yml
vendored
Normal file
47
.github/workflows/publish-docker-image.yml
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
name: publish-docker-image
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
|
||||
jobs:
|
||||
build-and-push-stable-image:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: aykhans
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
|
||||
- name: Extract metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: aykhans/bsky-pdsadmin
|
||||
tags: |
|
||||
type=ref,event=tag
|
||||
type=raw,value=latest
|
||||
type=raw,value=stable
|
||||
|
||||
- name: Build and Push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
12
Dockerfile
Normal file
12
Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM alpine:3.21.3
|
||||
|
||||
RUN apk add --no-cache bash curl jq util-linux openssl
|
||||
|
||||
COPY ./pdsadmin.sh /usr/local/bin/pdsadmin
|
||||
COPY ./commands /pdsadmin-commands
|
||||
|
||||
RUN chmod +x /usr/local/bin/pdsadmin
|
||||
RUN chmod +x /pdsadmin-commands/*
|
||||
|
||||
ENTRYPOINT [ "/usr/local/bin/pdsadmin" ]
|
||||
CMD [ "help" ]
|
60
README.md
Normal file
60
README.md
Normal file
@ -0,0 +1,60 @@
|
||||
# Bluesky PDS Admin (Dockerized)
|
||||
|
||||
This repository provides a Dockerized version of Bluesky's Personal Data Server (PDS) admin tool.
|
||||
You can use all available commands from the original PDS admin tool, **except** for the `update` command.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```sh
|
||||
docker run -it --rm aykhans/bsky-pdsadmin help
|
||||
```
|
||||
|
||||
### Account Management
|
||||
|
||||
**List accounts:**
|
||||
|
||||
```sh
|
||||
docker run -it --rm --env-file /pds/pds.env aykhans/bsky-pdsadmin account list
|
||||
```
|
||||
|
||||
**Create an account:**
|
||||
|
||||
```sh
|
||||
docker run -it --rm --env-file /pds/pds.env aykhans/bsky-pdsadmin account create
|
||||
```
|
||||
|
||||
**Delete an account:**
|
||||
|
||||
```sh
|
||||
docker run -it --rm --env-file /pds/pds.env aykhans/bsky-pdsadmin account delete <DID>
|
||||
```
|
||||
|
||||
**Takedown an account:**
|
||||
|
||||
```sh
|
||||
docker run -it --rm --env-file /pds/pds.env aykhans/bsky-pdsadmin account takedown <DID>
|
||||
```
|
||||
|
||||
**Untakedown an account:**
|
||||
|
||||
```sh
|
||||
docker run -it --rm --env-file /pds/pds.env aykhans/bsky-pdsadmin account untakedown <DID>
|
||||
```
|
||||
|
||||
**Reset an account password:**
|
||||
|
||||
```sh
|
||||
docker run -it --rm --env-file /pds/pds.env aykhans/bsky-pdsadmin account reset-password <DID>
|
||||
```
|
||||
|
||||
### Request a Crawl
|
||||
|
||||
```sh
|
||||
docker run --rm --env-file /pds/pds.env aykhans/bsky-pdsadmin request-crawl
|
||||
```
|
||||
|
||||
### Create an Invite Code
|
||||
|
||||
```sh
|
||||
docker run --rm --env-file /pds/pds.env aykhans/bsky-pdsadmin create-invite-code
|
||||
```
|
231
commands/account.sh
Normal file
231
commands/account.sh
Normal file
@ -0,0 +1,231 @@
|
||||
#!/bin/bash
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
# curl a URL and fail if the request fails.
|
||||
function curl_cmd_get {
|
||||
curl --fail --silent --show-error "$@"
|
||||
}
|
||||
|
||||
# curl a URL and fail if the request fails.
|
||||
function curl_cmd_post {
|
||||
curl --fail --silent --show-error --request POST --header "Content-Type: application/json" "$@"
|
||||
}
|
||||
|
||||
# curl a URL but do not fail if the request fails.
|
||||
function curl_cmd_post_nofail {
|
||||
curl --silent --show-error --request POST --header "Content-Type: application/json" "$@"
|
||||
}
|
||||
|
||||
# The subcommand to run.
|
||||
SUBCOMMAND="${1:-}"
|
||||
|
||||
#
|
||||
# account list
|
||||
#
|
||||
if [[ "${SUBCOMMAND}" == "list" ]]; then
|
||||
DIDS="$(curl_cmd_get \
|
||||
"https://${PDS_HOSTNAME}/xrpc/com.atproto.sync.listRepos?limit=100" | jq --raw-output '.repos[].did'
|
||||
)"
|
||||
OUTPUT='[{"handle":"Handle","email":"Email","did":"DID"}'
|
||||
for did in ${DIDS}; do
|
||||
ITEM="$(curl_cmd_get \
|
||||
--user "admin:${PDS_ADMIN_PASSWORD}" \
|
||||
"https://${PDS_HOSTNAME}/xrpc/com.atproto.admin.getAccountInfo?did=${did}"
|
||||
)"
|
||||
OUTPUT="${OUTPUT},${ITEM}"
|
||||
done
|
||||
OUTPUT="${OUTPUT}]"
|
||||
echo "${OUTPUT}" | jq --raw-output '.[] | [.handle, .email, .did] | @tsv' | column --table
|
||||
|
||||
#
|
||||
# account create
|
||||
#
|
||||
elif [[ "${SUBCOMMAND}" == "create" ]]; then
|
||||
EMAIL="${2:-}"
|
||||
HANDLE="${3:-}"
|
||||
|
||||
if [[ "${EMAIL}" == "" ]]; then
|
||||
read -p "Enter an email address (e.g. alice@${PDS_HOSTNAME}): " EMAIL
|
||||
fi
|
||||
if [[ "${HANDLE}" == "" ]]; then
|
||||
read -p "Enter a handle (e.g. alice.${PDS_HOSTNAME}): " HANDLE
|
||||
fi
|
||||
|
||||
if [[ "${EMAIL}" == "" || "${HANDLE}" == "" ]]; then
|
||||
echo "ERROR: missing EMAIL and/or HANDLE parameters." >/dev/stderr
|
||||
echo "Usage: $0 ${SUBCOMMAND} <EMAIL> <HANDLE>" >/dev/stderr
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PASSWORD="$(openssl rand -base64 30 | tr -d "=+/" | cut -c1-24)"
|
||||
INVITE_CODE="$(curl_cmd_post \
|
||||
--user "admin:${PDS_ADMIN_PASSWORD}" \
|
||||
--data '{"useCount": 1}' \
|
||||
"https://${PDS_HOSTNAME}/xrpc/com.atproto.server.createInviteCode" | jq --raw-output '.code'
|
||||
)"
|
||||
RESULT="$(curl_cmd_post_nofail \
|
||||
--data "{\"email\":\"${EMAIL}\", \"handle\":\"${HANDLE}\", \"password\":\"${PASSWORD}\", \"inviteCode\":\"${INVITE_CODE}\"}" \
|
||||
"https://${PDS_HOSTNAME}/xrpc/com.atproto.server.createAccount"
|
||||
)"
|
||||
|
||||
DID="$(echo $RESULT | jq --raw-output '.did')"
|
||||
if [[ "${DID}" != did:* ]]; then
|
||||
ERR="$(echo ${RESULT} | jq --raw-output '.message')"
|
||||
echo "ERROR: ${ERR}" >/dev/stderr
|
||||
echo "Usage: $0 ${SUBCOMMAND} <EMAIL> <HANDLE>" >/dev/stderr
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Account created successfully!"
|
||||
echo "-----------------------------"
|
||||
echo "Handle : ${HANDLE}"
|
||||
echo "DID : ${DID}"
|
||||
echo "Password : ${PASSWORD}"
|
||||
echo "-----------------------------"
|
||||
echo "Save this password, it will not be displayed again."
|
||||
echo
|
||||
|
||||
#
|
||||
# account delete
|
||||
#
|
||||
elif [[ "${SUBCOMMAND}" == "delete" ]]; then
|
||||
DID="${2:-}"
|
||||
|
||||
if [[ "${DID}" == "" ]]; then
|
||||
echo "ERROR: missing DID parameter." >/dev/stderr
|
||||
echo "Usage: $0 ${SUBCOMMAND} <DID>" >/dev/stderr
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${DID}" != did:* ]]; then
|
||||
echo "ERROR: DID parameter must start with \"did:\"." >/dev/stderr
|
||||
echo "Usage: $0 ${SUBCOMMAND} <DID>" >/dev/stderr
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "This action is permanent."
|
||||
read -r -p "Are you sure you'd like to delete ${DID}? [y/N] " response
|
||||
if [[ ! "${response}" =~ ^([yY][eE][sS]|[yY])$ ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
curl_cmd_post \
|
||||
--user "admin:${PDS_ADMIN_PASSWORD}" \
|
||||
--data "{\"did\": \"${DID}\"}" \
|
||||
"https://${PDS_HOSTNAME}/xrpc/com.atproto.admin.deleteAccount" >/dev/null
|
||||
|
||||
echo "${DID} deleted"
|
||||
|
||||
#
|
||||
# account takedown
|
||||
#
|
||||
elif [[ "${SUBCOMMAND}" == "takedown" ]]; then
|
||||
DID="${2:-}"
|
||||
TAKEDOWN_REF="$(date +%s)"
|
||||
|
||||
if [[ "${DID}" == "" ]]; then
|
||||
echo "ERROR: missing DID parameter." >/dev/stderr
|
||||
echo "Usage: $0 ${SUBCOMMAND} <DID>" >/dev/stderr
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${DID}" != did:* ]]; then
|
||||
echo "ERROR: DID parameter must start with \"did:\"." >/dev/stderr
|
||||
echo "Usage: $0 ${SUBCOMMAND} <DID>" >/dev/stderr
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PAYLOAD="$(cat <<EOF
|
||||
{
|
||||
"subject": {
|
||||
"\$type": "com.atproto.admin.defs#repoRef",
|
||||
"did": "${DID}"
|
||||
},
|
||||
"takedown": {
|
||||
"applied": true,
|
||||
"ref": "${TAKEDOWN_REF}"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
)"
|
||||
|
||||
curl_cmd_post \
|
||||
--user "admin:${PDS_ADMIN_PASSWORD}" \
|
||||
--data "${PAYLOAD}" \
|
||||
"https://${PDS_HOSTNAME}/xrpc/com.atproto.admin.updateSubjectStatus" >/dev/null
|
||||
|
||||
echo "${DID} taken down"
|
||||
|
||||
#
|
||||
# account untakedown
|
||||
#
|
||||
elif [[ "${SUBCOMMAND}" == "untakedown" ]]; then
|
||||
DID="${2:-}"
|
||||
|
||||
if [[ "${DID}" == "" ]]; then
|
||||
echo "ERROR: missing DID parameter." >/dev/stderr
|
||||
echo "Usage: $0 ${SUBCOMMAND} <DID>" >/dev/stderr
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${DID}" != did:* ]]; then
|
||||
echo "ERROR: DID parameter must start with \"did:\"." >/dev/stderr
|
||||
echo "Usage: $0 ${SUBCOMMAND} <DID>" >/dev/stderr
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PAYLOAD=$(cat <<EOF
|
||||
{
|
||||
"subject": {
|
||||
"\$type": "com.atproto.admin.defs#repoRef",
|
||||
"did": "${DID}"
|
||||
},
|
||||
"takedown": {
|
||||
"applied": false
|
||||
}
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
curl_cmd_post \
|
||||
--user "admin:${PDS_ADMIN_PASSWORD}" \
|
||||
--data "${PAYLOAD}" \
|
||||
"https://${PDS_HOSTNAME}/xrpc/com.atproto.admin.updateSubjectStatus" >/dev/null
|
||||
|
||||
echo "${DID} untaken down"
|
||||
#
|
||||
# account reset-password
|
||||
#
|
||||
elif [[ "${SUBCOMMAND}" == "reset-password" ]]; then
|
||||
DID="${2:-}"
|
||||
PASSWORD="$(openssl rand -base64 30 | tr -d "=+/" | cut -c1-24)"
|
||||
|
||||
if [[ "${DID}" == "" ]]; then
|
||||
echo "ERROR: missing DID parameter." >/dev/stderr
|
||||
echo "Usage: $0 ${SUBCOMMAND} <DID>" >/dev/stderr
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${DID}" != did:* ]]; then
|
||||
echo "ERROR: DID parameter must start with \"did:\"." >/dev/stderr
|
||||
echo "Usage: $0 ${SUBCOMMAND} <DID>" >/dev/stderr
|
||||
exit 1
|
||||
fi
|
||||
|
||||
curl_cmd_post \
|
||||
--user "admin:${PDS_ADMIN_PASSWORD}" \
|
||||
--data "{ \"did\": \"${DID}\", \"password\": \"${PASSWORD}\" }" \
|
||||
"https://${PDS_HOSTNAME}/xrpc/com.atproto.admin.updateAccountPassword" >/dev/null
|
||||
|
||||
echo
|
||||
echo "Password reset for ${DID}"
|
||||
echo "New password: ${PASSWORD}"
|
||||
echo
|
||||
|
||||
else
|
||||
echo "Unknown subcommand: ${SUBCOMMAND}" >/dev/stderr
|
||||
exit 1
|
||||
fi
|
14
commands/create-invite-code.sh
Normal file
14
commands/create-invite-code.sh
Normal file
@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
curl \
|
||||
--fail \
|
||||
--silent \
|
||||
--show-error \
|
||||
--request POST \
|
||||
--user "admin:${PDS_ADMIN_PASSWORD}" \
|
||||
--header "Content-Type: application/json" \
|
||||
--data '{"useCount": 1}' \
|
||||
"https://${PDS_HOSTNAME}/xrpc/com.atproto.server.createInviteCode" | jq --raw-output '.code'
|
41
commands/help.sh
Normal file
41
commands/help.sh
Normal file
@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
# This script is used to display help information for the pdsadmin command.
|
||||
cat <<HELP
|
||||
pdsadmin help
|
||||
--
|
||||
account
|
||||
list
|
||||
List accounts
|
||||
e.g. pdsadmin account list
|
||||
create <EMAIL> <HANDLE>
|
||||
Create a new account
|
||||
e.g. pdsadmin account create alice@example.com alice.example.com
|
||||
delete <DID>
|
||||
Delete an account specified by DID.
|
||||
e.g. pdsadmin account delete did:plc:xyz123abc456
|
||||
takedown <DID>
|
||||
Takedown an account specified by DID.
|
||||
e.g. pdsadmin account takedown did:plc:xyz123abc456
|
||||
untakedown <DID>
|
||||
Remove a takedown from an account specified by DID.
|
||||
e.g. pdsadmin account untakedown did:plc:xyz123abc456
|
||||
reset-password <DID>
|
||||
Reset a password for an account specified by DID.
|
||||
e.g. pdsadmin account reset-password did:plc:xyz123abc456
|
||||
|
||||
request-crawl [<RELAY HOST>]
|
||||
Request a crawl from a relay host.
|
||||
e.g. pdsadmin request-crawl bsky.network
|
||||
|
||||
create-invite-code
|
||||
Create a new invite code.
|
||||
e.g. pdsadmin create-invite-code
|
||||
|
||||
help
|
||||
Display this help information.
|
||||
|
||||
HELP
|
32
commands/request-crawl.sh
Normal file
32
commands/request-crawl.sh
Normal file
@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
RELAY_HOSTS="${1:-}"
|
||||
if [[ "${RELAY_HOSTS}" == "" ]]; then
|
||||
RELAY_HOSTS="${PDS_CRAWLERS}"
|
||||
fi
|
||||
|
||||
if [[ "${RELAY_HOSTS}" == "" ]]; then
|
||||
echo "ERROR: missing RELAY HOST parameter." >/dev/stderr
|
||||
echo "Usage: $0 <RELAY HOST>[,<RELAY HOST>,...]" >/dev/stderr
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for host in ${RELAY_HOSTS//,/ }; do
|
||||
echo "Requesting crawl from ${host}"
|
||||
if [[ $host != https:* && $host != http:* ]]; then
|
||||
host="https://${host}"
|
||||
fi
|
||||
curl \
|
||||
--fail \
|
||||
--silent \
|
||||
--show-error \
|
||||
--request POST \
|
||||
--header "Content-Type: application/json" \
|
||||
--data "{\"hostname\": \"${PDS_HOSTNAME}\"}" \
|
||||
"${host}/xrpc/com.atproto.sync.requestCrawl" >/dev/null
|
||||
done
|
||||
|
||||
echo "done"
|
18
pdsadmin.sh
Normal file
18
pdsadmin.sh
Normal file
@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
# Command to run.
|
||||
COMMAND="${1:-help}"
|
||||
shift || true
|
||||
|
||||
# Download the script, if it exists.
|
||||
SCRIPT_FILE="/pdsadmin-commands/${COMMAND}.sh"
|
||||
|
||||
if [ ! -f "${SCRIPT_FILE}" ]; then
|
||||
echo "ERROR: ${COMMAND} not found"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
"${SCRIPT_FILE}" "$@"
|
Loading…
x
Reference in New Issue
Block a user