diff --git a/database/model/model.go b/database/model/model.go index 5685460..396a1bd 100644 --- a/database/model/model.go +++ b/database/model/model.go @@ -35,6 +35,14 @@ type Client struct { Up int64 `json:"up" form:"up"` Desc string `json:"desc" form:"desc"` Group string `json:"group" form:"group"` + + // Delay start and periodic reset + DelayStart bool `json:"delayStart" form:"delayStart" gorm:"default:false;not null"` + AutoReset bool `json:"autoReset" form:"autoReset" gorm:"default:false;not null"` + ResetDays int `json:"resetDays" form:"resetDays" gorm:"default:0;not null"` + NextReset int64 `json:"nextReset" form:"nextReset" gorm:"default:0;not null"` + TotalUp int64 `json:"totalUp" form:"totalUp" gorm:"default:0;not null"` + TotalDown int64 `json:"totalDown" form:"totalDown" gorm:"default:0;not null"` } type Stats struct { diff --git a/service/client.go b/service/client.go index 034cba4..8fc9b90 100644 --- a/service/client.go +++ b/service/client.go @@ -38,7 +38,9 @@ func (s *ClientService) getById(id string) (*[]model.Client, error) { func (s *ClientService) GetAll() (*[]model.Client, error) { db := database.GetDB() var clients []model.Client - err := db.Model(model.Client{}).Select("`id`, `enable`, `name`, `desc`, `group`, `inbounds`, `up`, `down`, `volume`, `expiry`").Scan(&clients).Error + err := db.Model(model.Client{}). + Select("`id`, `enable`, `name`, `desc`, `group`, `inbounds`, `up`, `down`, `volume`, `expiry`"). + Scan(&clients).Error if err != nil { return nil, err } @@ -369,24 +371,34 @@ func (s *ClientService) DepleteClients() ([]uint, error) { var users []string var inboundIds []uint - now := time.Now().Unix() + dt := time.Now().Unix() db := database.GetDB() tx := db.Begin() defer func() { if err == nil { tx.Commit() + _, err1 := db.Raw("PRAGMA wal_checkpoint(FULL)").Rows() + if err1 != nil { + logger.Error("Error checkpointing WAL: ", err1.Error()) + } } 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 + // Reset clients + inboundIds, err = s.ResetClients(tx, dt) + if err != nil { + return nil, err + } + + // Deplete clients + err = tx.Model(model.Client{}).Where("enable = true AND ((volume >0 AND up+down > volume) OR (expiry > 0 AND expiry < ?))", dt).Scan(&clients).Error if err != nil { return nil, err } - dt := time.Now().Unix() for _, client := range clients { logger.Debug("Client ", client.Name, " is going to be disabled") users = append(users, client.Name) @@ -405,7 +417,7 @@ func (s *ClientService) DepleteClients() ([]uint, error) { // Save changes if len(changes) > 0 { - 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 + err = tx.Model(model.Client{}).Where("enable = true AND ((volume >0 AND up+down > volume) OR (expiry > 0 AND expiry < ?))", dt).Update("enable", false).Error if err != nil { return nil, err } @@ -419,6 +431,89 @@ func (s *ClientService) DepleteClients() ([]uint, error) { return inboundIds, nil } +func (s *ClientService) ResetClients(tx *gorm.DB, dt int64) ([]uint, error) { + var err error + var resetClients, allClients []*model.Client + var changes []model.Changes + var inboundIds []uint + // Set delay start without periodic reset + err = tx.Model(model.Client{}). + Where("enable = true AND delay_start = true AND auto_reset = false AND (Up + Down) > 0").Find(&resetClients).Error + if err != nil { + return nil, err + } + for _, client := range resetClients { + client.Expiry = dt + (int64(client.ResetDays) * 86400) + client.DelayStart = false + changes = append(changes, model.Changes{ + DateTime: dt, + Actor: "ResetJob", + Key: "clients", + Action: "reset", + Obj: json.RawMessage("\"" + client.Name + "\""), + }) + } + allClients = append(allClients, resetClients...) + + // Set delay start with periodic reset + err = tx.Model(model.Client{}). + Where("enable = true AND delay_start = true AND auto_reset = true AND (Up + Down) > 0").Find(&resetClients).Error + if err != nil { + return nil, err + } + for _, client := range resetClients { + client.NextReset = dt + (int64(client.ResetDays) * 86400) + client.DelayStart = false + changes = append(changes, model.Changes{ + DateTime: dt, + Actor: "ResetJob", + Key: "clients", + Action: "reset", + Obj: json.RawMessage("\"" + client.Name + "\""), + }) + } + allClients = append(allClients, resetClients...) + + // Set periodic reset + err = tx.Model(model.Client{}). + Where("delay_start = false AND auto_reset = true AND next_reset < ?", dt).Find(&resetClients).Error + if err != nil { + return nil, err + } + for _, client := range resetClients { + client.NextReset = dt + (int64(client.ResetDays) * 86400) + client.TotalUp += client.Up + client.TotalDown += client.Down + client.Up = 0 + client.Down = 0 + if !client.Enable { + client.Enable = true + var clientInboundIds []uint + json.Unmarshal(client.Inbounds, &clientInboundIds) + inboundIds = common.UnionUintArray(inboundIds, clientInboundIds) + } + } + allClients = append(allClients, resetClients...) + + // Save clients + if len(allClients) > 0 { + err = tx.Save(allClients).Error + if err != nil { + return nil, err + } + } + + // Save changes + if len(changes) > 0 { + err = tx.Model(model.Changes{}).Create(&changes).Error + if err != nil { + return nil, err + } + LastUpdate = dt + } + return inboundIds, nil +} + func (s *ClientService) findInboundsChanges(tx *gorm.DB, client *model.Client, fillOmitted bool) ([]uint, error) { var err error var oldClient model.Client diff --git a/service/config.go b/service/config.go index 53ec5c4..d2a130c 100644 --- a/service/config.go +++ b/service/config.go @@ -14,12 +14,12 @@ import ( ) var ( - LastUpdate int64 - corePtr *core.Core - startCoreMu sync.Mutex + LastUpdate int64 + corePtr *core.Core + startCoreMu sync.Mutex startCoreInProgress bool - lastStartFailTime time.Time - startCooldown = 15 * time.Second + lastStartFailTime time.Time + startCooldown = 15 * time.Second ) type ConfigService struct { diff --git a/service/server.go b/service/server.go index b10c29a..8db4112 100644 --- a/service/server.go +++ b/service/server.go @@ -268,8 +268,8 @@ func (s *ServerService) GetDatabaseInfo() map[string]int64 { db.Model(&model.Outbound{}).Count(&outboundsCount) db.Model(&model.Service{}).Count(&servicesCount) db.Model(&model.Endpoint{}).Count(&endpointsCount) - db.Model(&model.Client{}).Select("COALESCE(SUM(up),0)").Scan(&clientUp) - db.Model(&model.Client{}).Select("COALESCE(SUM(down),0)").Scan(&clientDown) + db.Model(&model.Client{}).Select("COALESCE(SUM(up+total_up),0)").Scan(&clientUp) + db.Model(&model.Client{}).Select("COALESCE(SUM(down+total_down),0)").Scan(&clientDown) info["clients"] = clientsCount info["inbounds"] = inboundsCount