diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index f05521d..f19373f 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -332,10 +332,8 @@ the quota reader has no access to session-log content. If cleanip.io sees a different exit than Cloudflare (split-routing proxies), its address is shown. Results are cached per IP for an hour in memory and for a week on disk (failed lookups are not cached), and a 429 stops further calls until the next UTC day. Country - codes are validated before being used as flag file names. `server/geoip/` still holds the systemd - timer that syncs MaxMind GeoLite2 onto `getopenstats.com/geoip/` (account and key in - `/etc/openstats/maxmind.env` on the server); the app no longer downloads those files, they are kept - for other uses. + codes are validated before being used as flag file names. The app does not download or read local + GeoIP databases; the retired database synchronization and deployment scripts have been removed. Each public-IP query owns a cancellable task and generation token. Disabling lookup clears the cache and invalidates the query; sleep or lock pauses it. Every asynchronous stage and final cache write checks both generation and current settings, so late callbacks cannot restore cleared data diff --git a/scripts/deploy_site.sh b/scripts/deploy_site.sh deleted file mode 100755 index de5a420..0000000 --- a/scripts/deploy_site.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash -# 把 web/ 发布到 getopenstats.com(Caddy 静态站点,前面是 Cloudflare 橙云)。 -# -# 静态资源在 Cloudflare 缓存一天,同一个 URL 换了内容,线上仍会拿到旧文件。 -# 所以每个资源 URL 带 ?v=内容指纹:内容变了指纹就变,URL 跟着变。 -# 最后按公网实际拿到的内容校验,而不是只看源站。 -set -euo pipefail -cd "$(dirname "$0")/.." - -HOST="${SITE_HOST:-debian@51.38.126.148}" -KEY="${SITE_KEY:-$HOME/.ssh/gentpan.pem}" -ROOT="${SITE_ROOT:-/var/www/getopenstats.com}" -DOMAIN="${SITE_DOMAIN:-getopenstats.com}" -SSH=(ssh -i "$KEY" -o BatchMode=yes) - -# 官网里的更新日志由 CHANGELOG.md 生成,发布前先同步 -python3 scripts/sync_changelog.py - -# 先洗掉 ?v= 再算指纹,否则指纹会把自己算进去,内容没变也每次换值 -STAMP="$( { cat web/styles.css web/replica.css web/app.js web/replica.js | /usr/bin/sed -E 's/\?v=[A-Za-z0-9]+//g' - find web/assets -type f | sort | xargs cat; } | shasum -a 256 | cut -c1-8)" -echo "内容指纹 v=$STAMP" -/usr/bin/sed -i '' -E "s/\?v=[A-Za-z0-9]+/?v=$STAMP/g" web/index.html - -# 目录归部署用户所有,Caddy 只需读取 -"${SSH[@]}" "$HOST" "sudo mkdir -p $ROOT && sudo chown -R \$(id -un):\$(id -gn) $ROOT" -# geoip/ 由服务器上的定时任务生成,download/ 是 publish_release.sh 上传的安装包,都不在 web/ 里—— -# 排除掉,否则 --delete 会把它们删了 -rsync -az --delete --exclude /geoip/ --exclude /download/ -e "${SSH[*]}" web/ "$HOST:$ROOT/" -"${SSH[@]}" "$HOST" "sudo chmod -R a+rX $ROOT" -echo "已同步到 $HOST:$ROOT" - -# 校验公网:Cloudflare 上拿到的文件大小与本地一致,首页引用的是新指纹 -fail=0 -for f in styles.css replica.css app.js replica.js; do - want=$(stat -f%z "web/$f") - got=$(curl -s -o /dev/null -w '%{size_download}' --max-time 20 "https://$DOMAIN/$f?v=$STAMP" || true) - if [ "$want" = "$got" ]; then - printf " ✅ %-12s %s B\n" "$f" "$got" - else - printf " ❌ %-12s 线上 %s B ≠ 本地 %s B\n" "$f" "$got" "$want" - fail=1 - fi -done -refs=$(curl -s --max-time 20 "https://$DOMAIN/" | grep -c "?v=$STAMP" || true) -if [ "${refs:-0}" -gt 0 ]; then - printf " ✅ 首页 引用 %s 处新指纹\n" "$refs" -else - printf " ❌ 首页仍在引用旧指纹,或域名尚未解析\n" - fail=1 -fi -exit $fail diff --git a/server/geoip/configure.sh b/server/geoip/configure.sh deleted file mode 100755 index c96c2f4..0000000 --- a/server/geoip/configure.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# 把 MaxMind 账号与 License Key 写到服务器的 /etc/openstats/maxmind.env,然后立刻同步一次 GeoLite2 数据库。 -# 值在本机终端里输入(Key 不回显),只经 ssh 传到服务器,不进仓库、不进日志。 -# -# ./server/geoip/configure.sh -set -euo pipefail -HOST="${SITE_HOST:-debian@51.38.126.148}" -KEY="${SITE_KEY:-$HOME/.ssh/gentpan.pem}" -SSH=(ssh -i "$KEY" -o BatchMode=yes "$HOST") - -read -r -p "MaxMind Account ID: " ACCOUNT -read -r -s -p "MaxMind License Key: " LICENSE; echo -[ -n "$ACCOUNT" ] && [ -n "$LICENSE" ] || { echo "两项都要填" >&2; exit 1; } -case "$ACCOUNT$LICENSE" in *[!A-Za-z0-9_]*) echo "账号或 Key 含有意外字符" >&2; exit 1 ;; esac - -# 通过标准输入写入,命令行参数里不出现 Key -printf 'MAXMIND_ACCOUNT_ID=%s\nMAXMIND_LICENSE_KEY=%s\n' "$ACCOUNT" "$LICENSE" \ - | "${SSH[@]}" 'sudo mkdir -p /etc/openstats && sudo tee /etc/openstats/maxmind.env >/dev/null && sudo chmod 600 /etc/openstats/maxmind.env && echo "已写入 /etc/openstats/maxmind.env"' -echo "开始第一次同步(三个库约 80 MB,需要一两分钟)…" -"${SSH[@]}" 'sudo systemctl start openstats-geoip.service && cat /var/www/getopenstats.com/geoip/manifest.json && echo' diff --git a/server/geoip/install.sh b/server/geoip/install.sh deleted file mode 100755 index 1474e43..0000000 --- a/server/geoip/install.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -# 把 GeoLite2 更新脚本与定时任务装到服务器(不包含 License Key)。 -# ./server/geoip/install.sh -set -euo pipefail -cd "$(dirname "$0")" -HOST="${SITE_HOST:-debian@51.38.126.148}" -KEY="${SITE_KEY:-$HOME/.ssh/gentpan.pem}" -SSH=(ssh -i "$KEY" -o BatchMode=yes "$HOST") - -scp -i "$KEY" -o BatchMode=yes openstats-geoip-update.sh openstats-geoip.service openstats-geoip.timer "$HOST:/tmp/" -"${SSH[@]}" 'set -e - sudo install -m 755 /tmp/openstats-geoip-update.sh /usr/local/bin/openstats-geoip-update - sudo install -m 644 /tmp/openstats-geoip.service /tmp/openstats-geoip.timer /etc/systemd/system/ - sudo install -d -m 755 /var/www/getopenstats.com/geoip - # 只保证目录存在,保留已有权限,避免影响同机共用此目录的其他服务。 - sudo mkdir -p /etc/openstats - sudo systemctl daemon-reload - sudo systemctl enable --now openstats-geoip.timer - rm -f /tmp/openstats-geoip-update.sh /tmp/openstats-geoip.service /tmp/openstats-geoip.timer - systemctl list-timers openstats-geoip.timer --no-pager | head -3 - if sudo test -f /etc/openstats/maxmind.env; then echo "已找到 License Key 配置"; else echo "还没有 /etc/openstats/maxmind.env"; fi' diff --git a/server/geoip/openstats-geoip-update.sh b/server/geoip/openstats-geoip-update.sh deleted file mode 100755 index 7c191e4..0000000 --- a/server/geoip/openstats-geoip-update.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash -# 在服务器上定时下载 MaxMind GeoLite2 数据库,放到官网 /geoip/ 下供 OpenStats 更新。 -# -# 账号与 License Key 只放在 /etc/openstats/maxmind.env(root 所有,权限 600), -# 不进仓库、不进应用。文件内容: -# MAXMIND_ACCOUNT_ID=你的账号 -# MAXMIND_LICENSE_KEY=你的 Key -# -# 每个库先下载 tar.gz 与官方 sha256 校验,解出 .mmdb 后原子替换,最后重写 manifest.json。 -set -euo pipefail - -ENV_FILE="${ENV_FILE:-/etc/openstats/maxmind.env}" -OUT="${OUT:-/var/www/getopenstats.com/geoip}" -EDITIONS=(${EDITIONS:-GeoLite2-ASN GeoLite2-Country GeoLite2-City}) - -[ -r "$ENV_FILE" ] || { echo "缺少 $ENV_FILE" >&2; exit 1; } -# shellcheck disable=SC1090 -source "$ENV_FILE" -: "${MAXMIND_ACCOUNT_ID:?}" "${MAXMIND_LICENSE_KEY:?}" - -WORK="$(mktemp -d)" -trap 'rm -rf "$WORK"' EXIT -mkdir -p "$OUT" - -fetch() { - # 凭据通过 curl 配置从标准输入传入,不出现在进程参数里 - printf 'user = "%s:%s"\n' "$MAXMIND_ACCOUNT_ID" "$MAXMIND_LICENSE_KEY" \ - | curl --config - -fsSL --retry 3 -o "$2" "https://download.maxmind.com/geoip/databases/$1/download?suffix=$3" -} - -entries=() -for edition in "${EDITIONS[@]}"; do - fetch "$edition" "$WORK/$edition.tar.gz" "tar.gz" - fetch "$edition" "$WORK/$edition.tar.gz.sha256" "tar.gz.sha256" - expected="$(cut -d' ' -f1 "$WORK/$edition.tar.gz.sha256")" - actual="$(sha256sum "$WORK/$edition.tar.gz" | cut -d' ' -f1)" - [ "$expected" = "$actual" ] || { echo "$edition 校验失败" >&2; exit 1; } - - tar -xzf "$WORK/$edition.tar.gz" -C "$WORK" - folder="$(find "$WORK" -maxdepth 1 -type d -name "${edition}_*" | sort | tail -1)" - build="${folder##*_}" # 目录名形如 GeoLite2-ASN_20260912 - install -m 644 "$folder/$edition.mmdb" "$OUT/.$edition.mmdb.new" - mv -f "$OUT/.$edition.mmdb.new" "$OUT/$edition.mmdb" - - sha="$(sha256sum "$OUT/$edition.mmdb" | cut -d' ' -f1)" - size="$(stat -c %s "$OUT/$edition.mmdb")" - entries+=("{\"edition\":\"$edition\",\"file\":\"$edition.mmdb\",\"build\":\"$build\",\"sha256\":\"$sha\",\"size\":$size}") - echo "$edition $build $size 字节" -done - -joined="$(IFS=,; echo "${entries[*]}")" -printf '{"generated":"%s","attribution":"This product includes GeoLite2 data created by MaxMind, available from https://www.maxmind.com.","databases":[%s]}\n' \ - "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$joined" > "$OUT/.manifest.json.new" -mv -f "$OUT/.manifest.json.new" "$OUT/manifest.json" -chmod 644 "$OUT/manifest.json" -echo "manifest.json 已更新" diff --git a/server/geoip/openstats-geoip.service b/server/geoip/openstats-geoip.service deleted file mode 100644 index 1369b2e..0000000 --- a/server/geoip/openstats-geoip.service +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=下载 MaxMind GeoLite2 数据库供 OpenStats 更新 -After=network-online.target -Wants=network-online.target - -[Service] -Type=oneshot -ExecStart=/usr/local/bin/openstats-geoip-update -# 只需要写官网的 geoip 目录 -ProtectSystem=strict -ReadWritePaths=/var/www/getopenstats.com/geoip /tmp -PrivateTmp=true -NoNewPrivileges=true diff --git a/server/geoip/openstats-geoip.timer b/server/geoip/openstats-geoip.timer deleted file mode 100644 index 06060c9..0000000 --- a/server/geoip/openstats-geoip.timer +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=每周二、周五更新 GeoLite2(MaxMind 在这两天发布新数据) - -[Timer] -OnCalendar=Tue,Fri *-*-* 08:00:00 UTC -RandomizedDelaySec=30m -Persistent=true - -[Install] -WantedBy=timers.target