Projects STRLCPY SeaMoon Commits e73154f8
🤬
  • ■ ■ ■ ■ ■
    cmd/client/client.go
    skipped 20 lines
    21 21  )
    22 22   
    23 23  func Serve(ctx context.Context, debug bool) {
    24  - // 控制总线,用于管控服务相关
    25  - go signal.Signal().Daemon(ctx)
     24 + // Signal 异步服务
     25 + runSignal(ctx)
    26 26   // Restful API 服务
    27 27   runApi(ctx, debug)
    28 28  }
    29 29   
     30 +func runSignal(ctx context.Context) {
     31 + // 控制总线,用于管控服务相关
     32 + go signal.Signal().Daemon(ctx)
     33 + // 如果配置了自动恢复设置,尝试发送恢复信号
     34 + rec, err := service.SVC.GetConfigByName(ctx, "auto_start")
     35 + if err != nil {
     36 + xlog.Error(errors.SignalGetObjError, "err", err)
     37 + return
     38 + }
     39 + signal.Signal().Recover(ctx, rec.Value)
     40 +}
     41 + 
    30 42  func runApi(ctx context.Context, debug bool) {
    31 43   logPath, err := service.SVC.GetConfigByName(ctx, "control_log")
    32 44   addr, err := service.SVC.GetConfigByName(ctx, "control_addr")
    skipped 7 lines
    40 52   
    41 53   webLogger, err := os.OpenFile(logPath.Value, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
    42 54   if err != nil {
    43  - gin.DefaultWriter = io.MultiWriter(os.Stdout)
     55 + gin.DefaultWriter = io.MultiWriter(xlog.Logger())
    44 56   } else {
    45  - gin.DefaultWriter = io.MultiWriter(webLogger)
     57 + gin.DefaultWriter = io.MultiWriter(xlog.Logger(), webLogger)
    46 58   }
    47 59   
    48 60   server := gin.Default()
    skipped 18 lines
  • ■ ■ ■ ■ ■ ■
    go.mod
    skipped 10 lines
    11 11   github.com/gin-gonic/gin v1.9.1
    12 12   github.com/glebarez/sqlite v1.10.0
    13 13   github.com/golang-jwt/jwt v3.2.2+incompatible
     14 + github.com/gookit/color v1.5.4
    14 15   github.com/gorilla/websocket v1.5.1
    15 16   github.com/pkg/errors v0.9.1
    16 17   github.com/showwin/speedtest-go v1.6.10
    skipped 2 lines
    19 20   github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cam v1.0.866
    20 21   github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.866
    21 22   github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.866
    22  - github.com/tg123/go-htpasswd v1.2.0
    23 23   github.com/v2fly/v2ray-core/v5 v5.15.1
    24 24   google.golang.org/grpc v1.62.1
    25 25   google.golang.org/protobuf v1.33.0
    skipped 6 lines
    32 32  )
    33 33   
    34 34  require (
    35  - github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 // indirect
    36 35   github.com/adrg/xdg v0.4.0 // indirect
    37 36   github.com/aead/cmac v0.0.0-20160719120800-7af84192f0b1 // indirect
    38 37   github.com/ajg/form v1.5.1 // indirect
    skipped 84 lines
    123 122   github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
    124 123   github.com/vincent-petithory/dataurl v1.0.0 // indirect
    125 124   github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432 // indirect
     125 + github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
    126 126   github.com/xtaci/smux v1.5.24 // indirect
    127 127   go.starlark.net v0.0.0-20230612165344-9532f5667272 // indirect
    128 128   go.uber.org/mock v0.3.0 // indirect
    skipped 38 lines
  • ■ ■ ■ ■ ■ ■
    go.sum
    skipped 15 lines
    16 16  github.com/DVKunion/fc-go-sdk v0.0.0-20240229064818-0899a1e1bf52 h1:RAsZRNTN5IK7osUzHL/IdgUeGPHwDG9aFTGj/HFcqzI=
    17 17  github.com/DVKunion/fc-go-sdk v0.0.0-20240229064818-0899a1e1bf52/go.mod h1:rYnxfOnoHRZ2QGKREUOESel1hmRe0frzF2RLpcDVtTU=
    18 18  github.com/FlowerWrong/water v0.0.0-20180301012659-01a4eaa1f6f2/go.mod h1:xrG5L7lq7T2DLnPr2frMnL906CNEoKRwLB+VYFhPq2w=
    19  -github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 h1:KeNholpO2xKjgaaSyd+DyQRrsQjhbSeS7qe4nEw8aQw=
    20  -github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo=
    21 19  github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
    22 20  github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
    23 21  github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
    skipped 193 lines
    217 215  github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
    218 216  github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
    219 217  github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
     218 +github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
     219 +github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
    220 220  github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
    221 221  github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
    222 222  github.com/gopherjs/gopherjs v0.0.0-20210420193930-a4630ec28c79 h1:ATVz3rDvK4xX0nHx57zYSHRVIK/+lFwln9KJr8wvuk0=
    skipped 254 lines
    477 477  github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.866/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
    478 478  github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.866 h1:LIMZ2+Cob57pryoNjPckpC5QSveSRkCPy8T8aYY35bg=
    479 479  github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.866/go.mod h1:UXHY/ZxZm9E62mEDacQ8Izc8vR+BtBSLjBbLtuH287o=
    480  -github.com/tg123/go-htpasswd v1.2.0 h1:UKp34m9H467/xklxUxU15wKRru7fwXoTojtxg25ITF0=
    481  -github.com/tg123/go-htpasswd v1.2.0/go.mod h1:h7IzlfpvIWnVJhNZ0nQ9HaFxHb7pn5uFJYLlEUJa2sM=
    482 480  github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
    483 481  github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
    484 482  github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
    skipped 17 lines
    502 500  github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
    503 501  github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432 h1:I/ATawgO2RerCq9ACwL0wBB8xNXZdE3J+93MCEHReRs=
    504 502  github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432/go.mod h1:QN7Go2ftTVfx0aCTh9RXHV8pkpi0FtmbwQw40dy61wQ=
     503 +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
     504 +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
    505 505  github.com/xtaci/smux v1.5.12/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
    506 506  github.com/xtaci/smux v1.5.15/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
    507 507  github.com/xtaci/smux v1.5.24 h1:77emW9dtnOxxOQ5ltR+8BbsX1kzcOxQ5gB+aaV9hXOY=
    skipped 19 lines
    527 527  golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
    528 528  golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
    529 529  golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
    530  -golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
    531 530  golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
    532 531  golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
    533 532  golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
    skipped 314 lines
  • ■ ■ ■ ■ ■
    pkg/api/controller/v1/proxy.go
    skipped 10 lines
    11 11   "github.com/DVKunion/SeaMoon/pkg/api/service"
    12 12   "github.com/DVKunion/SeaMoon/pkg/signal"
    13 13   "github.com/DVKunion/SeaMoon/pkg/system/errors"
     14 + "github.com/DVKunion/SeaMoon/pkg/tools"
    14 15  )
    15 16   
    16 17  func ListProxies(c *gin.Context) {
    skipped 71 lines
    88 89   
    89 90   // 朝着队列发送控制信号
    90 91   signal.Signal().SendProxySignal(obj.ID, *obj.Status)
    91  - 
    92  - if res, err := service.SVC.UpdateProxy(c, uint(id), obj.ToModel(false)); err != nil {
     92 + m := obj.ToModel(false)
     93 + // 这里愚蠢de做一个特殊处理: 当代理关闭时,自动将连接数清零
     94 + if *obj.Status == enum.ProxyStatusInactive {
     95 + m.Conn = tools.IntPtr(0)
     96 + }
     97 + if res, err := service.SVC.UpdateProxy(c, uint(id), m); err != nil {
    93 98   servant.ErrorMsg(c, http.StatusInternalServerError, errors.ApiError(errors.ApiServiceError, err))
    94 99   } else {
    95 100   servant.SuccessMsg(c, 1, res.ToApi())
    skipped 37 lines
  • ■ ■ ■ ■ ■ ■
    pkg/api/models/config.go
    skipped 19 lines
    20 20   Value: "seamoon-web.log",
    21 21   },
    22 22   {
     23 + Key: "auto_start",
     24 + Value: "true",
     25 + },
     26 + {
    23 27   Key: "version",
    24 28   Value: consts.Version,
    25 29   },
    skipped 15 lines
    41 45   ControlAddr string `json:"control_addr"`
    42 46   ControlPort string `json:"control_port"`
    43 47   ControlLog string `json:"control_log"`
     48 + AutoStart string `json:"auto_start"`
    44 49   
    45 50   Version string `json:"version"`
    46 51  }
    skipped 13 lines
    60 65   Key: "control_log",
    61 66   Value: c.ControlLog,
    62 67   })
    63  - // 不处理 version
    64  - //res = append(res, &Config{
    65  - // Key: "version",
    66  - // Value: c.Version,
    67  - //})
     68 + res = append(res, &Config{
     69 + Key: "auto_start",
     70 + Value: c.AutoStart,
     71 + })
    68 72   
    69 73   return res
    70 74  }
    skipped 8 lines
    79 83   res.ControlPort = s.Value
    80 84   case "control_log":
    81 85   res.ControlLog = s.Value
     86 + case "auto_start":
     87 + res.AutoStart = s.Value
    82 88   case "version":
    83 89   res.Version = s.Value
    84 90   }
    skipped 4 lines
  • ■ ■ ■ ■ ■
    pkg/signal/signal.go
    skipped 68 lines
    69 69   }
    70 70   server, err := listener.TCPListen(sigCtx, proxy, tun)
    71 71   if err != nil {
    72  - xlog.Error(errors.SignalListenerError, "id", pys.id, "addr", proxy.Addr(), "err", err)
     72 + xlog.Error(errors.SignalListenerError, "id", pys.id, "type", proxy.Type, "addr", proxy.Addr(), "err", err)
    73 73   }
    74 74   sb.canceler[pys.id] = cancel
    75 75   sb.listener[pys.id] = server
    76  - xlog.Info(xlog.SignalListenStart, "id", pys.id, "addr", proxy.Addr())
     76 + xlog.Info(xlog.SignalListenStart, "id", pys.id, "type", proxy.Type, "addr", proxy.Addr())
    77 77   case enum.ProxyStatusInactive:
    78 78   if cancel, ok := sb.canceler[pys.id]; ok {
    79 79   // 先调一下 cancel
    skipped 3 lines
    83 83   err := ln.Close()
    84 84   if err != nil {
    85 85   // 错了就错了吧,说明 ctx 挂了一般 goroutines 也跟着挂了
    86  - xlog.Error(errors.SignalListenerError, "id", pys.id, "addr", proxy.Addr(), "err", err)
     86 + xlog.Error(errors.SignalListenerError, "id", pys.id, "type", proxy.Type, "addr", proxy.Addr(), "err", err)
    87 87   }
    88 88   }
    89 89   }
    90  - xlog.Info(xlog.SignalListenStop, "id", pys.id, "addr", proxy.Addr())
     90 + xlog.Info(xlog.SignalListenStop, "id", pys.id, "type", proxy.Type, "addr", proxy.Addr())
    91 91   case enum.ProxyStatusSpeeding:
    92 92   if err = service.SVC.SpeedProxy(ctx, proxy); err != nil {
    93 93   _ = service.SVC.UpdateProxyStatus(ctx, proxy.ID, enum.ProxyStatusError, err.Error())
    94  - xlog.Error(errors.SignalSpeedTestError, "id", pys.id, "addr", proxy.Addr(), "err", err)
     94 + xlog.Error(errors.SignalSpeedTestError, "id", pys.id, "type", proxy.Type, "addr", proxy.Addr(), "err", err)
    95 95   }
    96 96   if err = service.SVC.UpdateProxyStatus(ctx, proxy.ID, enum.ProxyStatusActive, ""); err != nil {
    97  - xlog.Error(errors.SignalUpdateObjError, "id", pys.id, "addr", proxy.Addr(), "err", err)
     97 + xlog.Error(errors.SignalUpdateObjError, "id", pys.id, "type", proxy.Type, "addr", proxy.Addr(), "err", err)
    98 98   }
    99 99   }
    100 100   case prs := <-sb.providerChannel:
    skipped 8 lines
    109 109   if err != nil {
    110 110   xlog.Error(errors.SignalGetObjError, "err", err)
    111 111   }
     112 + }
     113 + }
     114 +}
     115 + 
     116 +func (sb *Bus) Recover(ctx context.Context, recover string) {
     117 + // 首先看一下是否需要恢复运行状态的服务
     118 + proxies, err := service.SVC.ListActiveProxies(ctx)
     119 + if err != nil {
     120 + xlog.Error(errors.SignalRecoverProxyError, "err", err)
     121 + }
     122 + for _, p := range proxies {
     123 + if recover == "true" {
     124 + xlog.Info(xlog.SignalListenRecover, "id", p.ID, "type", p.Type, "addr", p.Addr())
     125 + sb.SendProxySignal(p.ID, *p.Status)
     126 + } else {
     127 + _ = service.SVC.UpdateProxyStatus(ctx, p.ID, enum.ProxyStatusInactive, "reboot")
    112 128   }
    113 129   }
    114 130  }
    skipped 22 lines
  • ■ ■ ■ ■ ■ ■
    pkg/system/errors/consts.go
    skipped 54 lines
    55 55   
    56 56  // SIGNAL 相关错误
    57 57  const (
    58  - SignalGetObjError = "signal try to get object from db error"
    59  - SignalUpdateObjError = "signal try to update object from db error"
    60  - SignalListenerError = "signal listener unexpect error"
    61  - SignalSpeedTestError = "signal try test speed error"
     58 + SignalGetObjError = "signal try to get object from db error"
     59 + SignalUpdateObjError = "signal try to update object from db error"
     60 + SignalListenerError = "signal listener unexpect error"
     61 + SignalSpeedTestError = "signal try test speed error"
     62 + SignalRecoverProxyError = "signal try recover active proxy error"
    62 63  )
    63 64   
  • ■ ■ ■ ■ ■
    pkg/system/xlog/consts.go
    skipped 16 lines
    17 17   
    18 18  // SIGNAL 相关日志
    19 19  const (
    20  - SignalListenStart = "signal start proxy listener"
    21  - SignalListenStop = "signal stop proxy listener"
     20 + SignalListenStart = "signal start proxy listener"
     21 + SignalListenStop = "signal stop proxy listener"
     22 + SignalListenRecover = "signal send recover proxy signal"
    22 23  )
    23 24   
    24 25  // DB 相关日志
    skipped 11 lines
  • ■ ■ ■ ■ ■ ■
    pkg/system/xlog/handler.go
     1 +package xlog
     2 + 
     3 +//
     4 +//import (
     5 +// "bytes"
     6 +// "context"
     7 +// "fmt"
     8 +// "io"
     9 +// "log/slog"
     10 +// "os"
     11 +//
     12 +// "github.com/gookit/color"
     13 +//)
     14 +//
     15 +//type Handler struct {
     16 +// level slog.Level
     17 +// path string
     18 +//}
     19 +//
     20 +//func (h Handler) Enabled(_ context.Context, level slog.Level) bool {
     21 +// return level >= h.level
     22 +//}
     23 +//
     24 +//func (h Handler) Handle(_ context.Context, r slog.Record) error {
     25 +// var buf bytes.Buffer
     26 +// buf.WriteString(h.color(r.Level, fmt.Sprintf("[%s]", r.Level.String())))
     27 +// buf.WriteByte(' ')
     28 +// buf.WriteString(h.color(r.Level, r.Message))
     29 +// return h.print(&buf)
     30 +//}
     31 +//
     32 +//func (h Handler) WithAttrs(attrs []slog.Attr) slog.Handler {
     33 +// //TODO implement me
     34 +// panic("implement me")
     35 +//}
     36 +//
     37 +//func (h Handler) WithGroup(name string) slog.Handler {
     38 +// //TODO implement me
     39 +// panic("implement me")
     40 +//}
     41 +//
     42 +//func (h Handler) color(level slog.Level, msg string) string {
     43 +// switch level {
     44 +// case slog.LevelDebug:
     45 +// return color.Gray.Sprintf(msg)
     46 +// case slog.LevelInfo:
     47 +// return color.Cyan.Sprintf(msg)
     48 +// case slog.LevelWarn:
     49 +// return color.Yellow.Sprintf(msg)
     50 +// case slog.LevelError:
     51 +// return color.Red.Sprintf(msg)
     52 +// }
     53 +// return ""
     54 +//}
     55 +//
     56 +//func (h Handler) print(reader io.Reader) error {
     57 +// if h.path != "" {
     58 +// file, err := os.OpenFile(h.path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
     59 +// if err != nil {
     60 +// return err
     61 +// }
     62 +// writer := io.MultiWriter(os.Stderr, file)
     63 +// _, err = io.Copy(writer, reader)
     64 +// return err
     65 +// }
     66 +// _, err := io.Copy(os.Stderr, reader)
     67 +// return err
     68 +//}
     69 + 
  • ■ ■ ■ ■ ■
    pkg/system/xlog/logger.go
    skipped 3 lines
    4 4   "fmt"
    5 5   "log/slog"
    6 6   "strings"
     7 + 
     8 + "github.com/gookit/color"
    7 9  )
    8 10   
    9  -var defaultLog = slog.Default()
     11 +type logger struct {
     12 + *slog.Logger
     13 +}
     14 + 
     15 +func (l logger) Write(p []byte) (n int, err error) {
     16 + s := strings.Replace(string(p), "\n", "", -1)
     17 + group := "[GIN] "
     18 + if strings.Contains(s, "[GIN") {
     19 + group = ""
     20 + }
     21 + if strings.Contains(s, "debug") {
     22 + l.Debug(color.Gray.Sprintf("%s%s", group, s))
     23 + }
     24 + if strings.Contains(s, "WARNING") || strings.Contains(s, "| 403 |") ||
     25 + strings.Contains(s, "| 401 |") || strings.Contains(s, "| 400 |") {
     26 + l.Warn(color.Yellow.Sprintf("%s%s", group, s))
     27 + }
     28 + 
     29 + if strings.Contains(s, "| 500 |") {
     30 + l.Error(color.Red.Sprintf("%s%s", group, s))
     31 + } else {
     32 + l.Info(color.Cyan.Sprintf("%s%s", group, s))
     33 + }
     34 + return len(p), nil
     35 +}
     36 + 
     37 +var defaultLog = &logger{slog.Default()}
    10 38   
    11 39  var index = "UNEXPECT"
    12 40   
    13  -func Logger() *slog.Logger {
     41 +func Logger() *logger {
    14 42   return defaultLog
    15 43  }
    16 44   
    skipped 10 lines
    27 55   if strings.Contains(msg, " ") {
    28 56   index = strings.ToUpper(strings.Split(msg, " ")[0])
    29 57   }
    30  - defaultLog.Info(fmt.Sprintf("[%s] %s", index, msg), args...)
     58 + defaultLog.Info(color.Cyan.Sprintf("[%s] %s", index, msg), args...)
    31 59  }
    32 60   
    33 61  // Warn logs at LevelWarn.
    skipped 1 lines
    35 63   if strings.Contains(msg, " ") {
    36 64   index = strings.ToUpper(strings.Split(msg, " ")[0])
    37 65   }
    38  - defaultLog.Info(fmt.Sprintf("[%s] %s", index, msg), args...)
     66 + defaultLog.Info(color.Yellow.Sprintf("[%s] %s", index, msg), args...)
    39 67  }
    40 68   
    41 69  // Error logs at LevelError.
    skipped 1 lines
    43 71   if strings.Contains(msg, " ") {
    44 72   index = strings.ToUpper(strings.Split(msg, " ")[0])
    45 73   }
    46  - defaultLog.Error(fmt.Sprintf("[%s] %s", index, msg), args...)
     74 + defaultLog.Error(color.Red.Sprintf("[%s] %s", index, msg), args...)
     75 +}
     76 + 
     77 +func renderArgs(args ...any) {
     78 + 
    47 79  }
    48 80   
Please wait...
Page is in error, reload to recover