v1.0.0
This commit is contained in:
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// 缓存接口-支持Redis和内存使用
|
||||
type Cacher[T any] interface {
|
||||
// 设置
|
||||
Set(k string, v T, d time.Duration)
|
||||
|
||||
// 取值
|
||||
Get(k string) (T, bool)
|
||||
|
||||
// 设置-过期时间采用默认值
|
||||
SetDefault(k string, v T)
|
||||
|
||||
// 删除
|
||||
Delete(k string)
|
||||
|
||||
// 只有在给定Key项尚未存在,或者现有项已过期时,才能将项添加到缓存中。否则返回错误。
|
||||
// Add(k string, v T, d time.Duration)
|
||||
// IncrementInt(k string, n int) (num int, err error)
|
||||
|
||||
// 设置值,但不重置过期时间
|
||||
SetKeepExpiration(k string, v T)
|
||||
|
||||
// 项目总数
|
||||
ItemCount() (int64, error)
|
||||
|
||||
// 清空
|
||||
Flush()
|
||||
}
|
||||
Vendored
+96
@@ -0,0 +1,96 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/patrickmn/go-cache"
|
||||
)
|
||||
|
||||
// 参考:https://blog.csdn.net/u014459543/article/details/108429469
|
||||
type GoCacheStruct[T any] struct {
|
||||
gocahce *cache.Cache
|
||||
Result T
|
||||
}
|
||||
|
||||
type GoCacheValue[T any] struct {
|
||||
Value T
|
||||
}
|
||||
|
||||
// 创建一个goCache结构体
|
||||
// cache.New(5*time.Minute, 60*time.Second),清理过期的item间隔 0.不清理
|
||||
func NewGoCache[T any](defaultExpiration time.Duration, cleanupInterval time.Duration) *GoCacheStruct[T] {
|
||||
cacheAdapter := cache.New(defaultExpiration, cleanupInterval)
|
||||
return &GoCacheStruct[T]{
|
||||
gocahce: cacheAdapter,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *GoCacheStruct[T]) Set(k string, x T, d time.Duration) {
|
||||
c.gocahce.Set(k, GoCacheValue[T]{Value: x}, d)
|
||||
}
|
||||
|
||||
func (c *GoCacheStruct[T]) Get(k string) (T, bool) {
|
||||
if v, ok := c.gocahce.Get(k); ok {
|
||||
if value, okv := v.(GoCacheValue[T]); okv {
|
||||
return value.Value, true
|
||||
}
|
||||
}
|
||||
return c.Result, false
|
||||
}
|
||||
|
||||
// 设置cache 无时间参数
|
||||
func (c *GoCacheStruct[T]) SetDefault(k string, v T) {
|
||||
c.gocahce.SetDefault(k, GoCacheValue[T]{Value: v})
|
||||
}
|
||||
|
||||
// 设置并保持原始的过期时间
|
||||
func (c *GoCacheStruct[T]) SetKeepExpiration(k string, v T) {
|
||||
_, expirationTime, ok := c.gocahce.GetWithExpiration(k)
|
||||
|
||||
now := time.Now()
|
||||
differ := expirationTime.Sub(now)
|
||||
// 如果 过期值不为零值 && 未过期 && 过期时间大于现在的时间
|
||||
// 将保持不变原始的过期时间来计算时间
|
||||
if !expirationTime.IsZero() && ok && differ > 0 {
|
||||
// newExpiration := now.Unix() + int64(math.Round(differ.Seconds()))
|
||||
// fmt.Println("旧的过期时间", expirationTime.Unix())
|
||||
// fmt.Println("时间限制差", math.Round(differ.Seconds()))
|
||||
// fmt.Println("新的过期时间", newExpiration)
|
||||
c.gocahce.Set(k, GoCacheValue[T]{Value: v}, differ)
|
||||
} else {
|
||||
c.gocahce.SetDefault(k, GoCacheValue[T]{Value: v})
|
||||
}
|
||||
}
|
||||
|
||||
// 删除 cache
|
||||
func (c *GoCacheStruct[T]) Delete(k string) {
|
||||
c.gocahce.Delete(k)
|
||||
}
|
||||
|
||||
// Add() 加入缓存
|
||||
func (c *GoCacheStruct[T]) Add(k string, v T, d time.Duration) {
|
||||
c.gocahce.Add(k, GoCacheValue[T]{Value: v}, d)
|
||||
}
|
||||
|
||||
// IncrementInt() 对已存在的key 值自增n
|
||||
func (c *GoCacheStruct[T]) IncrementInt(k string, n int) (num int, err error) {
|
||||
return c.gocahce.IncrementInt(k, n)
|
||||
}
|
||||
|
||||
// ItemCount 获取已存在key的数量
|
||||
func (c *GoCacheStruct[T]) ItemCount() (int64, error) {
|
||||
return int64(c.gocahce.ItemCount()), nil
|
||||
}
|
||||
|
||||
// Flush 删除当前已存在的所有key
|
||||
func (c *GoCacheStruct[T]) Flush() {
|
||||
c.gocahce.Flush()
|
||||
}
|
||||
|
||||
// func (c *GoCacheStruct[T]) encode(value T) ([]byte, error) {
|
||||
// return json.Marshal(value)
|
||||
// }
|
||||
|
||||
// func (c *GoCacheStruct[T]) decode(valueByte []byte, value T) error {
|
||||
// return json.Unmarshal(valueByte, value)
|
||||
// }
|
||||
Vendored
+195
@@ -0,0 +1,195 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
redis "github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type RedisCacheStruct[T any] struct {
|
||||
Redis *redis.Client
|
||||
Ctx context.Context
|
||||
HashKey string
|
||||
Result T
|
||||
DefaultExpiration time.Duration
|
||||
CleanupInterval time.Duration
|
||||
}
|
||||
|
||||
type RedisValue[T any] struct {
|
||||
ExpirationTimeStamp int64
|
||||
IsExpiration bool // 是否有过期时间 false // 不过期
|
||||
Value T
|
||||
}
|
||||
|
||||
// cache.New(5*time.Minute, 60*time.Second)
|
||||
func NewRedisCache[T any](redisDb *redis.Client, hashKey string, defaultExpiration time.Duration, cleanupInterval time.Duration) *RedisCacheStruct[T] {
|
||||
obj := RedisCacheStruct[T]{
|
||||
Redis: redisDb,
|
||||
Ctx: context.Background(),
|
||||
HashKey: hashKey,
|
||||
DefaultExpiration: defaultExpiration,
|
||||
CleanupInterval: cleanupInterval,
|
||||
}
|
||||
|
||||
// 创建定时器判断是否过期
|
||||
if obj.CleanupInterval.Seconds() > 0 {
|
||||
go obj.expirationVerification()
|
||||
}
|
||||
|
||||
return &obj
|
||||
}
|
||||
|
||||
func (r *RedisCacheStruct[T]) Set(k string, v T, d time.Duration) {
|
||||
valueEncode := ""
|
||||
value := RedisValue[T]{}
|
||||
|
||||
// 设置过期时间
|
||||
if d.Seconds() > 0 {
|
||||
value.IsExpiration = true
|
||||
value.ExpirationTimeStamp = time.Now().Add(d).Unix()
|
||||
} else {
|
||||
value.IsExpiration = false // 不过期
|
||||
}
|
||||
|
||||
value.Value = v
|
||||
if j, e := json.Marshal(value); e == nil {
|
||||
valueEncode = string(j)
|
||||
}
|
||||
|
||||
r.Redis.HSet(r.Ctx, r.HashKey, k, valueEncode)
|
||||
// second := d.Seconds()
|
||||
// if second > 0 {
|
||||
// // 设置过期时间
|
||||
// err := r.Redis.Do(r.Ctx, "SETEX", r.HashKey+k, second, valueEncode).Err()
|
||||
// fmt.Println("设置结果", err)
|
||||
// } else {
|
||||
// r.Redis.HSet(r.Ctx, r.HashKey, k, valueEncode)
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
func (r *RedisCacheStruct[T]) Get(k string) (T, bool) {
|
||||
var valueEncode []byte
|
||||
value := RedisValue[T]{}
|
||||
cmd := r.Redis.HGet(r.Ctx, r.HashKey, k)
|
||||
if err := cmd.Scan(&valueEncode); err != nil {
|
||||
// log.Println(err)
|
||||
return r.Result, false
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(valueEncode, &value); err != nil {
|
||||
// log.Println(err)
|
||||
return r.Result, false
|
||||
}
|
||||
|
||||
// 已过期,清理掉key
|
||||
if value.IsExpiration && time.Now().Unix() > value.ExpirationTimeStamp {
|
||||
r.Delete(k)
|
||||
return r.Result, false
|
||||
}
|
||||
|
||||
return value.Value, true
|
||||
}
|
||||
|
||||
// 设置cache 无时间参数
|
||||
func (r *RedisCacheStruct[T]) SetDefault(k string, v T) {
|
||||
r.Set(k, v, r.DefaultExpiration)
|
||||
}
|
||||
|
||||
// 设置并保持原始的过期时间
|
||||
func (r *RedisCacheStruct[T]) SetKeepExpiration(k string, v T) {
|
||||
var valueEncode []byte
|
||||
value := RedisValue[T]{}
|
||||
cmd := r.Redis.HGet(r.Ctx, r.HashKey, k)
|
||||
if err := cmd.Scan(&valueEncode); err != nil {
|
||||
// fmt.Println("使用默认的过期时间")
|
||||
r.SetDefault(k, v)
|
||||
return
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(valueEncode, &value); err != nil {
|
||||
// fmt.Println("使用默认的过期时间")
|
||||
r.SetDefault(k, v)
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
timeDiffer := value.ExpirationTimeStamp - now.Unix()
|
||||
|
||||
// 如果设置了过期时间并且过期时间大于现在将保留原始的过期时间
|
||||
if value.IsExpiration && timeDiffer > 0 {
|
||||
// fmt.Println("重新计算过期时间")
|
||||
// fmt.Println("旧的过期时间", value.ExpirationTimeStamp)
|
||||
// fmt.Println("时间限制差", timeDiffer)
|
||||
// fmt.Println("新的过期时间", now.Unix()+timeDiffer)
|
||||
r.Set(k, v, time.Second*time.Duration(timeDiffer))
|
||||
} else {
|
||||
// fmt.Println("使用默认的过期时间")
|
||||
r.SetDefault(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除 cache
|
||||
func (r *RedisCacheStruct[T]) Delete(k string) {
|
||||
r.Redis.HDel(r.Ctx, r.HashKey, k)
|
||||
}
|
||||
|
||||
// Add() 加入缓存
|
||||
// func (r *RedisCacheStruct[T]) Add(k string, v T, d time.Duration) {
|
||||
// c.gocahce.Add(k, x, d)
|
||||
// }
|
||||
|
||||
// IncrementInt() 对已存在的key 值自增n
|
||||
// func (r *RedisCacheStruct[T]) IncrementInt(k string, n int) (num int, err error) {
|
||||
// if err := r.Redis.HIncrBy(r.Ctx, r.HashKey, k, int64(n)).Err(); err != nil {
|
||||
// return num, err
|
||||
// }
|
||||
|
||||
// if v, ok := r.Get(k); ok {
|
||||
// switch T {
|
||||
// case int:
|
||||
|
||||
// }
|
||||
// if vint, okint := v.(int); okint {
|
||||
|
||||
// }
|
||||
// }
|
||||
// return c.gocahce.IncrementInt(k, n)
|
||||
// }
|
||||
|
||||
// ItemCount 获取已存在key的数量
|
||||
func (r *RedisCacheStruct[T]) ItemCount() (int64, error) {
|
||||
if count, err := r.Redis.HLen(r.Ctx, r.HashKey).Result(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return count, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Flush 删除当前已存在的所有key
|
||||
func (r *RedisCacheStruct[T]) Flush() {
|
||||
r.Redis.Del(r.Ctx, r.HashKey)
|
||||
}
|
||||
|
||||
// 定时清理过期验证
|
||||
func (r *RedisCacheStruct[T]) expirationVerification() {
|
||||
ticker := time.NewTicker(r.CleanupInterval)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if fields, err := r.Redis.HKeys(r.Ctx, r.HashKey).Result(); err == nil {
|
||||
for _, v := range fields {
|
||||
// r.Redis.HGet(r.Ctx, r.HashKey, v)
|
||||
r.Get(v)
|
||||
// fmt.Println("redis定时器", v)
|
||||
}
|
||||
}
|
||||
// case <-j.stop:
|
||||
// ticker.Stop()
|
||||
// return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package captcha
|
||||
|
||||
import (
|
||||
"sun-panel/global"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/mojocn/base64Captcha"
|
||||
)
|
||||
|
||||
var Store = base64Captcha.DefaultMemStore
|
||||
|
||||
func NewDriver(width, height int) *base64Captcha.DriverString {
|
||||
driver := new(base64Captcha.DriverString)
|
||||
driver.Height = height
|
||||
driver.Width = width
|
||||
driver.NoiseCount = 0
|
||||
driver.ShowLineOptions = base64Captcha.OptionShowSlimeLine | base64Captcha.OptionShowHollowLine
|
||||
driver.Length = 4
|
||||
driver.Source = "1234567890qwertyuipkjhgfdsazxcvbnm"
|
||||
driver.Fonts = []string{"wqy-microhei.ttc"}
|
||||
return driver
|
||||
}
|
||||
|
||||
// 生成图形验证码
|
||||
func GenerateCaptchaHandler(id string, width, height int) string {
|
||||
var driver = NewDriver(width, height).ConvertFonts()
|
||||
c := base64Captcha.NewCaptcha(driver, Store)
|
||||
_, content, answer := c.Driver.GenerateIdQuestionAnswer()
|
||||
|
||||
item, _ := c.Driver.DrawCaptcha(content)
|
||||
c.Store.Set(id, answer)
|
||||
return item.EncodeB64string()
|
||||
}
|
||||
|
||||
// 验证
|
||||
func CaptchaVerifyHandle(id, vcode string) bool {
|
||||
return Store.Verify(id, vcode, true)
|
||||
}
|
||||
|
||||
// 根据key获取验证码ID
|
||||
func CaptchaGetIdByCookieHeader(c *gin.Context, key string) (captchaId string, err error) {
|
||||
|
||||
captchaId, err = c.Cookie("CaptchaId")
|
||||
if err != nil {
|
||||
global.Logger.Errorf("failed to get captchaId from cookie, err:%+v\n", err)
|
||||
return captchaId, err
|
||||
}
|
||||
if captchaId == "" {
|
||||
captchaId = c.GetHeader(key)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
package cmn
|
||||
|
||||
import (
|
||||
// "calendar-note-gin/assets"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sun-panel/assets"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// 时间格式
|
||||
|
||||
TimeFormatMode1 = "2006-01-02 15:04:05" // 标准格式
|
||||
TimeFormatMode4 = "2006-01-02 15:04" // 标准格式 无秒
|
||||
TimeFormatMode2 = "Mon Jan 2 15:04:05 -0700 MST 2006"
|
||||
TimeFormatMode3 = "Mon, 2 Jan 2006 15:04:05 -0700 MST" // webdav格式
|
||||
TimeYYYY_mm_dd = "2006-01-02"
|
||||
TIME_MODE_REMINDER_TIME = "200601021504" // 提醒定时器的执行时间格式
|
||||
|
||||
// 随机码字典
|
||||
|
||||
RAND_CODE_MODE1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" // 大写,小写,数字
|
||||
RAND_CODE_MODE2 = "abcdefghijklmnopqrstuvwxyz0123456789" // 小写,数字
|
||||
RAND_CODE_MODE3 = "0123456789" // 数字
|
||||
)
|
||||
|
||||
type Version_Info struct {
|
||||
Version string
|
||||
Version_code int
|
||||
}
|
||||
|
||||
func GetTime() string {
|
||||
return time.Unix(time.Now().Unix(), 0).Format(TimeFormatMode1)
|
||||
}
|
||||
|
||||
// 字符串转时间
|
||||
func StrToTime(timeMode, formatTimeStr string) (t time.Time, err error) {
|
||||
loc, err := time.LoadLocation("Local")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t, err = time.ParseInLocation(timeMode, formatTimeStr, loc) //使用模板在对应时区转化为time.time类型
|
||||
return
|
||||
}
|
||||
|
||||
// md5获取
|
||||
func Md5(str string) string {
|
||||
md5Byte := md5.Sum([]byte(str))
|
||||
return hex.EncodeToString(md5Byte[:])
|
||||
}
|
||||
|
||||
func RandNum(n int) int {
|
||||
rand.Seed(time.Now().Unix())
|
||||
return rand.Intn(n)
|
||||
}
|
||||
|
||||
// 随机生成编码
|
||||
// 随机码字典内容 参考常量 RAND_CODE_MODE*
|
||||
func BuildRandCode(count int, secret_content string) (code string) {
|
||||
return BuildRandCodeBySeed(count, secret_content, time.Now().UnixNano()+int64(rand.Intn(100)))
|
||||
}
|
||||
|
||||
// 随机生成编码 参考常量 RAND_CODE_MODE*
|
||||
func BuildRandCodeBySeed(count int, secret_content string, seed int64) (code string) {
|
||||
// 获取纳秒作为随机数种子
|
||||
rand.Seed(seed)
|
||||
if secret_content == "" {
|
||||
secret_content = RAND_CODE_MODE1
|
||||
}
|
||||
for i := 0; i < count; i++ {
|
||||
code += string(secret_content[rand.Intn(len(secret_content))])
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func InSlice(items []string, item string) bool {
|
||||
for _, eachItem := range items {
|
||||
if eachItem == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 字符串转int
|
||||
func StrToInt(str string) int {
|
||||
intStr, _ := strconv.Atoi(str)
|
||||
return intStr
|
||||
}
|
||||
|
||||
// uint 转string
|
||||
func UintToStr(c uint) string {
|
||||
return strconv.FormatUint(uint64(c), 10)
|
||||
}
|
||||
|
||||
// uint 转string
|
||||
func StrToUint(s string) uint {
|
||||
// i, _ := strconv.Atoi(s)
|
||||
u, _ := strconv.ParseUint(s, 10, 64)
|
||||
return uint(u)
|
||||
}
|
||||
|
||||
// 获取系统信息
|
||||
func GetSysVersionInfo() Version_Info {
|
||||
cBytes, _ := assets.Asset("assets/version")
|
||||
c := string(cBytes)
|
||||
info := strings.Split(c, "|")
|
||||
|
||||
return Version_Info{
|
||||
Version_code: StrToInt(info[0]),
|
||||
Version: info[1],
|
||||
}
|
||||
}
|
||||
|
||||
// 文件是否存在
|
||||
func PathExists(path string) (bool, error) {
|
||||
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
// 截取字符串,支持多字节字符
|
||||
// start:起始下标,负数从从尾部开始,最后一个为-1
|
||||
// length:截取长度,负数表示截取到末尾
|
||||
func SubRuneStr(str string, start int, length int) (result string) {
|
||||
s := []rune(str)
|
||||
total := len(s)
|
||||
if total == 0 {
|
||||
return
|
||||
}
|
||||
// 允许从尾部开始计算
|
||||
if start < 0 {
|
||||
start = total + start
|
||||
if start < 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
if start > total {
|
||||
return
|
||||
}
|
||||
// 到末尾
|
||||
if length < 0 {
|
||||
length = total
|
||||
}
|
||||
|
||||
end := start + length
|
||||
if end > total {
|
||||
result = string(s[start:])
|
||||
} else {
|
||||
result = string(s[start:end])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 字符串长度
|
||||
func RuneStrLen(str string) int {
|
||||
return len([]rune(str))
|
||||
}
|
||||
|
||||
// 是否在数组中
|
||||
func InStringArray(arr []string, item string) bool {
|
||||
for _, v := range arr {
|
||||
if v == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 从Assets文件夹中抽取文件保存到路劲
|
||||
// AssetsTakeFileToPath("config.ini", targetPath string)
|
||||
func AssetsTakeFileToPath(assetsPath, targetPath string) error {
|
||||
bytes, _ := assets.Asset("assets/" + assetsPath)
|
||||
targetPathPath := path.Dir(targetPath)
|
||||
exists, err := PathExists(targetPathPath)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
if err := os.MkdirAll(targetPathPath, 0777); err != nil {
|
||||
fmt.Println(456)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return ioutil.WriteFile(targetPath, bytes, 0666)
|
||||
}
|
||||
|
||||
// 密码加密
|
||||
func PasswordEncryption(password string) string {
|
||||
return Md5(Md5(Md5(password)))
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
package cmn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
type LogStruct struct {
|
||||
Writer io.Writer
|
||||
File *os.File
|
||||
Print_cfg bool // 此条打印到控制台
|
||||
Separator string
|
||||
}
|
||||
|
||||
type LogFileld map[string]string
|
||||
|
||||
var (
|
||||
LOG_DEBUG = "Debug"
|
||||
LOG_ERROR = "Error"
|
||||
LOG_Info = "Info"
|
||||
LOG_WARNING = "Warning"
|
||||
)
|
||||
|
||||
// 日志颜色
|
||||
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": " ",
|
||||
}
|
||||
|
||||
// 运行日志静态类
|
||||
var runLogStatic = LogStruct{}
|
||||
|
||||
// 控制台打印
|
||||
// Warning Panic Error Info Debug
|
||||
func Pln(prefix string, msg string) {
|
||||
fmt.Printf(
|
||||
"%s%s %s %s\n",
|
||||
colors[prefix]("["+prefix+"]"),
|
||||
spaces[prefix],
|
||||
time.Now().Format(TimeFormatMode1),
|
||||
msg,
|
||||
)
|
||||
}
|
||||
|
||||
// 控制台打印,支持颜色
|
||||
func Print(color, key, msg string) {
|
||||
fmt.Printf(
|
||||
"%s%s %s\n",
|
||||
colors[color](key),
|
||||
time.Now().Format(TimeFormatMode1),
|
||||
msg,
|
||||
)
|
||||
}
|
||||
|
||||
// func Debug(a ...interface{}) {
|
||||
// fmt.Print("[Debug] ")
|
||||
// fmt.Println(a...)
|
||||
// }
|
||||
|
||||
// // 错误并退出
|
||||
// func ErrorExit(err_title, err_msg string) {
|
||||
// newLog := NewLog("err.log")
|
||||
|
||||
// Pln("Error", err_title+err_msg)
|
||||
// newLog.Error(err_title, err_msg)
|
||||
// os.Exit(1)
|
||||
// }
|
||||
|
||||
// 写入日志的文件
|
||||
func NewLog(log_file_name string) *LogStruct {
|
||||
logStruct := &LogStruct{}
|
||||
logStruct.Separator = ""
|
||||
logDir := path.Dir(log_file_name)
|
||||
ok, _ := PathExists(logDir)
|
||||
if !ok {
|
||||
if err := os.MkdirAll(logDir, 0666); err != nil {
|
||||
fmt.Println("创建日志文件错误", err.Error())
|
||||
}
|
||||
}
|
||||
_, err := os.Stat(log_file_name)
|
||||
if err != nil {
|
||||
f, _ := os.Create(log_file_name)
|
||||
logStruct.File = f
|
||||
logStruct.Writer = io.MultiWriter(f)
|
||||
} else {
|
||||
f, _ := os.OpenFile(log_file_name, os.O_APPEND|os.O_WRONLY, 0666)
|
||||
logStruct.File = f
|
||||
logStruct.Writer = io.MultiWriter(f)
|
||||
}
|
||||
return logStruct
|
||||
}
|
||||
|
||||
// 运行日志直接静态
|
||||
func RunLog() *LogStruct {
|
||||
// 按小时/日/月/年
|
||||
// 先判断文件(夹)是否存在。否多级创建
|
||||
log_file := "res/runtime/log/"
|
||||
ok, _ := PathExists(log_file)
|
||||
if !ok {
|
||||
os.MkdirAll(log_file, 0777)
|
||||
}
|
||||
log_file_name := log_file + time.Unix(time.Now().Unix(), 1).Format("2006-01-02") + ".log"
|
||||
_, err := os.Stat(log_file_name)
|
||||
runLogStatic.Separator = "|"
|
||||
if err != nil {
|
||||
f, _ := os.Create(log_file_name)
|
||||
runLogStatic.File = f
|
||||
runLogStatic.Writer = io.MultiWriter(f)
|
||||
} else {
|
||||
if runLogStatic.File == nil {
|
||||
f, _ := os.OpenFile(log_file_name, os.O_APPEND|os.O_WRONLY, 0666)
|
||||
runLogStatic.File = f
|
||||
runLogStatic.Writer = io.MultiWriter(f)
|
||||
}
|
||||
}
|
||||
return &runLogStatic
|
||||
}
|
||||
|
||||
func (t *LogStruct) Write(content string) (n int, err error) {
|
||||
|
||||
return io.WriteString(t.Writer, content)
|
||||
}
|
||||
|
||||
func (t *LogStruct) Format(log_type string, content string) (n int, err error) {
|
||||
content = log_type + spaces[log_type] + " " + GetTime() + " " + content + "\n"
|
||||
return t.Write(content)
|
||||
}
|
||||
|
||||
func (t *LogStruct) Info(content ...string) (n int, err error) {
|
||||
str := ""
|
||||
for i := 0; i < len(content); i++ {
|
||||
if i != 0 {
|
||||
str += t.Separator + content[i]
|
||||
} else {
|
||||
str += content[i]
|
||||
}
|
||||
}
|
||||
n, err = t.Format("Info", str)
|
||||
if t.Print_cfg == true {
|
||||
Pln("Info", str)
|
||||
t.Print_cfg = false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *LogStruct) Debug(content string) {
|
||||
t.Format("Debug", content)
|
||||
if t.Print_cfg == true {
|
||||
Pln("Debug", content)
|
||||
t.Print_cfg = false
|
||||
}
|
||||
}
|
||||
|
||||
func (t *LogStruct) Error(content ...string) {
|
||||
content_str := ""
|
||||
for i := 0; i < len(content); i++ {
|
||||
if i != 0 {
|
||||
content_str += t.Separator + content[i]
|
||||
} else {
|
||||
content_str += content[i]
|
||||
}
|
||||
}
|
||||
t.Format("Error", content_str)
|
||||
if t.Print_cfg == true {
|
||||
Pln("Error", content_str)
|
||||
t.Print_cfg = false
|
||||
}
|
||||
}
|
||||
|
||||
// // 打印错误
|
||||
// func (t *LogStruct) ErrorPrint(key, value string) {
|
||||
// t.Print_cfg = true
|
||||
// t.Error(key, value)
|
||||
// }
|
||||
|
||||
// // 打印Debug
|
||||
// func (t *LogStruct) DebugPrint(key, value string) {
|
||||
// t.Print_cfg = true
|
||||
// content := key + " " + value
|
||||
// t.Debug(content)
|
||||
// }
|
||||
|
||||
// func (t *LogStruct) Print() *LogStruct {
|
||||
// t.Print_cfg = true
|
||||
// return t
|
||||
// }
|
||||
|
||||
// func (t *LogStruct) FormatFileld(field LogFileld) string {
|
||||
// str := ""
|
||||
// for k, v := range field {
|
||||
// str += k + ":\"" + v + "\"" + t.Separator
|
||||
// }
|
||||
// if len(str) != 0 {
|
||||
// str = str[0 : len(str)-1]
|
||||
// }
|
||||
// return str
|
||||
// }
|
||||
|
||||
// TODO(GgoCoder) 日志轮转
|
||||
func InitLogger(fileName string, level zapcore.LevelEnabler) *zap.SugaredLogger {
|
||||
fileWriteSyncer := getLogWriter(fileName)
|
||||
encoder := getEncoder()
|
||||
core := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(fileWriteSyncer, zapcore.AddSync(os.Stdout)), level)
|
||||
logger := zap.New(core, zap.AddCaller())
|
||||
return logger.Sugar()
|
||||
}
|
||||
|
||||
func getEncoder() zapcore.Encoder {
|
||||
logConf := zap.NewProductionEncoderConfig()
|
||||
logConf.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||
logConf.EncodeLevel = zapcore.CapitalLevelEncoder
|
||||
return zapcore.NewConsoleEncoder(logConf)
|
||||
}
|
||||
|
||||
func getLogWriter(fileName string) zapcore.WriteSyncer {
|
||||
file, err := os.OpenFile(fileName, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
log.Panic("failed to create log file", fileName)
|
||||
}
|
||||
return zapcore.AddSync(file)
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package systemSetting
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"sun-panel/lib/cache"
|
||||
"sun-panel/models"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const (
|
||||
SYSTEM_APPLICATION = "system_application"
|
||||
SYSTEM_EMAIL = "system_email"
|
||||
DISCLAIMER = "disclaimer" // 免责声明 储存类型:字符串
|
||||
WEB_ABOUT_DESCRIPTION = "web_about_description" // 关于的描述信息
|
||||
)
|
||||
|
||||
type SystemSettingCache struct {
|
||||
Cache cache.Cacher[interface{}]
|
||||
}
|
||||
|
||||
type Email struct {
|
||||
Host string `json:"host" binding:"required"`
|
||||
Port int `json:"port" binding:"required"`
|
||||
Mail string `json:"mail" binding:"required,email"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
type Register struct {
|
||||
EmailSuffix string `json:"emailSuffix"` // 注册邮箱后缀
|
||||
OpenRegister bool `json:"openRegister"` // 开放注册
|
||||
}
|
||||
|
||||
type Login struct {
|
||||
LoginCaptcha bool `json:"loginCaptcha"` // 登录验证码
|
||||
}
|
||||
|
||||
type ApplicationSetting struct {
|
||||
Register
|
||||
Login
|
||||
WebSiteUrl string `json:"webSiteUrl"` // 站点地址
|
||||
}
|
||||
|
||||
var (
|
||||
ErrorNoExists = errors.New("no exists")
|
||||
)
|
||||
|
||||
// 系统配置启用缓存功能
|
||||
func (s *SystemSettingCache) GetValueString(configName string) (result string, err error) {
|
||||
if v, ok := s.Cache.Get(configName); ok {
|
||||
if v1, ok1 := v.(string); ok1 {
|
||||
// fmt.Println("读取缓存")
|
||||
return v1, nil
|
||||
}
|
||||
}
|
||||
|
||||
mSetting := models.SystemSetting{}
|
||||
result, err = mSetting.Get(configName)
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
err = ErrorNoExists
|
||||
}
|
||||
// 查询出来,缓存起来
|
||||
s.Cache.SetDefault(configName, result)
|
||||
return
|
||||
}
|
||||
|
||||
// value 需为指针
|
||||
func (s *SystemSettingCache) GetValueByInterface(configName string, value interface{}) error {
|
||||
if v, ok := s.Cache.Get(configName); ok {
|
||||
// fmt.Println("缓存")
|
||||
if s, sok := v.(string); sok {
|
||||
if err := json.Unmarshal([]byte(s), value); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
mSetting := models.SystemSetting{}
|
||||
result, err := mSetting.Get(configName)
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
err = ErrorNoExists
|
||||
return err
|
||||
}
|
||||
err = json.Unmarshal([]byte(result), value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Cache.SetDefault(configName, result)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SystemSettingCache) Set(configName string, configValue interface{}) error {
|
||||
s.Cache.Delete(configName)
|
||||
mSetting := models.SystemSetting{}
|
||||
err := mSetting.Set(configName, configValue)
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package cmn
|
||||
|
||||
import "regexp"
|
||||
|
||||
// 公式
|
||||
const (
|
||||
VERIFY_EXP_USERNAME = `^[a-zA-Z0-9_\.\@]{5,80}$`
|
||||
VERIFY_EXP_PASSWORD = `^[a-zA-Z0-9_\.\&\@]{6,16}$`
|
||||
)
|
||||
|
||||
// 正则验证
|
||||
func VerifyFormat(exp, str string) bool {
|
||||
reg := regexp.MustCompile(exp)
|
||||
return reg.MatchString(str)
|
||||
}
|
||||
|
||||
// 验证邮箱
|
||||
func VerifyEmail(email string) bool {
|
||||
return VerifyFormat(`\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`, email)
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package computerInfo
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"gitlab.com/tingshuo/go-diskstate/diskstate"
|
||||
)
|
||||
|
||||
type Storage struct {
|
||||
Name string
|
||||
FileSystem string
|
||||
Total uint64
|
||||
Free uint64
|
||||
}
|
||||
|
||||
type storageInfo struct {
|
||||
Name string
|
||||
Size uint64
|
||||
FreeSpace uint64
|
||||
FileSystem string
|
||||
Used uint64
|
||||
}
|
||||
|
||||
// func GetStorageInfo() {
|
||||
// var storageinfo []storageInfo
|
||||
// var loaclStorages []Storage
|
||||
// err := wmi.Query("Select * from Win32_LogicalDisk", &storageinfo)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
|
||||
// for _, storage := range storageinfo {
|
||||
// info := Storage{
|
||||
// Name: storage.Name,
|
||||
// FileSystem: storage.FileSystem,
|
||||
// Total: storage.Size / 1024 / 1024 / 1024,
|
||||
// Free: storage.FreeSpace / 1024 / 1024 / 1024,
|
||||
// }
|
||||
// if info.Total >= 1 {
|
||||
// fmt.Printf("%s总大小%dG,可用%dG\n", info.Name, info.Total, info.Free)
|
||||
// loaclStorages = append(loaclStorages, info)
|
||||
// }
|
||||
// }
|
||||
// fmt.Printf("localStorages:= %v\n", loaclStorages)
|
||||
// }
|
||||
|
||||
func GetCurrentStorageInfo(path string) storageInfo {
|
||||
state := diskstate.DiskUsage(path)
|
||||
info := storageInfo{}
|
||||
info.Size = uint64(state.All / diskstate.B)
|
||||
info.FreeSpace = uint64(state.Free / diskstate.B)
|
||||
info.Used = uint64(state.Used / diskstate.B)
|
||||
|
||||
// fmt.Printf("All=%dM, Free=%dM, Available=%dM, Used=%dM, Usage=%d%%",
|
||||
// state.All/diskstate.B, state.Free/diskstate.MB, state.Available/diskstate.MB, state.Used/diskstate.MB, 100*state.Used/state.All)
|
||||
return info
|
||||
}
|
||||
|
||||
type ComputerMonitor struct {
|
||||
CPU float64 `json:"cpu"`
|
||||
Mem float64 `json:"mem"`
|
||||
}
|
||||
|
||||
// GetCPUPercent 获取CPU使用率
|
||||
func GetCPUPercent() float64 {
|
||||
percent, err := cpu.Percent(time.Second, false)
|
||||
if err != nil {
|
||||
log.Fatalln(err.Error())
|
||||
return -1
|
||||
}
|
||||
return percent[0]
|
||||
}
|
||||
|
||||
// GetMemPercent 获取内存使用率
|
||||
func GetMemPercent() float64 {
|
||||
memInfo, err := mem.VirtualMemory()
|
||||
if err != nil {
|
||||
log.Fatalln(err.Error())
|
||||
return -1
|
||||
}
|
||||
return memInfo.UsedPercent
|
||||
}
|
||||
|
||||
func GetCpuMem() ComputerMonitor {
|
||||
var res ComputerMonitor
|
||||
res.CPU = GetCPUPercent()
|
||||
res.Mem = GetMemPercent()
|
||||
return res
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package iniConfig
|
||||
|
||||
import (
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
type IniConfig struct {
|
||||
Err error
|
||||
Config *ini.File
|
||||
Default map[string]map[string]string // 默认配置
|
||||
FileName string
|
||||
}
|
||||
|
||||
// 获取配置
|
||||
func (t *IniConfig) GetValue(section string, name string) *ini.Key {
|
||||
return t.Config.Section(section).Key(name)
|
||||
}
|
||||
|
||||
// 设置配置
|
||||
func (t *IniConfig) SetValue(section string, name string, value string) error {
|
||||
t.Config.Section(section).Key(name).SetValue(value)
|
||||
return t.Config.SaveTo(t.FileName)
|
||||
}
|
||||
|
||||
// 获取配置
|
||||
func (t *IniConfig) GetValueString(section string, name string) string {
|
||||
return t.Config.Section(section).Key(name).String()
|
||||
}
|
||||
|
||||
// 获取字符串配置,如果没有会查找默认值
|
||||
func (t *IniConfig) GetValueStringOrDefault(section string, name string) string {
|
||||
value := t.GetValueString(section, name)
|
||||
if value == "" && t.Default[section] != nil && t.Default[section][name] != "" {
|
||||
return t.Default[section][name]
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
// 获取配置
|
||||
func (t *IniConfig) GetValueInt(section string, name string) int {
|
||||
return t.Config.Section(section).Key(name).MustInt()
|
||||
}
|
||||
|
||||
// 获取组配置
|
||||
func (t *IniConfig) GetSection(section string, result interface{}) error {
|
||||
if group, err := t.Config.GetSection(section); err != nil {
|
||||
return err
|
||||
} else {
|
||||
if err := group.MapTo(result); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除组
|
||||
func (t *IniConfig) DeleteSection(section string) {
|
||||
t.Config.DeleteSection(section)
|
||||
t.Config.SaveTo(t.FileName)
|
||||
}
|
||||
|
||||
// 创建一个配置对象
|
||||
func NewIniConfig(filename string) *IniConfig {
|
||||
config, err := ini.Load(filename)
|
||||
|
||||
return &IniConfig{
|
||||
Err: err,
|
||||
Config: config,
|
||||
FileName: filename,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package jsonConfig
|
||||
|
||||
type SpecialDayModel struct {
|
||||
ConfigModel
|
||||
Data SpecialDayDataModel
|
||||
}
|
||||
|
||||
type SpecialDayDataModel struct {
|
||||
OnlyId string `json:"onlyId"` // 唯一ID
|
||||
Name string `json:"name"` // 名称
|
||||
UpdateTime string `json:"updateTime"` // 更新时间
|
||||
// Year int `json:"year"` // 年份
|
||||
StartDate string `json:"startDate"` // 开始日期
|
||||
EndDate string `json:"endDate"` // 结束日期
|
||||
Days map[string]SpecialDayDataDaysModel `json:"days"` // 天数据
|
||||
}
|
||||
|
||||
type SpecialDayDataDaysModel struct {
|
||||
Holiday bool `json:"holiday"` // 唯一ID
|
||||
Name string `json:"name"` // 名称
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package jsonConfig
|
||||
|
||||
// 事件风格数据
|
||||
type EventStyleDataModel struct {
|
||||
Title string `json:"title"` // 标题
|
||||
ClassName string `json:"className"` // 类名称
|
||||
TextColor string `json:"textColor"` // 字体颜色
|
||||
BackgroundColor string `json:"backgroundColor"` // 背景颜色
|
||||
BorderColor string `json:"borderColor"` // 边框颜色
|
||||
}
|
||||
|
||||
type EventStyleModel struct {
|
||||
ConfigModel
|
||||
Data []EventStyleDataModel
|
||||
}
|
||||
|
||||
func (e *EventStyleModel) GetImportData() error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package jsonConfig
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type JsonConfiger interface {
|
||||
GetImportData() error
|
||||
// ExportFile()
|
||||
}
|
||||
|
||||
type ConfigModel struct {
|
||||
AppName string `json:"AppName"`
|
||||
Ability string `json:"Ability"`
|
||||
Version string `json:"Version"`
|
||||
AbilityVersion string `json:"AbilityVersion"`
|
||||
AppAllowLowVersion string `json:"AppAllowLowVersion"`
|
||||
Data interface{} `json:"Data"`
|
||||
}
|
||||
|
||||
var expoprtSuffix = ".lcn.json"
|
||||
|
||||
const (
|
||||
ABILITY_MODE_EVENT_STYLE = "EventStyle" // 时间风格
|
||||
ABILITY_MODE_SPECIAL_DAY = "SpecialDay" // 特殊的日期
|
||||
)
|
||||
|
||||
// 生成输出文件
|
||||
func BuildExportFile(cfgModel *ConfigModel) ([]byte, error) {
|
||||
content, err := json.Marshal(cfgModel)
|
||||
return content, err
|
||||
}
|
||||
|
||||
func Write(ctx *gin.Context, fileName string, content []byte) {
|
||||
ctx.Writer.Header().Add("Content-Type", "application/octet-stream")
|
||||
ctx.Writer.Header().Add("Content-disposition", "attachment;filename="+fileName+expoprtSuffix)
|
||||
ctx.Writer.Header().Add("Content-Transfer-Encoding", "binary")
|
||||
ctx.Writer.Write(content)
|
||||
}
|
||||
|
||||
func GetImportData(JsonConfiger) {
|
||||
|
||||
}
|
||||
|
||||
func NewConfigModel(ability, abilityVersion string) *ConfigModel {
|
||||
return &ConfigModel{
|
||||
AppName: "Li-Calendar",
|
||||
Version: "1",
|
||||
AppAllowLowVersion: "1",
|
||||
Ability: ability,
|
||||
AbilityVersion: abilityVersion,
|
||||
}
|
||||
}
|
||||
|
||||
// 验证配置模型数据是否相同
|
||||
func ConfigModelCheck(data *ConfigModel, ability, abilityVersion string) bool {
|
||||
newData := NewConfigModel(ability, abilityVersion)
|
||||
if *data != *newData {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// func InportConfigFile(f multipart.FileHeader, eventStyle EventStyleModel) (EventStyleModel, error) {
|
||||
|
||||
// src, err := f.Open()
|
||||
// defer src.Close()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// contentByte, err := ioutil.ReadAll(src)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// configFile := ConfigModel{}
|
||||
// if err := json.Unmarshal(contentByte, &configFile); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// v, ok := configFile.Data.(EventStyleModel)
|
||||
// return errors.New("格式")
|
||||
// if !ok {
|
||||
// return errors.New("格式错误")
|
||||
// }
|
||||
|
||||
// if err := json.Unmarshal(contentByte, &configFile); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// fileExt := strings.ToLower(path.Ext(f.Filename))
|
||||
// fileName := cmn.Md5(fmt.Sprintf("%s%s", f.Filename, time.Now().String()))
|
||||
// fildDir := fmt.Sprintf("%s/%d/%d/%d/", configUpload, time.Now().Year(), time.Now().Month(), time.Now().Day())
|
||||
// isExist, _ := cmn.PathExists(fildDir)
|
||||
// if !isExist {
|
||||
// os.MkdirAll(fildDir, os.ModePerm)
|
||||
// }
|
||||
// filepath := fmt.Sprintf("%s%s%s", fildDir, fileName, fileExt)
|
||||
|
||||
// }
|
||||
@@ -0,0 +1,60 @@
|
||||
package language
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"sun-panel/lib/cmn"
|
||||
"sun-panel/lib/iniConfig"
|
||||
)
|
||||
|
||||
type LangStructObj struct {
|
||||
LangContet *iniConfig.IniConfig
|
||||
}
|
||||
|
||||
func NewLang(langPath string) *LangStructObj {
|
||||
langObj := LangStructObj{}
|
||||
exists, _ := cmn.PathExists(langPath)
|
||||
|
||||
if exists {
|
||||
langObj.LangContet = iniConfig.NewIniConfig(langPath) // 读取配置
|
||||
} else {
|
||||
cmn.Pln(cmn.LOG_ERROR, "language file does not exist:"+langPath)
|
||||
os.Exit(1)
|
||||
}
|
||||
return &langObj
|
||||
}
|
||||
|
||||
// 获取
|
||||
// common.lang
|
||||
// 配置文件格式
|
||||
// [common]
|
||||
// lang=zh-cn
|
||||
func (l *LangStructObj) Get(key string) string {
|
||||
if key == "" {
|
||||
return key
|
||||
}
|
||||
keyArr := strings.Split(key, ".")
|
||||
if len(keyArr) < 2 {
|
||||
return l.LangContet.GetValueString(keyArr[0], "NOT EMPTY")
|
||||
} else {
|
||||
return l.LangContet.GetValueString(keyArr[0], keyArr[1])
|
||||
}
|
||||
}
|
||||
|
||||
// 获取并替换字段
|
||||
func (l *LangStructObj) GetWithFields(key string, fields map[string]string) string {
|
||||
c := l.Get(key)
|
||||
for k, v := range fields {
|
||||
c = strings.ReplaceAll(c, `{`+k+`}`, v)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// 获取值并向后追加
|
||||
func (l *LangStructObj) GetAndInsert(key string, insertContent ...string) string {
|
||||
content := l.Get(key) + " "
|
||||
for _, v := range insertContent {
|
||||
content += v
|
||||
}
|
||||
return content
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package mail
|
||||
|
||||
import (
|
||||
"sun-panel/global"
|
||||
"sun-panel/models"
|
||||
|
||||
"gopkg.in/gomail.v2"
|
||||
)
|
||||
|
||||
type EmailInfo struct {
|
||||
Username string // 账号
|
||||
Password string // 密码
|
||||
Host string // 服务器地址
|
||||
Port int // 端口 默认465
|
||||
}
|
||||
|
||||
type Emailer struct {
|
||||
EmailInfo EmailInfo
|
||||
Dialer *gomail.Dialer
|
||||
}
|
||||
|
||||
func NewEmailer(emailInfo EmailInfo) *Emailer {
|
||||
dialer := gomail.NewDialer(emailInfo.Host, emailInfo.Port, emailInfo.Username, emailInfo.Password)
|
||||
return &Emailer{
|
||||
Dialer: dialer,
|
||||
EmailInfo: emailInfo,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Emailer) Send(mailTo []string, send_name, title, body string) error {
|
||||
m := gomail.NewMessage()
|
||||
m.SetHeader("From", e.EmailInfo.Username)
|
||||
//这种方式可以添加别名,即“XX官方”
|
||||
//说明:如果是用网易邮箱账号发送,以下方法别名可以是中文,如果是qq企业邮箱,以下方法用中文别名,会报错,需要用上面此方法转码
|
||||
//m.SetHeader("From", "FB Sample"+"<"+mailConn["user"]+">") //这种方式可以添加别名,即“FB Sample”, 也可以直接用<code>m.SetHeader("From",mailConn["user"])</code> 读者可以自行实验下效果
|
||||
//m.SetHeader("From", mailConn["user"])
|
||||
m.SetHeader("To", mailTo...) //发送给多个用户
|
||||
m.SetHeader("Subject", title) //设置邮件主题
|
||||
m.SetBody("text/html", body) //设置邮件正文
|
||||
|
||||
return e.Dialer.DialAndSend(m)
|
||||
}
|
||||
|
||||
// 发送邮件
|
||||
func (m *Emailer) SendMail(mailTo string, title, content string) error {
|
||||
fromUrl := "127.0.0.1"
|
||||
appName := global.Lang.Get("common.app_name")
|
||||
mUser := models.User{Mail: mailTo}
|
||||
userInfo := mUser.GetUserInfoByMail()
|
||||
|
||||
nickName := ""
|
||||
if userInfo != nil && userInfo.Name != "" {
|
||||
nickName = " " + userInfo.Name
|
||||
}
|
||||
|
||||
body := `<meta charset="utf-8">
|
||||
<table width="600px" style="max-width: 600px;" align="center">
|
||||
<tr style="width: 600px;background-color: rgb(28, 197, 249);">
|
||||
<td align="left" style="width: 600px;padding: 22px 18px 11px;display: inline-block;">
|
||||
<div style="font-weight: 900;font-size: 18px;">
|
||||
<p>Hi` + nickName + `:</p>
|
||||
</div>
|
||||
</td>
|
||||
<td style="width: 100%;display: inline-block;border-top: 4px dashed rgb(255, 255, 255);"> </td>
|
||||
<td style="width: 600px;padding: 18px;display: inline-block;">
|
||||
<div align="left" style="color: rgb(57, 57, 57); line-height: 1.6; font-size: 14px; margin: 0px;font-weight: bolder;">
|
||||
` + content + `
|
||||
</div>
|
||||
</td>
|
||||
<td style="width: 600px;padding: 18px;display: inline-block;">
|
||||
<div align="rignt">
|
||||
<div style="font-size: 14px; margin: 0px;text-align: right;font-size: 14px; font-weight: bolder;">
|
||||
-- ` + global.Lang.Get("mail.from") + `[<a href="` + fromUrl + `" style="color: #575757;">` + appName + `</a>]</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>`
|
||||
return SendMail(m, []string{mailTo}, appName, title, body)
|
||||
}
|
||||
|
||||
// 发送链接邮件
|
||||
func (m *Emailer) SendMailOfLink(mailTo, title, content, btn_name, url string) error {
|
||||
content = content + getLabelHtmlBtn(btn_name, url)
|
||||
return m.SendMail(mailTo, title, content)
|
||||
}
|
||||
|
||||
// 发送注册邮件
|
||||
func (m *Emailer) SendMailOfRegister(mailTo, key string) error {
|
||||
fromUrl := "127.0.0.1"
|
||||
appName := global.Lang.Get("common.app_name")
|
||||
title := global.Lang.GetWithFields("mail.register_title", map[string]string{
|
||||
"AppName": appName,
|
||||
})
|
||||
content := global.Lang.GetWithFields("mail.register_content", map[string]string{
|
||||
"AppName": appName,
|
||||
})
|
||||
return m.SendMailOfLink(mailTo, title, content, global.Lang.Get("mail.register_click_btn"), fromUrl+"/profile/auth.html#/linkRegister?code="+key)
|
||||
}
|
||||
|
||||
// 发送验证码邮件
|
||||
func (m *Emailer) SendMailOfVCode(mailTo, title, content, vCode string) error {
|
||||
// appName := global.Lang.Get("common.app_name")
|
||||
content = content + getLabelHtmlVcode(vCode)
|
||||
return m.SendMail(mailTo, title, content)
|
||||
}
|
||||
|
||||
// 发送邮件
|
||||
//
|
||||
// @param emailer
|
||||
// @param mailTo
|
||||
// @param send_name
|
||||
// @param title
|
||||
// @param body
|
||||
// @return error
|
||||
func SendMail(emailer *Emailer, mailTo []string, send_name, title, body string) error {
|
||||
//定义邮箱服务器连接信息,如果是网易邮箱 pass填密码,qq邮箱填授权码
|
||||
if emailer.EmailInfo.Port == 0 {
|
||||
emailer.EmailInfo.Port = 465
|
||||
}
|
||||
m := gomail.NewMessage()
|
||||
m.SetHeader("From", m.FormatAddress(emailer.EmailInfo.Username, send_name))
|
||||
//这种方式可以添加别名,即“XX官方”
|
||||
//说明:如果是用网易邮箱账号发送,以下方法别名可以是中文,如果是qq企业邮箱,以下方法用中文别名,会报错,需要用上面此方法转码
|
||||
//m.SetHeader("From", "FB Sample"+"<"+mailConn["user"]+">") //这种方式可以添加别名,即“FB Sample”, 也可以直接用<code>m.SetHeader("From",mailConn["user"])</code> 读者可以自行实验下效果
|
||||
//m.SetHeader("From", mailConn["user"])
|
||||
m.SetHeader("To", mailTo...) //发送给多个用户
|
||||
m.SetHeader("Subject", title) //设置邮件主题
|
||||
m.SetBody("text/html", body) //设置邮件正文
|
||||
|
||||
// d.TLSConfig = &tls.Config{InsecureSkipVerify: true} // 跳过证书验证,不推荐
|
||||
err := emailer.Dialer.DialAndSend(m)
|
||||
return err
|
||||
}
|
||||
|
||||
func getLabelHtmlBtn(btn_name string, href string) string {
|
||||
return `<div><a style="color: #fff;background-color: #2e2e2e;display: inline-block;padding: 10px 30px;border-radius: 5px;" href="` + href + `">` + btn_name + `</a></div>`
|
||||
}
|
||||
|
||||
func getLabelHtmlVcode(vcode string) string {
|
||||
return `<p><div style="color: #fff;background-color: #2e2e2e;display: inline-block;padding: 10px 30px;border-radius: 5px;">` + vcode + `</div></p>`
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package mail
|
||||
|
||||
import (
|
||||
"sun-panel/global"
|
||||
)
|
||||
|
||||
// 发送注册验证码
|
||||
//
|
||||
// @param emailer
|
||||
// @param mailTo 收件人
|
||||
// @param vcode 验证码
|
||||
// @return error
|
||||
func SendRegisterEmail(emailer *Emailer, mailTo, vcode string) error {
|
||||
appName := global.Lang.Get("common.app_name")
|
||||
title := global.Lang.GetWithFields("mail.register_vcode_title", map[string]string{
|
||||
"AppName": appName,
|
||||
})
|
||||
content := global.Lang.GetWithFields("mail.register_vcode_content", map[string]string{
|
||||
"AppName": appName,
|
||||
"Minute": "10",
|
||||
})
|
||||
err := emailer.SendMailOfVCode(mailTo, title, content, vcode)
|
||||
if err != nil {
|
||||
global.Logger.Errorf("failed to send email to %s, err:%+v\n", mailTo, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 发送重置密码验证码
|
||||
//
|
||||
// @param emailer
|
||||
// @param mailTo
|
||||
// @param vcode
|
||||
// @return error
|
||||
func SendResetPasswordVCode(emailer *Emailer, mailTo, vcode string) error {
|
||||
title := global.Lang.Get("mail.reset_password_password_title")
|
||||
content := global.Lang.Get("mail.reset_password_password_content")
|
||||
err := emailer.SendMailOfVCode(mailTo, title, content, vcode)
|
||||
if err != nil {
|
||||
global.Logger.Errorf("failed to send email to %s, err:%+v\n", mailTo, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// // 事件提醒
|
||||
// //
|
||||
// // @param emailer
|
||||
// // @param mailTo
|
||||
// // @param eventReminder
|
||||
// // @return error
|
||||
// func SendEventReminder(emailer *Emailer, mailTo string, eventReminder models.EventReminder) error {
|
||||
// startTime := eventReminder.Event.StartTime.Time.Format(cmn.TimeFormatMode4)
|
||||
// endTime := eventReminder.Event.EndTime.Time.Format(cmn.TimeFormatMode4)
|
||||
// title := global.Lang.GetWithFields("mail.reminder_title", map[string]string{
|
||||
// "Title": eventReminder.Event.Title,
|
||||
// "Time": startTime,
|
||||
// })
|
||||
// contentParam := map[string]string{
|
||||
// "ItemTitle": eventReminder.Event.Item.Title,
|
||||
// "Time": startTime,
|
||||
// }
|
||||
// content := "<p>" + global.Lang.GetWithFields("mail.reminder_content", contentParam) + "</p>"
|
||||
// content += "<p>" + global.Lang.Get("mail.reminder_event_title") + " : " + eventReminder.Event.Title + "</p>"
|
||||
// content += "<p>" + global.Lang.Get("common.start_time") + " : " + startTime + "</p>"
|
||||
// content += "<p>" + global.Lang.Get("common.end_time") + " : " + endTime + "</p>"
|
||||
|
||||
// err := emailer.SendMail(mailTo, title, content)
|
||||
// if err != nil {
|
||||
// global.Logger.Errorf("failed to send email to %s, err:%+v\n", mailTo, err)
|
||||
// }
|
||||
// return err
|
||||
// }
|
||||
@@ -0,0 +1,21 @@
|
||||
package queue
|
||||
|
||||
// 队列器
|
||||
type Queuer interface {
|
||||
// 左侧插入
|
||||
LPush(value ...interface{}) error
|
||||
// 右侧插入
|
||||
RPush(value ...interface{}) error
|
||||
// 删除元素
|
||||
Delete(value interface{}) error
|
||||
// 使用下标获取值
|
||||
GetByIndex(index int64, v interface{}) error
|
||||
// 左侧读取并删除
|
||||
LPop(v interface{}) error
|
||||
// 右侧读取并删除
|
||||
RPop(v interface{}) error
|
||||
// 队列长度
|
||||
Length() (int64, error)
|
||||
// 清空队列
|
||||
Flush() error
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package queueMemory
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Pool struct {
|
||||
Values [][]byte
|
||||
Lock sync.RWMutex
|
||||
}
|
||||
|
||||
func New() *Pool {
|
||||
return &Pool{}
|
||||
}
|
||||
|
||||
func (k *Pool) LPush(value ...interface{}) error {
|
||||
k.Lock.Lock()
|
||||
defer k.Lock.Unlock()
|
||||
for i := 0; i < len(value); i++ {
|
||||
v, _ := json.Marshal(value[i])
|
||||
k.Values = append([][]byte{v}, k.Values...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Pool) RPush(value ...interface{}) error {
|
||||
k.Lock.Lock()
|
||||
defer k.Lock.Unlock()
|
||||
for i := 0; i < len(value); i++ {
|
||||
v, _ := json.Marshal(value[i])
|
||||
k.Values = append(k.Values, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Pool) Delete(value interface{}) error {
|
||||
k.Lock.Lock()
|
||||
defer k.Lock.Unlock()
|
||||
var index int64
|
||||
v, _ := json.Marshal(value)
|
||||
|
||||
for i, item := range k.Values {
|
||||
if reflect.DeepEqual(item, v) {
|
||||
index = int64(i)
|
||||
k.removeIndex(index)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 取出值
|
||||
func (k *Pool) GetByIndex(index int64, v interface{}) error {
|
||||
k.Lock.RLock()
|
||||
defer k.Lock.RUnlock()
|
||||
if int64(len(k.Values)) >= index {
|
||||
json.Unmarshal(k.Values[index], v)
|
||||
return nil
|
||||
} else {
|
||||
return errors.New("index non-existent")
|
||||
}
|
||||
}
|
||||
|
||||
// 左-取出并删除
|
||||
func (k *Pool) LPop(v interface{}) error {
|
||||
if err := k.GetByIndex(0, v); err != nil {
|
||||
return err
|
||||
} else {
|
||||
k.removeIndex(0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 右-取出并删除
|
||||
func (k *Pool) RPop(v interface{}) error {
|
||||
index := int64(len(k.Values) - 1)
|
||||
if err := k.GetByIndex(index, v); err != nil {
|
||||
return err
|
||||
} else {
|
||||
k.removeIndex(index)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Pool) removeIndex(index int64) error {
|
||||
k.Lock.Lock()
|
||||
defer k.Lock.Unlock()
|
||||
k.Values = append(k.Values[:index], k.Values[index+1:]...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Pool) Length() (int64, error) {
|
||||
return int64(len(k.Values)), nil
|
||||
}
|
||||
|
||||
func (k *Pool) Flush() error {
|
||||
k.Lock.Lock()
|
||||
defer k.Lock.Unlock()
|
||||
k.Values = nil
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package queueRedis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type Pool struct {
|
||||
Ctx context.Context
|
||||
Redis *redis.Client
|
||||
Name string
|
||||
}
|
||||
|
||||
func New(redisDb *redis.Client, name string) *Pool {
|
||||
ctx := context.Background()
|
||||
return &Pool{
|
||||
Ctx: ctx,
|
||||
Redis: redisDb,
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (k *Pool) LPush(value ...interface{}) error {
|
||||
var values []interface{}
|
||||
for _, v := range value {
|
||||
values = append(values, k.encode(v))
|
||||
}
|
||||
|
||||
return k.Redis.LPush(k.Ctx, k.Name, values...).Err()
|
||||
}
|
||||
|
||||
func (k *Pool) RPush(value ...interface{}) error {
|
||||
var values []interface{}
|
||||
for _, v := range value {
|
||||
values = append(values, k.encode(v))
|
||||
}
|
||||
return k.Redis.RPush(k.Ctx, k.Name, values...).Err()
|
||||
}
|
||||
|
||||
func (k *Pool) Delete(value interface{}) error {
|
||||
return k.Redis.LRem(k.Ctx, k.Name, 0, k.encode(value)).Err()
|
||||
}
|
||||
|
||||
// 取出值
|
||||
func (k *Pool) GetByIndex(index int64, v interface{}) error {
|
||||
if d, err := k.Redis.LIndex(k.Ctx, k.Name, index).Result(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
k.decode(d, v)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 左-取出并删除
|
||||
func (k *Pool) LPop(v interface{}) error {
|
||||
if d, err := k.Redis.LPop(k.Ctx, k.Name).Result(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
k.decode(d, v)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 右-取出并删除
|
||||
func (k *Pool) RPop(v interface{}) error {
|
||||
if d, err := k.Redis.RPop(k.Ctx, k.Name).Result(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
k.decode(d, v)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (k *Pool) encode(value any) string {
|
||||
data, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return "{}"
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func (k *Pool) decode(v string, value interface{}) {
|
||||
err := json.Unmarshal([]byte(v), value)
|
||||
_ = err
|
||||
}
|
||||
|
||||
func (k *Pool) Length() (int64, error) {
|
||||
r := k.Redis.LLen(k.Ctx, k.Name)
|
||||
return r.Result()
|
||||
}
|
||||
|
||||
func (k *Pool) Flush() error {
|
||||
return k.Redis.Del(k.Ctx, k.Name).Err()
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Logout(c *gin.Context) {
|
||||
c.SetCookie("cloud_tk", "", 0, "/source/", "", false, true)
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user