mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-08-23 03:16:52 +00:00
Merge branch 'xray_logs_viewer' of https://github.com/fgsfds/3x-ui into xray_logs_viewer
This commit is contained in:
commit
369d75bece
23 changed files with 131 additions and 15 deletions
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
|
@ -7,7 +7,7 @@ on:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
|
@ -31,7 +31,7 @@ jobs:
|
|||
- 386
|
||||
- armv5
|
||||
- s390x
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
|
|
@ -560,6 +560,8 @@ class TlsStreamSettings extends XrayCommonClass {
|
|||
enableSessionResumption = false,
|
||||
certificates = [new TlsStreamSettings.Cert()],
|
||||
alpn = [ALPN_OPTION.H2, ALPN_OPTION.HTTP1],
|
||||
echServerKeys = '',
|
||||
echForceQuery = 'none',
|
||||
settings = new TlsStreamSettings.Settings()
|
||||
) {
|
||||
super();
|
||||
|
@ -573,6 +575,8 @@ class TlsStreamSettings extends XrayCommonClass {
|
|||
this.enableSessionResumption = enableSessionResumption;
|
||||
this.certs = certificates;
|
||||
this.alpn = alpn;
|
||||
this.echServerKeys = echServerKeys;
|
||||
this.echForceQuery = echForceQuery;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
|
@ -592,7 +596,7 @@ class TlsStreamSettings extends XrayCommonClass {
|
|||
}
|
||||
|
||||
if (!ObjectUtil.isEmpty(json.settings)) {
|
||||
settings = new TlsStreamSettings.Settings(json.settings.allowInsecure, json.settings.fingerprint, json.settings.serverName, json.settings.domains);
|
||||
settings = new TlsStreamSettings.Settings(json.settings.allowInsecure, json.settings.fingerprint, json.settings.echConfigList);
|
||||
}
|
||||
return new TlsStreamSettings(
|
||||
json.serverName,
|
||||
|
@ -605,6 +609,8 @@ class TlsStreamSettings extends XrayCommonClass {
|
|||
json.enableSessionResumption,
|
||||
certs,
|
||||
json.alpn,
|
||||
json.echServerKeys,
|
||||
json.echForceQuery,
|
||||
settings,
|
||||
);
|
||||
}
|
||||
|
@ -621,6 +627,8 @@ class TlsStreamSettings extends XrayCommonClass {
|
|||
enableSessionResumption: this.enableSessionResumption,
|
||||
certificates: TlsStreamSettings.toJsonArray(this.certs),
|
||||
alpn: this.alpn,
|
||||
echServerKeys: this.echServerKeys,
|
||||
echForceQuery: this.echForceQuery,
|
||||
settings: this.settings,
|
||||
};
|
||||
}
|
||||
|
@ -701,21 +709,25 @@ TlsStreamSettings.Settings = class extends XrayCommonClass {
|
|||
constructor(
|
||||
allowInsecure = false,
|
||||
fingerprint = UTLS_FINGERPRINT.UTLS_CHROME,
|
||||
echConfigList = '',
|
||||
) {
|
||||
super();
|
||||
this.allowInsecure = allowInsecure;
|
||||
this.fingerprint = fingerprint;
|
||||
this.echConfigList = echConfigList;
|
||||
}
|
||||
static fromJson(json = {}) {
|
||||
return new TlsStreamSettings.Settings(
|
||||
json.allowInsecure,
|
||||
json.fingerprint,
|
||||
json.echConfigList,
|
||||
);
|
||||
}
|
||||
toJson() {
|
||||
return {
|
||||
allowInsecure: this.allowInsecure,
|
||||
fingerprint: this.fingerprint,
|
||||
echConfigList: this.echConfigList
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -1375,6 +1387,9 @@ class Inbound extends XrayCommonClass {
|
|||
if (!ObjectUtil.isEmpty(this.stream.tls.sni)) {
|
||||
params.set("sni", this.stream.tls.sni);
|
||||
}
|
||||
if (this.stream.tls.settings.echConfigList?.length > 0) {
|
||||
params.set("ech", this.stream.tls.settings.echConfigList);
|
||||
}
|
||||
if (type == "tcp" && !ObjectUtil.isEmpty(flow)) {
|
||||
params.set("flow", flow);
|
||||
}
|
||||
|
@ -1474,6 +1489,9 @@ class Inbound extends XrayCommonClass {
|
|||
if (this.stream.tls.settings.allowInsecure) {
|
||||
params.set("allowInsecure", "1");
|
||||
}
|
||||
if (this.stream.tls.settings.echConfigList?.length > 0) {
|
||||
params.set("ech", this.stream.tls.settings.echConfigList);
|
||||
}
|
||||
if (!ObjectUtil.isEmpty(this.stream.tls.sni)) {
|
||||
params.set("sni", this.stream.tls.sni);
|
||||
}
|
||||
|
@ -1552,6 +1570,9 @@ class Inbound extends XrayCommonClass {
|
|||
if (this.stream.tls.settings.allowInsecure) {
|
||||
params.set("allowInsecure", "1");
|
||||
}
|
||||
if (this.stream.tls.settings.echConfigList?.length > 0) {
|
||||
params.set("ech", this.stream.tls.settings.echConfigList);
|
||||
}
|
||||
if (!ObjectUtil.isEmpty(this.stream.tls.sni)) {
|
||||
params.set("sni", this.stream.tls.sni);
|
||||
}
|
||||
|
@ -2291,12 +2312,14 @@ Inbound.DokodemoSettings = class extends Inbound.Settings {
|
|||
protocol,
|
||||
address,
|
||||
port,
|
||||
portMap = [],
|
||||
network = 'tcp,udp',
|
||||
followRedirect = false
|
||||
) {
|
||||
super(protocol);
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
this.portMap = portMap;
|
||||
this.network = network;
|
||||
this.followRedirect = followRedirect;
|
||||
}
|
||||
|
@ -2306,6 +2329,7 @@ Inbound.DokodemoSettings = class extends Inbound.Settings {
|
|||
Protocols.DOKODEMO,
|
||||
json.address,
|
||||
json.port,
|
||||
XrayCommonClass.toHeaders(json.portMap),
|
||||
json.network,
|
||||
json.followRedirect,
|
||||
);
|
||||
|
@ -2315,6 +2339,7 @@ Inbound.DokodemoSettings = class extends Inbound.Settings {
|
|||
return {
|
||||
address: this.address,
|
||||
port: this.port,
|
||||
portMap: XrayCommonClass.toV2Headers(this.portMap, false),
|
||||
network: this.network,
|
||||
followRedirect: this.followRedirect,
|
||||
};
|
||||
|
|
|
@ -354,13 +354,15 @@ class TlsStreamSettings extends CommonClass {
|
|||
serverName = '',
|
||||
alpn = [],
|
||||
fingerprint = '',
|
||||
allowInsecure = false
|
||||
allowInsecure = false,
|
||||
echConfigList = '',
|
||||
) {
|
||||
super();
|
||||
this.serverName = serverName;
|
||||
this.alpn = alpn;
|
||||
this.fingerprint = fingerprint;
|
||||
this.allowInsecure = allowInsecure;
|
||||
this.echConfigList = echConfigList;
|
||||
}
|
||||
|
||||
static fromJson(json = {}) {
|
||||
|
@ -369,6 +371,7 @@ class TlsStreamSettings extends CommonClass {
|
|||
json.alpn,
|
||||
json.fingerprint,
|
||||
json.allowInsecure,
|
||||
json.echConfigList,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -378,6 +381,7 @@ class TlsStreamSettings extends CommonClass {
|
|||
alpn: this.alpn,
|
||||
fingerprint: this.fingerprint,
|
||||
allowInsecure: this.allowInsecure,
|
||||
echConfigList: this.echConfigList
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -782,7 +786,8 @@ class Outbound extends CommonClass {
|
|||
let alpn = url.searchParams.get('alpn');
|
||||
let allowInsecure = url.searchParams.get('allowInsecure');
|
||||
let sni = url.searchParams.get('sni') ?? '';
|
||||
stream.tls = new TlsStreamSettings(sni, alpn ? alpn.split(',') : [], fp, allowInsecure == 1);
|
||||
let ech = url.searchParams.get('ech') ?? '';
|
||||
stream.tls = new TlsStreamSettings(sni, alpn ? alpn.split(',') : [], fp, allowInsecure == 1, ech);
|
||||
}
|
||||
|
||||
if (security == 'reality') {
|
||||
|
|
|
@ -108,8 +108,8 @@ func (a *InboundController) addInbound(c *gin.Context) {
|
|||
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
|
||||
return
|
||||
}
|
||||
jsonMsgObj(c, I18nWeb(c, "pages.inbounds.toasts.inboundCreateSuccess"), inbound, err)
|
||||
if err == nil && needRestart {
|
||||
jsonMsgObj(c, I18nWeb(c, "pages.inbounds.toasts.inboundCreateSuccess"), inbound, nil)
|
||||
if needRestart {
|
||||
a.xrayService.SetToNeedRestart()
|
||||
}
|
||||
}
|
||||
|
@ -126,8 +126,8 @@ func (a *InboundController) delInbound(c *gin.Context) {
|
|||
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
|
||||
return
|
||||
}
|
||||
jsonMsgObj(c, I18nWeb(c, "pages.inbounds.toasts.inboundDeleteSuccess"), id, err)
|
||||
if err == nil && needRestart {
|
||||
jsonMsgObj(c, I18nWeb(c, "pages.inbounds.toasts.inboundDeleteSuccess"), id, nil)
|
||||
if needRestart {
|
||||
a.xrayService.SetToNeedRestart()
|
||||
}
|
||||
}
|
||||
|
@ -152,8 +152,8 @@ func (a *InboundController) updateInbound(c *gin.Context) {
|
|||
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
|
||||
return
|
||||
}
|
||||
jsonMsgObj(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), inbound, err)
|
||||
if err == nil && needRestart {
|
||||
jsonMsgObj(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), inbound, nil)
|
||||
if needRestart {
|
||||
a.xrayService.SetToNeedRestart()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) {
|
|||
g.POST("/importDB", a.importDB)
|
||||
g.POST("/getNewX25519Cert", a.getNewX25519Cert)
|
||||
g.POST("/getNewmldsa65", a.getNewmldsa65)
|
||||
g.POST("/getNewEchCert", a.getNewEchCert)
|
||||
}
|
||||
|
||||
func (a *ServerController) refreshStatus() {
|
||||
|
@ -215,3 +216,13 @@ func (a *ServerController) getNewmldsa65(c *gin.Context) {
|
|||
}
|
||||
jsonObj(c, cert, nil)
|
||||
}
|
||||
|
||||
func (a *ServerController) getNewEchCert(c *gin.Context) {
|
||||
sni := c.PostForm("sni")
|
||||
cert, err := a.serverService.GetNewEchCert(sni)
|
||||
if err != nil {
|
||||
jsonMsg(c, "get ech certificate", err)
|
||||
return
|
||||
}
|
||||
jsonObj(c, cert, nil)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,19 @@
|
|||
<a-form-item label='{{ i18n "pages.inbounds.destinationPort"}}'>
|
||||
<a-input-number v-model.number="inbound.settings.port"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.portMap"}}'>
|
||||
<a-button size="small" @click="inbound.settings.portMap.push({name: '', value: ''})">+</a-button>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span:24}">
|
||||
<a-input-group compact v-for="(pm, index) in inbound.settings.portMap">
|
||||
<a-input style="width: 50%" v-model.trim="pm.name" placeholder='{{ i18n "pages.inbounds.port"}}'>
|
||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
||||
</a-input>
|
||||
<a-input style="width: 50%" v-model.trim="pm.value" placeholder='{{ i18n "pages.inbounds.targetAddress" }}'>
|
||||
<a-button slot="addonAfter" size="small" @click="inbound.settings.portMap.splice(index,1)">-</a-button>
|
||||
</a-input>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.network"}}'>
|
||||
<a-select v-model="inbound.settings.network" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="tcp,udp">TCP,UDP</a-select-option>
|
||||
|
|
|
@ -106,6 +106,21 @@
|
|||
<a-switch v-model="cert.buildChain"></a-switch>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<a-form-item label='ECH key'>
|
||||
<a-input v-model="inbound.stream.tls.echServerKeys"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='ECH config'>
|
||||
<a-input v-model="inbound.stream.tls.settings.echConfigList"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='ECH force query'>
|
||||
<a-select v-model="inbound.stream.tls.echForceQuery"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in ['none', 'half', 'full']" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label=" ">
|
||||
<a-button type="primary" icon="import" @click="getNewEchCert">Get New ECH Cert</a-button>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
<!-- reality settings -->
|
||||
|
|
|
@ -152,6 +152,16 @@
|
|||
inModal.inbound.stream.reality.mldsa65Seed = msg.obj.seed;
|
||||
inModal.inbound.stream.reality.settings.mldsa65Verify = msg.obj.verify;
|
||||
},
|
||||
async getNewEchCert() {
|
||||
inModal.loading(true);
|
||||
const msg = await HttpUtil.post('/server/getNewEchCert', {sni: inModal.inbound.stream.tls.sni});
|
||||
inModal.loading(false);
|
||||
if (!msg.success) {
|
||||
return;
|
||||
}
|
||||
inModal.inbound.stream.tls.echServerKeys = msg.obj.echServerKeys;
|
||||
inModal.inbound.stream.tls.settings.echConfigList = msg.obj.echConfigList;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -775,3 +775,27 @@ func (s *ServerService) GetNewmldsa65() (any, error) {
|
|||
|
||||
return keyPair, nil
|
||||
}
|
||||
|
||||
func (s *ServerService) GetNewEchCert(sni string) (interface{}, error) {
|
||||
// Run the command
|
||||
cmd := exec.Command(xray.GetBinaryPath(), "tls", "ech", "--serverName", sni)
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lines := strings.Split(out.String(), "\n")
|
||||
if len(lines) < 4 {
|
||||
return nil, common.NewError("invalid ech cert")
|
||||
}
|
||||
|
||||
configList := lines[1]
|
||||
serverKeys := lines[3]
|
||||
|
||||
return map[string]interface{}{
|
||||
"echServerKeys": serverKeys,
|
||||
"echConfigList": configList,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -158,6 +158,7 @@
|
|||
"remark" = "ملاحظة"
|
||||
"protocol" = "بروتوكول"
|
||||
"port" = "بورت"
|
||||
"portMap" = "خريطة البورت"
|
||||
"traffic" = "الترافيك"
|
||||
"details" = "تفاصيل"
|
||||
"transportConfig" = "نقل"
|
||||
|
|
|
@ -158,6 +158,7 @@
|
|||
"remark" = "Remark"
|
||||
"protocol" = "Protocol"
|
||||
"port" = "Port"
|
||||
"portMap" = "Port Mapping"
|
||||
"traffic" = "Traffic"
|
||||
"details" = "Details"
|
||||
"transportConfig" = "Transport"
|
||||
|
|
|
@ -158,6 +158,7 @@
|
|||
"remark" = "Notas"
|
||||
"protocol" = "Protocolo"
|
||||
"port" = "Puerto"
|
||||
"portMap" = "Puertos de Destino"
|
||||
"traffic" = "Tráfico"
|
||||
"details" = "Detalles"
|
||||
"transportConfig" = "Transporte"
|
||||
|
|
|
@ -158,6 +158,7 @@
|
|||
"remark" = "نام"
|
||||
"protocol" = "پروتکل"
|
||||
"port" = "پورت"
|
||||
"portMap" = "پورتهای نظیر"
|
||||
"traffic" = "ترافیک"
|
||||
"details" = "توضیحات"
|
||||
"transportConfig" = "نحوه اتصال"
|
||||
|
|
|
@ -158,6 +158,7 @@
|
|||
"remark" = "Catatan"
|
||||
"protocol" = "Protokol"
|
||||
"port" = "Port"
|
||||
"portMap" = "Port Mapping"
|
||||
"traffic" = "Traffic"
|
||||
"details" = "Rincian"
|
||||
"transportConfig" = "Transport"
|
||||
|
|
|
@ -158,6 +158,7 @@
|
|||
"remark" = "備考"
|
||||
"protocol" = "プロトコル"
|
||||
"port" = "ポート"
|
||||
"portMap" = "ポートマッピング"
|
||||
"traffic" = "トラフィック"
|
||||
"details" = "詳細情報"
|
||||
"transportConfig" = "トランスポート設定"
|
||||
|
|
|
@ -158,6 +158,7 @@
|
|||
"remark" = "Observação"
|
||||
"protocol" = "Protocolo"
|
||||
"port" = "Porta"
|
||||
"portMap" = "Porta Mapeada"
|
||||
"traffic" = "Tráfego"
|
||||
"details" = "Detalhes"
|
||||
"transportConfig" = "Transporte"
|
||||
|
|
|
@ -158,6 +158,7 @@
|
|||
"remark" = "Примечание"
|
||||
"protocol" = "Протокол"
|
||||
"port" = "Порт"
|
||||
"portMap" = "Порт-маппинг"
|
||||
"traffic" = "Трафик"
|
||||
"details" = "Подробнее"
|
||||
"transportConfig" = "Транспорт"
|
||||
|
|
|
@ -158,6 +158,7 @@
|
|||
"remark" = "Açıklama"
|
||||
"protocol" = "Protokol"
|
||||
"port" = "Port"
|
||||
"portMap" = "Port Atama"
|
||||
"traffic" = "Trafik"
|
||||
"details" = "Detaylar"
|
||||
"transportConfig" = "Taşıma"
|
||||
|
|
|
@ -158,6 +158,7 @@
|
|||
"remark" = "Примітка"
|
||||
"protocol" = "Протокол"
|
||||
"port" = "Порт"
|
||||
"portMap" = "Порт-перехід"
|
||||
"traffic" = "Трафік"
|
||||
"details" = "Деталі"
|
||||
"transportConfig" = "Транспорт"
|
||||
|
|
|
@ -158,6 +158,7 @@
|
|||
"remark" = "Chú thích"
|
||||
"protocol" = "Giao thức"
|
||||
"port" = "Cổng"
|
||||
"portMap" = "Cổng tạo"
|
||||
"traffic" = "Lưu lượng"
|
||||
"details" = "Chi tiết"
|
||||
"transportConfig" = "Giao vận"
|
||||
|
|
|
@ -158,6 +158,7 @@
|
|||
"remark" = "备注"
|
||||
"protocol" = "协议"
|
||||
"port" = "端口"
|
||||
"portMap" = "端口映射"
|
||||
"traffic" = "流量"
|
||||
"details" = "详细信息"
|
||||
"transportConfig" = "传输配置"
|
||||
|
|
|
@ -158,6 +158,7 @@
|
|||
"remark" = "備註"
|
||||
"protocol" = "協議"
|
||||
"port" = "埠"
|
||||
"portMap" = "埠映射"
|
||||
"traffic" = "流量"
|
||||
"details" = "詳細資訊"
|
||||
"transportConfig" = "傳輸配置"
|
||||
|
|
Loading…
Reference in a new issue