mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-06 13:14:11 +00:00
refactor(traffic): drop all-time traffic tracking
Removes the AllTime field from Inbound and ClientTraffic and migrates existing DBs by dropping the all_time columns on startup. The counter duplicated up+down without adding signal, and the per-event accumulator ran on every traffic write. Frontend: drop the All-time column from the inbound list and the client-row table, the All-time row from the client info modal, and the All-Time Total Usage tile from the inbounds summary card. The allTimeTraffic/allTimeTrafficUsage i18n keys are removed across every locale. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
8a4101a96b
commit
f315ed269e
22 changed files with 24 additions and 130 deletions
|
|
@ -49,7 +49,6 @@ type Inbound struct {
|
||||||
Up int64 `json:"up" form:"up"` // Upload traffic in bytes
|
Up int64 `json:"up" form:"up"` // Upload traffic in bytes
|
||||||
Down int64 `json:"down" form:"down"` // Download traffic in bytes
|
Down int64 `json:"down" form:"down"` // Download traffic in bytes
|
||||||
Total int64 `json:"total" form:"total"` // Total traffic limit in bytes
|
Total int64 `json:"total" form:"total"` // Total traffic limit in bytes
|
||||||
AllTime int64 `json:"allTime" form:"allTime" gorm:"default:0"` // All-time traffic usage
|
|
||||||
Remark string `json:"remark" form:"remark"` // Human-readable remark
|
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
|
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
|
ExpiryTime int64 `json:"expiryTime" form:"expiryTime"` // Expiration timestamp
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ export class DBInbound {
|
||||||
this.up = 0;
|
this.up = 0;
|
||||||
this.down = 0;
|
this.down = 0;
|
||||||
this.total = 0;
|
this.total = 0;
|
||||||
this.allTime = 0;
|
|
||||||
this.remark = "";
|
this.remark = "";
|
||||||
this.enable = true;
|
this.enable = true;
|
||||||
this.expiryTime = 0;
|
this.expiryTime = 0;
|
||||||
|
|
|
||||||
|
|
@ -203,13 +203,6 @@ function close() {
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>{{ t('pages.inbounds.allTimeTraffic') || 'All-time' }}</td>
|
|
||||||
<td>
|
|
||||||
<a-tag>{{ SizeFormatter.sizeFormat(traffic?.allTime || used) }}</a-tag>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ t('pages.inbounds.expireDate') || 'Expiry' }}</td>
|
<td>{{ t('pages.inbounds.expireDate') || 'Expiry' }}</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
|
||||||
|
|
@ -86,14 +86,6 @@ function getRem(email) {
|
||||||
const r = s.total - s.up - s.down;
|
const r = s.total - s.up - s.down;
|
||||||
return r > 0 ? r : 0;
|
return r > 0 ? r : 0;
|
||||||
}
|
}
|
||||||
function getAllTime(email) {
|
|
||||||
const s = statsFor(email);
|
|
||||||
if (!s) return 0;
|
|
||||||
// allTime is the cumulative-historical counter; never let it dip
|
|
||||||
// below up+down (manual edits / partial migrations can push it under).
|
|
||||||
const current = (s.up || 0) + (s.down || 0);
|
|
||||||
return s.allTime > current ? s.allTime : current;
|
|
||||||
}
|
|
||||||
function isClientDepleted(email) {
|
function isClientDepleted(email) {
|
||||||
const s = statsFor(email);
|
const s = statsFor(email);
|
||||||
if (!s) return false;
|
if (!s) return false;
|
||||||
|
|
@ -286,7 +278,6 @@ function confirmBulkDelete() {
|
||||||
<div class="cell cell-client">{{ t('pages.inbounds.client') }}</div>
|
<div class="cell cell-client">{{ t('pages.inbounds.client') }}</div>
|
||||||
<div class="cell cell-traffic">{{ t('pages.inbounds.traffic') }}</div>
|
<div class="cell cell-traffic">{{ t('pages.inbounds.traffic') }}</div>
|
||||||
<div class="cell cell-remained">{{ t('remained') }}</div>
|
<div class="cell cell-remained">{{ t('remained') }}</div>
|
||||||
<div class="cell cell-alltime">{{ t('pages.inbounds.allTimeTraffic') }}</div>
|
|
||||||
<div class="cell cell-expiry">{{ t('pages.inbounds.expireDate') }}</div>
|
<div class="cell cell-expiry">{{ t('pages.inbounds.expireDate') }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -388,10 +379,6 @@ function confirmBulkDelete() {
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cell cell-alltime">
|
|
||||||
<a-tag>{{ SizeFormatter.sizeFormat(getAllTime(client.email)) }}</a-tag>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cell cell-expiry">
|
<div class="cell cell-expiry">
|
||||||
<template v-if="client.expiryTime !== 0 && client.reset > 0">
|
<template v-if="client.expiryTime !== 0 && client.reset > 0">
|
||||||
<a-popover>
|
<a-popover>
|
||||||
|
|
@ -499,10 +486,6 @@ function confirmBulkDelete() {
|
||||||
{{ SizeFormatter.sizeFormat(getRem(statsClient.email)) }}
|
{{ SizeFormatter.sizeFormat(getRem(statsClient.email)) }}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-row">
|
|
||||||
<span class="stat-label">{{ t('pages.inbounds.allTimeTraffic') }}</span>
|
|
||||||
<a-tag>{{ SizeFormatter.sizeFormat(getAllTime(statsClient.email)) }}</a-tag>
|
|
||||||
</div>
|
|
||||||
<div class="stat-row">
|
<div class="stat-row">
|
||||||
<span class="stat-label">{{ t('online') }}</span>
|
<span class="stat-label">{{ t('online') }}</span>
|
||||||
<a-tag v-if="statsClient.enable && isClientOnline(statsClient.email)" color="green">{{ t('online')
|
<a-tag v-if="statsClient.enable && isClientOnline(statsClient.email)" color="green">{{ t('online')
|
||||||
|
|
@ -571,8 +554,6 @@ function confirmBulkDelete() {
|
||||||
minmax(160px, 2fr)
|
minmax(160px, 2fr)
|
||||||
/* traffic */
|
/* traffic */
|
||||||
130px
|
130px
|
||||||
/* all-time */
|
|
||||||
130px
|
|
||||||
/* remained */
|
/* remained */
|
||||||
140px;
|
140px;
|
||||||
/* expiry */
|
/* expiry */
|
||||||
|
|
@ -597,8 +578,6 @@ function confirmBulkDelete() {
|
||||||
minmax(160px, 2fr)
|
minmax(160px, 2fr)
|
||||||
/* traffic */
|
/* traffic */
|
||||||
130px
|
130px
|
||||||
/* all-time */
|
|
||||||
130px
|
|
||||||
/* remained */
|
/* remained */
|
||||||
140px;
|
140px;
|
||||||
/* expiry */
|
/* expiry */
|
||||||
|
|
@ -628,7 +607,6 @@ function confirmBulkDelete() {
|
||||||
.cell-actions,
|
.cell-actions,
|
||||||
.cell-enable,
|
.cell-enable,
|
||||||
.cell-online,
|
.cell-online,
|
||||||
.cell-alltime,
|
|
||||||
.cell-remained {
|
.cell-remained {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,6 @@ const sortFns = {
|
||||||
port: (a, b) => a.port - b.port,
|
port: (a, b) => a.port - b.port,
|
||||||
protocol: (a, b) => a.protocol.localeCompare(b.protocol),
|
protocol: (a, b) => a.protocol.localeCompare(b.protocol),
|
||||||
traffic: (a, b) => (a.up + a.down) - (b.up + b.down),
|
traffic: (a, b) => (a.up + a.down) - (b.up + b.down),
|
||||||
allTimeInbound: (a, b) => (a.allTime || 0) - (b.allTime || 0),
|
|
||||||
expiryTime: (a, b) => (a.expiryTime || Infinity) - (b.expiryTime || Infinity),
|
expiryTime: (a, b) => (a.expiryTime || Infinity) - (b.expiryTime || Infinity),
|
||||||
node: (a, b) => {
|
node: (a, b) => {
|
||||||
const nameA = props.nodesById.get(a.nodeId)?.name ?? (a.nodeId == null ? '\uffff' : `node #${a.nodeId}`);
|
const nameA = props.nodesById.get(a.nodeId)?.name ?? (a.nodeId == null ? '\uffff' : `node #${a.nodeId}`);
|
||||||
|
|
@ -244,7 +243,6 @@ const desktopColumns = computed(() => {
|
||||||
sortableCol({ title: t('pages.inbounds.protocol'), key: 'protocol', align: 'left', width: 130 }, 'protocol'),
|
sortableCol({ title: t('pages.inbounds.protocol'), key: 'protocol', align: 'left', width: 130 }, 'protocol'),
|
||||||
sortableCol({ title: t('clients'), key: 'clients', align: 'left', width: 50 }, 'clients'),
|
sortableCol({ title: t('clients'), key: 'clients', align: 'left', width: 50 }, 'clients'),
|
||||||
sortableCol({ title: t('pages.inbounds.traffic'), key: 'traffic', align: 'center', width: 90 }, 'traffic'),
|
sortableCol({ title: t('pages.inbounds.traffic'), key: 'traffic', align: 'center', width: 90 }, 'traffic'),
|
||||||
sortableCol({ title: t('pages.inbounds.allTimeTraffic'), key: 'allTimeInbound', align: 'center', width: 95 }, 'allTimeInbound'),
|
|
||||||
sortableCol({ title: t('pages.inbounds.expireDate'), key: 'expiryTime', align: 'center', width: 40 }, 'expiryTime'),
|
sortableCol({ title: t('pages.inbounds.expireDate'), key: 'expiryTime', align: 'center', width: 40 }, 'expiryTime'),
|
||||||
);
|
);
|
||||||
return cols;
|
return cols;
|
||||||
|
|
@ -515,10 +513,6 @@ function showQrCodeMenu(dbInbound) {
|
||||||
<InfinityIcon v-else />
|
<InfinityIcon v-else />
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-row">
|
|
||||||
<span class="stat-label">{{ t('pages.inbounds.allTimeTraffic') }}</span>
|
|
||||||
<a-tag>{{ SizeFormatter.sizeFormat(statsRecord.allTime || 0) }}</a-tag>
|
|
||||||
</div>
|
|
||||||
<div v-if="clientCount[statsRecord.id]" class="stat-row">
|
<div v-if="clientCount[statsRecord.id]" class="stat-row">
|
||||||
<span class="stat-label">{{ t('clients') }}</span>
|
<span class="stat-label">{{ t('clients') }}</span>
|
||||||
<a-tag color="green" class="client-count-tag">{{ clientCount[statsRecord.id].clients }}</a-tag>
|
<a-tag color="green" class="client-count-tag">{{ clientCount[statsRecord.id].clients }}</a-tag>
|
||||||
|
|
@ -735,11 +729,6 @@ function showQrCodeMenu(dbInbound) {
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- ============== All-time inbound traffic ============== -->
|
|
||||||
<template v-else-if="column.key === 'allTimeInbound'">
|
|
||||||
<a-tag>{{ SizeFormatter.sizeFormat(record.allTime || 0) }}</a-tag>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- ============== Expiry ============== -->
|
<!-- ============== Expiry ============== -->
|
||||||
<template v-else-if="column.key === 'expiryTime'">
|
<template v-else-if="column.key === 'expiryTime'">
|
||||||
<a-popover v-if="record.expiryTime > 0">
|
<a-popover v-if="record.expiryTime > 0">
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import { Modal, message } from 'ant-design-vue';
|
||||||
import {
|
import {
|
||||||
SwapOutlined,
|
SwapOutlined,
|
||||||
PieChartOutlined,
|
PieChartOutlined,
|
||||||
HistoryOutlined,
|
|
||||||
BarsOutlined,
|
BarsOutlined,
|
||||||
TeamOutlined,
|
TeamOutlined,
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
|
|
@ -582,14 +581,6 @@ function onRowAction({ key, dbInbound }) {
|
||||||
</template>
|
</template>
|
||||||
</CustomStatistic>
|
</CustomStatistic>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :xs="12" :sm="12" :md="5">
|
|
||||||
<CustomStatistic :title="t('pages.inbounds.allTimeTrafficUsage')"
|
|
||||||
:value="SizeFormatter.sizeFormat(totals.allTime)">
|
|
||||||
<template #prefix>
|
|
||||||
<HistoryOutlined />
|
|
||||||
</template>
|
|
||||||
</CustomStatistic>
|
|
||||||
</a-col>
|
|
||||||
<a-col :xs="12" :sm="12" :md="5">
|
<a-col :xs="12" :sm="12" :md="5">
|
||||||
<CustomStatistic :title="t('pages.inbounds.inboundCount')" :value="String(dbInbounds.length)">
|
<CustomStatistic :title="t('pages.inbounds.inboundCount')" :value="String(dbInbounds.length)">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
|
|
|
||||||
|
|
@ -195,7 +195,6 @@ export function useInbounds() {
|
||||||
if (!upd) continue;
|
if (!upd) continue;
|
||||||
if (typeof upd.up === 'number') ib.up = upd.up;
|
if (typeof upd.up === 'number') ib.up = upd.up;
|
||||||
if (typeof upd.down === 'number') ib.down = upd.down;
|
if (typeof upd.down === 'number') ib.down = upd.down;
|
||||||
if (typeof upd.allTime === 'number') ib.allTime = upd.allTime;
|
|
||||||
if (typeof upd.total === 'number') ib.total = upd.total;
|
if (typeof upd.total === 'number') ib.total = upd.total;
|
||||||
if (typeof upd.enable === 'boolean') ib.enable = upd.enable;
|
if (typeof upd.enable === 'boolean') ib.enable = upd.enable;
|
||||||
touched = true;
|
touched = true;
|
||||||
|
|
@ -216,7 +215,6 @@ export function useInbounds() {
|
||||||
if (typeof upd.up === 'number') stat.up = upd.up;
|
if (typeof upd.up === 'number') stat.up = upd.up;
|
||||||
if (typeof upd.down === 'number') stat.down = upd.down;
|
if (typeof upd.down === 'number') stat.down = upd.down;
|
||||||
if (typeof upd.total === 'number') stat.total = upd.total;
|
if (typeof upd.total === 'number') stat.total = upd.total;
|
||||||
if (typeof upd.allTime === 'number') stat.allTime = upd.allTime;
|
|
||||||
if (typeof upd.expiryTime === 'number') stat.expiryTime = upd.expiryTime;
|
if (typeof upd.expiryTime === 'number') stat.expiryTime = upd.expiryTime;
|
||||||
if (typeof upd.enable === 'boolean') stat.enable = upd.enable;
|
if (typeof upd.enable === 'boolean') stat.enable = upd.enable;
|
||||||
touched = true;
|
touched = true;
|
||||||
|
|
@ -283,12 +281,9 @@ export function useInbounds() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aggregate totals shown in the dashboard summary card. allTime falls
|
|
||||||
// back to up+down when the per-inbound counter isn't populated yet.
|
|
||||||
const totals = computed(() => {
|
const totals = computed(() => {
|
||||||
let up = 0;
|
let up = 0;
|
||||||
let down = 0;
|
let down = 0;
|
||||||
let allTime = 0;
|
|
||||||
let clients = 0;
|
let clients = 0;
|
||||||
const deactive = [];
|
const deactive = [];
|
||||||
const depleted = [];
|
const depleted = [];
|
||||||
|
|
@ -297,7 +292,6 @@ export function useInbounds() {
|
||||||
for (const ib of dbInbounds.value) {
|
for (const ib of dbInbounds.value) {
|
||||||
up += ib.up || 0;
|
up += ib.up || 0;
|
||||||
down += ib.down || 0;
|
down += ib.down || 0;
|
||||||
allTime += ib.allTime || (ib.up + ib.down) || 0;
|
|
||||||
const c = clientCount.value[ib.id];
|
const c = clientCount.value[ib.id];
|
||||||
if (c) {
|
if (c) {
|
||||||
clients += c.clients;
|
clients += c.clients;
|
||||||
|
|
@ -307,7 +301,7 @@ export function useInbounds() {
|
||||||
online.push(...c.online);
|
online.push(...c.online);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { up, down, allTime, clients, deactive, depleted, expiring, online };
|
return { up, down, clients, deactive, depleted, expiring, online };
|
||||||
});
|
});
|
||||||
|
|
||||||
// ObjectUtil reference is wired at module load — keeping a no-op import
|
// ObjectUtil reference is wired at module load — keeping a no-op import
|
||||||
|
|
|
||||||
|
|
@ -1700,7 +1700,6 @@ func (s *InboundService) setRemoteTrafficLocked(nodeID int, snap *runtime.Traffi
|
||||||
ExpiryTime: snapIb.ExpiryTime,
|
ExpiryTime: snapIb.ExpiryTime,
|
||||||
Up: snapIb.Up,
|
Up: snapIb.Up,
|
||||||
Down: snapIb.Down,
|
Down: snapIb.Down,
|
||||||
AllTime: snapIb.AllTime,
|
|
||||||
}
|
}
|
||||||
if err := tx.Create(&newIb).Error; err != nil {
|
if err := tx.Create(&newIb).Error; err != nil {
|
||||||
logger.Warning("setRemoteTraffic: create central inbound for tag", snapIb.Tag, "failed:", err)
|
logger.Warning("setRemoteTraffic: create central inbound for tag", snapIb.Tag, "failed:", err)
|
||||||
|
|
@ -1730,9 +1729,6 @@ func (s *InboundService) setRemoteTrafficLocked(nodeID int, snap *runtime.Traffi
|
||||||
updates["up"] = snapIb.Up
|
updates["up"] = snapIb.Up
|
||||||
updates["down"] = snapIb.Down
|
updates["down"] = snapIb.Down
|
||||||
}
|
}
|
||||||
if snapIb.AllTime > c.AllTime {
|
|
||||||
updates["all_time"] = snapIb.AllTime
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Settings != snapIb.Settings ||
|
if c.Settings != snapIb.Settings ||
|
||||||
c.Remark != snapIb.Remark ||
|
c.Remark != snapIb.Remark ||
|
||||||
|
|
@ -1792,7 +1788,6 @@ func (s *InboundService) setRemoteTrafficLocked(nodeID int, snap *runtime.Traffi
|
||||||
Reset: cs.Reset,
|
Reset: cs.Reset,
|
||||||
Up: cs.Up,
|
Up: cs.Up,
|
||||||
Down: cs.Down,
|
Down: cs.Down,
|
||||||
AllTime: cs.AllTime,
|
|
||||||
LastOnline: cs.LastOnline,
|
LastOnline: cs.LastOnline,
|
||||||
}).Error; err != nil {
|
}).Error; err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
|
@ -1808,17 +1803,12 @@ func (s *InboundService) setRemoteTrafficLocked(nodeID int, snap *runtime.Traffi
|
||||||
structuralChange = true
|
structuralChange = true
|
||||||
}
|
}
|
||||||
|
|
||||||
allTime := existing.AllTime
|
|
||||||
if cs.AllTime > allTime {
|
|
||||||
allTime = cs.AllTime
|
|
||||||
}
|
|
||||||
|
|
||||||
if inGrace && cs.Up+cs.Down > 0 {
|
if inGrace && cs.Up+cs.Down > 0 {
|
||||||
if err := tx.Exec(
|
if err := tx.Exec(
|
||||||
`UPDATE client_traffics
|
`UPDATE client_traffics
|
||||||
SET enable = ?, total = ?, expiry_time = ?, reset = ?, all_time = ?
|
SET enable = ?, total = ?, expiry_time = ?, reset = ?
|
||||||
WHERE inbound_id = ? AND email = ?`,
|
WHERE inbound_id = ? AND email = ?`,
|
||||||
cs.Enable, cs.Total, cs.ExpiryTime, cs.Reset, allTime, c.Id, cs.Email,
|
cs.Enable, cs.Total, cs.ExpiryTime, cs.Reset, c.Id, cs.Email,
|
||||||
).Error; err != nil {
|
).Error; err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
@ -1828,9 +1818,9 @@ func (s *InboundService) setRemoteTrafficLocked(nodeID int, snap *runtime.Traffi
|
||||||
if err := tx.Exec(
|
if err := tx.Exec(
|
||||||
`UPDATE client_traffics
|
`UPDATE client_traffics
|
||||||
SET up = ?, down = ?, enable = ?, total = ?, expiry_time = ?, reset = ?,
|
SET up = ?, down = ?, enable = ?, total = ?, expiry_time = ?, reset = ?,
|
||||||
all_time = ?, last_online = MAX(last_online, ?)
|
last_online = MAX(last_online, ?)
|
||||||
WHERE inbound_id = ? AND email = ?`,
|
WHERE inbound_id = ? AND email = ?`,
|
||||||
cs.Up, cs.Down, cs.Enable, cs.Total, cs.ExpiryTime, cs.Reset, allTime,
|
cs.Up, cs.Down, cs.Enable, cs.Total, cs.ExpiryTime, cs.Reset,
|
||||||
cs.LastOnline, c.Id, cs.Email,
|
cs.LastOnline, c.Id, cs.Email,
|
||||||
).Error; err != nil {
|
).Error; err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
|
@ -1932,7 +1922,6 @@ func (s *InboundService) addInboundTraffic(tx *gorm.DB, traffics []*xray.Traffic
|
||||||
Updates(map[string]any{
|
Updates(map[string]any{
|
||||||
"up": gorm.Expr("up + ?", traffic.Up),
|
"up": gorm.Expr("up + ?", traffic.Up),
|
||||||
"down": gorm.Expr("down + ?", traffic.Down),
|
"down": gorm.Expr("down + ?", traffic.Down),
|
||||||
"all_time": gorm.Expr("COALESCE(all_time, 0) + ?", traffic.Up+traffic.Down),
|
|
||||||
}).Error
|
}).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -1987,7 +1976,6 @@ func (s *InboundService) addClientTraffic(tx *gorm.DB, traffics []*xray.ClientTr
|
||||||
}
|
}
|
||||||
dbClientTraffics[dbTraffic_index].Up += t.Up
|
dbClientTraffics[dbTraffic_index].Up += t.Up
|
||||||
dbClientTraffics[dbTraffic_index].Down += t.Down
|
dbClientTraffics[dbTraffic_index].Down += t.Down
|
||||||
dbClientTraffics[dbTraffic_index].AllTime += t.Up + t.Down
|
|
||||||
if t.Up+t.Down > 0 {
|
if t.Up+t.Down > 0 {
|
||||||
dbClientTraffics[dbTraffic_index].LastOnline = now
|
dbClientTraffics[dbTraffic_index].LastOnline = now
|
||||||
}
|
}
|
||||||
|
|
@ -3453,7 +3441,6 @@ type InboundTrafficSummary struct {
|
||||||
Up int64 `json:"up"`
|
Up int64 `json:"up"`
|
||||||
Down int64 `json:"down"`
|
Down int64 `json:"down"`
|
||||||
Total int64 `json:"total"`
|
Total int64 `json:"total"`
|
||||||
AllTime int64 `json:"allTime"`
|
|
||||||
Enable bool `json:"enable"`
|
Enable bool `json:"enable"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3461,7 +3448,7 @@ func (s *InboundService) GetInboundsTrafficSummary() ([]InboundTrafficSummary, e
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
var summaries []InboundTrafficSummary
|
var summaries []InboundTrafficSummary
|
||||||
if err := db.Model(&model.Inbound{}).
|
if err := db.Model(&model.Inbound{}).
|
||||||
Select("id, up, down, total, all_time, enable").
|
Select("id, up, down, total, enable").
|
||||||
Find(&summaries).Error; err != nil {
|
Find(&summaries).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -3491,7 +3478,6 @@ func (s *InboundService) UpdateClientTrafficByEmail(email string, upload int64,
|
||||||
Updates(map[string]any{
|
Updates(map[string]any{
|
||||||
"up": upload,
|
"up": upload,
|
||||||
"down": download,
|
"down": download,
|
||||||
"all_time": gorm.Expr("CASE WHEN COALESCE(all_time, 0) < ? THEN ? ELSE all_time END", upload+download, upload+download),
|
|
||||||
}).Error
|
}).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warningf("Error updating ClientTraffic with email %s: %v", email, err)
|
logger.Warningf("Error updating ClientTraffic with email %s: %v", email, err)
|
||||||
|
|
@ -3664,24 +3650,16 @@ func (s *InboundService) MigrationRequirements() {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Calculate and backfill all_time from up+down for inbounds and clients
|
if tx.Migrator().HasColumn(&model.Inbound{}, "all_time") {
|
||||||
err = tx.Exec(`
|
if err = tx.Migrator().DropColumn(&model.Inbound{}, "all_time"); err != nil {
|
||||||
UPDATE inbounds
|
|
||||||
SET all_time = IFNULL(up, 0) + IFNULL(down, 0)
|
|
||||||
WHERE IFNULL(all_time, 0) = 0 AND (IFNULL(up, 0) + IFNULL(down, 0)) > 0
|
|
||||||
`).Error
|
|
||||||
if err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = tx.Exec(`
|
}
|
||||||
UPDATE client_traffics
|
if tx.Migrator().HasColumn(&xray.ClientTraffic{}, "all_time") {
|
||||||
SET all_time = IFNULL(up, 0) + IFNULL(down, 0)
|
if err = tx.Migrator().DropColumn(&xray.ClientTraffic{}, "all_time"); err != nil {
|
||||||
WHERE IFNULL(all_time, 0) = 0 AND (IFNULL(up, 0) + IFNULL(down, 0)) > 0
|
|
||||||
`).Error
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fix inbounds based problems
|
// Fix inbounds based problems
|
||||||
var inbounds []*model.Inbound
|
var inbounds []*model.Inbound
|
||||||
|
|
|
||||||
|
|
@ -237,8 +237,6 @@
|
||||||
"customGeoEmpty": "لا توجد مصادر geo مخصصة بعد — انقر على «إضافة» لإنشاء واحد"
|
"customGeoEmpty": "لا توجد مصادر geo مخصصة بعد — انقر على «إضافة» لإنشاء واحد"
|
||||||
},
|
},
|
||||||
"inbounds": {
|
"inbounds": {
|
||||||
"allTimeTraffic": "إجمالي حركة المرور",
|
|
||||||
"allTimeTrafficUsage": "إجمالي الاستخدام طوال الوقت",
|
|
||||||
"title": "الإدخالات",
|
"title": "الإدخالات",
|
||||||
"totalDownUp": "إجمالي المرسل/المستقبل",
|
"totalDownUp": "إجمالي المرسل/المستقبل",
|
||||||
"totalUsage": "إجمالي الاستخدام",
|
"totalUsage": "إجمالي الاستخدام",
|
||||||
|
|
|
||||||
|
|
@ -238,8 +238,6 @@
|
||||||
"getConfigError": "An error occurred while retrieving the config file."
|
"getConfigError": "An error occurred while retrieving the config file."
|
||||||
},
|
},
|
||||||
"inbounds": {
|
"inbounds": {
|
||||||
"allTimeTraffic": "All-time Traffic",
|
|
||||||
"allTimeTrafficUsage": "All-Time Total Usage",
|
|
||||||
"title": "Inbounds",
|
"title": "Inbounds",
|
||||||
"totalDownUp": "Total Sent/Received",
|
"totalDownUp": "Total Sent/Received",
|
||||||
"totalUsage": "Total Usage",
|
"totalUsage": "Total Usage",
|
||||||
|
|
|
||||||
|
|
@ -237,8 +237,6 @@
|
||||||
"customGeoEmpty": "Aún no hay fuentes geo personalizadas — haz clic en Añadir para crear una"
|
"customGeoEmpty": "Aún no hay fuentes geo personalizadas — haz clic en Añadir para crear una"
|
||||||
},
|
},
|
||||||
"inbounds": {
|
"inbounds": {
|
||||||
"allTimeTraffic": "Tráfico Total",
|
|
||||||
"allTimeTrafficUsage": "Uso de datos histórico",
|
|
||||||
"title": "Entradas",
|
"title": "Entradas",
|
||||||
"totalDownUp": "Subidas/Descargas Totales",
|
"totalDownUp": "Subidas/Descargas Totales",
|
||||||
"totalUsage": "Uso Total",
|
"totalUsage": "Uso Total",
|
||||||
|
|
|
||||||
|
|
@ -240,8 +240,6 @@
|
||||||
"node": "نود",
|
"node": "نود",
|
||||||
"deployTo": "استقرار روی",
|
"deployTo": "استقرار روی",
|
||||||
"localPanel": "پنل لوکال",
|
"localPanel": "پنل لوکال",
|
||||||
"allTimeTraffic": "کل ترافیک",
|
|
||||||
"allTimeTrafficUsage": "کل استفاده در تمام مدت",
|
|
||||||
"title": "کاربران",
|
"title": "کاربران",
|
||||||
"totalDownUp": "دریافت/ارسال کل",
|
"totalDownUp": "دریافت/ارسال کل",
|
||||||
"totalUsage": "مصرف کل",
|
"totalUsage": "مصرف کل",
|
||||||
|
|
|
||||||
|
|
@ -237,8 +237,6 @@
|
||||||
"customGeoEmpty": "Belum ada sumber geo kustom — klik Tambah untuk membuatnya"
|
"customGeoEmpty": "Belum ada sumber geo kustom — klik Tambah untuk membuatnya"
|
||||||
},
|
},
|
||||||
"inbounds": {
|
"inbounds": {
|
||||||
"allTimeTraffic": "Total Lalu Lintas",
|
|
||||||
"allTimeTrafficUsage": "Total Penggunaan Sepanjang Waktu",
|
|
||||||
"title": "Masuk",
|
"title": "Masuk",
|
||||||
"totalDownUp": "Total Terkirim/Diterima",
|
"totalDownUp": "Total Terkirim/Diterima",
|
||||||
"totalUsage": "Penggunaan Total",
|
"totalUsage": "Penggunaan Total",
|
||||||
|
|
|
||||||
|
|
@ -237,8 +237,6 @@
|
||||||
"customGeoEmpty": "カスタム geo ソースはまだありません — 「追加」をクリックして作成してください"
|
"customGeoEmpty": "カスタム geo ソースはまだありません — 「追加」をクリックして作成してください"
|
||||||
},
|
},
|
||||||
"inbounds": {
|
"inbounds": {
|
||||||
"allTimeTraffic": "総トラフィック",
|
|
||||||
"allTimeTrafficUsage": "これまでの総使用量",
|
|
||||||
"title": "インバウンド一覧",
|
"title": "インバウンド一覧",
|
||||||
"totalDownUp": "総アップロード / ダウンロード",
|
"totalDownUp": "総アップロード / ダウンロード",
|
||||||
"totalUsage": "総使用量",
|
"totalUsage": "総使用量",
|
||||||
|
|
|
||||||
|
|
@ -237,8 +237,6 @@
|
||||||
"customGeoEmpty": "Ainda não há fontes geo personalizadas — clique em Adicionar para criar uma"
|
"customGeoEmpty": "Ainda não há fontes geo personalizadas — clique em Adicionar para criar uma"
|
||||||
},
|
},
|
||||||
"inbounds": {
|
"inbounds": {
|
||||||
"allTimeTraffic": "Tráfego Total",
|
|
||||||
"allTimeTrafficUsage": "Uso total de todos os tempos",
|
|
||||||
"title": "Inbounds",
|
"title": "Inbounds",
|
||||||
"totalDownUp": "Total Enviado/Recebido",
|
"totalDownUp": "Total Enviado/Recebido",
|
||||||
"totalUsage": "Uso Total",
|
"totalUsage": "Uso Total",
|
||||||
|
|
|
||||||
|
|
@ -237,8 +237,6 @@
|
||||||
"getConfigError": "Произошла ошибка при получении конфигурационного файла"
|
"getConfigError": "Произошла ошибка при получении конфигурационного файла"
|
||||||
},
|
},
|
||||||
"inbounds": {
|
"inbounds": {
|
||||||
"allTimeTraffic": "Общий трафик",
|
|
||||||
"allTimeTrafficUsage": "Общее использование за все время",
|
|
||||||
"title": "Подключения",
|
"title": "Подключения",
|
||||||
"totalDownUp": "Отправлено/получено",
|
"totalDownUp": "Отправлено/получено",
|
||||||
"totalUsage": "Всего трафика",
|
"totalUsage": "Всего трафика",
|
||||||
|
|
|
||||||
|
|
@ -237,8 +237,6 @@
|
||||||
"customGeoEmpty": "Henüz özel geo kaynağı yok — oluşturmak için Ekle'ye tıklayın"
|
"customGeoEmpty": "Henüz özel geo kaynağı yok — oluşturmak için Ekle'ye tıklayın"
|
||||||
},
|
},
|
||||||
"inbounds": {
|
"inbounds": {
|
||||||
"allTimeTraffic": "Toplam Trafik",
|
|
||||||
"allTimeTrafficUsage": "Tüm Zamanların Toplam Kullanımı",
|
|
||||||
"title": "Gelenler",
|
"title": "Gelenler",
|
||||||
"totalDownUp": "Toplam Gönderilen/Alınan",
|
"totalDownUp": "Toplam Gönderilen/Alınan",
|
||||||
"totalUsage": "Toplam Kullanım",
|
"totalUsage": "Toplam Kullanım",
|
||||||
|
|
|
||||||
|
|
@ -237,8 +237,6 @@
|
||||||
"customGeoEmpty": "Користувацьких джерел geo поки немає — натисніть «Додати», щоб створити"
|
"customGeoEmpty": "Користувацьких джерел geo поки немає — натисніть «Додати», щоб створити"
|
||||||
},
|
},
|
||||||
"inbounds": {
|
"inbounds": {
|
||||||
"allTimeTraffic": "Загальний трафік",
|
|
||||||
"allTimeTrafficUsage": "Загальне використання за весь час",
|
|
||||||
"title": "Вхідні",
|
"title": "Вхідні",
|
||||||
"totalDownUp": "Всього надісланих/отриманих",
|
"totalDownUp": "Всього надісланих/отриманих",
|
||||||
"totalUsage": "Всього використанно",
|
"totalUsage": "Всього використанно",
|
||||||
|
|
|
||||||
|
|
@ -237,8 +237,6 @@
|
||||||
"customGeoEmpty": "Chưa có nguồn geo tùy chỉnh nào — nhấp Thêm để tạo"
|
"customGeoEmpty": "Chưa có nguồn geo tùy chỉnh nào — nhấp Thêm để tạo"
|
||||||
},
|
},
|
||||||
"inbounds": {
|
"inbounds": {
|
||||||
"allTimeTraffic": "Tổng Lưu Lượng",
|
|
||||||
"allTimeTrafficUsage": "Tổng mức sử dụng mọi lúc",
|
|
||||||
"title": "Điểm vào (Inbounds)",
|
"title": "Điểm vào (Inbounds)",
|
||||||
"totalDownUp": "Tổng tải lên/tải xuống",
|
"totalDownUp": "Tổng tải lên/tải xuống",
|
||||||
"totalUsage": "Tổng sử dụng",
|
"totalUsage": "Tổng sử dụng",
|
||||||
|
|
|
||||||
|
|
@ -237,8 +237,6 @@
|
||||||
"customGeoEmpty": "暂无自定义 geo 源 — 点击「添加」以创建"
|
"customGeoEmpty": "暂无自定义 geo 源 — 点击「添加」以创建"
|
||||||
},
|
},
|
||||||
"inbounds": {
|
"inbounds": {
|
||||||
"allTimeTraffic": "累计总流量",
|
|
||||||
"allTimeTrafficUsage": "所有时间总使用量",
|
|
||||||
"title": "入站列表",
|
"title": "入站列表",
|
||||||
"totalDownUp": "总上传 / 下载",
|
"totalDownUp": "总上传 / 下载",
|
||||||
"totalUsage": "总用量",
|
"totalUsage": "总用量",
|
||||||
|
|
|
||||||
|
|
@ -237,8 +237,6 @@
|
||||||
"customGeoEmpty": "尚無自訂 geo 來源 — 點擊「新增」以建立"
|
"customGeoEmpty": "尚無自訂 geo 來源 — 點擊「新增」以建立"
|
||||||
},
|
},
|
||||||
"inbounds": {
|
"inbounds": {
|
||||||
"allTimeTraffic": "累計總流量",
|
|
||||||
"allTimeTrafficUsage": "所有时间总使用量",
|
|
||||||
"title": "入站列表",
|
"title": "入站列表",
|
||||||
"totalDownUp": "總上傳 / 下載",
|
"totalDownUp": "總上傳 / 下載",
|
||||||
"totalUsage": "總用量",
|
"totalUsage": "總用量",
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ type ClientTraffic struct {
|
||||||
SubId string `json:"subId" form:"subId" gorm:"-"`
|
SubId string `json:"subId" form:"subId" gorm:"-"`
|
||||||
Up int64 `json:"up" form:"up"`
|
Up int64 `json:"up" form:"up"`
|
||||||
Down int64 `json:"down" form:"down"`
|
Down int64 `json:"down" form:"down"`
|
||||||
AllTime int64 `json:"allTime" form:"allTime"`
|
|
||||||
ExpiryTime int64 `json:"expiryTime" form:"expiryTime"`
|
ExpiryTime int64 `json:"expiryTime" form:"expiryTime"`
|
||||||
Total int64 `json:"total" form:"total"`
|
Total int64 `json:"total" form:"total"`
|
||||||
Reset int `json:"reset" form:"reset" gorm:"default:0"`
|
Reset int `json:"reset" form:"reset" gorm:"default:0"`
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue