mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-10-27 10:30:08 +00:00
Compare commits
3 commits
96f12061e6
...
7a57cdbc98
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a57cdbc98 | ||
|
|
76afff2a6f | ||
|
|
9623e87511 |
24 changed files with 279 additions and 31 deletions
|
|
@ -34,8 +34,10 @@ type Inbound struct {
|
||||||
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"`
|
||||||
|
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"`
|
ClientStats []xray.ClientTraffic `gorm:"foreignKey:InboundId;references:Id" json:"clientStats" form:"clientStats"`
|
||||||
|
|
||||||
// config part
|
// config part
|
||||||
|
|
|
||||||
2
web/assets/css/custom.min.css
vendored
2
web/assets/css/custom.min.css
vendored
File diff suppressed because one or more lines are too long
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
{{ template "page/head_end" .}}
|
{{ template "page/head_end" .}}
|
||||||
|
|
||||||
{{ template "page/body_start" .}}
|
{{ template "page/body_start" .}}
|
||||||
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme + ' index-page'">
|
||||||
<a-sidebar></a-sidebar>
|
<a-sidebar></a-sidebar>
|
||||||
<a-layout id="content-layout">
|
<a-layout id="content-layout">
|
||||||
<a-layout-content>
|
<a-layout-content>
|
||||||
|
|
@ -88,7 +88,7 @@
|
||||||
</template>
|
</template>
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<template v-if="status.xray.state != 'error'">
|
<template v-if="status.xray.state != 'error'">
|
||||||
<a-badge status="processing" class="running-animation" :text="status.xray.stateMsg" :color="status.xray.color"/>
|
<a-badge status="processing" :class="({ green: 'xray-running-animation', orange: 'xray-stop-animation' }[status.xray.color]) || 'xray-processing-animation'" :text="status.xray.stateMsg" :color="status.xray.color"/>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
|
|
@ -105,7 +105,7 @@
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<span class="max-w-400" v-for="line in status.xray.errorMsg.split('\n')">[[ line ]]</span>
|
<span class="max-w-400" v-for="line in status.xray.errorMsg.split('\n')">[[ line ]]</span>
|
||||||
</template>
|
</template>
|
||||||
<a-badge :text="status.xray.stateMsg" :color="status.xray.color"/>
|
<a-badge :text="status.xray.stateMsg" :color="status.xray.color" :class="status.xray.color === 'red' ? 'xray-error-animation' : ''"/>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -102,14 +102,15 @@
|
||||||
<a-tag :color="inbound.stream.security == 'none' ? 'red' : 'green'">[[ inbound.stream.security ]]</a-tag>
|
<a-tag :color="inbound.stream.security == 'none' ? 'red' : 'green'">[[ inbound.stream.security ]]</a-tag>
|
||||||
<br />
|
<br />
|
||||||
<td>Authentication</td>
|
<td>Authentication</td>
|
||||||
<a-tag :color="inbound.settings.selectedAuth ? 'green' : 'red'">[[ inbound.settings.selectedAuth ? inbound.settings.selectedAuth : '' ]]</a-tag>
|
<a-tag v-if="inbound.settings.selectedAuth" color="green">[[ inbound.settings.selectedAuth ? inbound.settings.selectedAuth : '' ]]</a-tag>
|
||||||
|
<a-tag v-else color="red">{{ i18n "none" }}</a-tag>
|
||||||
<br />
|
<br />
|
||||||
{{ i18n "encryption" }}
|
{{ i18n "encryption" }}
|
||||||
<a-tag :color="inbound.settings.encryption ? 'green' : 'red'">[[ inbound.settings.encryption ? inbound.settings.encryption : '' ]]</a-tag>
|
<a-tag :color="inbound.settings.encryption ? 'green' : 'red'">[[ inbound.settings.encryption ? inbound.settings.encryption : '' ]]</a-tag>
|
||||||
<br />
|
<br />
|
||||||
<template v-if="inbound.stream.security != 'none'">
|
<template v-if="inbound.stream.security != 'none'">
|
||||||
{{ i18n "domainName" }}
|
{{ i18n "domainName" }}
|
||||||
<a-tag v-if="inbound.serverName" :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag>
|
<a-tag v-if="inbound.serverName" color="green">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag>
|
||||||
<a-tag v-else color="orange">{{ i18n "none" }}</a-tag>
|
<a-tag v-else color="orange">{{ i18n "none" }}</a-tag>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
{{ template "page/head_end" .}}
|
{{ template "page/head_end" .}}
|
||||||
|
|
||||||
{{ template "page/body_start" .}}
|
{{ template "page/body_start" .}}
|
||||||
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme + ' subscription-page'">
|
||||||
<a-layout-content class="p-2">
|
<a-layout-content class="p-2">
|
||||||
<a-row type="flex" justify="center" class="mt-2">
|
<a-row type="flex" justify="center" class="mt-2">
|
||||||
<a-col :xs="24" :sm="22" :md="18" :lg="14" :xl="12">
|
<a-col :xs="24" :sm="22" :md="18" :lg="14" :xl="12">
|
||||||
|
|
@ -200,7 +200,7 @@
|
||||||
style="text-align:center;">
|
style="text-align:center;">
|
||||||
<!-- Android dropdown -->
|
<!-- Android dropdown -->
|
||||||
<a-dropdown :trigger="['click']">
|
<a-dropdown :trigger="['click']">
|
||||||
<a-button :block="isMobile"
|
<a-button icon="android" :block="isMobile"
|
||||||
:style="{ marginTop: isMobile ? '6px' : 0 }"
|
:style="{ marginTop: isMobile ? '6px' : 0 }"
|
||||||
size="large" type="primary">
|
size="large" type="primary">
|
||||||
Android <a-icon type="down" />
|
Android <a-icon type="down" />
|
||||||
|
|
@ -225,7 +225,7 @@
|
||||||
style="text-align:center;">
|
style="text-align:center;">
|
||||||
<!-- iOS dropdown -->
|
<!-- iOS dropdown -->
|
||||||
<a-dropdown :trigger="['click']">
|
<a-dropdown :trigger="['click']">
|
||||||
<a-button :block="isMobile"
|
<a-button icon="apple" :block="isMobile"
|
||||||
:style="{ marginTop: isMobile ? '6px' : 0 }"
|
:style="{ marginTop: isMobile ? '6px' : 0 }"
|
||||||
size="large" type="primary">
|
size="large" type="primary">
|
||||||
iOS <a-icon type="down" />
|
iOS <a-icon type="down" />
|
||||||
|
|
|
||||||
44
web/job/periodic_traffic_reset_job.go
Normal file
44
web/job/periodic_traffic_reset_job.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -44,6 +44,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" {
|
||||||
|
|
@ -412,6 +422,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
|
||||||
|
|
@ -711,6 +722,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
|
||||||
|
|
@ -1703,6 +1715,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})
|
||||||
|
|
@ -1711,6 +1724,7 @@ func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1778,7 +1792,9 @@ 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
|
||||||
|
|
||||||
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
whereText := "inbound_id "
|
whereText := "inbound_id "
|
||||||
if id == -1 {
|
if id == -1 {
|
||||||
whereText += " > ?"
|
whereText += " > ?"
|
||||||
|
|
@ -1786,12 +1802,29 @@ func (s *InboundService) ResetAllClientTraffics(id int) error {
|
||||||
whereText += " = ?"
|
whereText += " = ?"
|
||||||
}
|
}
|
||||||
|
|
||||||
result := db.Model(xray.ClientTraffic{}).
|
// Reset client traffics
|
||||||
|
result := tx.Model(xray.ClientTraffic{}).
|
||||||
Where(whereText, id).
|
Where(whereText, id).
|
||||||
Updates(map[string]any{"enable": true, "up": 0, "down": 0})
|
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 {
|
||||||
|
|
|
||||||
|
|
@ -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" = "تم التحديث بنجاح"
|
||||||
|
|
|
||||||
|
|
@ -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."
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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" = "فراهمسازی"
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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" = "更新が成功しました"
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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" = "Обновление прошло успешно"
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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" = "Оновлення пройшло успішно"
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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" = "更新成功"
|
||||||
|
|
|
||||||
|
|
@ -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" = "更新成功"
|
||||||
|
|
|
||||||
13
web/web.go
13
web/web.go
|
|
@ -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()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue