Merge branch 'MHSanaei:main' into main

This commit is contained in:
Ilya05228 2025-08-28 21:22:28 +03:00 committed by GitHub
commit 2187f84b8b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 85 additions and 67 deletions

View file

@ -32,6 +32,7 @@ type Inbound struct {
Up int64 `json:"up" form:"up"`
Down int64 `json:"down" form:"down"`
Total int64 `json:"total" form:"total"`
AllTime int64 `json:"allTime" form:"allTime" gorm:"default:0"`
Remark string `json:"remark" form:"remark"`
Enable bool `json:"enable" form:"enable"`
ExpiryTime int64 `json:"expiryTime" form:"expiryTime"`
@ -45,7 +46,6 @@ type Inbound struct {
StreamSettings string `json:"streamSettings" form:"streamSettings"`
Tag string `json:"tag" form:"tag" gorm:"unique"`
Sniffing string `json:"sniffing" form:"sniffing"`
Allocate string `json:"allocate" form:"allocate"`
}
type OutboundTraffics struct {
@ -80,7 +80,6 @@ func (i *Inbound) GenXrayInboundConfig() *xray.InboundConfig {
StreamSettings: json_util.RawMessage(i.StreamSettings),
Tag: i.Tag,
Sniffing: json_util.RawMessage(i.Sniffing),
Allocate: json_util.RawMessage(i.Allocate),
}
}

View file

@ -6,6 +6,7 @@ class DBInbound {
this.up = 0;
this.down = 0;
this.total = 0;
this.allTime = 0;
this.remark = "";
this.enable = true;
this.expiryTime = 0;

View file

@ -1042,27 +1042,6 @@ class Sniffing extends XrayCommonClass {
}
}
class Allocate extends XrayCommonClass {
constructor(
strategy = "always",
refresh = 5,
concurrency = 3,
) {
super();
this.strategy = strategy;
this.refresh = refresh;
this.concurrency = concurrency;
}
static fromJson(json = {}) {
return new Allocate(
json.strategy,
json.refresh,
json.concurrency,
);
}
}
class Inbound extends XrayCommonClass {
constructor(
port = RandomUtil.randomInteger(10000, 60000),
@ -1072,7 +1051,6 @@ class Inbound extends XrayCommonClass {
streamSettings = new StreamSettings(),
tag = '',
sniffing = new Sniffing(),
allocate = new Allocate(),
clientStats = '',
) {
super();
@ -1083,7 +1061,6 @@ class Inbound extends XrayCommonClass {
this.stream = streamSettings;
this.tag = tag;
this.sniffing = sniffing;
this.allocate = allocate;
this.clientStats = clientStats;
}
getClientStats() {
@ -1248,7 +1225,6 @@ class Inbound extends XrayCommonClass {
this.stream = new StreamSettings();
this.tag = '';
this.sniffing = new Sniffing();
this.allocate = new Allocate();
}
genVmessLink(address = '', port = this.port, forceTls, remark = '', clientId, security) {
@ -1703,7 +1679,6 @@ class Inbound extends XrayCommonClass {
StreamSettings.fromJson(json.streamSettings),
json.tag,
Sniffing.fromJson(json.sniffing),
Allocate.fromJson(json.allocate),
json.clientStats
)
}
@ -1721,7 +1696,6 @@ class Inbound extends XrayCommonClass {
streamSettings: streamSettings,
tag: this.tag,
sniffing: this.sniffing.toJson(),
allocate: this.allocate.toJson(),
clientStats: this.clientStats
};
}

View file

@ -98,6 +98,10 @@
</table>
</a-popover>
</template>
<template slot="allTime" slot-scope="text, client">
<a-tag>[[ SizeFormatter.sizeFormat(getAllTimeClient(record, client.email)) ]]</a-tag>
</template>
<template slot="expiryTime" slot-scope="text, client, index">
<template v-if="client.expiryTime !=0 && client.reset >0">
<a-popover :overlay-class-name="themeSwitcher.currentTheme">

View file

@ -1,15 +0,0 @@
{{define "form/allocate"}}
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='Strategy'>
<a-select v-model="inbound.allocate.strategy" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="s in ['always','random']" :value="s">[[ s ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item label='Refresh'>
<a-input-number v-model.number="inbound.allocate.refresh" min="0"></a-input-number>
</a-form-item>
<a-form-item label='Concurrency'>
<a-input-number v-model.number="inbound.allocate.concurrency" min="0"></a-input-number>
</a-form-item>
</a-form>
{{end}}

View file

@ -121,13 +121,4 @@
</a-collapse-panel>
</a-collapse>
<!-- allocate -->
<!-- Temporarily disabled until we accepts range for port allocation
<a-collapse>
<a-collapse-panel header='Allocate'>
{{template "form/allocate"}}
</a-collapse-panel>
</a-collapse>
-->
{{end}}

View file

@ -167,28 +167,35 @@
<a-col>
<a-card size="small" :style="{ padding: '16px' }" hoverable>
<a-row>
<a-col :sm="12" :md="6">
<a-col :sm="12" :md="5">
<a-custom-statistic title='{{ i18n "pages.inbounds.totalDownUp" }}' :value="`${SizeFormatter.sizeFormat(total.up)} / ${SizeFormatter.sizeFormat(total.down)}`">
<template #prefix>
<a-icon type="swap"></a-icon>
</template>
</a-custom-statistic>
</a-col>
<a-col :sm="12" :md="6">
<a-col :sm="12" :md="5">
<a-custom-statistic title='{{ i18n "pages.inbounds.totalUsage" }}' :value="SizeFormatter.sizeFormat(total.up + total.down)" :style="{ marginTop: isMobile ? '10px' : 0 }">
<template #prefix>
<a-icon type="pie-chart"></a-icon>
</template>
</a-custom-statistic>
</a-col>
<a-col :sm="12" :md="6">
<a-col :sm="12" :md="5">
<a-custom-statistic title='{{ i18n "pages.inbounds.allTimeTrafficUsage" }}' :value="SizeFormatter.sizeFormat(total.allTime)" :style="{ marginTop: isMobile ? '10px' : 0 }">
<template #prefix>
<a-icon type="history"></a-icon>
</template>
</a-custom-statistic>
</a-col>
<a-col :sm="12" :md="5">
<a-custom-statistic title='{{ i18n "pages.inbounds.inboundCount" }}' :value="dbInbounds.length" :style="{ marginTop: isMobile ? '10px' : 0 }">
<template #prefix>
<a-icon type="bars"></a-icon>
</template>
</a-custom-statistic>
</a-col>
<a-col :sm="12" :md="6">
<a-col :sm="12" :md="4">
<a-custom-statistic title='{{ i18n "clients" }}' value=" " :style="{ marginTop: isMobile ? '10px' : 0 }">
<template #prefix>
<a-space direction="horizontal">
@ -484,6 +491,9 @@
</a-tag>
</a-popover>
</template>
<template slot="allTimeInbound" slot-scope="text, dbInbound">
<a-tag>[[ SizeFormatter.sizeFormat(dbInbound.allTime || 0) ]]</a-tag>
</template>
<template slot="enable" slot-scope="text, dbInbound">
<a-switch v-model="dbInbound.enable" @change="switchEnable(dbInbound.id,dbInbound.enable)"></a-switch>
</template>
@ -723,6 +733,11 @@
align: 'center',
width: 60,
scopedSlots: { customRender: 'traffic' },
}, {
title: '{{ i18n "pages.inbounds.allTimeTraffic" }}',
align: 'center',
width: 60,
scopedSlots: { customRender: 'allTimeInbound' },
}, {
title: '{{ i18n "pages.inbounds.expireDate" }}',
align: 'center',
@ -759,6 +774,7 @@
{ title: '{{ i18n "online" }}', width: 30, scopedSlots: { customRender: 'online' } },
{ title: '{{ i18n "pages.inbounds.client" }}', width: 80, scopedSlots: { customRender: 'client' } },
{ title: '{{ i18n "pages.inbounds.traffic" }}', width: 80, align: 'center', scopedSlots: { customRender: 'traffic' } },
{ title: '{{ i18n "pages.inbounds.allTimeTraffic" }}', width: 80, align: 'center', scopedSlots: { customRender: 'allTime' } },
{ title: '{{ i18n "pages.inbounds.expireDate" }}', width: 80, align: 'center', scopedSlots: { customRender: 'expiryTime' } },
{ title: '{{ i18n "pages.inbounds.createdAt" }}', width: 90, align: 'center', scopedSlots: { customRender: 'createdAt' } },
{ title: '{{ i18n "pages.inbounds.updatedAt" }}', width: 90, align: 'center', scopedSlots: { customRender: 'updatedAt' } },
@ -1077,7 +1093,6 @@
settings: Inbound.Settings.getSettings(baseInbound.protocol).toString(),
streamSettings: baseInbound.stream.toString(),
sniffing: baseInbound.sniffing.toString(),
allocate: baseInbound.allocate.toString(),
};
await this.submit('/panel/inbound/add', data, inModal);
},
@ -1127,7 +1142,6 @@
data.streamSettings = JSON.stringify({ sockopt: inbound.stream.sockopt.toJson() }, null, 2);
}
data.sniffing = inbound.sniffing.toString();
data.allocate = inbound.allocate.toString();
await this.submit('/panel/inbound/add', data, inModal);
},
@ -1151,7 +1165,6 @@
data.streamSettings = JSON.stringify({ sockopt: inbound.stream.sockopt.toJson() }, null, 2);
}
data.sniffing = inbound.sniffing.toString();
data.allocate = inbound.allocate.toString();
await this.submit(`/panel/inbound/update/${dbInbound.id}`, data, inModal);
},
@ -1419,6 +1432,12 @@
clientStats = dbInbound.clientStats.find(stats => stats.email === email);
return clientStats ? clientStats.up + clientStats.down : 0;
},
getAllTimeClient(dbInbound, email) {
if (email.length == 0) return 0;
clientStats = dbInbound.clientStats.find(stats => stats.email === email);
if (!clientStats) return 0;
return clientStats.allTime || (clientStats.up + clientStats.down);
},
getRemStats(dbInbound, email) {
if (email.length == 0) return 0;
clientStats = dbInbound.clientStats.find(stats => stats.email === email);
@ -1608,11 +1627,12 @@
},
computed: {
total() {
let down = 0, up = 0;
let down = 0, up = 0, allTime = 0;
let clients = 0, deactive = [], depleted = [], expiring = [];
this.dbInbounds.forEach(dbInbound => {
down += dbInbound.down;
up += dbInbound.up;
allTime += (dbInbound.allTime || (dbInbound.up + dbInbound.down));
if (this.clientCount[dbInbound.id]) {
clients += this.clientCount[dbInbound.id].clients;
deactive = deactive.concat(this.clientCount[dbInbound.id].deactive);
@ -1623,6 +1643,7 @@
return {
down: down,
up: up,
allTime: allTime,
clients: clients,
deactive: deactive,
depleted: depleted,

View file

@ -403,7 +403,6 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
oldInbound.Settings = inbound.Settings
oldInbound.StreamSettings = inbound.StreamSettings
oldInbound.Sniffing = inbound.Sniffing
oldInbound.Allocate = inbound.Allocate
if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" {
oldInbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
} else {
@ -915,8 +914,9 @@ func (s *InboundService) addInboundTraffic(tx *gorm.DB, traffics []*xray.Traffic
if traffic.IsInbound {
err = tx.Model(&model.Inbound{}).Where("tag = ?", traffic.Tag).
Updates(map[string]any{
"up": gorm.Expr("up + ?", traffic.Up),
"down": gorm.Expr("down + ?", traffic.Down),
"up": gorm.Expr("up + ?", traffic.Up),
"down": gorm.Expr("down + ?", traffic.Down),
"all_time": gorm.Expr("COALESCE(all_time, 0) + ?", traffic.Up+traffic.Down),
}).Error
if err != nil {
return err
@ -962,6 +962,7 @@ func (s *InboundService) addClientTraffic(tx *gorm.DB, traffics []*xray.ClientTr
if dbClientTraffics[dbTraffic_index].Email == traffics[traffic_index].Email {
dbClientTraffics[dbTraffic_index].Up += traffics[traffic_index].Up
dbClientTraffics[dbTraffic_index].Down += traffics[traffic_index].Down
dbClientTraffics[dbTraffic_index].AllTime += (traffics[traffic_index].Up + traffics[traffic_index].Down)
// Add user in onlineUsers array on traffic
if traffics[traffic_index].Up+traffics[traffic_index].Down > 0 {
@ -2036,6 +2037,25 @@ func (s *InboundService) MigrationRequirements() {
}
}()
// Calculate and backfill all_time from up+down for inbounds and clients
err = tx.Exec(`
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
}
err = tx.Exec(`
UPDATE client_traffics
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
}
// Fix inbounds based problems
var inbounds []*model.Inbound
err = tx.Model(model.Inbound{}).Where("protocol IN (?)", []string{"vmess", "vless", "trojan"}).Find(&inbounds).Error

View file

@ -151,6 +151,8 @@
"getConfigError" = "حدث خطأ أثناء استرجاع ملف الإعدادات"
[pages.inbounds]
"allTimeTraffic" = "إجمالي حركة المرور"
"allTimeTrafficUsage" = "إجمالي الاستخدام طوال الوقت"
"title" = "الإدخالات"
"totalDownUp" = "إجمالي المرسل/المستقبل"
"totalUsage" = "إجمالي الاستخدام"

View file

@ -151,6 +151,8 @@
"getConfigError" = "An error occurred while retrieving the config file."
[pages.inbounds]
"allTimeTraffic" = "All-time Traffic"
"allTimeTrafficUsage" = "All Time Total Usage"
"title" = "Inbounds"
"totalDownUp" = "Total Sent/Received"
"totalUsage" = "Total Usage"

View file

@ -151,6 +151,8 @@
"getConfigError" = "Ocurrió un error al obtener el archivo de configuración"
[pages.inbounds]
"allTimeTraffic" = "Tráfico Total"
"allTimeTrafficUsage" = "Uso total de todos los tiempos"
"title" = "Entradas"
"totalDownUp" = "Subidas/Descargas Totales"
"totalUsage" = "Uso Total"

View file

@ -151,6 +151,8 @@
"getConfigError" = "خطا در دریافت فایل پیکربندی"
[pages.inbounds]
"allTimeTraffic" = "کل ترافیک"
"allTimeTrafficUsage" = "کل استفاده در تمام مدت"
"title" = "کاربران"
"totalDownUp" = "دریافت/ارسال کل"
"totalUsage" = "‌‌‌مصرف کل"

View file

@ -151,6 +151,8 @@
"getConfigError" = "Terjadi kesalahan saat mengambil file konfigurasi"
[pages.inbounds]
"allTimeTraffic" = "Total Lalu Lintas"
"allTimeTrafficUsage" = "Total Penggunaan Sepanjang Waktu"
"title" = "Masuk"
"totalDownUp" = "Total Terkirim/Diterima"
"totalUsage" = "Penggunaan Total"

View file

@ -151,6 +151,8 @@
"getConfigError" = "設定ファイルの取得中にエラーが発生しました"
[pages.inbounds]
"allTimeTraffic" = "総トラフィック"
"allTimeTrafficUsage" = "これまでの総使用量"
"title" = "インバウンド一覧"
"totalDownUp" = "総アップロード / ダウンロード"
"totalUsage" = "総使用量"

View file

@ -151,6 +151,8 @@
"getConfigError" = "Ocorreu um erro ao recuperar o arquivo de configuração"
[pages.inbounds]
"allTimeTraffic" = "Tráfego Total"
"allTimeTrafficUsage" = "Uso total de todos os tempos"
"title" = "Inbounds"
"totalDownUp" = "Total Enviado/Recebido"
"totalUsage" = "Uso Total"

View file

@ -151,6 +151,8 @@
"getConfigError" = "Произошла ошибка при получении конфигурационного файла"
[pages.inbounds]
"allTimeTraffic" = "Общий трафик"
"allTimeTrafficUsage" = "Общее использование за все время"
"title" = "Инбаунды"
"totalDownUp" = "Объем отправленного/полученного трафика"
"totalUsage" = "Всего трафика"

View file

@ -151,6 +151,8 @@
"getConfigError" = "Yapılandırma dosyası alınırken bir hata oluştu"
[pages.inbounds]
"allTimeTraffic" = "Toplam Trafik"
"allTimeTrafficUsage" = "Tüm Zamanların Toplam Kullanımı"
"title" = "Gelenler"
"totalDownUp" = "Toplam Gönderilen/Alınan"
"totalUsage" = "Toplam Kullanım"

View file

@ -151,6 +151,8 @@
"getConfigError" = "Виникла помилка під час отримання файлу конфігурації"
[pages.inbounds]
"allTimeTraffic" = "Загальний трафік"
"allTimeTrafficUsage" = "Загальне використання за весь час"
"title" = "Вхідні"
"totalDownUp" = "Всього надісланих/отриманих"
"totalUsage" = "Всього використанно"

View file

@ -151,6 +151,8 @@
"getConfigError" = "Lỗi xảy ra khi truy xuất tệp cấu hình"
[pages.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)"
"totalDownUp" = "Tổng tải lên/tải xuống"
"totalUsage" = "Tổng sử dụng"

View file

@ -151,6 +151,8 @@
"getConfigError" = "检索配置文件时出错"
[pages.inbounds]
"allTimeTraffic" = "累计总流量"
"allTimeTrafficUsage" = "所有时间总使用量"
"title" = "入站列表"
"totalDownUp" = "总上传 / 下载"
"totalUsage" = "总用量"

View file

@ -151,6 +151,8 @@
"getConfigError" = "檢索設定檔時發生錯誤"
[pages.inbounds]
"allTimeTraffic" = "累計總流量"
"allTimeTrafficUsage" = "所有时间总使用量"
"title" = "入站列表"
"totalDownUp" = "總上傳 / 下載"
"totalUsage" = "總用量"

View file

@ -7,6 +7,7 @@ type ClientTraffic struct {
Email string `json:"email" form:"email" gorm:"unique"`
Up int64 `json:"up" form:"up"`
Down int64 `json:"down" form:"down"`
AllTime int64 `json:"allTime" form:"allTime"`
ExpiryTime int64 `json:"expiryTime" form:"expiryTime"`
Total int64 `json:"total" form:"total"`
Reset int `json:"reset" form:"reset" gorm:"default:0"`

View file

@ -14,7 +14,6 @@ type InboundConfig struct {
StreamSettings json_util.RawMessage `json:"streamSettings"`
Tag string `json:"tag"`
Sniffing json_util.RawMessage `json:"sniffing"`
Allocate json_util.RawMessage `json:"allocate"`
}
func (c *InboundConfig) Equals(other *InboundConfig) bool {
@ -39,8 +38,5 @@ func (c *InboundConfig) Equals(other *InboundConfig) bool {
if !bytes.Equal(c.Sniffing, other.Sniffing) {
return false
}
if !bytes.Equal(c.Allocate, other.Allocate) {
return false
}
return true
}