From fe428ed412ad7af0a64427f2d84a59e4e5e64124 Mon Sep 17 00:00:00 2001 From: Alireza Ahmadi Date: Fri, 3 Jan 2025 23:32:03 +0100 Subject: [PATCH] all adjustments --- backend/api/api.go | 118 ++- backend/api/utils.go | 2 +- backend/app/app.go | 4 +- backend/cmd/migration/1_2.go | 17 +- backend/core/box.go | 28 +- backend/core/endpoint.go | 4 +- backend/cronjob/checkCoreJob.go | 17 + backend/cronjob/cronJob.go | 10 +- backend/cronjob/depleteJob.go | 4 +- backend/database/model/endpoints.go | 12 +- backend/database/model/inbounds.go | 36 +- backend/database/model/outbounds.go | 8 +- backend/go.mod | 16 +- backend/go.sum | 21 + backend/logger/logger.go | 2 +- backend/service/client.go | 132 +++- backend/service/config.go | 369 ++------- backend/service/endpoints.go | 83 +- backend/service/inbounds.go | 186 ++++- backend/service/outbounds.go | 83 +- backend/service/setting.go | 8 + backend/service/stats.go | 8 +- backend/service/tls.go | 52 +- backend/sub/jsonService.go | 17 +- frontend/package-lock.json | 710 +++++++++--------- frontend/src/components/Listen.vue | 2 + frontend/src/components/OutJson.vue | 28 +- frontend/src/components/WgPeer.vue | 23 +- .../src/components/protocols/Wireguard.vue | 99 +-- frontend/src/components/tls/InTLS.vue | 223 +----- frontend/src/layouts/default/AppBar.vue | 23 +- frontend/src/layouts/default/Drawer.vue | 1 + frontend/src/layouts/modals/Client.vue | 16 +- frontend/src/layouts/modals/ClientBulk.vue | 9 +- frontend/src/layouts/modals/Endpoint.vue | 153 ++++ frontend/src/layouts/modals/Inbound.vue | 105 +-- frontend/src/layouts/modals/Outbound.vue | 14 +- frontend/src/layouts/modals/Tls.vue | 18 +- frontend/src/locales/en.ts | 7 +- frontend/src/locales/fa.ts | 7 +- frontend/src/locales/ru.ts | 7 +- frontend/src/locales/vi.ts | 5 +- frontend/src/locales/zhcn.ts | 5 +- frontend/src/locales/zhtw.ts | 5 +- frontend/src/plugins/inData.ts | 15 - frontend/src/plugins/link.ts | 118 +-- frontend/src/plugins/outJson.ts | 37 +- frontend/src/plugins/wgUtil.ts | 188 +++++ frontend/src/router/index.ts | 5 + frontend/src/store/modules/data.ts | 119 +-- frontend/src/types/clients.ts | 2 +- frontend/src/types/config.ts | 23 - frontend/src/types/endpoints.ts | 58 ++ frontend/src/types/inbounds.ts | 38 +- frontend/src/types/outbounds.ts | 20 +- frontend/src/views/Basics.vue | 144 +--- frontend/src/views/Clients.vue | 142 +--- frontend/src/views/Endpoints.vue | 166 ++++ frontend/src/views/Inbounds.vue | 287 +++---- frontend/src/views/Outbounds.vue | 81 +- frontend/src/views/Rules.vue | 34 +- frontend/src/views/Tls.vue | 153 ++-- 62 files changed, 2352 insertions(+), 1975 deletions(-) create mode 100644 backend/cronjob/checkCoreJob.go create mode 100644 frontend/src/layouts/modals/Endpoint.vue delete mode 100644 frontend/src/plugins/inData.ts create mode 100644 frontend/src/plugins/wgUtil.ts create mode 100644 frontend/src/types/endpoints.ts create mode 100644 frontend/src/views/Endpoints.vue diff --git a/backend/api/api.go b/backend/api/api.go index dd596ed..f562b08 100644 --- a/backend/api/api.go +++ b/backend/api/api.go @@ -1,9 +1,11 @@ package api import ( + "encoding/json" "s-ui/logger" "s-ui/service" "s-ui/util" + "s-ui/util/common" "strconv" "strings" @@ -17,6 +19,8 @@ type APIHandler struct { service.ClientService service.TlsService service.InboundService + service.OutboundService + service.EndpointService service.PanelService service.StatsService service.ServerService @@ -43,6 +47,7 @@ func (a *APIHandler) postHandler(c *gin.Context) { var err error action := c.Param("postAction") remoteIP := getRemoteIp(c) + loginUser := GetLoginUser(c) switch action { case "login": @@ -79,13 +84,21 @@ func (a *APIHandler) postHandler(c *gin.Context) { jsonMsg(c, "", err) } case "save": - loginUser := GetLoginUser(c) - data := map[string]string{} - err = c.ShouldBind(&data) - if err == nil { - err = a.ConfigService.SaveChanges(data, loginUser) + obj := c.Request.FormValue("object") + act := c.Request.FormValue("action") + data := c.Request.FormValue("data") + userLinks := c.Request.FormValue("userLinks") + outJsons := c.Request.FormValue("outJsons") + err = a.ConfigService.Save(obj, act, json.RawMessage(data), json.RawMessage(userLinks), json.RawMessage(outJsons), loginUser) + if err != nil { + jsonMsg(c, "save", err) + return } - jsonMsg(c, "save", err) + err = a.loadPartialData(c, obj, len(outJsons) > 5, len(userLinks) > 5) + if err != nil { + jsonMsg(c, obj, err) + } + return case "restartApp": err = a.PanelService.RestartPanel(3) jsonMsg(c, "restartApp", err) @@ -97,7 +110,7 @@ func (a *APIHandler) postHandler(c *gin.Context) { result, _, err := util.GetOutbound(link, 0) jsonObj(c, result, err) default: - jsonMsg(c, "API call", nil) + jsonMsg(c, "failed", common.NewError("unknown action: ", action)) } } @@ -119,6 +132,12 @@ func (a *APIHandler) getHandler(c *gin.Context) { return } jsonObj(c, data, nil) + case "inbounds", "outbounds", "endpoints", "tls", "clients", "config": + err := a.loadPartialData(c, action, false, false) + if err != nil { + jsonMsg(c, action, err) + } + return case "users": users, err := a.UserService.GetUsers() if err != nil { @@ -170,7 +189,7 @@ func (a *APIHandler) getHandler(c *gin.Context) { keypair := a.ServerService.GenKeypair(kType, options) jsonObj(c, keypair, nil) default: - jsonMsg(c, "API call", nil) + jsonMsg(c, "failed", common.NewError("unknown action: ", action)) } } @@ -195,7 +214,7 @@ func (a *APIHandler) loadData(c *gin.Context) (interface{}, error) { return "", err } if isUpdated { - config, err := a.ConfigService.GetConfig() + config, err := a.SettingService.GetConfig() if err != nil { return "", err } @@ -211,14 +230,24 @@ func (a *APIHandler) loadData(c *gin.Context) (interface{}, error) { if err != nil { return "", err } + outbounds, err := a.OutboundService.GetAll() + if err != nil { + return "", err + } + endpoints, err := a.EndpointService.GetAll() + if err != nil { + return "", err + } subURI, err := a.SettingService.GetFinalSubURI(strings.Split(c.Request.Host, ":")[0]) if err != nil { return "", err } - data["config"] = *config + data["config"] = json.RawMessage(config) data["clients"] = clients data["tls"] = tlsConfigs data["inbounds"] = inbounds + data["outbounds"] = outbounds + data["endpoints"] = endpoints data["subURI"] = subURI data["onlines"] = onlines } else { @@ -227,3 +256,72 @@ func (a *APIHandler) loadData(c *gin.Context) (interface{}, error) { return data, nil } + +func (a *APIHandler) loadPartialData(c *gin.Context, obj string, plusInbounds bool, plusClients bool) error { + data := make(map[string]interface{}, 0) + switch obj { + case "inbounds": + id := c.Query("id") + inbounds, err := a.InboundService.Get(id) + if err != nil { + return err + } + data[obj] = inbounds + case "outbounds": + outbounds, err := a.OutboundService.GetAll() + if err != nil { + return err + } + data[obj] = outbounds + case "endpoints": + endpoints, err := a.EndpointService.GetAll() + if err != nil { + return err + } + data[obj] = endpoints + case "tls": + tlsConfigs, err := a.TlsService.GetAll() + if err != nil { + return err + } + data[obj] = tlsConfigs + case "clients": + clients, err := a.ClientService.GetAll() + if err != nil { + return err + } + data[obj] = clients + case "config": + config, err := a.SettingService.GetConfig() + if err != nil { + return err + } + data[obj] = json.RawMessage(config) + } + + if plusInbounds { + inbounds, err := a.InboundService.GetAll() + if err != nil { + return err + } + data["inbounds"] = inbounds + } + if plusClients { + clients, err := a.ClientService.GetAll() + if err != nil { + return err + } + data["clients"] = clients + } + jsonObj(c, data, nil) + return nil +} + +func (a *APIHandler) postActions(c *gin.Context) (string, json.RawMessage, error) { + var data map[string]json.RawMessage + err := c.ShouldBind(&data) + if err != nil { + return "", nil, err + } + return string(data["action"]), data["data"], nil +} diff --git a/backend/api/utils.go b/backend/api/utils.go index 07aa2a7..ab19cb4 100644 --- a/backend/api/utils.go +++ b/backend/api/utils.go @@ -46,7 +46,7 @@ func jsonMsgObj(c *gin.Context, msg string, obj interface{}, err error) { } } else { m.Success = false - m.Msg = msg + err.Error() + m.Msg = msg + ": " + err.Error() logger.Warning("failed :", err) } c.JSON(http.StatusOK, m) diff --git a/backend/app/app.go b/backend/app/app.go index c9762a0..04f1e30 100644 --- a/backend/app/app.go +++ b/backend/app/app.go @@ -43,7 +43,7 @@ func (a *APP) Init() error { a.core = core.NewCore() - a.cronJob = cronjob.NewCronJob(a.core) + a.cronJob = cronjob.NewCronJob() a.webServer = web.NewServer() a.subServer = sub.NewServer() @@ -81,7 +81,7 @@ func (a *APP) Start() error { return err } - err = a.configService.StartCore() + err = a.configService.StartCore("") if err != nil { logger.Error(err) } diff --git a/backend/cmd/migration/1_2.go b/backend/cmd/migration/1_2.go index 47720ad..7d16d3f 100644 --- a/backend/cmd/migration/1_2.go +++ b/backend/cmd/migration/1_2.go @@ -60,9 +60,22 @@ func moveJsonToDb(db *gorm.DB) error { } else { tls_server, _ := json.MarshalIndent(tlsObj, "", " ") if len(tls_server) > 5 { + tlsObject := tlsObj.(map[string]interface{}) + tlsClientObj := map[string]interface{}{} + if enabled, ok := tlsObject["enabled"]; ok { + tlsClientObj["enabled"] = enabled + } + if alpn, ok := tlsObject["alpn"]; ok { + tlsClientObj["alpn"] = alpn + } + if sni, ok := tlsObject["server_name"]; ok { + tlsClientObj["server_name"] = sni + } + tls_client, _ := json.MarshalIndent(tlsClientObj, "", " ") newTls := &model.Tls{ Name: tag, Server: tls_server, + Client: tls_client, } err = db.Create(newTls).Error if err != nil { @@ -76,10 +89,10 @@ func moveJsonToDb(db *gorm.DB) error { var inbData InboundData db.Raw("select id,addrs,out_json from inbound_data where tag = ?", tag).Find(&inbData) if inbData.Id > 0 { - inbObj["outJson"] = inbData.OutJson + inbObj["out_json"] = inbData.OutJson inbObj["addrs"] = inbData.Addrs } else { - inbObj["outJson"] = json.RawMessage("{}") + inbObj["out_json"] = json.RawMessage("{}") inbObj["addrs"] = json.RawMessage("[]") } inbJson, _ := json.Marshal(inbObj) diff --git a/backend/core/box.go b/backend/core/box.go index 3287e73..da269ce 100644 --- a/backend/core/box.go +++ b/backend/core/box.go @@ -109,20 +109,16 @@ func NewBox(options Options) (*Box, error) { defaultLogWriter = io.Discard } var logFactory log.Factory - if factory == nil { - logFactory, err = NewFactory(log.Options{ - Context: ctx, - Options: sbCommon.PtrValueOrDefault(options.Log), - DefaultWriter: defaultLogWriter, - BaseTime: createdAt, - }) - if err != nil { - return nil, common.NewError("create log factory", err) - } - factory = logFactory - } else { - logFactory = factory + logFactory, err = NewFactory(log.Options{ + Context: ctx, + Options: sbCommon.PtrValueOrDefault(options.Log), + DefaultWriter: defaultLogWriter, + BaseTime: createdAt, + }) + if err != nil { + return nil, common.NewError("create log factory", err) } + factory = logFactory routeOptions := sbCommon.PtrValueOrDefault(options.Route) endpointManager := endpoint.NewManager(logFactory.NewLogger("endpoint"), endpointRegistry) @@ -158,7 +154,7 @@ func NewBox(options Options) (*Box, error) { endpointOptions.Options, ) if err != nil { - return nil, common.NewError("initialize endpoint["+string(i)+"] "+tag, err) + return nil, common.NewError("initialize endpoint["+F.ToString(i)+"] "+tag, err) } } for i, inboundOptions := range options.Inbounds { @@ -202,7 +198,7 @@ func NewBox(options Options) (*Box, error) { outboundOptions.Options, ) if err != nil { - return nil, common.NewError("initialize outbound["+string(i)+"] "+tag, err) + return nil, common.NewError("initialize outbound["+F.ToString(i)+"] "+tag, err) } } outboundManager.Initialize(sbCommon.Must1( @@ -368,7 +364,7 @@ func (s *Box) Close() error { close(s.done) } err := sbCommon.Close( - s.inbound, s.outbound, s.router, s.connection, s.network, + s.endpoint, s.inbound, s.outbound, s.router, s.connection, s.network, ) for _, lifecycleService := range s.services { err1 := lifecycleService.Close() diff --git a/backend/core/endpoint.go b/backend/core/endpoint.go index 4f0be03..d3c3a3c 100644 --- a/backend/core/endpoint.go +++ b/backend/core/endpoint.go @@ -58,7 +58,7 @@ func (c *Core) AddOutbound(config []byte) error { factory.NewLogger("outbound/"+outbound_config.Type+"["+outbound_config.Tag+"]"), outbound_config.Tag, outbound_config.Type, - outbound_config) + outbound_config.Options) if err != nil { return err } @@ -92,7 +92,7 @@ func (c *Core) AddEndpoint(config []byte) error { factory.NewLogger("endpoint/"+endpoint_config.Type+"["+endpoint_config.Tag+"]"), endpoint_config.Tag, endpoint_config.Type, - endpoint_config) + endpoint_config.Options) if err != nil { return err } diff --git a/backend/cronjob/checkCoreJob.go b/backend/cronjob/checkCoreJob.go new file mode 100644 index 0000000..0f90613 --- /dev/null +++ b/backend/cronjob/checkCoreJob.go @@ -0,0 +1,17 @@ +package cronjob + +import ( + "s-ui/service" +) + +type CheckCoreJob struct { + service.ConfigService +} + +func NewCheckCoreJob() *CheckCoreJob { + return &CheckCoreJob{} +} + +func (s *CheckCoreJob) Run() { + s.ConfigService.StartCore("") +} diff --git a/backend/cronjob/cronJob.go b/backend/cronjob/cronJob.go index 0a73f44..4bf3eff 100644 --- a/backend/cronjob/cronJob.go +++ b/backend/cronjob/cronJob.go @@ -1,7 +1,6 @@ package cronjob import ( - "s-ui/core" "time" "github.com/robfig/cron/v3" @@ -9,13 +8,10 @@ import ( type CronJob struct { cron *cron.Cron - Core *core.Core } -func NewCronJob(c *core.Core) *CronJob { - return &CronJob{ - Core: c, - } +func NewCronJob() *CronJob { + return &CronJob{} } func (c *CronJob) Start(loc *time.Location, trafficAge int) error { @@ -29,6 +25,8 @@ func (c *CronJob) Start(loc *time.Location, trafficAge int) error { c.cron.AddJob("@every 1m", NewDepleteJob()) // Start deleting old stats c.cron.AddJob("@daily", NewDelStatsJob(trafficAge)) + // Start core if it is not running + c.cron.AddJob("@every 5s", NewCheckCoreJob()) }() return nil diff --git a/backend/cronjob/depleteJob.go b/backend/cronjob/depleteJob.go index 0b0eb9b..d7adcffa 100644 --- a/backend/cronjob/depleteJob.go +++ b/backend/cronjob/depleteJob.go @@ -6,7 +6,7 @@ import ( ) type DepleteJob struct { - service.ConfigService + service.ClientService } func NewDepleteJob() *DepleteJob { @@ -14,7 +14,7 @@ func NewDepleteJob() *DepleteJob { } func (s *DepleteJob) Run() { - err := s.ConfigService.DepleteClients() + err := s.ClientService.DepleteClients() if err != nil { logger.Warning("Disable depleted users failed: ", err) return diff --git a/backend/database/model/endpoints.go b/backend/database/model/endpoints.go index d6eae27..43de727 100644 --- a/backend/database/model/endpoints.go +++ b/backend/database/model/endpoints.go @@ -1,11 +1,13 @@ package model -import "encoding/json" +import ( + "encoding/json" +) type Endpoint struct { Id uint `json:"id" form:"id" gorm:"primaryKey;autoIncrement"` Type string `json:"type" form:"type"` - Tag string `json:"tag" form:"tag"` + Tag string `json:"tag" form:"tag" gorm:"unique"` Options json.RawMessage `json:"-" form:"-"` } @@ -17,10 +19,10 @@ func (o *Endpoint) UnmarshalJSON(data []byte) error { } // Extract fixed fields and store the rest in Options - if val, exists := raw["id"]; exists { - o.Id = val.(uint) - delete(raw, "id") + if val, exists := raw["id"].(float64); exists { + o.Id = uint(val) } + delete(raw, "id") o.Type, _ = raw["type"].(string) delete(raw, "type") o.Tag = raw["tag"].(string) diff --git a/backend/database/model/inbounds.go b/backend/database/model/inbounds.go index 56c0c7c..4b3229b 100644 --- a/backend/database/model/inbounds.go +++ b/backend/database/model/inbounds.go @@ -7,14 +7,14 @@ import ( type Inbound struct { Id uint `json:"id" form:"id" gorm:"primaryKey;autoIncrement"` Type string `json:"type" form:"type"` - Tag string `json:"tag" form:"tag"` + Tag string `json:"tag" form:"tag" gorm:"unique"` // Foreign key to tls table TlsId uint `json:"tls_id" form:"tls_id"` Tls *Tls `json:"tls" form:"tls" gorm:"foreignKey:TlsId;references:Id"` Addrs json.RawMessage `json:"addrs" form:"addrs"` - OutJson json.RawMessage `json:"outJson" form:"outJson"` + OutJson json.RawMessage `json:"out_json" form:"out_json"` Options json.RawMessage `json:"-" form:"-"` } @@ -26,10 +26,10 @@ func (i *Inbound) UnmarshalJSON(data []byte) error { } // Extract fixed fields and store the rest in Options - if val, exists := raw["id"].(uint); exists { - i.Id = val - delete(raw, "id") + if val, exists := raw["id"].(float64); exists { + i.Id = uint(val) } + delete(raw, "id") i.Type, _ = raw["type"].(string) delete(raw, "type") i.Tag, _ = raw["tag"].(string) @@ -48,8 +48,8 @@ func (i *Inbound) UnmarshalJSON(data []byte) error { delete(raw, "addrs") // OutJson - i.OutJson, _ = json.MarshalIndent(raw["outJson"], "", " ") - delete(raw, "outJson") + i.OutJson, _ = json.MarshalIndent(raw["out_json"], "", " ") + delete(raw, "out_json") // Remaining fields i.Options, err = json.MarshalIndent(raw, "", " ") @@ -79,3 +79,25 @@ func (i Inbound) MarshalJSON() ([]byte, error) { return json.Marshal(combined) } + +func (i Inbound) MarshalFull() (*map[string]interface{}, error) { + combined := make(map[string]interface{}) + combined["id"] = i.Id + combined["type"] = i.Type + combined["tag"] = i.Tag + combined["tls_id"] = i.TlsId + combined["addrs"] = i.Addrs + combined["out_json"] = i.OutJson + + if i.Options != nil { + var restFields map[string]json.RawMessage + if err := json.Unmarshal(i.Options, &restFields); err != nil { + return nil, err + } + + for k, v := range restFields { + combined[k] = v + } + } + return &combined, nil +} diff --git a/backend/database/model/outbounds.go b/backend/database/model/outbounds.go index 0ae84ab..75b6fc2 100644 --- a/backend/database/model/outbounds.go +++ b/backend/database/model/outbounds.go @@ -5,7 +5,7 @@ import "encoding/json" type Outbound struct { Id uint `json:"id" form:"id" gorm:"primaryKey;autoIncrement"` Type string `json:"type" form:"type"` - Tag string `json:"tag" form:"tag"` + Tag string `json:"tag" form:"tag" gorm:"unique"` Options json.RawMessage `json:"-" form:"-"` } @@ -17,10 +17,10 @@ func (o *Outbound) UnmarshalJSON(data []byte) error { } // Extract fixed fields and store the rest in Options - if val, exists := raw["id"]; exists { - o.Id = val.(uint) - delete(raw, "id") + if val, exists := raw["id"].(float64); exists { + o.Id = uint(val) } + delete(raw, "id") o.Type, _ = raw["type"].(string) delete(raw, "type") o.Tag = raw["tag"].(string) diff --git a/backend/go.mod b/backend/go.mod index ad867e6..221d5d7 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -7,8 +7,8 @@ require ( github.com/gin-gonic/gin v1.10.0 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 github.com/robfig/cron/v3 v3.0.1 - github.com/sagernet/sing v0.6.0-beta.8 - github.com/sagernet/sing-box v1.11.0-beta.11 + github.com/sagernet/sing v0.6.0-beta.9 + github.com/sagernet/sing-box v1.11.0-beta.19 github.com/sagernet/sing-dns v0.4.0-beta.1 github.com/shirou/gopsutil/v3 v3.24.5 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 @@ -88,11 +88,11 @@ require ( github.com/sagernet/quic-go v0.48.2-beta.1 // indirect github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect github.com/sagernet/sing-mux v0.3.0-alpha.1 // indirect - github.com/sagernet/sing-quic v0.4.0-alpha.4 // indirect + github.com/sagernet/sing-quic v0.4.0-beta.2 // indirect github.com/sagernet/sing-shadowsocks v0.2.7 // indirect github.com/sagernet/sing-shadowsocks2 v0.2.0 // indirect github.com/sagernet/sing-shadowtls v0.2.0-alpha.2 // indirect - github.com/sagernet/sing-tun v0.6.0-beta.6 // indirect + github.com/sagernet/sing-tun v0.6.0-beta.7.0.20241229131914-aa9d9c62966f // indirect github.com/sagernet/sing-vmess v0.2.0-beta.1 // indirect github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect github.com/sagernet/utls v1.6.7 // indirect @@ -110,13 +110,13 @@ require ( go.uber.org/zap v1.27.0 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect golang.org/x/arch v0.11.0 // indirect - golang.org/x/crypto v0.29.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/mod v0.20.0 // indirect golang.org/x/net v0.31.0 // indirect - golang.org/x/sync v0.9.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/text v0.20.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.7.0 // indirect golang.org/x/tools v0.24.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect diff --git a/backend/go.sum b/backend/go.sum index 2bc2b2a..7082940 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -174,16 +174,24 @@ github.com/sagernet/sing v0.6.0-beta.5 h1:RD2j8WmJsvAbbBkAlJWaiYmnd+v/JohBiweoew github.com/sagernet/sing v0.6.0-beta.5/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing v0.6.0-beta.8 h1:PoxDdN7y8D4oImT3cQ05Sq1ZYnYsJberkUkIEHIGwWE= github.com/sagernet/sing v0.6.0-beta.8/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/sagernet/sing v0.6.0-beta.9 h1:P8lKa5hN53fRNAVCIKy5cWd6/kLO5c4slhdsfehSmHs= +github.com/sagernet/sing v0.6.0-beta.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing-box v1.11.0-beta.6 h1:MPdL2Yem/xM0RhejCO7krYvl1Zbd1zkSjKluKpHnHPQ= github.com/sagernet/sing-box v1.11.0-beta.6/go.mod h1:6dO5V0A37cLlhvKnxCmZinSpZXz7ZSk11x3rgI+xH1I= github.com/sagernet/sing-box v1.11.0-beta.11 h1:bVR0n3oQ3hGcuc/CSS7axsOeRNCRlCGkYVOKl0wxbsw= github.com/sagernet/sing-box v1.11.0-beta.11/go.mod h1:GZnZUzUHZ6Bgm7D/i8unNORv3537u1s03tLXFdxCRpg= +github.com/sagernet/sing-box v1.11.0-beta.15 h1:oWcs/PHgKaeWKbTfgz/020KEVvDqQv/tQWe7zpyktkc= +github.com/sagernet/sing-box v1.11.0-beta.15/go.mod h1:+QZDsF4HkdiGcMfz+JNOfONLh9CnZjIwJJQNWEzhiaQ= +github.com/sagernet/sing-box v1.11.0-beta.19 h1:uL2xlXpz4t7BduLbXiLe5QqpyiMhvNNRThBzhTJ4p00= +github.com/sagernet/sing-box v1.11.0-beta.19/go.mod h1:UXUN/lwRT9mAM8PK7upPOwgqooOV2vU+CcjBfwT1rYg= github.com/sagernet/sing-dns v0.4.0-beta.1 h1:W1XkdhigwxDOMgMDVB+9kdomCpb7ExsZfB4acPcTZFY= github.com/sagernet/sing-dns v0.4.0-beta.1/go.mod h1:8wuFcoFkWM4vJuQyg8e97LyvDwe0/Vl7G839WLcKDs8= github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg= github.com/sagernet/sing-mux v0.3.0-alpha.1/go.mod h1:FTcImmdfW38Lz7b+HQ+mxxOth1lz4ao8uEnz+MwIJQE= github.com/sagernet/sing-quic v0.4.0-alpha.4 h1:P9xAx3nIfcqb9M8jfgs0uLm+VxCcaY++FCqaBfHY3dQ= github.com/sagernet/sing-quic v0.4.0-alpha.4/go.mod h1:h5RkKTmUhudJKzK7c87FPXD5w1bJjVyxMN9+opZcctA= +github.com/sagernet/sing-quic v0.4.0-beta.2 h1:ikoQ7zTR0o/2rlI5H5FeNC0j5bQJJHb1uoyXFRu3yGk= +github.com/sagernet/sing-quic v0.4.0-beta.2/go.mod h1:1UNObFodd8CnS3aCT53x9cigjPSCl3P//8dfBMCwBDM= github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8= github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE= github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg= @@ -194,6 +202,10 @@ github.com/sagernet/sing-tun v0.6.0-beta.2 h1:GK7r2jWKm7RhlJGTq4QadgFcebQia1c3BO github.com/sagernet/sing-tun v0.6.0-beta.2/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= github.com/sagernet/sing-tun v0.6.0-beta.6 h1:xaIHoH78MqTSvZqQ4SQto8pC1A+X4qXReDRNaC8DQeI= github.com/sagernet/sing-tun v0.6.0-beta.6/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= +github.com/sagernet/sing-tun v0.6.0-beta.7 h1:FCSX8oGBqb0H57AAvfGeeH/jMGYWCOg6XWkN/oeES+0= +github.com/sagernet/sing-tun v0.6.0-beta.7/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= +github.com/sagernet/sing-tun v0.6.0-beta.7.0.20241229131914-aa9d9c62966f h1:dTnXP0e3LbSa4EpUmuOGhllanKPei4vPKfzlLvk76Pc= +github.com/sagernet/sing-tun v0.6.0-beta.7.0.20241229131914-aa9d9c62966f/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= github.com/sagernet/sing-vmess v0.2.0-beta.1 h1:5sXQ23uwNlZuDvygzi0dFtnG0Csm/SNqTjAHXJkpuj4= github.com/sagernet/sing-vmess v0.2.0-beta.1/go.mod h1:fLyE1emIcvQ5DV8reFWnufquZ7MkCSYM5ThodsR9NrQ= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= @@ -254,6 +266,8 @@ golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= @@ -264,6 +278,8 @@ golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -274,13 +290,18 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/backend/logger/logger.go b/backend/logger/logger.go index 6e30b4b..49f2ac0 100644 --- a/backend/logger/logger.go +++ b/backend/logger/logger.go @@ -26,7 +26,7 @@ func InitLogger(level logging.Level) { backend, err = logging.NewSyslogBackend("") if err != nil { - println("Unable to use syslog: " + err.Error()) + fmt.Println("Unable to use syslog: " + err.Error()) backend = logging.NewLogBackend(os.Stderr, "", 0) } if config.IsSystemd() && err != nil { diff --git a/backend/service/client.go b/backend/service/client.go index f66835d..d3798c4 100644 --- a/backend/service/client.go +++ b/backend/service/client.go @@ -11,6 +11,7 @@ import ( ) type ClientService struct { + InboundService } func (s *ClientService) GetAll() ([]model.Client, error) { @@ -23,48 +24,117 @@ func (s *ClientService) GetAll() ([]model.Client, error) { return clients, nil } -func (s *ClientService) Save(tx *gorm.DB, changes []model.Changes) error { +func (s *ClientService) Save(tx *gorm.DB, act string, data json.RawMessage) ([]uint, error) { var err error - for _, change := range changes { - client := model.Client{} - err = json.Unmarshal(change.Obj, &client) + var inboundIds []uint + + switch act { + case "new", "edit": + var client model.Client + err = json.Unmarshal(data, &client) + if err != nil { + return nil, err + } + err = json.Unmarshal(client.Inbounds, &inboundIds) + if err != nil { + return nil, err + } + err = tx.Save(&client).Error + if err != nil { + return nil, err + } + case "del": + var id uint + err = json.Unmarshal(data, &id) + if err != nil { + return nil, err + } + var client model.Client + err = tx.Where("id = ?", id).First(&client).Error + if err != nil { + return nil, err + } + err = json.Unmarshal(client.Inbounds, &inboundIds) + if err != nil { + return nil, err + } + err = tx.Where("id = ?", id).Delete(model.Client{}).Error + if err != nil { + return nil, err + } + } + + return inboundIds, nil +} + +func (s *ClientService) UpdateLinks(tx *gorm.DB, links json.RawMessage) error { + var userLinks []interface{} + err := json.Unmarshal(links, &userLinks) + if err != nil { + return err + } + for _, userLink := range userLinks { + userLinkData, _ := userLink.(map[string]interface{}) + userId, _ := userLinkData["id"].(float64) + links, err := json.MarshalIndent(userLinkData["links"], "", " ") if err != nil { return err } - switch change.Action { - case "new": - err = tx.Create(&client).Error - case "del": - err = tx.Where("id = ?", change.Index).Delete(model.Client{}).Error - default: - err = tx.Save(client).Error + if inbounds, ok := userLinkData["inbounds"]; ok { + inbounds, err := json.MarshalIndent(inbounds, "", " ") + if err != nil { + return err + } + err = tx.Model(model.Client{}).Where("id = ?", uint(userId)).Update("inbounds", inbounds).Error + if err != nil { + return err + } } + err = tx.Model(model.Client{}).Where("id = ?", uint(userId)).Update("links", links).Error if err != nil { return err } } - return err + return nil } -func (s *ClientService) DepleteClients() ([]string, []string, error) { +func (s *ClientService) DepleteClients() error { var err error var clients []model.Client var changes []model.Changes + var users []string + var inboundIds []uint + now := time.Now().Unix() db := database.GetDB() - err = db.Model(model.Client{}).Where("enable = true AND ((volume >0 AND up+down > volume) OR (expiry > 0 AND expiry < ?))", now).Scan(&clients).Error + + tx := db.Begin() + defer func() { + if err == nil { + tx.Commit() + if len(inboundIds) > 0 && corePtr.IsRunning() { + err1 := s.InboundService.RestartInbounds(tx, inboundIds) + if err1 != nil { + logger.Error("unable to restart inbounds: ", err1) + } + } + } else { + tx.Rollback() + } + }() + + err = tx.Model(model.Client{}).Where("enable = true AND ((volume >0 AND up+down > volume) OR (expiry > 0 AND expiry < ?))", now).Scan(&clients).Error if err != nil { - return nil, nil, err + return err } dt := time.Now().Unix() - var users, inbounds []string for _, client := range clients { logger.Debug("Client ", client.Name, " is going to be disabled") users = append(users, client.Name) - var userInbounds []string + var userInbounds []uint json.Unmarshal(client.Inbounds, &userInbounds) - inbounds = append(inbounds, userInbounds...) + inboundIds = s.uniqueAppendInboundIds(inboundIds, userInbounds) changes = append(changes, model.Changes{ DateTime: dt, Actor: "DepleteJob", @@ -76,16 +146,32 @@ func (s *ClientService) DepleteClients() ([]string, []string, error) { // Save changes if len(changes) > 0 { - err = db.Model(model.Client{}).Where("enable = true AND ((volume >0 AND up+down > volume) OR (expiry > 0 AND expiry < ?))", now).Update("enable", false).Error + err = tx.Model(model.Client{}).Where("enable = true AND ((volume >0 AND up+down > volume) OR (expiry > 0 AND expiry < ?))", now).Update("enable", false).Error if err != nil { - return nil, nil, err + return err } - err = db.Model(model.Changes{}).Create(&changes).Error + err = tx.Model(model.Changes{}).Create(&changes).Error if err != nil { - return nil, nil, err + return err } LastUpdate = dt } - return users, inbounds, nil + return nil +} + +// avoid duplicate inboundIds +func (s *ClientService) uniqueAppendInboundIds(a []uint, b []uint) []uint { + m := make(map[uint]bool) + for _, v := range a { + m[v] = true + } + for _, v := range b { + m[v] = true + } + var res []uint + for k := range m { + res = append(res, k) + } + return res } diff --git a/backend/service/config.go b/backend/service/config.go index 754de3d..78d1c4a 100644 --- a/backend/service/config.go +++ b/backend/service/config.go @@ -2,12 +2,12 @@ package service import ( "encoding/json" - "os" "s-ui/config" "s-ui/core" "s-ui/database" "s-ui/database/model" "s-ui/logger" + "s-ui/util/common" "strconv" "time" ) @@ -48,10 +48,13 @@ func (s *ConfigService) InitConfig() error { return nil } -func (s *ConfigService) GetConfig() (*SingBoxConfig, error) { - data, err := s.SettingService.GetConfig() - if err != nil { - return nil, err +func (s *ConfigService) GetConfig(data string) (*SingBoxConfig, error) { + var err error + if len(data) == 0 { + data, err = s.SettingService.GetConfig() + if err != nil { + return nil, err + } } singboxConfig := SingBoxConfig{} err = json.Unmarshal([]byte(data), &singboxConfig) @@ -74,8 +77,11 @@ func (s *ConfigService) GetConfig() (*SingBoxConfig, error) { return &singboxConfig, nil } -func (s *ConfigService) StartCore() error { - singboxConfig, err := s.GetConfig() +func (s *ConfigService) StartCore(defaultConfig string) error { + if corePtr.IsRunning() { + return nil + } + singboxConfig, err := s.GetConfig(defaultConfig) if err != nil { return err } @@ -93,11 +99,19 @@ func (s *ConfigService) StartCore() error { } func (s *ConfigService) RestartCore() error { - err := s.StartCore() + err := s.StopCore() if err != nil { return err } - return s.StartCore() + return s.StartCore("") +} + +func (s *ConfigService) restartCoreWithConfig(config json.RawMessage) error { + err := s.StopCore() + if err != nil { + return err + } + return s.StartCore(string(config)) } func (s *ConfigService) StopCore() error { @@ -109,220 +123,80 @@ func (s *ConfigService) StopCore() error { return nil } -func (s *ConfigService) SaveChanges(changes map[string]string, loginUser string) error { +func (s *ConfigService) Save(obj string, act string, data json.RawMessage, userLinks json.RawMessage, outJsons json.RawMessage, loginUser string) error { var err error - var clientChanges, tlsChanges, inChanges, settingChanges, configChanges []model.Changes - if _, ok := changes["clients"]; ok { - err = json.Unmarshal([]byte(changes["clients"]), &clientChanges) - if err != nil { - return err - } - } - if _, ok := changes["tls"]; ok { - err = json.Unmarshal([]byte(changes["tls"]), &tlsChanges) - if err != nil { - return err - } - } - if _, ok := changes["inData"]; ok { - err = json.Unmarshal([]byte(changes["inData"]), &inChanges) - if err != nil { - return err - } - } - if _, ok := changes["settings"]; ok { - err = json.Unmarshal([]byte(changes["settings"]), &settingChanges) - if err != nil { - return err - } - } - if _, ok := changes["config"]; ok { - err = json.Unmarshal([]byte(changes["config"]), &configChanges) - if err != nil { - return err - } - } + var inboundIds []uint db := database.GetDB() tx := db.Begin() defer func() { if err == nil { tx.Commit() + if len(inboundIds) > 0 && corePtr.IsRunning() { + err1 := s.InboundService.RestartInbounds(tx, inboundIds) + if err1 != nil { + logger.Error("unable to restart inbounds: ", err1) + } + } + // Try to start core if it is not running + if !corePtr.IsRunning() { + s.StartCore("") + } } else { tx.Rollback() } }() - if len(clientChanges) > 0 { - err = s.ClientService.Save(tx, clientChanges) + switch obj { + case "clients": + inboundIds, err = s.ClientService.Save(tx, act, data) + case "tls": + inboundIds, err = s.TlsService.Save(tx, act, data) + case "inbounds": + err = s.InboundService.Save(tx, act, data) + case "outbounds": + err = s.OutboundService.Save(tx, act, data) + case "endpoints": + err = s.EndpointService.Save(tx, act, data) + case "config": + err = s.SettingService.SaveConfig(tx, data) if err != nil { return err } + err = s.restartCoreWithConfig(data) + default: + return common.NewError("unknown object: ", obj) } - if len(tlsChanges) > 0 { - err = s.TlsService.Save(tx, tlsChanges) - if err != nil { - return err - } + if err != nil { + return err } - // if len(inChanges) > 0 { - // err = s.InDataService.Save(tx, inChanges) - // if err != nil { - // return err - // } - // } - if len(settingChanges) > 0 { - err = s.SettingService.Save(tx, settingChanges) - if err != nil { - return err - } - } - needRestart := false - if len(configChanges) > 0 { - singboxConfig, err := s.GetConfig() - if err != nil { - return err - } - newConfig := *singboxConfig - for _, change := range configChanges { - rawObject := change.Obj - switch change.Key { - case "all": - err = json.Unmarshal(rawObject, &newConfig) - if err != nil { - return err - } - needRestart = true - case "log": - newConfig.Log = rawObject - needRestart = true - case "dns": - newConfig.Dns = rawObject - needRestart = true - case "ntp": - newConfig.Ntp = rawObject - needRestart = true - case "route": - newConfig.Route = rawObject - needRestart = true - case "experimental": - newConfig.Experimental = rawObject - needRestart = true - case "inbounds": - if change.Action == "edit" { - var object map[string]interface{} - err = json.Unmarshal(newConfig.Inbounds[change.Index], &object) - if err != nil { - return err - } - if tag, ok := object["tag"].(string); ok { - err = corePtr.RemoveInbound(tag) - if err == nil { - err = corePtr.AddInbound(rawObject) - if err != nil { - needRestart = true - } - } else { - needRestart = true - } - } else { - needRestart = true - } - newConfig.Inbounds[change.Index] = rawObject - } else if change.Action == "del" { - var object map[string]interface{} - err = json.Unmarshal(newConfig.Inbounds[change.Index], &object) - if err != nil { - return err - } - if tag, ok := object["tag"].(string); ok { - err = corePtr.RemoveInbound(tag) - if err != nil { - needRestart = true - } - } else { - needRestart = true - } - newConfig.Inbounds = append(newConfig.Inbounds[:change.Index], newConfig.Inbounds[change.Index+1:]...) - } else { - newConfig.Inbounds = append(newConfig.Inbounds, rawObject) - err = corePtr.AddInbound(rawObject) - if err != nil { - logger.Debug(err) - needRestart = true - } - } - case "outbounds": - if change.Action == "edit" { - var object map[string]interface{} - err = json.Unmarshal(newConfig.Outbounds[change.Index], &object) - if err != nil { - return err - } - if tag, ok := object["tag"].(string); ok { - err = corePtr.RemoveOutbound(tag) - if err == nil { - err = corePtr.AddOutbound(rawObject) - if err != nil { - needRestart = true - } - } else { - needRestart = true - } - } else { - needRestart = true - } - newConfig.Outbounds[change.Index] = rawObject - } else if change.Action == "del" { - var object map[string]interface{} - err = json.Unmarshal(newConfig.Outbounds[change.Index], &object) - if err != nil { - return err - } - if tag, ok := object["tag"].(string); ok { - err = corePtr.RemoveOutbound(tag) - if err != nil { - needRestart = true - } - } else { - needRestart = true - } - newConfig.Outbounds = append(newConfig.Outbounds[:change.Index], newConfig.Outbounds[change.Index+1:]...) - } else { - err = corePtr.AddOutbound(rawObject) - if err != nil { - logger.Debug(err) - needRestart = true - } - newConfig.Outbounds = append(newConfig.Outbounds, rawObject) - } - } - } - err = s.Save(&newConfig, needRestart) + if len(userLinks) > 0 { + err = s.ClientService.UpdateLinks(tx, userLinks) + if err != nil { + return err + } + } + + if len(outJsons) > 0 { + err = s.InboundService.UpdateOutJsons(tx, outJsons) if err != nil { return err } } - // Log changes dt := time.Now().Unix() - allChanges := append(clientChanges, settingChanges...) - allChanges = append(allChanges, configChanges...) - allChanges = append(allChanges, tlsChanges...) - allChanges = append(allChanges, inChanges...) - if len(allChanges) > 0 { - for index := range allChanges { - allChanges[index].DateTime = dt - allChanges[index].Actor = loginUser - } - err = tx.Model(model.Changes{}).Create(&allChanges).Error - if err != nil { - return err - } + err = tx.Create(&model.Changes{ + DateTime: dt, + Actor: loginUser, + Key: obj, + Action: act, + Obj: data, + }).Error + if err != nil { + return err } - - LastUpdate = dt + LastUpdate = time.Now().Unix() return nil } @@ -345,105 +219,6 @@ func (s *ConfigService) CheckChanges(lu string) (bool, error) { } } -func (s *ConfigService) Save(singboxConfig *SingBoxConfig, needRestart bool) error { - configPath := config.GetBinFolderPath() - _, err := os.Stat(configPath + "/config.json") - if os.IsNotExist(err) { - err = os.MkdirAll(configPath, 01764) - if err != nil { - return err - } - } else if err != nil { - return err - } - - data, err := json.MarshalIndent(singboxConfig, "", " ") - if err != nil { - return err - } - - err = os.WriteFile(configPath+"/config.json", data, 0764) - if err != nil { - return err - } - - if needRestart { - err = s.RestartCore() - if err != nil { - return err - } - } - // s.Controller.Restart() - - return nil -} - -func (s *ConfigService) DepleteClients() error { - users, inboundIds, err := s.ClientService.DepleteClients() - if err != nil || len(users) == 0 || len(inboundIds) == 0 { - return err - } - - // inbounds, err := s.InboundService.FromIds(inboundIds) - // if err != nil { - // return err - // } - // for inbound_index, inbound := range inbounds { - // var inboundJson map[string]interface{} - // json.Unmarshal(inbound.Options, &inboundJson) - // inbound_users, ok := inboundJson["users"].([]interface{}) - // if ok { - // var updatedUsers []interface{} - // for _, user := range inbound_users { - // userMap, ok := user.(map[string]interface{}) - // if ok { - // name, exists := userMap["name"].(string) - // if exists && s.contains(users, name) { - // // Skip the user exists - // continue - // } - // username, exists := userMap["username"].(string) - // if exists && s.contains(users, username) { - // // Skip the username exists - // continue - // } - // } - // updatedUsers = append(updatedUsers, user) - // } - // // Exception for Naive and ShadowTLSv3 - // if len(updatedUsers) == 0 { - // if inboundJson["type"].(string) == "naive" || - // (inboundJson["type"].(string) == "shadowtls" && - // inboundJson["version"].(float64) == 3) { - // updatedUsers = append(updatedUsers, make(map[string]interface{})) - // } - // } - - // inboundJson["users"] = updatedUsers - // } - // modifiedInbound, err := json.MarshalIndent(inboundJson, "", " ") - // if err != nil { - // return err - // } - // inbounds[inbound_index] = modifiedInbound - // } - - // err = s.Save(singboxConfig, true) - // if err != nil { - // return err - // } - return nil -} - -func (s *ConfigService) contains(slice []string, item string) bool { - for _, str := range slice { - if str == item { - return true - } - } - return false -} - func (s *ConfigService) GetChanges(actor string, chngKey string, count string) []model.Changes { c, _ := strconv.Atoi(count) whereString := "`id`>0" diff --git a/backend/service/endpoints.go b/backend/service/endpoints.go index 704b48b..db52f14 100644 --- a/backend/service/endpoints.go +++ b/backend/service/endpoints.go @@ -2,6 +2,7 @@ package service import ( "encoding/json" + "os" "s-ui/database" "s-ui/database/model" @@ -10,24 +11,32 @@ import ( type EndpointService struct{} -func (o *EndpointService) GetAll() ([]*model.Endpoint, error) { +func (o *EndpointService) GetAll() (*[]map[string]interface{}, error) { db := database.GetDB() endpoints := []*model.Endpoint{} err := db.Model(model.Endpoint{}).Scan(&endpoints).Error if err != nil { return nil, err } - return endpoints, nil -} - -func (o *EndpointService) Get(id uint) (*model.Endpoint, error) { - db := database.GetDB() - endpoint := &model.Endpoint{} - err := db.First(endpoint, id).Error - if err != nil { - return nil, err + var data []map[string]interface{} + for _, endpoint := range endpoints { + epData := map[string]interface{}{ + "id": endpoint.Id, + "type": endpoint.Type, + "tag": endpoint.Tag, + } + if endpoint.Options != nil { + var restFields map[string]json.RawMessage + if err := json.Unmarshal(endpoint.Options, &restFields); err != nil { + return nil, err + } + for k, v := range restFields { + epData[k] = v + } + } + data = append(data, epData) } - return endpoint, nil + return &data, nil } func (o *EndpointService) GetAllConfig(db *gorm.DB) ([]json.RawMessage, error) { @@ -46,3 +55,55 @@ func (o *EndpointService) GetAllConfig(db *gorm.DB) ([]json.RawMessage, error) { } return endpointsJson, nil } + +func (s *EndpointService) Save(tx *gorm.DB, action string, data json.RawMessage) error { + var err error + + switch action { + case "new", "edit": + var endpoint model.Endpoint + err = endpoint.UnmarshalJSON(data) + if err != nil { + return err + } + + if corePtr.IsRunning() { + configData, err := endpoint.MarshalJSON() + if err != nil { + return err + } + if action == "edit" { + err = corePtr.RemoveEndpoint(endpoint.Tag) + if err != nil && err != os.ErrInvalid { + return err + } + } + err = corePtr.AddEndpoint(configData) + if err != nil { + return err + } + } + + err = tx.Save(&endpoint).Error + if err != nil { + return err + } + case "del": + var tag string + err = json.Unmarshal(data, &tag) + if err != nil { + return err + } + if corePtr.IsRunning() { + err = corePtr.RemoveEndpoint(tag) + if err != nil && err != os.ErrInvalid { + return err + } + } + err = tx.Where("tag = ?", tag).Delete(model.Endpoint{}).Error + if err != nil { + return err + } + } + return nil +} diff --git a/backend/service/inbounds.go b/backend/service/inbounds.go index de4c706..a16775e 100644 --- a/backend/service/inbounds.go +++ b/backend/service/inbounds.go @@ -2,22 +2,67 @@ package service import ( "encoding/json" + "os" "s-ui/database" "s-ui/database/model" + "strings" "gorm.io/gorm" ) type InboundService struct{} -func (s *InboundService) GetAll() (*[]map[string]interface{}, error) { +func (s *InboundService) Get(ids string) (*[]map[string]interface{}, error) { + if ids == "" { + return s.GetAll() + } + return s.getById(ids) +} + +func (s *InboundService) getById(ids string) (*[]map[string]interface{}, error) { + var inbound []model.Inbound + var result []map[string]interface{} db := database.GetDB() - inbounds := []map[string]interface{}{} - err := db.Model(model.Inbound{}).Select("id, tag, type, address, port, tls_id , count(users) as ucount").Scan(&inbounds).Error + err := db.Model(model.Inbound{}).Where("id in ?", strings.Split(ids, ",")).Scan(&inbound).Error if err != nil { return nil, err } - return &inbounds, nil + for _, inb := range inbound { + inbData, err := inb.MarshalFull() + if err != nil { + return nil, err + } + result = append(result, *inbData) + } + return &result, nil +} + +func (s *InboundService) GetAll() (*[]map[string]interface{}, error) { + db := database.GetDB() + inbounds := []model.Inbound{} + err := db.Model(model.Inbound{}).Scan(&inbounds).Error + if err != nil { + return nil, err + } + var data []map[string]interface{} + for _, inbound := range inbounds { + inbData := map[string]interface{}{ + "id": inbound.Id, + "type": inbound.Type, + "tag": inbound.Tag, + "tls_id": inbound.TlsId, + } + if inbound.Options != nil { + var restFields map[string]json.RawMessage + if err := json.Unmarshal(inbound.Options, &restFields); err != nil { + return nil, err + } + inbData["listen"] = restFields["listen"] + inbData["listen_port"] = restFields["listen_port"] + } + data = append(data, inbData) + } + return &data, nil } func (s *InboundService) FromIds(ids []uint) ([]*model.Inbound, error) { @@ -30,8 +75,91 @@ func (s *InboundService) FromIds(ids []uint) ([]*model.Inbound, error) { return inbounds, nil } -func (s *InboundService) Save(db *gorm.DB, inbounds []*model.Inbound) error { - return db.Save(inbounds).Error +func (s *InboundService) Save(tx *gorm.DB, act string, data json.RawMessage) error { + var err error + + switch act { + case "new", "edit": + var inbound model.Inbound + err = inbound.UnmarshalJSON(data) + if err != nil { + return err + } + if corePtr.IsRunning() { + if act == "edit" { + err = corePtr.RemoveInbound(inbound.Tag) + if err != nil && err != os.ErrInvalid { + return err + } + } + + if inbound.TlsId > 0 { + err = tx.Model(model.Tls{}).Where("id = ?", inbound.TlsId).Find(&inbound.Tls).Error + if err != nil { + return err + } + } + + inboundConfig, err := inbound.MarshalJSON() + if err != nil { + return err + } + + inboundConfig, err = s.addUsers(tx, inboundConfig, inbound.Id, inbound.Type) + if err != nil { + return err + } + + err = corePtr.AddInbound(inboundConfig) + if err != nil { + return err + } + } + + err = tx.Save(&inbound).Error + if err != nil { + return err + } + case "del": + var tag string + err = json.Unmarshal(data, &tag) + if err != nil { + return err + } + if corePtr.IsRunning() { + err = corePtr.RemoveInbound(tag) + if err != nil && err != os.ErrInvalid { + return err + } + } + err = tx.Where("tag = ?", tag).Delete(model.Inbound{}).Error + if err != nil { + return err + } + } + return nil +} + +func (s *InboundService) UpdateOutJsons(tx *gorm.DB, data json.RawMessage) error { + var outJsons []interface{} + err := json.Unmarshal(data, &outJsons) + if err != nil { + return err + } + for _, outJson := range outJsons { + outJsonData := outJson.(map[string]interface{}) + tag := outJsonData["tag"].(string) + outJson, err := json.MarshalIndent(outJsonData["out_json"], "", " ") + if err != nil { + return err + } + err = tx.Model(model.Inbound{}).Where("tag = ?", tag).Update("out_json", outJson).Error + if err != nil { + return err + } + } + + return nil } func (s *InboundService) GetAllConfig(db *gorm.DB) ([]json.RawMessage, error) { @@ -46,12 +174,9 @@ func (s *InboundService) GetAllConfig(db *gorm.DB) ([]json.RawMessage, error) { if err != nil { return nil, err } - switch inbound.Type { - case "mixed", "socks", "http", "shadowsocks", "vmess", "trojan", "naive", "hysteria", "shadowtls", "tuic", "hysteria2", "vless": - inboundJson, err = s.addUsers(db, inboundJson, inbound.Id, inbound.Type) - if err != nil { - return nil, err - } + inboundJson, err = s.addUsers(db, inboundJson, inbound.Id, inbound.Type) + if err != nil { + return nil, err } inboundsJson = append(inboundsJson, inboundJson) } @@ -59,11 +184,24 @@ func (s *InboundService) GetAllConfig(db *gorm.DB) ([]json.RawMessage, error) { } func (s *InboundService) addUsers(db *gorm.DB, inboundJson []byte, inboundId uint, inboundType string) ([]byte, error) { + switch inboundType { + case "mixed", "socks", "http", "shadowsocks", "vmess", "trojan", "naive", "hysteria", "shadowtls", "tuic", "hysteria2", "vless": + break + default: + return inboundJson, nil + } + var inbound map[string]interface{} err := json.Unmarshal(inboundJson, &inbound) if err != nil { return nil, err } + if inboundType == "shadowsocks" { + method, _ := inbound["method"].(string) + if method == "2022-blake3-aes-128-gcm" { + inboundType = "shadowsocks16" + } + } var users []string err = db.Raw(`SELECT json_extract(clients.config, ?) FROM clients, json_each(clients.inbounds) as je @@ -80,3 +218,27 @@ func (s *InboundService) addUsers(db *gorm.DB, inboundJson []byte, inboundId uin inbound["users"] = usersJson return json.Marshal(inbound) } + +func (s *InboundService) RestartInbounds(tx *gorm.DB, ids []uint) error { + var inbounds []*model.Inbound + err := tx.Model(model.Inbound{}).Preload("Tls").Where("id in ?", ids).Find(&inbounds).Error + if err != nil { + return err + } + for _, inbound := range inbounds { + err = corePtr.RemoveInbound(inbound.Tag) + if err != nil && err != os.ErrInvalid { + return err + } + inboundConfig, err := inbound.MarshalJSON() + if err != nil { + return err + } + inboundConfig, err = s.addUsers(tx, inboundConfig, inbound.Id, inbound.Type) + err = corePtr.AddInbound(inboundConfig) + if err != nil { + return err + } + } + return nil +} diff --git a/backend/service/outbounds.go b/backend/service/outbounds.go index 00419da..f8eb2f2 100644 --- a/backend/service/outbounds.go +++ b/backend/service/outbounds.go @@ -2,6 +2,7 @@ package service import ( "encoding/json" + "os" "s-ui/database" "s-ui/database/model" @@ -10,24 +11,32 @@ import ( type OutboundService struct{} -func (o *OutboundService) GetAll() ([]*model.Outbound, error) { +func (o *OutboundService) GetAll() (*[]map[string]interface{}, error) { db := database.GetDB() outbounds := []*model.Outbound{} err := db.Model(model.Outbound{}).Scan(&outbounds).Error if err != nil { return nil, err } - return outbounds, nil -} - -func (o *OutboundService) Get(id uint) (*model.Outbound, error) { - db := database.GetDB() - outbound := &model.Outbound{} - err := db.First(outbound, id).Error - if err != nil { - return nil, err + var data []map[string]interface{} + for _, outbound := range outbounds { + outData := map[string]interface{}{ + "id": outbound.Id, + "type": outbound.Type, + "tag": outbound.Tag, + } + if outbound.Options != nil { + var restFields map[string]json.RawMessage + if err := json.Unmarshal(outbound.Options, &restFields); err != nil { + return nil, err + } + for k, v := range restFields { + outData[k] = v + } + } + data = append(data, outData) } - return outbound, nil + return &data, nil } func (o *OutboundService) GetAllConfig(db *gorm.DB) ([]json.RawMessage, error) { @@ -46,3 +55,55 @@ func (o *OutboundService) GetAllConfig(db *gorm.DB) ([]json.RawMessage, error) { } return outboundsJson, nil } + +func (s *OutboundService) Save(tx *gorm.DB, action string, data json.RawMessage) error { + var err error + + switch action { + case "new", "edit": + var outbound model.Outbound + err = outbound.UnmarshalJSON(data) + if err != nil { + return err + } + + if corePtr.IsRunning() { + configData, err := outbound.MarshalJSON() + if err != nil { + return err + } + if action == "edit" { + err = corePtr.RemoveOutbound(outbound.Tag) + if err != nil && err != os.ErrInvalid { + return err + } + } + err = corePtr.AddOutbound(configData) + if err != nil { + return err + } + } + + err = tx.Save(&outbound).Error + if err != nil { + return err + } + case "del": + var tag string + err = json.Unmarshal(data, &tag) + if err != nil { + return err + } + if corePtr.IsRunning() { + err = corePtr.RemoveOutbound(tag) + if err != nil && err != os.ErrInvalid { + return err + } + } + err = tx.Where("tag = ?", tag).Delete(model.Outbound{}).Error + if err != nil { + return err + } + } + return nil +} diff --git a/backend/service/setting.go b/backend/service/setting.go index 4941c9c..3ca4f0d 100644 --- a/backend/service/setting.go +++ b/backend/service/setting.go @@ -343,6 +343,14 @@ func (s *SettingService) SetConfig(config string) error { return s.setString("config", config) } +func (s *SettingService) SaveConfig(tx *gorm.DB, config json.RawMessage) error { + configs, err := json.MarshalIndent(config, "", " ") + if err != nil { + return err + } + return tx.Model(model.Setting{}).Where("key = ?", "config").Update("value", string(configs)).Error +} + func (s *SettingService) Save(tx *gorm.DB, changes []model.Changes) error { var err error for _, change := range changes { diff --git a/backend/service/stats.go b/backend/service/stats.go index 2ff547c..876b1e1 100644 --- a/backend/service/stats.go +++ b/backend/service/stats.go @@ -74,7 +74,7 @@ func (s *StatsService) SaveStats() error { return err } -func (s *StatsService) GetStats(resorce string, tag string, limit int) ([]model.Stats, error) { +func (s *StatsService) GetStats(resource string, tag string, limit int) ([]model.Stats, error) { var err error var result []model.Stats @@ -82,7 +82,11 @@ func (s *StatsService) GetStats(resorce string, tag string, limit int) ([]model. timeDiff := currentTime - (int64(limit) * 3600) db := database.GetDB() - err = db.Model(model.Stats{}).Where("resource = ? AND tag = ? AND date_time > ?", resorce, tag, timeDiff).Scan(&result).Error + resources := []string{resource} + if resource == "endpoint" { + resources = []string{"inbound", "outbound"} + } + err = db.Model(model.Stats{}).Where("resource in ? AND tag = ? AND date_time > ?", resources, tag, timeDiff).Scan(&result).Error if err != nil { return nil, err } diff --git a/backend/service/tls.go b/backend/service/tls.go index ada8cb2..11d23ab 100644 --- a/backend/service/tls.go +++ b/backend/service/tls.go @@ -4,11 +4,13 @@ import ( "encoding/json" "s-ui/database" "s-ui/database/model" + "s-ui/util/common" "gorm.io/gorm" ) type TlsService struct { + InboundService } func (s *TlsService) GetAll() ([]model.Tls, error) { @@ -22,25 +24,45 @@ func (s *TlsService) GetAll() ([]model.Tls, error) { return tlsConfig, nil } -func (s *TlsService) Save(tx *gorm.DB, changes []model.Changes) error { +func (s *TlsService) Save(tx *gorm.DB, action string, data json.RawMessage) ([]uint, error) { var err error - for _, change := range changes { - tlsConfig := model.Tls{} - err = json.Unmarshal(change.Obj, &tlsConfig) + var inboundIds []uint + + switch action { + case "new", "edit": + var tls model.Tls + err = json.Unmarshal(data, &tls) if err != nil { - return err - } - switch change.Action { - case "new": - err = tx.Create(&tlsConfig).Error - case "del": - err = tx.Where("id = ?", change.Index).Delete(model.Tls{}).Error - default: - err = tx.Save(tlsConfig).Error + return nil, err } + err = tx.Save(&tls).Error if err != nil { - return err + return nil, err + } + err = tx.Model(model.Inbound{}).Select("id").Where("tls_id = ?", tls.Id).Scan(&inboundIds).Error + if err != nil { + return nil, err + } + return inboundIds, nil + case "del": + var id uint + err = json.Unmarshal(data, &id) + if err != nil { + return nil, err + } + var inboundCount int64 + err = tx.Model(model.Inbound{}).Where("tls_id = ?", id).Count(&inboundCount).Error + if err != nil { + return nil, err + } + if inboundCount > 0 { + return nil, common.NewError("tls in use") + } + err = tx.Where("id = ?", id).Delete(model.Tls{}).Error + if err != nil { + return nil, err } } - return err + + return nil, nil } diff --git a/backend/sub/jsonService.go b/backend/sub/jsonService.go index 48c5c93..33db779 100644 --- a/backend/sub/jsonService.go +++ b/backend/sub/jsonService.go @@ -87,27 +87,22 @@ func (j *JsonService) GetJson(subId string, format string) (*string, error) { return &resultStr, nil } -func (j *JsonService) getData(subId string) (*model.Client, *[]model.InboundData, error) { +func (j *JsonService) getData(subId string) (*model.Client, []*model.Inbound, error) { db := database.GetDB() client := &model.Client{} err := db.Model(model.Client{}).Where("enable = true and name = ?", subId).First(client).Error if err != nil { return nil, nil, err } - var inbounds []string - err = json.Unmarshal(client.Inbounds, &inbounds) + var inbounds []*model.Inbound + err = db.Model(model.Inbound{}).Where("tag in ?", client.Inbounds).Find(&inbounds).Error if err != nil { return nil, nil, err } - inDatas := &[]model.InboundData{} - err = db.Model(model.InboundData{}).Where("tag in ?", inbounds).Find(&inDatas).Error - if err != nil { - return nil, nil, err - } - return client, inDatas, nil + return client, inbounds, nil } -func (j *JsonService) getOutbounds(clientConfig json.RawMessage, inDatas *[]model.InboundData) (*[]map[string]interface{}, *[]string, error) { +func (j *JsonService) getOutbounds(clientConfig json.RawMessage, inbounds []*model.Inbound) (*[]map[string]interface{}, *[]string, error) { var outbounds []map[string]interface{} var configs map[string]interface{} var outTags []string @@ -116,7 +111,7 @@ func (j *JsonService) getOutbounds(clientConfig json.RawMessage, inDatas *[]mode if err != nil { return nil, nil, err } - for _, inData := range *inDatas { + for _, inData := range inbounds { if len(inData.OutJson) < 5 { continue } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cf837f0..9376f66 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -58,12 +58,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz", - "integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", + "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.3" }, "bin": { "parser": "bin/babel-parser.js" @@ -73,9 +73,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", + "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -497,14 +497,14 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", - "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", + "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", "dev": true, "license": "Apache-2.0", "peer": true, "dependencies": { - "@eslint/object-schema": "^2.1.4", + "@eslint/object-schema": "^2.1.5", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -513,20 +513,23 @@ } }, "node_modules/@eslint/core": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz", - "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", + "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", "dev": true, "license": "Apache-2.0", "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/eslintrc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", - "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", "dev": true, "license": "MIT", "peer": true, @@ -549,9 +552,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.13.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz", - "integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==", + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", + "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", "dev": true, "license": "MIT", "peer": true, @@ -560,9 +563,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", - "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", + "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", "dev": true, "license": "Apache-2.0", "peer": true, @@ -571,9 +574,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz", - "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", + "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", "dev": true, "license": "Apache-2.0", "peer": true, @@ -610,6 +613,21 @@ "node": ">=18.18.0" } }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -626,9 +644,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", "dev": true, "license": "Apache-2.0", "peer": true, @@ -641,13 +659,13 @@ } }, "node_modules/@intlify/core-base": { - "version": "9.14.1", - "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.1.tgz", - "integrity": "sha512-rG5/hlNW6Qfve41go37szEf0mVLcfhYuOu83JcY0jZKasnwsrcZYYWDzebCcuO5I/6Sy1JFWo9p+nvkQS1Dy+w==", + "version": "9.14.2", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.2.tgz", + "integrity": "sha512-DZyQ4Hk22sC81MP4qiCDuU+LdaYW91A6lCjq8AWPvY3+mGMzhGDfOCzvyR6YBQxtlPjFqMoFk9ylnNYRAQwXtQ==", "license": "MIT", "dependencies": { - "@intlify/message-compiler": "9.14.1", - "@intlify/shared": "9.14.1" + "@intlify/message-compiler": "9.14.2", + "@intlify/shared": "9.14.2" }, "engines": { "node": ">= 16" @@ -657,12 +675,12 @@ } }, "node_modules/@intlify/message-compiler": { - "version": "9.14.1", - "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.1.tgz", - "integrity": "sha512-MY8hwukJBnXvGAncVKlHsqKDQ5ZcQx4peqEmI8wBUTXn4pezrtTGYXNoz81cLyEEHB+L/zlKWVBSh5TiX4gYoQ==", + "version": "9.14.2", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.2.tgz", + "integrity": "sha512-YsKKuV4Qv4wrLNsvgWbTf0E40uRv+Qiw1BeLQ0LAxifQuhiMe+hfTIzOMdWj/ZpnTDj4RSZtkXjJM7JDiiB5LQ==", "license": "MIT", "dependencies": { - "@intlify/shared": "9.14.1", + "@intlify/shared": "9.14.2", "source-map-js": "^1.0.2" }, "engines": { @@ -673,9 +691,9 @@ } }, "node_modules/@intlify/shared": { - "version": "9.14.1", - "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.1.tgz", - "integrity": "sha512-XjHu6PEQup9MnP1x0W9y0nXXfq9jFftAYSfV11hryjtH4XqXP8HrzMvXI+ZVifF+jZLszaTzIhvukllplxTQTg==", + "version": "9.14.2", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.2.tgz", + "integrity": "sha512-uRAHAxYPeF+G5DBIboKpPgC/Waecd4Jz8ihtkpJQD5ycb5PwXp0k/+hBGl5dAjwF7w+l74kz/PKA8r8OK//RUw==", "license": "MIT", "engines": { "node": ">= 16" @@ -691,9 +709,9 @@ "license": "MIT" }, "node_modules/@kurkle/color": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", - "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", "license": "MIT" }, "node_modules/@mdi/font": { @@ -741,9 +759,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.2.tgz", - "integrity": "sha512-ufoveNTKDg9t/b7nqI3lwbCG/9IJMhADBNjjz/Jn6LxIZxD7T5L8l2uO/wD99945F1Oo8FvgbbZJRguyk/BdzA==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.29.1.tgz", + "integrity": "sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==", "cpu": [ "arm" ], @@ -754,9 +772,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.2.tgz", - "integrity": "sha512-iZoYCiJz3Uek4NI0J06/ZxUgwAfNzqltK0MptPDO4OR0a88R4h0DSELMsflS6ibMCJ4PnLvq8f7O1d7WexUvIA==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.29.1.tgz", + "integrity": "sha512-CaRfrV0cd+NIIcVVN/jx+hVLN+VRqnuzLRmfmlzpOzB87ajixsN/+9L5xNmkaUUvEbI5BmIKS+XTwXsHEb65Ew==", "cpu": [ "arm64" ], @@ -767,9 +785,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.2.tgz", - "integrity": "sha512-/UhrIxobHYCBfhi5paTkUDQ0w+jckjRZDZ1kcBL132WeHZQ6+S5v9jQPVGLVrLbNUebdIRpIt00lQ+4Z7ys4Rg==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.29.1.tgz", + "integrity": "sha512-2ORr7T31Y0Mnk6qNuwtyNmy14MunTAMx06VAPI6/Ju52W10zk1i7i5U3vlDRWjhOI5quBcrvhkCHyF76bI7kEw==", "cpu": [ "arm64" ], @@ -780,9 +798,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.2.tgz", - "integrity": "sha512-1F/jrfhxJtWILusgx63WeTvGTwE4vmsT9+e/z7cZLKU8sBMddwqw3UV5ERfOV+H1FuRK3YREZ46J4Gy0aP3qDA==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.29.1.tgz", + "integrity": "sha512-j/Ej1oanzPjmN0tirRd5K2/nncAhS9W6ICzgxV+9Y5ZsP0hiGhHJXZ2JQ53iSSjj8m6cRY6oB1GMzNn2EUt6Ng==", "cpu": [ "x64" ], @@ -793,9 +811,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.2.tgz", - "integrity": "sha512-1YWOpFcGuC6iGAS4EI+o3BV2/6S0H+m9kFOIlyFtp4xIX5rjSnL3AwbTBxROX0c8yWtiWM7ZI6mEPTI7VkSpZw==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.29.1.tgz", + "integrity": "sha512-91C//G6Dm/cv724tpt7nTyP+JdN12iqeXGFM1SqnljCmi5yTXriH7B1r8AD9dAZByHpKAumqP1Qy2vVNIdLZqw==", "cpu": [ "arm64" ], @@ -806,9 +824,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.2.tgz", - "integrity": "sha512-3qAqTewYrCdnOD9Gl9yvPoAoFAVmPJsBvleabvx4bnu1Kt6DrB2OALeRVag7BdWGWLhP1yooeMLEi6r2nYSOjg==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.29.1.tgz", + "integrity": "sha512-hEioiEQ9Dec2nIRoeHUP6hr1PSkXzQaCUyqBDQ9I9ik4gCXQZjJMIVzoNLBRGet+hIUb3CISMh9KXuCcWVW/8w==", "cpu": [ "x64" ], @@ -819,9 +837,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.2.tgz", - "integrity": "sha512-ArdGtPHjLqWkqQuoVQ6a5UC5ebdX8INPuJuJNWRe0RGa/YNhVvxeWmCTFQ7LdmNCSUzVZzxAvUznKaYx645Rig==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.29.1.tgz", + "integrity": "sha512-Py5vFd5HWYN9zxBv3WMrLAXY3yYJ6Q/aVERoeUFwiDGiMOWsMs7FokXihSOaT/PMWUty/Pj60XDQndK3eAfE6A==", "cpu": [ "arm" ], @@ -832,9 +850,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.2.tgz", - "integrity": "sha512-B6UHHeNnnih8xH6wRKB0mOcJGvjZTww1FV59HqJoTJ5da9LCG6R4SEBt6uPqzlawv1LoEXSS0d4fBlHNWl6iYw==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.29.1.tgz", + "integrity": "sha512-RiWpGgbayf7LUcuSNIbahr0ys2YnEERD4gYdISA06wa0i8RALrnzflh9Wxii7zQJEB2/Eh74dX4y/sHKLWp5uQ==", "cpu": [ "arm" ], @@ -845,9 +863,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.2.tgz", - "integrity": "sha512-kr3gqzczJjSAncwOS6i7fpb4dlqcvLidqrX5hpGBIM1wtt0QEVtf4wFaAwVv8QygFU8iWUMYEoJZWuWxyua4GQ==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.29.1.tgz", + "integrity": "sha512-Z80O+taYxTQITWMjm/YqNoe9d10OX6kDh8X5/rFCMuPqsKsSyDilvfg+vd3iXIqtfmp+cnfL1UrYirkaF8SBZA==", "cpu": [ "arm64" ], @@ -858,9 +876,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.2.tgz", - "integrity": "sha512-TDdHLKCWgPuq9vQcmyLrhg/bgbOvIQ8rtWQK7MRxJ9nvaxKx38NvY7/Lo6cYuEnNHqf6rMqnivOIPIQt6H2AoA==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.29.1.tgz", + "integrity": "sha512-fOHRtF9gahwJk3QVp01a/GqS4hBEZCV1oKglVVq13kcK3NeVlS4BwIFzOHDbmKzt3i0OuHG4zfRP0YoG5OF/rA==", "cpu": [ "arm64" ], @@ -870,10 +888,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.29.1.tgz", + "integrity": "sha512-5a7q3tnlbcg0OodyxcAdrrCxFi0DgXJSoOuidFUzHZ2GixZXQs6Tc3CHmlvqKAmOs5eRde+JJxeIf9DonkmYkw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.2.tgz", - "integrity": "sha512-xv9vS648T3X4AxFFZGWeB5Dou8ilsv4VVqJ0+loOIgDO20zIhYfDLkk5xoQiej2RiSQkld9ijF/fhLeonrz2mw==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.29.1.tgz", + "integrity": "sha512-9b4Mg5Yfz6mRnlSPIdROcfw1BU22FQxmfjlp/CShWwO3LilKQuMISMTtAu/bxmmrE6A902W2cZJuzx8+gJ8e9w==", "cpu": [ "ppc64" ], @@ -884,9 +915,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.2.tgz", - "integrity": "sha512-tbtXwnofRoTt223WUZYiUnbxhGAOVul/3StZ947U4A5NNjnQJV5irKMm76G0LGItWs6y+SCjUn/Q0WaMLkEskg==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.29.1.tgz", + "integrity": "sha512-G5pn0NChlbRM8OJWpJFMX4/i8OEU538uiSv0P6roZcbpe/WfhEO+AT8SHVKfp8qhDQzaz7Q+1/ixMy7hBRidnQ==", "cpu": [ "riscv64" ], @@ -897,9 +928,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.2.tgz", - "integrity": "sha512-gc97UebApwdsSNT3q79glOSPdfwgwj5ELuiyuiMY3pEWMxeVqLGKfpDFoum4ujivzxn6veUPzkGuSYoh5deQ2Q==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.29.1.tgz", + "integrity": "sha512-WM9lIkNdkhVwiArmLxFXpWndFGuOka4oJOZh8EP3Vb8q5lzdSCBuhjavJsw68Q9AKDGeOOIHYzYm4ZFvmWez5g==", "cpu": [ "s390x" ], @@ -910,9 +941,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.2.tgz", - "integrity": "sha512-jOG/0nXb3z+EM6SioY8RofqqmZ+9NKYvJ6QQaa9Mvd3RQxlH68/jcB/lpyVt4lCiqr04IyaC34NzhUqcXbB5FQ==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.29.1.tgz", + "integrity": "sha512-87xYCwb0cPGZFoGiErT1eDcssByaLX4fc0z2nRM6eMtV9njAfEE6OW3UniAoDhX4Iq5xQVpE6qO9aJbCFumKYQ==", "cpu": [ "x64" ], @@ -923,9 +954,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.2.tgz", - "integrity": "sha512-XAo7cJec80NWx9LlZFEJQxqKOMz/lX3geWs2iNT5CHIERLFfd90f3RYLLjiCBm1IMaQ4VOX/lTC9lWfzzQm14Q==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.29.1.tgz", + "integrity": "sha512-xufkSNppNOdVRCEC4WKvlR1FBDyqCSCpQeMMgv9ZyXqqtKBfkw1yfGMTUTs9Qsl6WQbJnsGboWCp7pJGkeMhKA==", "cpu": [ "x64" ], @@ -936,9 +967,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.2.tgz", - "integrity": "sha512-A+JAs4+EhsTjnPQvo9XY/DC0ztaws3vfqzrMNMKlwQXuniBKOIIvAAI8M0fBYiTCxQnElYu7mLk7JrhlQ+HeOw==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.29.1.tgz", + "integrity": "sha512-F2OiJ42m77lSkizZQLuC+jiZ2cgueWQL5YC9tjo3AgaEw+KJmVxHGSyQfDUoYR9cci0lAywv2Clmckzulcq6ig==", "cpu": [ "arm64" ], @@ -949,9 +980,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.2.tgz", - "integrity": "sha512-ZhcrakbqA1SCiJRMKSU64AZcYzlZ/9M5LaYil9QWxx9vLnkQ9Vnkve17Qn4SjlipqIIBFKjBES6Zxhnvh0EAEw==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.29.1.tgz", + "integrity": "sha512-rYRe5S0FcjlOBZQHgbTKNrqxCBUmgDJem/VQTCcTnA2KCabYSWQDrytOzX7avb79cAAweNmMUb/Zw18RNd4mng==", "cpu": [ "ia32" ], @@ -962,9 +993,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.2.tgz", - "integrity": "sha512-2mLH46K1u3r6uwc95hU+OR9q/ggYMpnS7pSp83Ece1HUQgF9Nh/QwTK5rcgbFnV9j+08yBrU5sA/P0RK2MSBNA==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.29.1.tgz", + "integrity": "sha512-+10CMg9vt1MoHj6x1pxyjPSMjHTIlqs8/tBztXvPAx24SKs9jwVnKqHJumlH/IzhaPUaj3T6T6wfZr8okdXaIg==", "cpu": [ "x64" ], @@ -990,9 +1021,9 @@ "peer": true }, "node_modules/@types/node": { - "version": "20.17.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.2.tgz", - "integrity": "sha512-OOHK4sjXqkL7yQ7VEEHcf6+0jSvKjWqwnaCtY7AKD/VLEvRHMsxxu7eI8ErnjxHS8VwmekD4PeVCpu4qZEZSxg==", + "version": "20.17.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.10.tgz", + "integrity": "sha512-/jrvh5h6NXhEauFFexRin69nA0uHJ5gwk4iDivp/DeoEua3uwCUto6PC86IpRITBOs4+6i2I56K5x5b6WYGXHA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -1000,96 +1031,96 @@ } }, "node_modules/@vitejs/plugin-vue": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.1.4.tgz", - "integrity": "sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz", + "integrity": "sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==", "dev": true, "license": "MIT", "engines": { "node": "^18.0.0 || >=20.0.0" }, "peerDependencies": { - "vite": "^5.0.0", + "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" } }, "node_modules/@volar/language-core": { - "version": "2.4.8", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.8.tgz", - "integrity": "sha512-K/GxMOXGq997bO00cdFhTNuR85xPxj0BEEAy+BaqqayTmy9Tmhfgmq2wpJcVspRhcwfgPoE2/mEJa26emUhG/g==", + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.11.tgz", + "integrity": "sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==", "dev": true, "license": "MIT", "dependencies": { - "@volar/source-map": "2.4.8" + "@volar/source-map": "2.4.11" } }, "node_modules/@volar/source-map": { - "version": "2.4.8", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.8.tgz", - "integrity": "sha512-jeWJBkC/WivdelMwxKkpFL811uH/jJ1kVxa+c7OvG48DXc3VrP7pplSWPP2W1dLMqBxD+awRlg55FQQfiup4cA==", + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.11.tgz", + "integrity": "sha512-ZQpmafIGvaZMn/8iuvCFGrW3smeqkq/IIh9F1SdSx9aUl0J4Iurzd6/FhmjNO5g2ejF3rT45dKskgXWiofqlZQ==", "dev": true, "license": "MIT" }, "node_modules/@volar/typescript": { - "version": "2.4.8", - "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.8.tgz", - "integrity": "sha512-6xkIYJ5xxghVBhVywMoPMidDDAFT1OoQeXwa27HSgJ6AiIKRe61RXLoik+14Z7r0JvnblXVsjsRLmCr42SGzqg==", + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.11.tgz", + "integrity": "sha512-2DT+Tdh88Spp5PyPbqhyoYavYCPDsqbHLFwcUI9K1NlY1YgUJvujGdrqUp0zWxnW7KWNTr3xSpMuv2WnaTKDAw==", "dev": true, "license": "MIT", "dependencies": { - "@volar/language-core": "2.4.8", + "@volar/language-core": "2.4.11", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "node_modules/@vue/compiler-core": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.12.tgz", - "integrity": "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz", + "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==", "license": "MIT", "dependencies": { "@babel/parser": "^7.25.3", - "@vue/shared": "3.5.12", + "@vue/shared": "3.5.13", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz", - "integrity": "sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz", + "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.12", - "@vue/shared": "3.5.12" + "@vue/compiler-core": "3.5.13", + "@vue/shared": "3.5.13" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz", - "integrity": "sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", + "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==", "license": "MIT", "dependencies": { "@babel/parser": "^7.25.3", - "@vue/compiler-core": "3.5.12", - "@vue/compiler-dom": "3.5.12", - "@vue/compiler-ssr": "3.5.12", - "@vue/shared": "3.5.12", + "@vue/compiler-core": "3.5.13", + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13", "estree-walker": "^2.0.2", "magic-string": "^0.30.11", - "postcss": "^8.4.47", + "postcss": "^8.4.48", "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz", - "integrity": "sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz", + "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.12", - "@vue/shared": "3.5.12" + "@vue/compiler-dom": "3.5.13", + "@vue/shared": "3.5.13" } }, "node_modules/@vue/compiler-vue2": { @@ -1110,17 +1141,17 @@ "license": "MIT" }, "node_modules/@vue/language-core": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.8.tgz", - "integrity": "sha512-DtPUKrIRqqzY1joGfVHxHWZoxXZbCQLmVtW+QTifuPInfcs1R/3UAdlJXDp+lpSpP9lI5m+jMYYlwDXXu3KSTg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.0.tgz", + "integrity": "sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==", "dev": true, "license": "MIT", "dependencies": { - "@volar/language-core": "~2.4.8", + "@volar/language-core": "~2.4.11", "@vue/compiler-dom": "^3.5.0", "@vue/compiler-vue2": "^2.7.16", "@vue/shared": "^3.5.0", - "alien-signals": "^0.2.0", + "alien-signals": "^0.4.9", "minimatch": "^9.0.3", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1" @@ -1161,53 +1192,53 @@ } }, "node_modules/@vue/reactivity": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.12.tgz", - "integrity": "sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz", + "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==", "license": "MIT", "dependencies": { - "@vue/shared": "3.5.12" + "@vue/shared": "3.5.13" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.12.tgz", - "integrity": "sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz", + "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.12", - "@vue/shared": "3.5.12" + "@vue/reactivity": "3.5.13", + "@vue/shared": "3.5.13" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.12.tgz", - "integrity": "sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz", + "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.12", - "@vue/runtime-core": "3.5.12", - "@vue/shared": "3.5.12", + "@vue/reactivity": "3.5.13", + "@vue/runtime-core": "3.5.13", + "@vue/shared": "3.5.13", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.12.tgz", - "integrity": "sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz", + "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==", "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.5.12", - "@vue/shared": "3.5.12" + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13" }, "peerDependencies": { - "vue": "3.5.12" + "vue": "3.5.13" } }, "node_modules/@vue/shared": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.12.tgz", - "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz", + "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==", "license": "MIT" }, "node_modules/@vuetify/loader-shared": { @@ -1266,9 +1297,9 @@ } }, "node_modules/alien-signals": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.2.0.tgz", - "integrity": "sha512-StlonZhBBrsPPwrDjiPAiVTf/rolxffLxVPT60Qv/t88BZ81BvUVzHgGqEFvJ1ii8HXtm1+zU2Icr59tfWEcag==", + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.4.10.tgz", + "integrity": "sha512-7S60rz/mMjz0Djq1VI9rd4bGqKNgxTUGE6k7kwrRO6tF95qt1S3ohz1qaQisvUsfbGh7yXnm6DPRrOhOl1ho1A==", "dev": true, "license": "MIT" }, @@ -1318,9 +1349,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -1407,9 +1438,9 @@ } }, "node_modules/chart.js": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.6.tgz", - "integrity": "sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA==", + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.7.tgz", + "integrity": "sha512-pwkcKfdzTMAU/+jNosKhNL2bHtJc/sSmYgVbuGTEDhzkrhmyihmP7vUc/5ZK9WopidMDHNe3Wm7jOd/WhuHWuw==", "license": "MIT", "dependencies": { "@kurkle/color": "^0.3.0" @@ -1508,9 +1539,9 @@ "license": "MIT" }, "node_modules/core-js": { - "version": "3.38.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", - "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==", + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", + "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", "hasInstallScript": true, "license": "MIT", "funding": { @@ -1519,9 +1550,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "peer": true, @@ -1561,9 +1592,9 @@ "license": "MIT" }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -1667,33 +1698,33 @@ } }, "node_modules/eslint": { - "version": "9.13.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz", - "integrity": "sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==", + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", + "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.11.0", - "@eslint/config-array": "^0.18.0", - "@eslint/core": "^0.7.0", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.13.0", - "@eslint/plugin-kit": "^0.2.0", - "@humanfs/node": "^0.16.5", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.9.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.17.0", + "@eslint/plugin-kit": "^0.2.3", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.1", + "@humanwhocodes/retry": "^0.4.1", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.1.0", - "eslint-visitor-keys": "^4.1.0", - "espree": "^10.2.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -1707,8 +1738,7 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" @@ -1729,9 +1759,9 @@ } }, "node_modules/eslint-plugin-vue": { - "version": "9.30.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.30.0.tgz", - "integrity": "sha512-CyqlRgShvljFkOeYK8wN5frh/OGTvkj1S7wlr2Q2pUvwq+X5VYiLd6ZjujpgSgLnys2W8qrBLkXQ41SUYaoPIQ==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.32.0.tgz", + "integrity": "sha512-b/Y05HYmnB/32wqVcjxjHZzNpwxj1onBOvqW89W+V+XNG1dRuaFbNd3vT9CLbr2LXjEoq+3vn8DanWf7XU22Ug==", "dev": true, "license": "MIT", "dependencies": { @@ -1768,9 +1798,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", - "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", "dev": true, "license": "BSD-2-Clause", "peer": true, @@ -1786,9 +1816,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", - "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, "license": "Apache-2.0", "peer": true, @@ -1800,16 +1830,16 @@ } }, "node_modules/espree": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", - "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "dev": true, "license": "BSD-2-Clause", "peer": true, "dependencies": { - "acorn": "^8.12.0", + "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.1.0" + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1926,9 +1956,9 @@ "peer": true }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", "dev": true, "license": "ISC", "dependencies": { @@ -1996,9 +2026,9 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "dev": true, "license": "ISC", "peer": true @@ -2357,9 +2387,9 @@ "peer": true }, "node_modules/magic-string": { - "version": "0.30.12", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", - "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -2477,9 +2507,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "funding": [ { "type": "github", @@ -2680,9 +2710,9 @@ } }, "node_modules/pinia": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.2.4.tgz", - "integrity": "sha512-K7ZhpMY9iJ9ShTC0cR2+PnxdQRuwVIsXDO/WIEV/RnMC/vmSoKDTKW/exNQYPI+4ij10UjXqdNiEHwn47McANQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.0.tgz", + "integrity": "sha512-ohZj3jla0LL0OH5PlLTDMzqKiVw2XARmC1XYLdLWIPBMdhDW/123ZWr4zVAhtJm+aoSkFa13pYXskAvAscIkhQ==", "license": "MIT", "dependencies": { "@vue/devtools-api": "^6.6.3", @@ -2692,49 +2722,19 @@ "url": "https://github.com/sponsors/posva" }, "peerDependencies": { - "@vue/composition-api": "^1.4.0", "typescript": ">=4.4.4", - "vue": "^2.6.14 || ^3.3.0" + "vue": "^2.7.0 || ^3.5.11" }, "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - }, "typescript": { "optional": true } } }, - "node_modules/pinia/node_modules/vue-demi": { - "version": "0.14.10", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", - "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } - } - }, "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "funding": [ { "type": "opencollective", @@ -2752,7 +2752,7 @@ "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.1.0", + "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { @@ -2802,9 +2802,9 @@ } }, "node_modules/qrcode.vue": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/qrcode.vue/-/qrcode.vue-3.5.1.tgz", - "integrity": "sha512-Mek5hpUgYP2KsRW4mnyPMUttknuXSe37UorUzymYi3rr/74rV0aTvejl2gF2phrxwAEm6zhpSvkGzIxftxj5Tg==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/qrcode.vue/-/qrcode.vue-3.6.0.tgz", + "integrity": "sha512-vQcl2fyHYHMjDO1GguCldJxepq2izQjBkDEEu9NENgfVKP6mv/e2SU62WbqYHGwTgWXLhxZ1NCD1dAZKHQq1fg==", "license": "MIT", "peerDependencies": { "vue": "^3.0.0" @@ -2889,9 +2889,9 @@ "license": "Apache-2.0" }, "node_modules/rollup": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.2.tgz", - "integrity": "sha512-do/DFGq5g6rdDhdpPq5qb2ecoczeK6y+2UAjdJ5trjQJj5f1AiVdLRWRc9A9/fFukfvJRgM0UXzxBIYMovm5ww==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.29.1.tgz", + "integrity": "sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==", "devOptional": true, "license": "MIT", "dependencies": { @@ -2905,24 +2905,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.2", - "@rollup/rollup-android-arm64": "4.24.2", - "@rollup/rollup-darwin-arm64": "4.24.2", - "@rollup/rollup-darwin-x64": "4.24.2", - "@rollup/rollup-freebsd-arm64": "4.24.2", - "@rollup/rollup-freebsd-x64": "4.24.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.2", - "@rollup/rollup-linux-arm-musleabihf": "4.24.2", - "@rollup/rollup-linux-arm64-gnu": "4.24.2", - "@rollup/rollup-linux-arm64-musl": "4.24.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.2", - "@rollup/rollup-linux-riscv64-gnu": "4.24.2", - "@rollup/rollup-linux-s390x-gnu": "4.24.2", - "@rollup/rollup-linux-x64-gnu": "4.24.2", - "@rollup/rollup-linux-x64-musl": "4.24.2", - "@rollup/rollup-win32-arm64-msvc": "4.24.2", - "@rollup/rollup-win32-ia32-msvc": "4.24.2", - "@rollup/rollup-win32-x64-msvc": "4.24.2", + "@rollup/rollup-android-arm-eabi": "4.29.1", + "@rollup/rollup-android-arm64": "4.29.1", + "@rollup/rollup-darwin-arm64": "4.29.1", + "@rollup/rollup-darwin-x64": "4.29.1", + "@rollup/rollup-freebsd-arm64": "4.29.1", + "@rollup/rollup-freebsd-x64": "4.29.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.29.1", + "@rollup/rollup-linux-arm-musleabihf": "4.29.1", + "@rollup/rollup-linux-arm64-gnu": "4.29.1", + "@rollup/rollup-linux-arm64-musl": "4.29.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.29.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.29.1", + "@rollup/rollup-linux-riscv64-gnu": "4.29.1", + "@rollup/rollup-linux-s390x-gnu": "4.29.1", + "@rollup/rollup-linux-x64-gnu": "4.29.1", + "@rollup/rollup-linux-x64-musl": "4.29.1", + "@rollup/rollup-win32-arm64-msvc": "4.29.1", + "@rollup/rollup-win32-ia32-msvc": "4.29.1", + "@rollup/rollup-win32-x64-msvc": "4.29.1", "fsevents": "~2.3.2" } }, @@ -3049,14 +3050,6 @@ "node": ">=8" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/tiny-emitter": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", @@ -3104,9 +3097,9 @@ } }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "devOptional": true, "license": "Apache-2.0", "bin": { @@ -3125,9 +3118,9 @@ "license": "MIT" }, "node_modules/unplugin": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.15.0.tgz", - "integrity": "sha512-jTPIs63W+DUEDW207ztbaoO7cQ4p5aVaB823LSlxpsFEU3Mykwxf3ZGC/wzxFJeZlASZYgVrWeo7LgOrqJZ8RA==", + "version": "2.0.0-beta.1", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.0.0-beta.1.tgz", + "integrity": "sha512-2qzQo5LN2DmUZXkWDHvGKLF5BP0WN+KthD6aPnPJ8plRBIjv4lh5O07eYcSxgO2znNw9s4MNhEO1sB+JDllDbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3135,30 +3128,22 @@ "webpack-virtual-modules": "^0.6.2" }, "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "webpack-sources": "^3" - }, - "peerDependenciesMeta": { - "webpack-sources": { - "optional": true - } + "node": ">=18.12.0" } }, "node_modules/unplugin-fonts": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unplugin-fonts/-/unplugin-fonts-1.1.1.tgz", - "integrity": "sha512-/Aw/rL9D2aslGGM0vi+2R2aG508RSwawLnnBuo+JDSqYc4cHJO1R1phllhN6GysEhBp/6a4B6+vSFPVapWyAAw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/unplugin-fonts/-/unplugin-fonts-1.3.1.tgz", + "integrity": "sha512-GmaJWPAWH6lBI4fP8xKdbMZJwTgsnr8PGJOfQE52jlod8QkqSO4M529Nox2L8zYapjB5hox2wCu4N3c/LOal/A==", "dev": true, "license": "MIT", "dependencies": { - "fast-glob": "^3.2.12", - "unplugin": "^1.3.1" + "fast-glob": "^3.3.2", + "unplugin": "2.0.0-beta.1" }, "peerDependencies": { "@nuxt/kit": "^3.0.0", - "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" + "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" }, "peerDependenciesMeta": { "@nuxt/kit": { @@ -3196,9 +3181,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "5.4.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", - "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", "devOptional": true, "license": "MIT", "dependencies": { @@ -3283,16 +3268,16 @@ "license": "MIT" }, "node_modules/vue": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.12.tgz", - "integrity": "sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", + "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.12", - "@vue/compiler-sfc": "3.5.12", - "@vue/runtime-dom": "3.5.12", - "@vue/server-renderer": "3.5.12", - "@vue/shared": "3.5.12" + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-sfc": "3.5.13", + "@vue/runtime-dom": "3.5.13", + "@vue/server-renderer": "3.5.13", + "@vue/shared": "3.5.13" }, "peerDependencies": { "typescript": "*" @@ -3304,15 +3289,41 @@ } }, "node_modules/vue-chartjs": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.1.tgz", - "integrity": "sha512-rZjqcHBxKiHrBl0CIvcOlVEBwRhpWAVf6rDU3vUfa7HuSRmGtCslc0Oc8m16oAVuk0erzc1FCtH1VCriHsrz+A==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.2.tgz", + "integrity": "sha512-NrkbRRoYshbXbWqJkTN6InoDVwVb90C0R7eAVgMWcB9dPikbruaOoTFjFYHE/+tNPdIe6qdLCDjfjPHQ0fw4jw==", "license": "MIT", "peerDependencies": { "chart.js": "^4.1.1", "vue": "^3.0.0-0 || ^2.7.0" } }, + "node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/vue-eslint-parser": { "version": "9.4.3", "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", @@ -3387,13 +3398,13 @@ } }, "node_modules/vue-i18n": { - "version": "9.14.1", - "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.1.tgz", - "integrity": "sha512-xjxV0LYc1xQ8TbAVfIyZiOSS8qoU1R0YwV7V5I8I6Fd64+zvsTsdPgtylPsie3Vdt9wekeYhr+smKDeaK6RBuA==", + "version": "9.14.2", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.2.tgz", + "integrity": "sha512-JK9Pm80OqssGJU2Y6F7DcM8RFHqVG4WkuCqOZTVsXkEzZME7ABejAUqUdA931zEBedc4thBgSUWxeQh4uocJAQ==", "license": "MIT", "dependencies": { - "@intlify/core-base": "9.14.1", - "@intlify/shared": "9.14.1", + "@intlify/core-base": "9.14.2", + "@intlify/shared": "9.14.2", "@vue/devtools-api": "^6.5.0" }, "engines": { @@ -3407,9 +3418,9 @@ } }, "node_modules/vue-router": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.5.tgz", - "integrity": "sha512-4fKZygS8cH1yCyuabAXGUAsyi1b2/o/OKgu/RUb+znIYOxPRxdkytJEx+0wGcpBE1pX6vUgh5jwWOKRGvuA/7Q==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.0.tgz", + "integrity": "sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==", "license": "MIT", "dependencies": { "@vue/devtools-api": "^6.6.4" @@ -3422,15 +3433,14 @@ } }, "node_modules/vue-tsc": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.1.8.tgz", - "integrity": "sha512-6+vjb7JLxKIzeD/1ktoUBZGAr+148FQoEFl8Lv5EpDJLO2PrUalhp7atMEuzEkLnoooM5bg3pJqjZI+oobxIaQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.0.tgz", + "integrity": "sha512-gtmM1sUuJ8aSb0KoAFmK9yMxb8TxjewmxqTJ1aKphD5Cbu0rULFY6+UQT51zW7SpUcenfPUuflKyVwyx9Qdnxg==", "dev": true, "license": "MIT", "dependencies": { - "@volar/typescript": "~2.4.8", - "@vue/language-core": "2.1.8", - "semver": "^7.5.4" + "@volar/typescript": "~2.4.11", + "@vue/language-core": "2.2.0" }, "bin": { "vue-tsc": "bin/vue-tsc.js" @@ -3449,9 +3459,9 @@ } }, "node_modules/vuetify": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.7.3.tgz", - "integrity": "sha512-bpuvBpZl1/+nLlXDgdVXekvMNR6W/ciaoa8CYlpeAzAARbY8zUFSoBq05JlLhkIHI58AnzKVy4c09d0OtfYAPg==", + "version": "3.7.6", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.7.6.tgz", + "integrity": "sha512-lol0Va5HtMIqZfjccSD5DLv5v31R/asJXzc6s7ULy51PHr1DjXxWylZejhq0kVpMGW64MiV1FmA/p8eYQfOWfQ==", "license": "MIT", "engines": { "node": "^12.20 || >=14.13" diff --git a/frontend/src/components/Listen.vue b/frontend/src/components/Listen.vue index 2b29110..8ea85dc 100644 --- a/frontend/src/components/Listen.vue +++ b/frontend/src/components/Listen.vue @@ -14,6 +14,8 @@ :label="$t('in.port')" hide-details type="number" + min="1" + max="65535" required v-model.number="inbound.listen_port"> diff --git a/frontend/src/components/OutJson.vue b/frontend/src/components/OutJson.vue index 35dbf74..afbc382 100644 --- a/frontend/src/components/OutJson.vue +++ b/frontend/src/components/OutJson.vue @@ -6,20 +6,20 @@ hide-details :items="['4','4a','5']" :label="$t('version')" - v-model="inData.outJson.version"> + v-model="inData.out_json.version"> - + - + + v-model="inData.out_json.path"> @@ -36,14 +36,14 @@ hide-details :label="$t('types.vmess.security')" :items="vmessSecurities" - v-model="inData.outJson.security"> + v-model="inData.out_json.security"> - + - + @@ -52,7 +52,7 @@ hide-details type="number" min="0" - v-model.number="inData.outJson.recv_window"> + v-model.number="inData.out_json.recv_window"> - + @@ -114,8 +114,8 @@ export default { needNetwork():boolean { return this.haveNetwork.includes(this.$props.type) }, needUot():boolean { return this.havUoT.includes(this.$props.type) }, packet_encoding: { - get() { return this.$props.inData.outJson.packet_encoding != undefined ? this.$props.inData.outJson.packet_encoding : 'none'; }, - set(v:string) { this.$props.inData.outJson.packet_encoding = v != "none" ? v : undefined } + get() { return this.$props.inData.out_json.packet_encoding != undefined ? this.$props.inData.out_json.packet_encoding : 'none'; }, + set(v:string) { this.$props.inData.out_json.packet_encoding = v != "none" ? v : undefined } }, }, components: { Network, UoT, Headers } diff --git a/frontend/src/components/WgPeer.vue b/frontend/src/components/WgPeer.vue index 6dffcc1..695fe3f 100644 --- a/frontend/src/components/WgPeer.vue +++ b/frontend/src/components/WgPeer.vue @@ -4,7 +4,7 @@ + v-model="address"> @@ -13,7 +13,16 @@ type="number" min="0" hide-details - v-model="data.server_port"> + v-model="port"> + + + + @@ -36,6 +45,8 @@ \ No newline at end of file diff --git a/frontend/src/components/protocols/Wireguard.vue b/frontend/src/components/protocols/Wireguard.vue index 4c2b7c2..d589474 100644 --- a/frontend/src/components/protocols/Wireguard.vue +++ b/frontend/src/components/protocols/Wireguard.vue @@ -2,22 +2,40 @@ - + + - - - - - - - + - - + + + + + + + + + - + - + + v-model="data.name"> - - - - - - - - @@ -66,10 +76,7 @@ - - - - + @@ -80,9 +87,6 @@ - - - @@ -95,7 +99,7 @@ \ No newline at end of file diff --git a/frontend/src/components/tls/InTLS.vue b/frontend/src/components/tls/InTLS.vue index 9a54cf5..5763141 100644 --- a/frontend/src/components/tls/InTLS.vue +++ b/frontend/src/components/tls/InTLS.vue @@ -1,242 +1,25 @@ diff --git a/frontend/src/layouts/default/Drawer.vue b/frontend/src/layouts/default/Drawer.vue index 4701039..13e8943 100644 --- a/frontend/src/layouts/default/Drawer.vue +++ b/frontend/src/layouts/default/Drawer.vue @@ -53,6 +53,7 @@ const menu = [ { title: 'pages.inbounds', icon: 'mdi-cloud-download', path: '/inbounds' }, { title: 'pages.clients', icon: 'mdi-account-multiple', path: '/clients' }, { title: 'pages.outbounds', icon: 'mdi-cloud-upload', path: '/outbounds' }, + { title: 'pages.endpoints', icon: 'mdi-cloud-tags', path: '/endpoints' }, { title: 'pages.rules', icon: 'mdi-routes', path: '/rules' }, { title: 'pages.tls', icon: 'mdi-certificate', path: '/tls' }, { title: 'pages.basics', icon: 'mdi-application-cog', path: '/basics' }, diff --git a/frontend/src/layouts/modals/Client.vue b/frontend/src/layouts/modals/Client.vue index cae827a..f9143cd 100644 --- a/frontend/src/layouts/modals/Client.vue +++ b/frontend/src/layouts/modals/Client.vue @@ -41,7 +41,7 @@ - +
@@ -80,11 +80,6 @@ > - - - - - @@ -189,7 +184,7 @@ import DatePick from '@/components/DateTime.vue' import { HumanReadable } from '@/plugins/utils' export default { - props: ['visible', 'data', 'index', 'inboundTags', 'groups', 'stats'], + props: ['visible', 'data', 'id', 'inboundTags', 'groups'], emits: ['close', 'save'], data() { return { @@ -206,7 +201,7 @@ export default { }, methods: { updateData() { - if (this.$props.index != -1) { + if (this.$props.id > 0) { const newData = JSON.parse(this.$props.data) this.client = createClient(newData) this.title = "edit" @@ -217,7 +212,6 @@ export default { this.title = "add" this.clientConfig = randomConfigs('client') } - this.clientStats = this.$props.stats this.links = this.client.links.filter(l => l.type == 'local') this.extLinks = this.client.links.filter(l => l.type == 'external') this.subLinks = this.client.links.filter(l => l.type == 'sub') @@ -243,8 +237,8 @@ export default { }, computed: { clientInbounds: { - get() { return this.client.inbounds.length>0 ? this.client.inbounds.filter(i => this.inboundTags.includes(i)) : [] }, - set(newValue:string[]) { this.client.inbounds = newValue.length == 0 ? [] : newValue } + get() { return this.client.inbounds.length>0 ? this.client.inbounds : [] }, + set(v:any[]) { this.client.inbounds = v.length == 0 ? [] : v.map(i => i.value) } }, expDate: { get() { return this.client.expiry}, diff --git a/frontend/src/layouts/modals/ClientBulk.vue b/frontend/src/layouts/modals/ClientBulk.vue index aaa3971..4f305dc 100644 --- a/frontend/src/layouts/modals/ClientBulk.vue +++ b/frontend/src/layouts/modals/ClientBulk.vue @@ -59,11 +59,6 @@ > - - - - - @@ -109,7 +104,6 @@ export default { clientInbounds: [], expiry: 0, Volume: 0, - clientStats: false, }, patterns: [ { title: i18n.global.t("bulk.random"), value: "random" }, @@ -129,7 +123,6 @@ export default { clientInbounds: [], expiry: 0, Volume: 0, - clientStats: false, } }, closeModal() { @@ -157,7 +150,7 @@ export default { group: this.bulkData.group })) } - this.$emit('save', this.clients, this.bulkData.clientInbounds, this.bulkData.clientStats) + this.$emit('save', this.clients, this.bulkData.clientInbounds) this.resetData() // reset to default this.loading = false }, diff --git a/frontend/src/layouts/modals/Endpoint.vue b/frontend/src/layouts/modals/Endpoint.vue new file mode 100644 index 0000000..ef958d4 --- /dev/null +++ b/frontend/src/layouts/modals/Endpoint.vue @@ -0,0 +1,153 @@ + + + \ No newline at end of file diff --git a/frontend/src/layouts/modals/Inbound.vue b/frontend/src/layouts/modals/Inbound.vue index cfd8cdf..026e885 100644 --- a/frontend/src/layouts/modals/Inbound.vue +++ b/frontend/src/layouts/modals/Inbound.vue @@ -1,12 +1,18 @@ @@ -349,8 +347,7 @@ import QrCode from '@/layouts/modals/QrCode.vue' import Stats from '@/layouts/modals/Stats.vue' import { Client, createClient } from '@/types/clients' import { computed, ref } from 'vue' -import { Config, V2rayApiStats } from '@/types/config' -import { InTypes, Inbound,InboundWithUser, ShadowTLS, VLESS } from '@/types/inbounds' +import { Inbound, inboundWithUsers } from '@/types/inbounds' import { Link, LinkUtil } from '@/plugins/link' import { HumanReadable } from '@/plugins/utils' import { i18n } from '@/locales' @@ -367,21 +364,13 @@ const isOnline = (cname: string) => computed(() => { return Data().onlines?.user ? Data().onlines.user.includes(cname) : false }) -const appConfig = computed((): Config => { - return Data().config -}) - -const v2rayStats = computed((): V2rayApiStats => { - return appConfig.value.experimental.v2ray_api.stats -}) - const inbounds = computed((): Inbound[] => { - return appConfig.value?.inbounds + return Data().inbounds?? [] }) -const inboundTags = computed((): string[] => { +const inboundTags = computed((): any[] => { if (!inbounds.value) return [] - return inbounds.value?.filter(i => i.tag != "" && Object.hasOwn(i,'users')).map(i => i.tag) + return inbounds.value?.filter(i => i.tag != "" && inboundWithUsers.includes(i.type)).map(i => { return { title: i.tag, value: i.id } }) }) const groups = computed((): string[] => { @@ -430,102 +419,44 @@ const groupBy = [ const modal = ref({ visible: false, - index: -1, + id: 0, data: "", - stats: false, }) const delOverlay = ref(new Array(clients.value.length).fill(false)) -const showModal = (id: number) => { - const index = id == -1 ? -1 : clients.value.findIndex(c => c.id == id) - modal.value.index = index - modal.value.data = index == -1 ? '' : JSON.stringify(clients.value[index]) - modal.value.stats = index == -1 ? false : v2rayStats.value.users.includes(clients.value[index].name) +const showModal = async (id: number) => { + modal.value.id = id + modal.value.data = id == 0 ? '' : JSON.stringify(clients.value.findLast(o => o.id == id)) modal.value.visible = true } const closeModal = () => { modal.value.visible = false } -const saveModal = (data:any, stats:boolean) => { +const saveModal = async (data:any) => { // Check duplicate name - const oldName = modal.value.index != -1 ? clients.value[modal.value.index].name : null + const oldName = modal.value.id > 0 ? clients.value.findLast(i => i.id == modal.value.id)?.name : null if (data.name != oldName && clients.value.findIndex(c => c.name == data.name) != -1) { push.error({ message: i18n.global.t('error.dplData') + ": " + i18n.global.t('client.name') }) return } - if(modal.value.index == -1) { - clients.value.push(data) - } else { - clients.value[modal.value.index] = data - } - - // Rebuild affected inbounds - buildInboundsUsers(data.inbounds) // Rebuild links - data.links = updateLinks(data) + const clientInbounds = data.inbounds.length == 0 ? [] : await Data().loadInbounds(data.inbounds) + data.links = updateLinks(data, clientInbounds) - // Set Client Stats - const sIndex = v2rayStats.value.users.findIndex(i => i == data.name) // Find if new user exists - - if (oldName != data.name) { - v2rayStats.value.users = v2rayStats.value.users.filter(item => item != oldName) - } - - if (stats) { - // Add if dos not exist - if (data.name.length>0 && sIndex == -1) v2rayStats.value.users.push(data.name) - } else { - // Delete if exists - if (sIndex != -1) v2rayStats.value.users.splice(sIndex,1) - } - - modal.value.visible = false + // save data + const success = await Data().save("clients", modal.value.id == 0 ? "new" : "edit", data) + if (success) modal.value.visible = false } -const buildInboundsUsers = (inboundTags:string[]) => { - inboundTags.forEach(tag => { - const inbound_index = inbounds.value.findIndex(i => i.tag == tag) - if (inbound_index != -1){ - const users = [] - const newInbound = inbounds.value[inbound_index] - const inboundClients = clients.value.filter(c => c.enable && c.inbounds.includes(tag)) - inboundClients.forEach(c => { - // Remove flow in non tls VLESS - if (newInbound.type == InTypes.VLESS) { - const vlessInbound = newInbound - if (!vlessInbound.tls?.enabled || vlessInbound.transport?.type) delete(c.config?.vless?.flow) - } - users.push(c.config[newInbound.type]) - }) - newInbound.users = users - // Exceptions for Naive and ShadowTLSv3 - if (users.length == 0){ - if (newInbound.type == InTypes.Naive) { - newInbound.users = [{}] - } else { - if (newInbound.type == InTypes.ShadowTLS){ - const ssTls = newInbound - if (ssTls.version == 3) newInbound.users = [{}] - } - } - } - - inbounds.value[inbound_index] = newInbound - } - }) -} -const updateLinks = (c:Client):Link[] => { - const clientInbounds = inbounds.value.filter(i => c.inbounds.includes(i.tag)) +const updateLinks = (c:Client, clientInbounds:Inbound[]):Link[] => { const newLinks = [] clientInbounds.forEach(i =>{ - const tlsConfig = Data().tlsConfigs?.findLast((t:any) => t.inbounds.includes(i.tag)) - const cData = Data().inData?.findLast((d:any) => d.tag == i.tag) - const addrs = cData ? cData.addrs : [] - const uris = LinkUtil.linkGenerator(c,i, tlsConfig?.client?? {}, addrs) + const tls = i.tls_id && i.tls_id>0 ? Data().tlsConfigs?.findLast((t:any) => t.id == i.tls_id) : undefined + const uris = LinkUtil.linkGenerator(c,i, tls, i.addrs) if (uris.length>0){ uris.forEach(uri => { newLinks.push({ type: 'local', remark: i.tag, uri: uri }) @@ -537,21 +468,10 @@ const updateLinks = (c:Client):Link[] => { return links } -const delClient = (id: number) => { - const clientIndex = clients.value.findIndex(c => c.id === id) - const oldData = createClient(clients.value[clientIndex]) - - // Delete stats if exists and will be orphaned - const tagCounts = clients.value.filter(i => i.name == oldData.name).length - const sIndex = v2rayStats.value.users.findIndex(i => i == oldData.name) - if (tagCounts == 1 && sIndex != -1){ - v2rayStats.value.users.splice(sIndex,1) - } - - clients.value.splice(clientIndex,1) - buildInboundsUsers(oldData.inbounds) - if (id>0) Data().delClient(id) - delOverlay.value[clientIndex] = false +const delClient = async (id: number) => { + const index = clients.value.findIndex(c => c.id === id) + const success = await Data().save("clients", "del", id) + if (success) delOverlay.value[index] = false } const qrcode = ref({ @@ -636,14 +556,12 @@ const closeBulk = () => { addBulkModal.value = false } -const saveBulk = (bulkClients: Client[], clientInbounds: string[], clientStats: boolean) => { +const saveBulk = async (bulkClients: Client[], clientInbounds: number[]) => { + const inboundData = clientInbounds.length == 0 ? [] : await Data().loadInbounds(clientInbounds) bulkClients.forEach((c,c_index) => { - bulkClients[c_index].links = updateLinks(c) + bulkClients[c_index].links = updateLinks(c, inboundData) }) clients.value.push(...bulkClients) - buildInboundsUsers(clientInbounds) - // Stats - if (clientStats) v2rayStats.value.users.push(...bulkClients.map(bc => bc.name)) closeBulk() } \ No newline at end of file diff --git a/frontend/src/views/Endpoints.vue b/frontend/src/views/Endpoints.vue new file mode 100644 index 0000000..2258da0 --- /dev/null +++ b/frontend/src/views/Endpoints.vue @@ -0,0 +1,166 @@ + + + \ No newline at end of file diff --git a/frontend/src/views/Inbounds.vue b/frontend/src/views/Inbounds.vue index 17c647d..9da8321 100644 --- a/frontend/src/views/Inbounds.vue +++ b/frontend/src/views/Inbounds.vue @@ -2,10 +2,7 @@ - {{ $t('actions.add') }} + {{ $t('actions.add') }} @@ -48,22 +45,25 @@ {{ $t('objects.tls') }} - {{ Object.hasOwn(item,'tls') ? $t(item.tls?.enabled ? 'enable' : 'disable') : '-' }} + {{ item.tls_id > 0 ? $t('enable') : $t('disable') }} {{ $t('pages.clients') }} - - {{ u }}
-
- {{ Array.isArray(item.users) ? item.users.length : '-' }} + +
{{ $t('online') }} -