From e6186ba6df8a10b80628d0743841b5882948ffbd Mon Sep 17 00:00:00 2001 From: Leonid Pershin Date: Mon, 10 Aug 2026 09:31:14 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=B0=D0=BF?= =?UTF-8?q?=D0=B4=D0=B5=D0=B9=D1=82=D0=B5=D1=80=20=D0=BD=D0=B0=20=D0=BD?= =?UTF-8?q?=D0=B5=D1=81=D0=BA=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE=20=D1=80=D0=B5?= =?UTF-8?q?=D0=BF=D0=BE=D0=B7=D0=B8=D1=82=D0=BE=D1=80=D0=B8=D0=B5=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit update-geodata.sh теперь ходит по списку SOURCES (строка «URL-базы|имя»), а не по одной базе: ipcheck.dat и оба discord-.dat обновляются за один запуск и с одним перезапуском сервиса. Старый вызов с DEST продолжает работать — каталог берётся из него. Co-Authored-By: Claude Opus 5 --- README.md | 23 ++++++++++++++--- tools/update-geodata.sh | 57 +++++++++++++++++++++++++++++------------ 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 3b9fb6b..602823c 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ x-ui restart ## Автообновление на сервере -`update-geodata.sh` качает оба `.dat`, только если их sha256 разошлись с +`update-geodata.sh` качает файлы, только если их sha256 разошлись с удалёнными, проверяет сумму до подмены и перезапускает сервис один раз — и только если что-то реально изменилось. @@ -122,14 +122,31 @@ cron раз в сутки: 23 4 * * * RESTART_CMD='x-ui restart' /usr/local/bin/update-geodata.sh >> /var/log/discord-geodata.log 2>&1 ``` +Скрипт умеет тянуть файлы из нескольких репозиториев сразу — один cron-job +вместо двух. По умолчанию это `ipcheck.dat` из +[ipcheck-domains](https://gitea.hsrv.site/mrleo1nid/ipcheck-domains) и оба +`.dat` отсюда; перезапуск при этом один на все файлы. + | Переменная | По умолчанию | Значение | | --- | --- | --- | -| `BASE_URL` | `https://gitea.hsrv.site/mrleo1nid/discord-geodata/raw/branch/release` | база, откуда качать | +| `SOURCES` | три строки ниже | что качать, по строке на файл: `URL-базы\|имя-файла` | +| `BASE_URL` | `…/ipcheck-domains/raw/branch/release` | база для строк без `\|` | +| `DISCORD_URL` | `…/discord-geodata/raw/branch/release` | база для файлов этого репозитория | | `DEST_DIR` | `/usr/local/x-ui/bin` | куда класть файлы | -| `NAMES` | `discord-geosite.dat discord-geoip.dat` | что качать | | `RESTART_CMD` | пусто | что выполнить после успешного обновления | | `TIMEOUT` | `30` | таймаут curl, секунды | +Нужны только файлы Discord — задайте `SOURCES` явно: + +```sh +SOURCES='https://gitea.hsrv.site/mrleo1nid/discord-geodata/raw/branch/release|discord-geosite.dat +https://gitea.hsrv.site/mrleo1nid/discord-geodata/raw/branch/release|discord-geoip.dat' \ + RESTART_CMD='x-ui restart' /usr/local/bin/update-geodata.sh +``` + +Старый вызов с `DEST=/путь/файл.dat` продолжает работать: каталог берётся из +него, а имя файла подставляется в первую строку `SOURCES`. + Если 3x-ui в Docker — укажите `DEST_DIR` на смонтированный каталог и `RESTART_CMD='docker restart 3x-ui'`. diff --git a/tools/update-geodata.sh b/tools/update-geodata.sh index 75b469c..0bbb7dd 100644 --- a/tools/update-geodata.sh +++ b/tools/update-geodata.sh @@ -1,6 +1,5 @@ #!/bin/sh -# Обновляет локальные discord-geosite.dat и discord-geoip.dat из ветки release. -# Рассчитан на запуск по cron. +# Обновляет geosite/geoip-файлы Xray из веток release. Рассчитан на cron. # # Каждый файл скачивается, только если его sha256 отличается от локального, # контрольная сумма проверяется до подмены, а сервис перезапускается один раз @@ -8,20 +7,35 @@ # cron не будет слать письма впустую. # # ./update-geodata.sh -# BASE_URL=https://gitea.hsrv.site/mrleo1nid/discord-geodata/raw/branch/release \ -# DEST_DIR=/usr/local/x-ui/bin RESTART_CMD='x-ui restart' ./update-geodata.sh +# RESTART_CMD='x-ui restart' ./update-geodata.sh +# +# Что качать, задаёт SOURCES — по строке на файл, формат «URL-базы|имя-файла» +# (без «|» берётся BASE_URL). По умолчанию это ipcheck.dat из ipcheck-domains +# и оба .dat из discord-geodata: +# +# SOURCES='https://example.org/raw/branch/release|my.dat' ./update-geodata.sh # -# NAMES задаёт список файлов; по умолчанию — оба .dat. # Коды возврата: 0 — обновлено или уже актуально, 1 — ошибка. set -eu -BASE_URL="${BASE_URL:-https://gitea.hsrv.site/mrleo1nid/discord-geodata/raw/branch/release}" DEST_DIR="${DEST_DIR:-/usr/local/x-ui/bin}" -NAMES="${NAMES:-discord-geosite.dat discord-geoip.dat}" +BASE_URL="${BASE_URL:-https://gitea.hsrv.site/mrleo1nid/ipcheck-domains/raw/branch/release}" +DISCORD_URL="${DISCORD_URL:-https://gitea.hsrv.site/mrleo1nid/discord-geodata/raw/branch/release}" RESTART_CMD="${RESTART_CMD:-}" TIMEOUT="${TIMEOUT:-30}" +# Совместимость со старым вызовом, где DEST задавал единственный файл целиком. +if [ -n "${DEST:-}" ]; then + DEST_DIR=$(dirname "$DEST") + NAME="${NAME:-$(basename "$DEST")}" +fi + +SOURCES="${SOURCES:-\ +$BASE_URL|${NAME:-ipcheck.dat} +$DISCORD_URL|discord-geosite.dat +$DISCORD_URL|discord-geoip.dat}" + log() { printf '%s update-geodata: %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$*"; } die() { log "ОШИБКА: $*" >&2; exit 1; } @@ -37,15 +51,26 @@ fi fetch() { curl -fsSL --retry 3 --retry-delay 2 --max-time "$TIMEOUT" "$1"; } +tmp="" +cleanup() { [ -z "$tmp" ] || rm -f "$tmp"; } +trap cleanup EXIT INT TERM + mkdir -p "$DEST_DIR" changed=0 -for name in $NAMES; do +# Цикл читает SOURCES через here-doc, а не из конвейера: в подоболочке +# счётчик changed не пережил бы выход из цикла. +while IFS='|' read -r base name; do + [ -n "$base" ] || continue + if [ -z "$name" ]; then + name="$base" + base="$BASE_URL" + fi dest="$DEST_DIR/$name" # Без промежуточной переменной статус вернул бы cut, а не curl. - remote_line=$(fetch "$BASE_URL/$name.sha256sum") || - die "не удалось скачать $BASE_URL/$name.sha256sum" + remote_line=$(fetch "$base/$name.sha256sum") || + die "не удалось скачать $base/$name.sha256sum" remote_sum=$(printf '%s' "$remote_line" | cut -d' ' -f1) case "$remote_sum" in [0-9a-f][0-9a-f]*) [ "${#remote_sum}" -eq 64 ] || die "битая контрольная сумма: $remote_sum" ;; @@ -56,10 +81,8 @@ for name in $NAMES; do continue # уже актуально — тишина fi - tmp=$(mktemp "${TMPDIR:-/tmp}/discord-geodata.XXXXXX") || die "не удалось создать временный файл" - trap 'rm -f "$tmp"' EXIT INT TERM - - fetch "$BASE_URL/$name" > "$tmp" || die "не удалось скачать $BASE_URL/$name" + tmp=$(mktemp "${TMPDIR:-/tmp}/geodata.XXXXXX") || die "не удалось создать временный файл" + fetch "$base/$name" > "$tmp" || die "не удалось скачать $base/$name" got=$(hash_of "$tmp") [ "$got" = "$remote_sum" ] || die "$name: ожидалось $remote_sum, получено $got" @@ -69,11 +92,13 @@ for name in $NAMES; do # mv в пределах одного каталога атомарен: Xray никогда не увидит половину файла. cat "$tmp" > "$dest.new" && mv -f "$dest.new" "$dest" || die "не удалось записать $dest" rm -f "$tmp" - trap - EXIT INT TERM + tmp="" log "обновлён $dest (sha256 $remote_sum)" changed=1 -done +done <