feat(netflow): локальные GeoLite2-базы для стран и ASN потоков
Docker images / prepare-release (push) Successful in 13s
Docker images / backend-test (push) Successful in 2m13s
Docker images / frontend-image (push) Successful in 4m31s
Docker images / updater-image (push) Successful in 48s
Docker images / backend-image (push) Successful in 3m1s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 15s

Lookup страны/ASN при ingest теперь идёт сначала по локальным mmdb
MaxMind GeoLite2 (Country + ASN, зеркало P3TERX, без ключей), с
мгновенным синхронным ответом для IPv4/IPv6; RIPEstat остаётся
fallback до первой загрузки баз и при промахе.

- backend/src/services/traffic-flow-geoip.ts: ридеры maxmind, фасад
  resolveFlowIp (geoip-first → RIPE-кэш), статус ридеров
- geoip-update-collector.ts: conditional GET по ETag, валидация пробоем
  8.8.8.8 (US / AS15169), атомарная подмена с .prev-откатом
- джоба планировщика geoip_update (по умолчанию раз в 7 дней),
  настройки geoip_settings + миграция 0004, API /api/geoip
  (GET/PUT/update), контракты @mmapp/contracts/geoip
- engine/analytics/map-hops переведены на resolveFlowIp
- секция «GeoIP-базы» в настройках NetFlow, метки джобы на странице
  сбора данных, тесты test:geoip
This commit is contained in:
Denozordec
2026-09-09 23:33:31 +07:00
parent d39e3454aa
commit a80caf5676
25 changed files with 1114 additions and 23 deletions
+14
View File
@@ -231,6 +231,20 @@ export const flowBuckets = pgTable("flow_buckets", {
index("idx_flow_buckets_server_time").on(t.serverId, t.bucketAt),
])
export const geoipSettings = pgTable("geoip_settings", {
id: idSingleton(),
enabled: boolean("enabled").notNull().default(true),
updateIntervalSec: integer("update_interval_sec").notNull().default(604800),
lastCheckAt: ts("last_check_at"),
lastSuccessAt: ts("last_success_at"),
lastError: text("last_error"),
countryBuildAt: ts("country_build_at"),
asnBuildAt: ts("asn_build_at"),
etagsJson: jsonb("etags_json").notNull().default(sql`'{}'::jsonb`),
createdAt: ts("created_at").notNull().defaultNow(),
updatedAt: ts("updated_at").notNull().defaultNow(),
})
export const flowIpMeta = pgTable("flow_ip_meta", {
prefix: text("prefix").primaryKey(),
asn: integer("asn").notNull().default(0),