summaryrefslogblamecommitdiff
path: root/cmd/importer/import.go
blob: f08117dd4c93ad84a83b8fe982202cd2e14ce051 (plain) (tree)





















































































































































































                                                                                            
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
}