Add Mastodon v4.7.3 package directory

This commit is contained in:
okxlin
2026-10-17 10:23:22 +08:00
parent 39910b4f2d
commit 6bf6df6bdd
7 changed files with 1097 additions and 0 deletions
@@ -0,0 +1,31 @@
CONTAINER_NAME=
PANEL_APP_PORT_HTTP=80
PANEL_APP_PORT_HTTPS=443
CONFIG_PATH=./data/config
LOCAL_DOMAIN=example.com
WEB_DOMAIN=
REDIS_HOST=mastodon-redis
REDIS_PORT=6379
DB_HOST=mastodon-db
DB_PORT=5432
DB_USER=mastodon
DB_NAME=mastodon
DB_PASSWORD=replace-with-db-password
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=replace-with-generated-active-record-primary-key
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=replace-with-generated-active-record-deterministic-key
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=replace-with-generated-active-record-salt
SECRET_KEY_BASE=replace-with-generated-secret-key-base
OTP_SECRET=replace-with-generated-otp-secret
VAPID_PRIVATE_KEY=replace-with-generated-vapid-private-key
VAPID_PUBLIC_KEY=replace-with-generated-vapid-public-key
SMTP_SERVER=mail.example.com
SMTP_PORT=25
SMTP_LOGIN=
SMTP_PASSWORD=
SMTP_FROM_ADDRESS=notifications@example.com
MASTODON_PROMETHEUS_EXPORTER_ENABLED=false
SIDEKIQ_THREADS=5
DB_POOL=5
DB_DATA_PATH=./data/db
REDIS_DATA_PATH=./data/redis
TIME_ZONE=Asia/Shanghai
+505
View File
@@ -0,0 +1,505 @@
additionalProperties:
formFields:
- default: 80
edit: true
envKey: PANEL_APP_PORT_HTTP
labelEn: HTTP Port
labelZh: HTTP 端口
label:
en: HTTP Port
zh: HTTP 端口
zh-Hant: HTTP 連接埠
ja: HTTP ポート
ko: HTTP 포트
ru: HTTP-порт
ms: Port HTTP
pt-br: Porta HTTP
required: true
type: number
rule: paramPort
- default: 443
edit: true
envKey: PANEL_APP_PORT_HTTPS
labelEn: HTTPS Port
labelZh: HTTPS 端口
label:
en: HTTPS Port
zh: HTTPS 端口
zh-Hant: HTTPS 連接埠
ja: HTTPS ポート
ko: HTTPS 포트
ru: HTTPS-порт
ms: Port HTTPS
pt-br: Porta HTTPS
required: true
type: number
rule: paramPort
- default: ./data/config
edit: true
envKey: CONFIG_PATH
labelEn: Config folder path
labelZh: 配置文件路径
label:
en: Config folder path
zh: 配置文件路径
zh-Hant: 設定檔資料夾路徑
ja: 設定フォルダーのパス
ko: 설정 폴더 경로
ru: Путь к папке конфигурации
ms: Laluan folder konfigurasi
pt-br: Caminho da pasta de configuração
required: true
type: text
- default: ''
edit: true
envKey: LOCAL_DOMAIN
labelEn: Local domain
labelZh: 实例域名
label:
en: Local domain
zh: 实例域名
zh-Hant: 實例網域
ja: ローカルドメイン
ko: 로컬 도메인
ru: Локальный домен
ms: Domain setempat
pt-br: Dominio local
required: true
type: text
- default: ''
edit: true
envKey: WEB_DOMAIN
labelEn: Web domain
labelZh: Web 域名
label:
en: Web domain
zh: Web 域名
zh-Hant: Web 網域
ja: Web ドメイン
ko: Web 도메인
ru: Web-домен
ms: Domain web
pt-br: Dominio web
required: false
type: text
- default: "false"
edit: true
envKey: MASTODON_PROMETHEUS_EXPORTER_ENABLED
labelEn: Prometheus exporter
labelZh: Prometheus 导出器
label:
en: Prometheus exporter
zh: Prometheus 导出器
zh-Hant: Prometheus 匯出器
ja: Prometheus エクスポーター
ko: Prometheus exporter
ru: Экспортер Prometheus
ms: Pengeksport Prometheus
pt-br: Exportador Prometheus
required: false
type: select
values:
- label: "false"
value: "false"
- label: "true"
value: "true"
- default: mastodon-db
edit: true
envKey: DB_HOST
labelEn: Database host
labelZh: 数据库主机
label:
en: Database host
zh: 数据库主机
zh-Hant: 資料庫主機
ja: データベースホスト
ko: 데이터베이스 호스트
ru: Хост базы данных
ms: Hos pangkalan data
pt-br: Host do banco
required: true
type: text
- default: 5432
edit: true
envKey: DB_PORT
labelEn: Database port
labelZh: 数据库端口
label:
en: Database port
zh: 数据库端口
zh-Hant: 資料庫連接埠
ja: データベースポート
ko: 데이터베이스 포트
ru: Порт базы данных
ms: Port pangkalan data
pt-br: Porta do banco
required: true
type: number
- default: mastodon
edit: true
envKey: DB_USER
labelEn: Database username
labelZh: 数据库用户名
label:
en: Database username
zh: 数据库用户名
zh-Hant: 資料庫使用者名稱
ja: データベースユーザー名
ko: 데이터베이스 사용자 이름
ru: Имя пользователя базы
ms: Nama pengguna pangkalan data
pt-br: Usuario do banco
required: true
type: text
- default: mastodon
edit: true
envKey: DB_NAME
labelEn: Database name
labelZh: 数据库名称
label:
en: Database name
zh: 数据库名称
zh-Hant: 資料庫名稱
ja: データベース名
ko: 데이터베이스 이름
ru: Имя базы данных
ms: Nama pangkalan data
pt-br: Nome do banco
required: true
type: text
- default: mastodon-redis
edit: true
envKey: REDIS_HOST
labelEn: Redis host
labelZh: Redis 主机
label:
en: Redis host
zh: Redis 主机
zh-Hant: Redis 主機
ja: Redis ホスト
ko: Redis 호스트
ru: Хост Redis
ms: Hos Redis
pt-br: Host do Redis
required: true
type: text
- default: 6379
edit: true
envKey: REDIS_PORT
labelEn: Redis port
labelZh: Redis 端口
label:
en: Redis port
zh: Redis 端口
zh-Hant: Redis 連接埠
ja: Redis ポート
ko: Redis 포트
ru: Порт Redis
ms: Port Redis
pt-br: Porta do Redis
required: true
type: number
- default: ''
edit: true
envKey: DB_PASSWORD
labelEn: Database password
labelZh: 数据库密码
label:
en: Database password
zh: 数据库密码
zh-Hant: 資料庫密碼
ja: データベースパスワード
ko: 데이터베이스 비밀번호
ru: Пароль базы данных
ms: Kata laluan pangkalan data
pt-br: Senha do banco de dados
required: true
random: true
type: password
rule: paramComplexity
- default: ''
edit: true
envKey: ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY
labelEn: Secret value
labelZh: 密钥值
label:
en: Secret value
zh: 密钥值
zh-Hant: 金鑰值
ja: シークレット値
ko: 시크릿 값
ru: Секретное значение
ms: Nilai rahsia
pt-br: Valor secreto
required: true
random: true
type: password
rule: paramComplexity
- default: ''
edit: true
envKey: ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY
labelEn: Secret value
labelZh: 密钥值
label:
en: Secret value
zh: 密钥值
zh-Hant: 金鑰值
ja: シークレット値
ko: 시크릿 값
ru: Секретное значение
ms: Nilai rahsia
pt-br: Valor secreto
required: true
random: true
type: password
rule: paramComplexity
- default: ''
edit: true
envKey: ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT
labelEn: Secret value
labelZh: 密钥值
label:
en: Secret value
zh: 密钥值
zh-Hant: 金鑰值
ja: シークレット値
ko: 시크릿 값
ru: Секретное значение
ms: Nilai rahsia
pt-br: Valor secreto
required: true
random: true
type: password
rule: paramComplexity
- default: ''
edit: true
envKey: SECRET_KEY_BASE
labelEn: Secret value
labelZh: 密钥值
label:
en: Secret value
zh: 密钥值
zh-Hant: 金鑰值
ja: シークレット値
ko: 시크릿 값
ru: Секретное значение
ms: Nilai rahsia
pt-br: Valor secreto
required: true
random: true
type: password
rule: paramComplexity
- default: ''
edit: true
envKey: OTP_SECRET
labelEn: Secret value
labelZh: 密钥值
label:
en: Secret value
zh: 密钥值
zh-Hant: 金鑰值
ja: シークレット値
ko: 시크릿 값
ru: Секретное значение
ms: Nilai rahsia
pt-br: Valor secreto
required: true
random: true
type: password
rule: paramComplexity
- default: ''
edit: true
envKey: VAPID_PRIVATE_KEY
labelEn: Secret value
labelZh: 密钥值
label:
en: Secret value
zh: 密钥值
zh-Hant: 金鑰值
ja: シークレット値
ko: 시크릿 값
ru: Секретное значение
ms: Nilai rahsia
pt-br: Valor secreto
required: true
random: true
type: password
rule: paramComplexity
- default: ''
edit: true
envKey: VAPID_PUBLIC_KEY
labelEn: Secret value
labelZh: 密钥值
label:
en: Secret value
zh: 密钥值
zh-Hant: 金鑰值
ja: シークレット値
ko: 시크릿 값
ru: Секретное значение
ms: Nilai rahsia
pt-br: Valor secreto
required: true
random: true
type: password
rule: paramComplexity
- default: ''
edit: true
envKey: SMTP_SERVER
labelEn: SMTP server
labelZh: SMTP 服务器
label:
en: SMTP server
zh: SMTP 服务器
zh-Hant: SMTP 伺服器
ja: SMTP サーバー
ko: SMTP 서버
ru: SMTP-сервер
ms: Pelayan SMTP
pt-br: Servidor SMTP
required: true
type: text
- default: 25
edit: true
envKey: SMTP_PORT
labelEn: SMTP port
labelZh: SMTP 端口
label:
en: SMTP port
zh: SMTP 端口
zh-Hant: SMTP 連接埠
ja: SMTP ポート
ko: SMTP 포트
ru: Порт SMTP
ms: Port SMTP
pt-br: Porta SMTP
required: true
type: number
- default: ''
edit: true
envKey: SMTP_LOGIN
labelEn: SMTP login
labelZh: SMTP 登录名
label:
en: SMTP login
zh: SMTP 登录名
zh-Hant: SMTP 登入名
ja: SMTP ログイン
ko: SMTP 로그인
ru: Логин SMTP
ms: Log masuk SMTP
pt-br: Login SMTP
required: false
type: text
- default: ''
edit: true
envKey: SMTP_PASSWORD
labelEn: SMTP password
labelZh: SMTP 密码
label:
en: SMTP password
zh: SMTP 密码
zh-Hant: SMTP 密碼
ja: SMTP パスワード
ko: SMTP 비밀번호
ru: Пароль SMTP
ms: Kata laluan SMTP
pt-br: Senha SMTP
required: false
type: password
- default: ''
edit: true
envKey: SMTP_FROM_ADDRESS
labelEn: SMTP from address
labelZh: SMTP 发件地址
label:
en: SMTP from address
zh: SMTP 发件地址
zh-Hant: SMTP 寄件地址
ja: SMTP 送信元アドレス
ko: SMTP 발신 주소
ru: SMTP адрес отправителя
ms: Alamat pengirim SMTP
pt-br: Endereco remetente SMTP
required: true
type: text
- default: 5
edit: true
envKey: SIDEKIQ_THREADS
labelEn: Sidekiq threads
labelZh: Sidekiq 线程数
label:
en: Sidekiq threads
zh: Sidekiq 线程数
zh-Hant: Sidekiq 執行緒數
ja: Sidekiq スレッド数
ko: Sidekiq 스레드 수
ru: Потоки Sidekiq
ms: Benang Sidekiq
pt-br: Threads Sidekiq
required: true
type: number
- default: 5
edit: true
envKey: DB_POOL
labelEn: Database pool size
labelZh: 数据库连接池大小
label:
en: Database pool size
zh: 数据库连接池大小
zh-Hant: 資料庫連線池大小
ja: データベースプールサイズ
ko: 데이터베이스 풀 크기
ru: Размер пула базы данных
ms: Saiz kolam pangkalan data
pt-br: Tamanho do pool do banco
required: true
type: number
- default: ./data/db
edit: true
envKey: DB_DATA_PATH
labelEn: Database data path
labelZh: 数据库数据目录
label:
en: Database data path
zh: 数据库数据目录
zh-Hant: 資料庫資料目錄
ja: データベースデータディレクトリ
ko: 데이터베이스 데이터 디렉터リ
ru: Каталог данных базы
ms: Laluan data pangkalan data
pt-br: Caminho de dados do banco
required: true
type: text
- default: ./data/redis
edit: true
envKey: REDIS_DATA_PATH
labelEn: Redis data path
labelZh: Redis 数据目录
label:
en: Redis data path
zh: Redis 数据目录
zh-Hant: Redis 資料目錄
ja: Redis データディレクトリ
ko: Redis 데이터 디렉터리
ru: Каталог данных Redis
ms: Laluan data Redis
pt-br: Caminho de dados Redis
required: true
type: text
- default: Asia/Shanghai
edit: true
envKey: TIME_ZONE
labelEn: Time zone
labelZh: 时区
label:
en: Time zone
zh: 时区
zh-Hant: 時區
ja: タイムゾーン
ko: 시간대
ru: Часовой пояс
ms: Zon waktu
pt-br: Fuso horário
required: true
type: text
@@ -0,0 +1,72 @@
networks:
1panel-network:
external: true
services:
mastodon:
image: "linuxserver/mastodon:4.7.3"
container_name: ${CONTAINER_NAME}
restart: always
networks:
- 1panel-network
ports:
- "${PANEL_APP_PORT_HTTP}:80"
- "${PANEL_APP_PORT_HTTPS}:443"
volumes:
- "${CONFIG_PATH}:/config"
environment:
- PUID=1000
- PGID=1000
- TZ=${TIME_ZONE}
- LOCAL_DOMAIN=${LOCAL_DOMAIN}
- REDIS_HOST=${REDIS_HOST}
- REDIS_PORT=${REDIS_PORT}
- DB_HOST=${DB_HOST}
- DB_USER=${DB_USER}
- DB_NAME=${DB_NAME}
- DB_PASS=${DB_PASSWORD}
- DB_PORT=${DB_PORT}
- ES_ENABLED=false
- ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=${ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY}
- ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=${ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY}
- ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=${ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT}
- SECRET_KEY_BASE=${SECRET_KEY_BASE}
- OTP_SECRET=${OTP_SECRET}
- VAPID_PRIVATE_KEY=${VAPID_PRIVATE_KEY}
- VAPID_PUBLIC_KEY=${VAPID_PUBLIC_KEY}
- SMTP_SERVER=${SMTP_SERVER}
- SMTP_PORT=${SMTP_PORT}
- SMTP_LOGIN=${SMTP_LOGIN:-}
- SMTP_PASSWORD=${SMTP_PASSWORD:-}
- SMTP_FROM_ADDRESS=${SMTP_FROM_ADDRESS}
- S3_ENABLED=false
- WEB_DOMAIN=${WEB_DOMAIN:-}
- MASTODON_PROMETHEUS_EXPORTER_ENABLED=${MASTODON_PROMETHEUS_EXPORTER_ENABLED}
- SIDEKIQ_THREADS=${SIDEKIQ_THREADS}
- DB_POOL=${DB_POOL}
depends_on:
- mastodon-db
- mastodon-redis
labels:
createdBy: "Apps"
mastodon-db:
image: "postgres:16-alpine"
restart: always
networks:
- 1panel-network
volumes:
- "${DB_DATA_PATH}:/var/lib/postgresql/data"
environment:
- POSTGRES_DB=${DB_NAME}
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASSWORD}
labels:
createdBy: "Apps"
mastodon-redis:
image: "redis:7-alpine"
restart: always
networks:
- 1panel-network
volumes:
- "${REDIS_DATA_PATH}:/data"
labels:
createdBy: "Apps"
+236
View File
@@ -0,0 +1,236 @@
#!/usr/bin/env bash
set -euo pipefail
ENV_FILE="${ENV_FILE:-./.env}"
MASTODON_IMAGE="lscr.io/linuxserver/mastodon:4.6.4"
LEGACY_DB_PASSWORD="mastodon-change-me"
LEGACY_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY="fGWkJDBwyRYhILyO7akZGLSSz0gAjPpo"
LEGACY_ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY="Z8BE3tc3XnmUr0MbRexRiPN7vcP52VX0"
LEGACY_ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT="3hRwOaLRCikUCsxd19cUcO5EgXCN6ig1"
LEGACY_SECRET_KEY_BASE="ae3ab77b9a041a4d760126dba8c4e24a33bdfcec7fdbadf3d973baf92017ee0d0b988d62c040aca882ee8030ea22adb04d9c26a9541c918a434029840c8719b0"
LEGACY_OTP_SECRET="b3d6069b87df718c733a1b17a9adcbd7705b76ba0b48dd3f3ce58870dd52bed0d32d947a43a4eadac4230d508e11df0cee6f59ed85e40670134a52545f1131ac"
LEGACY_VAPID_PRIVATE_KEY="1eF670mQsgs_-W_eb06ZZ46apD4qVDYNvFWu9eGWc7E="
LEGACY_VAPID_PUBLIC_KEY="BOOqXKvCVA9tb8Bas05sdez6fGavnA5SrDJe88s7FMeIu7txiQ5sOqOZqKIpTSS_xmm5Wkd_MKHqj2UCQIwl9_8="
read_env_value() {
local key="$1"
if [[ ! -f "$ENV_FILE" ]] || ! grep -qE "^${key}=" "$ENV_FILE"; then
return
fi
local current
current="$(sed -n -E "s/^${key}=//p" "$ENV_FILE" | tail -n 1)"
current="${current%\"}"
current="${current#\"}"
current="${current%\'}"
current="${current#\'}"
printf '%s\n' "$current"
}
read_cache_value() {
local cache_file="$1"
if [[ ! -s "$cache_file" ]]; then
return
fi
sed -n '1p' "$cache_file" | tr -d '\r\n'
}
write_cache_value() {
local cache_file="$1"
local value="$2"
mkdir -p "$(dirname "$cache_file")"
printf '%s\n' "$value" > "$cache_file"
chmod 600 "$cache_file" 2>/dev/null || true
}
escape_sed_replacement() {
printf '%s' "$1" | sed -e 's/[\/&|]/\\&/g'
}
set_env_value() {
local key="$1"
local value="$2"
if [[ ! -f "$ENV_FILE" ]]; then
echo "$ENV_FILE not found; skipped $key generation"
return
fi
local replacement
replacement="$(escape_sed_replacement "$value")"
if grep -qE "^${key}=" "$ENV_FILE"; then
sed -i -E "s|^${key}=.*|${key}=${replacement}|" "$ENV_FILE"
echo "Updated $key"
return
fi
printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE"
echo "Added $key"
}
generate_alnum_secret() {
local secret=""
local chunk=""
while [[ ${#secret} -lt 32 ]]; do
chunk="$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c $((32 - ${#secret})) || true)"
secret="${secret}${chunk}"
done
printf '%s\n' "$secret"
}
run_mastodon_generator() {
docker run --rm --entrypoint /bin/bash "$MASTODON_IMAGE" "$1"
}
extract_generator_value() {
local output="$1"
local key="$2"
printf '%s\n' "$output" | sed -n -E "s/^${key}=//p" | tail -n 1
}
is_active_record_secret_valid() {
local value="$1"
[[ "$value" =~ ^[A-Za-z0-9]{32}$ ]]
}
is_hex_secret_valid() {
local value="$1"
[[ "$value" =~ ^[a-f0-9]{128}$ ]]
}
is_vapid_key_valid() {
local value="$1"
[[ ${#value} -ge 40 && "$value" =~ ^[A-Za-z0-9_-]+=*$ ]]
}
CONFIG_PATH_VALUE="${CONFIG_PATH:-$(read_env_value CONFIG_PATH || true)}"
CONFIG_PATH_VALUE="${CONFIG_PATH_VALUE:-./data/config}"
SECRET_CACHE_DIR="${SECRET_CACHE_DIR:-${CONFIG_PATH_VALUE}/.mastodon_secret_cache}"
DB_PASSWORD_CACHE="${DB_PASSWORD_CACHE:-${SECRET_CACHE_DIR}/db_password}"
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE="${ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE:-${SECRET_CACHE_DIR}/active_record_encryption_primary_key}"
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE="${ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE:-${SECRET_CACHE_DIR}/active_record_encryption_deterministic_key}"
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE="${ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE:-${SECRET_CACHE_DIR}/active_record_encryption_key_derivation_salt}"
SECRET_KEY_BASE_CACHE="${SECRET_KEY_BASE_CACHE:-${SECRET_CACHE_DIR}/secret_key_base}"
OTP_SECRET_CACHE="${OTP_SECRET_CACHE:-${SECRET_CACHE_DIR}/otp_secret}"
VAPID_PRIVATE_KEY_CACHE="${VAPID_PRIVATE_KEY_CACHE:-${SECRET_CACHE_DIR}/vapid_private_key}"
VAPID_PUBLIC_KEY_CACHE="${VAPID_PUBLIC_KEY_CACHE:-${SECRET_CACHE_DIR}/vapid_public_key}"
if [[ -f "$ENV_FILE" ]]; then
db_password="$(read_env_value DB_PASSWORD || true)"
cached_db_password="$(read_cache_value "$DB_PASSWORD_CACHE" || true)"
if [[ -n "$db_password" && "$db_password" != "$LEGACY_DB_PASSWORD" ]]; then
write_cache_value "$DB_PASSWORD_CACHE" "$db_password"
elif [[ -n "$cached_db_password" ]]; then
set_env_value "DB_PASSWORD" "$cached_db_password"
else
db_password="$(generate_alnum_secret)"
set_env_value "DB_PASSWORD" "$db_password"
write_cache_value "$DB_PASSWORD_CACHE" "$db_password"
fi
active_primary="$(read_env_value ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY || true)"
active_deterministic="$(read_env_value ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY || true)"
active_salt="$(read_env_value ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT || true)"
cached_active_primary="$(read_cache_value "$ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE" || true)"
cached_active_deterministic="$(read_cache_value "$ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE" || true)"
cached_active_salt="$(read_cache_value "$ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE" || true)"
if is_active_record_secret_valid "$active_primary" \
&& is_active_record_secret_valid "$active_deterministic" \
&& is_active_record_secret_valid "$active_salt" \
&& [[ "$active_primary" != "$LEGACY_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" ]] \
&& [[ "$active_deterministic" != "$LEGACY_ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" ]] \
&& [[ "$active_salt" != "$LEGACY_ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" ]]; then
write_cache_value "$ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE" "$active_primary"
write_cache_value "$ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE" "$active_deterministic"
write_cache_value "$ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE" "$active_salt"
elif is_active_record_secret_valid "$cached_active_primary" \
&& is_active_record_secret_valid "$cached_active_deterministic" \
&& is_active_record_secret_valid "$cached_active_salt"; then
set_env_value "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" "$cached_active_primary"
set_env_value "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" "$cached_active_deterministic"
set_env_value "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" "$cached_active_salt"
elif [[ "$active_primary" == "$LEGACY_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" ]] \
|| [[ "$active_deterministic" == "$LEGACY_ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" ]] \
|| [[ "$active_salt" == "$LEGACY_ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" ]] \
|| ! is_active_record_secret_valid "$active_primary" \
|| ! is_active_record_secret_valid "$active_deterministic" \
|| ! is_active_record_secret_valid "$active_salt"; then
active_record_output="$(run_mastodon_generator generate-active-record)"
active_primary="$(extract_generator_value "$active_record_output" "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY")"
active_deterministic="$(extract_generator_value "$active_record_output" "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY")"
active_salt="$(extract_generator_value "$active_record_output" "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT")"
set_env_value "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" "$active_primary"
set_env_value "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" "$active_deterministic"
set_env_value "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" "$active_salt"
write_cache_value "$ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE" "$active_primary"
write_cache_value "$ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE" "$active_deterministic"
write_cache_value "$ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE" "$active_salt"
fi
secret_key_base="$(read_env_value SECRET_KEY_BASE || true)"
if [[ "$secret_key_base" == "$LEGACY_SECRET_KEY_BASE" ]] || ! is_hex_secret_valid "$secret_key_base"; then
cached_secret_key_base="$(read_cache_value "$SECRET_KEY_BASE_CACHE" || true)"
if is_hex_secret_valid "$cached_secret_key_base"; then
set_env_value "SECRET_KEY_BASE" "$cached_secret_key_base"
else
secret_key_base="$(run_mastodon_generator generate-secret | tail -n 1 | tr -d '\r\n')"
set_env_value "SECRET_KEY_BASE" "$secret_key_base"
write_cache_value "$SECRET_KEY_BASE_CACHE" "$secret_key_base"
fi
else
write_cache_value "$SECRET_KEY_BASE_CACHE" "$secret_key_base"
fi
otp_secret="$(read_env_value OTP_SECRET || true)"
if [[ "$otp_secret" == "$LEGACY_OTP_SECRET" ]] || ! is_hex_secret_valid "$otp_secret"; then
cached_otp_secret="$(read_cache_value "$OTP_SECRET_CACHE" || true)"
if is_hex_secret_valid "$cached_otp_secret"; then
set_env_value "OTP_SECRET" "$cached_otp_secret"
else
otp_secret="$(run_mastodon_generator generate-secret | tail -n 1 | tr -d '\r\n')"
set_env_value "OTP_SECRET" "$otp_secret"
write_cache_value "$OTP_SECRET_CACHE" "$otp_secret"
fi
else
write_cache_value "$OTP_SECRET_CACHE" "$otp_secret"
fi
vapid_private_key="$(read_env_value VAPID_PRIVATE_KEY || true)"
vapid_public_key="$(read_env_value VAPID_PUBLIC_KEY || true)"
cached_vapid_private_key="$(read_cache_value "$VAPID_PRIVATE_KEY_CACHE" || true)"
cached_vapid_public_key="$(read_cache_value "$VAPID_PUBLIC_KEY_CACHE" || true)"
if is_vapid_key_valid "$vapid_private_key" \
&& is_vapid_key_valid "$vapid_public_key" \
&& [[ "$vapid_private_key" != "$LEGACY_VAPID_PRIVATE_KEY" ]] \
&& [[ "$vapid_public_key" != "$LEGACY_VAPID_PUBLIC_KEY" ]]; then
write_cache_value "$VAPID_PRIVATE_KEY_CACHE" "$vapid_private_key"
write_cache_value "$VAPID_PUBLIC_KEY_CACHE" "$vapid_public_key"
elif is_vapid_key_valid "$cached_vapid_private_key" && is_vapid_key_valid "$cached_vapid_public_key"; then
set_env_value "VAPID_PRIVATE_KEY" "$cached_vapid_private_key"
set_env_value "VAPID_PUBLIC_KEY" "$cached_vapid_public_key"
elif [[ "$vapid_private_key" == "$LEGACY_VAPID_PRIVATE_KEY" ]] \
|| [[ "$vapid_public_key" == "$LEGACY_VAPID_PUBLIC_KEY" ]] \
|| ! is_vapid_key_valid "$vapid_private_key" \
|| ! is_vapid_key_valid "$vapid_public_key"; then
vapid_output="$(run_mastodon_generator generate-vapid)"
set_env_value "VAPID_PRIVATE_KEY" "$(extract_generator_value "$vapid_output" "VAPID_PRIVATE_KEY")"
set_env_value "VAPID_PUBLIC_KEY" "$(extract_generator_value "$vapid_output" "VAPID_PUBLIC_KEY")"
write_cache_value "$VAPID_PRIVATE_KEY_CACHE" "$(extract_generator_value "$vapid_output" "VAPID_PRIVATE_KEY")"
write_cache_value "$VAPID_PUBLIC_KEY_CACHE" "$(extract_generator_value "$vapid_output" "VAPID_PUBLIC_KEY")"
fi
fi
paths=(
"${CONFIG_PATH:-./data/config}"
"${DB_DATA_PATH:-./data/db}"
"${REDIS_DATA_PATH:-./data/redis}"
)
mkdir -p "${paths[@]}"
for path in "${paths[@]}"; do
case "$path" in
./*|../*) chown -R 1000:1000 "$path" 2>/dev/null || true ;;
esac
done
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
docker-compose down --volumes
+251
View File
@@ -0,0 +1,251 @@
#!/usr/bin/env bash
set -euo pipefail
ENV_FILE="${ENV_FILE:-./.env}"
MASTODON_IMAGE="lscr.io/linuxserver/mastodon:4.6.4"
LEGACY_DB_PASSWORD="mastodon-change-me"
LEGACY_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY="fGWkJDBwyRYhILyO7akZGLSSz0gAjPpo"
LEGACY_ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY="Z8BE3tc3XnmUr0MbRexRiPN7vcP52VX0"
LEGACY_ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT="3hRwOaLRCikUCsxd19cUcO5EgXCN6ig1"
LEGACY_SECRET_KEY_BASE="ae3ab77b9a041a4d760126dba8c4e24a33bdfcec7fdbadf3d973baf92017ee0d0b988d62c040aca882ee8030ea22adb04d9c26a9541c918a434029840c8719b0"
LEGACY_OTP_SECRET="b3d6069b87df718c733a1b17a9adcbd7705b76ba0b48dd3f3ce58870dd52bed0d32d947a43a4eadac4230d508e11df0cee6f59ed85e40670134a52545f1131ac"
LEGACY_VAPID_PRIVATE_KEY="1eF670mQsgs_-W_eb06ZZ46apD4qVDYNvFWu9eGWc7E="
LEGACY_VAPID_PUBLIC_KEY="BOOqXKvCVA9tb8Bas05sdez6fGavnA5SrDJe88s7FMeIu7txiQ5sOqOZqKIpTSS_xmm5Wkd_MKHqj2UCQIwl9_8="
ensure_env_default() {
local key="$1"
local value="$2"
if [[ ! -f "$ENV_FILE" ]]; then
echo "$ENV_FILE not found; skipped $key migration"
return
fi
if grep -qE "^${key}=" "$ENV_FILE"; then
echo "$key already exists"
return
fi
printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE"
echo "Added $key"
}
read_env_value() {
local key="$1"
if [[ ! -f "$ENV_FILE" ]] || ! grep -qE "^${key}=" "$ENV_FILE"; then
return
fi
local current
current="$(sed -n -E "s/^${key}=//p" "$ENV_FILE" | tail -n 1)"
current="${current%\"}"
current="${current#\"}"
current="${current%\'}"
current="${current#\'}"
printf '%s\n' "$current"
}
read_cache_value() {
local cache_file="$1"
if [[ ! -s "$cache_file" ]]; then
return
fi
sed -n '1p' "$cache_file" | tr -d '\r\n'
}
write_cache_value() {
local cache_file="$1"
local value="$2"
mkdir -p "$(dirname "$cache_file")"
printf '%s\n' "$value" > "$cache_file"
chmod 600 "$cache_file" 2>/dev/null || true
}
escape_sed_replacement() {
printf '%s' "$1" | sed -e 's/[\/&|]/\\&/g'
}
set_env_value() {
local key="$1"
local value="$2"
if [[ ! -f "$ENV_FILE" ]]; then
echo "$ENV_FILE not found; skipped $key generation"
return
fi
local replacement
replacement="$(escape_sed_replacement "$value")"
if grep -qE "^${key}=" "$ENV_FILE"; then
sed -i -E "s|^${key}=.*|${key}=${replacement}|" "$ENV_FILE"
echo "Updated $key"
return
fi
printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE"
echo "Added $key"
}
generate_alnum_secret() {
local secret=""
local chunk=""
while [[ ${#secret} -lt 32 ]]; do
chunk="$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c $((32 - ${#secret})) || true)"
secret="${secret}${chunk}"
done
printf '%s\n' "$secret"
}
run_mastodon_generator() {
docker run --rm --entrypoint /bin/bash "$MASTODON_IMAGE" "$1"
}
extract_generator_value() {
local output="$1"
local key="$2"
printf '%s\n' "$output" | sed -n -E "s/^${key}=//p" | tail -n 1
}
is_active_record_secret_valid() {
local value="$1"
[[ "$value" =~ ^[A-Za-z0-9]{32}$ ]]
}
is_hex_secret_valid() {
local value="$1"
[[ "$value" =~ ^[a-f0-9]{128}$ ]]
}
is_vapid_key_valid() {
local value="$1"
[[ ${#value} -ge 40 && "$value" =~ ^[A-Za-z0-9_-]+=*$ ]]
}
CONFIG_PATH_VALUE="${CONFIG_PATH:-$(read_env_value CONFIG_PATH || true)}"
CONFIG_PATH_VALUE="${CONFIG_PATH_VALUE:-./data/config}"
SECRET_CACHE_DIR="${SECRET_CACHE_DIR:-${CONFIG_PATH_VALUE}/.mastodon_secret_cache}"
DB_PASSWORD_CACHE="${DB_PASSWORD_CACHE:-${SECRET_CACHE_DIR}/db_password}"
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE="${ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE:-${SECRET_CACHE_DIR}/active_record_encryption_primary_key}"
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE="${ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE:-${SECRET_CACHE_DIR}/active_record_encryption_deterministic_key}"
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE="${ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE:-${SECRET_CACHE_DIR}/active_record_encryption_key_derivation_salt}"
SECRET_KEY_BASE_CACHE="${SECRET_KEY_BASE_CACHE:-${SECRET_CACHE_DIR}/secret_key_base}"
OTP_SECRET_CACHE="${OTP_SECRET_CACHE:-${SECRET_CACHE_DIR}/otp_secret}"
VAPID_PRIVATE_KEY_CACHE="${VAPID_PRIVATE_KEY_CACHE:-${SECRET_CACHE_DIR}/vapid_private_key}"
VAPID_PUBLIC_KEY_CACHE="${VAPID_PUBLIC_KEY_CACHE:-${SECRET_CACHE_DIR}/vapid_public_key}"
if [[ -f "$ENV_FILE" ]]; then
db_password="$(read_env_value DB_PASSWORD || true)"
cached_db_password="$(read_cache_value "$DB_PASSWORD_CACHE" || true)"
if [[ -n "$db_password" && "$db_password" != "$LEGACY_DB_PASSWORD" ]]; then
write_cache_value "$DB_PASSWORD_CACHE" "$db_password"
elif [[ -n "$cached_db_password" ]]; then
set_env_value "DB_PASSWORD" "$cached_db_password"
else
db_password="$(generate_alnum_secret)"
set_env_value "DB_PASSWORD" "$db_password"
write_cache_value "$DB_PASSWORD_CACHE" "$db_password"
fi
active_primary="$(read_env_value ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY || true)"
active_deterministic="$(read_env_value ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY || true)"
active_salt="$(read_env_value ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT || true)"
cached_active_primary="$(read_cache_value "$ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE" || true)"
cached_active_deterministic="$(read_cache_value "$ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE" || true)"
cached_active_salt="$(read_cache_value "$ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE" || true)"
if is_active_record_secret_valid "$active_primary" \
&& is_active_record_secret_valid "$active_deterministic" \
&& is_active_record_secret_valid "$active_salt" \
&& [[ "$active_primary" != "$LEGACY_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" ]] \
&& [[ "$active_deterministic" != "$LEGACY_ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" ]] \
&& [[ "$active_salt" != "$LEGACY_ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" ]]; then
write_cache_value "$ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE" "$active_primary"
write_cache_value "$ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE" "$active_deterministic"
write_cache_value "$ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE" "$active_salt"
elif is_active_record_secret_valid "$cached_active_primary" \
&& is_active_record_secret_valid "$cached_active_deterministic" \
&& is_active_record_secret_valid "$cached_active_salt"; then
set_env_value "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" "$cached_active_primary"
set_env_value "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" "$cached_active_deterministic"
set_env_value "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" "$cached_active_salt"
elif [[ "$active_primary" == "$LEGACY_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" ]] \
|| [[ "$active_deterministic" == "$LEGACY_ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" ]] \
|| [[ "$active_salt" == "$LEGACY_ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" ]] \
|| ! is_active_record_secret_valid "$active_primary" \
|| ! is_active_record_secret_valid "$active_deterministic" \
|| ! is_active_record_secret_valid "$active_salt"; then
active_record_output="$(run_mastodon_generator generate-active-record)"
active_primary="$(extract_generator_value "$active_record_output" "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY")"
active_deterministic="$(extract_generator_value "$active_record_output" "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY")"
active_salt="$(extract_generator_value "$active_record_output" "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT")"
set_env_value "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" "$active_primary"
set_env_value "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" "$active_deterministic"
set_env_value "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" "$active_salt"
write_cache_value "$ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_CACHE" "$active_primary"
write_cache_value "$ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY_CACHE" "$active_deterministic"
write_cache_value "$ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT_CACHE" "$active_salt"
fi
secret_key_base="$(read_env_value SECRET_KEY_BASE || true)"
if [[ "$secret_key_base" == "$LEGACY_SECRET_KEY_BASE" ]] || ! is_hex_secret_valid "$secret_key_base"; then
cached_secret_key_base="$(read_cache_value "$SECRET_KEY_BASE_CACHE" || true)"
if is_hex_secret_valid "$cached_secret_key_base"; then
set_env_value "SECRET_KEY_BASE" "$cached_secret_key_base"
else
secret_key_base="$(run_mastodon_generator generate-secret | tail -n 1 | tr -d '\r\n')"
set_env_value "SECRET_KEY_BASE" "$secret_key_base"
write_cache_value "$SECRET_KEY_BASE_CACHE" "$secret_key_base"
fi
else
write_cache_value "$SECRET_KEY_BASE_CACHE" "$secret_key_base"
fi
otp_secret="$(read_env_value OTP_SECRET || true)"
if [[ "$otp_secret" == "$LEGACY_OTP_SECRET" ]] || ! is_hex_secret_valid "$otp_secret"; then
cached_otp_secret="$(read_cache_value "$OTP_SECRET_CACHE" || true)"
if is_hex_secret_valid "$cached_otp_secret"; then
set_env_value "OTP_SECRET" "$cached_otp_secret"
else
otp_secret="$(run_mastodon_generator generate-secret | tail -n 1 | tr -d '\r\n')"
set_env_value "OTP_SECRET" "$otp_secret"
write_cache_value "$OTP_SECRET_CACHE" "$otp_secret"
fi
else
write_cache_value "$OTP_SECRET_CACHE" "$otp_secret"
fi
vapid_private_key="$(read_env_value VAPID_PRIVATE_KEY || true)"
vapid_public_key="$(read_env_value VAPID_PUBLIC_KEY || true)"
cached_vapid_private_key="$(read_cache_value "$VAPID_PRIVATE_KEY_CACHE" || true)"
cached_vapid_public_key="$(read_cache_value "$VAPID_PUBLIC_KEY_CACHE" || true)"
if is_vapid_key_valid "$vapid_private_key" \
&& is_vapid_key_valid "$vapid_public_key" \
&& [[ "$vapid_private_key" != "$LEGACY_VAPID_PRIVATE_KEY" ]] \
&& [[ "$vapid_public_key" != "$LEGACY_VAPID_PUBLIC_KEY" ]]; then
write_cache_value "$VAPID_PRIVATE_KEY_CACHE" "$vapid_private_key"
write_cache_value "$VAPID_PUBLIC_KEY_CACHE" "$vapid_public_key"
elif is_vapid_key_valid "$cached_vapid_private_key" && is_vapid_key_valid "$cached_vapid_public_key"; then
set_env_value "VAPID_PRIVATE_KEY" "$cached_vapid_private_key"
set_env_value "VAPID_PUBLIC_KEY" "$cached_vapid_public_key"
elif [[ "$vapid_private_key" == "$LEGACY_VAPID_PRIVATE_KEY" ]] \
|| [[ "$vapid_public_key" == "$LEGACY_VAPID_PUBLIC_KEY" ]] \
|| ! is_vapid_key_valid "$vapid_private_key" \
|| ! is_vapid_key_valid "$vapid_public_key"; then
vapid_output="$(run_mastodon_generator generate-vapid)"
set_env_value "VAPID_PRIVATE_KEY" "$(extract_generator_value "$vapid_output" "VAPID_PRIVATE_KEY")"
set_env_value "VAPID_PUBLIC_KEY" "$(extract_generator_value "$vapid_output" "VAPID_PUBLIC_KEY")"
write_cache_value "$VAPID_PRIVATE_KEY_CACHE" "$(extract_generator_value "$vapid_output" "VAPID_PRIVATE_KEY")"
write_cache_value "$VAPID_PUBLIC_KEY_CACHE" "$(extract_generator_value "$vapid_output" "VAPID_PUBLIC_KEY")"
fi
ensure_env_default "REDIS_HOST" "mastodon-redis"
ensure_env_default "REDIS_PORT" "6379"
ensure_env_default "DB_HOST" "mastodon-db"
ensure_env_default "DB_PORT" "5432"
ensure_env_default "DB_USER" "mastodon"
ensure_env_default "DB_NAME" "mastodon"
ensure_env_default "MASTODON_PROMETHEUS_EXPORTER_ENABLED" "false"
else
echo "$ENV_FILE not found; skipped LinuxServer environment migration"
fi