Исправил загрузку файлов (сравнение контрольных сумм)

This commit is contained in:
Sergey Kalinin
2025-12-11 18:32:48 +03:00
parent 88e5119dec
commit cee3178af5

608
main.go
View File

@@ -1,53 +1,53 @@
//----------------------------- //-----------------------------
//Distributed under GPL // Distributed under GPL
//Author Sergey Kalinin // Author Sergey Kalinin
//svk@nuk-svk.ru // svk@nuk-svk.ru
//------------------------------ //------------------------------
package main package main
import ( import (
"crypto/sha256"
"encoding/hex"
"encoding/json" "encoding/json"
"fmt"
"html/template" "html/template"
"io"
"log" "log"
"net" "net"
"net/http" "net/http"
"os" "strings"
"path/filepath" "os"
"strings" "io"
"sync" "path/filepath"
"time" "fmt"
"time"
"sync"
"encoding/hex"
"crypto/sha256"
"github.com/go-co-op/gocron"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/likexian/whois"
"github.com/oschwald/geoip2-golang" "github.com/oschwald/geoip2-golang"
"golang.org/x/text/language" "golang.org/x/text/language"
"github.com/likexian/whois"
"github.com/go-co-op/gocron"
) )
type GeoData struct { type GeoData struct {
// IP адрес // IP адрес
IPAddress string `json:"ip_address"` IPAddress string `json:"ip_address"`
// Местоположение // Местоположение
Location struct { Location struct {
Country string `json:"country,omitempty"` Country string `json:"country,omitempty"`
City string `json:"city,omitempty"` City string `json:"city,omitempty"`
Continent string `json:"continent,omitempty"` Continent string `json:"continent,omitempty"`
Region string `json:"region,omitempty"` Region string `json:"region,omitempty"`
RegionISO string `json:"region_iso,omitempty"` RegionISO string `json:"region_iso,omitempty"`
Latitude float64 `json:"latitude,omitempty"` Latitude float64 `json:"latitude,omitempty"`
Longitude float64 `json:"longitude,omitempty"` Longitude float64 `json:"longitude,omitempty"`
TimeZone string `json:"time_zone,omitempty"` TimeZone string `json:"time_zone,omitempty"`
PostalCode string `json:"postal_code,omitempty"` PostalCode string `json:"postal_code,omitempty"`
} `json:"location"` } `json:"location"`
// Данные о сети // Данные о сети
Network struct { Network struct {
ASN uint `json:"asn,omitempty"` ASN uint `json:"asn,omitempty"`
ASOrg string `json:"autonomous_system_organization,omitempty"` ASOrg string `json:"autonomous_system_organization,omitempty"`
} `json:"network"` } `json:"network"`
// ISO коды // ISO коды
Codes struct { Codes struct {
@@ -71,9 +71,9 @@ type TemplateData struct {
*GeoData *GeoData
Error string Error string
JSONData string JSONData string
WhoIsData string WhoIsData string
BaseURL string BaseURL string
DBVersion string DBVersion string
} }
type Config struct { type Config struct {
@@ -84,31 +84,33 @@ type Config struct {
} }
var ( var (
cityDB *geoip2.Reader cityDB *geoip2.Reader
countryDB *geoip2.Reader countryDB *geoip2.Reader
asnDB *geoip2.Reader asnDB *geoip2.Reader
templates *template.Template templates *template.Template
cfg *Config cfg *Config
dbMutex sync.RWMutex dbMutex sync.RWMutex
dbFilesVersion string dbFilesVersion string
) )
func main() { func main() {
var err error var err error
// Загрузка конфигурации // Загрузка конфигурации
cfg = LoadConfig() cfg = LoadConfig()
// Запускаем скачивание файлов // Запускаем скачивание файлов
mmdbDownload("GeoLite2-City.mmdb") mmdbDownload("GeoLite2-City.mmdb")
mmdbDownload("GeoLite2-Country.mmdb") mmdbDownload("GeoLite2-Country.mmdb")
mmdbDownload("GeoLite2-ASN.mmdb") mmdbDownload("GeoLite2-ASN.mmdb")
// Запускаем планировщик параллельно с основным процессом // Запускаем планировщик параллельно с основным процессом
go sheduler() go sheduler()
// Открываем БД // Открываем БД
pathGeoLite2City := filepath.Join(cfg.MMDBLocalPath, "GeoLite2-City.mmdb") pathGeoLite2City := filepath.Join(cfg.MMDBLocalPath, "GeoLite2-City.mmdb")
log.Println(pathGeoLite2City) log.Println(pathGeoLite2City)
cityDB, err = geoip2.Open(pathGeoLite2City) cityDB, err = geoip2.Open(pathGeoLite2City)
if err != nil { if err != nil {
@@ -116,26 +118,26 @@ func main() {
} }
defer cityDB.Close() defer cityDB.Close()
pathGeoLite2Country := filepath.Join(cfg.MMDBLocalPath, "GeoLite2-Country.mmdb") pathGeoLite2Country := filepath.Join(cfg.MMDBLocalPath,"GeoLite2-Country.mmdb")
countryDB, err = geoip2.Open(pathGeoLite2Country) countryDB, err = geoip2.Open(pathGeoLite2Country)
if err != nil { if err != nil {
log.Fatal("Failed to open Country database:", err) log.Fatal("Failed to open Country database:", err)
} }
defer countryDB.Close() defer countryDB.Close()
pathGeoLite2ASN := filepath.Join(cfg.MMDBLocalPath, "GeoLite2-ASN.mmdb") pathGeoLite2ASN := filepath.Join(cfg.MMDBLocalPath, "GeoLite2-ASN.mmdb")
asnDB, err = geoip2.Open(pathGeoLite2ASN) asnDB, err = geoip2.Open(pathGeoLite2ASN)
if err != nil { if err != nil {
log.Fatal("Failed to open ASN database:", err) log.Fatal("Failed to open ASN database:", err)
} }
defer asnDB.Close() defer asnDB.Close()
dbFilesVersion = getFileModTime(pathGeoLite2City) dbFilesVersion = getFileModTime(pathGeoLite2City)
// Загружаем шаблон // Загружаем шаблон
templates = template.Must(template.ParseFiles(filepath.Join(cfg.HTMLTemplatePath, "index.html"))) templates = template.Must(template.ParseFiles(filepath.Join(cfg.HTMLTemplatePath,"index.html")))
// Обработка ссылок // Обработка ссылок
r := mux.NewRouter() r := mux.NewRouter()
r.HandleFunc("/", homeHandler) r.HandleFunc("/", homeHandler)
r.HandleFunc("/api/{ip}", apiHandler) r.HandleFunc("/api/{ip}", apiHandler)
@@ -143,107 +145,106 @@ func main() {
r.HandleFunc("/lookup/{ip}", lookupHandler) r.HandleFunc("/lookup/{ip}", lookupHandler)
r.HandleFunc("/{ip}", lookupHandler) r.HandleFunc("/{ip}", lookupHandler)
log.Println("Server starting on:", cfg.ListenPort) log.Println("Server starting on:",cfg.ListenPort)
log.Fatal(http.ListenAndServe(":"+cfg.ListenPort, r)) log.Fatal(http.ListenAndServe(":" + cfg.ListenPort, r))
} }
func getFileModTime(filePath string) string { func getFileModTime(filePath string) string {
// Получаем информацию о файле // Получаем информацию о файле
fileInfo, err := os.Stat(filePath) fileInfo, err := os.Stat(filePath)
if err != nil { if err != nil {
log.Println("Ошибка в getFileModTime:", err) log.Println("Ошибка в getFileModTime:", err)
return "Error" return "Error"
} }
// Получаем время последнего изменения // Получаем время последнего изменения
modTime := fileInfo.ModTime() modTime := fileInfo.ModTime()
// fmt.Println("Время изменения файла:", modTime) // fmt.Println("Время изменения файла:", modTime)
// Форматируем вывод // Форматируем вывод
// fmt.Printf("Форматированное время: %s\n", modTime.Format("02/01/2006 15:04:05")) // fmt.Printf("Форматированное время: %s\n", modTime.Format("02/01/2006 15:04:05"))
return modTime.Format("02/01/2006 15:04:05") return modTime.Format("02/01/2006 15:04:05")
} }
// reopenDBs закрывает и заново открывает нужную БД // reopenDBs закрывает и заново открывает нужную БД
func reopenDBs(fileName string) { func reopenDBs(fileName string) {
dbMutex.Lock() dbMutex.Lock()
defer dbMutex.Unlock() defer dbMutex.Unlock()
var err error var err error
switch fileName { switch fileName {
case "GeoLite2-City.mmdb": case "GeoLite2-City.mmdb":
if cityDB != nil { if cityDB != nil {
cityDB.Close() cityDB.Close()
} }
// Открываем базы данных заново // Открываем базы данных заново
pathGeoLite2City := filepath.Join(cfg.MMDBLocalPath, fileName) pathGeoLite2City := filepath.Join(cfg.MMDBLocalPath, fileName)
cityDB, err = geoip2.Open(pathGeoLite2City) cityDB, err = geoip2.Open(pathGeoLite2City)
if err != nil { if err != nil {
log.Printf("Ошибка открытия базы данных %v: %v", fileName, err) log.Printf("Ошибка открытия базы данных %v: %v", fileName, err)
return return
} }
case "GeoLite2-Country.mmdb": case "GeoLite2-Country.mmdb":
if countryDB != nil { if countryDB != nil {
countryDB.Close() countryDB.Close()
} }
pathGeoLite2Country := filepath.Join(cfg.MMDBLocalPath, fileName) pathGeoLite2Country := filepath.Join(cfg.MMDBLocalPath, fileName)
countryDB, err = geoip2.Open(pathGeoLite2Country) countryDB, err = geoip2.Open(pathGeoLite2Country)
if err != nil { if err != nil {
log.Printf("Ошибка открытия базы данных %v: %v", fileName, err) log.Printf("Ошибка открытия базы данных %v: %v", fileName, err)
return return
} }
case "GeoLite2-ASN.mmdb": case "GeoLite2-ASN.mmdb":
if asnDB != nil { if asnDB != nil {
asnDB.Close() asnDB.Close()
} }
pathGeoLite2ASN := filepath.Join(cfg.MMDBLocalPath, fileName) pathGeoLite2ASN := filepath.Join(cfg.MMDBLocalPath, fileName)
asnDB, err = geoip2.Open(pathGeoLite2ASN) asnDB, err = geoip2.Open(pathGeoLite2ASN)
if err != nil { if err != nil {
log.Printf("Ошибка открытия базы данных %v: %v", fileName, err) log.Printf("Ошибка открытия базы данных %v: %v", fileName, err)
return return
} }
} }
log.Println("Открыта база данных:", fileName) log.Println("Открыта база данных:", fileName)
dbFilesVersion = getFileModTime(filepath.Join(cfg.MMDBLocalPath, fileName)) dbFilesVersion = getFileModTime(filepath.Join(cfg.MMDBLocalPath, fileName))
} }
func homeHandler(w http.ResponseWriter, r *http.Request) { func homeHandler(w http.ResponseWriter, r *http.Request) {
baseURL := getBaseURL(r) baseURL := getBaseURL(r)
// Обрабатываем параметр ip из GET запроса // Обрабатываем параметр ip из GET запроса
ipStr := r.URL.Query().Get("ip") ipStr := r.URL.Query().Get("ip")
var geoData *GeoData var geoData *GeoData
var errMsg string var errMsg string
var whoisData string var whoisData string
if ipStr != "" { if ipStr != "" {
ip := net.ParseIP(ipStr) ip := net.ParseIP(ipStr)
if ip == nil { if ip == nil {
errMsg = "Неверный IP адрес: " + ipStr errMsg = "Неверный IP адрес: " + ipStr
} else { } else {
preferredLocale := getPreferredLocale(r) preferredLocale := getPreferredLocale(r)
var err error var err error
geoData, err = getGeoData(ip, preferredLocale) geoData, err = getGeoData(ip, preferredLocale)
if err != nil { if err != nil {
errMsg = err.Error() errMsg = err.Error()
} }
whoisData = getWhoIsInfo(ipStr) whoisData = getWhoIsInfo(ipStr)
} }
} }
data := TemplateData{ data := TemplateData{
GeoData: geoData, GeoData: geoData,
WhoIsData: whoisData, WhoIsData: whoisData,
Error: errMsg, Error: errMsg,
BaseURL: baseURL, BaseURL: baseURL,
DBVersion: dbFilesVersion, DBVersion: dbFilesVersion,
} }
err := templates.ExecuteTemplate(w, "index.html", data) err := templates.ExecuteTemplate(w, "index.html", data)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
} }
} }
func apiCurrentHandler(w http.ResponseWriter, r *http.Request) { func apiCurrentHandler(w http.ResponseWriter, r *http.Request) {
@@ -292,15 +293,15 @@ func apiHandler(w http.ResponseWriter, r *http.Request) {
func lookupHandler(w http.ResponseWriter, r *http.Request) { func lookupHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
ipStr := vars["ip"] ipStr := vars["ip"]
baseURL := getBaseURL(r) baseURL := getBaseURL(r)
// Проверка IP // Проверка IP
ip := net.ParseIP(ipStr) ip := net.ParseIP(ipStr)
if ip == nil { if ip == nil {
// Если IP невалидный, показываем ошибку на странице // Если IP невалидный, показываем ошибку на странице
templates.ExecuteTemplate(w, "index.html", TemplateData{ templates.ExecuteTemplate(w, "index.html", TemplateData{
Error: "Invalid IP address: " + ipStr, Error: "Invalid IP address: " + ipStr,
BaseURL: baseURL, BaseURL: baseURL,
}) })
return return
} }
@@ -310,8 +311,8 @@ func lookupHandler(w http.ResponseWriter, r *http.Request) {
geoData, err := getGeoData(ip, preferredLocale) geoData, err := getGeoData(ip, preferredLocale)
if err != nil { if err != nil {
templates.ExecuteTemplate(w, "index.html", TemplateData{ templates.ExecuteTemplate(w, "index.html", TemplateData{
Error: err.Error(), Error: err.Error(),
BaseURL: baseURL, BaseURL: baseURL,
}) })
return return
} }
@@ -321,15 +322,15 @@ func lookupHandler(w http.ResponseWriter, r *http.Request) {
jsonData = []byte("{\"error\": \"Failed to generate JSON\"}") jsonData = []byte("{\"error\": \"Failed to generate JSON\"}")
} }
whoisData := getWhoIsInfo(ipStr) whoisData := getWhoIsInfo(ipStr)
// Передаем данные в шаблон // Передаем данные в шаблон
templates.ExecuteTemplate(w, "index.html", TemplateData{ templates.ExecuteTemplate(w, "index.html", TemplateData{
GeoData: geoData, GeoData: geoData,
JSONData: string(jsonData), JSONData: string(jsonData),
WhoIsData: whoisData, WhoIsData: whoisData,
BaseURL: baseURL, BaseURL: baseURL,
DBVersion: dbFilesVersion, DBVersion: dbFilesVersion,
}) })
} }
@@ -362,6 +363,7 @@ func getPreferredLocale(r *http.Request) string {
return "en" return "en"
} }
func sendJSONResponse(w http.ResponseWriter, data interface{}) { func sendJSONResponse(w http.ResponseWriter, data interface{}) {
w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Origin", "*")
@@ -436,18 +438,18 @@ func getLocalizedString(names map[string]string, preferredLocale string) string
} }
func hasRegisteredCountryData(registeredCountry struct { func hasRegisteredCountryData(registeredCountry struct {
Names map[string]string `maxminddb:"names"` Names map[string]string `maxminddb:"names"`
IsoCode string `maxminddb:"iso_code"` IsoCode string `maxminddb:"iso_code"`
GeoNameID uint `maxminddb:"geoname_id"` GeoNameID uint `maxminddb:"geoname_id"`
IsInEuropeanUnion bool `maxminddb:"is_in_european_union"` IsInEuropeanUnion bool `maxminddb:"is_in_european_union"`
}) bool { }) bool {
return registeredCountry.IsoCode != "" || len(registeredCountry.Names) > 0 return registeredCountry.IsoCode != "" || len(registeredCountry.Names) > 0
} }
// Получаем данные из БД MaxMind // Получаем данные из БД MaxMind
func getGeoData(ip net.IP, preferredLocale string) (*GeoData, error) { func getGeoData(ip net.IP, preferredLocale string) (*GeoData, error) {
dbMutex.RLock() dbMutex.RLock()
defer dbMutex.RUnlock() defer dbMutex.RUnlock()
data := &GeoData{} data := &GeoData{}
data.IPAddress = ip.String() data.IPAddress = ip.String()
@@ -497,11 +499,11 @@ func getGeoData(ip net.IP, preferredLocale string) (*GeoData, error) {
data.Metadata.DataSource = "country" data.Metadata.DataSource = "country"
} }
// Сперва определяем страну (main country) // Сперва определяем страну (main country)
countryName := getLocalizedString(country.Country.Names, preferredLocale) countryName := getLocalizedString(country.Country.Names, preferredLocale)
countryISO := country.Country.IsoCode countryISO := country.Country.IsoCode
// Если (main country) пустое то пробуем (registered country) // Если (main country) пустое то пробуем (registered country)
if countryName == "" && hasRegisteredCountryData(country.RegisteredCountry) { if countryName == "" && hasRegisteredCountryData(country.RegisteredCountry) {
countryName = getLocalizedString(country.RegisteredCountry.Names, preferredLocale) countryName = getLocalizedString(country.RegisteredCountry.Names, preferredLocale)
if countryISO == "" { if countryISO == "" {
@@ -545,31 +547,32 @@ func getGeoData(ip net.IP, preferredLocale string) (*GeoData, error) {
// Определяем базовый URL сайта где запуцщен проект // Определяем базовый URL сайта где запуцщен проект
func getBaseURL(r *http.Request) string { func getBaseURL(r *http.Request) string {
scheme := "http" scheme := "http"
if r.TLS != nil || r.Header.Get("X-Forwarded-Proto") == "https" { if r.TLS != nil || r.Header.Get("X-Forwarded-Proto") == "https" {
scheme = "https" scheme = "https"
} }
host := r.Host host := r.Host
// Убираем порт если это стандартный порт // Убираем порт если это стандартный порт
if strings.HasSuffix(host, ":80") && scheme == "http" { if strings.HasSuffix(host, ":80") && scheme == "http" {
host = strings.TrimSuffix(host, ":80") host = strings.TrimSuffix(host, ":80")
} else if strings.HasSuffix(host, ":443") && scheme == "https" { } else if strings.HasSuffix(host, ":443") && scheme == "https" {
host = strings.TrimSuffix(host, ":443") host = strings.TrimSuffix(host, ":443")
} }
return scheme + "://" + host return scheme + "://" + host
} }
// Запрос информации с серверов whois // Запрос информации с серверов whois
func getWhoIsInfo(address string) string { func getWhoIsInfo(address string) string {
result, err := whois.Whois(address) result, err := whois.Whois(address)
if err != nil { if err != nil {
return "WhoIs request error" return "WhoIs request error"
} }
return result
return result
} }
// Загрузка конфигурации // Загрузка конфигурации
@@ -590,188 +593,191 @@ func getEnv(key, defaultValue string) string {
return defaultValue return defaultValue
} }
// Запуск задания скачивания файлов // Запуск задания скачивания файлов
func sheduler() { func sheduler() {
// инициализируем объект планировщика // инициализируем объект планировщика
s := gocron.NewScheduler(time.UTC) s := gocron.NewScheduler(time.UTC)
// добавляем одну задачу на каждую минуту // добавляем одну задачу на каждую минуту
s.Cron("30 2 * * *").Do(startDownload) s.Cron("30 2 * * *").Do(startDownload)
// s.Cron("*/1 * * * *").Do(startDownload) // s.Cron("*/1 * * * *").Do(startDownload)
// запускаем планировщик с блокировкой текущего потока // запускаем планировщик с блокировкой текущего потока
s.StartBlocking() s.StartBlocking()
} }
// Отслеживает прогресс загрузки // Отслеживает прогресс загрузки
type WriteCounter struct { type WriteCounter struct {
Total uint64 Total uint64
} }
// Параллельный запуск загрузки БД // Параллельный запуск загрузки БД
func startDownload() { func startDownload() {
go mmdbDownload("GeoLite2-City.mmdb") go mmdbDownload("GeoLite2-City.mmdb")
go mmdbDownload("GeoLite2-Country.mmdb") go mmdbDownload("GeoLite2-Country.mmdb")
go mmdbDownload("GeoLite2-ASN.mmdb") go mmdbDownload("GeoLite2-ASN.mmdb")
} }
func (wc *WriteCounter) Write(p []byte) (int, error) { func (wc *WriteCounter) Write(p []byte) (int, error) {
n := len(p) n := len(p)
wc.Total += uint64(n) wc.Total += uint64(n)
wc.PrintProgress() wc.PrintProgress()
return n, nil return n, nil
} }
func (wc WriteCounter) PrintProgress() { func (wc WriteCounter) PrintProgress() {
fmt.Printf("\rЗагружено %d байт...", wc.Total) fmt.Printf("\rЗагружено %d байт...", wc.Total)
} }
// Проверка целостности файла. // Проверка целостности файла.
func isValidMMDB(filePath string) bool { func isValidMMDB(filePath string) bool {
db, err := geoip2.Open(filePath) db, err := geoip2.Open(filePath)
if err != nil { if err != nil {
return false return false
} }
db.Close() db.Close()
return true return true
} }
// Проверяем контрольную сумму локального файла // Проверяем контрольную сумму локального файла
func getFileChecksum(filePath string) (string, error) { func getFileChecksum(filePath string) (string, error) {
file, err := os.Open(filePath) file, err := os.Open(filePath)
if err != nil { if err != nil {
return "", err return "", err
} }
defer file.Close() defer file.Close()
hash := sha256.New() hash := sha256.New()
if _, err := io.Copy(hash, file); err != nil { if _, err := io.Copy(hash, file); err != nil {
return "", err return "", err
} }
return hex.EncodeToString(hash.Sum(nil)), nil return hex.EncodeToString(hash.Sum(nil)), nil
} }
// Проверяем контрольную сумму удаленного файла (скачиваемого) // Проверяем контрольную сумму удаленного файла (скачиваемого)
func getRemoteChecksum(url string) string { func getRemoteChecksum(url string) string {
resp, err := http.Get(url) resp, err := http.Get(url)
if err != nil { if err != nil {
return "" return ""
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return "" return ""
} }
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return "" return ""
} }
// Так как запрос возвращает сумму и путь к файлу то
// разбиваем строку на массив и берем первый элемент
words := strings.Fields(string(body))
checkSum := words[0]
// Предполагаем, что файл содержит только хеш return strings.TrimSpace(checkSum)
return strings.TrimSpace(string(body))
} }
func shouldDownload(fileName string) bool { func shouldDownload(fileName string) bool {
log.Printf("Проверяем контрольную сумму файла %s", fileName) log.Printf("Проверяем контрольную сумму файла %s", fileName)
fileURL := cfg.MMDBURL + fileName fileURL := cfg.MMDBURL + fileName
filePath := filepath.Join(cfg.MMDBLocalPath, fileName) filePath := filepath.Join(cfg.MMDBLocalPath, fileName)
// Если файла нет, нужно скачивать // Если файла нет, нужно скачивать
if _, err := os.Stat(filePath); os.IsNotExist(err) { if _, err := os.Stat(filePath); os.IsNotExist(err) {
return true return true
} }
// Проверяем возраст файла (скачиваем если старше 7 дней) // Проверяем возраст файла (скачиваем если старше 7 дней)
/* /*
if info, err := os.Stat(filePath); err == nil { if info, err := os.Stat(filePath); err == nil {
if time.Since(info.ModTime()) > 7*24*time.Hour { if time.Since(info.ModTime()) > 7*24*time.Hour {
return true return true
} }
} }
*/ */
// Проверка по контрольной сумме // Проверка по контрольной сумме
if checksumURL := fileURL + ".sha256"; checksumURL != "" { if checksumURL := fileURL + ".sha256"; checksumURL != "" {
remoteFileChecksum := getRemoteChecksum(checksumURL) remoteFileChecksum := getRemoteChecksum(checksumURL)
// fmt.Println("Remote", remoteFileChecksum) // fmt.Println("Remote", remoteFileChecksum)
if remoteFileChecksum != "" { if remoteFileChecksum != "" {
localFileChecksum, error := getFileChecksum(filePath) localFileChecksum, error := getFileChecksum(filePath)
if error == nil { if error == nil {
if localFileChecksum != "" { if localFileChecksum != "" {
fmt.Println(filePath, "Remote:", remoteFileChecksum, "Local:", localFileChecksum) fmt.Println(filePath, "Remote:", remoteFileChecksum, "Local:", localFileChecksum)
fmt.Println(remoteFileChecksum) fmt.Println(remoteFileChecksum)
fmt.Println(localFileChecksum) fmt.Println(localFileChecksum)
if localFileChecksum != remoteFileChecksum { if localFileChecksum != remoteFileChecksum {
return false return true
} }
} }
} else { } else {
fmt.Println("Error", filePath, error) fmt.Println("Error", filePath, error)
return true return true
} }
} }
} }
return false return false
} }
// Загрузка файлов БД с ВЭБ-сервера // Загрузка файлов БД с ВЭБ-сервера
func mmdbDownload(fileName string) { func mmdbDownload(fileName string) {
fileURL := cfg.MMDBURL + fileName fileURL := cfg.MMDBURL + fileName
filePath := filepath.Join(cfg.MMDBLocalPath, fileName) filePath := filepath.Join(cfg.MMDBLocalPath, fileName)
if !shouldDownload(fileName) { if !shouldDownload(fileName) {
log.Printf("Файл %s актуален, пропускаем загрузку", fileName) log.Printf("Файл %s актуален, пропускаем загрузку", fileName)
return return
} }
// Скачиваем во временный файл // Скачиваем во временный файл
tempPath := filePath + ".tmp" tempPath := filePath + ".tmp"
log.Println("Загружается файл:", fileURL) log.Println("Загружается файл:",fileURL)
// Создаём файл // Создаём файл
file, err := os.Create(tempPath) file, err := os.Create(tempPath)
if err != nil { if err != nil {
log.Printf("Ошибка создания файла %s: %v", tempPath, err) log.Printf("Ошибка создания файла %s: %v", tempPath, err)
return return
} }
defer file.Close() defer file.Close()
// Загружаем данные с отслеживанием прогресса // Загружаем данные с отслеживанием прогресса
counter := &WriteCounter{} counter := &WriteCounter{}
response, err := http.Get(fileURL) response, err := http.Get(fileURL)
if err != nil { if err != nil {
log.Printf("Ошибка загрузки файла %s: %v", fileURL, err) log.Printf("Ошибка загрузки файла %s: %v", fileURL, err)
return return
} }
defer response.Body.Close() defer response.Body.Close()
if response.StatusCode != http.StatusOK { if response.StatusCode != http.StatusOK {
log.Printf("Ошибка загрузки: %s", response.Status) log.Printf("Ошибка загрузки: %s", response.Status)
return return
} }
// Копируем через буфер с отслеживанием прогресса // Копируем через буфер с отслеживанием прогресса
_, err = io.Copy(file, io.TeeReader(response.Body, counter)) _, err = io.Copy(file, io.TeeReader(response.Body, counter))
if err != nil { if err != nil {
log.Printf("Ошибка копирования данных: %v", err) log.Printf("Ошибка копирования данных: %v", err)
return return
} }
log.Println("Загрузка завершена! Файл сохранен:", tempPath) log.Println("Загрузка завершена! Файл сохранен:", tempPath)
// Переоткрываем базы данных после успешной загрузки // Переоткрываем базы данных после успешной загрузки
// reopenDBs(fileName) // reopenDBs(fileName)
// Проверяем валидность перед заменой // Проверяем валидность перед заменой
if isValidMMDB(tempPath) { if isValidMMDB(tempPath) {
// Заменяем старый файл // Заменяем старый файл
os.Rename(tempPath, filePath) os.Rename(tempPath, filePath)
// Переоткрываем только эту базу // Переоткрываем только эту базу
reopenDBs(fileName) reopenDBs(fileName)
} else { } else {
log.Printf("Скачанный файл %s поврежден, удаляем", fileName) log.Printf("Скачанный файл %s поврежден, удаляем", fileName)
os.Remove(tempPath) os.Remove(tempPath)
} }
} }