subjson and multidomain

This commit is contained in:
Alireza Ahmadi
2024-06-28 15:55:37 +02:00
parent 6b24506ddd
commit 6672a2721f
32 changed files with 2163 additions and 282 deletions
+114
View File
@@ -0,0 +1,114 @@
<template>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-text-field
:label="$t('out.addr')"
hide-details
required
v-model="addr.server">
</v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field
:label="$t('out.port')"
hide-details
type="number"
required
v-model.number="addr.server_port"></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4" v-if="optionRemark">
<v-text-field
label="Remark"
hide-details
v-model="addr.remark">
</v-text-field>
</v-col>
</v-row>
<v-row>
<v-col cols="12" sm="6" md="4" v-if="optionTLS">
<v-switch
:label="$t('tls.enable')"
color="primary"
hide-details
@update:model-value="updateTls($event)"
v-model="addr.tls" />
</v-col>
<v-col cols="12" sm="6" md="4" v-if="optionSNI">
<v-text-field
label="SNI"
hide-details
v-model="addr.server_name">
</v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4" v-if="optionInsecure">
<v-switch
:label="$t('tls.insecure')"
hide-details
color="primary"
v-model="addr.insecure" />
</v-col>
</v-row>
<v-row>
<v-spacer></v-spacer>
<v-col cols="auto" align="end" justify="center">
<v-menu v-model="menu" :close-on-content-click="false" location="start">
<template v-slot:activator="{ props }">
<v-btn v-bind="props" hide-details variant="tonal">Address Options</v-btn>
</template>
<v-card>
<v-list>
<v-list-item>
<v-switch v-model="optionRemark" color="primary" label="Remark" hide-details></v-switch>
</v-list-item>
<v-list-item v-if="hasTls">
<v-switch v-model="optionTLS" color="primary" :label="$t('objects.tls')" hide-details></v-switch>
</v-list-item>
<v-list-item v-if="addr.tls">
<v-switch v-model="optionSNI" color="primary" label="SNI" hide-details></v-switch>
</v-list-item>
<v-list-item v-if="addr.tls">
<v-switch v-model="optionInsecure" color="primary" :label="$t('tls.insecure')" hide-details></v-switch>
</v-list-item>
</v-list>
</v-card>
</v-menu>
</v-col>
</v-row>
</template>
<script lang="ts">
export default {
props: ['addr', 'hasTls'],
data() {
return {
menu: false
}
},
computed: {
optionTLS: {
get(): boolean { return this.$props.addr.tls != undefined },
set(v:boolean) { this.$props.addr.tls = v ? true : undefined; this.updateTls(v) }
},
optionSNI: {
get(): boolean { return this.$props.addr.server_name != undefined },
set(v:boolean) { this.$props.addr.server_name = v ? '' : undefined }
},
optionRemark: {
get(): boolean { return this.$props.addr.remark != undefined },
set(v:boolean) { this.$props.addr.remark = v ? '' : undefined }
},
optionInsecure: {
get(): boolean { return this.$props.addr.insecure != undefined },
set(v:boolean) { this.$props.addr.insecure = v ? false : undefined }
}
},
methods: {
updateTls(v:boolean) {
if (!v) {
delete this.$props.addr.insecure
delete this.$props.addr.server_name
}
}
}
}
</script>
+123
View File
@@ -0,0 +1,123 @@
<template>
<v-card :subtitle="type">
<v-row>
<v-col cols="12" sm="6" md="4" v-if="type == inTypes.SOCKS">
<v-select
hide-details
:items="['4','4a','5']"
:label="$t('version')"
v-model="inData.outJson.version">
</v-select>
</v-col>
<v-col cols="12" sm="6" md="4" v-if="needNetwork">
<Network :data="inData.outJson" />
</v-col>
<v-col cols="12" sm="6" md="4" v-if="needUot">
<UoT :data="inData.outJson" />
</v-col>
<v-col cols="12" sm="6" md="4" v-if="type == inTypes.HTTP">
<v-text-field
:label="$t('transport.path')"
hide-details
v-model="inData.outJson.path">
</v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4" v-if="type == inTypes.VMess || type == inTypes.VLESS">
<v-select
hide-details
:label="$t('types.vless.udpEnc')"
:items="['none','packetaddr','xudp']"
v-model="packet_encoding">
</v-select>
</v-col>
<template v-if="type == inTypes.VMess">
<v-col cols="12" sm="6" md="4">
<v-select
hide-details
:label="$t('types.vmess.security')"
:items="vmessSecurities"
v-model="inData.outJson.security">
</v-select>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-switch v-model="inData.outJson.global_padding" color="primary" :label="$t('types.vmess.globalPadding')" hide-details></v-switch>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-switch v-model="inData.outJson.authenticated_length" color="primary" :label="$t('types.vmess.authLen')" hide-details></v-switch>
</v-col>
</template>
<v-col cols="12" sm="6" md="4" v-if="type == inTypes.Hysteria">
<v-text-field
label="Recv window"
hide-details
type="number"
min="0"
v-model.number="inData.outJson.recv_window">
</v-text-field>
</v-col>
<template v-if="type == inTypes.TUIC">
<v-col cols="12" sm="6" md="4">
<v-select
hide-details
label="UDP Relay Mode"
:items="['native', 'quic']"
clearable
@click:clear="delete inData.outJson.udp_relay_mode"
v-model="inData.outJson.udp_relay_mode">
</v-select>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-switch color="primary" label="UDP Over Stream" v-model="inData.outJson.udp_over_stream" hide-details></v-switch>
</v-col>
</template>
</v-row>
<Headers :data="inData.outJson" v-if="type == inTypes.HTTP" />
</v-card>
</template>
<script lang="ts">
import { InTypes } from '@/types/inbounds'
import Network from './Network.vue'
import UoT from './UoT.vue'
import Headers from './Headers.vue'
export default {
props: ['inData', 'type'],
data() {
return {
inTypes: InTypes,
vmessSecurities: [
"auto",
"none",
"zero",
"aes-128-gcm",
"aes-128-ctr",
"chacha20-poly1305",
],
haveNetwork: [
InTypes.SOCKS,
InTypes.Shadowsocks,
InTypes.VMess,
InTypes.Trojan,
InTypes.Hysteria,
InTypes.VLESS,
InTypes.TUIC,
InTypes.Hysteria2,
],
havUoT: [
InTypes.SOCKS,
InTypes.Shadowsocks,
],
}
},
computed: {
needNetwork():boolean { return this.haveNetwork.includes(this.$props.type) },
needUot():boolean { return this.havUoT.includes(this.$props.type) },
packet_encoding: {
get() { return this.$props.inData.outJson.packet_encoding != undefined ? this.$props.inData.outJson.packet_encoding : 'none'; },
set(v:string) { this.$props.inData.outJson.packet_encoding = v != "none" ? v : undefined }
},
},
components: { Network, UoT, Headers }
}
</script>
+342
View File
@@ -0,0 +1,342 @@
<template>
<v-card subtitle="Client JSON Template">
<v-row>
<v-col cols="12" sm="6" md="3" lg="2">
<v-select
v-model="ruleToDirect"
:items="geoList"
label="Rules To Direct"
multiple
chips
hide-details
></v-select>
</v-col>
<v-col cols="12" sm="6" md="3" lg="2">
<v-select
v-model="ruleToBlock"
:items="geoList"
label="Rules To Block"
multiple
chips
hide-details
></v-select>
</v-col>
</v-row>
<v-row v-if="enableLog">
<v-col cols="12" sm="6" md="3" lg="2">
<v-select
hide-details
:label="$t('basic.log.level')"
:items="levels"
v-model="subJsonExt.log.level">
</v-select>
</v-col>
<v-col cols="12" sm="6" md="3" lg="2">
<v-switch v-model="subJsonExt.log.timestamp" color="primary" label="Timestamp" hide-details />
</v-col>
</v-row>
<v-row v-if="enableDns">
<v-col cols="12" sm="6" md="3" lg="2">
<v-text-field
v-model="proxyDns"
hide-details
label="Golbal DNS"
></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="3" lg="2">
<v-text-field
v-model="directDns"
hide-details
clearable
label="Direct DNS"
></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="3" lg="2" v-if="directDns.length>0">
<v-select
v-model="dnsToDirect"
:items="geositeList"
label="DNS To Direct"
multiple
chips
hide-details
></v-select>
</v-col>
</v-row>
<v-card-actions>
<v-spacer></v-spacer>
<v-menu v-model="menu" :close-on-content-click="false" location="start">
<template v-slot:activator="{ props }">
<v-btn v-bind="props" hide-details variant="tonal">Options</v-btn>
</template>
<v-card>
<v-list>
<v-list-item>
<v-switch v-model="enableLog" color="primary" label="Log" hide-details></v-switch>
</v-list-item>
<v-list-item>
<v-switch v-model="enableDns" color="primary" label="DNS" hide-details></v-switch>
</v-list-item>
<v-list-item>
<v-switch v-model="enableExp" color="primary" label="Experimental" hide-details></v-switch>
</v-list-item>
</v-list>
</v-card>
</v-menu>
</v-card-actions>
</v-card>
</template>
<script lang="ts">
export default {
props: ['settings'],
data() {
return {
menu: false,
subJsonExt: <any>{},
levels: ["trace", "debug", "info", "warn", "error", "fatal", "panic"],
defaultLog: {
"level": "info",
"timestamp": true
},
defaultExp: {
"clash_api": {
"external_controller": "127.0.0.1:9090",
"external_ui": "ui",
"secret": "",
"external_ui_download_url": "https://mirror.ghproxy.com/https://github.com/MetaCubeX/Yacd-meta/archive/gh-pages.zip",
"external_ui_download_detour": "direct",
"default_mode": "rule"
},
"cache_file": {
"enabled": true,
"store_fakeip": false
}
},
defaultDns: {
"servers": [
{
"tag": "ProxyDns",
"address": "8.8.8.8",
"detour": "proxy"
},
{
"tag": "block",
"address": "rcode://success"
}
],
"rules": [
{
"clash_mode": "Global",
"server": "ProxyDns"
}
],
"final": "ProxyDns",
"strategy": "prefer_ipv4"
},
geositeList: [
{ title: "Private", value: "geosite-private" },
{ title: "Ads", value: "geosite-ads" },
{ title: "Iran", value: "geosite-ir" },
{ title: "China", value: "geosite-cn" },
{ title: "Vietnam", value: "geosite-vn" },
],
geoList: [
{ title: "DNS-Private", value: "geoip-private" },
{ title: "IP-Private", value: "geosite-private" },
{ title: "DNS-Ads", value: "geosite-ads" },
{ title: "DNS-Iran", value: "geosite-ir" },
{ title: "IP-Iran", value: "geoip-ir" },
{ title: "DNS-China", value: "geosite-cn" },
{ title: "IP-China", value: "geoip-cn" },
{ title: "DNS-Vietnam", value: "geosite-vn" },
{ title: "IP-Vietnam", value: "geoip-vn" },
],
geo: [
{
tag: "geosite-ads",
type: "remote",
format: "binary",
url: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geosite/category-ads-all.srs",
download_detour: "direct"
},
{
tag: "geosite-private",
type: "remote",
format: "binary",
url: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geosite/private.srs",
download_detour: "direct"
},
{
tag: "geosite-ir",
type: "remote",
format: "binary",
url: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geosite/category-ir.srs",
download_detour: "direct"
},
{
tag: "geosite-cn",
type: "remote",
format: "binary",
url: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geosite/cn.srs",
download_detour: "direct"
},
{
tag: "geosite-vn",
type: "remote",
format: "binary",
url: "https://github.com/Thaomtam/Geosite-vn/raw/rule-set/Geosite-vn.srs",
download_detour: "direct"
},
{
tag: "geoip-private",
type: "remote",
format: "binary",
url: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geoip/private.srs",
download_detour: "direct"
},
{
tag: "geoip-ir",
type: "remote",
format: "binary",
url: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geoip/ir.srs",
download_detour: "direct"
},
{
tag: "geoip-cn",
type: "remote",
format: "binary",
url: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geoip/cn.srs",
download_detour: "direct"
},
{
tag: "geoip-vn",
type: "remote",
format: "binary",
url: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geoip/vn.srs",
download_detour: "direct"
}
],
}
},
computed: {
enableLog: {
get() :boolean { return this.subJsonExt?.log != undefined },
set(v:boolean) { v ? this.subJsonExt.log = this.defaultLog : delete this.subJsonExt.log }
},
enableDns: {
get() :boolean { return this.subJsonExt?.dns != undefined },
set(v:boolean) { v ? this.subJsonExt.dns = this.defaultDns : delete this.subJsonExt.dns }
},
enableExp: {
get() :boolean { return this.subJsonExt?.experimental != undefined },
set(v:boolean) { v ? this.subJsonExt.experimental = this.defaultExp : delete this.subJsonExt.experimental }
},
dns():any { return this.subJsonExt?.dns?? undefined },
proxyDns: {
get() :string { return this.dns?.servers[0]?.address?? "" },
set(v:string) { this.dns.servers[0].address = v.length>0 ? v : "8.8.8.8" }
},
directDns: {
get() :string { return this.dns?.servers?.findLast((d:any) => d.tag == "DirectDns")?.address?? "" },
set(v:string) {
const sIndex = this.dns.servers.findIndex((d:any) => d.tag == "DirectDns")
if (v?.length>0) {
if (sIndex === -1) {
this.dns.servers.push({ tag: "DirectDns", address: v, detour: "direct" })
this.dns.rules.push({ clash_mode: "Direct", server: "DirectDns" })
} else {
this.dns.servers[sIndex].address = v
}
} else {
this.dns.servers.splice(sIndex,1)
this.dns.rules = this.dns.rules.filter((r:any) => r.server != "DirectDns")
}
},
},
dnsToDirect: {
get() :string[] {
const ruleIndex = this.dns?.rules?.findIndex((r:any) => r.server == "DirectDns" && Object.hasOwn(r,'rule_set'))
return ruleIndex >= 0 ? this.dns.rules[ruleIndex].rule_set : []
},
set(v:string[]) {
const ruleIndex = this.dns?.rules?.findIndex((r:any) => r.server == "DirectDns" && Object.hasOwn(r,'rule_set'))
if (v.length>0) {
if (ruleIndex >= 0){
this.dns.rules[ruleIndex].rule_set = v
} else {
this.dns.rules.push({ rule_set: v, server: "DirectDns" })
}
} else {
if (ruleIndex != -1) this.dns.rules.splice(ruleIndex,1)
}
this.updateRuleSets()
}
},
rules():any { return this.subJsonExt?.rules?? undefined },
ruleToDirect: {
get() :string[] {
const ruleIndex = this.rules?.findIndex((r:any) => r.outbound == "direct" && Object.hasOwn(r,'rule_set'))
return ruleIndex >= 0 ? this.rules[ruleIndex].rule_set : []
},
set(v:string[]) {
const ruleIndex = this.rules?.findIndex((r:any) => r.outbound == "direct" && Object.hasOwn(r,'rule_set'))
if (v.length>0) {
if (ruleIndex >= 0){
this.rules[ruleIndex].rule_set = v
} else {
if (this.rules == undefined) this.subJsonExt.rules = []
this.rules.push({ rule_set: v, outbound: "direct" })
}
} else {
if (ruleIndex != -1) this.rules.splice(ruleIndex,1)
}
this.updateRuleSets()
}
},
ruleToBlock: {
get() :string[] {
const ruleIndex = this.rules?.findIndex((r:any) => r.outbound == "block" && Object.hasOwn(r,'rule_set'))
return ruleIndex >= 0 ? this.rules[ruleIndex].rule_set : []
},
set(v:string[]) {
const ruleIndex = this.rules?.findIndex((r:any) => r.outbound == "block" && Object.hasOwn(r,'rule_set'))
if (v.length>0) {
if (ruleIndex >= 0){
this.rules[ruleIndex].rule_set = v
} else {
if (this.rules == undefined) this.subJsonExt.rules = []
this.rules.push({ rule_set: v, outbound: "block" })
}
} else {
if (ruleIndex != -1) this.rules.splice(ruleIndex,1)
}
this.updateRuleSets()
}
}
},
methods: {
updateRuleSets(){
let tags = <string[]>[]
if (this.dns?.rules?.length>0) this.dns.rules.forEach((r:any) => { if (r.rule_set) tags.push(...r.rule_set) })
if (this.rules?.length>0) this.rules.forEach((r:any) => { if (r.rule_set) tags.push(...r.rule_set) })
if (tags.length>0){
this.subJsonExt.rule_set = this.geo.filter((g:any) => tags.includes(g.tag))
} else {
delete this.subJsonExt.rule_set
}
if (this.rules.length == 0) delete this.subJsonExt.rules
}
},
mounted(){
this.subJsonExt = this.$props.settings?.subJsonExt?.length>0 ? JSON.parse(this.$props.settings.subJsonExt) : <any>{}
},
watch:{
subJsonExt:{
handler(v) {
this.$props.settings.subJsonExt = Object.keys(v).length>0 ? JSON.stringify(v, null, 2) : ""
},
deep: true
},
}
}
</script>
@@ -124,7 +124,7 @@
<v-spacer></v-spacer>
<v-menu v-model="menu" :close-on-content-click="false" location="start">
<template v-slot:activator="{ props }">
<v-btn v-bind="props" hide-details>{{ $t('tls.acme.options') }}</v-btn>
<v-btn v-bind="props" hide-details variant="tonal">{{ $t('tls.acme.options') }}</v-btn>
</template>
<v-card>
<v-list>
@@ -116,7 +116,7 @@
<v-spacer></v-spacer>
<v-menu v-model="menu" :close-on-content-click="false" location="start" v-if="tls.enabled">
<template v-slot:activator="{ props }">
<v-btn v-bind="props" hide-details>{{ $t('tls.options') }}</v-btn>
<v-btn v-bind="props" hide-details variant="tonal">{{ $t('tls.options') }}</v-btn>
</template>
<v-card>
<v-list>
@@ -177,7 +177,7 @@
<v-spacer></v-spacer>
<v-menu v-model="menu" :close-on-content-click="false" location="start">
<template v-slot:activator="{ props }">
<v-btn v-bind="props" hide-details>{{ $t('tls.options') }}</v-btn>
<v-btn v-bind="props" hide-details variant="tonal">{{ $t('tls.options') }}</v-btn>
</template>
<v-card>
<v-list>
+2 -2
View File
@@ -32,11 +32,11 @@ const saveChanges = () => {
}
const oldData = computed((): any => {
return {config: store.oldData.config, clients: store.oldData.clients, tls: store.oldData.tlsConfigs}
return {config: store.oldData.config, clients: store.oldData.clients, tls: store.oldData.tlsConfigs, inData: store.oldData.inData}
})
const newData = computed((): any => {
return {config: store.config, clients: store.clients, tls: store.tlsConfigs}
return {config: store.config, clients: store.clients, tls: store.tlsConfigs, inData: store.inData}
})
const stateChange = computed((): any => {
+110 -36
View File
@@ -5,35 +5,66 @@
{{ $t('actions.' + title) + " " + $t('objects.inbound') }}
</v-card-title>
<v-divider></v-divider>
<v-card-text>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-select
hide-details
:label="$t('type')"
:items="Object.keys(inTypes).map((key,index) => ({title: key, value: Object.values(inTypes)[index]}))"
v-model="inbound.type"
@update:modelValue="changeType">
</v-select>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="inbound.tag" :label="$t('objects.tag')" hide-details></v-text-field>
</v-col>
</v-row>
<Listen :inbound="inbound" :inTags="inTags" />
<Direct v-if="inbound.type == inTypes.Direct" direction="in" :data="inbound" />
<Shadowsocks v-if="inbound.type == inTypes.Shadowsocks" direction="in" :data="inbound" />
<Hysteria v-if="inbound.type == inTypes.Hysteria" direction="in" :data="inbound" />
<Hysteria2 v-if="inbound.type == inTypes.Hysteria2" direction="in" :data="inbound" />
<Naive v-if="inbound.type == inTypes.Naive" :inbound="inbound" />
<ShadowTls v-if="inbound.type == inTypes.ShadowTLS" direction="in" :data="inbound" :outTags="outTags" />
<Tuic v-if="inbound.type == inTypes.TUIC" direction="in" :data="inbound" />
<TProxy v-if="inbound.type == inTypes.TProxy" :inbound="inbound" />
<Transport v-if="Object.hasOwn(inbound,'transport')" :data="inbound" />
<Users v-if="HasOptionalUser.includes(inbound.type)" :inbound="inbound" :id="id" />
<InTls v-if="Object.hasOwn(inbound,'tls')" :inbound="inbound" :tlsConfigs="tlsConfigs" :tls_id="tls_id" />
<Multiplex v-if="Object.hasOwn(inbound,'multiplex')" direction="in" :data="inbound" />
<v-switch v-model="inboundStats" color="primary" :label="$t('stats.enable')" hide-details></v-switch>
<v-card-text style="padding-top: 0; overflow-y: scroll;">
<v-container style="padding: 0;">
<v-row>
<v-col cols="12" sm="6" md="4">
<v-select
hide-details
:label="$t('type')"
:items="Object.keys(inTypes).map((key,index) => ({title: key, value: Object.values(inTypes)[index]}))"
v-model="inbound.type"
@update:modelValue="changeType">
</v-select>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="inbound.tag" :label="$t('objects.tag')" hide-details></v-text-field>
</v-col>
</v-row>
<v-tabs
v-if="HasInData.includes(inbound.type)"
v-model="side"
density="compact"
fixed-tabs
align-tabs="center"
>
<v-tab value="s">Server Side</v-tab>
<v-tab value="c">Client Side</v-tab>
</v-tabs>
<v-window v-model="side" style="margin-top: 10px;">
<v-window-item value="s">
<Listen :inbound="inbound" :inTags="inTags" />
<Direct v-if="inbound.type == inTypes.Direct" direction="in" :data="inbound" />
<Shadowsocks v-if="inbound.type == inTypes.Shadowsocks" direction="in" :data="inbound" />
<Hysteria v-if="inbound.type == inTypes.Hysteria" direction="in" :data="inbound" />
<Hysteria2 v-if="inbound.type == inTypes.Hysteria2" direction="in" :data="inbound" />
<Naive v-if="inbound.type == inTypes.Naive" :inbound="inbound" />
<ShadowTls v-if="inbound.type == inTypes.ShadowTLS" direction="in" :data="inbound" :outTags="outTags" />
<Tuic v-if="inbound.type == inTypes.TUIC" direction="in" :data="inbound" />
<TProxy v-if="inbound.type == inTypes.TProxy" :inbound="inbound" />
<Transport v-if="Object.hasOwn(inbound,'transport')" :data="inbound" />
<Users v-if="HasOptionalUser.includes(inbound.type)" :inbound="inbound" />
<InTls v-if="Object.hasOwn(inbound,'tls')" :inbound="inbound" :tlsConfigs="tlsConfigs" :tls_id="tls_id" />
<Multiplex v-if="Object.hasOwn(inbound,'multiplex')" direction="in" :data="inbound" />
<v-switch v-model="inboundStats" color="primary" :label="$t('stats.enable')" hide-details></v-switch>
</v-window-item>
<v-window-item value="c">
<OutJsonVue :inData="inData" :type="inbound.type" />
<v-card>
<v-card-subtitle>
Multi Domain
<v-icon @click="add_addr" icon="mdi-plus"></v-icon>
</v-card-subtitle>
<template v-for="addr,index in inData.addrs">
Address #{{ (index+1) }} <v-icon icon="mdi-delete" @click="inData.addrs.splice(index,1)" />
<v-divider></v-divider>
<AddrVue :addr="addr" :hasTls="Object.hasOwn(inbound,'tls')" />
</template>
</v-card>
<pre dir="ltr">{{ inData }}</pre>
</v-window-item>
</v-window>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
@@ -59,6 +90,9 @@
<script lang="ts">
import { InTypes, createInbound } from '@/types/inbounds'
import { Addr, InData } from '@/plugins/inData'
import RandomUtil from '@/plugins/randomUtil'
import Listen from '@/components/Listen.vue'
import Direct from '@/components/protocols/Direct.vue'
import Users from '@/components/Users.vue'
@@ -68,47 +102,83 @@ import Hysteria2 from '@/components/protocols/Hysteria2.vue'
import Naive from '@/components/protocols/Naive.vue'
import ShadowTls from '@/components/protocols/ShadowTls.vue'
import Tuic from '@/components/protocols/Tuic.vue'
import InTls from '@/components/InTLS.vue'
import InTls from '@/components/tls/InTLS.vue'
import TProxy from '@/components/protocols/TProxy.vue'
import RandomUtil from '@/plugins/randomUtil'
import Multiplex from '@/components/Multiplex.vue'
import Transport from '@/components/Transport.vue'
import AddrVue from '@/components/Addr.vue'
import OutJsonVue from '@/components/OutJson.vue'
export default {
props: ['visible', 'data', 'id', 'stats', 'inTags', 'outTags', 'tlsConfigs'],
props: ['visible', 'data', 'cData', 'index', 'stats', 'inTags', 'outTags', 'tlsConfigs'],
emits: ['close', 'save'],
data() {
return {
inbound: createInbound("direct",{ "tag": "" }),
inData: <InData>{},
title: "add",
loading: false,
side: "s",
inTypes: InTypes,
inboundStats: false,
tls_id: { value: 0 },
HasOptionalUser: [InTypes.Mixed,InTypes.SOCKS,InTypes.HTTP,InTypes.Shadowsocks],
HasInData: [
InTypes.SOCKS,
InTypes.HTTP,
InTypes.Shadowsocks,
InTypes.VMess,
InTypes.ShadowTLS,
InTypes.Trojan,
InTypes.Hysteria,
InTypes.VLESS,
InTypes.TUIC,
InTypes.Hysteria2,
InTypes.Naive,
]
}
},
methods: {
updateData() {
if (this.$props.id != -1) {
if (this.$props.index != -1) {
const newData = JSON.parse(this.$props.data)
this.inbound = createInbound(newData.type, newData)
this.tls_id.value = this.$props.tlsConfigs?.findLast((t:any) => t.inbounds?.includes(this.inbound.tag))?.id?? 0
if (this.HasInData.includes(this.inbound.type)){
this.inData = this.$props.cData?.length> 0 ? <InData>JSON.parse(this.$props.cData) : <InData>{id: 0, tag: this.inbound.tag, addrs: [], outJson: {}}
} else {
this.inData = <InData>{id: -1}
}
this.title = "edit"
}
else {
const port = RandomUtil.randomIntRange(10000, 60000)
this.inbound = createInbound("direct",{ tag: "direct-"+port ,listen: "::", listen_port: port })
this.tls_id.value = 0
if (this.HasInData.includes(this.inbound.type)){
this.inData = <InData>{id: 0, tag: this.inbound.tag, addrs: [], outJson: {}}
} else {
this.inData = <InData>{id: -1}
}
this.title = "add"
}
this.inboundStats = this.$props.stats
this.side = "s"
},
changeType() {
// Tag change only in add outbound
const tag = this.$props.id != -1 ? this.inbound.tag : this.inbound.type + "-" + this.inbound.listen_port
const tag = this.$props.index != -1 ? this.inbound.tag : this.inbound.type + "-" + this.inbound.listen_port
// Use previous data
const prevConfig = { tag: tag ,listen: this.inbound.listen, listen_port: this.inbound.listen_port }
this.inbound = createInbound(this.inbound.type, prevConfig)
if (this.HasInData.includes(this.inbound.type)){
this.inData = <InData>{id: 0, tag: this.inbound.tag, addrs: [], outJson: {}}
} else {
this.inData = <InData>{id: -1}
}
this.side = "s"
},
add_addr() {
this.inData.addrs.push(<Addr>{ server: location.hostname, server_port: this.inbound.listen_port })
},
closeModal() {
this.updateData() // reset
@@ -116,7 +186,7 @@ export default {
},
saveChanges() {
this.loading = true
this.$emit('save', this.inbound, this.inboundStats, this.tls_id.value)
this.$emit('save', this.inbound, this.inboundStats, this.tls_id.value, this.inData)
this.loading = false
},
},
@@ -127,6 +197,10 @@ export default {
}
},
},
components: { Listen, InTls, Hysteria2, Naive, Direct, Shadowsocks, Users, Hysteria, ShadowTls, TProxy, Multiplex, Tuic, Transport }
components: {
Listen, InTls, Hysteria2, Naive, Direct, Shadowsocks,
Users, Hysteria, ShadowTls, TProxy, Multiplex, Tuic, Transport,
AddrVue, OutJsonVue
}
}
</script>
+1 -1
View File
@@ -89,7 +89,7 @@ import RandomUtil from '@/plugins/randomUtil'
import Dial from '@/components/Dial.vue'
import Multiplex from '@/components/Multiplex.vue'
import Transport from '@/components/Transport.vue'
import OutTLS from '@/components/OutTLS.vue'
import OutTLS from '@/components/tls/OutTLS.vue'
import Direct from '@/components/protocols/Direct.vue'
import Socks from '@/components/protocols/Socks.vue'
import Http from '@/components/protocols/Http.vue'
+2 -2
View File
@@ -289,8 +289,8 @@
<script lang="ts">
import { iTls, defaultInTls } from '@/types/inTls'
import { oTls, defaultOutTls } from '@/types/outTls'
import AcmeVue from '@/components/Acme.vue'
import EchVue from '@/components/Ech.vue'
import AcmeVue from '@/components/tls/Acme.vue'
import EchVue from '@/components/tls/Ech.vue'
import HttpUtils from '@/plugins/httputil'
import { push } from 'notivue'
import { i18n } from '@/locales'
+15
View File
@@ -0,0 +1,15 @@
export interface Addr {
server: string
server_port: number
tls?: boolean
insecure?: boolean
server_name?: string
remark?: string
}
export interface InData {
id: number
tag: string
addrs: Addr[]
outJson: any
}
+286 -73
View File
@@ -14,49 +14,63 @@ function utf8ToBase64(utf8String: string): string {
}
export namespace LinkUtil {
export function linkGenerator(user: string, inbound: Inbound, tlsClient: any = null): string {
const addr = location.hostname
export function linkGenerator(user: string, inbound: Inbound, tlsClient: any = {}, addrs: any[] = []): string[] {
switch(inbound.type){
case InTypes.Shadowsocks:
return shadowsocksLink(user,<Shadowsocks>inbound, addr)
return shadowsocksLink(user,<Shadowsocks>inbound, addrs)
case InTypes.Naive:
return naiveLink(user,<Naive>inbound, addr, tlsClient)
return naiveLink(user,<Naive>inbound, addrs, tlsClient)
case InTypes.Hysteria:
return hysteriaLink(user,<Hysteria>inbound, addr, tlsClient)
return hysteriaLink(user,<Hysteria>inbound, addrs, tlsClient)
case InTypes.Hysteria2:
return hysteria2Link(user,<Hysteria2>inbound, addr, tlsClient)
return hysteria2Link(user,<Hysteria2>inbound, addrs, tlsClient)
case InTypes.TUIC:
return tuicLink(user,<TUIC>inbound, addr, tlsClient)
return tuicLink(user,<TUIC>inbound, addrs, tlsClient)
case InTypes.VLESS:
return vlessLink(user,<VLESS>inbound, addr, tlsClient)
return vlessLink(user,<VLESS>inbound, addrs, tlsClient)
case InTypes.Trojan:
return trojanLink(user,<Trojan>inbound, addr, tlsClient)
return trojanLink(user,<Trojan>inbound, addrs, tlsClient)
case InTypes.VMess:
return vmessLink(user,<VMess>inbound, addr, tlsClient)
return vmessLink(user,<VMess>inbound, addrs, tlsClient)
}
return ''
return []
}
function shadowsocksLink(user: string, inbound: Shadowsocks, addr: string): string {
function shadowsocksLink(user: string, inbound: Shadowsocks, addrs: any[]): string[] {
const userPass = inbound.users?.find(i => i.name == user)?.password
const password = [userPass]
if (inbound.method.startsWith('2022')) password.push(inbound.password)
const params = {
tfo: inbound.tcp_fast_open? 1 : null,
network: inbound.network?? null
}
const uri = new URL(`ss://${utf8ToBase64(inbound.method + ':' + password.join(':'))}@${addr}:${inbound.listen_port}`)
for (const [key, value] of Object.entries(params)){
if (value) {
uri.searchParams.set(key, value.toString())
}
}
uri.hash = encodeURIComponent(inbound.tag)
return uri.toString()
let links = <string[]>[]
if (addrs.length == 0) {
const uri = new URL(`ss://${utf8ToBase64(inbound.method + ':' + password.join(':'))}@${location.hostname}:${inbound.listen_port}`)
for (const [key, value] of Object.entries(params)){
if (value) {
uri.searchParams.set(key, value.toString())
}
}
uri.hash = encodeURIComponent(inbound.tag)
links.push(uri.toString())
} else {
addrs.forEach(a => {
const uri = new URL(`ss://${utf8ToBase64(inbound.method + ':' + password.join(':'))}@${a.server}:${a.server_port}`)
for (const [key, value] of Object.entries(params)){
if (value) {
uri.searchParams.set(key, value.toString())
}
}
uri.hash = encodeURIComponent(a.remark ? inbound.tag + a.remark : inbound.tag)
links.push(uri.toString())
})
}
return links
}
function hysteriaLink(user: string, inbound: Hysteria, addr: string, tlsClient: any): string {
function hysteriaLink(user: string, inbound: Hysteria, addrs: any[], tlsClient: any): string[] {
const auth = inbound.users.find(i => i.name == user)?.auth_str
const params = {
upmbps: inbound.up_mbps?? null,
@@ -68,17 +82,43 @@ export namespace LinkUtil {
fastopen: inbound.tcp_fast_open? 1 : 0,
insecure: tlsClient?.insecure ? 1 : null
}
const uri = new URL(`hysteria://${addr}:${inbound.listen_port}`)
for (const [key, value] of Object.entries(params)){
if (value) {
uri.searchParams.set(key, value.toString())
let links = <string[]>[]
if (addrs.length == 0) {
const uri = new URL(`hysteria://${location.hostname}:${inbound.listen_port}`)
for (const [key, value] of Object.entries(params)){
if (value) {
uri.searchParams.set(key, value.toString())
}
}
uri.hash = encodeURIComponent(inbound.tag)
links.push(uri.toString())
} else {
addrs.forEach(a => {
const uri = new URL(`hysteria://${a.server}:${a.server_port}`)
for (const [key, value] of Object.entries(params)){
if (value) {
uri.searchParams.set(key, value.toString())
}
}
if (a.server_name?.length>0) {
uri.searchParams.set('peer', a.server_name)
} else {
inbound.tls.server_name ? uri.searchParams.set('peer', inbound.tls.server_name) : uri.searchParams.delete('peer')
}
if (a.insecure) {
uri.searchParams.set('insecure', '1')
} else {
tlsClient.insecure ? uri.searchParams.set('insecure', '1') : uri.searchParams.delete('insecure')
}
uri.hash = encodeURIComponent(a.remark ? inbound.tag + a.remark : inbound.tag)
links.push(uri.toString())
})
}
uri.hash = encodeURIComponent(inbound.tag)
return uri.toString()
return links
}
function hysteria2Link(user: string, inbound: Hysteria2, addr: string, tlsClient: any): string {
function hysteria2Link(user: string, inbound: Hysteria2, addrs: any[], tlsClient: any): string[] {
const password = inbound.users.find(i => i.name == user)?.password
const params = {
upmbps: inbound.up_mbps?? null,
@@ -90,36 +130,85 @@ export namespace LinkUtil {
fastopen: inbound.tcp_fast_open? 1 : 0,
insecure: tlsClient?.insecure ? 1 : null
}
const uri = new URL(`hysteria2://${password}@${addr}:${inbound.listen_port}`)
for (const [key, value] of Object.entries(params)){
if (value) {
uri.searchParams.set(key, value.toString())
let links = <string[]>[]
if (addrs.length == 0) {
const uri = new URL(`hysteria2://${password}@${location.hostname}:${inbound.listen_port}`)
for (const [key, value] of Object.entries(params)){
if (value) {
uri.searchParams.set(key, value.toString())
}
}
uri.hash = encodeURIComponent(inbound.tag)
links.push(uri.toString())
} else {
addrs.forEach(a => {
const uri = new URL(`hysteria2://${password}@${a.server}:${a.server_port}`)
for (const [key, value] of Object.entries(params)){
if (value) {
uri.searchParams.set(key, value.toString())
}
}
if (a.server_name?.length>0) {
uri.searchParams.set('sni', a.server_name)
} else {
inbound.tls.server_name ? uri.searchParams.set('sni', inbound.tls.server_name) : uri.searchParams.delete('sni')
}
if (a.insecure) {
uri.searchParams.set('insecure', '1')
} else {
tlsClient.insecure ? uri.searchParams.set('insecure', '1') : uri.searchParams.delete('insecure')
}
uri.hash = encodeURIComponent(a.remark ? inbound.tag + a.remark : inbound.tag)
links.push(uri.toString())
})
}
uri.hash = encodeURIComponent(inbound.tag)
return uri.toString()
return links
}
function naiveLink(user: string, inbound: Naive, addr: string, tlsClient: any): string {
function naiveLink(user: string, inbound: Naive, addrs: any[], tlsClient: any): string[] {
const password = inbound.users.find(i => i.username == user)?.password
const params = {
padding: 1,
peer: inbound.tls.server_name?? null,
alpn: inbound.tls.alpn?.join(',')?? null,
tfo: inbound.tcp_fast_open? 1 : 0,
allowInsecure: tlsClient?.insecure ? 1 : null
}
const uri = `http2://${utf8ToBase64(user + ":" + password + "@" + addr + ":" + inbound.listen_port)}`
const paramsArray = []
for (const [key, value] of Object.entries(params)){
if (value) {
paramsArray.push(`${key}=${encodeURIComponent(value.toString())}`)
let links = <string[]>[]
if (addrs.length == 0) {
const params = {
padding: 1,
peer: inbound.tls.server_name?? null,
alpn: inbound.tls.alpn?.join(',')?? null,
tfo: inbound.tcp_fast_open? 1 : 0,
allowInsecure: tlsClient?.insecure ? 1 : null
}
const uri = `http2://${utf8ToBase64(user + ":" + password + "@" + location.hostname + ":" + inbound.listen_port)}`
const paramsArray = []
for (const [key, value] of Object.entries(params)){
if (value) {
paramsArray.push(`${key}=${encodeURIComponent(value.toString())}`)
}
}
links.push(uri.toString() + "?" + paramsArray.join('&') + "#" + inbound.tag)
} else {
addrs.forEach(a => {
const params = {
padding: 1,
peer: a.server_name?.length>0 ? a.server_name : inbound.tls.server_name?? null,
alpn: inbound.tls.alpn?.join(',')?? null,
tfo: inbound.tcp_fast_open? 1 : 0,
allowInsecure: a.insecure ? 1 : tlsClient?.insecure ? 1 : null
}
const uri = `http2://${utf8ToBase64(user + ":" + password + "@" + a.server + ":" + a.server_port)}`
const paramsArray = []
for (const [key, value] of Object.entries(params)){
if (value) {
paramsArray.push(`${key}=${encodeURIComponent(value.toString())}`)
}
}
links.push(uri.toString() + "?" + paramsArray.join('&') + "#" + encodeURIComponent(a.remark ? inbound.tag + a.remark : inbound.tag))
})
}
return uri.toString() + "?" + paramsArray.join('&') + "#" + inbound.tag
return links
}
function tuicLink(user: string, inbound: TUIC, addr: string, tlsClient: any): string {
function tuicLink(user: string, inbound: TUIC, addrs: any[], tlsClient: any): string[] {
const u = inbound.users.find(i => i.name == user)
const params = {
sni: inbound.tls.server_name?? null,
@@ -128,14 +217,40 @@ export namespace LinkUtil {
allowInsecure: tlsClient?.insecure ? 1 : null,
disable_sni: tlsClient?.disable_sni ? 1 : null
}
const uri = new URL(`tuic://${u?.uuid}:${u?.password}@${addr}:${inbound.listen_port}`)
for (const [key, value] of Object.entries(params)){
if (value) {
uri.searchParams.set(key, value.toString())
let links = <string[]>[]
if (addrs.length == 0) {
const uri = new URL(`tuic://${u?.uuid}:${u?.password}@${location.hostname}:${inbound.listen_port}`)
for (const [key, value] of Object.entries(params)){
if (value) {
uri.searchParams.set(key, value.toString())
}
}
uri.hash = encodeURIComponent(inbound.tag)
links.push(uri.toString())
} else {
addrs.forEach(a => {
const uri = new URL(`tuic://${u?.uuid}:${u?.password}@${a.server}:${a.server_port}`)
for (const [key, value] of Object.entries(params)){
if (value) {
uri.searchParams.set(key, value.toString())
}
}
if (a.server_name?.length>0) {
uri.searchParams.set('sni', a.server_name)
} else {
inbound.tls.server_name ? uri.searchParams.set('sni', inbound.tls.server_name) : uri.searchParams.delete('sni')
}
if (a.insecure) {
uri.searchParams.set('allowInsecure', '1')
} else {
tlsClient.insecure ? uri.searchParams.set('allowInsecure', '1') : uri.searchParams.delete('allowInsecure')
}
uri.hash = encodeURIComponent(a.remark ? inbound.tag + a.remark : inbound.tag)
links.push(uri.toString())
})
}
uri.hash = encodeURIComponent(inbound.tag)
return uri.toString()
return links
}
function getTransportParams(t:Transport): any {
@@ -171,7 +286,7 @@ export namespace LinkUtil {
return params
}
function vlessLink(user: string, inbound: VLESS, addr: string, tlsClient: any): string {
function vlessLink(user: string, inbound: VLESS, addrs: any[], tlsClient: any): string[] {
const u = inbound.users.find(i => i.name == user)
const transport = <Transport>inbound.transport
@@ -188,17 +303,52 @@ export namespace LinkUtil {
pbk: tlsClient?.reality?.public_key?? null,
sid: inbound.tls?.reality?.enabled ? (inbound.tls?.reality?.short_id?.length>0 ? inbound.tls.reality.short_id[RandomUtil.randomInt(inbound.tls.reality.short_id.length)] : null) : null
}
const uri = new URL(`vless://${u?.uuid}@${addr}:${inbound.listen_port}`)
for (const [key, value] of Object.entries({...params, ...tParams})){
if (value) {
uri.searchParams.set(key, value.toString())
let links = <string[]>[]
if (addrs.length == 0) {
const uri = new URL(`vless://${u?.uuid}@${location.hostname}:${inbound.listen_port}`)
for (const [key, value] of Object.entries({...params, ...tParams})){
if (value) {
uri.searchParams.set(key, value.toString())
}
}
uri.hash = encodeURIComponent(inbound.tag)
links.push(uri.toString())
} else {
addrs.forEach(a => {
const uri = new URL(`vless://${u?.uuid}@${a.server}:${a.server_port}`)
for (const [key, value] of Object.entries({...params, ...tParams})){
if (value) {
uri.searchParams.set(key, value.toString())
}
}
if (a.tls != undefined){
if (a.tls) {
uri.searchParams.set('security','tls')
} else {
uri.searchParams.delete('security')
uri.searchParams.delete('sni')
uri.searchParams.delete('alpn')
uri.searchParams.delete('allowInsecure')
}
}
if (a.server_name?.length>0) {
uri.searchParams.set('sni', a.server_name)
} else {
inbound.tls?.server_name ? uri.searchParams.set('sni', inbound.tls.server_name) : uri.searchParams.delete('sni')
}
if (a.insecure) {
uri.searchParams.set('allowInsecure', '1')
} else {
tlsClient.insecure ? uri.searchParams.set('allowInsecure', '1') : uri.searchParams.delete('allowInsecure')
}
uri.hash = encodeURIComponent(a.remark ? inbound.tag + a.remark : inbound.tag)
links.push(uri.toString())
})
}
uri.hash = encodeURIComponent(inbound.tag)
return uri.toString()
return links
}
function trojanLink(user: string, inbound: Trojan, addr: string, tlsClient: any): string {
function trojanLink(user: string, inbound: Trojan, addrs: any[], tlsClient: any): string[] {
const u = inbound.users.find(i => i.name == user)
const transport = <Transport>inbound.transport
@@ -214,17 +364,53 @@ export namespace LinkUtil {
pbk: tlsClient?.reality?.public_key?? null,
sid: inbound.tls?.reality?.enabled ? (inbound.tls?.reality?.short_id?.length>0 ? inbound.tls.reality.short_id[RandomUtil.randomInt(inbound.tls.reality.short_id.length)] : null) : null
}
const uri = new URL(`trojan://${u?.password}@${addr}:${inbound.listen_port}`)
for (const [key, value] of Object.entries({...params, ...tParams})){
if (value) {
uri.searchParams.set(key, value.toString())
let links = <string[]>[]
if (addrs.length == 0) {
const uri = new URL(`trojan://${u?.password}@${location.hostname}:${inbound.listen_port}`)
for (const [key, value] of Object.entries({...params, ...tParams})){
if (value) {
uri.searchParams.set(key, value.toString())
}
}
uri.hash = encodeURIComponent(inbound.tag)
links.push(uri.toString())
} else {
addrs.forEach(a => {
const uri = new URL(`trojan://${u?.password}@${a.server}:${a.server_port}`)
for (const [key, value] of Object.entries({...params, ...tParams})){
if (value) {
uri.searchParams.set(key, value.toString())
}
}
if (a.tls != undefined){
if (a.tls) {
uri.searchParams.set('security','tls')
} else {
uri.searchParams.delete('security')
uri.searchParams.delete('sni')
uri.searchParams.delete('alpn')
uri.searchParams.delete('allowInsecure')
}
}
if (a.server_name?.length>0) {
uri.searchParams.set('sni', a.server_name)
} else {
inbound.tls?.server_name ? uri.searchParams.set('sni', inbound.tls.server_name) : uri.searchParams.delete('sni')
}
if (a.insecure) {
uri.searchParams.set('allowInsecure', '1')
} else {
tlsClient.insecure ? uri.searchParams.set('allowInsecure', '1') : uri.searchParams.delete('allowInsecure')
}
uri.hash = encodeURIComponent(a.remark ? inbound.tag + a.remark : inbound.tag)
links.push(uri.toString())
})
}
uri.hash = encodeURIComponent(inbound.tag)
return uri.toString()
return links
}
function vmessLink(user: string, inbound: VMess, addr: string, tlsClient: any): string {
function vmessLink(user: string, inbound: VMess, addrs: any[], tlsClient: any): string[] {
const u = inbound.users.find(i => i.name == user)
const transport = <Transport>inbound.transport
@@ -233,7 +419,7 @@ export namespace LinkUtil {
const params = {
v: 2,
add: addr,
add: location.hostname,
aid: u?.alterId,
host: tParams.host?? undefined,
id: u?.uuid,
@@ -246,6 +432,33 @@ export namespace LinkUtil {
tls: Object.keys(inbound.tls).length>0? 'tls' : 'none',
allowInsecure: tlsClient?.insecure ? 1 : undefined
}
return 'vmess://' + utf8ToBase64(JSON.stringify(params, null, 2))
let links = <string[]>[]
if (addrs.length == 0) {
links.push('vmess://' + utf8ToBase64(JSON.stringify(params, null, 2)))
} else {
addrs.forEach(a => {
let newParams = {...params}
newParams.add = a.server
newParams.port = a.server_port
if (a.tls != undefined){
if (a.tls) {
newParams.tls = 'tls'
} else {
newParams.tls = 'none'
delete newParams.sni
delete newParams.allowInsecure
}
}
if (a.server_name?.length>0) {
newParams.sni = a.server_name
}
if (a.insecure) {
newParams.allowInsecure = 1
}
newParams.ps = encodeURIComponent(a.remark ? inbound.tag + a.remark : inbound.tag)
links.push('vmess://' + utf8ToBase64(JSON.stringify(newParams, null, 2)))
})
}
return links
}
}
+104
View File
@@ -0,0 +1,104 @@
import { Hysteria, Hysteria2, Inbound, InTypes, Shadowsocks, Trojan, TUIC, VLESS, VMess, ShadowTLS } from "@/types/inbounds"
import { iTls } from "@/types/inTls"
import { oTls } from "@/types/outTls"
export function fillData(out: any, inbound: Inbound, tlsClient: any) {
if (Object.hasOwn(inbound, 'tls')) {
const inb = <any>inbound
addTls(out,inb.tls,tlsClient)
} else {
delete out.tls
}
out.type = inbound.type
out.tag = inbound.tag
out.server = location.hostname
out.server_port = inbound.listen_port
switch(inbound.type){
case InTypes.HTTP || InTypes.SOCKS:
return
case InTypes.Shadowsocks:
shadowsocksOut(out, <Shadowsocks>inbound)
return
case InTypes.ShadowTLS:
shadowTlsOut(out, <ShadowTLS>inbound)
return
case InTypes.Hysteria:
hysteriaOut(out, <Hysteria>inbound)
return
case InTypes.Hysteria2:
hysteria2Out(out, <Hysteria2>inbound)
return
case InTypes.TUIC:
tuicOut(out, <TUIC>inbound)
return
case InTypes.VLESS:
vlessOut(out, <VLESS>inbound)
return
case InTypes.Trojan:
trojanOut(out, <Trojan>inbound)
return
case InTypes.VMess:
vmessOut(out, <VMess>inbound)
return
}
Object.keys(out).forEach(key => delete out[key])
}
function addTls(out: any, tls: iTls, tlsClient: oTls){
out.tls = tlsClient
if(tls.enabled) out.tls.enabled = tls.enabled
if(tls.server_name) out.tls.server_name = tls.server_name
if(tls.alpn) out.tls.alpn = tls.alpn
if(tls.min_version) out.tls.min_version = tls.min_version
if(tls.max_version) out.tls.max_version = tls.max_version
if(tls.cipher_suites) out.tls.cipher_suites = tls.cipher_suites
}
function shadowsocksOut(out: any, inbound: Shadowsocks) {
out.method = inbound.method
out.multiplex = inbound.multiplex
}
function shadowTlsOut(out: any, inbound: ShadowTLS) {
if (inbound.version == 3) {
out.version = 3
} else {
Object.keys(out).forEach(key => delete out[key])
}
out.tls = { enabled: true }
}
function hysteriaOut(out: any, inbound: Hysteria) {
out.up_mbps = inbound.down_mbps
out.down_mbps = inbound.up_mbps
out.obfs = inbound.obfs
out.recv_window_conn = inbound.recv_window_conn
out.disable_mtu_discovery = inbound.disable_mtu_discovery
}
function hysteria2Out(out: any, inbound: Hysteria2) {
out.up_mbps = inbound.down_mbps
out.down_mbps = inbound.up_mbps
out.obfs = inbound.obfs
}
function tuicOut(out: any, inbound: TUIC) {
out.congestion_control = inbound.congestion_control?? "cubic"
out.zero_rtt_handshake = inbound.zero_rtt_handshake
out.heartbeat = inbound.heartbeat
}
function vlessOut(out: any, inbound: VLESS) {
out.multiplex = inbound.multiplex
out.transport = inbound.transport
}
function trojanOut(out: any, inbound: Trojan) {
out.multiplex = inbound.multiplex
out.transport = inbound.transport
}
function vmessOut(out: any, inbound: VMess) {
out.multiplex = inbound.multiplex
out.transport = inbound.transport
}
+3 -3
View File
@@ -37,11 +37,11 @@ export const FindDiff = {
return differences
},
Clients(value1: any[], value2: any[]): any {
ArrObj(value1: any[], value2: any[], key: string): any {
const differences: any[] = []
value1.forEach((v1,index) => {
if(index >= value2.length) differences.push({key: "clients", action: "new", obj: v1})
else if(!this.deepCompare(v1,value2[index])) differences.push({key: "clients", action: "edit", obj: v1})
if(index >= value2.length) differences.push({key: key, action: "new", obj: v1})
else if(!this.deepCompare(v1,value2[index])) differences.push({key: key, action: "edit", obj: v1})
})
return differences
},
+29 -7
View File
@@ -10,10 +10,11 @@ const Data = defineStore('Data', {
reloadItems: localStorage.getItem("reloadItems")?.split(',')?? <string[]>[],
subURI: "",
onlines: {inbound: <string[]>[], outbound: <string[]>[], user: <string[]>[]},
oldData: <{config: any, clients: any[], tlsConfigs: any[]}>{},
config: {},
oldData: <{config: any, clients: any[], tlsConfigs: any[], inData: any[]}>{},
config: <any>{},
clients: [],
tlsConfigs: [],
inData: [],
}),
actions: {
async loadData() {
@@ -25,6 +26,7 @@ const Data = defineStore('Data', {
if (msg.obj.config) this.oldData.config = msg.obj.config
if (msg.obj.clients) this.oldData.clients = msg.obj.clients
if (msg.obj.tls) this.oldData.tlsConfigs = msg.obj.tls
if (msg.obj.inData) this.oldData.inData = msg.obj.inData
this.onlines = msg.obj.onlines
if (msg.obj.lastLog) {
push.error({
@@ -41,31 +43,51 @@ const Data = defineStore('Data', {
if (data.config) this.config = data.config
if (data.clients) this.clients = data.clients
if (data.tls) this.tlsConfigs = data.tls
if (data.inData) this.inData = data.inData
}
}
},
async pushData() {
const diff = {
config: JSON.stringify(FindDiff.Config(this.config,this.oldData.config)),
clients: JSON.stringify(FindDiff.Clients(this.clients,this.oldData.clients)),
tls: JSON.stringify(FindDiff.Clients(this.tlsConfigs,this.oldData.tlsConfigs)),
config: JSON.stringify(FindDiff.Config(this.config,this.oldData.config), null, 2),
clients: JSON.stringify(FindDiff.ArrObj(this.clients,this.oldData.clients, "clients"), null, 2),
tls: JSON.stringify(FindDiff.ArrObj(this.tlsConfigs,this.oldData.tlsConfigs, "tls"), null, 2),
inData: JSON.stringify(FindDiff.ArrObj(this.inData,this.oldData.inData, "inData"), null, 2),
}
const msg = await HttpUtils.post('api/save',diff)
if(msg.success) {
this.lastLoad = 0
this.loadData()
}
},
async delInbound(index: number) {
const diff = {
config: JSON.stringify([{key: "inbounds", action: "del", index: index, obj: null}]),
clients: JSON.stringify(FindDiff.Clients(this.clients,this.oldData.clients)),
tls: JSON.stringify(FindDiff.Clients(this.tlsConfigs,this.oldData.tlsConfigs)),
clients: JSON.stringify(FindDiff.ArrObj(this.clients,this.oldData.clients, "clients"), null, 2),
tls: JSON.stringify(FindDiff.ArrObj(this.tlsConfigs,this.oldData.tlsConfigs, "tls"), null, 2),
inData: <string|undefined> undefined,
}
// Validate inData
let invalidInData = <any[]>[]
this.inData.forEach((d:any) => {
const inboundIndex = this.config.inbounds.findIndex((i:any) => i.tag == d.tag)
if (inboundIndex == -1) invalidInData.push({key: "inData", action: "del", index: d.id, obj: null})
})
if (invalidInData.length>0) {
diff.inData = JSON.stringify(invalidInData)
}
const msg = await HttpUtils.post('api/save',diff)
if(msg.success) {
this.loadData()
}
},
async delInData(id: number) {
const diff = {
inData: JSON.stringify([{key: "inData", action: "del", index: id, obj: null}])
}
await HttpUtils.post('api/save',diff)
},
async delOutbound(index: number) {
const diff = {
config: JSON.stringify([{key: "outbounds", action: "del", index: index, obj: null}]),
+7 -3
View File
@@ -291,9 +291,13 @@ const updateLinks = (c:Client):Link[] => {
const newLinks = <Link[]>[]
clientInbounds.forEach(i =>{
const tlsConfig = <any>Data().tlsConfigs?.findLast((t:any) => t.inbounds.includes(i.tag))
const uri = LinkUtil.linkGenerator(c.name,i,tlsConfig?.client)
if (uri.length>0){
newLinks.push(<Link>{ type: 'local', remark: i.tag, uri: uri })
const cData = <any>Data().inData?.findLast((d:any) => d.tag == i.tag)
const addrs = cData ? <any[]>cData.addrs : []
const uris = LinkUtil.linkGenerator(c.name,i, tlsConfig?.client?? {}, addrs)
if (uris.length>0){
uris.forEach(uri => {
newLinks.push(<Link>{ type: 'local', remark: i.tag, uri: uri })
})
}
})
let links = c.links && c.links.length>0? c.links : <Link[]>[]
+58 -23
View File
@@ -2,9 +2,10 @@
<InboundVue
v-model="modal.visible"
:visible="modal.visible"
:id="modal.id"
:index="modal.index"
:stats="modal.stats"
:data="modal.data"
:cData="modal.cData"
:inTags="inTags"
:outTags="outTags"
:tlsConfigs="tlsConfigs"
@@ -111,6 +112,7 @@ import { Client } from '@/types/clients'
import { Link, LinkUtil } from '@/plugins/link'
import { i18n } from '@/locales'
import { push } from 'notivue'
import { fillData } from '@/plugins/outJson'
const appConfig = computed((): Config => {
return <Config> Data().config
@@ -124,6 +126,10 @@ const tlsConfigs = computed((): any[] => {
return <any[]> Data().tlsConfigs
})
const inData = computed((): any[] => {
return <any[]> Data().inData
})
const inTags = computed((): string[] => {
return inbounds.value?.map(i => i.tag)
})
@@ -146,44 +152,56 @@ const v2rayStats = computed((): V2rayApiStats => {
const modal = ref({
visible: false,
id: -1,
index: -1,
data: "",
cData: "",
stats: false,
})
let delOverlay = ref(new Array<boolean>)
const showModal = (id: number) => {
modal.value.id = id
modal.value.data = id == -1 ? '' : JSON.stringify(inbounds.value[id])
modal.value.stats = id == -1 ? false : v2rayStats.value.inbounds.includes(inbounds.value[id].tag)
const showModal = (index: number) => {
modal.value.index = index
if (index == -1){
modal.value.data = ''
modal.value.cData = ''
modal.value.stats = false
} else {
modal.value.data = JSON.stringify(inbounds.value[index])
modal.value.stats = v2rayStats.value.inbounds.includes(inbounds.value[index].tag)
const inDataIndex = inData.value.findIndex(d => d.tag == inbounds.value[index].tag)
modal.value.cData = inDataIndex == -1 ? '' : JSON.stringify(inData.value[inDataIndex])
}
modal.value.visible = true
}
const closeModal = () => {
modal.value.visible = false
}
const saveModal = (data:Inbound, stats: boolean, tls_id: number) => {
const saveModal = (data:Inbound, stats: boolean, tls_id: number, cData: any) => {
// Check duplicate tag
const oldTag = modal.value.id != -1 ? inbounds.value[modal.value.id].tag : null
const oldTag = modal.value.index != -1 ? inbounds.value[modal.value.index].tag : null
if (data.tag != oldTag && inTags.value.includes(data.tag)) {
push.error({
message: i18n.global.t('error.dplData') + ": " + i18n.global.t('objects.tag')
})
return
}
if (cData.id != -1) {
cData.tag = data.tag
fillData(cData.outJson, data,tls_id>0 ? tlsConfigs.value.findLast(t => t.id == tls_id).client : {})
}
// New or Edit
if (modal.value.id == -1) {
if (modal.value.index == -1) {
inbounds.value.push(data)
if (stats && data.tag.length>0) {
v2rayStats.value.inbounds.push(data.tag)
}
// Update tls preset
if (tls_id>0) {
tlsConfigs.value.findLast(t => t.id == tls_id).inbounds.push(data.tag)
if (cData.id != -1){
inData.value.push(cData)
}
} else {
const oldTag = inbounds.value[modal.value.id].tag
const oldTag = inbounds.value[modal.value.index].tag
const sIndex = v2rayStats.value.inbounds.findIndex(i => i == data.tag) // Find if new tag exists
// Update tls preset
@@ -191,9 +209,6 @@ const saveModal = (data:Inbound, stats: boolean, tls_id: number) => {
if (oldTlsConfigIndex != -1){
tlsConfigs.value[oldTlsConfigIndex].inbounds = tlsConfigs?.value[oldTlsConfigIndex].inbounds.filter((i:string) => i != oldTag)
}
if (tls_id>0) {
tlsConfigs.value.findLast(t => t.id == tls_id).inbounds.push(data.tag)
}
if (oldTag != data.tag) {
v2rayStats.value.inbounds = v2rayStats.value.inbounds.filter(item => item != oldTag)
@@ -208,8 +223,24 @@ const saveModal = (data:Inbound, stats: boolean, tls_id: number) => {
if (sIndex != -1) v2rayStats.value.inbounds.splice(sIndex,1)
}
inbounds.value[modal.value.id] = data
inbounds.value[modal.value.index] = data
const inDataIndex = inData.value.findIndex(indata => indata.tag == oldTag)
if (cData.id != -1) {
if (inDataIndex == -1){
inData.value.push(cData)
} else {
inData.value[inDataIndex] = cData
}
} else if (inDataIndex != -1) {
Data().delInData(inData.value[inDataIndex].id)
inData.value.splice(inDataIndex,1)
}
}
// Update tls preset
if (tls_id>0) {
tlsConfigs.value.findLast(t => t.id == tls_id).inbounds.push(data.tag)
}
if (Object.hasOwn(data,'users')) {
// Set users
data = buildInboundsUsers(data)
@@ -226,10 +257,14 @@ const updateLinks = (i: any) => {
const clientInbounds = <Inbound[]>inbounds.value.filter(inb => client?.inbounds.includes(inb.tag))
const newLinks = <Link[]>[]
clientInbounds.forEach(i =>{
const tlsClient = tlsConfigs?.value.findLast((t:any) => t.inbounds.includes(i.tag))?.client?? null
const uri = LinkUtil.linkGenerator(client.name,i, tlsClient)
if (uri.length>0){
newLinks.push(<Link>{ type: 'local', remark: i.tag, uri: uri })
const tlsClient = tlsConfigs?.value.findLast((t:any) => t.inbounds.includes(i.tag))?.client?? {}
const cData = <any>Data().inData?.findLast((d:any) => d.tag == i.tag)
const addrs = cData ? <any[]>cData.addrs : []
const uris = LinkUtil.linkGenerator(client.name,i, tlsClient, addrs)
if (uris.length>0){
uris.forEach(uri => {
newLinks.push(<Link>{ type: 'local', remark: i.tag, uri: uri })
})
}
})
let links = client.links && client.links.length>0? client.links : <Link[]>[]
@@ -251,8 +286,8 @@ const delInbound = (index: number) => {
inbU.users.forEach((u:any) => {
const c_index = clients.value.findIndex(c => u.username? u.username == c.name : u.name == c.name)
if (c_index != -1) {
const clientInbounds = clients.value[c_index].inbounds.filter((x:string) => x!=tag)
clients.value[c_index].inbounds = clientInbounds
clients.value[c_index].inbounds = clients.value[c_index].inbounds.filter((x:string) => x!=tag)
clients.value[c_index].links = clients.value[c_index].links.filter((x:any) => x.remark!=tag)
}
})
}
+12 -5
View File
@@ -8,7 +8,8 @@
>
<v-tab value="t1">{{ $t('setting.interface') }}</v-tab>
<v-tab value="t2">{{ $t('setting.sub') }}</v-tab>
<v-tab value="t3">Language</v-tab>
<v-tab value="t3">{{ $t('setting.sub') }} JSON</v-tab>
<v-tab value="t4">Language</v-tab>
</v-tabs>
<v-card-text>
<v-row align="center" justify="center" style="margin-bottom: 10px;">
@@ -128,6 +129,10 @@
</v-window-item>
<v-window-item value="t3">
<SubJsonExtVue :settings="settings" />
</v-window-item>
<v-window-item value="t4">
<v-row>
<v-col cols="12" sm="6" md="4">
<v-select
@@ -146,11 +151,12 @@
</template>
<script lang="ts" setup>
import { useLocale } from "vuetify"
import { useLocale } from 'vuetify'
import { languages } from '@/locales'
import { Ref, computed, inject, onMounted, ref } from "vue"
import HttpUtils from "@/plugins/httputil"
import { FindDiff } from "@/plugins/utils"
import { Ref, computed, inject, onMounted, ref } from 'vue'
import HttpUtils from '@/plugins/httputil'
import { FindDiff } from '@/plugins/utils'
import SubJsonExtVue from '@/components/SubJsonExt.vue'
const locale = useLocale()
const tab = ref("t1")
const loading:Ref = inject('loading')?? ref(false)
@@ -177,6 +183,7 @@ const settings = ref({
subEncode: "true",
subShowInfo: "false",
subURI: "",
subJsonExt: "",
})
onMounted(async () => {loadData()})
+7 -3
View File
@@ -144,9 +144,13 @@ const updateLinks = (i:any,tlsClient:any) => {
const clientInbounds = <Inbound[]>inbounds.value.filter(inb => client?.inbounds.includes(inb.tag))
const newLinks = <Link[]>[]
clientInbounds.forEach(i =>{
const uri = LinkUtil.linkGenerator(client.name,i,tlsClient)
if (uri.length>0){
newLinks.push(<Link>{ type: 'local', remark: i.tag, uri: uri })
const cData = <any>Data().inData?.findLast((d:any) => d.tag == i.tag)
const addrs = cData ? <any[]>cData.addrs : []
const uris = LinkUtil.linkGenerator(client,i, tlsClient, addrs)
if (uris.length>0){
uris.forEach(uri => {
newLinks.push(<Link>{ type: 'local', remark: i.tag, uri: uri })
})
}
})
let links = client.links && client.links.length>0? client.links : <Link[]>[]