|
|
@@ -9,8 +9,10 @@ import (
|
|
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database"
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
|
+ "github.com/mhsanaei/3x-ui/v3/internal/logger"
|
|
|
|
|
|
"gorm.io/gorm"
|
|
|
+ "gorm.io/gorm/clause"
|
|
|
)
|
|
|
|
|
|
type HwidRequest struct {
|
|
|
@@ -110,12 +112,15 @@ func (s *ClientService) EnforceHwidForSubID(subID string, req HwidRequest) (Hwid
|
|
|
if err != nil {
|
|
|
return res, err
|
|
|
}
|
|
|
+ req = normalizeHwidRequest(req)
|
|
|
if limit <= 0 {
|
|
|
res.Allowed = true
|
|
|
+ if len(req.Hwid) >= minHwidLength {
|
|
|
+ trackUnlimitedHwid(db, subID, req)
|
|
|
+ }
|
|
|
return res, nil
|
|
|
}
|
|
|
|
|
|
- req = normalizeHwidRequest(req)
|
|
|
res.Active = true
|
|
|
res.Limit = limit
|
|
|
if len(req.Hwid) < minHwidLength {
|
|
|
@@ -177,6 +182,19 @@ func (s *ClientService) EnforceHwidForSubID(subID string, req HwidRequest) (Hwid
|
|
|
return res, err
|
|
|
}
|
|
|
|
|
|
+// trackUnlimitedHwid lists devices of a sub with no HWID limit in the panel. It is
|
|
|
+// best-effort: a failed write must not deny a subscription nothing restricts.
|
|
|
+func trackUnlimitedHwid(db *gorm.DB, subID string, req HwidRequest) {
|
|
|
+ now := time.Now().UnixMilli()
|
|
|
+ err := db.Clauses(clause.OnConflict{
|
|
|
+ Columns: []clause.Column{{Name: "sub_id"}, {Name: "hwid_hash"}},
|
|
|
+ DoUpdates: clause.AssignmentColumns([]string{"last_seen", "user_agent", "device_os", "os_version", "device_model"}),
|
|
|
+ }).Create(&model.ClientHwid{SubID: subID, HwidHash: hashHwid(req.Hwid), FirstSeen: now, LastSeen: now, UserAgent: req.UserAgent, DeviceOS: req.DeviceOS, OsVersion: req.OsVersion, DeviceModel: req.DeviceModel}).Error
|
|
|
+ if err != nil {
|
|
|
+ logger.Warning("track HWID for unlimited subscription failed:", err)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// HwidSlotStatusForSubID is SELECT-only: it must never write client_hwids or
|
|
|
// last_seen. Enabled-clients scope mirrors the gate, so limit == limit enforced.
|
|
|
func (s *ClientService) HwidSlotStatusForSubID(subID string) (status HwidSlotStatus, found bool, err error) {
|