mirror of https://github.com/TZGyn/shortener
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
192 lines
3.5 KiB
Go
192 lines
3.5 KiB
Go
package main
|
|
|
|
import (
|
|
"archive/tar"
|
|
"compress/gzip"
|
|
"encoding/base64"
|
|
"errors"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/oschwald/geoip2-golang"
|
|
)
|
|
|
|
func getCity(db *geoip2.Reader, queryIp string) (*geoip2.City, error) {
|
|
// If you are using strings that may be invalid, check that ip is not nil
|
|
ip := net.ParseIP(queryIp)
|
|
if ip == nil {
|
|
return nil, errors.New("invalid ip")
|
|
}
|
|
|
|
record, err := db.City(ip)
|
|
if err != nil {
|
|
return nil, errors.New("no data")
|
|
}
|
|
return record, nil
|
|
}
|
|
|
|
func downloadAndExtractDB() error {
|
|
_, err := os.ReadDir("./data")
|
|
if err != nil {
|
|
os.MkdirAll("./data", os.ModePerm)
|
|
}
|
|
|
|
_, err = os.ReadDir("./dist")
|
|
if err != nil {
|
|
os.MkdirAll("./dist", os.ModePerm)
|
|
}
|
|
|
|
err = downloadDB()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
r, err := os.Open("./db.tar.gz")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = Untar("./dist", r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
files, err := os.ReadDir("./dist")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fileInfo := files[0]
|
|
|
|
err = os.Rename("./dist/"+fileInfo.Name()+"/GeoLite2-City.mmdb", "./data/GeoLite2-City.mmdb")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = os.RemoveAll("./dist/")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = os.MkdirAll("./dist/", os.ModePerm)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = os.Remove("./db.tar.gz")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func downloadDB() error {
|
|
accountID := os.Getenv("GEOIPUPDATE_ACCOUNT_ID")
|
|
if len(accountID) == 0 {
|
|
return errors.New("please provide ACCOUNT_ID as env")
|
|
}
|
|
|
|
licenseKey := os.Getenv("GEOIPUPDATE_LICENSE_KEY")
|
|
if len(licenseKey) == 0 {
|
|
return errors.New("please provide LICENSE_KEY as env")
|
|
}
|
|
|
|
out, err := os.Create("db.tar.gz")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer out.Close()
|
|
|
|
auth := base64.StdEncoding.EncodeToString([]byte(accountID + ":" + licenseKey))
|
|
|
|
req, err := http.NewRequest(http.MethodGet, "https://download.maxmind.com/geoip/databases/GeoLite2-City/download?suffix=tar.gz", nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Add("Authorization", "Basic "+auth)
|
|
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer res.Body.Close()
|
|
|
|
_, err = io.Copy(out, res.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func Untar(dst string, r io.Reader) error {
|
|
|
|
gzr, err := gzip.NewReader(r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer gzr.Close()
|
|
|
|
tr := tar.NewReader(gzr)
|
|
|
|
for {
|
|
header, err := tr.Next()
|
|
|
|
switch {
|
|
|
|
// if no more files are found return
|
|
case err == io.EOF:
|
|
return nil
|
|
|
|
// return any other error
|
|
case err != nil:
|
|
return err
|
|
|
|
// if the header is nil, just skip it (not sure how this happens)
|
|
case header == nil:
|
|
continue
|
|
}
|
|
|
|
// the target location where the dir/file should be created
|
|
target := filepath.Join(dst, header.Name)
|
|
|
|
// the following switch could also be done using fi.Mode(), not sure if there
|
|
// a benefit of using one vs. the other.
|
|
// fi := header.FileInfo()
|
|
|
|
// check the file type
|
|
switch header.Typeflag {
|
|
|
|
// if its a dir and it doesn't exist create it
|
|
case tar.TypeDir:
|
|
if _, err := os.Stat(target); err != nil {
|
|
if err := os.MkdirAll(target, 0755); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// if it's a file create it
|
|
case tar.TypeReg:
|
|
f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// copy over contents
|
|
if _, err := io.Copy(f, tr); err != nil {
|
|
return err
|
|
}
|
|
|
|
// manually close here after each file operation; defering would cause each file close
|
|
// to wait until all operations have completed.
|
|
f.Close()
|
|
}
|
|
}
|
|
}
|