feat: add shared PostgreSQL fnOS service and refresh UI
This commit is contained in:
@@ -3,7 +3,8 @@ set -eu
|
||||
|
||||
PASSWORD="${wizard_admin_password:-}"
|
||||
PASSWORD_CONFIRM="${wizard_admin_password_confirm:-}"
|
||||
unset wizard_admin_password wizard_admin_password_confirm
|
||||
POSTGRES_ENROLLMENT_TOKEN="${wizard_postgres_enrollment_token:-}"
|
||||
unset wizard_admin_password wizard_admin_password_confirm wizard_postgres_enrollment_token
|
||||
|
||||
fail() {
|
||||
printf '%s\n' "$1" >&2
|
||||
@@ -19,10 +20,16 @@ fail() {
|
||||
case "$PASSWORD" in
|
||||
*$'\n'*|*$'\r'*) fail "管理员密码不能包含换行符。" ;;
|
||||
esac
|
||||
[ "${#POSTGRES_ENROLLMENT_TOKEN}" -ge 20 ] || fail "PostgreSQL 应用接入令牌至少需要 20 个字符。"
|
||||
[ "${#POSTGRES_ENROLLMENT_TOKEN}" -le 256 ] || fail "PostgreSQL 应用接入令牌不能超过 256 个字符。"
|
||||
case "$POSTGRES_ENROLLMENT_TOKEN" in
|
||||
*$'\n'*|*$'\r'*) fail "PostgreSQL 应用接入令牌不能包含换行符。" ;;
|
||||
esac
|
||||
|
||||
mkdir -p "${TRIM_PKGVAR}/run" "${TRIM_PKGVAR}/log"
|
||||
chmod 0700 "${TRIM_PKGVAR}" "${TRIM_PKGVAR}/run" 2>/dev/null || true
|
||||
umask 077
|
||||
printf '%s\n' "$PASSWORD" >"${TRIM_PKGVAR}/admin-password.seed"
|
||||
unset PASSWORD PASSWORD_CONFIRM
|
||||
printf '%s\n' "$POSTGRES_ENROLLMENT_TOKEN" >"${TRIM_PKGVAR}/postgres-enrollment-token.seed"
|
||||
unset PASSWORD PASSWORD_CONFIRM POSTGRES_ENROLLMENT_TOKEN
|
||||
exit 0
|
||||
|
||||
+185
-3
@@ -21,11 +21,22 @@ APP_PID_FILE="$RUN_ROOT/liverecorder.pid"
|
||||
APP_LOG="$LOG_ROOT/liverecorder.log"
|
||||
PG_LOG="$LOG_ROOT/postgresql.log"
|
||||
ADMIN_PASSWORD_FILE="$DATA_ROOT/admin-password.seed"
|
||||
POSTGRES_ENROLLMENT_TOKEN_FILE="$DATA_ROOT/postgres-enrollment-token.seed"
|
||||
POSTGRES_CREDENTIALS_FILE="$DATA_ROOT/postgres-client.conf"
|
||||
POSTGRES_MIGRATION_ROOT="$DATA_ROOT/postgres-migration"
|
||||
POSTGRES_MIGRATION_MARKER="$POSTGRES_MIGRATION_ROOT/shared-database.active"
|
||||
PG_PORT="${LIVE_RECORDER_POSTGRES_PORT:-54329}"
|
||||
SERVICE_PORT="${TRIM_SERVICE_PORT:-18080}"
|
||||
POSTGRES_SERVICE_API="${POSTGRES_SERVICE_API:-http://127.0.0.1:15433}"
|
||||
SYSTEM_PATH="${PATH:-/usr/local/bin:/usr/bin:/bin}"
|
||||
RUNTIME_PATH="$RUNTIME_ROOT/bin:$SYSTEM_PATH"
|
||||
RUNTIME_LIBRARY_PATH="$RUNTIME_ROOT/lib:$PG_LIB"
|
||||
DATABASE_CONNECTION_STRING=""
|
||||
SHARED_DB_HOST=""
|
||||
SHARED_DB_PORT=""
|
||||
SHARED_DB_NAME=""
|
||||
SHARED_DB_USER=""
|
||||
SHARED_DB_PASSWORD=""
|
||||
|
||||
log_message() {
|
||||
mkdir -p "$LOG_ROOT"
|
||||
@@ -52,6 +63,177 @@ run_native() {
|
||||
SSL_CERT_FILE="$CA_BUNDLE" CURL_CA_BUNDLE="$CA_BUNDLE" "$@"
|
||||
}
|
||||
|
||||
read_credential_field() {
|
||||
key=$1
|
||||
sed -n "s/^${key}=//p" "$POSTGRES_CREDENTIALS_FILE" | sed -n '1p'
|
||||
}
|
||||
|
||||
configure_shared_credentials() {
|
||||
[ -s "$POSTGRES_CREDENTIALS_FILE" ] || return 1
|
||||
SHARED_DB_HOST=$(read_credential_field host)
|
||||
SHARED_DB_PORT=$(read_credential_field port)
|
||||
SHARED_DB_NAME=$(read_credential_field database)
|
||||
SHARED_DB_USER=$(read_credential_field username)
|
||||
SHARED_DB_PASSWORD=$(read_credential_field password)
|
||||
[ "$SHARED_DB_HOST" = "127.0.0.1" ] || return 1
|
||||
case "$SHARED_DB_PORT" in ''|*[!0-9]*) return 1 ;; esac
|
||||
case "$SHARED_DB_NAME$SHARED_DB_USER" in *[!a-z0-9_]*) return 1 ;; esac
|
||||
[ -n "$SHARED_DB_PASSWORD" ] || return 1
|
||||
DATABASE_CONNECTION_STRING="Host=$SHARED_DB_HOST;Port=$SHARED_DB_PORT;Database=$SHARED_DB_NAME;Username=$SHARED_DB_USER;Password=$SHARED_DB_PASSWORD;SSL Mode=Disable;Timeout=15;Command Timeout=120;Keepalive=30"
|
||||
}
|
||||
|
||||
enroll_shared_database() {
|
||||
[ -s "$POSTGRES_ENROLLMENT_TOKEN_FILE" ] || {
|
||||
log_message "缺少 PostgreSQL 共享服务接入令牌。请在升级或安装向导中重新填写。"
|
||||
return 1
|
||||
}
|
||||
token=$(sed -n '1p' "$POSTGRES_ENROLLMENT_TOKEN_FILE")
|
||||
[ -n "$token" ] || return 1
|
||||
response_file="$DATA_ROOT/postgres-enrollment-response.tmp"
|
||||
rm -f "$response_file"
|
||||
attempt=0
|
||||
while [ "$attempt" -lt 60 ]; do
|
||||
if run_native "$CURL_BIN" \
|
||||
--fail --silent --show-error \
|
||||
--connect-timeout 3 --max-time 10 \
|
||||
-H "Authorization: Bearer $token" \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data '{"appId":"liverecorder","displayName":"Live Recorder","requestedExtensions":[]}' \
|
||||
"$POSTGRES_SERVICE_API/internal/v1/enroll" >"$response_file" 2>>"$APP_LOG"; then
|
||||
break
|
||||
fi
|
||||
attempt=$((attempt + 1))
|
||||
sleep 2
|
||||
done
|
||||
unset token
|
||||
[ -s "$response_file" ] || {
|
||||
log_message "无法从 PostgreSQL 共享服务取得数据库凭据。"
|
||||
rm -f "$response_file"
|
||||
return 1
|
||||
}
|
||||
|
||||
host=$(sed -n 's/.*"host":"\([^"]*\)".*/\1/p' "$response_file")
|
||||
port=$(sed -n 's/.*"port":\([0-9][0-9]*\).*/\1/p' "$response_file")
|
||||
database=$(sed -n 's/.*"database":"\([^"]*\)".*/\1/p' "$response_file")
|
||||
username=$(sed -n 's/.*"username":"\([^"]*\)".*/\1/p' "$response_file")
|
||||
password=$(sed -n 's/.*"password":"\([^"]*\)".*/\1/p' "$response_file")
|
||||
[ "$host" = "127.0.0.1" ] && [ -n "$port" ] && [ -n "$database" ] && [ -n "$username" ] && [ -n "$password" ] || {
|
||||
log_message "PostgreSQL 共享服务返回了无效凭据。"
|
||||
rm -f "$response_file"
|
||||
return 1
|
||||
}
|
||||
umask 077
|
||||
{
|
||||
printf 'host=%s\n' "$host"
|
||||
printf 'port=%s\n' "$port"
|
||||
printf 'database=%s\n' "$database"
|
||||
printf 'username=%s\n' "$username"
|
||||
printf 'password=%s\n' "$password"
|
||||
} >"$POSTGRES_CREDENTIALS_FILE.tmp"
|
||||
mv "$POSTGRES_CREDENTIALS_FILE.tmp" "$POSTGRES_CREDENTIALS_FILE"
|
||||
chmod 0600 "$POSTGRES_CREDENTIALS_FILE"
|
||||
rm -f "$POSTGRES_ENROLLMENT_TOKEN_FILE" "$response_file"
|
||||
configure_shared_credentials
|
||||
}
|
||||
|
||||
ensure_shared_credentials() {
|
||||
if configure_shared_credentials; then
|
||||
return 0
|
||||
fi
|
||||
rm -f "$POSTGRES_CREDENTIALS_FILE"
|
||||
enroll_shared_database
|
||||
}
|
||||
|
||||
run_shared_pg() {
|
||||
env LD_LIBRARY_PATH="$RUNTIME_LIBRARY_PATH" PATH="$PG_BIN:$RUNTIME_PATH" \
|
||||
PGPASSWORD="$SHARED_DB_PASSWORD" "$@"
|
||||
}
|
||||
|
||||
collect_database_counts() {
|
||||
mode=$1
|
||||
destination=$2
|
||||
: >"$destination"
|
||||
for table_name in AppSettings CleanupOperations LiveRooms RecordResults RecordSessions RecordTasks RecordUploadJobs SystemLogEntries UserAccounts UserSessions; do
|
||||
if [ "$mode" = "private" ]; then
|
||||
count=$(run_pg "$PG_BIN/psql" -h "$RUN_ROOT" -p "$PG_PORT" -U liverecorder -d live_recorder -Atqc \
|
||||
"SELECT count(*) FROM \"$table_name\"" 2>/dev/null) || return 1
|
||||
else
|
||||
count=$(run_shared_pg "$PG_BIN/psql" -h "$SHARED_DB_HOST" -p "$SHARED_DB_PORT" -U "$SHARED_DB_USER" -d "$SHARED_DB_NAME" -Atqc \
|
||||
"SELECT count(*) FROM \"$table_name\"" 2>/dev/null) || return 1
|
||||
fi
|
||||
printf '%s=%s\n' "$table_name" "$count" >>"$destination"
|
||||
done
|
||||
}
|
||||
|
||||
migrate_private_postgres() {
|
||||
mkdir -p "$POSTGRES_MIGRATION_ROOT"
|
||||
chmod 0700 "$POSTGRES_MIGRATION_ROOT"
|
||||
dump_file="$POSTGRES_MIGRATION_ROOT/private-postgres-15.dump"
|
||||
source_counts="$POSTGRES_MIGRATION_ROOT/source-counts.txt"
|
||||
target_counts="$POSTGRES_MIGRATION_ROOT/target-counts.txt"
|
||||
start_postgres || return 1
|
||||
|
||||
log_message "开始导出原内置 PostgreSQL 数据库。"
|
||||
run_pg "$PG_BIN/pg_dump" \
|
||||
-h "$RUN_ROOT" -p "$PG_PORT" -U liverecorder -d live_recorder \
|
||||
--format=custom --no-owner --no-acl --file "$dump_file.tmp" >>"$PG_LOG" 2>&1 || return 1
|
||||
mv "$dump_file.tmp" "$dump_file"
|
||||
sha256sum "$dump_file" >"$dump_file.sha256"
|
||||
collect_database_counts private "$source_counts" || return 1
|
||||
|
||||
log_message "开始恢复数据到 PostgreSQL 共享服务。"
|
||||
run_shared_pg "$PG_BIN/psql" \
|
||||
-h "$SHARED_DB_HOST" -p "$SHARED_DB_PORT" -U "$SHARED_DB_USER" -d "$SHARED_DB_NAME" \
|
||||
-v ON_ERROR_STOP=1 -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public AUTHORIZATION \"$SHARED_DB_USER\"" >>"$PG_LOG" 2>&1 || return 1
|
||||
run_shared_pg "$PG_BIN/pg_restore" \
|
||||
-h "$SHARED_DB_HOST" -p "$SHARED_DB_PORT" -U "$SHARED_DB_USER" -d "$SHARED_DB_NAME" \
|
||||
--no-owner --no-acl --exit-on-error "$dump_file" >>"$PG_LOG" 2>&1 || return 1
|
||||
collect_database_counts shared "$target_counts" || return 1
|
||||
if ! cmp -s "$source_counts" "$target_counts"; then
|
||||
log_message "共享数据库行数校验失败,继续使用原数据库。"
|
||||
return 1
|
||||
fi
|
||||
|
||||
umask 077
|
||||
{
|
||||
printf 'migrated_at=%s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
|
||||
printf 'database=%s\n' "$SHARED_DB_NAME"
|
||||
printf 'dump=%s\n' "$dump_file"
|
||||
} >"$POSTGRES_MIGRATION_MARKER"
|
||||
stop_postgres || return 1
|
||||
log_message "内置 PostgreSQL 已迁移到共享服务,旧数据和迁移转储已保留。"
|
||||
}
|
||||
|
||||
select_database_connection() {
|
||||
if ! ensure_shared_credentials; then
|
||||
if [ -f "$POSTGRES_MIGRATION_MARKER" ]; then
|
||||
log_message "已完成共享数据库迁移,但当前凭据不可用;为避免使用过期旧库,应用不会启动。"
|
||||
return 1
|
||||
fi
|
||||
if [ -f "$PG_DATA/PG_VERSION" ]; then
|
||||
log_message "共享服务暂不可用,本次启动继续使用原内置 PostgreSQL。"
|
||||
start_postgres || return 1
|
||||
DATABASE_CONNECTION_STRING="Host=$RUN_ROOT;Port=$PG_PORT;Database=live_recorder;Username=liverecorder;Timeout=15;Command Timeout=120;Keepalive=30"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -f "$PG_DATA/PG_VERSION" ] && [ ! -f "$POSTGRES_MIGRATION_MARKER" ]; then
|
||||
if ! migrate_private_postgres; then
|
||||
log_message "共享数据库迁移失败,本次启动继续使用原内置 PostgreSQL;下次启动会重试迁移。"
|
||||
start_postgres || return 1
|
||||
DATABASE_CONNECTION_STRING="Host=$RUN_ROOT;Port=$PG_PORT;Database=live_recorder;Username=liverecorder;Timeout=15;Command Timeout=120;Keepalive=30"
|
||||
return 0
|
||||
fi
|
||||
elif [ ! -f "$PG_DATA/PG_VERSION" ] && [ ! -f "$POSTGRES_MIGRATION_MARKER" ]; then
|
||||
mkdir -p "$POSTGRES_MIGRATION_ROOT"
|
||||
umask 077
|
||||
printf 'fresh_install_at=%s\ndatabase=%s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$SHARED_DB_NAME" >"$POSTGRES_MIGRATION_MARKER"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
system_media_tools_available() {
|
||||
missing_tools=""
|
||||
for tool_name in ffmpeg ffprobe; do
|
||||
@@ -142,7 +324,7 @@ launch_app_process() {
|
||||
(
|
||||
export ASPNETCORE_ENVIRONMENT=Production
|
||||
export ASPNETCORE_URLS="http://0.0.0.0:$SERVICE_PORT"
|
||||
export ConnectionStrings__DefaultConnection="Host=$RUN_ROOT;Port=$PG_PORT;Database=live_recorder;Username=liverecorder;Timeout=15;Command Timeout=120;Keepalive=30"
|
||||
export ConnectionStrings__DefaultConnection="$DATABASE_CONNECTION_STRING"
|
||||
export LIVE_RECORDER_DEFAULT_OUTPUT_ROOT="$RECORD_ROOT"
|
||||
if [ -f "$ADMIN_PASSWORD_FILE" ]; then
|
||||
LIVE_RECORDER_DEFAULT_ADMIN_PASSWORD=$(sed -n '1p' "$ADMIN_PASSWORD_FILE")
|
||||
@@ -183,7 +365,7 @@ start_app() {
|
||||
fi
|
||||
|
||||
rm -f "$APP_PID_FILE"
|
||||
start_postgres || return 1
|
||||
select_database_connection || return 1
|
||||
launch_attempt=1
|
||||
launch_app_process
|
||||
pid=$APP_PROCESS_PID
|
||||
@@ -235,7 +417,7 @@ case "${1:-status}" in
|
||||
stop) stop_app ;;
|
||||
restart) stop_app && start_app ;;
|
||||
status)
|
||||
if app_pid >/dev/null && postgres_running; then
|
||||
if app_pid >/dev/null; then
|
||||
exit 0
|
||||
fi
|
||||
exit 3
|
||||
|
||||
@@ -1,2 +1,15 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
TOKEN="${wizard_postgres_enrollment_token:-}"
|
||||
unset wizard_postgres_enrollment_token
|
||||
if [ -n "$TOKEN" ]; then
|
||||
[ "${#TOKEN}" -ge 20 ] || { printf '%s\n' "PostgreSQL 应用接入令牌至少需要 20 个字符。" >&2; exit 1; }
|
||||
case "$TOKEN" in
|
||||
*$'\n'*|*$'\r'*) printf '%s\n' "PostgreSQL 应用接入令牌不能包含换行符。" >&2; exit 1 ;;
|
||||
esac
|
||||
umask 077
|
||||
printf '%s\n' "$TOKEN" >"${TRIM_PKGVAR}/postgres-enrollment-token.seed"
|
||||
fi
|
||||
unset TOKEN
|
||||
exit 0
|
||||
|
||||
+3
-2
@@ -1,7 +1,7 @@
|
||||
appname=liverecorder
|
||||
version=1.0.1
|
||||
version=1.1.0
|
||||
display_name=Live Recorder
|
||||
desc=原生直播录制系统,离线内置 PostgreSQL、Node.js 和 Web 管理界面,支持分片录制、弹幕采集与 OpenList 自动上传
|
||||
desc=原生直播录制系统,使用独立 PostgreSQL 共享服务,支持分片录制、弹幕采集与 OpenList 自动上传
|
||||
platform=x86
|
||||
source=thirdparty
|
||||
maintainer=Live Recorder Contributors
|
||||
@@ -10,3 +10,4 @@ desktop_uidir=ui
|
||||
desktop_applaunchname=liverecorder.Application
|
||||
checkport=true
|
||||
ctl_stop=true
|
||||
install_dep_apps=nxsir.postgresql
|
||||
|
||||
@@ -43,6 +43,26 @@
|
||||
"message": "管理员密码不能超过 256 个字符"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "password",
|
||||
"field": "wizard_postgres_enrollment_token",
|
||||
"label": "PostgreSQL 应用接入令牌",
|
||||
"helpText": "填写安装 PostgreSQL 共享服务时设置的应用接入令牌。令牌只用于首次签发独立数据库凭据。",
|
||||
"rules": [
|
||||
{
|
||||
"required": true,
|
||||
"message": "请输入 PostgreSQL 应用接入令牌"
|
||||
},
|
||||
{
|
||||
"min": 20,
|
||||
"message": "接入令牌至少需要 20 个字符"
|
||||
},
|
||||
{
|
||||
"max": 256,
|
||||
"message": "接入令牌不能超过 256 个字符"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+13
-1
@@ -4,7 +4,19 @@
|
||||
"items": [
|
||||
{
|
||||
"type": "tips",
|
||||
"helpText": "升级会保留 PostgreSQL 数据库、系统设置、上传任务和录制文件。"
|
||||
"helpText": "本次升级会自动把原内置 PostgreSQL 数据迁移到共享服务。迁移成功前仍可回退到原数据库,旧数据不会自动删除。"
|
||||
},
|
||||
{
|
||||
"type": "password",
|
||||
"field": "wizard_postgres_enrollment_token",
|
||||
"label": "PostgreSQL 应用接入令牌",
|
||||
"helpText": "首次迁移需要填写共享服务的应用接入令牌;已经完成迁移的后续升级可以留空。",
|
||||
"rules": [
|
||||
{
|
||||
"max": 256,
|
||||
"message": "接入令牌不能超过 256 个字符"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user