feat(mtproto): enforce per-client quota & expiry via mtg-multi limits

Map each mtproto client's totalGB and expiryTime onto mtg-multi's new
[secret-limits] (quota/expires): emit them into the generated config and
hot-apply through PUT /secrets so live connections survive. Quota is
written as an exact "<n>B" byte count that round-trips through both the
config and API parsers without the precision loss of a base-2 unit.

The sidecar's quota counter is not pruned when a secret is dropped, so a
panel-side traffic reset re-pushes the client's secret and then calls
POST /secrets/{name}/reset-quota (wired into every reset path) so a
renewed client is not immediately re-blocked.

Resolve the mtg-multi binary from the fork's latest release tag in
DockerInit.sh and release.yml instead of a hardcoded version pin, so the
panel no longer needs a manual bump per fork release.
This commit is contained in:
MHSanaei
2026-07-08 15:30:56 +02:00
parent 61e12e4c29
commit 328d920e98
8 changed files with 251 additions and 28 deletions
+13 -2
View File
@@ -509,7 +509,7 @@ func (s *InboundService) delClientStatsByEmails(tx *gorm.DB, emails []string) er
}
func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error {
return submitTrafficWrite(func() error {
err := submitTrafficWrite(func() error {
return database.GetDB().Transaction(func(tx *gorm.DB) error {
if err := adjustGroupBaselinesForRemovedTraffic(tx, []string{clientEmail}); err != nil {
return err
@@ -525,6 +525,10 @@ func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error {
return tx.Where("email = ?", clientEmail).Delete(&model.NodeClientTraffic{}).Error
})
})
if err == nil {
s.resetMtprotoClientQuota(clientEmail)
}
return err
}
func (s *InboundService) ResetClientTraffic(id int, clientEmail string) (needRestart bool, err error) {
@@ -533,6 +537,9 @@ func (s *InboundService) ResetClientTraffic(id int, clientEmail string) (needRes
needRestart, inner = s.resetClientTrafficLocked(id, clientEmail)
return inner
})
if err == nil {
s.resetMtprotoClientQuota(clientEmail)
}
return
}
@@ -646,9 +653,13 @@ func (s *InboundService) resetClientTrafficLocked(id int, clientEmail string) (b
}
func (s *InboundService) ResetAllTraffics() error {
return submitTrafficWrite(func() error {
err := submitTrafficWrite(func() error {
return s.resetAllTrafficsLocked()
})
if err == nil {
s.resetAllMtprotoQuotas()
}
return err
}
func (s *InboundService) resetAllTrafficsLocked() error {