release: sync public installer asset install.sh
This commit is contained in:
+96
-7
@@ -21,6 +21,9 @@ PG_HOST_CLI=0
|
||||
API_PORT=${XYMEDIA_API_PORT:-18080}
|
||||
WEBDAV_PORT=${XYMEDIA_WEBDAV_PORT:-18081}
|
||||
TVBOX_PORT=${XYMEDIA_TVBOX_PORT:-18082}
|
||||
XIAOYA_WEB_PORT=${XYMEDIA_XIAOYA_PORT:-5678}
|
||||
XIAOYA_ADMIN_PORT=${XYMEDIA_XIAOYA_ADMIN_PORT:-2345}
|
||||
XIAOYA_PROXY_PORT=${XYMEDIA_XIAOYA_PROXY_PORT:-2346}
|
||||
TOTAL_STEPS=9
|
||||
STEP=0
|
||||
INSTALL_LOG=
|
||||
@@ -34,7 +37,9 @@ XIAOYA_DIR=${XYMEDIA_XIAOYA_DIR:-}
|
||||
XIAOYA_CONTAINER_EXPLICIT=${XYMEDIA_XIAOYA_CONTAINER:+1}
|
||||
XIAOYA_CONTAINER=${XYMEDIA_XIAOYA_CONTAINER:-xymedia-xiaoya}
|
||||
OPTION2_XIAOYA_PENDING=0
|
||||
OPTION2_XIAOYA_CHOICE=
|
||||
XIAOYA_FOUND=0
|
||||
XIAOYA_STANDALONE=0
|
||||
CONTROLLER_ONLY=0
|
||||
MENU_PROFILES=
|
||||
CONTROLLER_URL=${XYMEDIA_XIAOYA_CONTROLLER_URL:-}
|
||||
@@ -512,6 +517,52 @@ ensure_xiaoya_dir() {
|
||||
fi
|
||||
mkdir -p "$XIAOYA_DIR" "$INSTALL_DIR/secrets"
|
||||
}
|
||||
validate_xiaoya_ports() {
|
||||
local port
|
||||
for port in "$XIAOYA_WEB_PORT" "$XIAOYA_ADMIN_PORT" "$XIAOYA_PROXY_PORT"; do
|
||||
[[ $port =~ ^[0-9]+$ && $port -ge 1 && $port -le 65535 ]] || die '小雅主机端口必须是 1 到 65535 之间的数字'
|
||||
done
|
||||
[[ $XIAOYA_WEB_PORT != "$XIAOYA_ADMIN_PORT" && $XIAOYA_WEB_PORT != "$XIAOYA_PROXY_PORT" && $XIAOYA_ADMIN_PORT != "$XIAOYA_PROXY_PORT" ]] || die '小雅三个主机端口不能重复'
|
||||
for port in "$XIAOYA_WEB_PORT" "$XIAOYA_ADMIN_PORT" "$XIAOYA_PROXY_PORT"; do
|
||||
if ! python3 - "$port" <<'PY'
|
||||
import socket, sys
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
sock.bind(('0.0.0.0', int(sys.argv[1])))
|
||||
except OSError:
|
||||
raise SystemExit(1)
|
||||
finally:
|
||||
sock.close()
|
||||
PY
|
||||
then die "小雅主机端口已被占用:$port"; fi
|
||||
done
|
||||
}
|
||||
next_free_xiaoya_port() {
|
||||
XIAOYA_PORT_START=$1 XIAOYA_PORT_USED=${2:-} python3 - <<'PY'
|
||||
import os, socket
|
||||
port = int(os.environ['XIAOYA_PORT_START'])
|
||||
used = set(filter(None, os.environ.get('XIAOYA_PORT_USED', '').split(',')))
|
||||
while port <= 65535:
|
||||
if str(port) not in used:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
sock.bind(('0.0.0.0', port)); print(port); break
|
||||
except OSError: pass
|
||||
finally: sock.close()
|
||||
port += 1
|
||||
else: raise SystemExit(1)
|
||||
PY
|
||||
}
|
||||
select_xiaoya_ports() {
|
||||
local default_port choice used=
|
||||
default_port=$(next_free_xiaoya_port 5678) || die '无法选择小雅 Web 主机端口'
|
||||
prompt_tty_into choice "小雅 Web 主机端口 [$default_port]:"; XIAOYA_WEB_PORT=${choice:-$default_port}; used=$XIAOYA_WEB_PORT
|
||||
default_port=$(next_free_xiaoya_port 2345 "$used") || die '无法选择小雅 2345 主机端口'
|
||||
prompt_tty_into choice "小雅 2345 主机端口 [$default_port]:"; XIAOYA_ADMIN_PORT=${choice:-$default_port}; used="$XIAOYA_WEB_PORT,$XIAOYA_ADMIN_PORT"
|
||||
default_port=$(next_free_xiaoya_port 2346 "$used") || die '无法选择小雅 2346 主机端口'
|
||||
prompt_tty_into choice "小雅 2346 主机端口 [$default_port]:"; XIAOYA_PROXY_PORT=${choice:-$default_port}
|
||||
validate_xiaoya_ports
|
||||
}
|
||||
write_controller_token() {
|
||||
local file; mkdir -p "$INSTALL_DIR/secrets"; file=$(controller_token_file)
|
||||
if [[ ! -s $file ]]; then umask 077; if command -v openssl >/dev/null; then openssl rand -hex 32 >"$file"; else od -An -N32 -tx1 /dev/urandom | tr -d ' \n' >"$file"; fi; chmod 600 "$file"; fi
|
||||
@@ -524,7 +575,7 @@ menu() {
|
||||
read_tty choice || die '无法读取终端输入'
|
||||
case "$choice" in
|
||||
1) return 0;;
|
||||
2) ensure_xiaoya_dir; printf '小雅目录已准备:%s;安装器将在 Docker 检查后检测并复用已有小雅。\n' "$XIAOYA_DIR" >>$TTY_OUT; write_controller_token; CONTROLLER_URL='http://xymedia-controller:19090'; OPTION2_XIAOYA_PENDING=1; return 0;;
|
||||
2) write_controller_token; CONTROLLER_URL='http://xymedia-controller:19090'; OPTION2_XIAOYA_PENDING=1; return 0;;
|
||||
3) local url name key; url=$(prompt_tty '控制器地址:'); key=$(prompt_tty '控制器密钥:'); name=$(prompt_tty '显示名称:'); [[ $url == https://* || ${ALLOW_HTTP:-0} == 1 ]] || die '远程连接默认要求 HTTPS;设置 ALLOW_HTTP=1 后才允许 HTTP'; curl --fail --silent --show-error --location --max-time 10 -H "X-XyMedia-Controller-Token: $key" "${url%/}/health" >/dev/null || die '控制器健康检查失败,未保存连接'; REMOTE_MODE=1; REMOTE_TOKEN=$key; CONTROLLER_URL="${url%/}"; printf '远程连接 %s (%s) 校验成功,配置已保存;控制器密钥不会显示。\n' "$name" "$CONTROLLER_URL" >>$TTY_OUT; return 0;;
|
||||
4) ensure_xiaoya_dir 1; if [[ ${XYMEDIA_INSTALL_TEST_DISCOVERY_ONLY:-0} != 1 && -z $CONTROLLER_BIND_EXPLICIT ]]; then prompt_tty_into CONTROLLER_BIND_ADDRESS "控制器监听地址 [${CONTROLLER_BIND_ADDRESS}]: "; CONTROLLER_BIND_ADDRESS=${CONTROLLER_BIND_ADDRESS:-0.0.0.0}; fi; if [[ ${XYMEDIA_INSTALL_TEST_DISCOVERY_ONLY:-0} != 1 && -z $CONTROLLER_PORT_EXPLICIT ]]; then prompt_tty_into CONTROLLER_PORT "控制器端口 [${CONTROLLER_PORT}]: "; CONTROLLER_PORT=${CONTROLLER_PORT:-19090}; fi; write_controller_token; CONTROLLER_ONLY=1; printf '小雅控制器安装目录已准备。\n' >>$TTY_OUT; MENU_PROFILES='xiaoya-control'; return 0;;
|
||||
5) while :; do
|
||||
@@ -554,6 +605,9 @@ if [[ -f "$INSTALL_DIR/.env" ]]; then
|
||||
XIAOYA_CONTAINER_EXPLICIT=1
|
||||
fi
|
||||
fi
|
||||
if [[ -z ${XYMEDIA_XIAOYA_PORT:-} ]]; then value=$(read_install_env XYMEDIA_XIAOYA_PORT); [[ -z $value ]] || XIAOYA_WEB_PORT=$value; fi
|
||||
if [[ -z ${XYMEDIA_XIAOYA_ADMIN_PORT:-} ]]; then value=$(read_install_env XYMEDIA_XIAOYA_ADMIN_PORT); [[ -z $value ]] || XIAOYA_ADMIN_PORT=$value; fi
|
||||
if [[ -z ${XYMEDIA_XIAOYA_PROXY_PORT:-} ]]; then value=$(read_install_env XYMEDIA_XIAOYA_PROXY_PORT); [[ -z $value ]] || XIAOYA_PROXY_PORT=$value; fi
|
||||
fi
|
||||
menu "$@"
|
||||
validate_controller_settings() {
|
||||
@@ -662,6 +716,8 @@ except (ValueError, IndexError, TypeError, AttributeError):
|
||||
if ((${#ordered[@]} == 0)); then
|
||||
if (( OPTION2_XIAOYA_PENDING )); then
|
||||
XIAOYA_FOUND=0
|
||||
OPTION2_XIAOYA_CHOICE=standalone
|
||||
XIAOYA_STANDALONE=1
|
||||
XIAOYA_CONTAINER=xymedia-xiaoya
|
||||
return 0
|
||||
fi
|
||||
@@ -672,9 +728,21 @@ except (ValueError, IndexError, TypeError, AttributeError):
|
||||
XIAOYA_FOUND=1
|
||||
printf '已确认手动指定的小雅容器:%s\n' "$XIAOYA_CONTAINER" >>$TTY_OUT
|
||||
elif ((${#ordered[@]} == 1)); then
|
||||
IFS=$'\t' read -r XIAOYA_CONTAINER image state <<<"${ordered[0]}"
|
||||
printf '自动发现小雅容器:%s(镜像:%s,状态:%s)\n' "$XIAOYA_CONTAINER" "$image" "$state" >>$TTY_OUT
|
||||
IFS=$'\t' read -r selected image state <<<"${ordered[0]}"
|
||||
if (( OPTION2_XIAOYA_PENDING )); then
|
||||
printf '发现已有小雅容器:%s(镜像:%s,状态:%s)\n' "$selected" "$image" "$state" >>$TTY_OUT
|
||||
prompt_tty_into choice '请选择:1) 复用该容器 2) 安装独立小雅 0) 取消:'
|
||||
case "$choice" in
|
||||
1) XIAOYA_CONTAINER=$selected; XIAOYA_FOUND=1; OPTION2_XIAOYA_CHOICE=reuse;;
|
||||
2) XIAOYA_CONTAINER=xymedia-xiaoya; XIAOYA_FOUND=0; XIAOYA_STANDALONE=1; OPTION2_XIAOYA_CHOICE=standalone; return 0;;
|
||||
0) exit 0;;
|
||||
*) die '无效选择,请输入 1、2 或 0';;
|
||||
esac
|
||||
else
|
||||
XIAOYA_CONTAINER=$selected
|
||||
XIAOYA_FOUND=1
|
||||
printf '自动发现小雅容器:%s(镜像:%s,状态:%s)\n' "$XIAOYA_CONTAINER" "$image" "$state" >>$TTY_OUT
|
||||
fi
|
||||
[[ $state == running ]] || printf '警告:小雅容器当前已停止;仍允许安装控制器,状态页会提示。\n' >>$TTY_OUT
|
||||
else
|
||||
printf '发现多个可能的小雅容器(运行中的优先):\n' >>$TTY_OUT
|
||||
@@ -684,12 +752,25 @@ except (ValueError, IndexError, TypeError, AttributeError):
|
||||
printf '%d) %s(镜像:%s,状态:%s)\n' "$index" "$name" "$image" "$state" >>$TTY_OUT
|
||||
index=$((index + 1))
|
||||
done
|
||||
if (( OPTION2_XIAOYA_PENDING )); then
|
||||
prompt_tty_into choice '请选择:1) 复用已有容器 2) 安装独立小雅 0) 取消:'
|
||||
else
|
||||
prompt_tty_into choice '请选择容器编号:'
|
||||
[[ $choice =~ ^[0-9]+$ && $choice -ge 1 && $choice -le ${#ordered[@]} ]] || die '无效的小雅容器编号'
|
||||
IFS=$'\t' read -r XIAOYA_CONTAINER image state <<<"${ordered[$((choice - 1))]}"
|
||||
XIAOYA_FOUND=1
|
||||
printf '已选择小雅容器:%s(镜像:%s,状态:%s)\n' "$XIAOYA_CONTAINER" "$image" "$state" >>$TTY_OUT
|
||||
[[ $state == running ]] || printf '警告:小雅容器当前已停止;仍允许安装控制器,状态页会提示。\n' >>$TTY_OUT
|
||||
return 0
|
||||
fi
|
||||
case "$choice" in
|
||||
1) prompt_tty_into choice '请选择容器编号:'; [[ $choice =~ ^[0-9]+$ && $choice -ge 1 && $choice -le ${#ordered[@]} ]] || die '无效的小雅容器编号'; IFS=$'\t' read -r XIAOYA_CONTAINER image state <<<"${ordered[$((choice - 1))]}"; XIAOYA_FOUND=1; OPTION2_XIAOYA_CHOICE=reuse;;
|
||||
2) XIAOYA_CONTAINER=xymedia-xiaoya; XIAOYA_FOUND=0; XIAOYA_STANDALONE=1; OPTION2_XIAOYA_CHOICE=standalone; return 0;;
|
||||
0) exit 0;;
|
||||
*) die '无效选择,请输入 1、2 或 0';;
|
||||
esac
|
||||
printf '已选择小雅容器:%s(镜像:%s,状态:%s)\n' "$XIAOYA_CONTAINER" "$image" "$state" >>$TTY_OUT
|
||||
[[ $state == running ]] || printf '警告:小雅容器当前已停止;仍允许安装控制器,状态页会提示。\n' >>$TTY_OUT
|
||||
fi
|
||||
}
|
||||
prepare_option2_xiaoya() {
|
||||
@@ -697,14 +778,20 @@ prepare_option2_xiaoya() {
|
||||
discover_xiaoya_container
|
||||
validate_container_name
|
||||
if (( XIAOYA_FOUND )); then
|
||||
ensure_xiaoya_dir 1
|
||||
validate_xiaoya_data_dir
|
||||
XYMEDIA_XIAOYA_MANAGED=0
|
||||
OPTION2_XIAOYA_CHOICE=reuse
|
||||
MENU_PROFILES='xiaoya-control'
|
||||
printf '检测到已有小雅容器:%s,将复用,不创建新的小雅容器。\n' "$XIAOYA_CONTAINER" >>$TTY_OUT
|
||||
else
|
||||
XIAOYA_STANDALONE=1
|
||||
XIAOYA_DIR=$INSTALL_DIR/xiaoya
|
||||
XYMEDIA_XIAOYA_MANAGED=1
|
||||
OPTION2_XIAOYA_CHOICE=standalone
|
||||
MENU_PROFILES='xiaoya xiaoya-control'
|
||||
printf '未检测到已有小雅容器,将安装新的小雅容器:%s。\n' "$XIAOYA_CONTAINER" >>$TTY_OUT
|
||||
select_xiaoya_ports
|
||||
printf '将安装独立的小雅容器:%s(数据目录:%s)。\n' "$XIAOYA_CONTAINER" "$XIAOYA_DIR" >>$TTY_OUT
|
||||
fi
|
||||
}
|
||||
validate_xiaoya_data_dir() {
|
||||
@@ -1015,14 +1102,16 @@ env_tmp=$(mktemp "$INSTALL_DIR/.env.tmp.XXXXXX") || die '无法创建安全的 .
|
||||
chmod 600 "$env_tmp"
|
||||
if [[ -f "$INSTALL_DIR/.env" ]]; then cp "$INSTALL_DIR/.env" "$env_tmp"; fi
|
||||
XIAOYA_MANAGED_ENV=$(awk -F= '$1 == "XYMEDIA_XIAOYA_MANAGED" {print $2; exit}' "$env_tmp" 2>/dev/null || true)
|
||||
[[ ${XYMEDIA_XIAOYA_MANAGED:-0} == 1 ]] || XYMEDIA_XIAOYA_MANAGED=${XIAOYA_MANAGED_ENV:-0}
|
||||
if [[ -z $OPTION2_XIAOYA_CHOICE ]]; then
|
||||
[[ ${XYMEDIA_XIAOYA_MANAGED:-0} == 1 ]] || XYMEDIA_XIAOYA_MANAGED=${XIAOYA_MANAGED_ENV:-0}
|
||||
fi
|
||||
FUSE_ENABLED=$(awk -F= '$1 == "XYMEDIA_FUSE_ENABLED" {print $2; exit}' "$env_tmp" 2>/dev/null || true)
|
||||
FUSE_HOST_PATH=$(awk -F= '$1 == "XYMEDIA_FUSE_HOST_PATH" {print $2; exit}' "$env_tmp" 2>/dev/null || true)
|
||||
[[ $FUSE_ENABLED == true || $FUSE_ENABLED == false ]] || FUSE_ENABLED=false
|
||||
python3 - "$env_tmp" "$INSTALL_DIR" "$PG_HOST" "$PG_PORT" "$PG_DB" "$PG_USER" "$API_PORT" "$WEBDAV_PORT" "$TVBOX_PORT" "$CONTROLLER_URL" "${XIAOYA_DIR:-$INSTALL_DIR/xiaoya}" "$XIAOYA_CONTAINER" "$HOST_ADDRESSES_ENV" "$CONTROLLER_BIND_ADDRESS" "$CONTROLLER_PORT" "$FUSE_ENABLED" "$FUSE_HOST_PATH" "${XYMEDIA_XIAOYA_MANAGED:-0}" "$XYMEDIA_INSTANCE_ID" "$LOCAL_DB" "$XYMEDIA_POSTGRES_PUBLIC_PORT" <<'PY'
|
||||
python3 - "$env_tmp" "$INSTALL_DIR" "$PG_HOST" "$PG_PORT" "$PG_DB" "$PG_USER" "$API_PORT" "$WEBDAV_PORT" "$TVBOX_PORT" "$CONTROLLER_URL" "${XIAOYA_DIR:-$INSTALL_DIR/xiaoya}" "$XIAOYA_CONTAINER" "$HOST_ADDRESSES_ENV" "$CONTROLLER_BIND_ADDRESS" "$CONTROLLER_PORT" "$FUSE_ENABLED" "$FUSE_HOST_PATH" "${XYMEDIA_XIAOYA_MANAGED:-0}" "$XYMEDIA_INSTANCE_ID" "$LOCAL_DB" "$XYMEDIA_POSTGRES_PUBLIC_PORT" "$XIAOYA_WEB_PORT" "$XIAOYA_ADMIN_PORT" "$XIAOYA_PROXY_PORT" <<'PY'
|
||||
import pathlib, sys
|
||||
p=pathlib.Path(sys.argv[1])
|
||||
keys=('XYMEDIA_INSTALL_DIR','XYMEDIA_POSTGRES_HOST','XYMEDIA_POSTGRES_PORT','XYMEDIA_POSTGRES_DATABASE','XYMEDIA_POSTGRES_USER','XYMEDIA_API_PORT','XYMEDIA_WEBDAV_PORT','XYMEDIA_TVBOX_PORT','XYMEDIA_XIAOYA_CONTROLLER_URL','XYMEDIA_XIAOYA_DATA_DIR','XYMEDIA_XIAOYA_CONTAINER','XYMEDIA_HOST_ADDRESSES','XYMEDIA_CONTROLLER_BIND_ADDRESS','XYMEDIA_CONTROLLER_PORT','XYMEDIA_FUSE_ENABLED','XYMEDIA_FUSE_HOST_PATH','XYMEDIA_XIAOYA_MANAGED','XYMEDIA_INSTANCE_ID','XYMEDIA_POSTGRES_LOCAL_DB','XYMEDIA_POSTGRES_PUBLIC_PORT')
|
||||
keys=('XYMEDIA_INSTALL_DIR','XYMEDIA_POSTGRES_HOST','XYMEDIA_POSTGRES_PORT','XYMEDIA_POSTGRES_DATABASE','XYMEDIA_POSTGRES_USER','XYMEDIA_API_PORT','XYMEDIA_WEBDAV_PORT','XYMEDIA_TVBOX_PORT','XYMEDIA_XIAOYA_CONTROLLER_URL','XYMEDIA_XIAOYA_DATA_DIR','XYMEDIA_XIAOYA_CONTAINER','XYMEDIA_HOST_ADDRESSES','XYMEDIA_CONTROLLER_BIND_ADDRESS','XYMEDIA_CONTROLLER_PORT','XYMEDIA_FUSE_ENABLED','XYMEDIA_FUSE_HOST_PATH','XYMEDIA_XIAOYA_MANAGED','XYMEDIA_INSTANCE_ID','XYMEDIA_POSTGRES_LOCAL_DB','XYMEDIA_POSTGRES_PUBLIC_PORT','XYMEDIA_XIAOYA_PORT','XYMEDIA_XIAOYA_ADMIN_PORT','XYMEDIA_XIAOYA_PROXY_PORT')
|
||||
values=dict(zip(keys, sys.argv[2:]))
|
||||
seen=set(); output=[]
|
||||
for line in p.read_text().splitlines():
|
||||
|
||||
Reference in New Issue
Block a user