diff --git a/README.md b/README.md index 3974783..7a0fef7 100644 --- a/README.md +++ b/README.md @@ -665,3 +665,16 @@ IP посетителей не отправляются во внешние се в истории и списке IP; местоположение приблизительное и может относиться к VPN или провайдеру. Для части IP город неизвестен. База устанавливается отдельно от релиза, автоматически каждый месяц не обновляется. + +## Фильтр истории и зеркало GeoIP (v0.25.1) + +В окнах стран и событий есть локальный переключатель «Все события / HTTP / +Срабатывания / Решения CrowdSec». Он меняет тип событий только внутри окна, +сохраняя страну, домен, остальные фильтры и снимок времени; пагинация начинается +с первой страницы. Сводные карточки над историей сохраняют исходные итоги. + +DB-IP City Lite загружается сначала из вложения релиза v0.25.1 в Gitea, +при ошибке — с официального сервера DB-IP. Для собственного адреса Gitea можно +задать `GITEA_URL` в окружении сервиса Dashboard. База хранится отдельно от Git-истории +и бинарника приложения; контрольная сумма проверяется независимо от источника. +Официальный файл: https://download.db-ip.com/free/dbip-city-lite-2026-10.mmdb.gz diff --git a/assets/traffic.html b/assets/traffic.html index bd0254d..957e669 100644 --- a/assets/traffic.html +++ b/assets/traffic.html @@ -26,7 +26,7 @@ html[data-theme="light"] .metric i{background:#e2f2e7}html[data-theme="light"] . html[data-theme="light"] .section-label,html[data-theme="light"] .event .path,html[data-theme="light"] dialog p{color:var(--text)}html[data-theme="light"] .event .meta{color:var(--muted)}html[data-theme="light"] .event{border-color:var(--line)}html[data-theme="light"] .event:hover{background:#eaf1f4} html[data-theme="light"] dialog,html[data-theme="light"] .tooltip,html[data-theme="light"] #networkSummary{background:#fff;color:var(--text);border-color:var(--line);box-shadow:0 12px 40px #4e64752b}html[data-theme="light"] dialog::backdrop{background:#1e2d3759}html[data-theme="light"] .monitoring-tag{color:var(--green)} @media(max-width:760px){header nav{gap:5px}#themeToggle{padding:5px}.map-controls{bottom:3px;right:3px}.map-controls button{padding:4px 8px}} -.history-pager{display:flex;align-items:center;justify-content:space-between;gap:12px;position:sticky;bottom:-22px;background:var(--panel);padding:12px 0}dialog{width:min(760px,calc(100vw - 30px))} +#historyFilters{display:flex;flex-wrap:wrap;gap:6px;margin:16px 0}#historyFilters[hidden]{display:none}#historyFilters button[aria-pressed="true"]{border-color:var(--blue);background:var(--panel);box-shadow:inset 0 -2px currentColor}.history-pager{display:flex;align-items:center;justify-content:space-between;gap:12px;position:sticky;bottom:-22px;background:var(--panel);padding:12px 0}dialog{width:min(760px,calc(100vw - 30px))}
'+explanation+'
Загружаем историю…
Загружаем историю…
IP не найдены.
'):eventList(d.events);el('historyPanel').innerHTML=''+num(start)+'–'+num(end)+' из '+num(d.total)+' · последние 24 часа на момент открытия. '+(d.cityReady?'Город/регион приблизительны; отсутствие названия означает, что база их не определила.':'Для городов установите базу GeoIP в настройках карты.')+'
'+body+''+safe(e.message)+'
'}finally{if(version===historyVersion)historyBusy=false}} el('details').addEventListener('close',()=>{historyVersion++;historyBusy=false;historyQuery=null}); applyTrafficTheme(document.documentElement.dataset.theme||'dark');clampMap(); diff --git a/traffic_city.go b/traffic_city.go index 5d37dad..64806eb 100644 --- a/traffic_city.go +++ b/traffic_city.go @@ -101,9 +101,25 @@ func trafficCityRoutes(mux *http.ServeMux) { writeJSON(w, status, nil) }) } -func installTrafficCity() error { return installTrafficCityFrom(cityDownloadURL, cityDatabaseSHA1) } +func cityMirrorURL() string { + base := strings.TrimRight(os.Getenv("GITEA_URL"), "/") + if base == "" { + base = "https://git.myown.center" + } + return base + "/maxim/ProxmoxDash/releases/download/v0.25.1/dbip-city-lite-2026-10.mmdb.gz" +} +func installTrafficCity() error { + if err := installTrafficCityFrom(cityMirrorURL(), cityDatabaseSHA1); err == nil { + return nil + } + return installTrafficCityFrom(cityDownloadURL, cityDatabaseSHA1) +} func installTrafficCityFrom(source, expectedHash string) error { - client := &http.Client{Timeout: 15 * time.Minute} + transport := http.DefaultTransport.(*http.Transport).Clone() + transport.ResponseHeaderTimeout = 20 * time.Second + transport.DialContext = (&net.Dialer{Timeout: 10 * time.Second, KeepAlive: 30 * time.Second}).DialContext + defer transport.CloseIdleConnections() + client := &http.Client{Timeout: 15 * time.Minute, Transport: transport} response, err := client.Get(source) if err != nil { return err