mirror of
https://github.com/m1k1o/neko.git
synced 2024-07-24 14:40:50 +12:00
move shared code to pkg.
This commit is contained in:
24
pkg/utils/array.go
Normal file
24
pkg/utils/array.go
Normal 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
34
pkg/utils/color.go
Normal 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
133
pkg/utils/http.go
Normal 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
39
pkg/utils/image.go
Normal 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
29
pkg/utils/json.go
Normal 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
22
pkg/utils/request.go
Normal 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
98
pkg/utils/uid.go
Normal 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
114
pkg/utils/zip.go
Normal 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
|
||||
}
|
Reference in New Issue
Block a user