Update On Thu Nov 6 19:42:18 CET 2025

This commit is contained in:
github-action[bot]
2025-11-06 19:42:18 +01:00
parent 4a37a53ca8
commit f7290fe662
95 changed files with 1936 additions and 1084 deletions

View File

@@ -3,66 +3,37 @@
<b-navbar ref="navs" fixed-top shadow type="is-light">
<template slot="brand">
<b-navbar-item href="/">
<img
src="@/assets/img/logo2.png"
alt="v2rayA"
class="logo no-select"
/>
<img src="@/assets/img/logo2.png" alt="v2rayA" class="logo no-select" />
</b-navbar-item>
<b-navbar-item tag="div">
<b-tag
id="statusTag"
class="pointerTag"
:type="statusMap[runningState.running]"
@mouseenter.native="handleOnStatusMouseEnter"
@mouseleave.native="handleOnStatusMouseLeave"
@click.native="handleClickStatus"
>{{ coverStatusText ? coverStatusText : runningState.running }}
<b-tag id="statusTag" class="pointerTag" :type="statusMap[runningState.running]"
@mouseenter.native="handleOnStatusMouseEnter" @mouseleave.native="handleOnStatusMouseLeave"
@click.native="handleClickStatus">{{ coverStatusText ? coverStatusText : runningState.running }}
</b-tag>
</b-navbar-item>
<b-navbar-item tag="div">
<b-dropdown
v-if="updateOutboundDropdown"
:triggers="isMobile ? ['click'] : ['click', 'hover']"
aria-role="list"
:close-on-click="false"
@mouseenter.native="handleOutboundDropdownActiveChange"
@active-change="handleOutboundDropdownActiveChange"
>
<b-dropdown v-if="updateOutboundDropdown" :triggers="isMobile ? ['click'] : ['click', 'hover']"
aria-role="list" :close-on-click="false" @mouseenter.native="handleOutboundDropdownActiveChange"
@active-change="handleOutboundDropdownActiveChange">
<template #trigger>
<b-tag class="pointerTag" type="is-info" icon-right="menu-down"
>{{ outboundName.toUpperCase() }}
<b-tag class="pointerTag" type="is-info" icon-right="menu-down">{{ outboundName.toUpperCase() }}
</b-tag>
</template>
<b-dropdown-item
v-for="outbound in outbounds"
:key="outbound"
aria-role="listitem"
<b-dropdown-item v-for="outbound in outbounds" :key="outbound" aria-role="listitem"
class="is-flex padding-right-1rem justify-content-space-between outbound-dropdown"
@mouseenter.native="handleOnOutboundMouseEnter(outbound)"
@mouseleave.native="handleOnOutboundMouseLeave"
@click="outboundName = outbound"
><p class="is-relative is-fullwidth">
@mouseenter.native="handleOnOutboundMouseEnter(outbound)" @mouseleave.native="handleOnOutboundMouseLeave"
@click="outboundName = outbound">
<p class="is-relative is-fullwidth">
<span>{{ outboundNameDecorator(outbound) }}</span>
<span>
<i
v-show="isMobile || outboundDropdownHover[outbound]"
class="iconfont icon-setting outbound-setting"
@click="handleClickOutboundSetting($event, outbound)"
></i
></span></p
></b-dropdown-item>
<b-dropdown-item
aria-role="listitem"
class="is-flex padding-right-1rem"
separator
></b-dropdown-item>
<b-dropdown-item
aria-role="listitem"
class="is-flex padding-right-1rem"
@click="handleAddOutbound"
>{{ $t("operations.addOutbound") }}
<i v-show="isMobile || outboundDropdownHover[outbound]" class="iconfont icon-setting outbound-setting"
@click="handleClickOutboundSetting($event, outbound)"></i></span>
</p>
</b-dropdown-item>
<b-dropdown-item aria-role="listitem" class="is-flex padding-right-1rem" separator></b-dropdown-item>
<b-dropdown-item aria-role="listitem" class="is-flex padding-right-1rem" @click="handleAddOutbound">{{
$t("operations.addOutbound") }}
</b-dropdown-item>
</b-dropdown>
</b-navbar-item>
@@ -86,74 +57,34 @@
<i class="iconfont icon-info" style="font-size: 1.25em"></i>
{{ $t("common.log") }}
</b-navbar-item>
<b-dropdown
position="is-bottom-left"
aria-role="menu"
style="margin-right: 10px"
class="menudropdown"
>
<b-dropdown position="is-bottom-left" aria-role="menu" style="margin-right: 10px" class="menudropdown">
<a slot="trigger" class="navbar-item" role="button">
<span class="no-select">{{ username }}</span>
<i
class="iconfont icon-caret-down"
style="position: relative; top: 1px; left: 2px"
></i>
<i class="iconfont icon-caret-down" style="position: relative; top: 1px; left: 2px"></i>
</a>
<b-dropdown-item
custom
aria-role="menuitem"
v-html="$t('common.loggedAs', { username })"
>
<b-dropdown-item custom aria-role="menuitem" v-html="$t('common.loggedAs', { username })">
</b-dropdown-item>
<b-dropdown-item
custom
aria-role="menuitem"
class="is-flex"
style="
<b-dropdown-item custom aria-role="menuitem" class="is-flex" style="
box-sizing: content-box;
height: 16px;
width: 60px;
justify-content: space-between;
"
>
<img
v-for="lang of langs"
:key="lang.flag"
:src="require(`@/assets/img/flags/flag_${lang.flag}.svg`)"
:alt="lang.alt"
style="height: 100%; flex-shrink: 0; cursor: pointer"
@click="handleClickLang(lang.flag)"
/>
">
<img v-for="lang of langs" :key="lang.flag" :src="require(`@/assets/img/flags/flag_${lang.flag}.svg`)"
:alt="lang.alt" style="height: 100%; flex-shrink: 0; cursor: pointer"
@click="handleClickLang(lang.flag)" />
</b-dropdown-item>
<hr class="dropdown-divider" />
<b-dropdown-item
value="logout"
aria-role="menuitem"
class="no-select"
@click="handleClickLogout"
>
<i
class="iconfont icon-logout"
style="position: relative; top: 1px"
></i>
<b-dropdown-item value="logout" aria-role="menuitem" class="no-select" @click="handleClickLogout">
<i class="iconfont icon-logout" style="position: relative; top: 1px"></i>
{{ $t("operations.logout") }}
</b-dropdown-item>
</b-dropdown>
</template>
</b-navbar>
<node
v-model="runningState"
:outbound="outboundName"
:observatory="observatory"
/>
<b-modal
:active.sync="showCustomPorts"
has-modal-card
trap-focus
aria-role="dialog"
aria-modal
class="modal-custom-ports"
>
<node v-model="runningState" :outbound="outboundName" :observatory="observatory" />
<b-modal :active.sync="showCustomPorts" has-modal-card trap-focus aria-role="dialog" aria-modal
class="modal-custom-ports">
<ModalCustomAddress @close="showCustomPorts = false" />
</b-modal>
<div id="login"></div>
@@ -297,9 +228,8 @@ export default {
if (u.protocol === "https") {
protocol = "wss";
}
url = `${protocol}://${u.host}:${
u.port
}/api/message?Authorization=${encodeURIComponent(localStorage["token"])}`;
url = `${protocol}://${u.host}:${u.port
}/api/message?Authorization=${encodeURIComponent(localStorage["token"])}`;
if (this.ws) {
// console.log("ws close");
this.ws.close();
@@ -343,9 +273,8 @@ export default {
if (
typeof this.runningState.outboundToServerName[outbound] === "number"
) {
return `${outbound} - ${this.$t("common.loadBalance")} (${
this.runningState.outboundToServerName[outbound]
})`;
return `${outbound} - ${this.$t("common.loadBalance")} (${this.runningState.outboundToServerName[outbound]
})`;
} else {
return `${outbound} - ${this.runningState.outboundToServerName[outbound]}`;
}
@@ -519,6 +448,7 @@ export default {
handleClickStatus() {
if (this.runningState.running === this.$t("common.notRunning")) {
let cancel;
let loading = this.$buefy.loading.open();
waitingConnected(
this.$axios({
url: apiRoot + "/v2ray",
@@ -541,6 +471,8 @@ export default {
queue: false,
});
}
}).finally(() => {
loading.close();
}),
3 * 1000,
cancel

View File

@@ -4,52 +4,26 @@
<p class="modal-card-title">{{ $t("common.setting") }}</p>
</header>
<section class="modal-card-body rules">
<b-field
label="GFWList"
horizontal
custom-class="modal-setting-label"
style="position: relative"
><span>{{ $t("common.latest") }}:</span>
<a
href="https://github.com/v2rayA/dist-v2ray-rules-dat/releases"
target="_blank"
class="is-link"
>{{ remoteGFWListVersion }}</a
><span>{{ $t("common.local") }}:</span>
<b-tooltip
v-if="dayjs(localGFWListVersion).isAfter(dayjs(remoteGFWListVersion))"
:label="$t('setting.messages.gfwlist')"
position="is-bottom"
type="is-danger"
dashed
multilined
animated
>
<b-field label="GFWList" horizontal custom-class="modal-setting-label" style="position: relative"><span>{{
$t("common.latest") }}:</span>
<a href="https://github.com/v2rayA/dist-v2ray-rules-dat/releases" target="_blank" class="is-link">{{
remoteGFWListVersion }}</a><span>{{ $t("common.local") }}:</span>
<b-tooltip v-if="dayjs(localGFWListVersion).isAfter(dayjs(remoteGFWListVersion))"
:label="$t('setting.messages.gfwlist')" position="is-bottom" type="is-danger" dashed multilined animated>
{{ localGFWListVersion ? localGFWListVersion : $t("common.none") }}
</b-tooltip>
<span v-else>{{ localGFWListVersion ? localGFWListVersion : $t("common.none") }}</span>
<b-button
size="is-small"
style="position: relative; top: -2px; text-decoration: none; font-weight: bold"
@click="handleClickUpdateGFWList"
>{{ $t("operations.update") }}
<b-button size="is-small" style="position: relative; top: -2px; text-decoration: none; font-weight: bold"
@click="handleClickUpdateGFWList">{{ $t("operations.update") }}
</b-button>
</b-field>
<hr class="dropdown-divider" style="margin: 1.25rem 0 1.25rem" />
<b-field label-position="on-border" class="with-icon-alert">
<template slot="label">
{{ $t("setting.transparentProxy") }}
<b-tooltip
type="is-dark"
:label="$t('setting.messages.transparentProxy')"
multilined
position="is-right"
>
<b-icon
size="is-small"
icon=" iconfont icon-help-circle-outline"
style="position: relative; top: 2px; right: 3px; font-weight: normal"
/>
<b-tooltip type="is-dark" :label="$t('setting.messages.transparentProxy')" multilined position="is-right">
<b-icon size="is-small" icon=" iconfont icon-help-circle-outline"
style="position: relative; top: 2px; right: 3px; font-weight: normal" />
</b-tooltip>
</template>
<b-select v-model="transparent" expanded>
@@ -69,59 +43,48 @@
{{ $t("setting.options.sameAsPacMode") }}
</option>
</b-select>
<b-checkbox-button
v-show="!lite"
v-model="ipforward"
:native-value="true"
style="position: relative; left: -1px"
>{{ $t("setting.ipForwardOn") }}
<b-checkbox-button v-show="!lite" v-model="ipforward" :native-value="true"
style="position: relative; left: -1px">{{
$t("setting.ipForwardOn") }}
</b-checkbox-button>
<b-checkbox-button
v-model="portSharing"
:native-value="true"
style="position: relative; left: -1px"
>{{ $t("setting.portSharingOn") }}
<b-checkbox-button v-model="portSharing" :native-value="true" style="position: relative; left: -1px">{{
$t("setting.portSharingOn") }}
</b-checkbox-button>
</b-field>
<b-field v-show="transparent !== 'close'" label-position="on-border">
<template slot="label">
{{ $t("setting.transparentType") }}
<b-tooltip
type="is-dark"
multilined
:label="$t('setting.messages.transparentType')"
position="is-right"
>
<b-icon
size="is-small"
icon=" iconfont icon-help-circle-outline"
style="position: relative; top: 2px; right: 3px; font-weight: normal"
/>
<b-tooltip type="is-dark" multilined :label="$t('setting.messages.transparentType')" position="is-right">
<b-icon size="is-small" icon=" iconfont icon-help-circle-outline"
style="position: relative; top: 2px; right: 3px; font-weight: normal" />
</b-tooltip>
</template>
<b-select v-model="transparentType" expanded class="left-border">
<b-select v-model="transparentType" expanded>
<option v-show="!lite" value="redirect">redirect</option>
<option v-show="!lite" value="tproxy">tproxy</option>
<option v-show="!lite" value="gvisor_tun">gvisor tun</option>
<option v-show="!lite" value="system_tun">system tun</option>
<option value="system_proxy">system proxy</option>
</b-select>
<template v-if="transparentType == 'tproxy'">
<b-button style="
margin-left: 0;
border-bottom-left-radius: 0;
border-top-left-radius: 0;
color: rgba(0, 0, 0, 0.75);
" outlined @click="handleClickTproxyWhiteIpGroups">{{ $t("operations.tproxyWhiteIpGroups") }}
</b-button>
</template>
</b-field>
<b-field label-position="on-border">
<template slot="label">
{{ $t("setting.pacMode") }}
<b-tooltip
type="is-dark"
:label="$t('setting.messages.pacMode')"
multilined
position="is-right"
>
<b-icon
size="is-small"
icon=" iconfont icon-help-circle-outline"
style="position: relative; top: 2px; right: 3px; font-weight: normal"
/>
<b-tooltip type="is-dark" :label="$t('setting.messages.pacMode')" multilined position="is-right">
<b-icon size="is-small" icon=" iconfont icon-help-circle-outline"
style="position: relative; top: 2px; right: 3px; font-weight: normal" />
</b-tooltip>
</template>
<b-select v-model="pacMode" expanded style="flex-shrink: 0">
@@ -135,30 +98,21 @@
<option value="routingA">RoutingA</option>
</b-select>
<template v-if="pacMode === 'custom'">
<b-button
type="is-primary"
style="
<b-button type="is-primary" style="
margin-left: 0;
border-bottom-left-radius: 0;
border-top-left-radius: 0;
color: rgba(0, 0, 0, 0.75);
"
outlined
@click="handleClickConfigurePac"
>{{ $t("operations.configure") }}
" outlined @click="handleClickConfigurePac">{{ $t("operations.configure") }}
</b-button>
</template>
<template v-if="pacMode === 'routingA'">
<b-button
style="
<b-button style="
margin-left: 0;
border-bottom-left-radius: 0;
border-top-left-radius: 0;
color: rgba(0, 0, 0, 0.75);
"
outlined
@click="handleClickConfigureRoutingA"
>{{ $t("operations.configure") }}
" outlined @click="handleClickConfigureRoutingA">{{ $t("operations.configure") }}
</b-button>
</template>
<p></p>
@@ -166,17 +120,9 @@
<b-field label-position="on-border">
<template slot="label">
{{ $t("setting.preventDnsSpoofing") }}
<b-tooltip
type="is-dark"
:label="$t('setting.messages.preventDnsSpoofing')"
multilined
position="is-right"
>
<b-icon
size="is-small"
icon=" iconfont icon-help-circle-outline"
style="position: relative; top: 2px; right: 3px; font-weight: normal"
/>
<b-tooltip type="is-dark" :label="$t('setting.messages.preventDnsSpoofing')" multilined position="is-right">
<b-icon size="is-small" icon=" iconfont icon-help-circle-outline"
style="position: relative; top: 2px; right: 3px; font-weight: normal" />
</b-tooltip>
</template>
<b-select v-model="antipollution" expanded class="left-border">
@@ -190,14 +136,10 @@
<option value="doh">{{ $t("setting.options.doh") }}</option>
<option value="advanced">{{ $t("setting.options.advanced") }}</option>
</b-select>
<b-button
v-if="antipollution === 'advanced'"
:class="{
'right-extra-button': antipollution === 'closed',
'no-border-radius': antipollution !== 'closed',
}"
@click="handleClickDnsSetting"
>
<b-button v-if="antipollution === 'advanced'" :class="{
'right-extra-button': antipollution === 'closed',
'no-border-radius': antipollution !== 'closed',
}" @click="handleClickDnsSetting">
{{ $t("operations.configure") }}
</b-button>
<p></p>
@@ -205,17 +147,9 @@
<b-field v-show="showSpecialMode" label-position="on-border">
<template slot="label">
{{ $t("setting.specialMode") }}
<b-tooltip
type="is-dark"
multilined
:label="$t('setting.messages.specialMode')"
position="is-right"
>
<b-icon
size="is-small"
icon=" iconfont icon-help-circle-outline"
style="position: relative; top: 2px; right: 3px; font-weight: normal"
/>
<b-tooltip type="is-dark" multilined :label="$t('setting.messages.specialMode')" position="is-right">
<b-icon size="is-small" icon=" iconfont icon-help-circle-outline"
style="position: relative; top: 2px; right: 3px; font-weight: normal" />
</b-tooltip>
</template>
<b-select v-model="specialMode" expanded class="left-border">
@@ -227,17 +161,9 @@
<b-field label-position="on-border">
<template slot="label">
TCPFastOpen
<b-tooltip
type="is-dark"
:label="$t('setting.messages.tcpFastOpen')"
multilined
position="is-right"
>
<b-icon
size="is-small"
icon=" iconfont icon-help-circle-outline"
style="position: relative; top: 2px; right: 3px; font-weight: normal"
/>
<b-tooltip type="is-dark" :label="$t('setting.messages.tcpFastOpen')" multilined position="is-right">
<b-icon size="is-small" icon=" iconfont icon-help-circle-outline"
style="position: relative; top: 2px; right: 3px; font-weight: normal" />
</b-tooltip>
</template>
<b-select v-model="tcpFastOpen" expanded>
@@ -246,20 +172,13 @@
<option value="no">{{ $t("setting.options.off") }}</option>
</b-select>
</b-field>
<b-field label-position="on-border">
<template slot="label">
{{ $t("setting.inboundSniffing") }}
<b-tooltip
type="is-dark"
:label="$t('setting.messages.inboundSniffing')"
multilined
position="is-right"
>
<b-icon
size="is-small"
icon=" iconfont icon-help-circle-outline"
style="position: relative; top: 2px; right: 3px; font-weight: normal"
/>
<b-tooltip type="is-dark" :label="$t('setting.messages.inboundSniffing')" multilined position="is-right">
<b-icon size="is-small" icon=" iconfont icon-help-circle-outline"
style="position: relative; top: 2px; right: 3px; font-weight: normal" />
</b-tooltip>
</template>
<b-select v-model="inboundSniffing" expanded>
@@ -268,58 +187,36 @@
<option value="http,tls,quic">Http + TLS + Quic</option>
</b-select>
<template v-if="inboundSniffing != 'disable'">
<b-button
type="is-primary"
style="
<b-button style="
margin-left: 0;
border-bottom-left-radius: 0;
border-top-left-radius: 0;
border-radius: 0px;
color: rgba(0, 0, 0, 0.75);
"
outlined
@click="handleClickDomainsExcluded"
>{{ $t("operations.domainsExcluded") }}
" outlined @click="handleClickDomainsExcluded">{{ $t("operations.domainsExcluded") }}
</b-button>
<b-checkbox-button v-model="routeOnly" :native-value="true" style="position: relative; left: -1px;">
RouteOnly
</b-checkbox-button>
</template>
</b-field>
<b-field label-position="on-border" class="with-icon-alert">
<template slot="label">
{{ $t("setting.mux") }}
<b-tooltip
type="is-dark"
:label="$t('setting.messages.mux')"
multilined
position="is-right"
>
<b-icon
size="is-small"
icon=" iconfont icon-help-circle-outline"
style="position: relative; top: 2px; right: 3px; font-weight: normal"
/>
<b-tooltip type="is-dark" :label="$t('setting.messages.mux')" multilined position="is-right">
<b-icon size="is-small" icon=" iconfont icon-help-circle-outline"
style="position: relative; top: 2px; right: 3px; font-weight: normal" />
</b-tooltip>
</template>
<b-select v-model="muxOn" expanded style="flex: 1">
<option value="no">{{ $t("setting.options.off") }}</option>
<option value="yes">{{ $t("setting.options.on") }}</option>
</b-select>
<cus-b-input
v-if="muxOn === 'yes'"
ref="muxinput"
v-model="mux"
:placeholder="$t('setting.concurrency')"
custom-class="no-shadow"
type="number"
min="1"
max="1024"
validation-icon=" iconfont icon-alert"
style="flex: 1"
/>
<cus-b-input v-if="muxOn === 'yes'" ref="muxinput" v-model="mux" :placeholder="$t('setting.concurrency')"
custom-class="no-shadow" type="number" min="1" max="1024" validation-icon=" iconfont icon-alert"
style="flex: 1" />
</b-field>
<b-field
v-show="pacMode === 'gfwlist' || transparent === 'gfwlist'"
:label="$t('setting.autoUpdateGfwlist')"
label-position="on-border"
>
<b-field v-show="pacMode === 'gfwlist' || transparent === 'gfwlist'" :label="$t('setting.autoUpdateGfwlist')"
label-position="on-border">
<b-select v-model="pacAutoUpdateMode" expanded>
<option value="none">{{ $t("setting.options.off") }}</option>
<option value="auto_update">
@@ -329,16 +226,9 @@
{{ $t("setting.options.updateGfwlistAtIntervals") }}
</option>
</b-select>
<cus-b-input
v-if="pacAutoUpdateMode === 'auto_update_at_intervals'"
ref="autoUpdatePacInput"
v-model="pacAutoUpdateIntervalHour"
custom-class="no-shadow"
type="number"
min="1"
validation-icon=" iconfont icon-alert"
style="flex: 1"
/>
<cus-b-input v-if="pacAutoUpdateMode === 'auto_update_at_intervals'" ref="autoUpdatePacInput"
v-model="pacAutoUpdateIntervalHour" custom-class="no-shadow" type="number" min="1"
validation-icon=" iconfont icon-alert" style="flex: 1" />
</b-field>
<b-field :label="$t('setting.autoUpdateSub')" label-position="on-border">
<b-select v-model="subscriptionAutoUpdateMode" expanded>
@@ -350,16 +240,9 @@
{{ $t("setting.options.updateSubAtIntervals") }}
</option>
</b-select>
<cus-b-input
v-if="subscriptionAutoUpdateMode === 'auto_update_at_intervals'"
ref="autoUpdateSubInput"
v-model="subscriptionAutoUpdateIntervalHour"
custom-class="no-shadow"
type="number"
min="1"
validation-icon=" iconfont icon-alert"
style="flex: 1"
/>
<cus-b-input v-if="subscriptionAutoUpdateMode === 'auto_update_at_intervals'" ref="autoUpdateSubInput"
v-model="subscriptionAutoUpdateIntervalHour" custom-class="no-shadow" type="number" min="1"
validation-icon=" iconfont icon-alert" style="flex: 1" />
</b-field>
<b-field :label="$t('setting.preferModeWhenUpdate')" label-position="on-border">
<b-select v-model="proxyModeWhenSubscribe" expanded>
@@ -376,11 +259,7 @@
</b-field>
</section>
<footer class="modal-card-foot flex-end">
<button
class="button footer-absolute-left"
type="button"
@click="$emit('clickPorts')"
>
<button class="button footer-absolute-left" type="button" @click="$emit('clickPorts')">
{{ $t("customAddressPort.title") }}
</button>
<button class="button" type="button" @click="$parent.close()">
@@ -399,6 +278,7 @@ import dayjs from "dayjs";
import ModalCustomRouting from "@/components/modalCustomRouting";
import ModalCustomRoutingA from "@/components/modalCustomRoutingA";
import modalDomainsExcluded from "@/components/modalDomainsExcluded";
import modalTproxyWhiteIpGroups from "@/components/modalTproxyWhiteIpGroups";
import modalUpdateGfwList from "@/components/modalUpdateGfwList";
import CusBInput from "./input/Input.vue";
import { parseURL, toInt } from "@/assets/js/utils";
@@ -418,12 +298,13 @@ export default {
muxOn: "no",
mux: "8",
transparent: "close",
transparentType: "redirect",
transparentType: "tproxy",
ipforward: false,
portSharing: false,
dnsForceMode: false,
dnsforward: "no",
antipollution: "none",
routeOnly: false,
specialMode: "none",
pacAutoUpdateMode: "none",
pacAutoUpdateIntervalHour: 0,
@@ -493,6 +374,7 @@ export default {
});
},
requestUpdateSetting() {
let loading = this.$buefy.loading.open();
let cancel;
waitingConnected(
this.$axios({
@@ -515,6 +397,7 @@ export default {
transparentType: this.transparentType,
ipforward: this.ipforward,
portSharing: this.portSharing,
routeOnly: this.routeOnly,
dnsforward: this.antipollution === "dnsforward" ? "yes" : "no", //版本兼容
antipollution: this.antipollution,
specialMode: this.specialMode,
@@ -539,6 +422,7 @@ export default {
// FIXME: tricky
this.$parent.$parent.runningState.running = this.$t("common.notRunning");
}
loading.close();
}),
3 * 1000,
cancel
@@ -592,6 +476,14 @@ export default {
canCancel: true,
});
},
handleClickTproxyWhiteIpGroups() {
this.$buefy.modal.open({
parent: this,
component: modalTproxyWhiteIpGroups,
hasModalCard: true,
canCancel: true,
});
},
handleClickDomainsExcluded() {
this.$buefy.modal.open({
parent: this,
@@ -673,15 +565,19 @@ export default {
position: absolute;
left: 20px;
}
.left-border select {
border-radius: 4px 0 0 4px !important;
}
.right-extra-button {
border-radius: 0 4px 4px 0;
}
.no-border-radius {
border-radius: 0;
}
.modal-setting {
.b-checkbox.checkbox {
margin-right: 0;

View File

@@ -0,0 +1,98 @@
<template>
<div class="modal-card" style="max-width: 450px; margin: auto">
<header class="modal-card-head">
<p class="modal-card-title">
{{ $t("tproxyWhiteIpGroups.title") }}
</p>
</header>
<section class="modal-card-body">
<b-message type="is-info" class="after-line-dot5">
<p>{{ $t("tproxyWhiteIpGroups.messages.0") }}</p>
</b-message>
<b-field :label="$t('tproxyWhiteIpGroups.formName1')">
<b-select multiple v-model="countryCodes" expanded>
<option value="CN">{{ $t("tproxyWhiteIpGroups.cn") }}</option>
<option value="PRIVATE">{{ $t("tproxyWhiteIpGroups.private") }}</option>
<option value="US">{{ $t("tproxyWhiteIpGroups.us") }}</option>
<option value="CLOUDFLARE">{{ $t("tproxyWhiteIpGroups.cloudflare") }}</option>
</b-select>
</b-field>
<b-field :label="$t('tproxyWhiteIpGroups.formName2')">
<b-input v-model="customIps" type="textarea" :placeholder="$t('tproxyWhiteIpGroups.formPlaceholder2')"
custom-class="full-min-height horizon-scroll code-font" />
</b-field>
<b-message type="is-warning" class="after-line-dot5">
<p>{{ $t("tproxyWhiteIpGroups.messages.1") }}</p>
</b-message>
</section>
<footer class="modal-card-foot flex-end">
<button class="button" @click="$emit('close')">
{{ $t("operations.cancel") }}
</button>
<button class="button is-primary" @click="handleClickSubmit">
{{ $t("operations.save") }}
</button>
</footer>
</div>
</template>
<script>
import { handleResponse } from "@/assets/js/utils";
export default {
name: "modalTproxyWhiteIpGroups",
data: () => ({
countryCodes: [],
customIps: "",
}),
created() {
this.$axios({
url: apiRoot + "/tproxyWhiteIpGroups",
}).then((res) => {
handleResponse(res, this, () => {
if (res.data.data.countryCodes) {
this.countryCodes = res.data.data.countryCodes;
}
if (res.data.data.customIps) {
this.customIps = res.data.data.customIps.join("\n");
}
});
});
},
methods: {
validateCIDRArray(arr) {
const ipv4Cidr = /^(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)){3}\/(?:[0-9]|[12]\d|3[0-2])$/;
const ipv6Cidr = /^(?:(?:[A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}|(?:[A-Fa-f0-9]{1,4}:){1,7}:|(?:[A-Fa-f0-9]{1,4}:){1,6}:[A-Fa-f0-9]{1,4}|(?:[A-Fa-f0-9]{1,4}:){1,5}(?::[A-Fa-f0-9]{1,4}){1,2}|(?:[A-Fa-f0-9]{1,4}:){1,4}(?::[A-Fa-f0-9]{1,4}){1,3}|(?:[A-Fa-f0-9]{1,4}:){1,3}(?::[A-Fa-f0-9]{1,4}){1,4}|(?:[A-Fa-f0-9]{1,4}:){1,2}(?::[A-Fa-f0-9]{1,4}){1,5}|[A-Fa-f0-9]{1,4}:(?:(?::[A-Fa-f0-9]{1,4}){1,6})|:(?:(?::[A-Fa-f0-9]{1,4}){1,7}|:))\/(?:12[0-8]|1[01]\d|[1-9]?\d)$/;
const invalid = arr
.map(s => (typeof s === 'string' ? s.trim() : ''))
.filter(s => s.length === 0 || !(ipv4Cidr.test(s) || ipv6Cidr.test(s)));
return invalid.length === 0;
},
handleClickSubmit() {
if (!this.validateCIDRArray(this.customIps.split("\n").filter(line => line.trim() !== ''))) {
this.$buefy.toast.open({
message: this.$t("tproxyWhiteIpGroups.invalidCustomIps"),
type: "is-danger",
position: "is-top",
queue: false,
duration: 10000,
});
return
}
this.$axios({
url: apiRoot + "/tproxyWhiteIpGroups",
method: "put",
data: {
countryCodes: this.countryCodes.length ? this.countryCodes : ['NONE'],
customIps: this.customIps.split("\n").filter(line => line.trim() !== ''),
},
}).then((res) => {
handleResponse(res, this, () => {
this.$emit("close");
});
});
},
},
};
</script>

View File

@@ -64,6 +64,7 @@ export default {
},
operations: {
name: "Operations",
tproxyWhiteIpGroups: "Direct Whitelist IP Groups",
update: "Update",
autoUpdate: "Auto Update",
manualUpdate: "Manual Update",
@@ -91,7 +92,7 @@ export default {
no: "No",
switchSite: "Switch to alternate site",
addOutbound: "Add an outbound",
domainsExcluded:"Domains Excluded"
domainsExcluded: "Domains Excluded"
},
register: {
title: "Create an admin account first",
@@ -328,6 +329,21 @@ export default {
seconds: "seconds",
autoScoll: "Auto Scroll",
},
tproxyWhiteIpGroups: {
title: "White IP Groups",
messages: [
"The selected IP group will bypass the XRay/V2Ray core and go directly outbound (through Nftables/Iptables). Please ensure your DNS server is reliable and free of contamination so that clients can resolve the correct IPs.",
"It's best to use this feature when your system is using Nftables, as iptables may experience performance issues when adding a large number of IPs."
],
formName1: "Hold down Ctrl to select multiple items.",
formName2: "Custom IPs (one per line, standard CIDR format)",
formPlaceholder2: "172.30.0.0/16\nfd00:aaaa:bbbb::/48",
invalidCustomIps:"Invalid Custom IPs",
cn: 'China Mainland',
private: 'Private',
us: 'United States',
cloudflare: 'Cloudflare',
},
domainsExcluded: {
title: "Domains Excluded",
messages: [

View File

@@ -62,6 +62,7 @@ export default {
subscription: "订阅",
},
operations: {
tproxyWhiteIpGroups: "直通白名单IP组",
name: "操作",
update: "更新",
autoUpdate: "自动更新",
@@ -330,6 +331,22 @@ export default {
seconds: "秒",
autoScoll: "自动滚动",
},
tproxyWhiteIpGroups: {
title: "直通白名单IP组",
messages: [
"选中的IP组将会不经过XRay/V2Ray核心直接出站通过Nftables/Iptables直接转发请确保你的DNS服务器足够可靠无污染能使客户端能解析到正确的IP",
"最好系统使用Nftables时使用此功能Iptables可能在添加大量IP时存在性能问题"
],
formName1: "按住Ctrl可以多选",
formName2: "自定义IP一行一个标准CIDR格式",
formPlaceholder2: "172.30.0.0/16\nfd00:dead:beef::/48",
invalidCustomIps:"自定义IP格式错误",
cn: '中国大陆',
private: '私网网段',
us: '美国',
cloudflare: 'Cloudflare',
},
domainsExcluded: {
title: "排除域名",
messages: [

View File

@@ -1294,6 +1294,7 @@ export default {
let cancel;
if (!row.connected) {
//该节点并未处于连接状态,因此进行连接
let loading = this.$buefy.loading.open();
waitingConnected(
this.$axios({
url: apiRoot + "/connection",
@@ -1308,6 +1309,7 @@ export default {
cancel = c;
}),
}).then((res) => {
loading.close();
if (res.data.code === "SUCCESS") {
Object.assign(this.runningState, {
running: res.data.data.running