mirror of
https://github.com/okxlin/appstore.git
synced 2026-09-27 08:01:06 +00:00
Add mobile Codex sessions through the published Paseo-enabled image, default the host listener to loopback, preserve existing permissions and persistent state, and migrate missing or empty bind settings without overwriting custom ports or addresses.
185 lines
5.6 KiB
Bash
Executable File
185 lines
5.6 KiB
Bash
Executable File
#!/bin/bash
|
|
# upgrade.sh — codex-claude-workstation 1Panel upgrade helper
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
ROOT_DIR="$(cd -- "${SCRIPT_DIR}/.." && pwd -P)"
|
|
ENV_FILE="${ROOT_DIR}/.env"
|
|
|
|
strip_matching_quotes() {
|
|
local value="$1"
|
|
if [[ ${#value} -ge 2 ]]; then
|
|
if [[ "${value:0:1}" == '"' && "${value: -1}" == '"' ]]; then
|
|
value="${value:1:${#value}-2}"
|
|
elif [[ "${value:0:1}" == "'" && "${value: -1}" == "'" ]]; then
|
|
value="${value:1:${#value}-2}"
|
|
fi
|
|
fi
|
|
printf '%s\n' "$value"
|
|
}
|
|
|
|
get_env_value() {
|
|
local key="$1"
|
|
local value
|
|
|
|
value="$(sed -n -E "s/^${key}=//p" "$ENV_FILE" | tail -n 1)"
|
|
strip_matching_quotes "$value"
|
|
}
|
|
|
|
resolve_app_path() {
|
|
local key="$1"
|
|
local raw="$2"
|
|
local clean candidate resolved current part
|
|
local -a parts=()
|
|
case "$raw" in
|
|
""|/*|.|..|../*|*/../*|*/..) echo "unsafe ${key} path" >&2; return 1 ;;
|
|
esac
|
|
if [[ "$raw" =~ [[:cntrl:]] ]]; then
|
|
echo "unsafe ${key} path" >&2
|
|
return 1
|
|
fi
|
|
clean="${raw#./}"
|
|
[[ -n "$clean" ]] || { echo "unsafe ${key} path" >&2; return 1; }
|
|
command -v realpath >/dev/null 2>&1 || { echo "realpath is required" >&2; return 1; }
|
|
candidate="$ROOT_DIR/$clean"
|
|
resolved="$(realpath -m -- "$candidate")" || { echo "unsafe ${key} path" >&2; return 1; }
|
|
case "$resolved" in
|
|
"$ROOT_DIR"/*) ;;
|
|
*) echo "unsafe ${key} path" >&2; return 1 ;;
|
|
esac
|
|
current="$ROOT_DIR"
|
|
IFS='/' read -r -a parts <<< "$clean"
|
|
for part in "${parts[@]}"; do
|
|
[[ -z "$part" || "$part" == "." ]] && continue
|
|
current="$current/$part"
|
|
if [[ -L "$current" ]]; then
|
|
echo "unsafe ${key} path" >&2
|
|
return 1
|
|
fi
|
|
done
|
|
printf '%s\n' "$resolved"
|
|
}
|
|
|
|
paseo_password_is_websocket_token() {
|
|
local value="${1-}"
|
|
local token_pattern="^[A-Za-z0-9!#\$%&'*+.^_\`|~-]+$"
|
|
|
|
[[ ${#value} -ge 1 && ${#value} -le 128 && "$value" =~ $token_pattern ]]
|
|
}
|
|
|
|
paseo_bind_ip_is_valid() {
|
|
local value="$1"
|
|
local octet decimal
|
|
local -a octets=()
|
|
|
|
[[ "$value" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]] || return 1
|
|
IFS='.' read -r -a octets <<< "$value"
|
|
[[ ${#octets[@]} -eq 4 ]] || return 1
|
|
for octet in "${octets[@]}"; do
|
|
decimal=$((10#${octet}))
|
|
(( decimal <= 255 )) || return 1
|
|
done
|
|
}
|
|
|
|
ensure_env_default() {
|
|
local key="$1"
|
|
local value="$2"
|
|
local fill_empty="${3:-false}"
|
|
|
|
if grep -qE "^${key}=" "$ENV_FILE"; then
|
|
if [[ "$fill_empty" == "true" && -z "$(get_env_value "$key")" ]]; then
|
|
local replacement_file
|
|
replacement_file="$(mktemp "${ENV_FILE}.tmp.XXXXXX")"
|
|
awk -v key="$key" -v value="$value" '
|
|
BEGIN {
|
|
prefix = key "="
|
|
written = 0
|
|
}
|
|
index($0, prefix) == 1 {
|
|
if (written == 0) {
|
|
print prefix value
|
|
written = 1
|
|
}
|
|
next
|
|
}
|
|
{ print }
|
|
END {
|
|
if (written == 0) {
|
|
print prefix value
|
|
}
|
|
}
|
|
' "$ENV_FILE" > "$replacement_file"
|
|
chmod --reference="$ENV_FILE" -- "$replacement_file"
|
|
chown --reference="$ENV_FILE" -- "$replacement_file" 2>/dev/null || true
|
|
mv -- "$replacement_file" "$ENV_FILE"
|
|
echo "Filled empty ${key}"
|
|
return
|
|
fi
|
|
echo "${key} already exists"
|
|
return
|
|
fi
|
|
|
|
printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE"
|
|
echo "Added ${key}"
|
|
}
|
|
|
|
if [[ -L "$ENV_FILE" ]]; then
|
|
echo "Refusing to update a symlinked .env file" >&2
|
|
exit 1
|
|
elif [[ -f "$ENV_FILE" ]]; then
|
|
paseo_bind_ip_value="$(get_env_value PASEO_BIND_IP)"
|
|
if [[ -n "$paseo_bind_ip_value" ]] && ! paseo_bind_ip_is_valid "$paseo_bind_ip_value"; then
|
|
echo "PASEO_BIND_IP must be a valid IPv4 address" >&2
|
|
exit 1
|
|
fi
|
|
|
|
paseo_password_value="$(get_env_value PASEO_PASSWORD)"
|
|
if [[ -n "$paseo_password_value" ]]; then
|
|
if ! paseo_password_is_websocket_token "$paseo_password_value"; then
|
|
echo "PASEO_PASSWORD must be 1-128 HTTP-token-safe characters; use: openssl rand -hex 24" >&2
|
|
exit 1
|
|
elif [[ ${#paseo_password_value} -lt 20 ]]; then
|
|
echo "PASEO_PASSWORD must contain at least 20 characters for public access" >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
custom_env_file_raw="$(get_env_value CUSTOM_ENV_FILE)"
|
|
custom_env_file_raw="${custom_env_file_raw:-./data/custom.env}"
|
|
custom_env_file_abs="$(resolve_app_path CUSTOM_ENV_FILE "$custom_env_file_raw")"
|
|
if [[ -L "$custom_env_file_abs" || -d "$custom_env_file_abs" ]]; then
|
|
echo "CUSTOM_ENV_FILE must be a regular non-symlink file" >&2
|
|
exit 1
|
|
fi
|
|
|
|
ensure_env_default "PANEL_APP_PORT_PASEO" "6767"
|
|
ensure_env_default "PASEO_BIND_IP" "127.0.0.1" "true"
|
|
ensure_env_default "PASEO_PASSWORD" ""
|
|
ensure_env_default "FIX_WORKSPACE_OWNERSHIP_RECURSIVE" "false"
|
|
ensure_env_default "CUSTOM_ENV_FILE" "./data/custom.env"
|
|
ensure_env_default "GITHUB_TOKEN" ""
|
|
ensure_env_default "GIT_AUTHOR_NAME" ""
|
|
ensure_env_default "GIT_AUTHOR_EMAIL" ""
|
|
ensure_env_default "GIT_COMMITTER_NAME" ""
|
|
ensure_env_default "GIT_COMMITTER_EMAIL" ""
|
|
chmod 0600 -- "$ENV_FILE"
|
|
|
|
mkdir -p -- "$(dirname -- "$custom_env_file_abs")"
|
|
[[ "$(resolve_app_path CUSTOM_ENV_FILE "$custom_env_file_raw")" == "$custom_env_file_abs" ]]
|
|
if [[ -L "$custom_env_file_abs" || -d "$custom_env_file_abs" ]]; then
|
|
echo "CUSTOM_ENV_FILE must be a regular non-symlink file" >&2
|
|
exit 1
|
|
elif [[ ! -e "$custom_env_file_abs" ]]; then
|
|
install -m 0600 /dev/null "$custom_env_file_abs"
|
|
elif [[ ! -f "$custom_env_file_abs" ]]; then
|
|
echo "CUSTOM_ENV_FILE must be a regular non-symlink file" >&2
|
|
exit 1
|
|
else
|
|
chmod 0600 -- "$custom_env_file_abs"
|
|
fi
|
|
else
|
|
echo "${ENV_FILE} not found; skipped environment migration"
|
|
fi
|
|
|
|
echo "Codex Claude Workstation upgrade migration completed."
|