Исправил загрузку файлов (сравнение контрольных сумм)
This commit is contained in:
608
main.go
608
main.go
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user