feat: add interactive installer network setup
TallyNote release / linux-x64 (push) Successful in 5m54s
TallyNote release / linux-x64 (push) Successful in 5m54s
This commit is contained in:
+120
-6
@@ -41,6 +41,13 @@ INSTALL_HOST=${TALLYNOTE_HOST-127.0.0.1}
|
||||
INSTALL_PORT=${TALLYNOTE_PORT-3000}
|
||||
INSTALL_PUBLIC_ORIGIN=${TALLYNOTE_PUBLIC_ORIGIN-}
|
||||
INSTALL_ALLOW_INSECURE_HTTP=${TALLYNOTE_ALLOW_INSECURE_HTTP-false}
|
||||
NON_INTERACTIVE=0
|
||||
|
||||
# The production prompt uses the controlling terminal, even when the
|
||||
# installer itself is read from `curl | sudo bash`.
|
||||
PROMPT_INPUT=/dev/tty
|
||||
PROMPT_OUTPUT=/dev/tty
|
||||
PROMPT_REPLY=''
|
||||
|
||||
INSTALL_SWITCHED=0
|
||||
INSTALL_COMMITTED=0
|
||||
@@ -65,6 +72,7 @@ Usage: install.sh [--dry-run] [--version VERSION] [--release-base-url HTTPS_URL]
|
||||
[--signature-format ed25519|gpg]
|
||||
[--update-public-key-file FILE]
|
||||
[--keep-releases N] [--allow-downgrade] [--allow-unsigned] [--apply]
|
||||
[--non-interactive]
|
||||
|
||||
Without arguments, the installer resolves the latest compatible release and
|
||||
installs it. SHA-256 from SHA256SUMS is always required. Detached signature
|
||||
@@ -73,7 +81,10 @@ TALLYNOTE_INSTALL_REQUIRE_SIGNATURE=true and provide a public key. Use
|
||||
--dry-run to inspect the selected release without downloading or changing the
|
||||
host. For direct IP access, pass TALLYNOTE_HOST=0.0.0.0 and an actual
|
||||
TALLYNOTE_PUBLIC_ORIGIN such as http://203.0.113.10:3000; HTTP also requires
|
||||
TALLYNOTE_ALLOW_INSECURE_HTTP=true. --apply is accepted for backwards compatibility.
|
||||
TALLYNOTE_ALLOW_INSECURE_HTTP=true. On a fresh terminal install, the listener
|
||||
and public URL can be selected interactively. Use --non-interactive (or
|
||||
TALLYNOTE_NON_INTERACTIVE=true) for automation. --apply is accepted for
|
||||
backwards compatibility.
|
||||
EOF
|
||||
}
|
||||
die() { printf 'tallynote installer: %s\n' "$*" >&2; exit 1; }
|
||||
@@ -81,6 +92,107 @@ log() { printf 'tallynote installer: %s\n' "$*"; }
|
||||
stage() { log "[阶段] $*"; }
|
||||
stage_done() { log "[完成] $*"; }
|
||||
|
||||
case "${TALLYNOTE_NON_INTERACTIVE:-false}" in
|
||||
true|1) NON_INTERACTIVE=1 ;;
|
||||
false|0) ;;
|
||||
*) die 'TALLYNOTE_NON_INTERACTIVE 必须是 true 或 false' ;;
|
||||
esac
|
||||
|
||||
prompt_value() {
|
||||
local label=$1 default=${2-} reply
|
||||
if [[ -n "$default" ]]; then
|
||||
printf '%s [%s]: ' "$label" "$default" > "$PROMPT_OUTPUT"
|
||||
else
|
||||
printf '%s: ' "$label" > "$PROMPT_OUTPUT"
|
||||
fi
|
||||
if ! IFS= read -r reply <&9; then
|
||||
die '无法读取终端输入;请使用 --non-interactive 或通过环境变量配置'
|
||||
fi
|
||||
PROMPT_REPLY=${reply:-$default}
|
||||
}
|
||||
|
||||
has_network_environment() {
|
||||
[[ -n "${TALLYNOTE_HOST+x}" || -n "${TALLYNOTE_PORT+x}" || -n "${TALLYNOTE_PUBLIC_ORIGIN+x}" || -n "${TALLYNOTE_ALLOW_INSECURE_HTTP+x}" ]]
|
||||
}
|
||||
|
||||
interactive_network_available() {
|
||||
(( APPLY )) || return 1
|
||||
(( NON_INTERACTIVE == 0 )) || return 1
|
||||
has_network_environment && return 1
|
||||
[[ ! -e "$CONFIG_DIR/tallynote.env" && ! -L "$CONFIG_DIR/tallynote.env" ]] || return 1
|
||||
[[ -r "$PROMPT_INPUT" && -w "$PROMPT_OUTPUT" ]] || return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
configure_network_interactively() {
|
||||
interactive_network_available || return 0
|
||||
|
||||
exec 9<"$PROMPT_INPUT" || die '无法打开终端输入;请使用 --non-interactive 或通过环境变量配置'
|
||||
|
||||
stage '配置服务网络监听(可直接回车使用默认值)'
|
||||
{
|
||||
printf '\nTallyNote 服务监听配置\n'
|
||||
printf ' 1) 仅本机访问:127.0.0.1(更安全)\n'
|
||||
printf ' 2) 局域网/公网访问:0.0.0.0(需要填写实际访问地址)\n'
|
||||
} > "$PROMPT_OUTPUT"
|
||||
|
||||
local choice selected_port origin answer
|
||||
while :; do
|
||||
prompt_value '请选择监听方式 1/2' '1'
|
||||
choice=$PROMPT_REPLY
|
||||
case "$choice" in
|
||||
1|2) break ;;
|
||||
*) printf '请输入 1 或 2。\n' > "$PROMPT_OUTPUT" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
while :; do
|
||||
prompt_value '监听端口' "$INSTALL_PORT"
|
||||
selected_port=$PROMPT_REPLY
|
||||
if [[ "$selected_port" =~ ^[1-9][0-9]*$ && "$selected_port" -le 65535 ]]; then
|
||||
break
|
||||
fi
|
||||
printf '端口必须是 1-65535 的整数,请重试。\n' > "$PROMPT_OUTPUT"
|
||||
done
|
||||
|
||||
if [[ "$choice" == 1 ]]; then
|
||||
INSTALL_HOST=127.0.0.1
|
||||
INSTALL_PORT=$selected_port
|
||||
INSTALL_PUBLIC_ORIGIN="http://127.0.0.1:${selected_port}"
|
||||
INSTALL_ALLOW_INSECURE_HTTP=false
|
||||
else
|
||||
INSTALL_HOST=0.0.0.0
|
||||
INSTALL_PORT=$selected_port
|
||||
while :; do
|
||||
prompt_value '实际访问地址(例如 http://203.0.113.10:3000 或 https://tallynote.example.com)' ''
|
||||
origin=$PROMPT_REPLY
|
||||
if validate_env_value "$origin" '公开访问地址' >/dev/null 2>&1 && validate_public_origin "$origin" >/dev/null 2>&1; then
|
||||
INSTALL_PUBLIC_ORIGIN=$origin
|
||||
break
|
||||
fi
|
||||
printf '地址无效:请输入不含路径、凭据或通配监听地址的 http:// 或 https:// 地址。\n' > "$PROMPT_OUTPUT"
|
||||
done
|
||||
INSTALL_ALLOW_INSECURE_HTTP=false
|
||||
if [[ "$INSTALL_PUBLIC_ORIGIN" == http://* ]]; then
|
||||
{
|
||||
printf '\n警告:直连 HTTP 不加密,登录信息和账目数据可能被窃听。\n'
|
||||
printf '仅在受控局域网或你明确接受风险时继续。\n'
|
||||
} > "$PROMPT_OUTPUT"
|
||||
while :; do
|
||||
prompt_value '确认允许公网 HTTP?输入 yes 继续,其他内容取消' 'no'
|
||||
answer=$PROMPT_REPLY
|
||||
case "$answer" in
|
||||
yes|YES|Yes|y|Y) INSTALL_ALLOW_INSECURE_HTTP=true; break ;;
|
||||
no|NO|No|n|N|'') die '已取消:公网 HTTP 必须明确确认;请改用 HTTPS 或重新运行安装器' ;;
|
||||
*) printf '请输入 yes 或 no。\n' > "$PROMPT_OUTPUT" ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
fi
|
||||
exec 9<&-
|
||||
stage_done "网络配置已选择:${INSTALL_HOST}:${INSTALL_PORT}"
|
||||
}
|
||||
|
||||
[[ "$REQUIRE_SIGNATURE" == true || "$REQUIRE_SIGNATURE" == false ]] || die 'TALLYNOTE_INSTALL_REQUIRE_SIGNATURE 必须是 true 或 false'
|
||||
[[ "$ALLOW_DOWNGRADE" == true || "$ALLOW_DOWNGRADE" == false ]] || die 'TALLYNOTE_ALLOW_DOWNGRADE 必须是 true 或 false'
|
||||
[[ "$SIGNATURE_FORMAT" == ed25519 || "$SIGNATURE_FORMAT" == gpg ]] || die '签名格式必须是 ed25519 或 gpg'
|
||||
@@ -115,6 +227,7 @@ while (($#)); do
|
||||
--keep-releases) KEEP_RELEASES=${2:?missing value for --keep-releases}; shift ;;
|
||||
--allow-downgrade) ALLOW_DOWNGRADE=true ;;
|
||||
--allow-unsigned) ALLOW_UNSIGNED=1; REQUIRE_SIGNATURE=false ;;
|
||||
--non-interactive) NON_INTERACTIVE=1 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown option: $1" ;;
|
||||
esac
|
||||
@@ -654,7 +767,7 @@ validate_listen_port() {
|
||||
}
|
||||
|
||||
validate_public_origin() {
|
||||
local value=$1 authority host path_part port suffix
|
||||
local value=$1 authority host path_part origin_port suffix
|
||||
case "$value" in
|
||||
http://*|https://*) ;;
|
||||
*) die '公开访问地址必须是 http:// 或 https:// 地址' ;;
|
||||
@@ -668,13 +781,13 @@ validate_public_origin() {
|
||||
suffix=${authority#*\]}
|
||||
if [[ -n "$suffix" ]]; then
|
||||
[[ "$suffix" =~ ^:([0-9]+)$ ]] || die '公开访问地址端口无效'
|
||||
port=${BASH_REMATCH[1]}
|
||||
origin_port=${BASH_REMATCH[1]}
|
||||
fi
|
||||
else
|
||||
if [[ "$authority" == *:* ]]; then
|
||||
[[ "$authority" =~ ^([^:]+):([0-9]+)$ ]] || die '公开访问地址端口无效'
|
||||
host=${BASH_REMATCH[1]}
|
||||
port=${BASH_REMATCH[2]}
|
||||
origin_port=${BASH_REMATCH[2]}
|
||||
else
|
||||
host=$authority
|
||||
fi
|
||||
@@ -682,8 +795,8 @@ validate_public_origin() {
|
||||
[[ -n "$host" ]] || die '公开访问地址缺少主机名'
|
||||
[[ "$host" != 0.0.0.0 && "$host" != :: && "$host" != \* ]] || die '公开访问地址不能使用通配监听地址,请填写服务器 IP 或域名'
|
||||
[[ "$host" =~ ^[A-Za-z0-9.-]+$ || "$host" =~ ^[0-9A-Fa-f:]+$ ]] || die '公开访问地址主机名无效'
|
||||
if [[ -n "$port" ]]; then
|
||||
[[ "$port" =~ ^[0-9]{1,5}$ && "$port" -ge 1 && "$port" -le 65535 ]] || die '公开访问地址端口必须是 1-65535 的整数'
|
||||
if [[ -n "$origin_port" ]]; then
|
||||
[[ "$origin_port" =~ ^[0-9]{1,5}$ && "$origin_port" -ge 1 && "$origin_port" -le 65535 ]] || die '公开访问地址端口必须是 1-65535 的整数'
|
||||
fi
|
||||
path_part=${value#*://}
|
||||
path_part=${path_part#"$authority"}
|
||||
@@ -857,6 +970,7 @@ main() {
|
||||
validate_trusted_tool "$OPENSSL_BIN" 'openssl'
|
||||
fi
|
||||
detect_platform
|
||||
configure_network_interactively
|
||||
validate_listen_host "$INSTALL_HOST"
|
||||
validate_listen_port "$INSTALL_PORT"
|
||||
if [[ -n "${TALLYNOTE_PUBLIC_ORIGIN+x}" && -z "$INSTALL_PUBLIC_ORIGIN" ]]; then
|
||||
|
||||
Reference in New Issue
Block a user