| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- #!/bin/sh
- set -eu
- SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
- APP_DIR=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)
- CONFIG_PATH=${1:-"$APP_DIR/router_local.conf"}
- if [ ! -r "$CONFIG_PATH" ]; then
- echo "[router-local] config not found: $CONFIG_PATH" >&2
- exit 1
- fi
- # shellcheck disable=SC1090
- . "$CONFIG_PATH"
- CFST_WORK_DIR=${CFST_WORK_DIR:-"$APP_DIR/cfst"}
- CFST_BIN=${CFST_BIN:-"./cfst"}
- CFST_IP_FILE=${CFST_IP_FILE:-"ip.txt"}
- CFST_RESULT_FILE=${CFST_RESULT_FILE:-"result.csv"}
- CFST_DISPLAY_COUNT=${CFST_DISPLAY_COUNT:-10}
- CFST_THREADS=${CFST_THREADS:-}
- CFST_TEST_COUNT=${CFST_TEST_COUNT:-}
- CFST_DOWNLOAD_COUNT=${CFST_DOWNLOAD_COUNT:-}
- CFST_DOWNLOAD_TIME=${CFST_DOWNLOAD_TIME:-}
- CFST_PORT=${CFST_PORT:-443}
- CFST_URL=${CFST_URL:-}
- CFST_HTTPING=${CFST_HTTPING:-0}
- CFST_HTTPING_CODE=${CFST_HTTPING_CODE:-}
- CFST_CFCOLO=${CFST_CFCOLO:-}
- CFST_LATENCY_LIMIT=${CFST_LATENCY_LIMIT:-}
- CFST_LATENCY_LOWER=${CFST_LATENCY_LOWER:-}
- CFST_LOSS_LIMIT=${CFST_LOSS_LIMIT:-}
- CFST_SPEED_LIMIT=${CFST_SPEED_LIMIT:-}
- CFST_DISABLE_DOWNLOAD=${CFST_DISABLE_DOWNLOAD:-0}
- CFST_ALL_IP=${CFST_ALL_IP:-0}
- CFST_DEBUG=${CFST_DEBUG:-0}
- CFST_SKIP_RUN=${CFST_SKIP_RUN:-0}
- TOP_N=${TOP_N:-3}
- RUNTIME_DIR=${RUNTIME_DIR:-"$APP_DIR/cfip_runtime"}
- VALUE_TEXT_FILE=${VALUE_TEXT_FILE:-"current_ip.txt"}
- VALUE_JSON_FILE=${VALUE_JSON_FILE:-"current_ip.json"}
- STATE_FILE=${STATE_FILE:-"state.json"}
- EXPORT_VARS_FILE=${EXPORT_VARS_FILE:-"substore_vars.json"}
- VALUE_JSON_KEY=${VALUE_JSON_KEY:-ip}
- STATE_LAST_GOOD_KEY=${STATE_LAST_GOOD_KEY:-last_good_ip}
- EXPORT_VALUE_KEY=${EXPORT_VALUE_KEY:-AUTO_CFIP}
- CURRENT_TEXT_PATH="$RUNTIME_DIR/$VALUE_TEXT_FILE"
- CURRENT_JSON_PATH="$RUNTIME_DIR/$VALUE_JSON_FILE"
- STATE_PATH="$RUNTIME_DIR/$STATE_FILE"
- EXPORT_VARS_PATH="$RUNTIME_DIR/$EXPORT_VARS_FILE"
- RESULT_PATH="$CFST_WORK_DIR/$CFST_RESULT_FILE"
- UPDATED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
- mkdir -p "$RUNTIME_DIR"
- json_get_string() {
- key="$1"
- file="$2"
- if [ ! -f "$file" ]; then
- return 0
- fi
- sed -n "s/.*\"$key\":[[:space:]]*\"\([^\"]*\)\".*/\1/p" "$file" | head -n 1
- }
- json_escape() {
- printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'
- }
- trim_cr() {
- printf '%s' "$1" | tr -d '\r'
- }
- build_top_candidates_json() {
- awk -F',' -v top_n="$TOP_N" '
- function trim(v) {
- gsub(/\r/, "", v)
- sub(/^[[:space:]]+/, "", v)
- sub(/[[:space:]]+$/, "", v)
- return v
- }
- function esc(v) {
- gsub(/\\/,"\\\\",v)
- gsub(/"/,"\\\"",v)
- return v
- }
- NR == 1 { next }
- count >= top_n { exit }
- NF < 1 { next }
- {
- ip = esc(trim($1))
- sent = esc(trim($2))
- received = esc(trim($3))
- loss = esc(trim($4))
- latency = esc(trim($5))
- speed = esc(trim($6))
- region = esc(trim($7))
- if (count > 0) {
- printf(",")
- }
- printf("{\"ip\":\"%s\",\"domain\":\"%s\",\"sent\":\"%s\",\"received\":\"%s\",\"loss_rate\":\"%s\",\"avg_latency\":\"%s\",\"download_speed\":\"%s\",\"region\":\"%s\"}", ip, ip, sent, received, loss, latency, speed, region)
- count++
- }
- END {
- if (count == 0) {
- printf("")
- }
- }
- ' "$RESULT_PATH"
- }
- write_success_outputs() {
- selected_ip="$1"
- top_candidates_json="$2"
- source_count="$3"
- escaped_ip=$(json_escape "$selected_ip")
- printf '%s\n' "$selected_ip" > "$CURRENT_TEXT_PATH"
- cat > "$CURRENT_JSON_PATH" <<EOF
- {
- "$VALUE_JSON_KEY": "$escaped_ip",
- "updated_at": "$UPDATED_AT",
- "status": "ok",
- "source_type": "cfst_local_busybox",
- "source_count": $source_count,
- "checked_count": 0,
- "top_candidates": [${top_candidates_json}]
- }
- EOF
- cat > "$STATE_PATH" <<EOF
- {
- "updated_at": "$UPDATED_AT",
- "$STATE_LAST_GOOD_KEY": "$escaped_ip",
- "status": "ok",
- "source_count": $source_count,
- "checked_count": 0,
- "source_type": "cfst_local_busybox"
- }
- EOF
- cat > "$EXPORT_VARS_PATH" <<EOF
- {
- "$EXPORT_VALUE_KEY": "$escaped_ip",
- "UPDATED_AT": "$UPDATED_AT",
- "STATUS": "ok"
- }
- EOF
- }
- write_error_with_fallback() {
- last_good_ip="$1"
- error_message="$2"
- escaped_ip=$(json_escape "$last_good_ip")
- escaped_error=$(json_escape "$error_message")
- printf '%s\n' "$last_good_ip" > "$CURRENT_TEXT_PATH"
- cat > "$CURRENT_JSON_PATH" <<EOF
- {
- "$VALUE_JSON_KEY": "$escaped_ip",
- "updated_at": "$UPDATED_AT",
- "status": "error_use_last_good",
- "error": "$escaped_error",
- "source_type": "cfst_local_busybox"
- }
- EOF
- cat > "$STATE_PATH" <<EOF
- {
- "updated_at": "$UPDATED_AT",
- "$STATE_LAST_GOOD_KEY": "$escaped_ip",
- "status": "error",
- "error": "$escaped_error",
- "source_type": "cfst_local_busybox"
- }
- EOF
- cat > "$EXPORT_VARS_PATH" <<EOF
- {
- "$EXPORT_VALUE_KEY": "$escaped_ip",
- "UPDATED_AT": "$UPDATED_AT",
- "STATUS": "error_use_last_good"
- }
- EOF
- }
- run_cfst() (
- cd "$CFST_WORK_DIR"
- set -- "$CFST_BIN" -f "$CFST_IP_FILE" -o "$CFST_RESULT_FILE" -p "$CFST_DISPLAY_COUNT" -tp "$CFST_PORT"
- if [ -n "$CFST_THREADS" ]; then
- set -- "$@" -n "$CFST_THREADS"
- fi
- if [ -n "$CFST_TEST_COUNT" ]; then
- set -- "$@" -t "$CFST_TEST_COUNT"
- fi
- if [ -n "$CFST_DOWNLOAD_COUNT" ]; then
- set -- "$@" -dn "$CFST_DOWNLOAD_COUNT"
- fi
- if [ -n "$CFST_DOWNLOAD_TIME" ]; then
- set -- "$@" -dt "$CFST_DOWNLOAD_TIME"
- fi
- if [ -n "$CFST_URL" ]; then
- set -- "$@" -url "$CFST_URL"
- fi
- if [ "$CFST_HTTPING" = "1" ]; then
- set -- "$@" -httping
- fi
- if [ -n "$CFST_HTTPING_CODE" ]; then
- set -- "$@" -httping-code "$CFST_HTTPING_CODE"
- fi
- if [ -n "$CFST_CFCOLO" ]; then
- set -- "$@" -cfcolo "$CFST_CFCOLO"
- fi
- if [ -n "$CFST_LATENCY_LIMIT" ]; then
- set -- "$@" -tl "$CFST_LATENCY_LIMIT"
- fi
- if [ -n "$CFST_LATENCY_LOWER" ]; then
- set -- "$@" -tll "$CFST_LATENCY_LOWER"
- fi
- if [ -n "$CFST_LOSS_LIMIT" ]; then
- set -- "$@" -tlr "$CFST_LOSS_LIMIT"
- fi
- if [ -n "$CFST_SPEED_LIMIT" ]; then
- set -- "$@" -sl "$CFST_SPEED_LIMIT"
- fi
- if [ "$CFST_DISABLE_DOWNLOAD" = "1" ]; then
- set -- "$@" -dd
- fi
- if [ "$CFST_ALL_IP" = "1" ]; then
- set -- "$@" -allip
- fi
- if [ "$CFST_DEBUG" = "1" ]; then
- set -- "$@" -debug
- fi
- "$@"
- )
- LAST_GOOD_IP=$(json_get_string "$STATE_LAST_GOOD_KEY" "$STATE_PATH")
- if [ "$CFST_SKIP_RUN" != "1" ]; then
- if ! run_cfst; then
- if [ -n "$LAST_GOOD_IP" ]; then
- write_error_with_fallback "$LAST_GOOD_IP" "cfst run failed"
- echo "[router-local] cfst run failed, fallback to last good ip: $LAST_GOOD_IP"
- exit 0
- fi
- echo "[router-local] cfst run failed and no last good ip available" >&2
- exit 1
- fi
- fi
- if [ ! -s "$RESULT_PATH" ]; then
- if [ -n "$LAST_GOOD_IP" ]; then
- write_error_with_fallback "$LAST_GOOD_IP" "cfst result file missing or empty"
- echo "[router-local] empty result, fallback to last good ip: $LAST_GOOD_IP"
- exit 0
- fi
- echo "[router-local] result file missing or empty: $RESULT_PATH" >&2
- exit 1
- fi
- BEST_LINE=$(sed -n '2p' "$RESULT_PATH" | tr -d '\r')
- BEST_IP=$(printf '%s' "$BEST_LINE" | cut -d',' -f1 | tr -d ' ')
- SOURCE_COUNT=$(awk -F',' 'NR > 1 && NF > 0 { count++ } END { print count + 0 }' "$RESULT_PATH")
- TOP_CANDIDATES_JSON=$(build_top_candidates_json)
- if [ -z "$BEST_IP" ]; then
- if [ -n "$LAST_GOOD_IP" ]; then
- write_error_with_fallback "$LAST_GOOD_IP" "no valid ip found in cfst result"
- echo "[router-local] no valid ip found, fallback to last good ip: $LAST_GOOD_IP"
- exit 0
- fi
- echo "[router-local] no valid ip found in result file" >&2
- exit 1
- fi
- write_success_outputs "$BEST_IP" "$TOP_CANDIDATES_JSON" "$SOURCE_COUNT"
- echo "[router-local] selected ip: $BEST_IP"
|