diff options
Diffstat (limited to 'cmd/importer/import.go')
-rw-r--r-- | cmd/importer/import.go | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/cmd/importer/import.go b/cmd/importer/import.go new file mode 100644 index 0000000..f08117d --- /dev/null +++ b/cmd/importer/import.go @@ -0,0 +1,182 @@ +package importer + +import ( + "context" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "encoding/json" + "errors" + "io" + "net/http" + "os" + "path/filepath" + "strings" + "unicode/utf16" + + "git.neonxp.ru/neonxp/guessr/pkg/config" + "git.neonxp.ru/neonxp/guessr/pkg/db" + "git.neonxp.ru/neonxp/guessr/pkg/model" + "github.com/urfave/cli/v3" +) + +var Command = &cli.Command{ + Name: "import", + Usage: "import json with places", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "download-img", + Usage: "download images locally", + }, + }, + ArgsUsage: "path to json file", + Action: func(ctx context.Context, c *cli.Command) error { + downloadImg := c.Bool("download-img") + + cfg, err := config.New() + if err != nil { + return err + } + + dbClient := db.New(cfg.DB) + + fp, err := os.Open(c.Args().First()) + if err != nil { + return err + } + defer fp.Close() + + places := Places{} + dec := json.NewDecoder(fp) + + if err := dec.Decode(&places); err != nil { + return err + } + + for _, p := range places { + decodedValue := unescape(p.Name) + h := hash(p.Img) + prefix := h[0:1] + path := filepath.Join(".", "static", "places", prefix) + + file := filepath.Join(path, h+".jpg") + + if downloadImg { + //nolint:gomnd + if err := os.MkdirAll(path, 0o777); err != nil { + return err + } + + if err := downloadFile(p.Img, file); err != nil { + return err + } + } + + np := model.Place{ + GUID: p.GUID, + Img: strings.Replace(file, "static", "", 1), + Name: strings.ReplaceAll(decodedValue, "�", `"`), + Position: &model.Point{ + Lat: p.Position.Lat, + Lon: p.Position.Lng, + }, + } + + if _, err := dbClient.NewInsert().Model(&np).Exec(ctx); err != nil { + return err + } + } + + return nil + }, +} + +type Place struct { + GUID string `json:"guid"` + Position PositionEntity `json:"position"` + Img string `json:"img"` + Name string `json:"name"` +} + +type PositionEntity struct { + Lat float64 `json:"lat"` + Lng float64 `json:"lng"` +} + +type Places []Place + +func hash(in string) string { + hasher := sha256.New() + if _, err := hasher.Write([]byte(in)); err != nil { + return "" + } + + return base64.URLEncoding.EncodeToString(hasher.Sum(nil)) +} + +func unescape(input string) string { + output := make([]rune, 0, len(input)) + length := len(input) + + for index := 0; index < length; { + //nolint:nestif + if input[index] == '%' { + if index <= length-6 && input[index+1] == 'u' { + byte16, err := hex.DecodeString(input[index+2 : index+6]) + if err == nil { + //nolint:gomnd + value := uint16(byte16[0])<<8 + uint16(byte16[1]) + chr := utf16.Decode([]uint16{value})[0] + output = append(output, chr) + index += 6 + + continue + } + } + + if index <= length-3 { + byte8, err := hex.DecodeString(input[index+1 : index+3]) + if err == nil { + value := uint16(byte8[0]) + chr := utf16.Decode([]uint16{value})[0] + output = append(output, chr) + index += 3 + + continue + } + } + } + + output = append(output, rune(input[index])) + index++ + } + + return string(output) +} + +func downloadFile(fileURL, fileName string) error { + //nolint:gosec,noctx + response, err := http.Get(fileURL) + if err != nil { + return err + } + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { + return errors.New("received non 200 response code") + } + // Create a empty file + file, err := os.Create(fileName) + if err != nil { + return err + } + defer file.Close() + + // Write the bytes to the fiel + _, err = io.Copy(file, response.Body) + if err != nil { + return err + } + + return nil +} |