move shared code to pkg.

This commit is contained in:
Miroslav Šedivý
2022-03-20 11:43:00 +01:00
parent 94c17e9a42
commit 8593d2d0fd
93 changed files with 132 additions and 133 deletions

24
pkg/utils/array.go Normal file
View File

@ -0,0 +1,24 @@
package utils
import (
"reflect"
)
func ArrayIn(val interface{}, array interface{}) (exists bool, index int) {
exists = false
index = -1
switch reflect.TypeOf(array).Kind() {
case reflect.Slice:
s := reflect.ValueOf(array)
for i := 0; i < s.Len(); i++ {
if reflect.DeepEqual(val, s.Index(i).Interface()) {
index = i
exists = true
return
}
}
}
return
}

34
pkg/utils/color.go Normal file
View File

@ -0,0 +1,34 @@
package utils
import (
"fmt"
"regexp"
)
const (
char = "&"
)
// Colors: http://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html
var re = regexp.MustCompile(char + `(?m)([0-9]{1,2};[0-9]{1,2}|[0-9]{1,2})`)
func Color(str string) string {
result := ""
lastIndex := 0
for _, v := range re.FindAllSubmatchIndex([]byte(str), -1) {
groups := []string{}
for i := 0; i < len(v); i += 2 {
groups = append(groups, str[v[i]:v[i+1]])
}
result += str[lastIndex:v[0]] + "\033[" + groups[1] + "m"
lastIndex = v[1]
}
return result + str[lastIndex:]
}
func Colorf(format string, a ...interface{}) string {
return fmt.Sprintf(Color(format), a...)
}

133
pkg/utils/http.go Normal file
View File

@ -0,0 +1,133 @@
package utils
import (
"encoding/json"
"fmt"
"io"
"net/http"
"github.com/rs/zerolog/log"
)
func HttpJsonRequest(w http.ResponseWriter, r *http.Request, res interface{}) error {
err := json.NewDecoder(r.Body).Decode(res)
if err == nil {
return nil
}
if err == io.EOF {
return HttpBadRequest("no data provided").WithInternalErr(err)
}
return HttpBadRequest("unable to parse provided data").WithInternalErr(err)
}
func HttpJsonResponse(w http.ResponseWriter, code int, res interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
if err := json.NewEncoder(w).Encode(res); err != nil {
log.Err(err).Str("module", "http").Msg("sending http json response failed")
}
}
func HttpSuccess(w http.ResponseWriter, res ...interface{}) error {
if len(res) == 0 {
w.WriteHeader(http.StatusNoContent)
} else {
HttpJsonResponse(w, http.StatusOK, res[0])
}
return nil
}
// HTTPError is an error with a message and an HTTP status code.
type HTTPError struct {
Code int `json:"code"`
Message string `json:"message"`
InternalErr error `json:"-"`
InternalMsg string `json:"-"`
}
func (e *HTTPError) Error() string {
if e.InternalMsg != "" {
return e.InternalMsg
}
return fmt.Sprintf("%d: %s", e.Code, e.Message)
}
func (e *HTTPError) Cause() error {
if e.InternalErr != nil {
return e.InternalErr
}
return e
}
// WithInternalErr adds internal error information to the error
func (e *HTTPError) WithInternalErr(err error) *HTTPError {
e.InternalErr = err
return e
}
// WithInternalMsg adds internal message information to the error
func (e *HTTPError) WithInternalMsg(msg string) *HTTPError {
e.InternalMsg = msg
return e
}
// WithInternalMsg adds internal formated message information to the error
func (e *HTTPError) WithInternalMsgf(fmtStr string, args ...interface{}) *HTTPError {
e.InternalMsg = fmt.Sprintf(fmtStr, args...)
return e
}
// Sends error with custom formated message
func (e *HTTPError) Msgf(fmtSt string, args ...interface{}) *HTTPError {
e.Message = fmt.Sprintf(fmtSt, args...)
return e
}
// Sends error with custom message
func (e *HTTPError) Msg(str string) *HTTPError {
e.Message = str
return e
}
func HttpError(code int, res ...string) *HTTPError {
err := &HTTPError{
Code: code,
Message: http.StatusText(code),
}
if len(res) == 1 {
err.Message = res[0]
}
return err
}
func HttpBadRequest(res ...string) *HTTPError {
return HttpError(http.StatusBadRequest, res...)
}
func HttpUnauthorized(res ...string) *HTTPError {
return HttpError(http.StatusUnauthorized, res...)
}
func HttpForbidden(res ...string) *HTTPError {
return HttpError(http.StatusForbidden, res...)
}
func HttpNotFound(res ...string) *HTTPError {
return HttpError(http.StatusNotFound, res...)
}
func HttpUnprocessableEntity(res ...string) *HTTPError {
return HttpError(http.StatusUnprocessableEntity, res...)
}
func HttpInternalServerError(res ...string) *HTTPError {
return HttpError(http.StatusInternalServerError, res...)
}

39
pkg/utils/image.go Normal file
View File

@ -0,0 +1,39 @@
package utils
import (
"bytes"
"encoding/base64"
"image"
"image/jpeg"
"image/png"
)
func CreatePNGImage(img *image.RGBA) ([]byte, error) {
out := new(bytes.Buffer)
err := png.Encode(out, img)
if err != nil {
return nil, err
}
return out.Bytes(), nil
}
func CreateJPGImage(img *image.RGBA, quality int) ([]byte, error) {
out := new(bytes.Buffer)
err := jpeg.Encode(out, img, &jpeg.Options{Quality: quality})
if err != nil {
return nil, err
}
return out.Bytes(), nil
}
func CreatePNGImageURI(img *image.RGBA) (string, error) {
data, err := CreatePNGImage(img)
if err != nil {
return "", err
}
uri := "data:image/png;base64," + base64.StdEncoding.EncodeToString(data)
return uri, nil
}

29
pkg/utils/json.go Normal file
View File

@ -0,0 +1,29 @@
package utils
import (
"encoding/json"
"reflect"
)
func Unmarshal(in interface{}, raw []byte, callback func() error) error {
if err := json.Unmarshal(raw, &in); err != nil {
return err
}
return callback()
}
func JsonStringAutoDecode(m interface{}) func(rf reflect.Kind, rt reflect.Kind, data interface{}) (interface{}, error) {
return func(rf reflect.Kind, rt reflect.Kind, data interface{}) (interface{}, error) {
if rf != reflect.String || rt == reflect.String {
return data, nil
}
raw := data.(string)
if raw != "" && (raw[0:1] == "{" || raw[0:1] == "[") {
err := json.Unmarshal([]byte(raw), &m)
return m, err
}
return data, nil
}
}

22
pkg/utils/request.go Normal file
View File

@ -0,0 +1,22 @@
package utils
import (
"bytes"
"io"
"net/http"
)
func HttpRequestGET(url string) (string, error) {
rsp, err := http.Get(url)
if err != nil {
return "", err
}
defer rsp.Body.Close()
buf, err := io.ReadAll(rsp.Body)
if err != nil {
return "", err
}
return string(bytes.TrimSpace(buf)), nil
}

98
pkg/utils/uid.go Normal file
View File

@ -0,0 +1,98 @@
package utils
import (
"crypto/rand"
"fmt"
"math"
)
const (
defaultAlphabet = "_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" // len=64
defaultSize = 21
defaultMaskSize = 5
)
// Generator function
type Generator func([]byte) (int, error)
// BytesGenerator is the default bytes generator
var BytesGenerator Generator = rand.Read
func initMasks(params ...int) []uint {
var size int
if len(params) == 0 {
size = defaultMaskSize
} else {
size = params[0]
}
masks := make([]uint, size)
for i := 0; i < size; i++ {
shift := 3 + i
masks[i] = (2 << uint(shift)) - 1
}
return masks
}
func getMask(alphabet string, masks []uint) int {
for i := 0; i < len(masks); i++ {
curr := int(masks[i])
if curr >= len(alphabet)-1 {
return curr
}
}
return 0
}
// GenerateUID is a low-level function to change alphabet and ID size.
func GenerateUID(alphabet string, size int) (string, error) {
if len(alphabet) == 0 || len(alphabet) > 255 {
return "", fmt.Errorf("alphabet must not empty and contain no more than 255 chars. Current len is %d", len(alphabet))
}
if size <= 0 {
return "", fmt.Errorf("size must be positive integer")
}
masks := initMasks(size)
mask := getMask(alphabet, masks)
ceilArg := 1.6 * float64(mask*size) / float64(len(alphabet))
step := int(math.Ceil(ceilArg))
id := make([]byte, size)
bytes := make([]byte, step)
for j := 0; ; {
_, err := BytesGenerator(bytes)
if err != nil {
return "", err
}
for i := 0; i < step; i++ {
currByte := bytes[i] & byte(mask)
if currByte < byte(len(alphabet)) {
id[j] = alphabet[currByte]
j++
if j == size {
return string(id[:size]), nil
}
}
}
}
}
// NewUID generates secure URL-friendly unique ID.
func NewUID(param ...int) (string, error) {
var size int
if len(param) == 0 {
size = defaultSize
} else {
size = param[0]
}
bytes := make([]byte, size)
_, err := BytesGenerator(bytes)
if err != nil {
return "", err
}
id := make([]byte, size)
for i := 0; i < size; i++ {
id[i] = defaultAlphabet[bytes[i]&63]
}
return string(id[:size]), nil
}

114
pkg/utils/zip.go Normal file
View File

@ -0,0 +1,114 @@
package utils
import (
"archive/zip"
"io"
"os"
"path/filepath"
"strings"
)
func Zip(source, zipPath string) error {
archiveFile, err := os.Create(zipPath)
if err != nil {
return err
}
defer archiveFile.Close()
archive := zip.NewWriter(archiveFile)
defer archive.Close()
return filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && !info.Mode().IsRegular() {
return nil
}
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
header.Name = strings.TrimPrefix(path, source)
if info.IsDir() {
header.Name += "/"
} else {
header.Method = zip.Deflate
}
writer, err := archive.CreateHeader(header)
if err != nil {
return err
}
if info.IsDir() {
return nil
}
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(writer, file)
return err
})
}
func Unzip(zipPath, target string) error {
reader, err := zip.OpenReader(zipPath)
if err != nil {
return err
}
if err := os.MkdirAll(target, 0755); err != nil {
return err
}
for _, file := range reader.File {
path := filepath.Join(target, file.Name)
if file.FileInfo().IsDir() {
if err := os.MkdirAll(path, file.Mode()); err != nil {
return err
}
continue
}
fileReader, err := file.Open()
if err != nil {
if fileReader != nil {
fileReader.Close()
}
return err
}
targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
if err != nil {
fileReader.Close()
if targetFile != nil {
targetFile.Close()
}
return err
}
if _, err := io.Copy(targetFile, fileReader); err != nil {
fileReader.Close()
targetFile.Close()
return err
}
fileReader.Close()
targetFile.Close()
}
return nil
}