feat: Simple periodic traffic reset (for Inbounds) – daily | weekly | monthly (#3407)

* Add periodic traffic reset feature model and ui with localization support

* Remove periodic traffic reset fields from client

* fix: add periodicTrafficReset field to inbound data structure

* feat: implement periodic traffic reset job and integrate with cron scheduler

* feat: enhance periodic traffic reset functionality with scheduling and inbound filtering

* refactor: rename periodicTrafficReset to trafficReset and add lastTrafficResetTime field

* feat: add periodic client traffic reset job and schedule tasks

* Update web/job/periodic_traffic_reset_job.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update web/job/periodic_client_traffic_reset_job.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update web/service/inbound.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* refactor: rename periodicTrafficReset to trafficReset and add lastTrafficResetTime

* feat: add last traffic reset time display and update logic in inbound service

* fix: correct log message for completed periodic traffic reset

* refactor: update traffic reset fields in Inbound model and remove unused client traffic reset job

* refactor: remove unused traffic reset logic and clean up client model fields

* cleanup comments

* fix
This commit is contained in:
Vadim Iskuchekov 2025-09-16 09:24:32 +02:00 committed by GitHub
parent bc0518391e
commit 9623e87511
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 269 additions and 22 deletions

View file

@ -27,16 +27,18 @@ type User struct {
} }
type Inbound struct { type Inbound struct {
Id int `json:"id" form:"id" gorm:"primaryKey;autoIncrement"` Id int `json:"id" form:"id" gorm:"primaryKey;autoIncrement"`
UserId int `json:"-"` UserId int `json:"-"`
Up int64 `json:"up" form:"up"` Up int64 `json:"up" form:"up"`
Down int64 `json:"down" form:"down"` Down int64 `json:"down" form:"down"`
Total int64 `json:"total" form:"total"` Total int64 `json:"total" form:"total"`
AllTime int64 `json:"allTime" form:"allTime" gorm:"default:0"` AllTime int64 `json:"allTime" form:"allTime" gorm:"default:0"`
Remark string `json:"remark" form:"remark"` Remark string `json:"remark" form:"remark"`
Enable bool `json:"enable" form:"enable"` Enable bool `json:"enable" form:"enable" gorm:"index:idx_enable_traffic_reset,priority:1"`
ExpiryTime int64 `json:"expiryTime" form:"expiryTime"` ExpiryTime int64 `json:"expiryTime" form:"expiryTime"`
ClientStats []xray.ClientTraffic `gorm:"foreignKey:InboundId;references:Id" json:"clientStats" form:"clientStats"` TrafficReset string `json:"trafficReset" form:"trafficReset" gorm:"default:never;index:idx_enable_traffic_reset,priority:2"`
LastTrafficResetTime int64 `json:"lastTrafficResetTime" form:"lastTrafficResetTime" gorm:"default:0"`
ClientStats []xray.ClientTraffic `gorm:"foreignKey:InboundId;references:Id" json:"clientStats" form:"clientStats"`
// config part // config part
Listen string `json:"listen" form:"listen"` Listen string `json:"listen" form:"listen"`

View file

@ -10,6 +10,8 @@ class DBInbound {
this.remark = ""; this.remark = "";
this.enable = true; this.enable = true;
this.expiryTime = 0; this.expiryTime = 0;
this.trafficReset = "never";
this.lastTrafficResetTime = 0;
this.listen = ""; this.listen = "";
this.port = 0; this.port = 0;

View file

@ -44,6 +44,30 @@
<a-input-number v-model.number="dbInbound.totalGB" :min="0"></a-input-number> <a-input-number v-model.number="dbInbound.totalGB" :min="0"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item>
<template slot="label">
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.inbounds.periodicTrafficResetDesc" }}</span>
<br v-if="dbInbound.lastTrafficResetTime && dbInbound.lastTrafficResetTime > 0">
<span v-if="dbInbound.lastTrafficResetTime && dbInbound.lastTrafficResetTime > 0">
<strong>{{ i18n "pages.inbounds.lastReset" }}:</strong>
<span v-if="datepicker == 'gregorian'">[[ moment(dbInbound.lastTrafficResetTime).format('YYYY-MM-DD HH:mm:ss') ]]</span>
<span v-else>[[ DateUtil.convertToJalalian(moment(dbInbound.lastTrafficResetTime)) ]]</span>
</span>
</template>
{{ i18n "pages.inbounds.periodicTrafficResetTitle" }}
<a-icon type="question-circle"></a-icon>
</a-tooltip>
</template>
<a-select v-model="dbInbound.trafficReset" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="never">{{ i18n "pages.inbounds.periodicTrafficReset.never" }}</a-select-option>
<a-select-option value="daily">{{ i18n "pages.inbounds.periodicTrafficReset.daily" }}</a-select-option>
<a-select-option value="weekly">{{ i18n "pages.inbounds.periodicTrafficReset.weekly" }}</a-select-option>
<a-select-option value="monthly">{{ i18n "pages.inbounds.periodicTrafficReset.monthly" }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item> <a-form-item>
<template slot="label"> <template slot="label">
<a-tooltip> <a-tooltip>

View file

@ -503,6 +503,12 @@
</a-tag> </a-tag>
</td> </td>
</tr> </tr>
<tr>
<td>{{ i18n "pages.inbounds.periodicTrafficResetTitle" }}</td>
<td>
<a-tag color="blue">[[ i18n("pages.inbounds.periodicTrafficReset." + dbInbound.trafficReset) ]]</a-tag>
</td>
</tr>
</table> </table>
</template> </template>
<a-badge> <a-badge>
@ -951,6 +957,8 @@
remark: dbInbound.remark + " - Cloned", remark: dbInbound.remark + " - Cloned",
enable: dbInbound.enable, enable: dbInbound.enable,
expiryTime: dbInbound.expiryTime, expiryTime: dbInbound.expiryTime,
trafficReset: dbInbound.trafficReset,
lastTrafficResetTime: dbInbound.lastTrafficResetTime,
listen: '', listen: '',
port: RandomUtil.randomInteger(10000, 60000), port: RandomUtil.randomInteger(10000, 60000),
@ -995,6 +1003,8 @@
remark: dbInbound.remark, remark: dbInbound.remark,
enable: dbInbound.enable, enable: dbInbound.enable,
expiryTime: dbInbound.expiryTime, expiryTime: dbInbound.expiryTime,
trafficReset: dbInbound.trafficReset,
lastTrafficResetTime: dbInbound.lastTrafficResetTime,
listen: inbound.listen, listen: inbound.listen,
port: inbound.port, port: inbound.port,
@ -1018,6 +1028,8 @@
remark: dbInbound.remark, remark: dbInbound.remark,
enable: dbInbound.enable, enable: dbInbound.enable,
expiryTime: dbInbound.expiryTime, expiryTime: dbInbound.expiryTime,
trafficReset: dbInbound.trafficReset,
lastTrafficResetTime: dbInbound.lastTrafficResetTime,
listen: inbound.listen, listen: inbound.listen,
port: inbound.port, port: inbound.port,

View file

@ -0,0 +1,44 @@
package job
import (
"x-ui/logger"
"x-ui/web/service"
)
type Period string
type PeriodicTrafficResetJob struct {
inboundService service.InboundService
period Period
}
func NewPeriodicTrafficResetJob(period Period) *PeriodicTrafficResetJob {
return &PeriodicTrafficResetJob{
period: period,
}
}
func (j *PeriodicTrafficResetJob) Run() {
inbounds, err := j.inboundService.GetInboundsByTrafficReset(string(j.period))
logger.Infof("Running periodic traffic reset job for period: %s", j.period)
if err != nil {
logger.Warning("Failed to get inbounds for traffic reset:", err)
return
}
resetCount := 0
for _, inbound := range inbounds {
if err := j.inboundService.ResetAllClientTraffics(inbound.Id); err != nil {
logger.Warning("Failed to reset traffic for inbound", inbound.Id, ":", err)
continue
}
resetCount++
logger.Infof("Reset traffic for inbound %d (%s)", inbound.Id, inbound.Remark)
}
if resetCount > 0 {
logger.Infof("Periodic traffic reset completed: %d inbounds reset", resetCount)
}
}

View file

@ -41,6 +41,16 @@ func (s *InboundService) GetAllInbounds() ([]*model.Inbound, error) {
return inbounds, nil return inbounds, nil
} }
func (s *InboundService) GetInboundsByTrafficReset(period string) ([]*model.Inbound, error) {
db := database.GetDB()
var inbounds []*model.Inbound
err := db.Model(model.Inbound{}).Where("traffic_reset = ?", period).Find(&inbounds).Error
if err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
return inbounds, nil
}
func (s *InboundService) checkPortExist(listen string, port int, ignoreId int) (bool, error) { func (s *InboundService) checkPortExist(listen string, port int, ignoreId int) (bool, error) {
db := database.GetDB() db := database.GetDB()
if listen == "" || listen == "0.0.0.0" || listen == "::" || listen == "::0" { if listen == "" || listen == "0.0.0.0" || listen == "::" || listen == "::0" {
@ -409,6 +419,7 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
oldInbound.Remark = inbound.Remark oldInbound.Remark = inbound.Remark
oldInbound.Enable = inbound.Enable oldInbound.Enable = inbound.Enable
oldInbound.ExpiryTime = inbound.ExpiryTime oldInbound.ExpiryTime = inbound.ExpiryTime
oldInbound.TrafficReset = inbound.TrafficReset
oldInbound.Listen = inbound.Listen oldInbound.Listen = inbound.Listen
oldInbound.Port = inbound.Port oldInbound.Port = inbound.Port
oldInbound.Protocol = inbound.Protocol oldInbound.Protocol = inbound.Protocol
@ -698,6 +709,7 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
} }
func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId string) (bool, error) { func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId string) (bool, error) {
// TODO: check if TrafficReset field is updating
clients, err := s.GetClients(data) clients, err := s.GetClients(data)
if err != nil { if err != nil {
return false, err return false, err
@ -1684,6 +1696,7 @@ func (s *InboundService) ResetClientTrafficLimitByEmail(clientEmail string, tota
func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error { func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error {
db := database.GetDB() db := database.GetDB()
// Reset traffic stats in ClientTraffic table
result := db.Model(xray.ClientTraffic{}). result := db.Model(xray.ClientTraffic{}).
Where("email = ?", clientEmail). Where("email = ?", clientEmail).
Updates(map[string]any{"enable": true, "up": 0, "down": 0}) Updates(map[string]any{"enable": true, "up": 0, "down": 0})
@ -1692,6 +1705,7 @@ func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error {
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
@ -1759,20 +1773,39 @@ func (s *InboundService) ResetClientTraffic(id int, clientEmail string) (bool, e
func (s *InboundService) ResetAllClientTraffics(id int) error { func (s *InboundService) ResetAllClientTraffics(id int) error {
db := database.GetDB() db := database.GetDB()
now := time.Now().Unix() * 1000
whereText := "inbound_id " return db.Transaction(func(tx *gorm.DB) error {
if id == -1 { whereText := "inbound_id "
whereText += " > ?" if id == -1 {
} else { whereText += " > ?"
whereText += " = ?" } else {
} whereText += " = ?"
}
result := db.Model(xray.ClientTraffic{}). // Reset client traffics
Where(whereText, id). result := tx.Model(xray.ClientTraffic{}).
Updates(map[string]any{"enable": true, "up": 0, "down": 0}) Where(whereText, id).
Updates(map[string]any{"enable": true, "up": 0, "down": 0})
err := result.Error if result.Error != nil {
return err return result.Error
}
// Update lastTrafficResetTime for the inbound(s)
inboundWhereText := "id "
if id == -1 {
inboundWhereText += " > ?"
} else {
inboundWhereText += " = ?"
}
result = tx.Model(model.Inbound{}).
Where(inboundWhereText, id).
Update("last_traffic_reset_time", now)
return result.Error
})
} }
func (s *InboundService) ResetAllTraffics() error { func (s *InboundService) ResetAllTraffics() error {

View file

@ -244,6 +244,9 @@
"exportInbound" = "تصدير الإدخال" "exportInbound" = "تصدير الإدخال"
"import" = "استيراد" "import" = "استيراد"
"importInbound" = "استيراد إدخال" "importInbound" = "استيراد إدخال"
"periodicTrafficResetTitle" = "إعادة تعيين حركة المرور"
"periodicTrafficResetDesc" = "إعادة تعيين عداد حركة المرور تلقائيًا في فترات محددة"
"lastReset" = "آخر إعادة تعيين"
[pages.client] [pages.client]
"add" = "أضف عميل" "add" = "أضف عميل"
@ -263,6 +266,12 @@
"renew" = "تجديد تلقائي" "renew" = "تجديد تلقائي"
"renewDesc" = "تجديد تلقائي بعد انتهاء الصلاحية. (0 = تعطيل)(الوحدة: يوم)" "renewDesc" = "تجديد تلقائي بعد انتهاء الصلاحية. (0 = تعطيل)(الوحدة: يوم)"
[pages.inbounds.periodicTrafficReset]
"never" = "أبداً"
"daily" = "يومياً"
"weekly" = "أسبوعياً"
"monthly" = "شهرياً"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "تم الحصول عليه" "obtain" = "تم الحصول عليه"
"updateSuccess" = "تم التحديث بنجاح" "updateSuccess" = "تم التحديث بنجاح"

View file

@ -244,6 +244,9 @@
"exportInbound" = "Export Inbound" "exportInbound" = "Export Inbound"
"import" = "Import" "import" = "Import"
"importInbound" = "Import an Inbound" "importInbound" = "Import an Inbound"
"periodicTrafficResetTitle" = "Traffic Reset"
"periodicTrafficResetDesc" = "Automatically reset traffic counter at specified intervals"
"lastReset" = "Last Reset"
[pages.client] [pages.client]
"add" = "Add Client" "add" = "Add Client"
@ -263,6 +266,12 @@
"renew" = "Auto Renew" "renew" = "Auto Renew"
"renewDesc" = "Auto-renewal after expiration. (0 = disable)(unit: day)" "renewDesc" = "Auto-renewal after expiration. (0 = disable)(unit: day)"
[pages.inbounds.periodicTrafficReset]
"never" = "Never"
"daily" = "Daily"
"weekly" = "Weekly"
"monthly" = "Monthly"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "Obtain" "obtain" = "Obtain"
"updateSuccess" = "The update was successful." "updateSuccess" = "The update was successful."

View file

@ -244,6 +244,9 @@
"exportInbound" = "Exportación entrante" "exportInbound" = "Exportación entrante"
"import" = "Importar" "import" = "Importar"
"importInbound" = "Importar un entrante" "importInbound" = "Importar un entrante"
"periodicTrafficResetTitle" = "Reset de Tráfico"
"periodicTrafficResetDesc" = "Reiniciar automáticamente el contador de tráfico en intervalos especificados"
"lastReset" = "Último reinicio"
[pages.client] [pages.client]
"add" = "Agregar Cliente" "add" = "Agregar Cliente"
@ -263,6 +266,12 @@
"renew" = "Renovación automática" "renew" = "Renovación automática"
"renewDesc" = "Renovación automática después de la expiración. (0 = desactivar) (unidad: día)" "renewDesc" = "Renovación automática después de la expiración. (0 = desactivar) (unidad: día)"
[pages.inbounds.periodicTrafficReset]
"never" = "Nunca"
"daily" = "Diariamente"
"weekly" = "Semanalmente"
"monthly" = "Mensualmente"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "Recibir" "obtain" = "Recibir"
"updateSuccess" = "La actualización fue exitosa" "updateSuccess" = "La actualización fue exitosa"

View file

@ -244,6 +244,9 @@
"exportInbound" = "استخراج ورودی" "exportInbound" = "استخراج ورودی"
"import" = "افزودن" "import" = "افزودن"
"importInbound" = "افزودن یک ورودی" "importInbound" = "افزودن یک ورودی"
"periodicTrafficResetTitle" = "بازنشانی ترافیک"
"periodicTrafficResetDesc" = "بازنشانی خودکار شمارنده ترافیک در فواصل زمانی مشخص"
"lastReset" = "آخرین بازنشانی"
[pages.client] [pages.client]
"add" = "کاربر جدید" "add" = "کاربر جدید"
@ -261,7 +264,13 @@
"expireDays" = "مدت زمان" "expireDays" = "مدت زمان"
"days" = "(روز)" "days" = "(روز)"
"renew" = "تمدید خودکار" "renew" = "تمدید خودکار"
"renewDesc" = "(تمدید خودکار پس‌از ‌انقضا. (0 = غیرفعال)(واحد: روز" "renewDesc" = "تمدید خودکار پس‌از ‌انقضا. (0 = غیرفعال)(واحد: روز)"
[pages.inbounds.periodicTrafficReset]
"never" = "هرگز"
"daily" = "روزانه"
"weekly" = "هفتگی"
"monthly" = "ماهانه"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "فراهم‌سازی" "obtain" = "فراهم‌سازی"

View file

@ -244,6 +244,9 @@
"exportInbound" = "Ekspor Masuk" "exportInbound" = "Ekspor Masuk"
"import" = "Impor" "import" = "Impor"
"importInbound" = "Impor Masuk" "importInbound" = "Impor Masuk"
"periodicTrafficResetTitle" = "Reset Trafik Berkala"
"periodicTrafficResetDesc" = "Reset otomatis penghitung trafik pada interval tertentu"
"lastReset" = "Reset Terakhir"
[pages.client] [pages.client]
"add" = "Tambah Klien" "add" = "Tambah Klien"
@ -263,6 +266,12 @@
"renew" = "Perpanjang Otomatis" "renew" = "Perpanjang Otomatis"
"renewDesc" = "Perpanjangan otomatis setelah kedaluwarsa. (0 = nonaktif)(unit: hari)" "renewDesc" = "Perpanjangan otomatis setelah kedaluwarsa. (0 = nonaktif)(unit: hari)"
[pages.inbounds.periodicTrafficReset]
"never" = "Tidak Pernah"
"daily" = "Harian"
"weekly" = "Mingguan"
"monthly" = "Bulanan"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "Dapatkan" "obtain" = "Dapatkan"
"updateSuccess" = "Pembaruan berhasil" "updateSuccess" = "Pembaruan berhasil"

View file

@ -244,6 +244,9 @@
"exportInbound" = "インバウンドルールをエクスポート" "exportInbound" = "インバウンドルールをエクスポート"
"import" = "インポート" "import" = "インポート"
"importInbound" = "インバウンドルールをインポート" "importInbound" = "インバウンドルールをインポート"
"periodicTrafficResetTitle" = "トラフィックリセット"
"periodicTrafficResetDesc" = "指定された間隔でトラフィックカウンタを自動的にリセット"
"lastReset" = "最後のリセット"
[pages.client] [pages.client]
"add" = "クライアント追加" "add" = "クライアント追加"
@ -263,6 +266,12 @@
"renew" = "自動更新" "renew" = "自動更新"
"renewDesc" = "期限が切れた後に自動更新。0 = 無効)(単位:日)" "renewDesc" = "期限が切れた後に自動更新。0 = 無効)(単位:日)"
[pages.inbounds.periodicTrafficReset]
"never" = "なし"
"daily" = "毎日"
"weekly" = "毎週"
"monthly" = "毎月"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "取得" "obtain" = "取得"
"updateSuccess" = "更新が成功しました" "updateSuccess" = "更新が成功しました"

View file

@ -244,6 +244,9 @@
"exportInbound" = "Exportar Inbound" "exportInbound" = "Exportar Inbound"
"import" = "Importar" "import" = "Importar"
"importInbound" = "Importar um Inbound" "importInbound" = "Importar um Inbound"
"periodicTrafficResetTitle" = "Reset de Tráfego"
"periodicTrafficResetDesc" = "Reinicia automaticamente o contador de tráfego em intervalos especificados"
"lastReset" = "Último Reset"
[pages.client] [pages.client]
"add" = "Adicionar Cliente" "add" = "Adicionar Cliente"
@ -263,6 +266,12 @@
"renew" = "Renovação Automática" "renew" = "Renovação Automática"
"renewDesc" = "Renovação automática após expiração. (0 = desativado)(unidade: dia)" "renewDesc" = "Renovação automática após expiração. (0 = desativado)(unidade: dia)"
[pages.inbounds.periodicTrafficReset]
"never" = "Nunca"
"daily" = "Diariamente"
"weekly" = "Semanalmente"
"monthly" = "Mensalmente"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "Obter" "obtain" = "Obter"
"updateSuccess" = "A atualização foi bem-sucedida" "updateSuccess" = "A atualização foi bem-sucedida"

View file

@ -244,6 +244,9 @@
"exportInbound" = "Экспорт инбаундов" "exportInbound" = "Экспорт инбаундов"
"import" = "Импортировать" "import" = "Импортировать"
"importInbound" = "Импорт инбаундов" "importInbound" = "Импорт инбаундов"
"periodicTrafficResetTitle" = "Сброс трафика"
"periodicTrafficResetDesc" = "Автоматический сброс счетчика трафика через указанные интервалы"
"lastReset" = "Последний сброс"
[pages.client] [pages.client]
"add" = "Создать клиента" "add" = "Создать клиента"
@ -263,6 +266,12 @@
"renew" = "Автопродление" "renew" = "Автопродление"
"renewDesc" = "Автопродление после истечения срока действия. (0 = отключить)(единица: день)" "renewDesc" = "Автопродление после истечения срока действия. (0 = отключить)(единица: день)"
[pages.inbounds.periodicTrafficReset]
"never" = "Никогда"
"daily" = "Ежедневно"
"weekly" = "Еженедельно"
"monthly" = "Ежемесячно"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "Получить" "obtain" = "Получить"
"updateSuccess" = "Обновление прошло успешно" "updateSuccess" = "Обновление прошло успешно"

View file

@ -244,6 +244,9 @@
"exportInbound" = "Geleni Dışa Aktar" "exportInbound" = "Geleni Dışa Aktar"
"import" = "İçe Aktar" "import" = "İçe Aktar"
"importInbound" = "Bir Gelen İçe Aktar" "importInbound" = "Bir Gelen İçe Aktar"
"periodicTrafficResetTitle" = "Trafik Sıfırlama"
"periodicTrafficResetDesc" = "Belirtilen aralıklarla trafik sayacını otomatik olarak sıfırla"
"lastReset" = "Son Sıfırlama"
[pages.client] [pages.client]
"add" = "Müşteri Ekle" "add" = "Müşteri Ekle"
@ -263,6 +266,12 @@
"renew" = "Otomatik Yenile" "renew" = "Otomatik Yenile"
"renewDesc" = "Süresi dolduktan sonra otomatik yenileme. (0 = devre dışı)(birim: gün)" "renewDesc" = "Süresi dolduktan sonra otomatik yenileme. (0 = devre dışı)(birim: gün)"
[pages.inbounds.periodicTrafficReset]
"never" = "Asla"
"daily" = "Günlük"
"weekly" = "Haftalık"
"monthly" = "Aylık"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "Elde Et" "obtain" = "Elde Et"
"updateSuccess" = "Güncelleme başarılı oldu" "updateSuccess" = "Güncelleme başarılı oldu"

View file

@ -244,6 +244,9 @@
"exportInbound" = "Експортувати вхідні" "exportInbound" = "Експортувати вхідні"
"import" = "Імпорт" "import" = "Імпорт"
"importInbound" = "Імпортувати вхідний" "importInbound" = "Імпортувати вхідний"
"periodicTrafficResetTitle" = "Скидання трафіку"
"periodicTrafficResetDesc" = "Автоматично скидати лічильник трафіку через певні проміжки часу"
"lastReset" = "Останнє скидання"
[pages.client] [pages.client]
"add" = "Додати клієнта" "add" = "Додати клієнта"
@ -263,6 +266,12 @@
"renew" = "Автоматичне оновлення" "renew" = "Автоматичне оновлення"
"renewDesc" = "Автоматичне поновлення після закінчення терміну дії. (0 = вимкнено)(одиниця: день)" "renewDesc" = "Автоматичне поновлення після закінчення терміну дії. (0 = вимкнено)(одиниця: день)"
[pages.inbounds.periodicTrafficReset]
"never" = "Ніколи"
"daily" = "Щодня"
"weekly" = "Щотижня"
"monthly" = "Щомісяця"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "Отримати" "obtain" = "Отримати"
"updateSuccess" = "Оновлення пройшло успішно" "updateSuccess" = "Оновлення пройшло успішно"

View file

@ -244,6 +244,9 @@
"exportInbound" = "Xuất nhập khẩu" "exportInbound" = "Xuất nhập khẩu"
"import" = "Nhập" "import" = "Nhập"
"importInbound" = "Nhập inbound" "importInbound" = "Nhập inbound"
"periodicTrafficResetTitle" = "Đặt lại lưu lượng"
"periodicTrafficResetDesc" = "Tự động đặt lại bộ đếm lưu lượng theo khoảng thời gian xác định"
"lastReset" = "Đặt lại lần cuối"
[pages.client] [pages.client]
"add" = "Thêm người dùng" "add" = "Thêm người dùng"
@ -263,6 +266,12 @@
"renew" = "Tự động gia hạn" "renew" = "Tự động gia hạn"
"renewDesc" = "Tự động gia hạn sau khi hết hạn. (0 = tắt)(đơn vị: ngày)" "renewDesc" = "Tự động gia hạn sau khi hết hạn. (0 = tắt)(đơn vị: ngày)"
[pages.inbounds.periodicTrafficReset]
"never" = "Không bao giờ"
"daily" = "Hàng ngày"
"weekly" = "Hàng tuần"
"monthly" = "Hàng tháng"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "Nhận" "obtain" = "Nhận"
"updateSuccess" = "Cập nhật thành công" "updateSuccess" = "Cập nhật thành công"

View file

@ -244,6 +244,9 @@
"exportInbound" = "导出入站规则" "exportInbound" = "导出入站规则"
"import"="导入" "import"="导入"
"importInbound" = "导入入站规则" "importInbound" = "导入入站规则"
"periodicTrafficResetTitle" = "流量重置"
"periodicTrafficResetDesc" = "按指定间隔自动重置流量计数器"
"lastReset" = "上次重置"
[pages.client] [pages.client]
"add" = "添加客户端" "add" = "添加客户端"
@ -263,6 +266,12 @@
"renew" = "自动续订" "renew" = "自动续订"
"renewDesc" = "到期后自动续订。(0 = 禁用)(单位: 天)" "renewDesc" = "到期后自动续订。(0 = 禁用)(单位: 天)"
[pages.inbounds.periodicTrafficReset]
"never" = "从不"
"daily" = "每日"
"weekly" = "每周"
"monthly" = "每月"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "获取" "obtain" = "获取"
"updateSuccess" = "更新成功" "updateSuccess" = "更新成功"

View file

@ -244,6 +244,9 @@
"exportInbound" = "匯出入站規則" "exportInbound" = "匯出入站規則"
"import"="匯入" "import"="匯入"
"importInbound" = "匯入入站規則" "importInbound" = "匯入入站規則"
"periodicTrafficResetTitle" = "流量重置"
"periodicTrafficResetDesc" = "按指定間隔自動重置流量計數器"
"lastReset" = "上次重置"
[pages.client] [pages.client]
"add" = "新增客戶端" "add" = "新增客戶端"
@ -263,6 +266,12 @@
"renew" = "自動續訂" "renew" = "自動續訂"
"renewDesc" = "到期後自動續訂。(0 = 禁用)(單位: 天)" "renewDesc" = "到期後自動續訂。(0 = 禁用)(單位: 天)"
[pages.inbounds.periodicTrafficReset]
"never" = "從不"
"daily" = "每日"
"weekly" = "每週"
"monthly" = "每月"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "獲取" "obtain" = "獲取"
"updateSuccess" = "更新成功" "updateSuccess" = "更新成功"

View file

@ -289,6 +289,19 @@ func (s *Server) startTask() {
// check client ips from log file every day // check client ips from log file every day
s.cron.AddJob("@daily", job.NewClearLogsJob()) s.cron.AddJob("@daily", job.NewClearLogsJob())
// Periodic traffic resets
logger.Info("Scheduling periodic traffic reset jobs")
{
// Inbound traffic reset jobs
// Run once a day, midnight
s.cron.AddJob("@daily", job.NewPeriodicTrafficResetJob("daily"))
// Run once a week, midnight between Sat/Sun
s.cron.AddJob("@weekly", job.NewPeriodicTrafficResetJob("weekly"))
// Run once a month, midnight, first of month
s.cron.AddJob("@monthly", job.NewPeriodicTrafficResetJob("monthly"))
}
// Make a traffic condition every day, 8:30 // Make a traffic condition every day, 8:30
var entry cron.EntryID var entry cron.EntryID
isTgbotenabled, err := s.settingService.GetTgbotEnabled() isTgbotenabled, err := s.settingService.GetTgbotEnabled()