package mmdb import ( "errors" "net/netip" "git.kor-elf.net/kor-elf-shield/geoip2" "git.kor-elf.net/kor-elf-shield/geoip2/internal/pkg" "git.kor-elf.net/kor-elf-shield/geoip2/internal/service/maxmind/mmdb" oschwaldGeoip2 "github.com/oschwald/geoip2-golang/v2" ) const ( DownloadURLCityLite = "https://download.maxmind.com/geoip/databases/GeoLite2-City/download?suffix=tar.gz" NameDirNameCityLite = "GeoLite2-City" ) // City is a structure that contains information about the IP address. type City struct { // lang is a language. lang language } // NewCity creates a new City instance. // @param download - an interface for downloading MaxMind GeoIP2 database // @param logger - a logger // @param dir - a directory for storing MaxMind GeoIP2 database // @return geoip2.RefreshableGeoIP2 - a MaxMind GeoIP2 database service func NewCity(download *Download, logger geoip2.Logger, dir string, language language) geoip2.RefreshableGeoIP2 { city := &City{ lang: language, } newDir := pkg.NewDir(dir, NameDirNameCityLite) return mmdb.NewMMDB(download, city.infoCity, logger, newDir) } // infoCity returns information about the IP address. // @param ip - IP address // @param reader - MaxMind GeoIP2 database reader // @return geoip2.Info - information about the IP address // @return error - an error if the IP address could not be found func (c *City) infoCity(ip netip.Addr, reader *oschwaldGeoip2.Reader) (geoip2.Info, error) { record, err := reader.City(ip) if err != nil { return geoip2.Info{}, err } if !record.HasData() { return geoip2.Info{}, errors.New(geoip2.ErrNotFound) } var timeZone string if record.Location.HasData() { timeZone = record.Location.TimeZone } return geoip2.Info{ IP: ip, ISOCode: record.Country.ISOCode, Country: c.country(record.Country), City: c.city(record.City), CitySubdivisions: c.subdivisions(record.Subdivisions), TimeZone: timeZone, Continent: c.continent(record.Continent), }, nil } func (c *City) continent(continent oschwaldGeoip2.Continent) string { if !continent.HasData() { return "" } switch c.lang { case LanguageRussian: return continent.Names.Russian case LanguageEnglish: return continent.Names.English case LanguageBrazilianPortuguese: return continent.Names.BrazilianPortuguese case LanguageSimplifiedChinese: return continent.Names.SimplifiedChinese case LanguageJapanese: return continent.Names.Japanese case LanguageGerman: return continent.Names.German case LanguageFrench: return continent.Names.French case LanguageSpanish: return continent.Names.Spanish default: return continent.Names.English } } func (c *City) country(country oschwaldGeoip2.CountryRecord) string { if !country.HasData() { return "" } switch c.lang { case LanguageRussian: return country.Names.Russian case LanguageEnglish: return country.Names.English case LanguageBrazilianPortuguese: return country.Names.BrazilianPortuguese case LanguageSimplifiedChinese: return country.Names.SimplifiedChinese case LanguageJapanese: return country.Names.Japanese case LanguageGerman: return country.Names.German case LanguageFrench: return country.Names.French case LanguageSpanish: return country.Names.Spanish default: return country.Names.English } } func (c *City) city(city oschwaldGeoip2.CityRecord) string { if !city.HasData() { return "" } switch c.lang { case LanguageRussian: return city.Names.Russian case LanguageEnglish: return city.Names.English case LanguageBrazilianPortuguese: return city.Names.BrazilianPortuguese case LanguageSimplifiedChinese: return city.Names.SimplifiedChinese case LanguageJapanese: return city.Names.Japanese case LanguageGerman: return city.Names.German case LanguageFrench: return city.Names.French case LanguageSpanish: return city.Names.Spanish default: return city.Names.English } } func (c *City) subdivisions(subdivisions []oschwaldGeoip2.CitySubdivision) []string { var citySubdivisions []string if len(subdivisions) == 0 { return citySubdivisions } for _, subdivision := range subdivisions { citySubdivisions = append(citySubdivisions, c.subdivision(subdivision)) } return citySubdivisions } func (c *City) subdivision(subdivision oschwaldGeoip2.CitySubdivision) string { if !subdivision.HasData() { return "" } switch c.lang { case LanguageRussian: return subdivision.Names.Russian case LanguageEnglish: return subdivision.Names.English case LanguageBrazilianPortuguese: return subdivision.Names.BrazilianPortuguese case LanguageSimplifiedChinese: return subdivision.Names.SimplifiedChinese case LanguageJapanese: return subdivision.Names.Japanese case LanguageGerman: return subdivision.Names.German case LanguageFrench: return subdivision.Names.French case LanguageSpanish: return subdivision.Names.Spanish default: return subdivision.Names.English } }