mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-12-23 14:52:43 +00:00
Compare commits
1 commit
6e4f9063ae
...
c1870db8e7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1870db8e7 |
7 changed files with 92 additions and 139 deletions
|
|
@ -1897,12 +1897,6 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
// Ensure testseed is always initialized as an array
|
|
||||||
let testseed = [900, 500, 900, 256];
|
|
||||||
if (json.testseed && Array.isArray(json.testseed) && json.testseed.length >= 4) {
|
|
||||||
testseed = json.testseed;
|
|
||||||
}
|
|
||||||
|
|
||||||
const obj = new Inbound.VLESSSettings(
|
const obj = new Inbound.VLESSSettings(
|
||||||
Protocols.VLESS,
|
Protocols.VLESS,
|
||||||
(json.clients || []).map(client => Inbound.VLESSSettings.VLESS.fromJson(client)),
|
(json.clients || []).map(client => Inbound.VLESSSettings.VLESS.fromJson(client)),
|
||||||
|
|
@ -1910,7 +1904,7 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
|
||||||
json.encryption,
|
json.encryption,
|
||||||
Inbound.VLESSSettings.Fallback.fromJson(json.fallbacks || []),
|
Inbound.VLESSSettings.Fallback.fromJson(json.fallbacks || []),
|
||||||
json.selectedAuth,
|
json.selectedAuth,
|
||||||
testseed
|
json.testseed && json.testseed.length >= 4 ? json.testseed : [900, 500, 900, 256]
|
||||||
);
|
);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,14 +19,7 @@ class WebSocketClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
// Ensure basePath ends with '/' for proper URL construction
|
const wsUrl = `${protocol}//${window.location.host}${this.basePath}ws`;
|
||||||
let basePath = this.basePath || '';
|
|
||||||
if (basePath && !basePath.endsWith('/')) {
|
|
||||||
basePath += '/';
|
|
||||||
}
|
|
||||||
const wsUrl = `${protocol}//${window.location.host}${basePath}ws`;
|
|
||||||
|
|
||||||
console.log('WebSocket connecting to:', wsUrl, 'basePath:', this.basePath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.ws = new WebSocket(wsUrl);
|
this.ws = new WebSocket(wsUrl);
|
||||||
|
|
|
||||||
|
|
@ -171,12 +171,55 @@ func (w *WebSocketController) writePump(client *websocket.Client, conn *ws.Conn)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send each message individually (no batching)
|
writer, err := conn.NextWriter(ws.TextMessage)
|
||||||
// This ensures each JSON message is sent separately and can be parsed correctly
|
if err != nil {
|
||||||
if err := conn.WriteMessage(ws.TextMessage, message); err != nil {
|
|
||||||
logger.Debugf("WebSocket write error for client %s: %v", client.ID, err)
|
logger.Debugf("WebSocket write error for client %s: %v", client.ID, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
writer.Write(message)
|
||||||
|
|
||||||
|
// Optimization: message batching with smart limit
|
||||||
|
// Process accumulated messages but limit to prevent delays
|
||||||
|
n := len(client.Send)
|
||||||
|
maxQueued := 20 // Increased from 10 to 20 for better throughput
|
||||||
|
if n > maxQueued {
|
||||||
|
// Skip old messages, keep only the latest for relevance
|
||||||
|
skipped := n - maxQueued
|
||||||
|
for i := 0; i < skipped; i++ {
|
||||||
|
select {
|
||||||
|
case <-client.Send:
|
||||||
|
// Skip old message
|
||||||
|
default:
|
||||||
|
// Channel closed or empty, stop skipping
|
||||||
|
goto skipDone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
skipDone:
|
||||||
|
n = len(client.Send) // Update count after skipping
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batching: send multiple messages in one frame
|
||||||
|
// Safe reading with channel close check
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
select {
|
||||||
|
case msg, ok := <-client.Send:
|
||||||
|
if !ok {
|
||||||
|
// Channel closed, exit
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writer.Write([]byte{'\n'})
|
||||||
|
writer.Write(msg)
|
||||||
|
default:
|
||||||
|
// No more messages in queue, stop batching
|
||||||
|
goto batchDone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
batchDone:
|
||||||
|
|
||||||
|
if err := writer.Close(); err != nil {
|
||||||
|
logger.Debugf("WebSocket writer close error for client %s: %v", client.ID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
conn.SetWriteDeadline(time.Now().Add(writeWait))
|
conn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
<a-divider v-if="inbound.settings.selectedAuth" :style="{ margin: '5px 0' }"></a-divider>
|
<a-divider v-if="inbound.settings.selectedAuth && inbound.settings.vlesses.some(c => c.flow === 'xtls-rprx-vision' || c.flow === 'xtls-rprx-vision-udp443')" :style="{ margin: '5px 0' }"></a-divider>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="inbound.isTcp && !inbound.settings.selectedAuth">
|
<template v-if="inbound.isTcp && !inbound.settings.selectedAuth">
|
||||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
|
|
@ -75,23 +75,23 @@
|
||||||
<a-form-item label="Vision Seed">
|
<a-form-item label="Vision Seed">
|
||||||
<a-row :gutter="8">
|
<a-row :gutter="8">
|
||||||
<a-col :span="6">
|
<a-col :span="6">
|
||||||
<a-input-number :value="(inbound.settings.testseed && inbound.settings.testseed[0] !== undefined) ? inbound.settings.testseed[0] : 900" @change="(val) => updateTestseed(0, val)" :min="0" :max="9999" :style="{ width: '100%' }" placeholder="900" addon-before="[0]"></a-input-number>
|
<a-input-number v-model.number="inbound.settings.testseed[0]" :min="0" :max="9999" :style="{ width: '100%' }" placeholder="900" addon-before="[0]"></a-input-number>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="6">
|
<a-col :span="6">
|
||||||
<a-input-number :value="(inbound.settings.testseed && inbound.settings.testseed[1] !== undefined) ? inbound.settings.testseed[1] : 500" @change="(val) => updateTestseed(1, val)" :min="0" :max="9999" :style="{ width: '100%' }" placeholder="500" addon-before="[1]"></a-input-number>
|
<a-input-number v-model.number="inbound.settings.testseed[1]" :min="0" :max="9999" :style="{ width: '100%' }" placeholder="500" addon-before="[1]"></a-input-number>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="6">
|
<a-col :span="6">
|
||||||
<a-input-number :value="(inbound.settings.testseed && inbound.settings.testseed[2] !== undefined) ? inbound.settings.testseed[2] : 900" @change="(val) => updateTestseed(2, val)" :min="0" :max="9999" :style="{ width: '100%' }" placeholder="900" addon-before="[2]"></a-input-number>
|
<a-input-number v-model.number="inbound.settings.testseed[2]" :min="0" :max="9999" :style="{ width: '100%' }" placeholder="900" addon-before="[2]"></a-input-number>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="6">
|
<a-col :span="6">
|
||||||
<a-input-number :value="(inbound.settings.testseed && inbound.settings.testseed[3] !== undefined) ? inbound.settings.testseed[3] : 256" @change="(val) => updateTestseed(3, val)" :min="0" :max="9999" :style="{ width: '100%' }" placeholder="256" addon-before="[3]"></a-input-number>
|
<a-input-number v-model.number="inbound.settings.testseed[3]" :min="0" :max="9999" :style="{ width: '100%' }" placeholder="256" addon-before="[3]"></a-input-number>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
<a-space :size="8" :style="{ marginTop: '8px' }">
|
<a-space :size="8" :style="{ marginTop: '8px' }">
|
||||||
<a-button type="primary" @click="setRandomTestseed">
|
<a-button type="primary" @click="inbound.settings.testseed = [Math.floor(Math.random()*1000), Math.floor(Math.random()*1000), Math.floor(Math.random()*1000), Math.floor(Math.random()*1000)]">
|
||||||
Rand
|
Rand
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button @click="resetTestseed">
|
<a-button @click="inbound.settings.testseed = [900, 500, 900, 256]">
|
||||||
Reset
|
Reset
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
|
|
|
||||||
|
|
@ -1128,11 +1128,8 @@
|
||||||
},
|
},
|
||||||
openEditClient(dbInboundId, client) {
|
openEditClient(dbInboundId, client) {
|
||||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||||
if (!dbInbound) return;
|
|
||||||
clients = this.getInboundClients(dbInbound);
|
clients = this.getInboundClients(dbInbound);
|
||||||
if (!clients || !Array.isArray(clients)) return;
|
|
||||||
index = this.findIndexOfClient(dbInbound.protocol, clients, client);
|
index = this.findIndexOfClient(dbInbound.protocol, clients, client);
|
||||||
if (index < 0) return;
|
|
||||||
clientModal.show({
|
clientModal.show({
|
||||||
title: '{{ i18n "pages.client.edit"}}',
|
title: '{{ i18n "pages.client.edit"}}',
|
||||||
okText: '{{ i18n "pages.client.submitEdit"}}',
|
okText: '{{ i18n "pages.client.submitEdit"}}',
|
||||||
|
|
@ -1147,14 +1144,11 @@
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
findIndexOfClient(protocol, clients, client) {
|
findIndexOfClient(protocol, clients, client) {
|
||||||
if (!clients || !Array.isArray(clients) || !client) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
switch (protocol) {
|
switch (protocol) {
|
||||||
case Protocols.TROJAN:
|
case Protocols.TROJAN:
|
||||||
case Protocols.SHADOWSOCKS:
|
case Protocols.SHADOWSOCKS:
|
||||||
return clients.findIndex(item => item && item.password === client.password && item.email === client.email);
|
return clients.findIndex(item => item.password === client.password && item.email === client.email);
|
||||||
default: return clients.findIndex(item => item && item.id === client.id && item.email === client.email);
|
default: return clients.findIndex(item => item.id === client.id && item.email === client.email);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async addClient(clients, dbInboundId, modal) {
|
async addClient(clients, dbInboundId, modal) {
|
||||||
|
|
@ -1277,15 +1271,11 @@
|
||||||
},
|
},
|
||||||
showInfo(dbInboundId, client) {
|
showInfo(dbInboundId, client) {
|
||||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||||
if (!dbInbound) return;
|
|
||||||
index = 0;
|
index = 0;
|
||||||
if (dbInbound.isMultiUser()) {
|
if (dbInbound.isMultiUser()) {
|
||||||
inbound = dbInbound.toInbound();
|
inbound = dbInbound.toInbound();
|
||||||
clients = inbound && inbound.clients ? inbound.clients : null;
|
clients = inbound.clients;
|
||||||
if (clients && Array.isArray(clients)) {
|
|
||||||
index = this.findIndexOfClient(dbInbound.protocol, clients, client);
|
index = this.findIndexOfClient(dbInbound.protocol, clients, client);
|
||||||
if (index < 0) index = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
newDbInbound = this.checkFallback(dbInbound);
|
newDbInbound = this.checkFallback(dbInbound);
|
||||||
infoModal.show(newDbInbound, index);
|
infoModal.show(newDbInbound, index);
|
||||||
|
|
@ -1298,12 +1288,9 @@
|
||||||
async switchEnableClient(dbInboundId, client) {
|
async switchEnableClient(dbInboundId, client) {
|
||||||
this.loading()
|
this.loading()
|
||||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||||
if (!dbInbound) return;
|
|
||||||
inbound = dbInbound.toInbound();
|
inbound = dbInbound.toInbound();
|
||||||
clients = inbound && inbound.clients ? inbound.clients : null;
|
clients = inbound.clients;
|
||||||
if (!clients || !Array.isArray(clients)) return;
|
|
||||||
index = this.findIndexOfClient(dbInbound.protocol, clients, client);
|
index = this.findIndexOfClient(dbInbound.protocol, clients, client);
|
||||||
if (index < 0 || !clients[index]) return;
|
|
||||||
clients[index].enable = !clients[index].enable;
|
clients[index].enable = !clients[index].enable;
|
||||||
clientId = this.getClientId(dbInbound.protocol, clients[index]);
|
clientId = this.getClientId(dbInbound.protocol, clients[index]);
|
||||||
await this.updateClient(clients[index], dbInboundId, clientId);
|
await this.updateClient(clients[index], dbInboundId, clientId);
|
||||||
|
|
@ -1316,9 +1303,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getInboundClients(dbInbound) {
|
getInboundClients(dbInbound) {
|
||||||
if (!dbInbound) return null;
|
return dbInbound.toInbound().clients;
|
||||||
const inbound = dbInbound.toInbound();
|
|
||||||
return inbound && inbound.clients ? inbound.clients : null;
|
|
||||||
},
|
},
|
||||||
resetClientTraffic(client, dbInboundId, confirmation = true) {
|
resetClientTraffic(client, dbInboundId, confirmation = true) {
|
||||||
if (confirmation) {
|
if (confirmation) {
|
||||||
|
|
@ -1458,12 +1443,7 @@
|
||||||
formatLastOnline(email) {
|
formatLastOnline(email) {
|
||||||
const ts = this.getLastOnline(email)
|
const ts = this.getLastOnline(email)
|
||||||
if (!ts) return '-'
|
if (!ts) return '-'
|
||||||
// Check if IntlUtil is available (may not be loaded yet)
|
|
||||||
if (typeof IntlUtil !== 'undefined' && IntlUtil.formatDate) {
|
|
||||||
return IntlUtil.formatDate(ts)
|
return IntlUtil.formatDate(ts)
|
||||||
}
|
|
||||||
// Fallback to simple date formatting if IntlUtil is not available
|
|
||||||
return new Date(ts).toLocaleString()
|
|
||||||
},
|
},
|
||||||
isRemovable(dbInboundId) {
|
isRemovable(dbInboundId) {
|
||||||
return this.getInboundClients(this.dbInbounds.find(row => row.id === dbInboundId)).length > 1;
|
return this.getInboundClients(this.dbInbounds.find(row => row.id === dbInboundId)).length > 1;
|
||||||
|
|
@ -1608,16 +1588,15 @@
|
||||||
|
|
||||||
// Listen for traffic updates
|
// Listen for traffic updates
|
||||||
window.wsClient.on('traffic', (payload) => {
|
window.wsClient.on('traffic', (payload) => {
|
||||||
if (payload && payload.clientTraffics && Array.isArray(payload.clientTraffics)) {
|
if (payload && payload.clientTraffics) {
|
||||||
// Update client traffic statistics
|
// Update client traffic statistics
|
||||||
payload.clientTraffics.forEach(clientTraffic => {
|
payload.clientTraffics.forEach(clientTraffic => {
|
||||||
const dbInbound = this.dbInbounds.find(ib => {
|
const dbInbound = this.dbInbounds.find(ib => {
|
||||||
if (!ib) return false;
|
|
||||||
const clients = this.getInboundClients(ib);
|
const clients = this.getInboundClients(ib);
|
||||||
return clients && Array.isArray(clients) && clients.some(c => c && c.email === clientTraffic.email);
|
return clients && clients.some(c => c.email === clientTraffic.email);
|
||||||
});
|
});
|
||||||
if (dbInbound && dbInbound.clientStats && Array.isArray(dbInbound.clientStats)) {
|
if (dbInbound && dbInbound.clientStats) {
|
||||||
const stats = dbInbound.clientStats.find(s => s && s.email === clientTraffic.email);
|
const stats = dbInbound.clientStats.find(s => s.email === clientTraffic.email);
|
||||||
if (stats) {
|
if (stats) {
|
||||||
stats.up = clientTraffic.up || stats.up;
|
stats.up = clientTraffic.up || stats.up;
|
||||||
stats.down = clientTraffic.down || stats.down;
|
stats.down = clientTraffic.down || stats.down;
|
||||||
|
|
@ -1645,7 +1624,17 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Notifications disabled - white notifications are not needed
|
// Listen for notifications
|
||||||
|
window.wsClient.on('notification', (payload) => {
|
||||||
|
if (payload && payload.title) {
|
||||||
|
const type = payload.level || 'info';
|
||||||
|
this.$notification[type]({
|
||||||
|
message: payload.title,
|
||||||
|
description: payload.message || '',
|
||||||
|
duration: 4.5,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Fallback to polling if WebSocket fails
|
// Fallback to polling if WebSocket fails
|
||||||
window.wsClient.on('error', () => {
|
window.wsClient.on('error', () => {
|
||||||
|
|
|
||||||
|
|
@ -1161,7 +1161,17 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Notifications disabled - white notifications are not needed
|
// Listen for notifications
|
||||||
|
window.wsClient.on('notification', (payload) => {
|
||||||
|
if (payload && payload.title) {
|
||||||
|
const type = payload.level || 'info';
|
||||||
|
this.$notification[type]({
|
||||||
|
message: payload.title,
|
||||||
|
description: payload.message || '',
|
||||||
|
duration: 4.5,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Fallback to polling if WebSocket fails
|
// Fallback to polling if WebSocket fails
|
||||||
window.wsClient.on('error', () => {
|
window.wsClient.on('error', () => {
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,7 @@
|
||||||
</a-modal>
|
</a-modal>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
// Make inModal globally available to ensure it works with any base path
|
const inModal = {
|
||||||
const inModal = window.inModal = {
|
|
||||||
title: '',
|
title: '',
|
||||||
visible: false,
|
visible: false,
|
||||||
confirmLoading: false,
|
confirmLoading: false,
|
||||||
|
|
@ -27,14 +26,6 @@
|
||||||
} else {
|
} else {
|
||||||
this.inbound = new Inbound();
|
this.inbound = new Inbound();
|
||||||
}
|
}
|
||||||
// Always ensure testseed is initialized for VLESS protocol (even if vision flow is not set yet)
|
|
||||||
// This ensures Vue reactivity works properly
|
|
||||||
if (this.inbound.protocol === Protocols.VLESS && this.inbound.settings) {
|
|
||||||
if (!this.inbound.settings.testseed || !Array.isArray(this.inbound.settings.testseed) || this.inbound.settings.testseed.length < 4) {
|
|
||||||
// Create a new array to ensure Vue reactivity
|
|
||||||
this.inbound.settings.testseed = [900, 500, 900, 256].slice();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (dbInbound) {
|
if (dbInbound) {
|
||||||
this.dbInbound = new DBInbound(dbInbound);
|
this.dbInbound = new DBInbound(dbInbound);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -51,43 +42,9 @@
|
||||||
loading(loading = true) {
|
loading(loading = true) {
|
||||||
inModal.confirmLoading = loading;
|
inModal.confirmLoading = loading;
|
||||||
},
|
},
|
||||||
// Vision Seed methods - always available regardless of Vue context
|
|
||||||
updateTestseed(index, value) {
|
|
||||||
// Use inModal.inbound explicitly to ensure correct context
|
|
||||||
if (!inModal.inbound || !inModal.inbound.settings) return;
|
|
||||||
// Ensure testseed is initialized
|
|
||||||
if (!inModal.inbound.settings.testseed || !Array.isArray(inModal.inbound.settings.testseed)) {
|
|
||||||
inModal.inbound.settings.testseed = [900, 500, 900, 256];
|
|
||||||
}
|
|
||||||
// Ensure array has enough elements
|
|
||||||
while (inModal.inbound.settings.testseed.length <= index) {
|
|
||||||
inModal.inbound.settings.testseed.push(0);
|
|
||||||
}
|
|
||||||
// Update value
|
|
||||||
inModal.inbound.settings.testseed[index] = value;
|
|
||||||
},
|
|
||||||
setRandomTestseed() {
|
|
||||||
// Use inModal.inbound explicitly to ensure correct context
|
|
||||||
if (!inModal.inbound || !inModal.inbound.settings) return;
|
|
||||||
// Ensure testseed is initialized
|
|
||||||
if (!inModal.inbound.settings.testseed || !Array.isArray(inModal.inbound.settings.testseed) || inModal.inbound.settings.testseed.length < 4) {
|
|
||||||
inModal.inbound.settings.testseed = [900, 500, 900, 256].slice();
|
|
||||||
}
|
|
||||||
// Create new array with random values
|
|
||||||
inModal.inbound.settings.testseed = [Math.floor(Math.random()*1000), Math.floor(Math.random()*1000), Math.floor(Math.random()*1000), Math.floor(Math.random()*1000)];
|
|
||||||
},
|
|
||||||
resetTestseed() {
|
|
||||||
// Use inModal.inbound explicitly to ensure correct context
|
|
||||||
if (!inModal.inbound || !inModal.inbound.settings) return;
|
|
||||||
// Reset testseed to default values
|
|
||||||
inModal.inbound.settings.testseed = [900, 500, 900, 256].slice();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Store Vue instance globally to ensure methods are always accessible
|
new Vue({
|
||||||
let inboundModalVueInstance = null;
|
|
||||||
|
|
||||||
inboundModalVueInstance = new Vue({
|
|
||||||
delimiters: ['[[', ']]'],
|
delimiters: ['[[', ']]'],
|
||||||
el: '#inbound-modal',
|
el: '#inbound-modal',
|
||||||
data: {
|
data: {
|
||||||
|
|
@ -103,7 +60,7 @@
|
||||||
return inModal.isEdit;
|
return inModal.isEdit;
|
||||||
},
|
},
|
||||||
get client() {
|
get client() {
|
||||||
return inModal.inbound && inModal.inbound.clients && inModal.inbound.clients.length > 0 ? inModal.inbound.clients[0] : null;
|
return inModal.inbound.clients[0];
|
||||||
},
|
},
|
||||||
get datepicker() {
|
get datepicker() {
|
||||||
return app.datepicker;
|
return app.datepicker;
|
||||||
|
|
@ -138,18 +95,6 @@
|
||||||
client.flow = "";
|
client.flow = "";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
|
||||||
// Ensure testseed is always initialized when vision flow is enabled
|
|
||||||
'inModal.inbound.settings.vlesses': {
|
|
||||||
handler() {
|
|
||||||
if (inModal.inbound.protocol === Protocols.VLESS && inModal.inbound.settings && inModal.inbound.settings.vlesses) {
|
|
||||||
const hasVisionFlow = inModal.inbound.settings.vlesses.some(c => c.flow === 'xtls-rprx-vision' || c.flow === 'xtls-rprx-vision-udp443');
|
|
||||||
if (hasVisionFlow && (!inModal.inbound.settings.testseed || !Array.isArray(inModal.inbound.settings.testseed) || inModal.inbound.settings.testseed.length < 4)) {
|
|
||||||
inModal.inbound.settings.testseed = [900, 500, 900, 256];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
deep: true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -269,29 +214,8 @@
|
||||||
this.inbound.settings.decryption = 'none';
|
this.inbound.settings.decryption = 'none';
|
||||||
this.inbound.settings.encryption = 'none';
|
this.inbound.settings.encryption = 'none';
|
||||||
this.inbound.settings.selectedAuth = undefined;
|
this.inbound.settings.selectedAuth = undefined;
|
||||||
},
|
|
||||||
// Vision Seed methods - must be in Vue methods for proper binding
|
|
||||||
updateTestseed(index, value) {
|
|
||||||
// Ensure testseed is initialized
|
|
||||||
if (!this.inbound.settings.testseed || !Array.isArray(this.inbound.settings.testseed)) {
|
|
||||||
this.$set(this.inbound.settings, 'testseed', [900, 500, 900, 256]);
|
|
||||||
}
|
|
||||||
// Ensure array has enough elements
|
|
||||||
while (this.inbound.settings.testseed.length <= index) {
|
|
||||||
this.inbound.settings.testseed.push(0);
|
|
||||||
}
|
|
||||||
// Update value using Vue.set for reactivity
|
|
||||||
this.$set(this.inbound.settings.testseed, index, value);
|
|
||||||
},
|
|
||||||
setRandomTestseed() {
|
|
||||||
// Create new array with random values and use Vue.set for reactivity
|
|
||||||
const newSeed = [Math.floor(Math.random()*1000), Math.floor(Math.random()*1000), Math.floor(Math.random()*1000), Math.floor(Math.random()*1000)];
|
|
||||||
this.$set(this.inbound.settings, 'testseed', newSeed);
|
|
||||||
},
|
|
||||||
resetTestseed() {
|
|
||||||
// Reset testseed to default values using Vue.set for reactivity
|
|
||||||
this.$set(this.inbound.settings, 'testseed', [900, 500, 900, 256]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue