Browse Source

feat(externalTrafficJob): External Traffic Inform (#2660)

* Add Setting entity + GUI field in panel settings

* Add a missing 'Traffic' in InformEnabale field

* Add ExternalTrafficURL Post request call

* Add translation + cleanup

* Move options to General tab

---------

Co-authored-by: root <[email protected]>
Co-authored-by: root <[email protected]>
AAA 1 month ago
parent
commit
1bbf31df9f

+ 2 - 0
web/assets/js/model/setting.js

@@ -31,6 +31,8 @@ class AllSetting {
         this.subPath = "/sub/";
         this.subJsonPath = "/json/";
         this.subDomain = "";
+        this.externalTrafficInformEnable = false;
+        this.externalTrafficInformURI = "";
         this.subCertFile = "";
         this.subKeyFile = "";
         this.subUpdates = 12;

+ 43 - 41
web/entity/entity.go

@@ -16,47 +16,49 @@ type Msg struct {
 }
 
 type AllSetting struct {
-	WebListen        string `json:"webListen" form:"webListen"`
-	WebDomain        string `json:"webDomain" form:"webDomain"`
-	WebPort          int    `json:"webPort" form:"webPort"`
-	WebCertFile      string `json:"webCertFile" form:"webCertFile"`
-	WebKeyFile       string `json:"webKeyFile" form:"webKeyFile"`
-	WebBasePath      string `json:"webBasePath" form:"webBasePath"`
-	SessionMaxAge    int    `json:"sessionMaxAge" form:"sessionMaxAge"`
-	PageSize         int    `json:"pageSize" form:"pageSize"`
-	ExpireDiff       int    `json:"expireDiff" form:"expireDiff"`
-	TrafficDiff      int    `json:"trafficDiff" form:"trafficDiff"`
-	RemarkModel      string `json:"remarkModel" form:"remarkModel"`
-	TgBotEnable      bool   `json:"tgBotEnable" form:"tgBotEnable"`
-	TgBotToken       string `json:"tgBotToken" form:"tgBotToken"`
-	TgBotProxy       string `json:"tgBotProxy" form:"tgBotProxy"`
-	TgBotAPIServer   string `json:"tgBotAPIServer" form:"tgBotAPIServer"`
-	TgBotChatId      string `json:"tgBotChatId" form:"tgBotChatId"`
-	TgRunTime        string `json:"tgRunTime" form:"tgRunTime"`
-	TgBotBackup      bool   `json:"tgBotBackup" form:"tgBotBackup"`
-	TgBotLoginNotify bool   `json:"tgBotLoginNotify" form:"tgBotLoginNotify"`
-	TgCpu            int    `json:"tgCpu" form:"tgCpu"`
-	TgLang           string `json:"tgLang" form:"tgLang"`
-	TimeLocation     string `json:"timeLocation" form:"timeLocation"`
-	SecretEnable     bool   `json:"secretEnable" form:"secretEnable"`
-	SubEnable        bool   `json:"subEnable" form:"subEnable"`
-	SubListen        string `json:"subListen" form:"subListen"`
-	SubPort          int    `json:"subPort" form:"subPort"`
-	SubPath          string `json:"subPath" form:"subPath"`
-	SubDomain        string `json:"subDomain" form:"subDomain"`
-	SubCertFile      string `json:"subCertFile" form:"subCertFile"`
-	SubKeyFile       string `json:"subKeyFile" form:"subKeyFile"`
-	SubUpdates       int    `json:"subUpdates" form:"subUpdates"`
-	SubEncrypt       bool   `json:"subEncrypt" form:"subEncrypt"`
-	SubShowInfo      bool   `json:"subShowInfo" form:"subShowInfo"`
-	SubURI           string `json:"subURI" form:"subURI"`
-	SubJsonPath      string `json:"subJsonPath" form:"subJsonPath"`
-	SubJsonURI       string `json:"subJsonURI" form:"subJsonURI"`
-	SubJsonFragment  string `json:"subJsonFragment" form:"subJsonFragment"`
-	SubJsonNoises    string `json:"subJsonNoises" form:"subJsonNoises"`
-	SubJsonMux       string `json:"subJsonMux" form:"subJsonMux"`
-	SubJsonRules     string `json:"subJsonRules" form:"subJsonRules"`
-	Datepicker       string `json:"datepicker" form:"datepicker"`
+	WebListen                   string `json:"webListen" form:"webListen"`
+	WebDomain                   string `json:"webDomain" form:"webDomain"`
+	WebPort                     int    `json:"webPort" form:"webPort"`
+	WebCertFile                 string `json:"webCertFile" form:"webCertFile"`
+	WebKeyFile                  string `json:"webKeyFile" form:"webKeyFile"`
+	WebBasePath                 string `json:"webBasePath" form:"webBasePath"`
+	SessionMaxAge               int    `json:"sessionMaxAge" form:"sessionMaxAge"`
+	PageSize                    int    `json:"pageSize" form:"pageSize"`
+	ExpireDiff                  int    `json:"expireDiff" form:"expireDiff"`
+	TrafficDiff                 int    `json:"trafficDiff" form:"trafficDiff"`
+	RemarkModel                 string `json:"remarkModel" form:"remarkModel"`
+	TgBotEnable                 bool   `json:"tgBotEnable" form:"tgBotEnable"`
+	TgBotToken                  string `json:"tgBotToken" form:"tgBotToken"`
+	TgBotProxy                  string `json:"tgBotProxy" form:"tgBotProxy"`
+	TgBotAPIServer              string `json:"tgBotAPIServer" form:"tgBotAPIServer"`
+	TgBotChatId                 string `json:"tgBotChatId" form:"tgBotChatId"`
+	TgRunTime                   string `json:"tgRunTime" form:"tgRunTime"`
+	TgBotBackup                 bool   `json:"tgBotBackup" form:"tgBotBackup"`
+	TgBotLoginNotify            bool   `json:"tgBotLoginNotify" form:"tgBotLoginNotify"`
+	TgCpu                       int    `json:"tgCpu" form:"tgCpu"`
+	TgLang                      string `json:"tgLang" form:"tgLang"`
+	TimeLocation                string `json:"timeLocation" form:"timeLocation"`
+	SecretEnable                bool   `json:"secretEnable" form:"secretEnable"`
+	SubEnable                   bool   `json:"subEnable" form:"subEnable"`
+	SubListen                   string `json:"subListen" form:"subListen"`
+	SubPort                     int    `json:"subPort" form:"subPort"`
+	SubPath                     string `json:"subPath" form:"subPath"`
+	SubDomain                   string `json:"subDomain" form:"subDomain"`
+	SubCertFile                 string `json:"subCertFile" form:"subCertFile"`
+	SubKeyFile                  string `json:"subKeyFile" form:"subKeyFile"`
+	SubUpdates                  int    `json:"subUpdates" form:"subUpdates"`
+	ExternalTrafficInformEnable bool   `json:"externalTrafficInformEnable" form:"externalTrafficInformEnable"`
+	ExternalTrafficInformURI    string `json:"externalTrafficInformURI" form:"externalTrafficInformURI"`
+	SubEncrypt                  bool   `json:"subEncrypt" form:"subEncrypt"`
+	SubShowInfo                 bool   `json:"subShowInfo" form:"subShowInfo"`
+	SubURI                      string `json:"subURI" form:"subURI"`
+	SubJsonPath                 string `json:"subJsonPath" form:"subJsonPath"`
+	SubJsonURI                  string `json:"subJsonURI" form:"subJsonURI"`
+	SubJsonFragment             string `json:"subJsonFragment" form:"subJsonFragment"`
+	SubJsonNoises               string `json:"subJsonNoises" form:"subJsonNoises"`
+	SubJsonMux                  string `json:"subJsonMux" form:"subJsonMux"`
+	SubJsonRules                string `json:"subJsonRules" form:"subJsonRules"`
+	Datepicker                  string `json:"datepicker" form:"datepicker"`
 }
 
 func (s *AllSetting) CheckValid() error {

+ 2 - 0
web/html/xui/settings.html

@@ -142,6 +142,8 @@
                   <setting-list-item type="number" title='{{ i18n "pages.settings.pageSize" }}' desc='{{ i18n "pages.settings.pageSizeDesc" }}' v-model="allSetting.pageSize" :min="0" :step="5"></setting-list-item>
                   <setting-list-item type="number" title='{{ i18n "pages.settings.expireTimeDiff" }}' desc='{{ i18n "pages.settings.expireTimeDiffDesc" }}' v-model="allSetting.expireDiff" :min="0"></setting-list-item>
                   <setting-list-item type="number" title='{{ i18n "pages.settings.trafficDiff" }}' desc='{{ i18n "pages.settings.trafficDiffDesc" }}' v-model="allSetting.trafficDiff" :min="0"></setting-list-item>
+                  <setting-list-item type="switch" title='{{ i18n "pages.settings.externalTrafficInformEnable"}}' desc='{{ i18n "pages.settings.externalTrafficInformEnableDesc"}}' v-model="allSetting.externalTrafficInformEnable"></setting-list-item>
+                  <setting-list-item type="text" title='{{ i18n "pages.settings.externalTrafficInformURI"}}' desc='{{ i18n "pages.settings.externalTrafficInformURIDesc"}}' v-model="allSetting.externalTrafficInformURI" placeholder="(http|https)://domain[:port]/path/"></setting-list-item>
                   <setting-list-item type="text" title='{{ i18n "pages.settings.timeZone"}}' desc='{{ i18n "pages.settings.timeZoneDesc"}}' v-model="allSetting.timeLocation"></setting-list-item>
                   <a-list-item>
                     <a-row style="padding: 20px">

+ 34 - 0
web/job/xray_traffic_job.go

@@ -1,11 +1,16 @@
 package job
 
 import (
+	"encoding/json"
 	"x-ui/logger"
 	"x-ui/web/service"
+	"x-ui/xray"
+
+	"github.com/valyala/fasthttp"
 )
 
 type XrayTrafficJob struct {
+	settingService  service.SettingService
 	xrayService     service.XrayService
 	inboundService  service.InboundService
 	outboundService service.OutboundService
@@ -31,7 +36,36 @@ func (j *XrayTrafficJob) Run() {
 	if err != nil {
 		logger.Warning("add outbound traffic failed:", err)
 	}
+	if ExternalTrafficInformEnable, err := j.settingService.GetExternalTrafficInformEnable(); ExternalTrafficInformEnable {
+		j.informTrafficToExternalAPI(traffics, clientTraffics)
+	} else if err != nil {
+		logger.Warning("get ExternalTrafficInformEnable failed:", err)
+	}
 	if needRestart0 || needRestart1 {
 		j.xrayService.SetToNeedRestart()
 	}
 }
+
+func (j *XrayTrafficJob) informTrafficToExternalAPI(inboundTraffics []*xray.Traffic, clientTraffics []*xray.ClientTraffic) {
+	informURL, err := j.settingService.GetExternalTrafficInformURI()
+	if err != nil {
+		logger.Warning("get ExternalTrafficInformURI failed:", err)
+		return
+	}
+	requestBody, err := json.Marshal(map[string]interface{}{"clientTraffics": clientTraffics, "inboundTraffics": inboundTraffics})
+	if err != nil {
+		logger.Warning("parse client/inbound traffic failed:", err)
+		return
+	}
+	request := fasthttp.AcquireRequest()
+	defer fasthttp.ReleaseRequest(request)
+	request.Header.SetMethod("POST")
+	request.Header.SetContentType("application/json; charset=UTF-8")
+	request.SetBody([]byte(requestBody))
+	request.SetRequestURI(informURL)
+	response := fasthttp.AcquireResponse()
+	defer fasthttp.ReleaseResponse(response)
+	if err := fasthttp.Do(request, response); err != nil {
+		logger.Warning("POST ExternalTrafficInformURI failed:", err)
+	}
+}

+ 16 - 0
web/service/setting.go

@@ -496,6 +496,22 @@ func (s *SettingService) SetWarp(data string) error {
 	return s.setString("warp", data)
 }
 
+func (s *SettingService) GetExternalTrafficInformEnable() (bool, error) {
+	return s.getBool("externalTrafficInformEnable")
+}
+
+func (s *SettingService) SetExternalTrafficInformEnable(value bool) error {
+	return s.setBool("externalTrafficInformEnable", value)
+}
+
+func (s *SettingService) GetExternalTrafficInformURI() (string, error) {
+	return s.getString("externalTrafficInformURI")
+}
+
+func (s *SettingService) SetExternalTrafficInformURI(InformURI string) error {
+	return s.setString("externalTrafficInformURI", InformURI)
+}
+
 func (s *SettingService) GetIpLimitEnable() (bool, error) {
 	accessLogPath, err := xray.GetAccessLogPath()
 	if err != nil {

+ 4 - 0
web/translation/translate.en_US.toml

@@ -307,6 +307,10 @@
 "subShowInfoDesc" = "The remaining traffic and date will be displayed in the client apps."
 "subURI" = "Reverse Proxy URI"
 "subURIDesc" = "The URI path of the subscription URL for use behind proxies."
+"externalTrafficInformEnable" = "External Traffic Inform"
+"externalTrafficInformEnableDesc" = "Inform external API on every traffic update."
+"externalTrafficInformURI" = "External Traffic Inform URI"
+"externalTrafficInformURIDesc" = "Traffic updates are sent to this URI."
 "fragment" = "Fragmentation"
 "fragmentDesc" = "Enable fragmentation for TLS hello packet."
 "fragmentSett" = "Fragmentation Settings"

+ 4 - 0
web/translation/translate.es_ES.toml

@@ -306,6 +306,10 @@
 "subShowInfo" = "Mostrar información de uso"
 "subShowInfoDesc" = "Mostrar tráfico restante y fecha después del nombre de configuración."
 "subURI" = "URI de proxy inverso"
+"externalTrafficInformEnable" = "Informe de tráfico externo"
+"externalTrafficInformEnableDesc" = "Informar a la API externa sobre cada actualización de tráfico."
+"externalTrafficInformURI" = "URI de información de tráfico externo"
+"externalTrafficInformURIDesc" = "Las actualizaciones de tráfico se envían a este URI."
 "subURIDesc" = "Cambiar el URI base de la URL de suscripción para usar detrás de los servidores proxy"
 "fragment" = "Fragmentación"
 "fragmentDesc" = "Habilitar la fragmentación para el paquete de saludo de TLS"

+ 4 - 0
web/translation/translate.fa_IR.toml

@@ -301,6 +301,10 @@
 "subDomainDesc" = "آدرس دامنه برای سرویس سابسکریپشن. برای گوش دادن به تمام دامنه‌ها و آی‌پی‌ها خالی‌بگذارید‌"
 "subUpdates" = "فاصله بروزرسانی‌ سابسکریپشن"
 "subUpdatesDesc" = "(فاصله مابین بروزرسانی در برنامه‌های کاربری. (واحد: ساعت"
+"externalTrafficInformEnable" = "اطلاع رسانی خارجی مصرف ترافیک"
+"externalTrafficInformEnableDesc" = "مصرف ترافیک به سرویس خارجی ارسال می شود"
+"externalTrafficInformURI" = "لینک اطلاع رسانی خارجی مصرف ترافیک"
+"externalTrafficInformURIDesc" = "ترافیک های مصرفی به این لینک هم ارسال می شود"
 "subEncrypt" = "کدگذاری"
 "subEncryptDesc" = "کدگذاری خواهدشد Base64 محتوای برگشتی سرویس سابسکریپشن برپایه"
 "subShowInfo" = "نمایش اطلاعات مصرف"

+ 4 - 1
web/translation/translate.id_ID.toml

@@ -306,7 +306,10 @@
 "subShowInfo" = "Tampilkan Info Penggunaan"
 "subShowInfoDesc" = "Sisa traffic dan tanggal akan ditampilkan di aplikasi klien."
 "subURI" = "URI Proxy Terbalik"
-"subURIDesc" = "URI path URL langganan untuk penggunaan di belakang proxy."
+"externalTrafficInformEnable" = "Informasikan API eksternal pada setiap pembaruan lalu lintas."
+"externalTrafficInformEnableDesc" = "Inform external API on every traffic update."
+"externalTrafficInformURI" = "Lalu Lintas Eksternal Menginformasikan URI"
+"externalTrafficInformURIDesc" = "Pembaruan lalu lintas dikirim ke URI ini."
 "fragment" = "Fragmentasi"
 "fragmentDesc" = "Aktifkan fragmentasi untuk paket hello TLS"
 "fragmentSett" = "Pengaturan Fragmentasi"

+ 4 - 0
web/translation/translate.ja_JP.toml

@@ -307,6 +307,10 @@
 "subShowInfoDesc" = "クライアントアプリで残りのトラフィックと日付情報を表示する"
 "subURI" = "リバースプロキシURI"
 "subURIDesc" = "プロキシ後ろのサブスクリプションURLのURIパスに使用する"
+"externalTrafficInformEnable" = "外部トラフィック情報"
+"externalTrafficInformEnableDesc" = "トラフィックの更新ごとに外部 API に通知します。"
+"externalTrafficInformURI" = "外部トラフィック通知 URI"
+"externalTrafficInformURIDesc" = "トラフィックの更新ごとに外部 API に通知します。"
 "fragment" = "フラグメント"
 "fragmentDesc" = "TLS helloパケットのフラグメントを有効にする"
 "fragmentSett" = "設定"

+ 4 - 0
web/translation/translate.pt_BR.toml

@@ -307,6 +307,10 @@
 "subShowInfoDesc" = "O tráfego restante e a data serão exibidos nos aplicativos de cliente."
 "subURI" = "URI de Proxy Reverso"
 "subURIDesc" = "O caminho URI da URL de assinatura para uso por trás de proxies."
+"externalTrafficInformEnable" = "Informações de tráfego externo"
+"externalTrafficInformEnableDesc" = "Informar a API externa sobre cada atualização de tráfego."
+"externalTrafficInformURI" = "URI de informação de tráfego externo"
+"externalTrafficInformURIDesc" = "As atualizações de tráfego são enviadas para este URI."
 "fragment" = "Fragmentação"
 "fragmentDesc" = "Ativa a fragmentação para o pacote TLS hello."
 "fragmentSett" = "Configurações de Fragmentação"

+ 4 - 0
web/translation/translate.ru_RU.toml

@@ -307,6 +307,10 @@
 "subShowInfoDesc" = "Показывать оставшиеся трафик и дату после имени конфигурации"
 "subURI" = "URI обратного прокси"
 "subURIDesc" = "Изменить базовый URI URL-адреса подписки для использования за прокси-серверами"
+"externalTrafficInformEnable" = "Информация о внешнем трафике"
+"externalTrafficInformEnableDesc" = "Информировать внешний API о каждом обновлении трафика"
+"externalTrafficInformURI" = "URI информации о внешнем трафике"
+"externalTrafficInformURIDesc" = "Обновления трафика отправляются на этот URI"
 "fragment" = "Фрагментация"
 "fragmentDesc" = "Включить фрагментацию для пакета приветствия TLS"
 "fragmentSett" = "Настройки фрагментации"

+ 4 - 0
web/translation/translate.tr_TR.toml

@@ -307,6 +307,10 @@
 "subShowInfoDesc" = "Kalan trafik ve tarih müşteri uygulamalarında görüntülenir."
 "subURI" = "Ters Proxy URI"
 "subURIDesc" = "Proxy arkasında kullanılacak abonelik URL'sinin URI yolu."
+"externalTrafficInformEnable" = "Harici Trafik Bilgisi"
+"externalTrafficInformEnableDesc" = "Her trafik güncellemesinde harici API'yi bilgilendirin."
+"externalTrafficInformURI" = "Harici Trafik Bilgisi URI'si"
+"externalTrafficInformURIDesc" = "Trafik güncellemeleri bu URI'ye gönderildi."
 "fragment" = "Parçalama"
 "fragmentDesc" = "TLS merhaba paketinin parçalanmasını etkinleştir."
 "fragmentSett" = "Parçalama Ayarları"

+ 4 - 0
web/translation/translate.uk_UA.toml

@@ -307,6 +307,10 @@
 "subShowInfoDesc" = "Залишок трафіку та дата відображатимуться в клієнтських програмах."
 "subURI" = "URI зворотного проксі"
 "subURIDesc" = "URI до URL-адреси підписки для використання за проксі."
+"externalTrafficInformEnable" = "Інформація про зовнішній трафік"
+"externalTrafficInformEnableDesc" = "Інформувати зовнішній API про кожне оновлення трафіку."
+"externalTrafficInformURI" = "Інформаційний URI зовнішнього трафіку"
+"externalTrafficInformURIDesc" = "Оновлення трафіку надсилаються на цей URI."
 "fragment" = "Фрагментація"
 "fragmentDesc" = "Увімкнути фрагментацію для пакету привітання TLS"
 "fragmentSett" = "Параметри фрагментації"

+ 4 - 0
web/translation/translate.vi_VN.toml

@@ -307,6 +307,10 @@
 "subShowInfoDesc" = "Hiển thị lưu lượng truy cập còn lại và ngày sau tên cấu hình"
 "subURI" = "URI proxy trung gian"
 "subURIDesc" = "Thay đổi URI cơ sở của URL gói đăng ký để sử dụng cho proxy trung gian"
+"externalTrafficInformEnable" = "Thông báo giao thông bên ngoài"
+"externalTrafficInformEnableDesc" = "Thông báo cho API bên ngoài về mọi cập nhật lưu lượng truy cập."
+"externalTrafficInformURI" = "URI thông báo lưu lượng truy cập bên ngoài"
+"externalTrafficInformURIDesc" = "Cập nhật lưu lượng truy cập được gửi tới URI này."
 "fragment" = "Sự phân mảnh"
 "fragmentDesc" = "Kích hoạt phân mảnh cho gói TLS hello"
 "fragmentSett" = "Cài đặt phân mảnh"

+ 4 - 0
web/translation/translate.zh_CN.toml

@@ -307,6 +307,10 @@
 "subShowInfoDesc" = "客户端应用中将显示剩余流量和日期信息"
 "subURI" = "反向代理 URI"
 "subURIDesc" = "用于代理后面的订阅 URL 的 URI 路径"
+"externalTrafficInformEnable" = "外部交通通知"
+"externalTrafficInformEnableDesc" = "每次流量更新时通知外部 API"
+"externalTrafficInformURI" = "外部流量通知 URI"
+"externalTrafficInformURIDesc" = "流量更新将发送到此 URI"
 "fragment" = "分片"
 "fragmentDesc" = "启用 TLS hello 数据包分片"
 "fragmentSett" = "设置"

+ 4 - 0
web/translation/translate.zh_TW.toml

@@ -307,6 +307,10 @@
 "subShowInfoDesc" = "客戶端應用中將顯示剩餘流量和日期資訊"
 "subURI" = "反向代理 URI"
 "subURIDesc" = "用於代理後面的訂閱 URL 的 URI 路徑"
+"externalTrafficInformEnable" = "外部交通通知"
+"externalTrafficInformEnableDesc" = "每次流量更新時通知外部 API"
+"externalTrafficInformURI" = "外部流量通知 URI"
+"externalTrafficInformURIDesc" = "流量更新將會傳送到此 URI"
 "fragment" = "分片"
 "fragmentDesc" = "啟用 TLS hello 資料包分片"
 "fragmentSett" = "設定"