mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-05-13 09:36:05 +00:00
fix(xray): align DNS outbound to spec and repair item-list rules UI
DNS outbound now mirrors xray-core's documented shape: rewriteNetwork / rewriteAddress / rewritePort / userLevel replace the legacy network / address / port keys, and unset values are dropped on the wire. Old configs are still accepted on read so saved configs migrate cleanly. While there, fix two latent bugs in repeat-item editors (DNS rules, Freedom noise, WireGuard peers): - The "+" buttons pushed plain objects into arrays of class instances, so toJson() crashed on the next read and the JSON tab silently went blank. Push proper class instances instead. - Each item heading lived outside any a-form-item, so the delete icon ignored the form's column grid and slumped left. Wrap the heading in a form-item with the standard offset wrapper-col and switch the flex to space-between so the icon sits at the right of the input column, in line with the fields below it.
This commit is contained in:
parent
60e2af088d
commit
f68a14a3ca
3 changed files with 63 additions and 43 deletions
|
|
@ -1292,7 +1292,6 @@ export class Outbound extends CommonClass {
|
|||
|
||||
hasAddressPort() {
|
||||
return [
|
||||
Protocols.DNS,
|
||||
Protocols.VMess,
|
||||
Protocols.VLESS,
|
||||
Protocols.Trojan,
|
||||
|
|
@ -1846,15 +1845,17 @@ Outbound.DNSRule = class extends CommonClass {
|
|||
|
||||
Outbound.DNSSettings = class extends CommonClass {
|
||||
constructor(
|
||||
network = 'udp',
|
||||
address = '',
|
||||
port = 53,
|
||||
rewriteNetwork = '',
|
||||
rewriteAddress = '',
|
||||
rewritePort = 53,
|
||||
userLevel = 0,
|
||||
rules = []
|
||||
) {
|
||||
super();
|
||||
this.network = network;
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
this.rewriteNetwork = rewriteNetwork;
|
||||
this.rewriteAddress = rewriteAddress;
|
||||
this.rewritePort = rewritePort;
|
||||
this.userLevel = userLevel;
|
||||
this.rules = Array.isArray(rules) ? rules.map(rule => rule instanceof Outbound.DNSRule ? rule : Outbound.DNSRule.fromJson(rule)) : [];
|
||||
}
|
||||
|
||||
|
|
@ -1867,25 +1868,25 @@ Outbound.DNSSettings = class extends CommonClass {
|
|||
}
|
||||
|
||||
static fromJson(json = {}) {
|
||||
// Spec uses rewrite{Network,Address,Port}; older configs used the
|
||||
// bare network/address/port keys — accept both so existing saved
|
||||
// configs keep working after the migration.
|
||||
return new Outbound.DNSSettings(
|
||||
json.network,
|
||||
json.address,
|
||||
json.port,
|
||||
json.rewriteNetwork ?? json.network ?? '',
|
||||
json.rewriteAddress ?? json.address ?? '',
|
||||
Number(json.rewritePort ?? json.port ?? 53) || 53,
|
||||
Number(json.userLevel ?? 0) || 0,
|
||||
getDNSRulesFromJson(json),
|
||||
);
|
||||
}
|
||||
|
||||
toJson() {
|
||||
const json = {
|
||||
network: this.network,
|
||||
address: this.address,
|
||||
port: this.port,
|
||||
};
|
||||
|
||||
if (this.rules.length > 0) {
|
||||
json.rules = Outbound.DNSRule.toJsonArray(this.rules);
|
||||
}
|
||||
|
||||
const json = {};
|
||||
if (!ObjectUtil.isEmpty(this.rewriteNetwork)) json.rewriteNetwork = this.rewriteNetwork;
|
||||
if (!ObjectUtil.isEmpty(this.rewriteAddress)) json.rewriteAddress = this.rewriteAddress;
|
||||
if (this.rewritePort > 0) json.rewritePort = this.rewritePort;
|
||||
if (this.userLevel > 0) json.userLevel = this.userLevel;
|
||||
if (this.rules.length > 0) json.rules = Outbound.DNSRule.toJsonArray(this.rules);
|
||||
return json;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -268,21 +268,23 @@ function regenerateWgKeys() {
|
|||
|
||||
<a-form-item label="Noises">
|
||||
<a-switch :checked="(outbound.settings.noises || []).length > 0"
|
||||
@change="(checked) => outbound.settings.noises = checked ? [{ type: 'rand', packet: '10-20', delay: '10-16', applyTo: 'ip' }] : []" />
|
||||
@change="(checked) => outbound.settings.noises = checked ? [new Outbound.FreedomSettings.Noise()] : []" />
|
||||
<a-button v-if="outbound.settings.noises && outbound.settings.noises.length > 0" size="small"
|
||||
type="primary" class="ml-8"
|
||||
@click="outbound.settings.noises.push({ type: 'rand', packet: '10-20', delay: '10-16', applyTo: 'ip' })">
|
||||
@click="outbound.settings.noises.push(new Outbound.FreedomSettings.Noise())">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
<template v-for="(noise, index) in outbound.settings.noises || []" :key="index">
|
||||
<a-form-item :wrapper-col="{ md: { span: 14, offset: 8 } }" :colon="false">
|
||||
<div class="item-heading">
|
||||
<span>Noise {{ index + 1 }}</span>
|
||||
<DeleteOutlined v-if="outbound.settings.noises.length > 1" class="danger-icon"
|
||||
@click="outbound.settings.noises.splice(index, 1)" />
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="Type">
|
||||
<a-select v-model:value="noise.type">
|
||||
<a-select-option v-for="x in ['rand', 'base64', 'str', 'hex']" :key="x" :value="x">{{ x
|
||||
|
|
@ -325,24 +327,36 @@ function regenerateWgKeys() {
|
|||
|
||||
<!-- ============== DNS ============== -->
|
||||
<template v-if="isDNS">
|
||||
<a-form-item :label="t('pages.inbounds.network')">
|
||||
<a-select v-model:value="outbound.settings.network">
|
||||
<a-form-item label="Rewrite network">
|
||||
<a-select v-model:value="outbound.settings.rewriteNetwork" allow-clear placeholder="(unchanged)">
|
||||
<a-select-option v-for="x in ['udp', 'tcp']" :key="x" :value="x">{{ x }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="Rewrite address">
|
||||
<a-input v-model:value="outbound.settings.rewriteAddress" placeholder="(unchanged) e.g. 1.1.1.1" />
|
||||
</a-form-item>
|
||||
<a-form-item label="Rewrite port">
|
||||
<a-input-number v-model:value="outbound.settings.rewritePort" :min="0" :max="65535"
|
||||
:style="{ width: '100%' }" placeholder="(unchanged)" />
|
||||
</a-form-item>
|
||||
<a-form-item label="User level">
|
||||
<a-input-number v-model:value="outbound.settings.userLevel" :min="0" :style="{ width: '100%' }" />
|
||||
</a-form-item>
|
||||
<a-form-item label="Rules">
|
||||
<a-button size="small" type="primary"
|
||||
@click="outbound.settings.rules.push({ action: 'direct', qtype: '', domain: '' })">
|
||||
@click="outbound.settings.rules.push(new Outbound.DNSRule())">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
<template v-for="(rule, index) in outbound.settings.rules || []" :key="index">
|
||||
<a-form-item :wrapper-col="{ md: { span: 14, offset: 8 } }" :colon="false">
|
||||
<div class="item-heading">
|
||||
<span>Rule {{ index + 1 }}</span>
|
||||
<DeleteOutlined class="danger-icon" @click="outbound.settings.rules.splice(index, 1)" />
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="Action">
|
||||
<a-select v-model:value="rule.action">
|
||||
<a-select-option v-for="a in DNSRuleActions" :key="a" :value="a">{{ a }}</a-select-option>
|
||||
|
|
@ -393,18 +407,20 @@ function regenerateWgKeys() {
|
|||
</a-form-item>
|
||||
<a-form-item label="Peers">
|
||||
<a-button size="small" type="primary"
|
||||
@click="outbound.settings.peers.push({ endpoint: '', publicKey: '', psk: '', allowedIPs: [''], keepAlive: 0 })">
|
||||
@click="outbound.settings.peers.push(new Outbound.WireguardSettings.Peer())">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
<template v-for="(peer, index) in outbound.settings.peers || []" :key="index">
|
||||
<a-form-item :wrapper-col="{ md: { span: 14, offset: 8 } }" :colon="false">
|
||||
<div class="item-heading">
|
||||
<span>Peer {{ index + 1 }}</span>
|
||||
<DeleteOutlined v-if="outbound.settings.peers.length > 1" class="danger-icon"
|
||||
@click="outbound.settings.peers.splice(index, 1)" />
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="Endpoint">
|
||||
<a-input v-model:value="peer.endpoint" />
|
||||
</a-form-item>
|
||||
|
|
@ -993,9 +1009,9 @@ function regenerateWgKeys() {
|
|||
.item-heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
font-weight: 500;
|
||||
margin: 8px 0 4px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -128,8 +128,11 @@ function outboundAddresses(o) {
|
|||
case Protocols.Trojan:
|
||||
serverObj = o.settings?.servers;
|
||||
break;
|
||||
case Protocols.DNS:
|
||||
return [`${o.settings?.address || ''}:${o.settings?.port || ''}`];
|
||||
case Protocols.DNS: {
|
||||
const addr = o.settings?.rewriteAddress || o.settings?.address || '';
|
||||
const port = o.settings?.rewritePort || o.settings?.port || '';
|
||||
return addr || port ? [`${addr}:${port}`] : [];
|
||||
}
|
||||
case Protocols.Wireguard:
|
||||
return (o.settings?.peers || []).map((p) => p.endpoint);
|
||||
default:
|
||||
|
|
|
|||
Loading…
Reference in a new issue