mirror of
https://github.com/okxlin/appstore.git
synced 2026-10-30 16:05:49 +00:00
Merge remote-tracking branch 'refs/remotes/origin/localApps' into maint-refresh-pr-7681
This commit is contained in:
@@ -93,5 +93,14 @@
|
||||
],
|
||||
"zipline": [
|
||||
"zipline"
|
||||
],
|
||||
"bookstack-linuxserver": [
|
||||
"bookstack"
|
||||
],
|
||||
"mastodon-linuxserver": [
|
||||
"mastodon"
|
||||
],
|
||||
"odoo": [
|
||||
"odoo"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ networks:
|
||||
external: true
|
||||
services:
|
||||
adguardhome-sync:
|
||||
image: "linuxserver/adguardhome-sync:0.9.3"
|
||||
image: "linuxserver/adguardhome-sync:0.9.4"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
networks:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
bark:
|
||||
image: "finab/bark-server:v2.3.7"
|
||||
image: "finab/bark-server:v2.3.8"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
networks:
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
CONTAINER_NAME=
|
||||
PANEL_APP_PORT_HTTP=6875
|
||||
CONFIG_PATH=./data/config
|
||||
APP_URL=http://bookstack.example.com
|
||||
APP_KEY=base64:replace-with-generated-app-key
|
||||
DB_HOST=bookstack-db
|
||||
DB_PORT=3306
|
||||
DB_USERNAME=bookstack
|
||||
DB_DATABASE=bookstack
|
||||
DB_PASSWORD=replace-with-db-password
|
||||
QUEUE_CONNECTION=
|
||||
DB_DATA_PATH=./data/db
|
||||
TIME_ZONE=Asia/Shanghai
|
||||
@@ -0,0 +1,204 @@
|
||||
additionalProperties:
|
||||
formFields:
|
||||
- default: 6875
|
||||
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: ./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: APP_URL
|
||||
labelEn: Application URL
|
||||
labelZh: 应用访问 URL
|
||||
label:
|
||||
en: Application URL
|
||||
zh: 应用访问 URL
|
||||
zh-Hant: 應用程式存取 URL
|
||||
ja: アプリケーション URL
|
||||
ko: 애플리케이션 URL
|
||||
ru: URL приложения
|
||||
ms: URL aplikasi
|
||||
pt-br: URL do aplicativo
|
||||
required: true
|
||||
type: text
|
||||
- default: ''
|
||||
edit: true
|
||||
envKey: APP_KEY
|
||||
labelEn: Application key
|
||||
labelZh: 应用密钥
|
||||
label:
|
||||
en: Application key
|
||||
zh: 应用密钥
|
||||
zh-Hant: 應用程式密鑰
|
||||
ja: アプリケーションキー
|
||||
ko: 애플리케이션 키
|
||||
ru: Ключ приложения
|
||||
ms: Kunci aplikasi
|
||||
pt-br: Chave do aplicativo
|
||||
required: true
|
||||
random: true
|
||||
type: password
|
||||
rule: paramComplexity
|
||||
- default: bookstack-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: 3306
|
||||
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: bookstack
|
||||
edit: true
|
||||
envKey: DB_USERNAME
|
||||
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: bookstack
|
||||
edit: true
|
||||
envKey: DB_DATABASE
|
||||
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: ''
|
||||
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: QUEUE_CONNECTION
|
||||
labelEn: Async queue connection
|
||||
labelZh: 异步队列连接
|
||||
label:
|
||||
en: Async queue connection
|
||||
zh: 异步队列连接
|
||||
zh-Hant: 非同步佇列連線
|
||||
ja: 非同期キュー接続
|
||||
ko: 비동기 큐 연결
|
||||
ru: Подключение асинхронной очереди
|
||||
ms: Sambungan baris gilir tak segerak
|
||||
pt-br: Conexao de fila assincrona
|
||||
required: false
|
||||
type: select
|
||||
values:
|
||||
- label: disabled
|
||||
value: ''
|
||||
- label: database
|
||||
value: database
|
||||
- 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: 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,44 @@
|
||||
networks:
|
||||
1panel-network:
|
||||
external: true
|
||||
services:
|
||||
bookstack:
|
||||
image: "linuxserver/bookstack:26.09.20260924"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
networks:
|
||||
- 1panel-network
|
||||
ports:
|
||||
- "${PANEL_APP_PORT_HTTP}:80"
|
||||
volumes:
|
||||
- "${CONFIG_PATH}:/config"
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=${TIME_ZONE}
|
||||
- APP_URL=${APP_URL}
|
||||
- APP_KEY=${APP_KEY}
|
||||
- DB_HOST=${DB_HOST}
|
||||
- DB_PORT=${DB_PORT}
|
||||
- DB_USERNAME=${DB_USERNAME}
|
||||
- DB_PASSWORD=${DB_PASSWORD}
|
||||
- DB_DATABASE=${DB_DATABASE}
|
||||
- QUEUE_CONNECTION=${QUEUE_CONNECTION:-}
|
||||
depends_on:
|
||||
- bookstack-db
|
||||
labels:
|
||||
createdBy: "Apps"
|
||||
bookstack-db:
|
||||
image: "mariadb:11.4"
|
||||
restart: always
|
||||
networks:
|
||||
- 1panel-network
|
||||
volumes:
|
||||
- "${DB_DATA_PATH}:/var/lib/mysql"
|
||||
environment:
|
||||
- MARIADB_ROOT_PASSWORD=${DB_PASSWORD}
|
||||
- MARIADB_DATABASE=${DB_DATABASE}
|
||||
- MARIADB_USER=${DB_USERNAME}
|
||||
- MARIADB_PASSWORD=${DB_PASSWORD}
|
||||
labels:
|
||||
createdBy: "Apps"
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ENV_FILE="${ENV_FILE:-./.env}"
|
||||
LEGACY_BOOKSTACK_APP_KEY="base64:j1wQ2YbRrQ3j0tG5h6v8cYpV1gW9mN4uQ2xZ7aE3sT8="
|
||||
LEGACY_BOOKSTACK_DB_PASSWORD="bookstack-change-me"
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
generate_laravel_app_key() {
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
printf 'base64:%s\n' "$(openssl rand -base64 32 | tr -d '\r\n')"
|
||||
return
|
||||
fi
|
||||
|
||||
if command -v base64 >/dev/null 2>&1; then
|
||||
printf 'base64:%s\n' "$(head -c 32 /dev/urandom | base64 | tr -d '\r\n')"
|
||||
return
|
||||
fi
|
||||
|
||||
generate_alnum_secret
|
||||
}
|
||||
|
||||
laravel_app_key_is_valid() {
|
||||
local value="$1"
|
||||
[[ "$value" =~ ^base64:[A-Za-z0-9+/=]{43,}$ ]] && return 0
|
||||
[[ "$value" =~ ^[A-Za-z0-9]{32,}$ ]] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
CONFIG_PATH_VALUE="${CONFIG_PATH:-$(read_env_value CONFIG_PATH || true)}"
|
||||
CONFIG_PATH_VALUE="${CONFIG_PATH_VALUE:-./data/config}"
|
||||
APP_KEY_CACHE="${APP_KEY_CACHE:-${CONFIG_PATH_VALUE}/.bookstack_app_key}"
|
||||
DB_PASSWORD_CACHE="${DB_PASSWORD_CACHE:-${CONFIG_PATH_VALUE}/.bookstack_db_password}"
|
||||
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
app_key="$(read_env_value APP_KEY || true)"
|
||||
cached_app_key="$(read_cache_value "$APP_KEY_CACHE" || true)"
|
||||
if laravel_app_key_is_valid "$app_key" && [[ "$app_key" != "$LEGACY_BOOKSTACK_APP_KEY" ]]; then
|
||||
write_cache_value "$APP_KEY_CACHE" "$app_key"
|
||||
elif laravel_app_key_is_valid "$cached_app_key"; then
|
||||
set_env_value "APP_KEY" "$cached_app_key"
|
||||
else
|
||||
app_key="$(generate_laravel_app_key)"
|
||||
set_env_value "APP_KEY" "$app_key"
|
||||
write_cache_value "$APP_KEY_CACHE" "$app_key"
|
||||
fi
|
||||
|
||||
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_BOOKSTACK_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
|
||||
fi
|
||||
|
||||
paths=(
|
||||
"${CONFIG_PATH:-./data/config}"
|
||||
"${DB_DATA_PATH:-./data/db}"
|
||||
)
|
||||
|
||||
mkdir -p "${paths[@]}"
|
||||
for path in "${paths[@]}"; do
|
||||
case "$path" in
|
||||
./*|../*) chown -R 1000:1000 "$path" 2>/dev/null || true ;;
|
||||
esac
|
||||
done
|
||||
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
docker-compose down --volumes
|
||||
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ENV_FILE="${ENV_FILE:-./.env}"
|
||||
LEGACY_BOOKSTACK_APP_KEY="base64:j1wQ2YbRrQ3j0tG5h6v8cYpV1gW9mN4uQ2xZ7aE3sT8="
|
||||
LEGACY_BOOKSTACK_DB_PASSWORD="bookstack-change-me"
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
generate_laravel_app_key() {
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
printf 'base64:%s\n' "$(openssl rand -base64 32 | tr -d '\r\n')"
|
||||
return
|
||||
fi
|
||||
|
||||
if command -v base64 >/dev/null 2>&1; then
|
||||
printf 'base64:%s\n' "$(head -c 32 /dev/urandom | base64 | tr -d '\r\n')"
|
||||
return
|
||||
fi
|
||||
|
||||
generate_alnum_secret
|
||||
}
|
||||
|
||||
laravel_app_key_is_valid() {
|
||||
local value="$1"
|
||||
[[ "$value" =~ ^base64:[A-Za-z0-9+/=]{43,}$ ]] && return 0
|
||||
[[ "$value" =~ ^[A-Za-z0-9]{32,}$ ]] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
CONFIG_PATH_VALUE="${CONFIG_PATH:-$(read_env_value CONFIG_PATH || true)}"
|
||||
CONFIG_PATH_VALUE="${CONFIG_PATH_VALUE:-./data/config}"
|
||||
APP_KEY_CACHE="${APP_KEY_CACHE:-${CONFIG_PATH_VALUE}/.bookstack_app_key}"
|
||||
DB_PASSWORD_CACHE="${DB_PASSWORD_CACHE:-${CONFIG_PATH_VALUE}/.bookstack_db_password}"
|
||||
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
app_key="$(read_env_value APP_KEY || true)"
|
||||
cached_app_key="$(read_cache_value "$APP_KEY_CACHE" || true)"
|
||||
if laravel_app_key_is_valid "$app_key" && [[ "$app_key" != "$LEGACY_BOOKSTACK_APP_KEY" ]]; then
|
||||
write_cache_value "$APP_KEY_CACHE" "$app_key"
|
||||
elif laravel_app_key_is_valid "$cached_app_key"; then
|
||||
set_env_value "APP_KEY" "$cached_app_key"
|
||||
else
|
||||
app_key="$(generate_laravel_app_key)"
|
||||
set_env_value "APP_KEY" "$app_key"
|
||||
write_cache_value "$APP_KEY_CACHE" "$app_key"
|
||||
fi
|
||||
|
||||
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_BOOKSTACK_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
|
||||
|
||||
ensure_env_default "DB_HOST" "bookstack-db"
|
||||
ensure_env_default "DB_PORT" "3306"
|
||||
ensure_env_default "DB_USERNAME" "bookstack"
|
||||
ensure_env_default "DB_DATABASE" "bookstack"
|
||||
ensure_env_default "QUEUE_CONNECTION" ""
|
||||
else
|
||||
echo "$ENV_FILE not found; skipped LinuxServer environment migration"
|
||||
fi
|
||||
@@ -3,7 +3,7 @@ networks:
|
||||
external: true
|
||||
services:
|
||||
calibre:
|
||||
image: "linuxserver/calibre:9.15.0"
|
||||
image: "linuxserver/calibre:9.16.0"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
networks:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
cpa-manager-plus:
|
||||
image: "seakee/cpa-manager-plus:v1.14.4"
|
||||
image: "seakee/cpa-manager-plus:v1.14.5"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
ports:
|
||||
- "${PANEL_APP_PORT_HTTP}:18317"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
dagu:
|
||||
image: "ghcr.io/dagucloud/dagu:2.18.2"
|
||||
image: "ghcr.io/dagucloud/dagu:2.19.0"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
networks:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
dailytxt:
|
||||
image: "phitux/dailytxt:2.6.3"
|
||||
image: "phitux/dailytxt:2.6.4"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
|
||||
@@ -3,7 +3,7 @@ networks:
|
||||
external: true
|
||||
services:
|
||||
darktable:
|
||||
image: "linuxserver/darktable:25.6.1"
|
||||
image: "linuxserver/darktable:25.6.2"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
networks:
|
||||
|
||||
@@ -3,7 +3,7 @@ networks:
|
||||
external: true
|
||||
services:
|
||||
flexget:
|
||||
image: "linuxserver/flexget:3.22.1"
|
||||
image: "linuxserver/flexget:3.22.2"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
networks:
|
||||
|
||||
@@ -14,7 +14,7 @@ services:
|
||||
- PGID=1000
|
||||
- TZ=${TIME_ZONE}
|
||||
- FG_WEBUI_PASSWD=${WEB_PASSWORD}
|
||||
image: wiserain/flexget:3.21.3
|
||||
image: wiserain/flexget:3.22.2
|
||||
labels:
|
||||
createdBy: "Apps"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
gotosocial:
|
||||
image: "docker.io/superseriousbusiness/gotosocial:0.22.1"
|
||||
image: "docker.io/superseriousbusiness/gotosocial:0.22.2"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
user: "1000:1000"
|
||||
restart: always
|
||||
|
||||
@@ -3,7 +3,7 @@ networks:
|
||||
external: true
|
||||
services:
|
||||
grav:
|
||||
image: "linuxserver/grav:2.2.5"
|
||||
image: "linuxserver/grav:2.2.6"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
networks:
|
||||
|
||||
@@ -3,7 +3,7 @@ networks:
|
||||
external: true
|
||||
services:
|
||||
healthchecks:
|
||||
image: "linuxserver/healthchecks:4.4.20261005"
|
||||
image: "linuxserver/healthchecks:4.4.20261012"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
networks:
|
||||
|
||||
Executable
+97
@@ -0,0 +1,97 @@
|
||||
# Core runtime
|
||||
CONTAINER_NAME=hermes-agent
|
||||
TZ=Asia/Shanghai
|
||||
APP_DATA_DIR=./data
|
||||
HERMES_UID=10000
|
||||
HERMES_GID=10000
|
||||
PANEL_APP_PORT_API=8642
|
||||
GATEWAY_STATIC_IP=172.18.0.240
|
||||
API_SERVER_KEY=change_me_with_a_random_api_key
|
||||
API_SERVER_CORS_ORIGINS=*
|
||||
|
||||
# LLM providers
|
||||
OPENAI_API_KEY=
|
||||
OPENROUTER_API_KEY=
|
||||
ANTHROPIC_TOKEN=
|
||||
ANTHROPIC_BASE_URL=
|
||||
GOOGLE_API_KEY=
|
||||
GLM_API_KEY=
|
||||
GLM_BASE_URL=https://api.z.ai/api/paas/v4
|
||||
KIMI_API_KEY=
|
||||
KIMI_BASE_URL=https://api.kimi.com/coding/v1
|
||||
MINIMAX_API_KEY=
|
||||
MINIMAX_BASE_URL=https://api.minimax.io/v1
|
||||
GROQ_API_KEY=
|
||||
MISTRAL_API_KEY=
|
||||
COHERE_API_KEY=
|
||||
DEEPSEEK_API_KEY=
|
||||
TOGETHER_API_KEY=
|
||||
PERPLEXITY_API_KEY=
|
||||
FIREWORKS_API_KEY=
|
||||
XAI_API_KEY=
|
||||
HF_TOKEN=
|
||||
|
||||
# Search / crawling / telemetry
|
||||
EXA_API_KEY=
|
||||
PARALLEL_API_KEY=
|
||||
FIRECRAWL_API_KEY=
|
||||
FIRECRAWL_API_URL=
|
||||
HELICONE_API_KEY=
|
||||
|
||||
# Gateway access control
|
||||
GATEWAY_ALLOWED_USERS=
|
||||
|
||||
# Telegram
|
||||
TELEGRAM_BOT_TOKEN=
|
||||
TELEGRAM_ALLOWED_USERS=
|
||||
TELEGRAM_HOME_CHANNEL=
|
||||
TELEGRAM_HOME_CHANNEL_NAME=
|
||||
|
||||
# Discord
|
||||
DISCORD_HOME_CHANNEL=
|
||||
DISCORD_HOME_CHANNEL_NAME=
|
||||
DISCORD_REQUIRE_MENTION=false
|
||||
DISCORD_FREE_RESPONSE_CHANNELS=
|
||||
DISCORD_AUTO_THREAD=false
|
||||
|
||||
# Slack
|
||||
SLACK_HOME_CHANNEL=
|
||||
SLACK_HOME_CHANNEL_NAME=
|
||||
SLACK_ALLOWED_USERS=
|
||||
|
||||
# WhatsApp
|
||||
WHATSAPP_ENABLED=false
|
||||
WHATSAPP_MODE=personal
|
||||
WHATSAPP_ALLOWED_USERS=
|
||||
|
||||
# Signal
|
||||
SIGNAL_HTTP_URL=
|
||||
SIGNAL_ACCOUNT=
|
||||
SIGNAL_ALLOWED_USERS=
|
||||
SIGNAL_GROUP_ALLOWED_USERS=
|
||||
SIGNAL_HOME_CHANNEL=
|
||||
SIGNAL_HOME_CHANNEL_NAME=
|
||||
SIGNAL_IGNORE_STORIES=false
|
||||
|
||||
# Email
|
||||
EMAIL_ADDRESS=
|
||||
EMAIL_PASSWORD=
|
||||
EMAIL_IMAP_HOST=
|
||||
EMAIL_SMTP_HOST=
|
||||
EMAIL_HOME_ADDRESS=
|
||||
EMAIL_HOME_ADDRESS_NAME=
|
||||
|
||||
# GitHub
|
||||
GH_TOKEN=
|
||||
GITHUB_APP_ID=
|
||||
GITHUB_APP_PRIVATE_KEY_PATH=
|
||||
GITHUB_APP_INSTALLATION_ID=
|
||||
|
||||
# Home Assistant
|
||||
HASS_URL=
|
||||
HASS_TOKEN=
|
||||
|
||||
# Modal / Daytona
|
||||
MODAL_TOKEN_ID=
|
||||
MODAL_TOKEN_SECRET=
|
||||
DAYTONA_API_KEY=
|
||||
Executable
+1215
File diff suppressed because it is too large
Load Diff
+92
@@ -0,0 +1,92 @@
|
||||
services:
|
||||
hermes-agent:
|
||||
image: "nousresearch/hermes-agent:v2026.9.24"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
command:
|
||||
- gateway
|
||||
- run
|
||||
environment:
|
||||
- TZ=${TZ}
|
||||
- HERMES_UID=${HERMES_UID:-10000}
|
||||
- HERMES_GID=${HERMES_GID:-10000}
|
||||
- API_SERVER_ENABLED=true
|
||||
- API_SERVER_HOST=0.0.0.0
|
||||
- API_SERVER_KEY=${API_SERVER_KEY}
|
||||
- API_SERVER_CORS_ORIGINS=${API_SERVER_CORS_ORIGINS:-*}
|
||||
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-}
|
||||
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
||||
- GOOGLE_API_KEY=${GOOGLE_API_KEY:-}
|
||||
- GLM_API_KEY=${GLM_API_KEY:-}
|
||||
- GLM_BASE_URL=${GLM_BASE_URL:-}
|
||||
- KIMI_API_KEY=${KIMI_API_KEY:-}
|
||||
- KIMI_BASE_URL=${KIMI_BASE_URL:-}
|
||||
- MINIMAX_API_KEY=${MINIMAX_API_KEY:-}
|
||||
- MINIMAX_BASE_URL=${MINIMAX_BASE_URL:-}
|
||||
- ANTHROPIC_TOKEN=${ANTHROPIC_TOKEN:-}
|
||||
- GROQ_API_KEY=${GROQ_API_KEY:-}
|
||||
- MISTRAL_API_KEY=${MISTRAL_API_KEY:-}
|
||||
- COHERE_API_KEY=${COHERE_API_KEY:-}
|
||||
- DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY:-}
|
||||
- TOGETHER_API_KEY=${TOGETHER_API_KEY:-}
|
||||
- PERPLEXITY_API_KEY=${PERPLEXITY_API_KEY:-}
|
||||
- FIREWORKS_API_KEY=${FIREWORKS_API_KEY:-}
|
||||
- XAI_API_KEY=${XAI_API_KEY:-}
|
||||
- HF_TOKEN=${HF_TOKEN:-}
|
||||
- EXA_API_KEY=${EXA_API_KEY:-}
|
||||
- PARALLEL_API_KEY=${PARALLEL_API_KEY:-}
|
||||
- FIRECRAWL_API_KEY=${FIRECRAWL_API_KEY:-}
|
||||
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN:-}
|
||||
- TELEGRAM_ALLOWED_USERS=${TELEGRAM_ALLOWED_USERS:-}
|
||||
- TELEGRAM_HOME_CHANNEL=${TELEGRAM_HOME_CHANNEL:-}
|
||||
- ANTHROPIC_BASE_URL=${ANTHROPIC_BASE_URL:-}
|
||||
- TELEGRAM_HOME_CHANNEL_NAME=${TELEGRAM_HOME_CHANNEL_NAME:-}
|
||||
- DISCORD_HOME_CHANNEL=${DISCORD_HOME_CHANNEL:-}
|
||||
- DISCORD_HOME_CHANNEL_NAME=${DISCORD_HOME_CHANNEL_NAME:-}
|
||||
- DISCORD_REQUIRE_MENTION=${DISCORD_REQUIRE_MENTION:-}
|
||||
- DISCORD_FREE_RESPONSE_CHANNELS=${DISCORD_FREE_RESPONSE_CHANNELS:-}
|
||||
- DISCORD_AUTO_THREAD=${DISCORD_AUTO_THREAD:-}
|
||||
- SLACK_HOME_CHANNEL=${SLACK_HOME_CHANNEL:-}
|
||||
- SLACK_HOME_CHANNEL_NAME=${SLACK_HOME_CHANNEL_NAME:-}
|
||||
- SLACK_ALLOWED_USERS=${SLACK_ALLOWED_USERS:-}
|
||||
- WHATSAPP_ENABLED=${WHATSAPP_ENABLED:-}
|
||||
- WHATSAPP_MODE=${WHATSAPP_MODE:-}
|
||||
- WHATSAPP_ALLOWED_USERS=${WHATSAPP_ALLOWED_USERS:-}
|
||||
- SIGNAL_HTTP_URL=${SIGNAL_HTTP_URL:-}
|
||||
- SIGNAL_ACCOUNT=${SIGNAL_ACCOUNT:-}
|
||||
- SIGNAL_ALLOWED_USERS=${SIGNAL_ALLOWED_USERS:-}
|
||||
- SIGNAL_GROUP_ALLOWED_USERS=${SIGNAL_GROUP_ALLOWED_USERS:-}
|
||||
- SIGNAL_HOME_CHANNEL=${SIGNAL_HOME_CHANNEL:-}
|
||||
- SIGNAL_HOME_CHANNEL_NAME=${SIGNAL_HOME_CHANNEL_NAME:-}
|
||||
- SIGNAL_IGNORE_STORIES=${SIGNAL_IGNORE_STORIES:-}
|
||||
- HASS_URL=${HASS_URL:-}
|
||||
- HASS_TOKEN=${HASS_TOKEN:-}
|
||||
- HELICONE_API_KEY=${HELICONE_API_KEY:-}
|
||||
- FIRECRAWL_API_URL=${FIRECRAWL_API_URL:-}
|
||||
- GH_TOKEN=${GH_TOKEN:-}
|
||||
- GITHUB_APP_ID=${GITHUB_APP_ID:-}
|
||||
- GITHUB_APP_PRIVATE_KEY_PATH=${GITHUB_APP_PRIVATE_KEY_PATH:-}
|
||||
- GITHUB_APP_INSTALLATION_ID=${GITHUB_APP_INSTALLATION_ID:-}
|
||||
- EMAIL_ADDRESS=${EMAIL_ADDRESS:-}
|
||||
- EMAIL_PASSWORD=${EMAIL_PASSWORD:-}
|
||||
- EMAIL_IMAP_HOST=${EMAIL_IMAP_HOST:-}
|
||||
- EMAIL_SMTP_HOST=${EMAIL_SMTP_HOST:-}
|
||||
- EMAIL_HOME_ADDRESS=${EMAIL_HOME_ADDRESS:-}
|
||||
- EMAIL_HOME_ADDRESS_NAME=${EMAIL_HOME_ADDRESS_NAME:-}
|
||||
- GATEWAY_ALLOWED_USERS=${GATEWAY_ALLOWED_USERS:-}
|
||||
- MODAL_TOKEN_ID=${MODAL_TOKEN_ID:-}
|
||||
- MODAL_TOKEN_SECRET=${MODAL_TOKEN_SECRET:-}
|
||||
- DAYTONA_API_KEY=${DAYTONA_API_KEY:-}
|
||||
networks:
|
||||
1panel-network:
|
||||
ipv4_address: ${GATEWAY_STATIC_IP}
|
||||
ports:
|
||||
- "${PANEL_APP_PORT_API}:8642"
|
||||
volumes:
|
||||
- "${APP_DATA_DIR}:/opt/data"
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
createdBy: "Apps"
|
||||
|
||||
networks:
|
||||
1panel-network:
|
||||
external: true
|
||||
Executable
+76
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
ENV_FILE="$ROOT_DIR/.env"
|
||||
APP_DATA_DIR_RAW="${APP_DATA_DIR:-./data}"
|
||||
DEFAULT_UID="10000"
|
||||
DEFAULT_GID="10000"
|
||||
TARGET_UID="${HERMES_UID:-$DEFAULT_UID}"
|
||||
TARGET_GID="${HERMES_GID:-$DEFAULT_GID}"
|
||||
|
||||
if [[ "$APP_DATA_DIR_RAW" = /* ]]; then
|
||||
APP_DATA_DIR_ABS="$APP_DATA_DIR_RAW"
|
||||
else
|
||||
APP_DATA_DIR_ABS="$ROOT_DIR/${APP_DATA_DIR_RAW#./}"
|
||||
fi
|
||||
|
||||
mkdir -p "$APP_DATA_DIR_ABS"
|
||||
|
||||
upsert_env_if_missing() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then
|
||||
printf '\n%s=%s\n' "$key" "$value" >> "$ENV_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
generate_random_value() {
|
||||
local length="$1"
|
||||
local value=""
|
||||
|
||||
while [[ ${#value} -lt $length ]]; do
|
||||
value+="$(LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c "$((length - ${#value}))" || true)"
|
||||
done
|
||||
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
ensure_env_value() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then
|
||||
printf '\n%s=%s\n' "$key" "$value" >> "$ENV_FILE"
|
||||
elif grep -Eq "^${key}=([[:space:]]*|\"\"|'')$" "$ENV_FILE"; then
|
||||
sed -i "s|^${key}=.*|${key}=${value}|" "$ENV_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
EXISTING_UID=$(grep '^HERMES_UID=' "$ENV_FILE" 2>/dev/null | tail -n 1 | cut -d '=' -f 2- || true)
|
||||
EXISTING_GID=$(grep '^HERMES_GID=' "$ENV_FILE" 2>/dev/null | tail -n 1 | cut -d '=' -f 2- || true)
|
||||
[[ -n "$EXISTING_UID" ]] && TARGET_UID="$EXISTING_UID"
|
||||
[[ -n "$EXISTING_GID" ]] && TARGET_GID="$EXISTING_GID"
|
||||
fi
|
||||
|
||||
upsert_env_if_missing "HERMES_UID" "$TARGET_UID"
|
||||
upsert_env_if_missing "HERMES_GID" "$TARGET_GID"
|
||||
ensure_env_value "API_SERVER_KEY" "$(generate_random_value 48)"
|
||||
ensure_env_value "API_SERVER_CORS_ORIGINS" "*"
|
||||
|
||||
CURRENT_UID=$(stat -c '%u' "$APP_DATA_DIR_ABS" 2>/dev/null || echo '')
|
||||
CURRENT_GID=$(stat -c '%g' "$APP_DATA_DIR_ABS" 2>/dev/null || echo '')
|
||||
|
||||
if [[ "$CURRENT_UID" != "$TARGET_UID" || "$CURRENT_GID" != "$TARGET_GID" ]]; then
|
||||
chown -R "$TARGET_UID:$TARGET_GID" "$APP_DATA_DIR_ABS" 2>/dev/null || true
|
||||
fi
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
docker-compose down --volumes
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
ENV_FILE="$ROOT_DIR/.env"
|
||||
APP_DATA_DIR_RAW="${APP_DATA_DIR:-./data}"
|
||||
DEFAULT_UID="10000"
|
||||
DEFAULT_GID="10000"
|
||||
TARGET_UID="${HERMES_UID:-$DEFAULT_UID}"
|
||||
TARGET_GID="${HERMES_GID:-$DEFAULT_GID}"
|
||||
|
||||
if [[ "$APP_DATA_DIR_RAW" = /* ]]; then
|
||||
APP_DATA_DIR_ABS="$APP_DATA_DIR_RAW"
|
||||
else
|
||||
APP_DATA_DIR_ABS="$ROOT_DIR/${APP_DATA_DIR_RAW#./}"
|
||||
fi
|
||||
|
||||
mkdir -p "$APP_DATA_DIR_ABS"
|
||||
|
||||
upsert_env_if_missing() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then
|
||||
printf '\n%s=%s\n' "$key" "$value" >> "$ENV_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
generate_random_value() {
|
||||
local length="$1"
|
||||
local value=""
|
||||
|
||||
while [[ ${#value} -lt $length ]]; do
|
||||
value+="$(LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c "$((length - ${#value}))" || true)"
|
||||
done
|
||||
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
ensure_env_value() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then
|
||||
printf '\n%s=%s\n' "$key" "$value" >> "$ENV_FILE"
|
||||
elif grep -Eq "^${key}=([[:space:]]*|\"\"|'')$" "$ENV_FILE"; then
|
||||
sed -i "s|^${key}=.*|${key}=${value}|" "$ENV_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
EXISTING_UID=$(grep '^HERMES_UID=' "$ENV_FILE" 2>/dev/null | tail -n 1 | cut -d '=' -f 2- || true)
|
||||
EXISTING_GID=$(grep '^HERMES_GID=' "$ENV_FILE" 2>/dev/null | tail -n 1 | cut -d '=' -f 2- || true)
|
||||
[[ -n "$EXISTING_UID" ]] && TARGET_UID="$EXISTING_UID"
|
||||
[[ -n "$EXISTING_GID" ]] && TARGET_GID="$EXISTING_GID"
|
||||
fi
|
||||
|
||||
upsert_env_if_missing "HERMES_UID" "$TARGET_UID"
|
||||
upsert_env_if_missing "HERMES_GID" "$TARGET_GID"
|
||||
ensure_env_value "API_SERVER_KEY" "$(generate_random_value 48)"
|
||||
ensure_env_value "API_SERVER_CORS_ORIGINS" "*"
|
||||
|
||||
CURRENT_UID=$(stat -c '%u' "$APP_DATA_DIR_ABS" 2>/dev/null || echo '')
|
||||
CURRENT_GID=$(stat -c '%g' "$APP_DATA_DIR_ABS" 2>/dev/null || echo '')
|
||||
|
||||
if [[ "$CURRENT_UID" != "$TARGET_UID" || "$CURRENT_GID" != "$TARGET_GID" ]]; then
|
||||
chown -R "$TARGET_UID:$TARGET_GID" "$APP_DATA_DIR_ABS" 2>/dev/null || true
|
||||
fi
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
CONTAINER_NAME=hermes-dashboard
|
||||
TZ=Asia/Shanghai
|
||||
APP_DATA_DIR=/opt/1panel/apps/local/hermes-agent/hermes-agent/data
|
||||
HERMES_UID=10000
|
||||
HERMES_GID=10000
|
||||
PANEL_APP_PORT_HTTP=9119
|
||||
GATEWAY_HEALTH_URL=http://172.18.0.240:8642
|
||||
DASHBOARD_USERNAME=admin
|
||||
DASHBOARD_PASSWORD=change_me_with_a_strong_password
|
||||
DASHBOARD_SESSION_SECRET=change_me_with_a_random_session_secret
|
||||
Executable
+169
@@ -0,0 +1,169 @@
|
||||
additionalProperties:
|
||||
formFields:
|
||||
- default: "Hermes Dashboard 是附属应用。请先部署 Hermes gateway,并把 APP_DATA_DIR 指向 Hermes Agent 的同一份数据目录,再把 GATEWAY_HEALTH_URL 填成它的完整地址。公开绑定现在强制启用认证,`--insecure` 已不能绕过认证;请设置 Dashboard 用户名、强密码和会话签名密钥。旧版本升级时,脚本只会为缺失或空白的认证项生成随机值,不会覆盖已有配置;升级完成后请在应用参数中设置自己知道的密码。"
|
||||
disabled: true
|
||||
envKey: TIPS
|
||||
labelEn: Tips
|
||||
labelZh: 提示
|
||||
label:
|
||||
en: Tips
|
||||
zh: 提示
|
||||
zh-Hant: 提示
|
||||
ja: ヒント
|
||||
ko: 팁
|
||||
ru: Подсказка
|
||||
ms: Petua
|
||||
pt-br: Dicas
|
||||
edit: true
|
||||
required: true
|
||||
type: text
|
||||
- default: Asia/Shanghai
|
||||
envKey: TZ
|
||||
labelEn: Timezone
|
||||
labelZh: 时区
|
||||
label:
|
||||
en: Timezone
|
||||
zh: 时区
|
||||
zh-Hant: 時區
|
||||
ja: タイムゾーン
|
||||
ko: 시간대
|
||||
ru: Часовой пояс
|
||||
ms: Zon waktu
|
||||
pt-br: Fuso horário
|
||||
edit: true
|
||||
required: true
|
||||
type: text
|
||||
- default: /opt/1panel/apps/local/hermes-agent/hermes-agent/data
|
||||
envKey: APP_DATA_DIR
|
||||
labelEn: Data Directory
|
||||
labelZh: 数据目录
|
||||
label:
|
||||
en: Data Directory
|
||||
zh: 数据目录
|
||||
zh-Hant: 資料目錄
|
||||
ja: データディレクトリ
|
||||
ko: 데이터 디렉터리
|
||||
ru: Каталог данных
|
||||
ms: Direktori data
|
||||
pt-br: Diretório de dados
|
||||
edit: true
|
||||
required: true
|
||||
type: text
|
||||
- default: 10000
|
||||
envKey: HERMES_UID
|
||||
labelEn: Hermes Runtime UID
|
||||
labelZh: Hermes 运行用户 UID
|
||||
label:
|
||||
en: Hermes Runtime UID
|
||||
zh: Hermes 运行用户 UID
|
||||
zh-Hant: Hermes 執行使用者 UID
|
||||
ja: Hermes 実行ユーザー UID
|
||||
ko: Hermes 실행 사용자 UID
|
||||
ru: UID пользователя Hermes
|
||||
ms: UID pengguna runtime Hermes
|
||||
pt-br: UID do usuário de execução do Hermes
|
||||
edit: true
|
||||
required: true
|
||||
type: number
|
||||
- default: 10000
|
||||
envKey: HERMES_GID
|
||||
labelEn: Hermes Runtime GID
|
||||
labelZh: Hermes 运行用户 GID
|
||||
label:
|
||||
en: Hermes Runtime GID
|
||||
zh: Hermes 运行用户 GID
|
||||
zh-Hant: Hermes 執行使用者 GID
|
||||
ja: Hermes 実行ユーザー GID
|
||||
ko: Hermes 실행 사용자 GID
|
||||
ru: GID пользователя Hermes
|
||||
ms: GID pengguna runtime Hermes
|
||||
pt-br: GID do usuário de execução do Hermes
|
||||
edit: true
|
||||
required: true
|
||||
type: number
|
||||
- default: 9119
|
||||
envKey: PANEL_APP_PORT_HTTP
|
||||
labelEn: Dashboard Port
|
||||
labelZh: Dashboard 端口
|
||||
label:
|
||||
en: Dashboard Port
|
||||
zh: Dashboard 端口
|
||||
zh-Hant: Dashboard 連接埠
|
||||
ja: Dashboard ポート
|
||||
ko: 대시보드 포트
|
||||
ru: Порт dashboard
|
||||
ms: Port dashboard
|
||||
pt-br: Porta do dashboard
|
||||
edit: true
|
||||
required: true
|
||||
rule: paramPort
|
||||
type: number
|
||||
- default: http://172.18.0.240:8642
|
||||
envKey: GATEWAY_HEALTH_URL
|
||||
labelEn: Gateway Health URL
|
||||
labelZh: 网关健康检查 URL
|
||||
label:
|
||||
en: Gateway Health URL
|
||||
zh: 网关健康检查 URL
|
||||
zh-Hant: 閘道健康檢查 URL
|
||||
ja: Gateway ヘルス URL
|
||||
ko: 게이트웨이 헬스 URL
|
||||
ru: URL проверки шлюза
|
||||
ms: URL kesihatan gateway
|
||||
pt-br: URL de verificação do gateway
|
||||
edit: true
|
||||
required: true
|
||||
rule: paramExtUrl
|
||||
type: text
|
||||
- default: admin
|
||||
envKey: DASHBOARD_USERNAME
|
||||
labelEn: Dashboard Username
|
||||
labelZh: Dashboard 用户名
|
||||
label:
|
||||
en: Dashboard Username
|
||||
zh: Dashboard 用户名
|
||||
zh-Hant: Dashboard 使用者名稱
|
||||
ja: Dashboard ユーザー名
|
||||
ko: Dashboard 사용자 이름
|
||||
ru: Имя пользователя dashboard
|
||||
ms: Nama pengguna dashboard
|
||||
pt-br: Usuário do dashboard
|
||||
edit: true
|
||||
required: true
|
||||
type: text
|
||||
- default: ""
|
||||
envKey: DASHBOARD_PASSWORD
|
||||
labelEn: Dashboard Password
|
||||
labelZh: Dashboard 密码
|
||||
label:
|
||||
en: Dashboard Password
|
||||
zh: Dashboard 密码
|
||||
zh-Hant: Dashboard 密碼
|
||||
ja: Dashboard パスワード
|
||||
ko: Dashboard 비밀번호
|
||||
ru: Пароль dashboard
|
||||
ms: Kata laluan dashboard
|
||||
pt-br: Senha do dashboard
|
||||
edit: true
|
||||
random: true
|
||||
required: true
|
||||
rule: paramComplexity
|
||||
type: password
|
||||
- default: ""
|
||||
envKey: DASHBOARD_SESSION_SECRET
|
||||
labelEn: Dashboard Session Secret
|
||||
labelZh: Dashboard 会话签名密钥
|
||||
label:
|
||||
en: Dashboard Session Secret
|
||||
zh: Dashboard 会话签名密钥
|
||||
zh-Hant: Dashboard 工作階段簽章密鑰
|
||||
ja: Dashboard セッション署名シークレット
|
||||
ko: Dashboard 세션 서명 비밀값
|
||||
ru: Секрет подписи сессии dashboard
|
||||
ms: Rahsia tandatangan sesi dashboard
|
||||
pt-br: Segredo de assinatura da sessão do dashboard
|
||||
edit: true
|
||||
random: true
|
||||
required: true
|
||||
rule: paramComplexity
|
||||
type: password
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
services:
|
||||
hermes-dashboard:
|
||||
image: "nousresearch/hermes-agent:v2026.9.24"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
command:
|
||||
- dashboard
|
||||
- --host
|
||||
- 0.0.0.0
|
||||
- --no-open
|
||||
environment:
|
||||
- TZ=${TZ}
|
||||
- HERMES_UID=${HERMES_UID:-10000}
|
||||
- HERMES_GID=${HERMES_GID:-10000}
|
||||
- GATEWAY_HEALTH_URL=${GATEWAY_HEALTH_URL}
|
||||
- HERMES_DASHBOARD_BASIC_AUTH_USERNAME=${DASHBOARD_USERNAME}
|
||||
- HERMES_DASHBOARD_BASIC_AUTH_PASSWORD=${DASHBOARD_PASSWORD}
|
||||
- HERMES_DASHBOARD_BASIC_AUTH_SECRET=${DASHBOARD_SESSION_SECRET}
|
||||
ports:
|
||||
- "${PANEL_APP_PORT_HTTP}:9119"
|
||||
volumes:
|
||||
- "${APP_DATA_DIR}:/opt/data"
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- 1panel-network
|
||||
labels:
|
||||
createdBy: "Apps"
|
||||
|
||||
networks:
|
||||
1panel-network:
|
||||
external: true
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
ENV_FILE="$ROOT_DIR/.env"
|
||||
APP_DATA_DIR_RAW="${APP_DATA_DIR:-./data}"
|
||||
DEFAULT_UID="10000"
|
||||
DEFAULT_GID="10000"
|
||||
TARGET_UID="${HERMES_UID:-$DEFAULT_UID}"
|
||||
TARGET_GID="${HERMES_GID:-$DEFAULT_GID}"
|
||||
|
||||
if [[ "$APP_DATA_DIR_RAW" = /* ]]; then
|
||||
APP_DATA_DIR_ABS="$APP_DATA_DIR_RAW"
|
||||
else
|
||||
APP_DATA_DIR_ABS="$ROOT_DIR/${APP_DATA_DIR_RAW#./}"
|
||||
fi
|
||||
|
||||
mkdir -p "$APP_DATA_DIR_ABS"
|
||||
|
||||
upsert_env_if_missing() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then
|
||||
printf '\n%s=%s\n' "$key" "$value" >> "$ENV_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
generate_random_value() {
|
||||
local length="$1"
|
||||
local value=""
|
||||
|
||||
while [[ ${#value} -lt $length ]]; do
|
||||
value+="$(LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c "$((length - ${#value}))" || true)"
|
||||
done
|
||||
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
ensure_env_value() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then
|
||||
printf '\n%s=%s\n' "$key" "$value" >> "$ENV_FILE"
|
||||
elif grep -Eq "^${key}=([[:space:]]*|\"\"|'')$" "$ENV_FILE"; then
|
||||
sed -i "s|^${key}=.*|${key}=${value}|" "$ENV_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
EXISTING_UID=$(grep '^HERMES_UID=' "$ENV_FILE" 2>/dev/null | tail -n 1 | cut -d '=' -f 2- || true)
|
||||
EXISTING_GID=$(grep '^HERMES_GID=' "$ENV_FILE" 2>/dev/null | tail -n 1 | cut -d '=' -f 2- || true)
|
||||
[[ -n "$EXISTING_UID" ]] && TARGET_UID="$EXISTING_UID"
|
||||
[[ -n "$EXISTING_GID" ]] && TARGET_GID="$EXISTING_GID"
|
||||
fi
|
||||
|
||||
upsert_env_if_missing "HERMES_UID" "$TARGET_UID"
|
||||
upsert_env_if_missing "HERMES_GID" "$TARGET_GID"
|
||||
ensure_env_value "DASHBOARD_USERNAME" "admin"
|
||||
ensure_env_value "DASHBOARD_PASSWORD" "$(generate_random_value 32)"
|
||||
ensure_env_value "DASHBOARD_SESSION_SECRET" "$(generate_random_value 48)"
|
||||
|
||||
CURRENT_UID=$(stat -c '%u' "$APP_DATA_DIR_ABS" 2>/dev/null || echo '')
|
||||
CURRENT_GID=$(stat -c '%g' "$APP_DATA_DIR_ABS" 2>/dev/null || echo '')
|
||||
|
||||
if [[ "$CURRENT_UID" != "$TARGET_UID" || "$CURRENT_GID" != "$TARGET_GID" ]]; then
|
||||
chown -R "$TARGET_UID:$TARGET_GID" "$APP_DATA_DIR_ABS" 2>/dev/null || true
|
||||
fi
|
||||
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
docker-compose down --volumes
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
ENV_FILE="$ROOT_DIR/.env"
|
||||
APP_DATA_DIR_RAW="${APP_DATA_DIR:-./data}"
|
||||
DEFAULT_UID="10000"
|
||||
DEFAULT_GID="10000"
|
||||
TARGET_UID="${HERMES_UID:-$DEFAULT_UID}"
|
||||
TARGET_GID="${HERMES_GID:-$DEFAULT_GID}"
|
||||
|
||||
if [[ "$APP_DATA_DIR_RAW" = /* ]]; then
|
||||
APP_DATA_DIR_ABS="$APP_DATA_DIR_RAW"
|
||||
else
|
||||
APP_DATA_DIR_ABS="$ROOT_DIR/${APP_DATA_DIR_RAW#./}"
|
||||
fi
|
||||
|
||||
mkdir -p "$APP_DATA_DIR_ABS"
|
||||
|
||||
upsert_env_if_missing() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then
|
||||
printf '\n%s=%s\n' "$key" "$value" >> "$ENV_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
generate_random_value() {
|
||||
local length="$1"
|
||||
local value=""
|
||||
|
||||
while [[ ${#value} -lt $length ]]; do
|
||||
value+="$(LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c "$((length - ${#value}))" || true)"
|
||||
done
|
||||
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
ensure_env_value() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then
|
||||
printf '\n%s=%s\n' "$key" "$value" >> "$ENV_FILE"
|
||||
elif grep -Eq "^${key}=([[:space:]]*|\"\"|'')$" "$ENV_FILE"; then
|
||||
sed -i "s|^${key}=.*|${key}=${value}|" "$ENV_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
EXISTING_UID=$(grep '^HERMES_UID=' "$ENV_FILE" 2>/dev/null | tail -n 1 | cut -d '=' -f 2- || true)
|
||||
EXISTING_GID=$(grep '^HERMES_GID=' "$ENV_FILE" 2>/dev/null | tail -n 1 | cut -d '=' -f 2- || true)
|
||||
[[ -n "$EXISTING_UID" ]] && TARGET_UID="$EXISTING_UID"
|
||||
[[ -n "$EXISTING_GID" ]] && TARGET_GID="$EXISTING_GID"
|
||||
fi
|
||||
|
||||
upsert_env_if_missing "HERMES_UID" "$TARGET_UID"
|
||||
upsert_env_if_missing "HERMES_GID" "$TARGET_GID"
|
||||
ensure_env_value "DASHBOARD_USERNAME" "admin"
|
||||
ensure_env_value "DASHBOARD_PASSWORD" "$(generate_random_value 32)"
|
||||
ensure_env_value "DASHBOARD_SESSION_SECRET" "$(generate_random_value 48)"
|
||||
|
||||
CURRENT_UID=$(stat -c '%u' "$APP_DATA_DIR_ABS" 2>/dev/null || echo '')
|
||||
CURRENT_GID=$(stat -c '%g' "$APP_DATA_DIR_ABS" 2>/dev/null || echo '')
|
||||
|
||||
if [[ "$CURRENT_UID" != "$TARGET_UID" || "$CURRENT_GID" != "$TARGET_GID" ]]; then
|
||||
chown -R "$TARGET_UID:$TARGET_GID" "$APP_DATA_DIR_ABS" 2>/dev/null || true
|
||||
fi
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
iptv-api:
|
||||
image: "guovern/iptv-api:3.0.0"
|
||||
image: "guovern/iptv-api:3.1.0"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
tty: true
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
docker-compose down --volumes
|
||||
+251
@@ -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
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
mt-photos:
|
||||
image: "mtphotos/mt-photos:1.57.1"
|
||||
image: "mtphotos/mt-photos:1.57.2"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
networks:
|
||||
|
||||
@@ -3,7 +3,7 @@ networks:
|
||||
external: true
|
||||
services:
|
||||
nzbget:
|
||||
image: "linuxserver/nzbget:26.3.20261002"
|
||||
image: "linuxserver/nzbget:26.3.20261009"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
networks:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
odoo:
|
||||
image: "odoo:19.0"
|
||||
image: "odoo:20.0"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
user: root
|
||||
restart: always
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
piclist:
|
||||
image: "kuingsmile/piclist:v2.4.2"
|
||||
image: "kuingsmile/piclist:v3.0.0"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
networks:
|
||||
|
||||
@@ -3,7 +3,7 @@ networks:
|
||||
external: true
|
||||
services:
|
||||
qdirstat:
|
||||
image: "linuxserver/qdirstat:2.0.20260928"
|
||||
image: "linuxserver/qdirstat:2.0.20261012"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
networks:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
rocketchat:
|
||||
image: "registry.rocket.chat/rocketchat/rocket.chat:8.8.1"
|
||||
image: "registry.rocket.chat/rocketchat/rocket.chat:8.9.0"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
depends_on:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
rocketchat:
|
||||
image: "registry.rocket.chat/rocketchat/rocket.chat:8.8.1"
|
||||
image: "registry.rocket.chat/rocketchat/rocket.chat:8.9.0"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
depends_on:
|
||||
|
||||
@@ -3,7 +3,7 @@ networks:
|
||||
external: true
|
||||
services:
|
||||
ungoogled-chromium:
|
||||
image: "linuxserver/ungoogled-chromium:154.0.8037"
|
||||
image: "linuxserver/ungoogled-chromium:155.0.8059"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
networks:
|
||||
|
||||
@@ -3,7 +3,7 @@ networks:
|
||||
external: true
|
||||
services:
|
||||
vlc:
|
||||
image: "linuxserver/vlc:3.0.23"
|
||||
image: "linuxserver/vlc:3.0.24"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
networks:
|
||||
|
||||
@@ -3,7 +3,7 @@ networks:
|
||||
external: true
|
||||
services:
|
||||
zen:
|
||||
image: "linuxserver/zen:1.23.1"
|
||||
image: "linuxserver/zen:1.23.2"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: always
|
||||
networks:
|
||||
|
||||
Reference in New Issue
Block a user