diff --git a/database/model/model.go b/database/model/model.go index 2e7095d3..86ab0487 100644 --- a/database/model/model.go +++ b/database/model/model.go @@ -104,4 +104,6 @@ type Client struct { SubID string `json:"subId" form:"subId"` Comment string `json:"comment" form:"comment"` Reset int `json:"reset" form:"reset"` + CreatedAt int64 `json:"created_at,omitempty"` + UpdatedAt int64 `json:"updated_at,omitempty"` } diff --git a/web/service/inbound.go b/web/service/inbound.go index 6e10e798..19cc855f 100644 --- a/web/service/inbound.go +++ b/web/service/inbound.go @@ -175,6 +175,30 @@ func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, boo return inbound, false, err } + // Ensure created_at and updated_at on clients in settings + if len(clients) > 0 { + var settings map[string]any + if err2 := json.Unmarshal([]byte(inbound.Settings), &settings); err2 == nil && settings != nil { + now := time.Now().Unix() * 1000 + updatedClients := make([]model.Client, 0, len(clients)) + for _, c := range clients { + if c.CreatedAt == 0 { + c.CreatedAt = now + } + c.UpdatedAt = now + updatedClients = append(updatedClients, c) + } + settings["clients"] = updatedClients + if bs, err3 := json.MarshalIndent(settings, "", " "); err3 == nil { + inbound.Settings = string(bs) + } else { + logger.Debug("Unable to marshal inbound settings with timestamps:", err3) + } + } else if err2 != nil { + logger.Debug("Unable to parse inbound settings for timestamps:", err2) + } + } + // Secure client ID for _, client := range clients { switch inbound.Protocol { @@ -320,6 +344,53 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound, return inbound, false, err } + // Ensure created_at and updated_at exist in inbound.Settings clients + { + var oldSettings map[string]any + _ = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings) + emailToCreated := map[string]int64{} + if oldSettings != nil { + if oc, ok := oldSettings["clients"].([]any); ok { + for _, it := range oc { + if m, ok2 := it.(map[string]any); ok2 { + if email, ok3 := m["email"].(string); ok3 { + switch v := m["created_at"].(type) { + case float64: + emailToCreated[email] = int64(v) + case int64: + emailToCreated[email] = v + } + } + } + } + } + } + var newSettings map[string]any + if err2 := json.Unmarshal([]byte(inbound.Settings), &newSettings); err2 == nil && newSettings != nil { + now := time.Now().Unix() * 1000 + if nSlice, ok := newSettings["clients"].([]any); ok { + for i := range nSlice { + if m, ok2 := nSlice[i].(map[string]any); ok2 { + email, _ := m["email"].(string) + if _, ok3 := m["created_at"]; !ok3 { + if v, ok4 := emailToCreated[email]; ok4 && v > 0 { + m["created_at"] = v + } else { + m["created_at"] = now + } + } + m["updated_at"] = now + nSlice[i] = m + } + } + newSettings["clients"] = nSlice + if bs, err3 := json.MarshalIndent(newSettings, "", " "); err3 == nil { + inbound.Settings = string(bs) + } + } + } + } + oldInbound.Up = inbound.Up oldInbound.Down = inbound.Down oldInbound.Total = inbound.Total @@ -422,6 +493,17 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) (bool, error) { } interfaceClients := settings["clients"].([]any) + // Add timestamps for new clients being appended + nowTs := time.Now().Unix() * 1000 + for i := range interfaceClients { + if cm, ok := interfaceClients[i].(map[string]any); ok { + if _, ok2 := cm["created_at"]; !ok2 { + cm["created_at"] = nowTs + } + cm["updated_at"] = nowTs + interfaceClients[i] = cm + } + } existEmail, err := s.checkEmailsExistForClients(clients) if err != nil { return false, err @@ -672,6 +754,25 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin return false, err } settingsClients := oldSettings["clients"].([]any) + // Preserve created_at and set updated_at for the replacing client + var preservedCreated any + if clientIndex >= 0 && clientIndex < len(settingsClients) { + if oldMap, ok := settingsClients[clientIndex].(map[string]any); ok { + if v, ok2 := oldMap["created_at"]; ok2 { + preservedCreated = v + } + } + } + if len(interfaceClients) > 0 { + if newMap, ok := interfaceClients[0].(map[string]any); ok { + if preservedCreated == nil { + preservedCreated = time.Now().Unix() * 1000 + } + newMap["created_at"] = preservedCreated + newMap["updated_at"] = time.Now().Unix() * 1000 + interfaceClients[0] = newMap + } + } settingsClients[clientIndex] = interfaceClients[0] oldSettings["clients"] = settingsClients @@ -909,10 +1010,22 @@ func (s *InboundService) adjustTraffics(tx *gorm.DB, dbClientTraffics []*xray.Cl oldExpiryTime := c["expiryTime"].(float64) newExpiryTime := (time.Now().Unix() * 1000) - int64(oldExpiryTime) c["expiryTime"] = newExpiryTime + c["updated_at"] = time.Now().Unix() * 1000 dbClientTraffics[traffic_index].ExpiryTime = newExpiryTime break } } + // Remove "flow": "xtls-rprx-direct" + if _, ok := c["flow"]; ok { + if c["flow"] == "xtls-rprx-direct" { + c["flow"] = "" + } + } + // Backfill created_at and updated_at + if _, ok := c["created_at"]; !ok { + c["created_at"] = time.Now().Unix() * 1000 + } + c["updated_at"] = time.Now().Unix() * 1000 newClients = append(newClients, any(c)) } settings["clients"] = newClients @@ -1274,6 +1387,7 @@ func (s *InboundService) SetClientTelegramUserID(trafficId int, tgId int64) (boo c := clients[client_index].(map[string]any) if c["email"] == clientEmail { c["tgId"] = tgId + c["updated_at"] = time.Now().Unix() * 1000 newClients = append(newClients, any(c)) } } @@ -1360,6 +1474,7 @@ func (s *InboundService) ToggleClientEnableByEmail(clientEmail string) (bool, bo c := clients[client_index].(map[string]any) if c["email"] == clientEmail { c["enable"] = !clientOldEnabled + c["updated_at"] = time.Now().Unix() * 1000 newClients = append(newClients, any(c)) } } @@ -1423,6 +1538,7 @@ func (s *InboundService) ResetClientIpLimitByEmail(clientEmail string, count int c := clients[client_index].(map[string]any) if c["email"] == clientEmail { c["limitIp"] = count + c["updated_at"] = time.Now().Unix() * 1000 newClients = append(newClients, any(c)) } } @@ -1481,6 +1597,7 @@ func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry c := clients[client_index].(map[string]any) if c["email"] == clientEmail { c["expiryTime"] = expiry_time + c["updated_at"] = time.Now().Unix() * 1000 newClients = append(newClients, any(c)) } } @@ -1542,6 +1659,7 @@ func (s *InboundService) ResetClientTrafficLimitByEmail(clientEmail string, tota c := clients[client_index].(map[string]any) if c["email"] == clientEmail { c["totalGB"] = totalGB * 1024 * 1024 * 1024 + c["updated_at"] = time.Now().Unix() * 1000 newClients = append(newClients, any(c)) } } @@ -1962,6 +2080,11 @@ func (s *InboundService) MigrationRequirements() { c["flow"] = "" } } + // Backfill created_at and updated_at + if _, ok := c["created_at"]; !ok { + c["created_at"] = time.Now().Unix() * 1000 + } + c["updated_at"] = time.Now().Unix() * 1000 newClients = append(newClients, any(c)) } settings["clients"] = newClients