chore: 初始化 Cloudreve 源码仓库
This commit is contained in:
@@ -0,0 +1,360 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
cryptoRand "crypto/rand"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gofrs/uuid"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
var (
|
||||
RandomVariantAll = []rune("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
RandomLowerCases = []rune("1234567890abcdefghijklmnopqrstuvwxyz")
|
||||
)
|
||||
|
||||
// RandStringRunes 返回随机字符串
|
||||
func RandStringRunes(n int) string {
|
||||
b := make([]rune, n)
|
||||
for i := range b {
|
||||
b[i] = RandomVariantAll[rand.Intn(len(RandomVariantAll))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func RandStringRunesCrypto(n int) string {
|
||||
b := make([]rune, n)
|
||||
for i := range b {
|
||||
num, err := cryptoRand.Int(cryptoRand.Reader, big.NewInt(int64(len(RandomVariantAll))))
|
||||
if err != nil {
|
||||
// fallback to math/rand on crypto failure
|
||||
b[i] = RandomVariantAll[rand.Intn(len(RandomVariantAll))]
|
||||
} else {
|
||||
b[i] = RandomVariantAll[num.Int64()]
|
||||
}
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// RandString returns random string in given length and variant
|
||||
func RandString(n int, variant []rune) string {
|
||||
b := make([]rune, n)
|
||||
for i := range b {
|
||||
b[i] = variant[rand.Intn(len(variant))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// ContainsUint 返回list中是否包含
|
||||
func ContainsUint(s []uint, e uint) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInExtensionList 返回文件的扩展名是否在给定的列表范围内
|
||||
func IsInExtensionList(extList []string, fileName string) bool {
|
||||
ext := Ext(fileName)
|
||||
// 无扩展名时
|
||||
if len(ext) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if ContainsString(extList, ext) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInExtensionList 返回文件的扩展名是否在给定的列表范围内
|
||||
func IsInExtensionListExt(extList []string, ext string) bool {
|
||||
// 无扩展名时
|
||||
if len(ext) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if ContainsString(extList, ext) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainsString 返回list中是否包含
|
||||
func ContainsString(s []string, e string) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Replace 根据替换表执行批量替换
|
||||
func Replace(table map[string]string, s string) string {
|
||||
for key, value := range table {
|
||||
s = strings.Replace(s, key, value, -1)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ReplaceMagicVar 动态替换字符串中的魔法变量
|
||||
func ReplaceMagicVar(rawString string, fsSeparator string, pathAvailable bool, blobAvailable bool,
|
||||
timeConst time.Time, userId int, originName string, originPath string, completeBlobPath string) string {
|
||||
re := regexp.MustCompile(`\{[^{}]+\}`)
|
||||
return re.ReplaceAllStringFunc(rawString, func(match string) string {
|
||||
switch match {
|
||||
case "{randomkey16}":
|
||||
return RandStringRunes(16)
|
||||
case "{randomkey8}":
|
||||
return RandStringRunes(8)
|
||||
case "{timestamp}":
|
||||
return strconv.FormatInt(timeConst.Unix(), 10)
|
||||
case "{timestamp_nano}":
|
||||
return strconv.FormatInt(timeConst.UnixNano(), 10)
|
||||
case "{randomnum2}":
|
||||
return strconv.Itoa(rand.Intn(2))
|
||||
case "{randomnum3}":
|
||||
return strconv.Itoa(rand.Intn(3))
|
||||
case "{randomnum4}":
|
||||
return strconv.Itoa(rand.Intn(4))
|
||||
case "{randomnum8}":
|
||||
return strconv.Itoa(rand.Intn(8))
|
||||
case "{uid}":
|
||||
return strconv.Itoa(userId)
|
||||
case "{datetime}":
|
||||
return timeConst.Format("20060102150405")
|
||||
case "{date}":
|
||||
return timeConst.Format("20060102")
|
||||
case "{year}":
|
||||
return timeConst.Format("2006")
|
||||
case "{month}":
|
||||
return timeConst.Format("01")
|
||||
case "{day}":
|
||||
return timeConst.Format("02")
|
||||
case "{hour}":
|
||||
return timeConst.Format("15")
|
||||
case "{minute}":
|
||||
return timeConst.Format("04")
|
||||
case "{second}":
|
||||
return timeConst.Format("05")
|
||||
case "{uuid}":
|
||||
return uuid.Must(uuid.NewV4()).String()
|
||||
case "{ext}":
|
||||
return filepath.Ext(originName)
|
||||
case "{originname}":
|
||||
return originName
|
||||
case "{originname_without_ext}":
|
||||
return strings.TrimSuffix(originName, filepath.Ext(originName))
|
||||
case "{path}":
|
||||
if pathAvailable {
|
||||
return originPath + fsSeparator
|
||||
}
|
||||
return match
|
||||
case "{blob_name}":
|
||||
if blobAvailable {
|
||||
return filepath.Base(completeBlobPath)
|
||||
}
|
||||
return match
|
||||
case "{blob_name_without_ext}":
|
||||
if blobAvailable {
|
||||
return strings.TrimSuffix(filepath.Base(completeBlobPath), filepath.Ext(completeBlobPath))
|
||||
}
|
||||
return match
|
||||
case "{blob_path}":
|
||||
if blobAvailable {
|
||||
return path.Dir(completeBlobPath) + fsSeparator
|
||||
}
|
||||
return match
|
||||
default:
|
||||
return match
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BuildRegexp 构建用于SQL查询用的多条件正则
|
||||
func BuildRegexp(search []string, prefix, suffix, condition string) string {
|
||||
var res string
|
||||
for key, value := range search {
|
||||
res += prefix + regexp.QuoteMeta(value) + suffix
|
||||
if key < len(search)-1 {
|
||||
res += condition
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// BuildConcat 根据数据库类型构建字符串连接表达式
|
||||
func BuildConcat(str1, str2 string, DBType string) string {
|
||||
switch DBType {
|
||||
case "mysql":
|
||||
return "CONCAT(" + str1 + "," + str2 + ")"
|
||||
default:
|
||||
return str1 + "||" + str2
|
||||
}
|
||||
}
|
||||
|
||||
// SliceIntersect 求两个切片交集
|
||||
func SliceIntersect(slice1, slice2 []string) []string {
|
||||
m := make(map[string]int)
|
||||
nn := make([]string, 0)
|
||||
for _, v := range slice1 {
|
||||
m[v]++
|
||||
}
|
||||
|
||||
for _, v := range slice2 {
|
||||
times, _ := m[v]
|
||||
if times == 1 {
|
||||
nn = append(nn, v)
|
||||
}
|
||||
}
|
||||
return nn
|
||||
}
|
||||
|
||||
// SliceDifference 求两个切片差集
|
||||
func SliceDifference(slice1, slice2 []string) []string {
|
||||
m := make(map[string]int)
|
||||
nn := make([]string, 0)
|
||||
inter := SliceIntersect(slice1, slice2)
|
||||
for _, v := range inter {
|
||||
m[v]++
|
||||
}
|
||||
|
||||
for _, value := range slice1 {
|
||||
times, _ := m[value]
|
||||
if times == 0 {
|
||||
nn = append(nn, value)
|
||||
}
|
||||
}
|
||||
return nn
|
||||
}
|
||||
|
||||
// WithValue inject key-value pair into request context.
|
||||
func WithValue(c *gin.Context, key any, value any) {
|
||||
c.Request = c.Request.WithContext(context.WithValue(c.Request.Context(), key, value))
|
||||
}
|
||||
|
||||
// BoolToString transform bool to string
|
||||
func BoolToString(b bool) string {
|
||||
if b {
|
||||
return "1"
|
||||
}
|
||||
return "0"
|
||||
}
|
||||
|
||||
func EncodeTimeFlowString(str string, timeNow int64) string {
|
||||
timeNow = timeNow / 1000
|
||||
timeDigits := []int{}
|
||||
timeDigitIndex := 0
|
||||
|
||||
if len(str) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
str = fmt.Sprintf("%d|%s", timeNow, str)
|
||||
|
||||
res := make([]int32, 0, utf8.RuneCountInString(str))
|
||||
for timeNow > 0 {
|
||||
timeDigits = append(timeDigits, int(timeNow%int64(10)))
|
||||
timeNow = timeNow / 10
|
||||
}
|
||||
|
||||
add := false
|
||||
for pos, rune := range str {
|
||||
// take single digit with index timeDigitIndex from timeNow
|
||||
newIndex := pos
|
||||
if add {
|
||||
newIndex = pos + timeDigits[timeDigitIndex]*timeDigitIndex
|
||||
} else {
|
||||
newIndex = 2*timeDigitIndex*timeDigits[timeDigitIndex] - pos
|
||||
}
|
||||
|
||||
if newIndex < 0 {
|
||||
newIndex = newIndex * -1
|
||||
}
|
||||
|
||||
res = append(res, rune)
|
||||
newIndex = newIndex % len(res)
|
||||
|
||||
res[newIndex], res[len(res)-1] = res[len(res)-1], res[newIndex]
|
||||
|
||||
add = !add
|
||||
// Add timeDigitIndex by 1, but does not exceed total digits in timeNow
|
||||
timeDigitIndex++
|
||||
timeDigitIndex = timeDigitIndex % len(timeDigits)
|
||||
}
|
||||
|
||||
return string(res)
|
||||
}
|
||||
|
||||
func DecodeTimeFlowStringTime(str string, timeNow int64) string {
|
||||
timeNow = timeNow / 1000
|
||||
timeDigits := []int{}
|
||||
|
||||
if len(str) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
for timeNow > 0 {
|
||||
timeDigits = append(timeDigits, int(timeNow%int64(10)))
|
||||
timeNow = timeNow / 10
|
||||
}
|
||||
|
||||
res := make([]int32, utf8.RuneCountInString(str))
|
||||
secret := []rune(str)
|
||||
add := false
|
||||
if len(secret)%2 == 0 {
|
||||
add = true
|
||||
}
|
||||
timeDigitIndex := (len(secret) - 1) % len(timeDigits)
|
||||
for pos := range secret {
|
||||
// take single digit with index timeDigitIndex from timeNow
|
||||
newIndex := len(res) - 1 - pos
|
||||
if add {
|
||||
newIndex = newIndex + timeDigits[timeDigitIndex]*timeDigitIndex
|
||||
} else {
|
||||
newIndex = 2*timeDigitIndex*timeDigits[timeDigitIndex] - newIndex
|
||||
}
|
||||
|
||||
if newIndex < 0 {
|
||||
newIndex = newIndex * -1
|
||||
}
|
||||
|
||||
newIndex = newIndex % len(secret)
|
||||
|
||||
res[len(res)-1-pos] = secret[newIndex]
|
||||
secret[newIndex], secret[len(res)-1-pos] = secret[len(res)-1-pos], secret[newIndex]
|
||||
secret = secret[:len(secret)-1]
|
||||
|
||||
add = !add
|
||||
// Add timeDigitIndex by 1, but does not exceed total digits in timeNow
|
||||
timeDigitIndex--
|
||||
if timeDigitIndex < 0 {
|
||||
timeDigitIndex = len(timeDigits) - 1
|
||||
}
|
||||
}
|
||||
|
||||
return string(res)
|
||||
}
|
||||
|
||||
func ToPtr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRandStringRunes(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
|
||||
// 0 长度字符
|
||||
randStr := RandStringRunes(0)
|
||||
asserts.Len(randStr, 0)
|
||||
|
||||
// 16 长度字符
|
||||
randStr = RandStringRunes(16)
|
||||
asserts.Len(randStr, 16)
|
||||
|
||||
// 32 长度字符
|
||||
randStr = RandStringRunes(32)
|
||||
asserts.Len(randStr, 32)
|
||||
|
||||
//相同长度字符
|
||||
sameLenStr1 := RandStringRunes(32)
|
||||
sameLenStr2 := RandStringRunes(32)
|
||||
asserts.NotEqual(sameLenStr1, sameLenStr2)
|
||||
}
|
||||
|
||||
func TestContainsUint(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
asserts.True(ContainsUint([]uint{0, 2, 3, 65, 4}, 65))
|
||||
asserts.True(ContainsUint([]uint{65}, 65))
|
||||
asserts.False(ContainsUint([]uint{65}, 6))
|
||||
}
|
||||
|
||||
func TestContainsString(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
asserts.True(ContainsString([]string{"", "1"}, ""))
|
||||
asserts.True(ContainsString([]string{"", "1"}, "1"))
|
||||
asserts.False(ContainsString([]string{"", "1"}, " "))
|
||||
}
|
||||
|
||||
func TestReplace(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
|
||||
asserts.Equal("origin", Replace(map[string]string{
|
||||
"123": "321",
|
||||
}, "origin"))
|
||||
|
||||
asserts.Equal("321origin321", Replace(map[string]string{
|
||||
"123": "321",
|
||||
}, "123origin123"))
|
||||
asserts.Equal("321new321", Replace(map[string]string{
|
||||
"123": "321",
|
||||
"origin": "new",
|
||||
}, "123origin123"))
|
||||
}
|
||||
|
||||
func TestBuildRegexp(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
|
||||
asserts.Equal("^/dir/", BuildRegexp([]string{"/dir"}, "^", "/", "|"))
|
||||
asserts.Equal("^/dir/|^/dir/di\\*r/", BuildRegexp([]string{"/dir", "/dir/di*r"}, "^", "/", "|"))
|
||||
}
|
||||
|
||||
func TestBuildConcat(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
asserts.Equal("CONCAT(1,2)", BuildConcat("1", "2", "mysql"))
|
||||
asserts.Equal("1||2", BuildConcat("1", "2", "sqlite"))
|
||||
}
|
||||
|
||||
func TestSliceDifference(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
|
||||
{
|
||||
s1 := []string{"1", "2", "3", "4"}
|
||||
s2 := []string{"2", "4"}
|
||||
asserts.Equal([]string{"1", "3"}, SliceDifference(s1, s2))
|
||||
}
|
||||
|
||||
{
|
||||
s2 := []string{"1", "2", "3", "4"}
|
||||
s1 := []string{"2", "4"}
|
||||
asserts.Equal([]string{}, SliceDifference(s1, s2))
|
||||
}
|
||||
|
||||
{
|
||||
s1 := []string{"1", "2", "3", "4"}
|
||||
s2 := []string{"1", "2", "3", "4"}
|
||||
asserts.Equal([]string{}, SliceDifference(s1, s2))
|
||||
}
|
||||
|
||||
{
|
||||
s1 := []string{"1", "2", "3", "4"}
|
||||
s2 := []string{}
|
||||
asserts.Equal([]string{"1", "2", "3", "4"}, SliceDifference(s1, s2))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Exists reports whether the named file or directory exists.
|
||||
func Exists(name string) bool {
|
||||
if _, err := os.Stat(name); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// CreatNestedFile 给定path创建文件,如果目录不存在就递归创建
|
||||
func CreatNestedFile(path string) (*os.File, error) {
|
||||
basePath := filepath.Dir(path)
|
||||
if !Exists(basePath) {
|
||||
err := os.MkdirAll(basePath, 0700)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return os.Create(path)
|
||||
}
|
||||
|
||||
// CreatNestedFolder creates a folder with the given path, if the directory does not exist,
|
||||
// it will be created recursively.
|
||||
func CreatNestedFolder(path string) error {
|
||||
if !Exists(path) {
|
||||
err := os.MkdirAll(path, 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsEmpty 返回给定目录是否为空目录
|
||||
func IsEmpty(name string) (bool, error) {
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.Readdirnames(1) // Or f.Readdir(1)
|
||||
if err == io.EOF {
|
||||
return true, nil
|
||||
}
|
||||
return false, err // Either not empty or error, suits both cases
|
||||
}
|
||||
|
||||
type CallbackReader struct {
|
||||
reader io.Reader
|
||||
callback func(int64)
|
||||
}
|
||||
|
||||
func NewCallbackReader(reader io.Reader, callback func(int64)) *CallbackReader {
|
||||
return &CallbackReader{
|
||||
reader: reader,
|
||||
callback: callback,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *CallbackReader) Read(p []byte) (n int, err error) {
|
||||
n, err = r.reader.Read(p)
|
||||
r.callback(int64(n))
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExists(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
asserts.True(Exists("io_test.go"))
|
||||
asserts.False(Exists("io_test.js"))
|
||||
}
|
||||
|
||||
func TestCreatNestedFile(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
|
||||
// 父目录不存在
|
||||
{
|
||||
file, err := CreatNestedFile("test/nest.txt")
|
||||
asserts.NoError(err)
|
||||
asserts.NoError(file.Close())
|
||||
asserts.FileExists("test/nest.txt")
|
||||
}
|
||||
|
||||
// 父目录存在
|
||||
{
|
||||
file, err := CreatNestedFile("test/direct.txt")
|
||||
asserts.NoError(err)
|
||||
asserts.NoError(file.Close())
|
||||
asserts.FileExists("test/direct.txt")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEmpty(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
|
||||
asserts.False(IsEmpty(""))
|
||||
asserts.False(IsEmpty("not_exist"))
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fatih/color"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// LevelError 错误
|
||||
LevelError = iota
|
||||
// LevelWarning 警告
|
||||
LevelWarning
|
||||
// LevelInformational 提示
|
||||
LevelInformational
|
||||
// LevelDebug 除错
|
||||
LevelDebug
|
||||
)
|
||||
|
||||
var GloablLogger *Logger
|
||||
var Level = LevelDebug
|
||||
|
||||
// Logger 日志
|
||||
type Logger struct {
|
||||
level int
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// 日志颜色
|
||||
var colors = map[string]func(a ...interface{}) string{
|
||||
"Warning": color.New(color.FgYellow).Add(color.Bold).SprintFunc(),
|
||||
"Panic": color.New(color.BgRed).Add(color.Bold).SprintFunc(),
|
||||
"Error": color.New(color.FgRed).Add(color.Bold).SprintFunc(),
|
||||
"Info": color.New(color.FgCyan).Add(color.Bold).SprintFunc(),
|
||||
"Debug": color.New(color.FgWhite).Add(color.Bold).SprintFunc(),
|
||||
}
|
||||
|
||||
// 不同级别前缀与时间的间隔,保持宽度一致
|
||||
var spaces = map[string]string{
|
||||
"Warning": "",
|
||||
"Panic": " ",
|
||||
"Error": " ",
|
||||
"Info": " ",
|
||||
"Debug": " ",
|
||||
}
|
||||
|
||||
// Println 打印
|
||||
func (ll *Logger) Println(prefix string, msg string) {
|
||||
// TODO Release时去掉
|
||||
// color.NoColor = false
|
||||
|
||||
c := color.New()
|
||||
|
||||
ll.mu.Lock()
|
||||
defer ll.mu.Unlock()
|
||||
|
||||
_, _ = c.Printf(
|
||||
"%s%s %s %s\n",
|
||||
colors[prefix]("["+prefix+"]"),
|
||||
spaces[prefix],
|
||||
time.Now().Format("2006-01-02 15:04:05"),
|
||||
msg,
|
||||
)
|
||||
}
|
||||
|
||||
// Panic 极端错误
|
||||
func (ll *Logger) Panic(format string, v ...interface{}) {
|
||||
if LevelError > ll.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf(format, v...)
|
||||
ll.Println("Panic", msg)
|
||||
panic(msg)
|
||||
}
|
||||
|
||||
// Error 错误
|
||||
func (ll *Logger) Error(format string, v ...interface{}) {
|
||||
if LevelError > ll.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf(format, v...)
|
||||
ll.Println("Error", msg)
|
||||
}
|
||||
|
||||
// Warning 警告
|
||||
func (ll *Logger) Warning(format string, v ...interface{}) {
|
||||
if LevelWarning > ll.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf(format, v...)
|
||||
ll.Println("Warning", msg)
|
||||
}
|
||||
|
||||
// Info 信息
|
||||
func (ll *Logger) Info(format string, v ...interface{}) {
|
||||
if LevelInformational > ll.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf(format, v...)
|
||||
ll.Println("Info", msg)
|
||||
}
|
||||
|
||||
// Debug 校验
|
||||
func (ll *Logger) Debug(format string, v ...interface{}) {
|
||||
if LevelDebug > ll.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf(format, v...)
|
||||
ll.Println("Debug", msg)
|
||||
}
|
||||
|
||||
// Print GORM 的 Logger实现
|
||||
//func (ll *Logger) Print(v ...interface{}) {
|
||||
// if LevelDebug > ll.level {
|
||||
// return
|
||||
// }
|
||||
// msg := fmt.Sprintf("[SQL] %s", v...)
|
||||
// ll.println(msg)
|
||||
//}
|
||||
|
||||
// BuildLogger 构建logger
|
||||
func BuildLogger(level string) {
|
||||
intLevel := LevelError
|
||||
switch level {
|
||||
case "error":
|
||||
intLevel = LevelError
|
||||
case "warning":
|
||||
intLevel = LevelWarning
|
||||
case "info":
|
||||
intLevel = LevelInformational
|
||||
case "debug":
|
||||
intLevel = LevelDebug
|
||||
}
|
||||
l := Logger{
|
||||
level: intLevel,
|
||||
}
|
||||
GloablLogger = &l
|
||||
}
|
||||
|
||||
// Log 返回日志对象
|
||||
func Log() *Logger {
|
||||
if GloablLogger == nil {
|
||||
l := Logger{
|
||||
level: Level,
|
||||
}
|
||||
GloablLogger = &l
|
||||
}
|
||||
return GloablLogger
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// +build !race
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBuildLogger(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
asserts.NotPanics(func() {
|
||||
BuildLogger("error")
|
||||
})
|
||||
asserts.NotPanics(func() {
|
||||
BuildLogger("warning")
|
||||
})
|
||||
asserts.NotPanics(func() {
|
||||
BuildLogger("info")
|
||||
})
|
||||
asserts.NotPanics(func() {
|
||||
BuildLogger("?")
|
||||
})
|
||||
asserts.NotPanics(func() {
|
||||
BuildLogger("debug")
|
||||
})
|
||||
}
|
||||
|
||||
func TestLog(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
asserts.NotNil(Log())
|
||||
GloablLogger = nil
|
||||
asserts.NotNil(Log())
|
||||
}
|
||||
|
||||
func TestLogger_Debug(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
l := Logger{
|
||||
level: LevelDebug,
|
||||
}
|
||||
asserts.NotPanics(func() {
|
||||
l.Debug("123")
|
||||
})
|
||||
l.level = LevelError
|
||||
asserts.NotPanics(func() {
|
||||
l.Debug("123")
|
||||
})
|
||||
}
|
||||
|
||||
func TestLogger_Info(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
l := Logger{
|
||||
level: LevelDebug,
|
||||
}
|
||||
asserts.NotPanics(func() {
|
||||
l.Info("123")
|
||||
})
|
||||
l.level = LevelError
|
||||
asserts.NotPanics(func() {
|
||||
l.Info("123")
|
||||
})
|
||||
}
|
||||
func TestLogger_Warning(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
l := Logger{
|
||||
level: LevelDebug,
|
||||
}
|
||||
asserts.NotPanics(func() {
|
||||
l.Warning("123")
|
||||
})
|
||||
l.level = LevelError
|
||||
asserts.NotPanics(func() {
|
||||
l.Warning("123")
|
||||
})
|
||||
}
|
||||
|
||||
func TestLogger_Error(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
l := Logger{
|
||||
level: LevelDebug,
|
||||
}
|
||||
asserts.NotPanics(func() {
|
||||
l.Error("123")
|
||||
})
|
||||
l.level = -1
|
||||
asserts.NotPanics(func() {
|
||||
l.Error("123")
|
||||
})
|
||||
}
|
||||
|
||||
func TestLogger_Panic(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
l := Logger{
|
||||
level: LevelDebug,
|
||||
}
|
||||
asserts.Panics(func() {
|
||||
l.Panic("123")
|
||||
})
|
||||
l.level = -1
|
||||
asserts.NotPanics(func() {
|
||||
l.Error("123")
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
DataFolder = "data"
|
||||
)
|
||||
|
||||
var UseWorkingDir = false
|
||||
|
||||
// DotPathToStandardPath 将","分割的路径转换为标准路径
|
||||
func DotPathToStandardPath(path string) string {
|
||||
return "/" + strings.Replace(path, ",", "/", -1)
|
||||
}
|
||||
|
||||
// FillSlash 给路径补全`/`
|
||||
func FillSlash(path string) string {
|
||||
if path == "/" {
|
||||
return path
|
||||
}
|
||||
return path + "/"
|
||||
}
|
||||
|
||||
// RemoveSlash 移除路径最后的`/`
|
||||
func RemoveSlash(path string) string {
|
||||
if len(path) > 1 {
|
||||
return strings.TrimSuffix(path, "/")
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
// SplitPath 分割路径为列表
|
||||
func SplitPath(path string) []string {
|
||||
if len(path) == 0 || path[0] != '/' {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
if path == "/" {
|
||||
return []string{"/"}
|
||||
}
|
||||
|
||||
pathSplit := strings.Split(path, "/")
|
||||
pathSplit[0] = "/"
|
||||
return pathSplit
|
||||
}
|
||||
|
||||
// FormSlash 将path中的反斜杠'\'替换为'/'
|
||||
func FormSlash(old string) string {
|
||||
return path.Clean(strings.ReplaceAll(old, "\\", "/"))
|
||||
}
|
||||
|
||||
// RelativePath 获取相对可执行文件的路径
|
||||
func RelativePath(name string) string {
|
||||
if UseWorkingDir {
|
||||
return name
|
||||
}
|
||||
|
||||
if filepath.IsAbs(name) {
|
||||
return name
|
||||
}
|
||||
e, _ := os.Executable()
|
||||
return filepath.Join(filepath.Dir(e), name)
|
||||
}
|
||||
|
||||
// DataPath relative path for store persist data file
|
||||
func DataPath(child string) string {
|
||||
dataPath := RelativePath(DataFolder)
|
||||
if !Exists(dataPath) {
|
||||
os.MkdirAll(dataPath, 0700)
|
||||
}
|
||||
|
||||
if filepath.IsAbs(child) {
|
||||
return child
|
||||
}
|
||||
|
||||
return filepath.Join(dataPath, child)
|
||||
}
|
||||
|
||||
// MkdirIfNotExist create directory if not exist
|
||||
func MkdirIfNotExist(ctx context.Context, p string) {
|
||||
if !Exists(p) {
|
||||
os.MkdirAll(p, 0700)
|
||||
}
|
||||
}
|
||||
|
||||
// SlashClean is equivalent to but slightly more efficient than
|
||||
// path.Clean("/" + name).
|
||||
func SlashClean(name string) string {
|
||||
if name == "" || name[0] != '/' {
|
||||
name = "/" + name
|
||||
}
|
||||
return path.Clean(name)
|
||||
}
|
||||
|
||||
// Ext returns the file name extension used by path, without the dot.
|
||||
func Ext(name string) string {
|
||||
ext := strings.ToLower(filepath.Ext(name))
|
||||
if len(ext) > 0 {
|
||||
ext = ext[1:]
|
||||
}
|
||||
return ext
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDotPathToStandardPath(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
|
||||
asserts.Equal("/", DotPathToStandardPath(""))
|
||||
asserts.Equal("/目录", DotPathToStandardPath("目录"))
|
||||
asserts.Equal("/目录/目录2", DotPathToStandardPath("目录,目录2"))
|
||||
}
|
||||
|
||||
func TestFillSlash(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
asserts.Equal("/", FillSlash("/"))
|
||||
asserts.Equal("/", FillSlash(""))
|
||||
asserts.Equal("/123/", FillSlash("/123"))
|
||||
}
|
||||
|
||||
func TestRemoveSlash(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
asserts.Equal("/", RemoveSlash("/"))
|
||||
asserts.Equal("/123/1236", RemoveSlash("/123/1236"))
|
||||
asserts.Equal("/123/1236", RemoveSlash("/123/1236/"))
|
||||
}
|
||||
|
||||
func TestSplitPath(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
asserts.Equal([]string{}, SplitPath(""))
|
||||
asserts.Equal([]string{}, SplitPath("1"))
|
||||
asserts.Equal([]string{"/"}, SplitPath("/"))
|
||||
asserts.Equal([]string{"/", "123", "321"}, SplitPath("/123/321"))
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// SetSession 设置session
|
||||
func SetSession(c *gin.Context, list map[string]interface{}) {
|
||||
s := sessions.Default(c)
|
||||
for key, value := range list {
|
||||
s.Set(key, value)
|
||||
}
|
||||
|
||||
err := s.Save()
|
||||
if err != nil {
|
||||
Log().Warning("无法设置 Session 值:%s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// GetSession 获取session
|
||||
func GetSession(c *gin.Context, key any) interface{} {
|
||||
s := sessions.Default(c)
|
||||
return s.Get(key)
|
||||
}
|
||||
|
||||
// DeleteSession 删除session
|
||||
func DeleteSession(c *gin.Context, key any) {
|
||||
s := sessions.Default(c)
|
||||
s.Delete(key)
|
||||
s.Save()
|
||||
}
|
||||
|
||||
// ClearSession 清空session
|
||||
func ClearSession(c *gin.Context) {
|
||||
s := sessions.Default(c)
|
||||
s.Clear()
|
||||
s.Save()
|
||||
}
|
||||
Reference in New Issue
Block a user