initial commit
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<v-text-field
|
||||
id="expiry"
|
||||
:label="$t('date.expiry')"
|
||||
v-model="dateFormatted"
|
||||
prepend-inner-icon="mdi-calendar"
|
||||
readonly
|
||||
hide-details
|
||||
></v-text-field>
|
||||
<DatePicker
|
||||
v-model="Input"
|
||||
@input="Input=$event"
|
||||
:locale="$i18n.locale"
|
||||
element="expiry"
|
||||
compact-time
|
||||
type="datetime">
|
||||
<template v-slot:next-month>
|
||||
<v-icon icon="mdi-chevron-right" />
|
||||
</template>
|
||||
<template v-slot:prev-month>
|
||||
<v-icon icon="mdi-chevron-left" />
|
||||
</template>
|
||||
<template #submit-btn="{ submit, canSubmit }">
|
||||
<v-btn
|
||||
:disabled="!canSubmit"
|
||||
@click="submit"
|
||||
>{{ $t('submit') }}</v-btn>
|
||||
</template>
|
||||
<template #cancel-btn="{ vm }">
|
||||
<v-btn
|
||||
@click="reset(vm)"
|
||||
>{{ $t('reset') }}</v-btn>
|
||||
</template>
|
||||
<template #now-btn="{ goToday }">
|
||||
<v-btn
|
||||
@click="goToday"
|
||||
>{{ $t('now') }}</v-btn>
|
||||
</template>
|
||||
</DatePicker>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import DatePicker from 'vue3-persian-datetime-picker'
|
||||
import { i18n } from '@/locales'
|
||||
|
||||
export default {
|
||||
props: ['expiry'],
|
||||
emits: ['submit'],
|
||||
data() {
|
||||
return {
|
||||
menu: false,
|
||||
input: new Date(),
|
||||
}
|
||||
},
|
||||
components: { DatePicker },
|
||||
computed: {
|
||||
dateFormatted() {
|
||||
if (this.expDate == 0) return i18n.global.t('unlimited')
|
||||
const date = new Date(this.expDate*1000)
|
||||
return date.toLocaleString(i18n.global.locale.value)
|
||||
},
|
||||
expDate() {
|
||||
return parseInt(this.expiry?? 0)
|
||||
},
|
||||
Input: {
|
||||
get() { return this.expDate == 0 ? new Date() : new Date(this.expDate*1000) },
|
||||
set(v:string) {
|
||||
this.input = new Date(v)
|
||||
this.submit()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateInput(v:Date) {
|
||||
this.input = v
|
||||
},
|
||||
setNow() {
|
||||
this.input = new Date()
|
||||
},
|
||||
submit() {
|
||||
this.$emit('submit',Math.floor(this.input.getTime()/1000))
|
||||
},
|
||||
reset(vm:any) {
|
||||
this.$emit('submit',0)
|
||||
this.input = new Date()
|
||||
vm.visible = false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
menu(v) {
|
||||
if (v) {
|
||||
this.input = this.expiry == 0 ? new Date() : new Date(this.expDate*1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.vpd-addon-list,
|
||||
.vpd-addon-list-item {
|
||||
background-color: rgb(var(--v-theme-background));
|
||||
border-color: rgb(var(--v-theme-background));
|
||||
}
|
||||
.vpd-content {
|
||||
background-color: rgb(var(--v-theme-background));
|
||||
}
|
||||
.vpd-addon-list-item.vpd-selected,
|
||||
.vpd-addon-list-item:hover {
|
||||
background-color: rgb(var(--v-theme-primary));
|
||||
}
|
||||
.vpd-close-addon {
|
||||
color: rgb(var(--v-theme-on-surface));
|
||||
background-color: transparent;
|
||||
}
|
||||
.vpd-controls {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.vpd-month-label {
|
||||
width: auto;
|
||||
}
|
||||
.vpd-actions button:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
.vpd-wrapper[data-type=datetime].vpd-compact-time .vpd-time {
|
||||
border-top: 0;
|
||||
}
|
||||
.vpd-time .vpd-time-h .vpd-counter-item,
|
||||
.vpd-time .vpd-time-m .vpd-counter-item {
|
||||
vertical-align: top;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<v-card subtitle="Dial" style="background-color: inherit;">
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4" v-if="optionDetour">
|
||||
<v-text-field
|
||||
label="Forward to Outbound tag"
|
||||
hide-details
|
||||
v-model="dial.detour"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" v-if="optionBind">
|
||||
<v-text-field
|
||||
label="Bind to Network Interface"
|
||||
hide-details
|
||||
v-model="dial.bind_interface"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4" v-if="optionIPV4">
|
||||
<v-text-field
|
||||
label="Bind to IPv4"
|
||||
hide-details
|
||||
v-model="dial.inet4_bind_address"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" v-if="optionIPV6">
|
||||
<v-text-field
|
||||
label="Bind to IPv6"
|
||||
hide-details
|
||||
v-model="dial.inet6_bind_address"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4" v-if="optionRM">
|
||||
<v-text-field
|
||||
label="Linux Routing Mark"
|
||||
hide-details
|
||||
type="number"
|
||||
min="0"
|
||||
v-model.number="routingMark"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" v-if="optionRA">
|
||||
<v-switch v-model="dial.reuse_addr" color="primary" label="Reuse listener address" hide-details></v-switch>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="optionTCP">
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-switch v-model="dial.tcp_fast_open" color="primary" label="TCP Fast Open" hide-details></v-switch>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-switch v-model="dial.tcp_multi_path" color="primary" label="TCP Multi Path" hide-details></v-switch>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4" v-if="optionUDP">
|
||||
<v-switch v-model="dial.udp_fragment" color="primary" label="UDP Fragment" hide-details></v-switch>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" v-if="optionCT">
|
||||
<v-text-field
|
||||
label="Connection Timeout"
|
||||
hide-details
|
||||
type="number"
|
||||
min="1"
|
||||
suffix="s"
|
||||
v-model.number="connectTimeout"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="optionDS">
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-select
|
||||
hide-details
|
||||
clearable
|
||||
@click:clear="delete dial.domain_strategy"
|
||||
width="100"
|
||||
label="Domain to IP Strategy"
|
||||
:items="['prefer_ipv4','prefer_ipv6','ipv4_only','ipv6_only']"
|
||||
v-model="dial.domain_strategy">
|
||||
</v-select>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Fallback Timeout"
|
||||
hide-details
|
||||
type="number"
|
||||
min="50"
|
||||
step="50"
|
||||
suffix="ms"
|
||||
v-model.number="fallbackDelay"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-card-actions class="pt-0">
|
||||
<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>Dial Options</v-btn>
|
||||
</template>
|
||||
<v-card>
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionDetour" color="primary" label="Detour" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionBind" color="primary" label="Bind Interface" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionIPV4" color="primary" label="Bind to IPv4" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionIPV6" color="primary" label="Bind to IPv6" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionRM" color="primary" label="Routing Mark" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionRA" color="primary" label="Reuse Address" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionTCP" color="primary" label="TCP Options" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionUDP" color="primary" label="UDP Options" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionCT" color="primary" label="Connection Timeout" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionDS" color="primary" label="Domain Strategy" 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: ['dial'],
|
||||
data() {
|
||||
return {
|
||||
menu: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
fallbackDelay: {
|
||||
get() { return this.$props.dial.fallback_delay ? parseInt(this.$props.dial.fallback_delay.replace('ms','')) : 300 },
|
||||
set(newValue:number) { this.$props.dial.fallback_delay = newValue > 0 ? newValue + 'ms' : '300ms' }
|
||||
},
|
||||
connectTimeout: {
|
||||
get() { return this.$props.dial.connect_timeout ? parseInt(this.$props.dial.connect_timeout.replace('s','')) : 5 },
|
||||
set(newValue:number) { this.$props.dial.connect_timeout = newValue > 0 ? newValue + 's' : '5s' }
|
||||
},
|
||||
routingMark: {
|
||||
get() { return this.$props.dial.routing_mark?? 0 },
|
||||
set(newValue:number) { this.$props.dial.routing_mark = newValue > 0 ? newValue : 0 }
|
||||
},
|
||||
optionDetour: {
|
||||
get(): boolean { return this.$props.dial.detour != undefined },
|
||||
set(v:boolean) { v ? this.$props.dial.detour = '' : delete this.$props.dial.detour }
|
||||
},
|
||||
optionBind: {
|
||||
get(): boolean { return this.$props.dial.bind_interface != undefined },
|
||||
set(v:boolean) { v ? this.$props.dial.bind_interface = '' : delete this.$props.dial.bind_interface }
|
||||
},
|
||||
optionIPV4: {
|
||||
get(): boolean { return this.$props.dial.inet4_bind_address != undefined },
|
||||
set(v:boolean) { v ? this.$props.dial.inet4_bind_address = '' : delete this.$props.dial.inet4_bind_address }
|
||||
},
|
||||
optionIPV6: {
|
||||
get(): boolean { return this.$props.dial.inet6_bind_address != undefined },
|
||||
set(v:boolean) { v ? this.$props.dial.inet6_bind_address = '' : delete this.$props.dial.inet6_bind_address }
|
||||
},
|
||||
optionRM: {
|
||||
get(): boolean { return this.$props.dial.routing_mark != undefined },
|
||||
set(v:boolean) { v ? this.$props.dial.routing_mark = 0 : delete this.$props.dial.routing_mark }
|
||||
},
|
||||
optionRA: {
|
||||
get(): boolean { return this.$props.dial.reuse_addr != undefined },
|
||||
set(v:boolean) { v ? this.$props.dial.reuse_addr = true : delete this.$props.dial.reuse_addr }
|
||||
},
|
||||
optionTCP: {
|
||||
get(): boolean {
|
||||
return this.$props.dial.tcp_fast_open != undefined &&
|
||||
this.$props.dial.tcp_multi_path != undefined
|
||||
},
|
||||
set(v:boolean) {
|
||||
if (v) {
|
||||
this.$props.dial.tcp_fast_open = false
|
||||
this.$props.dial.tcp_multi_path = false
|
||||
} else {
|
||||
delete this.$props.dial.tcp_fast_open
|
||||
delete this.$props.dial.tcp_multi_path
|
||||
}
|
||||
}
|
||||
},
|
||||
optionUDP: {
|
||||
get(): boolean { return this.$props.dial.udp_fragment != undefined },
|
||||
set(v:boolean) { v ? this.$props.dial.udp_fragment = true : delete this.$props.dial.udp_fragment }
|
||||
},
|
||||
optionCT: {
|
||||
get(): boolean { return this.$props.dial.connect_timeout != undefined },
|
||||
set(v:boolean) { v ? this.$props.dial.connect_timeout = '5s' : delete this.$props.dial.connect_timeout }
|
||||
},
|
||||
optionDS: {
|
||||
get(): boolean { return this.$props.dial.domain_strategy != undefined },
|
||||
set(v:boolean) {
|
||||
if (v) {
|
||||
this.$props.dial.domain_strategy = 'prefer_ipv4'
|
||||
this.$props.dial.fallback_delay = '300ms'
|
||||
} else {
|
||||
delete this.$props.dial.domain_strategy
|
||||
delete this.$props.dial.fallback_delay
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<v-card :subtitle="$t('in.multiplex')">
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-switch color="primary" label="Enable Multiplex" v-model="muxEnable" hide-details></v-switch>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" v-if="mux.enabled">
|
||||
<v-switch color="primary" label="Reject Non-Padded" v-model="mux.padding" hide-details></v-switch>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" v-if="mux.enabled">
|
||||
<v-switch color="primary" label="Enable Brutal" v-model="burtalEnable" hide-details></v-switch>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="mux.brutal?.enabled">
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Uplink Bandwidth"
|
||||
hide-details
|
||||
type="number"
|
||||
suffix="Mbps"
|
||||
v-model.number="up_mbps">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Downlink Bandwidth"
|
||||
hide-details
|
||||
type="number"
|
||||
suffix="Mbps"
|
||||
min="0"
|
||||
v-model.number="down_mbps">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { iMultiplex } from '@/types/inMultiplex'
|
||||
export default {
|
||||
props: ['inbound'],
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
mux(): iMultiplex {
|
||||
return <iMultiplex> this.$props.inbound.multiplex
|
||||
},
|
||||
muxEnable: {
|
||||
get(): boolean { return this.$props.inbound.multiplex ? this.mux.enabled : false },
|
||||
set(newValue:boolean) { this.$props.inbound.multiplex = newValue ? { enabled: newValue } : {} }
|
||||
},
|
||||
burtalEnable: {
|
||||
get(): boolean { return this.mux.brutal ? this.mux.brutal.enabled : false },
|
||||
set(newValue:boolean) { this.mux.brutal = { enabled: newValue, up_mbps: 100, down_mbps: 100 } }
|
||||
},
|
||||
down_mbps: {
|
||||
get() { return this.mux.brutal && this.mux.brutal.down_mbps ? this.mux.brutal.down_mbps : 0 },
|
||||
set(newValue:any) {
|
||||
if (this.mux.brutal){
|
||||
this.mux.brutal.down_mbps = newValue.length != 0 ? newValue : 0
|
||||
}
|
||||
}
|
||||
},
|
||||
up_mbps: {
|
||||
get() { return this.mux.brutal && this.mux.brutal.up_mbps ? this.mux.brutal.up_mbps : 0 },
|
||||
set(newValue:any) {
|
||||
if (this.mux.brutal){
|
||||
this.mux.brutal.up_mbps = newValue.length != 0 ? newValue : 0
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,214 @@
|
||||
<template>
|
||||
<v-card :subtitle="$t('in.tls')">
|
||||
<v-row v-if="tlsOptional">
|
||||
<v-col cols="auto">
|
||||
<v-switch color="primary" :label="$t('tls.enable')" v-model="tlsEnable" hide-details></v-switch>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<template v-if="tls.enabled">
|
||||
<v-row>
|
||||
<v-col cols="auto">
|
||||
<v-btn-toggle v-model="usePath"
|
||||
class="rounded-xl"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
shaped
|
||||
mandatory>
|
||||
<v-btn
|
||||
@click="tls.key=undefined; tls.certificate=undefined"
|
||||
>{{ $t('tls.usePath') }}</v-btn>
|
||||
<v-btn
|
||||
@click="tls.key_path=undefined; tls.certificate_path=undefined"
|
||||
>{{ $t('tls.useText') }}</v-btn>
|
||||
</v-btn-toggle>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="usePath == 0">
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
:label="$t('tls.certPath')"
|
||||
hide-details
|
||||
v-model="tls.certificate_path">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
:label="$t('tls.keyPath')"
|
||||
hide-details
|
||||
v-model="tls.key_path">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-else>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-textarea
|
||||
:label="$t('tls.cert')"
|
||||
hide-details
|
||||
v-model="certText">
|
||||
</v-textarea>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-textarea
|
||||
:label="$t('tls.key')"
|
||||
hide-details
|
||||
v-model="keyText">
|
||||
</v-textarea>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4" v-if="tls.server_name != undefined">
|
||||
<v-text-field
|
||||
label="SNI"
|
||||
hide-details
|
||||
v-model="tls.server_name">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" v-if="tls.alpn">
|
||||
<v-select
|
||||
hide-details
|
||||
label="ALPN"
|
||||
multiple
|
||||
:items="alpn"
|
||||
v-model="tls.alpn">
|
||||
</v-select>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4" v-if="tls.min_version">
|
||||
<v-select
|
||||
hide-details
|
||||
label="Minimum Version"
|
||||
:items="tlsVersions"
|
||||
v-model="tls.min_version">
|
||||
</v-select>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" v-if="tls.max_version">
|
||||
<v-select
|
||||
hide-details
|
||||
label="Maximum Version"
|
||||
:items="tlsVersions"
|
||||
v-model="tls.max_version">
|
||||
</v-select>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" md="8" v-if="tls.cipher_suites != undefined">
|
||||
<v-select
|
||||
hide-details
|
||||
label="Cipher Suites"
|
||||
multiple
|
||||
:items="cipher_suites"
|
||||
v-model="tls.cipher_suites">
|
||||
</v-select>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
<v-card-actions>
|
||||
<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>TLS Options</v-btn>
|
||||
</template>
|
||||
<v-card>
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionSNI" color="primary" label="SNI" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionALPN" color="primary" label="ALPN" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionMinV" color="primary" label="Min Version" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionMaxV" color="primary" label="Max Version" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionCS" color="primary" label="Cipher Suites" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</v-menu>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { iTls, defaultInTls } from '@/types/inTls'
|
||||
export default {
|
||||
props: ['inbound'],
|
||||
data() {
|
||||
return {
|
||||
menu: false,
|
||||
usePath: 0,
|
||||
defaults: defaultInTls,
|
||||
alpn: [
|
||||
{ title: "H3", value: 'HTTP/3' },
|
||||
{ title: "H2", value: 'HTTP/2' },
|
||||
{ title: "Http1.1", value: 'HTTP/1.1' },
|
||||
],
|
||||
tlsVersions: [ '1.0', '1.1', '1.2', '1.3' ],
|
||||
cipher_suites: [
|
||||
{ title: "Automatic", value: "" },
|
||||
{ title: "RSA-AES128-CBC-SHA", value: "TLS_RSA_WITH_AES_128_CBC_SHA" },
|
||||
{ title: "RSA-AES256-CBC-SHA", value: "TLS_RSA_WITH_AES_256_CBC_SHA" },
|
||||
{ title: "RSA-AES128-GCM-SHA256", value: "TLS_RSA_WITH_AES_128_GCM_SHA256" },
|
||||
{ title: "RSA-AES256-GCM-SHA384", value: "TLS_RSA_WITH_AES_256_GCM_SHA384" },
|
||||
{ title: "AES128-GCM-SHA256", value: "TLS_AES_128_GCM_SHA256" },
|
||||
{ title: "AES256-GCM-SHA384", value: "TLS_AES_256_GCM_SHA384" },
|
||||
{ title: "CHACHA20-POLY1305-SHA256", value: "TLS_CHACHA20_POLY1305_SHA256" },
|
||||
{ title: "ECDHE-ECDSA-AES128-CBC-SHA", value: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA" },
|
||||
{ title: "ECDHE-ECDSA-AES256-CBC-SHA", value: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA" },
|
||||
{ title: "ECDHE-RSA-AES128-CBC-SHA", value: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" },
|
||||
{ title: "ECDHE-RSA-AES256-CBC-SHA", value: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" },
|
||||
{ title: "ECDHE-ECDSA-AES128-GCM-SHA256", value: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" },
|
||||
{ title: "ECDHE-ECDSA-AES256-GCM-SHA384", value: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" },
|
||||
{ title: "ECDHE-RSA-AES128-GCM-SHA256", value: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" },
|
||||
{ title: "ECDHE-RSA-AES256-GCM-SHA384", value: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" },
|
||||
{ title: "ECDHE-ECDSA-CHACHA20-POLY1305-SHA256", value: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" },
|
||||
{ title: "ECDHE-RSA-CHACHA20-POLY1305-SHA256", value: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" }
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tls(): iTls {
|
||||
return <iTls> this.$props.inbound.tls
|
||||
},
|
||||
tlsEnable: {
|
||||
get() { return Object.hasOwn(this.$props.inbound.tls, 'enabled') ? this.tls.enabled : false },
|
||||
set(newValue: boolean) { this.$props.inbound.tls = newValue ? { enabled: true } : {} }
|
||||
},
|
||||
tlsOptional(): boolean {
|
||||
return !['hysteria','hysteria2','tuic','naive'].includes(this.$props.inbound.type)
|
||||
},
|
||||
certText: {
|
||||
get(): string { return this.tls.certificate ? this.tls.certificate.join('\n') : '' },
|
||||
set(newValue:string) { this.tls.certificate = newValue.split('\n') }
|
||||
},
|
||||
keyText: {
|
||||
get(): string { return this.tls.key ? this.tls.key.join('\n') : '' },
|
||||
set(newValue:string) { this.tls.key = newValue.split('\n') }
|
||||
},
|
||||
optionSNI: {
|
||||
get(): boolean { return this.tls.server_name != undefined },
|
||||
set(v:boolean) { this.$props.inbound.tls.server_name = v ? '' : undefined }
|
||||
},
|
||||
optionALPN: {
|
||||
get(): boolean { return this.tls.alpn != undefined },
|
||||
set(v:boolean) { this.$props.inbound.tls.alpn = v ? defaultInTls.alpn : undefined }
|
||||
},
|
||||
optionMinV: {
|
||||
get(): boolean { return this.tls.min_version != undefined },
|
||||
set(v:boolean) { this.$props.inbound.tls.min_version = v ? defaultInTls.min_version : undefined }
|
||||
},
|
||||
optionMaxV: {
|
||||
get(): boolean { return this.tls.max_version != undefined },
|
||||
set(v:boolean) { this.$props.inbound.tls.max_version = v ? defaultInTls.max_version : undefined }
|
||||
},
|
||||
optionCS: {
|
||||
get(): boolean { return this.tls.cipher_suites != undefined },
|
||||
set(v:boolean) { this.$props.inbound.tls.cipher_suites = v ? defaultInTls.cipher_suites : undefined }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<v-card subtitle="Listen">
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
:label="$t('in.addr')"
|
||||
hide-details
|
||||
required
|
||||
v-model="inbound.listen">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
:label="$t('in.port')"
|
||||
hide-details
|
||||
type="number"
|
||||
required
|
||||
v-model.number="inbound.listen_port"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4" v-if="optionDetour">
|
||||
<v-text-field
|
||||
label="Forward to Inbound tag"
|
||||
hide-details
|
||||
v-model="inbound.detour"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-switch v-model="inbound.sniff" color="primary" :label="$t('in.sniffing')" hide-details></v-switch>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="inbound.sniff">
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-switch v-model="inbound.sniff_override_destination" color="primary" label="Override Sniffed Domain" hide-details></v-switch>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Sniffing Timeout"
|
||||
hide-details
|
||||
type="number"
|
||||
min="50"
|
||||
step="50"
|
||||
suffix="ms"
|
||||
v-model.number="sniffTimeout"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="optionTCP">
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-switch v-model="inbound.tcp_fast_open" color="primary" label="TCP Fast Open" hide-details></v-switch>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-switch v-model="inbound.tcp_multi_path" color="primary" label="TCP Multi Path" hide-details></v-switch>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="optionUDP">
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-switch v-model="inbound.udp_fragment" color="primary" label="UDP Fragment" hide-details></v-switch>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="UDP NAT expiration"
|
||||
hide-details
|
||||
type="number"
|
||||
min="1"
|
||||
suffix="Min"
|
||||
v-model.number="udpTimeout"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="optionDS">
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-select
|
||||
hide-details
|
||||
width="100"
|
||||
label="Domain to IP Strategy"
|
||||
:items="['prefer_ipv4','prefer_ipv6','ipv4_only','ipv6_only']"
|
||||
v-model="inbound.domain_strategy">
|
||||
</v-select>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-card-actions class="pt-0">
|
||||
<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>Listen Options</v-btn>
|
||||
</template>
|
||||
<v-card>
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionTCP" color="primary" label="TCP Options" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionUDP" color="primary" label="UDP Options" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionDetour" color="primary" label="Detour" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionDS" color="primary" label="Domain Strategy" 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: ['inbound'],
|
||||
data() {
|
||||
return {
|
||||
menu: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
udpTimeout: {
|
||||
get() { return this.$props.inbound.udp_timeout ? parseInt(this.$props.inbound.udp_timeout.replace('m','')) : 5 },
|
||||
set(newValue:number) { this.$props.inbound.udp_timeout = newValue > 0 ? newValue + 'm' : '5m' }
|
||||
},
|
||||
sniffTimeout: {
|
||||
get() { return this.$props.inbound.sniff_timeout ? parseInt(this.$props.inbound.sniff_timeout.replace('ms','')) : 300 },
|
||||
set(newValue:number) { this.$props.inbound.sniff_timeout = newValue > 0 ? newValue + 'ms' : '300ms' }
|
||||
},
|
||||
optionTCP: {
|
||||
get(): boolean {
|
||||
return this.$props.inbound.tcp_fast_open != undefined &&
|
||||
this.$props.inbound.tcp_multi_path != undefined
|
||||
},
|
||||
set(v:boolean) {
|
||||
this.$props.inbound.tcp_fast_open = v ? false : undefined
|
||||
this.$props.inbound.tcp_multi_path = v ? false : undefined
|
||||
}
|
||||
},
|
||||
optionUDP: {
|
||||
get(): boolean {
|
||||
return this.$props.inbound.udp_fragment != undefined &&
|
||||
this.$props.inbound.udp_timeout != undefined
|
||||
},
|
||||
set(v:boolean) {
|
||||
this.$props.inbound.udp_fragment = v ? false : undefined
|
||||
this.$props.inbound.udp_timeout = v ? false : undefined
|
||||
}
|
||||
},
|
||||
optionDetour: {
|
||||
get(): boolean { return this.$props.inbound.detour != undefined },
|
||||
set(v:boolean) { this.$props.inbound.detour = v ? '' : undefined }
|
||||
},
|
||||
optionDS: {
|
||||
get(): boolean { return this.$props.inbound.domain_strategy != undefined },
|
||||
set(v:boolean) { this.$props.inbound.domain_strategy = v ? 'prefer_ipv4' : undefined }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,218 @@
|
||||
<template>
|
||||
<v-container class="fill-height">
|
||||
<v-responsive :class="reloadItems.length>0 ? 'fill-height text-center' : 'align-center'" >
|
||||
<v-row class="d-flex align-center justify-center">
|
||||
<v-col cols="auto">
|
||||
<v-img src="@/assets/logo.svg" :width="reloadItems.length>0 ? 100 : 200"></v-img>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class="d-flex align-center justify-center">
|
||||
<v-col cols="auto">
|
||||
<v-dialog v-model="menu" :close-on-content-click="false" transition="scale-transition" max-width="800">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" variant="tonal">{{ $t('main.tiles') }} <v-icon icon="mdi-star-plus" /></v-btn>
|
||||
</template>
|
||||
<v-card rounded="xl">
|
||||
<v-card-title>
|
||||
<v-row>
|
||||
<v-col>
|
||||
{{ $t('main.tiles') }}
|
||||
</v-col>
|
||||
<v-spacer></v-spacer>
|
||||
<v-col cols="auto"><v-icon icon="mdi-close" @click="menu = false"></v-icon></v-col>
|
||||
</v-row>
|
||||
</v-card-title>
|
||||
<v-divider></v-divider>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4" v-for="items in menuItems">
|
||||
<v-card variant="flat" :title="items.title">
|
||||
<v-list v-for="item in items.value">
|
||||
<v-list-item>
|
||||
<v-switch
|
||||
v-model="reloadItems"
|
||||
:value="item.value"
|
||||
color="primary"
|
||||
:label="item.title"
|
||||
hide-details></v-switch>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="3" v-for="i in reloadItems" :key="i">
|
||||
<v-card class="rounded-lg" variant="outlined" height="200px"
|
||||
:title="menuItems.flatMap(cat => cat.value).find(m => m.value == i)?.title">
|
||||
<v-card-text style="padding: 0 16px;">
|
||||
<Gauge :tilesData="tilesData" :type="i" v-if="i.charAt(0) == 'g'" />
|
||||
<History :tilesData="tilesData" :type="i" v-if="i.charAt(0) == 'h'" />
|
||||
<template v-if="i == 'i-sys'">
|
||||
<v-row>
|
||||
<v-col cols="3">{{ $t('main.info.host') }}</v-col>
|
||||
<v-col cols="9" style="text-wrap: nowrap; overflow: hidden">{{ tilesData.sys?.hostName }}</v-col>
|
||||
<v-col cols="3">{{ $t('main.info.cpu') }}</v-col>
|
||||
<v-col cols="9">
|
||||
<v-chip density="compact" variant="flat">
|
||||
<v-tooltip activator="parent" location="top" style="direction: ltr;">
|
||||
{{ tilesData.sys?.cpuType }}
|
||||
</v-tooltip>
|
||||
{{ tilesData.sys?.cpuCount }} {{ $t('main.info.core') }}
|
||||
</v-chip>
|
||||
</v-col>
|
||||
<v-col cols="3">IP</v-col>
|
||||
<v-col cols="9">
|
||||
<v-chip density="compact" color="primary" variant="flat" v-if="tilesData.sys?.ipv4?.length>0">
|
||||
<v-tooltip activator="parent" location="top" style="direction: ltr;">
|
||||
<span v-html="tilesData.sys?.ipv4?.join('<br />')"></span>
|
||||
</v-tooltip>
|
||||
IPv4
|
||||
</v-chip>
|
||||
<v-chip density="compact" color="primary" variant="flat" v-if="tilesData.sys?.ipv6?.length>0">
|
||||
<v-tooltip activator="parent" location="top" style="direction: ltr;">
|
||||
<span v-html="tilesData.sys?.ipv6?.join('<br />')"></span>
|
||||
</v-tooltip>
|
||||
IPv6
|
||||
</v-chip>
|
||||
</v-col>
|
||||
<v-col cols="3">S-UI</v-col>
|
||||
<v-col cols="9">
|
||||
<v-chip density="compact" color="primary" variant="flat">
|
||||
<v-tooltip activator="parent" location="top">
|
||||
{{ $t('main.info.threads') }}: {{ tilesData.sys?.appThreads }}<br />
|
||||
{{ $t('main.info.memory') }}: {{ HumanReadable.sizeFormat(tilesData.sys?.appMem) }}
|
||||
</v-tooltip>
|
||||
v{{ tilesData.sys?.appVersion }}
|
||||
</v-chip>
|
||||
</v-col>
|
||||
<v-col cols="3">{{ $t('main.info.uptime') }}</v-col>
|
||||
<v-col cols="9">{{ HumanReadable.formatSecond(tilesData.uptime) }}</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
<template v-if="i == 'i-sbd'">
|
||||
<v-row>
|
||||
<v-col cols="4">{{ $t('main.info.running') }}</v-col>
|
||||
<v-col cols="8">
|
||||
<v-chip density="compact" color="success" variant="flat" v-if="tilesData.sbd?.running">{{ $t('yes') }}</v-chip>
|
||||
<v-chip density="compact" color="error" variant="flat" v-else>{{ $t('no') }}</v-chip>
|
||||
</v-col>
|
||||
<v-col cols="4">{{ $t('main.info.memory') }}</v-col>
|
||||
<v-col cols="8">
|
||||
<v-chip density="compact" color="primary" variant="flat" v-if="tilesData.sbd?.stats?.Alloc">
|
||||
{{ HumanReadable.sizeFormat(tilesData.sbd?.stats?.Alloc) }}
|
||||
</v-chip>
|
||||
</v-col>
|
||||
<v-col cols="4">{{ $t('main.info.threads') }}</v-col>
|
||||
<v-col cols="8">
|
||||
<v-chip density="compact" color="primary" variant="flat" v-if="tilesData.sbd?.stats?.NumGoroutine">
|
||||
{{ tilesData.sbd?.stats?.NumGoroutine }}
|
||||
</v-chip>
|
||||
</v-col>
|
||||
<v-col cols="4">{{ $t('main.info.uptime') }}</v-col>
|
||||
<v-col cols="8">{{ HumanReadable.formatSecond(tilesData.sbd?.stats?.Uptime) }}</v-col>
|
||||
<v-col cols="4">{{ $t('online') }}</v-col>
|
||||
<v-col cols="8">
|
||||
<template v-if="tilesData.sbd?.running">
|
||||
<v-chip density="compact" color="primary" variant="flat" v-if="Data().onlines.user">
|
||||
<v-tooltip activator="parent" location="top" :text="$t('pages.clients')" />
|
||||
{{ Data().onlines.user?.length }}
|
||||
</v-chip>
|
||||
<v-chip density="compact" color="success" variant="flat" v-if="Data().onlines.inbound">
|
||||
<v-tooltip activator="parent" location="top" :text="$t('pages.inbounds')" />
|
||||
{{ Data().onlines.inbound?.length }}
|
||||
</v-chip>
|
||||
<v-chip density="compact" color="info" variant="flat" v-if="Data().onlines.outbound">
|
||||
<v-tooltip activator="parent" location="top" :text="$t('pages.outbounds')" />
|
||||
{{ Data().onlines.outbound?.length }}
|
||||
</v-chip>
|
||||
</template>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-responsive>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import HttpUtils from '@/plugins/httputil'
|
||||
import { HumanReadable } from '@/plugins/utils'
|
||||
import Data from '@/store/modules/data'
|
||||
import Gauge from '@/components/tiles/Gauge.vue'
|
||||
import History from '@/components/tiles/History.vue'
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { i18n } from '@/locales'
|
||||
|
||||
const menu = ref(false)
|
||||
const menuItems = [
|
||||
{ title: i18n.global.t('main.gauges'), value: [
|
||||
{ title: i18n.global.t('main.gauge.cpu'), value: "g-cpu" },
|
||||
{ title: i18n.global.t('main.gauge.mem'), value: "g-mem" },
|
||||
]
|
||||
},
|
||||
{ title: i18n.global.t('main.charts'), value: [
|
||||
{ title: i18n.global.t('main.chart.cpu'), value: "h-cpu" },
|
||||
{ title: i18n.global.t('main.chart.mem'), value: "h-mem" },
|
||||
{ title: i18n.global.t('main.chart.net'), value: "h-net" },
|
||||
{ title: i18n.global.t('main.chart.pnet'), value: "hp-net" },
|
||||
]
|
||||
},
|
||||
{ title: i18n.global.t('main.infos'), value: [
|
||||
{ title: i18n.global.t('main.info.sys'), value: "i-sys" },
|
||||
{ title: i18n.global.t('main.info.sbd'), value: "i-sbd" },
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
const tilesData = ref(<any>{})
|
||||
|
||||
const reloadItems = computed({
|
||||
get() { return Data().reloadItems },
|
||||
set(v:string[]) {
|
||||
if (Data().reloadItems.length == 0 && v.length>0) startTimer()
|
||||
if (Data().reloadItems.length > 0 && v.length == 0) stopTimer()
|
||||
Data().reloadItems = v
|
||||
v.length>0 ? localStorage.setItem("reloadItems",v.join(',')) : localStorage.removeItem("reloadItems")
|
||||
}
|
||||
})
|
||||
|
||||
const reloadData = async () => {
|
||||
const request = [...new Set(reloadItems.value.map(r => r.split('-')[1]))]
|
||||
const data = await HttpUtils.get('/api/status',{ r: request.join(',')})
|
||||
if (data.success) {
|
||||
tilesData.value = data.obj
|
||||
}
|
||||
}
|
||||
|
||||
let intervalId: NodeJS.Timeout | null = null
|
||||
|
||||
const startTimer = () => {
|
||||
intervalId = setInterval(() => {
|
||||
reloadData()
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
const stopTimer = () => {
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
intervalId = null
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (Data().reloadItems.length != 0) {
|
||||
reloadData()
|
||||
startTimer()
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
stopTimer()
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<v-select
|
||||
hide-details
|
||||
:label="$t('network')"
|
||||
:items="networks"
|
||||
v-model="Network">
|
||||
</v-select>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
props: ['inbound'],
|
||||
data() {
|
||||
return {
|
||||
networks: [
|
||||
{ title: "TCP/UDP", value: '' },
|
||||
{ title: "TCP", value: 'tcp' },
|
||||
{ title: "UDP", value: 'udp' },
|
||||
],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
Network: {
|
||||
get():string { return this.$props.inbound.network?? '' },
|
||||
set(v:string) { this.$props.inbound.network = v != '' ? v : undefined }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<v-card :subtitle="$t('in.transport')">
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-switch color="primary" :label="$t('transport.enable')" v-model="tpEnable" hide-details></v-switch>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" v-if="tpEnable">
|
||||
<v-select
|
||||
hide-details
|
||||
width="100"
|
||||
:label="$t('type')"
|
||||
:items="Object.keys(trspTypes).map((key,index) => ({title: key, value: Object.values(trspTypes)[index]}))"
|
||||
v-model="transportType">
|
||||
</v-select>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<Http v-if="Transport.type == trspTypes.HTTP" :transport="Transport" />
|
||||
<WebSocket v-if="Transport.type == trspTypes.WebSocket" :transport="Transport" />
|
||||
<GRPC v-if="Transport.type == trspTypes.gRPC" :transport="Transport" />
|
||||
<HttpUpgrade v-if="Transport.type == trspTypes.HTTPUpgrade" :transport="Transport" />
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { TrspTypes, Transport } from '@/types/transport'
|
||||
import Http from './transports/Http.vue'
|
||||
import WebSocket from './transports/WebSocket.vue'
|
||||
import GRPC from './transports/gRPC.vue'
|
||||
import HttpUpgrade from './transports/HttpUpgrade.vue'
|
||||
export default {
|
||||
props: ['inbound'],
|
||||
data() {
|
||||
return {
|
||||
trspTypes: TrspTypes
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
Transport() {
|
||||
return <Transport>this.$props.inbound.transport
|
||||
},
|
||||
tpEnable: {
|
||||
get() { return Object.hasOwn(this.$props.inbound.transport, 'type') },
|
||||
set(newValue: boolean) { this.$props.inbound.transport = newValue ? { type: 'http' } : {} }
|
||||
},
|
||||
transportType: {
|
||||
get() { return this.Transport.type },
|
||||
set(newValue: string) { this.$props.inbound.transport = { type: newValue } }
|
||||
}
|
||||
},
|
||||
components: { Http, WebSocket, GRPC, HttpUpgrade }
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<v-card subtitle="Clients">
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-switch
|
||||
v-model="hasUser"
|
||||
@change="() => {inbound.users = hasUser? [] : undefined}"
|
||||
color="primary"
|
||||
:label="$t('in.clients')"
|
||||
hide-details></v-switch>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
export default {
|
||||
props: ['inbound', 'id'],
|
||||
data() {
|
||||
return {
|
||||
hasUser: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cardTitle() {
|
||||
this.hasUser = Object.hasOwn(this.$props.inbound,'users')
|
||||
return this.$props.inbound?.type.toUpperCase()
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.hasUser = Object.hasOwn(this.$props.inbound,'users')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<v-snackbar
|
||||
v-model="sb.showMsg"
|
||||
location="top"
|
||||
:color="snackbar.color"
|
||||
:timeout="snackbar.timeout">
|
||||
{{ snackbar.message }}
|
||||
</v-snackbar>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import Message from '@/store/modules/message'
|
||||
|
||||
const sb = Message()
|
||||
|
||||
const snackbar = ref(sb.snackbar)
|
||||
</script>
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<v-card subtitle="Direct">
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<Network :inbound="inbound" />
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Override Address"
|
||||
hide-details
|
||||
v-model="inbound.override_address">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Override Port"
|
||||
type="number"
|
||||
min="0"
|
||||
hide-details
|
||||
v-model="override_port">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Network from '@/components/Network.vue'
|
||||
|
||||
export default {
|
||||
props: ['inbound'],
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
override_port: {
|
||||
get() { return this.$props.inbound.override_port ? this.$props.inbound.override_port : ''; },
|
||||
set(newValue: any) { this.$props.inbound.override_port = newValue.length == 0 || newValue == 0 ? undefined : parseInt(newValue); }
|
||||
},
|
||||
},
|
||||
components: { Network }
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<v-card subtitle="Hysteria">
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Uplink Limit"
|
||||
hide-details
|
||||
type="number"
|
||||
suffix="Mbps"
|
||||
v-model.number="up_mbps">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Downlink Limit"
|
||||
hide-details
|
||||
type="number"
|
||||
suffix="Mbps"
|
||||
min="0"
|
||||
v-model.number="down_mbps">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="obfs Password"
|
||||
hide-details
|
||||
v-model="inbound.obfs">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
export default {
|
||||
props: ['inbound'],
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
down_mbps: {
|
||||
get() { return this.$props.inbound.down_mbps ? this.$props.inbound.down_mbps : 0 },
|
||||
set(newValue:any) {
|
||||
if (newValue.length != 0 ){
|
||||
this.$props.inbound.down_mbps = newValue
|
||||
this.$props.inbound.down = "" + newValue + " Mbps"
|
||||
} else {
|
||||
this.$props.inbound.down_mbps = 0
|
||||
this.$props.inbound.down = "0 Mbps"
|
||||
}
|
||||
}
|
||||
},
|
||||
up_mbps: {
|
||||
get() { return this.$props.inbound.up_mbps ? this.$props.inbound.up_mbps : 0 },
|
||||
set(newValue:number) { this.$props.inbound.up_mbps = newValue > 0 ? newValue : 0 }
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<v-card subtitle="Hysteria2">
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Masquerade"
|
||||
hide-details
|
||||
v-model="hysteria2.masquerade"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-switch v-model="hysteria2.ignore_client_bandwidth" color="primary" label="Ignore Client Bandwidth" hide-details></v-switch>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="!hysteria2.ignore_client_bandwidth">
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Uplink Limit"
|
||||
hide-details
|
||||
type="number"
|
||||
suffix="Mbps"
|
||||
v-model.number="up_mbps">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Downlink Limit"
|
||||
hide-details
|
||||
type="number"
|
||||
suffix="Mbps"
|
||||
min="0"
|
||||
v-model.number="down_mbps">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="hysteria2.obfs">
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="obfs Password"
|
||||
hide-details
|
||||
v-model="hysteria2.obfs.password">
|
||||
</v-text-field>
|
||||
</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>Options</v-btn>
|
||||
</template>
|
||||
<v-card>
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<v-switch v-model="optionObfs" color="primary" label="Obfs" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</v-menu>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Hysteria2, createInbound } from '@/types/inbounds'
|
||||
|
||||
export default {
|
||||
props: ['inbound'],
|
||||
data() {
|
||||
return {
|
||||
menu: false,
|
||||
hysteria2: <Hysteria2> createInbound("hysteria2",{ "tag": "" }),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
down_mbps: {
|
||||
get() { return this.hysteria2.down_mbps ? this.hysteria2.down_mbps : 0 },
|
||||
set(newValue:any) { this.hysteria2.down_mbps = newValue.length == 0 ? undefined : this.hysteria2.down_mbps }
|
||||
},
|
||||
up_mbps: {
|
||||
get() { return this.hysteria2.up_mbps ? this.hysteria2.up_mbps : 0 },
|
||||
set(newValue:any) { this.hysteria2.up_mbps = newValue.length == 0 ? undefined : this.hysteria2.up_mbps }
|
||||
},
|
||||
optionObfs: {
|
||||
get(): boolean { return this.hysteria2.obfs != undefined },
|
||||
set(v:boolean) { this.$props.inbound.obfs = v ? { type: "salamander", password: ""} : undefined }
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.hysteria2 = <Hysteria2> this.$props.inbound
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<v-card subtitle="Naive">
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<Network :inbound="inbound" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Network from '@/components/Network.vue'
|
||||
|
||||
export default {
|
||||
props: ['inbound'],
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
components: { Network }
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<v-card subtitle="ShadowTls">
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-select
|
||||
hide-details
|
||||
:items="[1,2,3]"
|
||||
label="Version"
|
||||
v-model="version">
|
||||
</v-select>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" v-if="inbound.password != undefined">
|
||||
<v-text-field
|
||||
label="Password"
|
||||
hide-details
|
||||
v-model="inbound.password">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Handshake Server"
|
||||
hide-details
|
||||
v-model="Inbound.handshake.server">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Server Port"
|
||||
type="number"
|
||||
min="0"
|
||||
hide-details
|
||||
v-model="server_port">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<Dial :dial="Inbound.handshake" />
|
||||
<v-row v-if="Inbound.handshake_for_server_name != undefined">
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Add Hanshake Server"
|
||||
hide-details
|
||||
append-icon="mdi-plus"
|
||||
@click:append="addHandshakeServer()"
|
||||
v-model="handshake_server">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-card
|
||||
v-for="(value, key) in Inbound.handshake_for_server_name"
|
||||
border
|
||||
density="compact"
|
||||
style="margin: 5px;"
|
||||
color="background">
|
||||
<v-card-title>
|
||||
<v-row>
|
||||
<v-col>{{ key }}</v-col>
|
||||
<v-spacer></v-spacer>
|
||||
<v-col>
|
||||
<v-btn @click="Inbound.handshake_for_server_name ? delete Inbound.handshake_for_server_name[key] : null"
|
||||
icon="mdi-delete"
|
||||
></v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-title>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Handshake Server"
|
||||
hide-details
|
||||
v-model="value.server">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Server Port"
|
||||
type="number"
|
||||
min="0"
|
||||
hide-details
|
||||
v-model="value.server_port">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<Dial :dial="value" />
|
||||
</v-card>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ShadowTLS } from '@/types/inbounds'
|
||||
import Dial from '../Dial.vue'
|
||||
|
||||
export default {
|
||||
props: ['inbound'],
|
||||
data() {
|
||||
return {
|
||||
handshake_server: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addHandshakeServer() {
|
||||
this.inbound.handshake_for_server_name[this.handshake_server] = {}
|
||||
// Clear the input field after adding the server
|
||||
this.handshake_server = ''
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.version = this.Inbound.version
|
||||
},
|
||||
computed: {
|
||||
version: {
|
||||
get() { this.version = this.Inbound.version; return this.Inbound.version; },
|
||||
set(newValue: any) {
|
||||
switch (newValue) {
|
||||
case 1:
|
||||
this.Inbound.password = undefined
|
||||
this.Inbound.users = undefined
|
||||
this.Inbound.handshake_for_server_name = undefined
|
||||
break;
|
||||
case 2:
|
||||
if (!this.Inbound.password) {
|
||||
this.Inbound.password = ""
|
||||
}
|
||||
this.Inbound.users = undefined
|
||||
if (!this.Inbound.handshake_for_server_name) {
|
||||
this.Inbound.handshake_for_server_name = {}
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
this.Inbound.password = undefined
|
||||
if (Object.hasOwn(this.Inbound, 'users')) {
|
||||
this.Inbound.users = []
|
||||
}
|
||||
if (!this.Inbound.handshake_for_server_name) {
|
||||
this.Inbound.handshake_for_server_name = {}
|
||||
}
|
||||
break;
|
||||
}
|
||||
this.Inbound.version = newValue;
|
||||
}
|
||||
},
|
||||
Inbound(): ShadowTLS {
|
||||
return <ShadowTLS>this.$props.inbound;
|
||||
},
|
||||
server_port: {
|
||||
get() { return this.Inbound.handshake.server_port ? this.Inbound.handshake.server_port : 443; },
|
||||
set(newValue: any) { this.Inbound.handshake.server_port = newValue.length == 0 || newValue == 0 ? 443 : parseInt(newValue); }
|
||||
},
|
||||
},
|
||||
components: { Dial }
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<v-card subtitle="Shadowsocks">
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-select
|
||||
hide-details
|
||||
label="Method"
|
||||
:items="ssMethods"
|
||||
v-model="inbound.method">
|
||||
</v-select>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field v-model="inbound.password" label="Password" hide-details></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<Network :inbound="inbound" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Network from '@/components/Network.vue'
|
||||
|
||||
export default {
|
||||
props: ['inbound'],
|
||||
data() {
|
||||
return {
|
||||
ssMethods: [
|
||||
"none",
|
||||
"aes-128-gcm",
|
||||
"aes-192-gcm",
|
||||
"aes-256-gcm",
|
||||
"chacha20-ietf-poly1305",
|
||||
"xchacha20-ietf-poly1305",
|
||||
"2022-blake3-aes-128-gcm",
|
||||
"2022-blake3-aes-256-gcm",
|
||||
"2022-blake3-chacha20-poly1305"
|
||||
]
|
||||
}
|
||||
},
|
||||
components: { Network }
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<v-card subtitle="TProxy">
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<Network :inbound="inbound" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Network from '@/components/Network.vue'
|
||||
|
||||
export default {
|
||||
props: ['inbound'],
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
components: { Network }
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<v-card subtitle="TUIC">
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-select
|
||||
hide-details
|
||||
label="Congestion Control"
|
||||
:items="congestion_controls"
|
||||
v-model="inbound.congestion_control">
|
||||
</v-select>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-switch color="primary" label="Zero-RTT Handshake" v-model="inbound.zero_rtt_handshake" hide-details></v-switch>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Authentication Timeout"
|
||||
hide-details
|
||||
type="number"
|
||||
suffix="s"
|
||||
min="1"
|
||||
v-model.number="auth_timeout">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Heartbeat"
|
||||
hide-details
|
||||
type="number"
|
||||
suffix="s"
|
||||
min="1"
|
||||
v-model.number="heartbeat">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { TUIC } from '@/types/inbounds'
|
||||
export default {
|
||||
props: ['inbound'],
|
||||
data() {
|
||||
return {
|
||||
congestion_controls: [
|
||||
"cubic","new_reno", "bbr"
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
Inbound(): TUIC {
|
||||
return <TUIC> this.$props.inbound
|
||||
},
|
||||
auth_timeout: {
|
||||
get() { return this.Inbound.auth_timeout ? parseInt(this.Inbound.auth_timeout.replace('s','')) : '' },
|
||||
set(newValue:number) { this.$props.inbound.auth_timeout = newValue ? newValue + 's' : '' }
|
||||
},
|
||||
heartbeat: {
|
||||
get() { return this.Inbound.heartbeat ? parseInt(this.Inbound.heartbeat.replace('s','')) : '' },
|
||||
set(newValue:number) { this.$props.inbound.heartbeat = newValue ? newValue + 's' : '' }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,111 @@
|
||||
<script lang="ts" setup>
|
||||
import { HumanReadable } from '@/plugins/utils';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
tilesData: <any>{},
|
||||
type: String
|
||||
})
|
||||
|
||||
const data = computed(() => {
|
||||
const d = props.tilesData
|
||||
if (!d.mem && !d.cpu) return { percent: 0, text: '-' }
|
||||
switch (props.type) {
|
||||
case 'g-cpu':
|
||||
return { percent: d.cpu, text: Math.ceil(d.cpu) + "%" }
|
||||
case 'g-mem':
|
||||
const curr = HumanReadable.sizeFormat(d.mem.current,0).split(' ')
|
||||
const total = HumanReadable.sizeFormat(d.mem.total,0).split(' ')
|
||||
if (curr[1] == total[1]) curr[1] = ''
|
||||
return {
|
||||
percent: Math.ceil(d.mem.current*100/d.mem.total),
|
||||
text: curr[0] + "<sup>" + (curr[1]?? ' ') + "</sup>/" + total[0] + "<sup>" + (total[1]?? '') + "</sup>"
|
||||
}
|
||||
}
|
||||
return { percent: 0, text: '-'}
|
||||
})
|
||||
|
||||
const cssTransformRotateValue = computed(() => {
|
||||
const percentageAsFraction = data.value.percent / 100
|
||||
const halfPercentage = percentageAsFraction / 2
|
||||
|
||||
return `${halfPercentage}turn`
|
||||
})
|
||||
|
||||
const gaugeColor = computed(() => {
|
||||
if (data.value.percent > 90) return 'error'
|
||||
if (data.value.percent > 70) return 'warning'
|
||||
return 'primary'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="gauge__outer">
|
||||
<div class="gauge__inner">
|
||||
<div
|
||||
class="gauge__fill"
|
||||
:style="{
|
||||
transform: `rotate(${cssTransformRotateValue})`,
|
||||
background: `rgb(var(--v-theme-${gaugeColor}))`
|
||||
}">
|
||||
</div>
|
||||
<span class="gauge__cover" dir="ltr" v-html="data.text">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.gauge__outer {
|
||||
width: 100%;
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
.gauge__inner {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
padding-bottom: 50%;
|
||||
background: rgb(var(--v-theme-surface));
|
||||
position: relative;
|
||||
border-top-left-radius: 100% 200%;
|
||||
border-top-right-radius: 100% 200%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gauge__fill {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
width: inherit;
|
||||
height: 100%;
|
||||
background: rgb(var(--v-theme-primary));
|
||||
transform-origin: center top;
|
||||
transform: rotate(0turn);
|
||||
transition: transform 0.2s ease-out;
|
||||
}
|
||||
|
||||
.gauge__cover {
|
||||
width: 75%;
|
||||
height: 150%;
|
||||
background: rgb(var(--v-theme-background));
|
||||
position: absolute;
|
||||
top: 25%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
border-radius: 50%;
|
||||
|
||||
/* Text */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-bottom: 25%;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Lexend', sans-serif;
|
||||
font-weight: bold;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
sup {
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,200 @@
|
||||
<template>
|
||||
<Line v-if="loaded" :data="data" :options="<any>options" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { Line } from 'vue-chartjs'
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement,
|
||||
Title,
|
||||
Filler,
|
||||
} from 'chart.js'
|
||||
import { HumanReadable } from '@/plugins/utils'
|
||||
ChartJS.register(
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement,
|
||||
Title,
|
||||
Filler
|
||||
)
|
||||
ChartJS.defaults.font.family = 'Vazirmatn'
|
||||
export default {
|
||||
components: {
|
||||
Line
|
||||
},
|
||||
props: ['tilesData','type'],
|
||||
data() {
|
||||
return {
|
||||
loaded: false,
|
||||
labels: new Array(20).fill(''),
|
||||
oldValues: <any>{},
|
||||
options1: {
|
||||
animation: false,
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: {
|
||||
intersect: false,
|
||||
mode: 'index',
|
||||
},
|
||||
plugins: {
|
||||
tooltip: {
|
||||
enabled: false
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
min: 0,
|
||||
max: 100,
|
||||
grid: {
|
||||
color: () => { return this.$vuetify.theme.current.colors.secondary },
|
||||
},
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
steps: 10,
|
||||
stepValue: 5,
|
||||
max: 100
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
optionsNet: {
|
||||
animation: false,
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: {
|
||||
intersect: false,
|
||||
mode: 'index',
|
||||
},
|
||||
plugins: {
|
||||
tooltip: {
|
||||
enabled: false
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
grid: {
|
||||
color: () => { return this.$vuetify.theme.current.colors.secondary },
|
||||
},
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
callback: (label:any, index: number) => { return parseInt(label).toString() },
|
||||
count: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data: ref(<any>{})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
options() {
|
||||
switch (this.$props.type){
|
||||
case "h-net":
|
||||
this.optionsNet.scales.y.ticks.callback = (label:any, index: number) => {
|
||||
return label == 0 ? "0" : HumanReadable.sizeFormat(label,0)
|
||||
}
|
||||
return this.optionsNet
|
||||
case "hp-net":
|
||||
this.optionsNet.scales.y.ticks.callback = (label:any, index: number) => {
|
||||
return label == 0 ? "0" : HumanReadable.packetFormat(label,0)
|
||||
}
|
||||
return this.optionsNet
|
||||
}
|
||||
return this.options1
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateData1(value1: number) {
|
||||
const newData = <number[]>[]
|
||||
if (this.data.datasets){
|
||||
newData.push(...this.data.datasets[0].data,value1)
|
||||
}
|
||||
if (newData.length>20) newData.shift()
|
||||
this.data = {
|
||||
labels: this.labels,
|
||||
datasets: [
|
||||
{
|
||||
label: '',
|
||||
backgroundColor: 'rgba(255, 165, 0, 0.2)',
|
||||
borderColor: 'rgba(255, 165, 0,0.8)',
|
||||
fill: true,
|
||||
data: newData
|
||||
}
|
||||
],
|
||||
}
|
||||
this.loaded = true
|
||||
},
|
||||
updateData2(value1: number, value2:number) {
|
||||
const newData1 = <number[]>[]
|
||||
const newData2 = <number[]>[]
|
||||
if (this.data.datasets){
|
||||
newData1.push(...this.data.datasets[0].data,value1)
|
||||
newData2.push(...this.data.datasets[1].data,value2)
|
||||
}
|
||||
if (newData1.length>20) newData1.shift()
|
||||
if (newData2.length>20) newData2.shift()
|
||||
this.data = {
|
||||
labels: this.labels,
|
||||
datasets: [
|
||||
{
|
||||
label: '',
|
||||
backgroundColor: 'rgba(255, 165, 0, 0.2)',
|
||||
borderColor: 'rgba(255, 165, 0,0.8)',
|
||||
fill: true,
|
||||
data: newData1
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
backgroundColor: 'rgba(0, 128, 0, 0.1)',
|
||||
borderColor: 'rgba(0, 128, 0,0.8)',
|
||||
fill: true,
|
||||
data: newData2
|
||||
}
|
||||
],
|
||||
}
|
||||
this.loaded = true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
tilesData(v:any) {
|
||||
switch (this.$props.type) {
|
||||
case 'h-cpu':
|
||||
this.updateData1(v.cpu)
|
||||
break
|
||||
case 'h-mem':
|
||||
this.updateData1(v.mem.current*100/v.mem.total)
|
||||
break
|
||||
case 'h-net':
|
||||
if (this.oldValues.sent) {
|
||||
const downSpeed = (v.net.recv-this.oldValues.recv)/2 // Each 2 sec
|
||||
const upSpeed = (v.net.sent-this.oldValues.sent)/2 // Each 2 sec
|
||||
this.updateData2(upSpeed,downSpeed)
|
||||
}
|
||||
this.oldValues = v.net
|
||||
break
|
||||
case 'hp-net':
|
||||
if (this.oldValues.psent) {
|
||||
const downSpeed = (v.net.precv-this.oldValues.precv)/2 // Each 2 sec
|
||||
const upSpeed = (v.net.psent-this.oldValues.psent)/2 // Each 2 sec
|
||||
this.updateData2(upSpeed,downSpeed)
|
||||
}
|
||||
this.oldValues = v.net
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
:label="$t('transport.hosts')"
|
||||
hide-details
|
||||
v-model="hosts">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
:label="$t('transport.path')"
|
||||
hide-details
|
||||
v-model="transport.path">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Method"
|
||||
hide-details
|
||||
v-model="transport.method">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Idle Timeout"
|
||||
hide-details
|
||||
type="number"
|
||||
suffix="s"
|
||||
min="1"
|
||||
v-model.number="idle_timeout">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Ping Timeout"
|
||||
hide-details
|
||||
type="number"
|
||||
suffix="s"
|
||||
min="1"
|
||||
v-model.number="ping_timeout">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { HTTP } from '../../types/transport'
|
||||
export default {
|
||||
props: ['transport'],
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
Http(): HTTP {
|
||||
return <HTTP> this.$props.transport?? {}
|
||||
},
|
||||
hosts: {
|
||||
get() { return this.Http.host ? this.Http.host.join(',') : '' },
|
||||
set(newValue:string) { this.$props.transport.host = newValue.length>0 ? newValue.split(',') : [] }
|
||||
},
|
||||
idle_timeout: {
|
||||
get() { return this.Http.idle_timeout ? parseInt(this.Http.idle_timeout.replace('s','')) : '' },
|
||||
set(newValue:number) { this.$props.transport.idle_timeout = newValue ? newValue + 's' : '' }
|
||||
},
|
||||
ping_timeout: {
|
||||
get() { return this.Http.ping_timeout ? parseInt(this.Http.ping_timeout.replace('s','')) : '' },
|
||||
set(newValue:number) { this.$props.transport.ping_timeout = newValue ? newValue + 's' : '' }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
:label="$t('transport.hosts')"
|
||||
hide-details
|
||||
v-model="transport.host">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
:label="$t('transport.path')"
|
||||
hide-details
|
||||
v-model="transport.path">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
props: ['transport'],
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
:label="$t('transport.path')"
|
||||
hide-details
|
||||
v-model="transport.path">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Max Early Data"
|
||||
hide-details
|
||||
type="number"
|
||||
min="0"
|
||||
v-model.number="max_early_data">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Early Data Header Name"
|
||||
hide-details
|
||||
v-model="transport.early_data_header_name">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { WebSocket } from '../../types/transport'
|
||||
export default {
|
||||
props: ['transport'],
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
WS(): WebSocket {
|
||||
return <WebSocket> this.$props.transport
|
||||
},
|
||||
max_early_data: {
|
||||
get() { return this.WS.max_early_data ? this.WS.max_early_data : '' },
|
||||
set(newValue:number) { this.$props.transport.max_early_data = newValue != 0 ? newValue : undefined }
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.WS.early_data_header_name = 'Sec-WebSocket-Protocol'
|
||||
this.WS.path = '/'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Service Name"
|
||||
hide-details
|
||||
v-model="transport.service_name">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-switch
|
||||
color="primary"
|
||||
v-model="transport.permit_without_stream"
|
||||
label="Permit Without Stream"
|
||||
hide-details>
|
||||
</v-switch>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Idle Timeout"
|
||||
hide-details
|
||||
type="number"
|
||||
suffix="s"
|
||||
min="1"
|
||||
v-model.number="idle_timeout">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field
|
||||
label="Ping Timeout"
|
||||
hide-details
|
||||
type="number"
|
||||
suffix="s"
|
||||
min="1"
|
||||
v-model.number="ping_timeout">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { gRPC } from '../../types/transport'
|
||||
export default {
|
||||
props: ['transport'],
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
GRPC(): gRPC {
|
||||
return <gRPC> this.$props.transport?? {}
|
||||
},
|
||||
idle_timeout: {
|
||||
get() { return this.GRPC.idle_timeout ? parseInt(this.GRPC.idle_timeout.replace('s','')) : '' },
|
||||
set(newValue:number) { this.$props.transport.idle_timeout = newValue ? newValue + 's' : '' }
|
||||
},
|
||||
ping_timeout: {
|
||||
get() { return this.GRPC.ping_timeout ? parseInt(this.GRPC.ping_timeout.replace('s','')) : '' },
|
||||
set(newValue:number) { this.$props.transport.ping_timeout = newValue ? newValue + 's' : '' }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user