From 83b61d9da4c4c7c82f5e6de672f89f501cf4bdac Mon Sep 17 00:00:00 2001 From: Sora39831 <540587985@qq.com> Date: Mon, 6 Apr 2026 10:46:48 +0800 Subject: [PATCH] feat(limit): add inbound device-limit enforcement with safe unban flow - add inbound deviceLimit model/frontend fields and translations - add CheckDeviceLimitJob with observation window and xray API ban/unban - prevent job re-entrancy and restore users when limit is disabled - reduce lock scope via snapshots to avoid blocking log parsing --- database/model/model.go | 1 + web/assets/js/model/dbinbound.js | 3 +- web/html/form/inbound.html | 16 +- web/html/inbounds.html | 8 + web/job/check_device_limit_job.go | 345 +++++++++++++++++++++++++++ web/service/inbound.go | 1 + web/service/xray.go | 8 + web/translation/translate.en_US.toml | 2 + web/translation/translate.zh_CN.toml | 2 + web/web.go | 2 + 10 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 web/job/check_device_limit_job.go diff --git a/database/model/model.go b/database/model/model.go index 1865d1a9..98252f62 100644 --- a/database/model/model.go +++ b/database/model/model.go @@ -42,6 +42,7 @@ type Inbound struct { Remark string `json:"remark" form:"remark"` // Human-readable remark Enable bool `json:"enable" form:"enable" gorm:"index:idx_enable_traffic_reset,priority:1"` // Whether the inbound is enabled ExpiryTime int64 `json:"expiryTime" form:"expiryTime"` // Expiration timestamp + DeviceLimit int `json:"deviceLimit" form:"deviceLimit" gorm:"column:device_limit;default:0"` // Active device/IP limit by inbound TrafficReset string `json:"trafficReset" form:"trafficReset" gorm:"default:never;index:idx_enable_traffic_reset,priority:2"` // Traffic reset schedule LastTrafficResetTime int64 `json:"lastTrafficResetTime" form:"lastTrafficResetTime" gorm:"default:0"` // Last traffic reset timestamp ClientStats []xray.ClientTraffic `gorm:"foreignKey:InboundId;references:Id" json:"clientStats" form:"clientStats"` // Client traffic statistics diff --git a/web/assets/js/model/dbinbound.js b/web/assets/js/model/dbinbound.js index befc618e..ea20d855 100644 --- a/web/assets/js/model/dbinbound.js +++ b/web/assets/js/model/dbinbound.js @@ -10,6 +10,7 @@ class DBInbound { this.remark = ""; this.enable = true; this.expiryTime = 0; + this.deviceLimit = 0; this.trafficReset = "never"; this.lastTrafficResetTime = 0; @@ -148,4 +149,4 @@ class DBInbound { const inbound = this.toInbound(); return inbound.genInboundLinks(this.remark, remarkModel); } -} \ No newline at end of file +} diff --git a/web/html/form/inbound.html b/web/html/form/inbound.html index 8b59dc28..dcce4e0f 100644 --- a/web/html/form/inbound.html +++ b/web/html/form/inbound.html @@ -49,6 +49,20 @@ :min="0"> + + + + +