Migrated configuration sorting to Pinia store

Fixed #841
This commit is contained in:
Donald Zou
2025-08-12 17:22:40 +08:00
parent ffc9176225
commit 21c6d0b8f9
10 changed files with 137 additions and 55 deletions

View File

@@ -25,6 +25,7 @@
"npm": "^10.5.0", "npm": "^10.5.0",
"ol": "^10.2.1", "ol": "^10.2.1",
"pinia": "^3.0.3", "pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.5.0",
"qrcode": "^1.5.3", "qrcode": "^1.5.3",
"qrcodejs": "^1.0.0", "qrcodejs": "^1.0.0",
"simple-code-editor": "^2.0.9", "simple-code-editor": "^2.0.9",
@@ -3030,6 +3031,12 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/deep-pick-omit": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/deep-pick-omit/-/deep-pick-omit-1.2.1.tgz",
"integrity": "sha512-2J6Kc/m3irCeqVG42T+SaUMesaK7oGWaedGnQQK/+O0gYc+2SP5bKh/KKTE7d7SJ+GCA9UUE1GRzh6oDe0EnGw==",
"license": "MIT"
},
"node_modules/defaults": { "node_modules/defaults": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmmirror.com/defaults/-/defaults-1.0.4.tgz", "resolved": "https://registry.npmmirror.com/defaults/-/defaults-1.0.4.tgz",
@@ -3051,6 +3058,12 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/defu": {
"version": "6.1.4",
"resolved": "https://registry.npmmirror.com/defu/-/defu-6.1.4.tgz",
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
"license": "MIT"
},
"node_modules/delayed-stream": { "node_modules/delayed-stream": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -3060,6 +3073,12 @@
"node": ">=0.4.0" "node": ">=0.4.0"
} }
}, },
"node_modules/destr": {
"version": "2.0.5",
"resolved": "https://registry.npmmirror.com/destr/-/destr-2.0.5.tgz",
"integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==",
"license": "MIT"
},
"node_modules/detect-libc": { "node_modules/detect-libc": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.4.tgz", "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.4.tgz",
@@ -7564,6 +7583,33 @@
} }
} }
}, },
"node_modules/pinia-plugin-persistedstate": {
"version": "4.5.0",
"resolved": "https://registry.npmmirror.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.5.0.tgz",
"integrity": "sha512-QTkP1xJVyCdr2I2p3AKUZM84/e+IS+HktRxKGAIuDzkyaKKV48mQcYkJFVVDuvTxlI5j6X3oZObpqoVB8JnWpw==",
"license": "MIT",
"dependencies": {
"deep-pick-omit": "^1.2.1",
"defu": "^6.1.4",
"destr": "^2.0.5"
},
"peerDependencies": {
"@nuxt/kit": ">=3.0.0",
"@pinia/nuxt": ">=0.10.0",
"pinia": ">=3.0.0"
},
"peerDependenciesMeta": {
"@nuxt/kit": {
"optional": true
},
"@pinia/nuxt": {
"optional": true
},
"pinia": {
"optional": true
}
}
},
"node_modules/pinia/node_modules/@vue/devtools-api": { "node_modules/pinia/node_modules/@vue/devtools-api": {
"version": "7.7.7", "version": "7.7.7",
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.7.tgz", "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.7.tgz",

View File

@@ -29,6 +29,7 @@
"npm": "^10.5.0", "npm": "^10.5.0",
"ol": "^10.2.1", "ol": "^10.2.1",
"pinia": "^3.0.3", "pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.5.0",
"qrcode": "^1.5.3", "qrcode": "^1.5.3",
"qrcodejs": "^1.0.0", "qrcodejs": "^1.0.0",
"simple-code-editor": "^2.0.9", "simple-code-editor": "^2.0.9",

View File

@@ -41,6 +41,7 @@ export default {
<template> <template>
<div class="card shadow-sm rounded-3 peerCard" <div class="card shadow-sm rounded-3 peerCard"
:id="'peer_'+Peer.id"
:class="{'border-warning': Peer.restricted}"> :class="{'border-warning': Peer.restricted}">
<div> <div>
<div v-if="!Peer.restricted" class="card-header bg-transparent d-flex align-items-center gap-2 border-0"> <div v-if="!Peer.restricted" class="card-header bg-transparent d-flex align-items-center gap-2 border-0">

View File

@@ -189,6 +189,17 @@ const searchPeers = computed(() => {
return 0; return 0;
}).slice(0, showPeersCount.value) }).slice(0, showPeersCount.value)
}) })
watch(() => route.query.id, (newValue) => {
if (newValue){
wireguardConfigurationStore.searchString = newValue
}else{
wireguardConfigurationStore.searchString = undefined
}
}, {
immediate: true
})
</script> </script>
<template> <template>

View File

@@ -15,7 +15,6 @@ export default {
return {store, wireguardConfigurationStore} return {store, wireguardConfigurationStore}
}, },
props: { props: {
configuration: Object configuration: Object
}, },
data(){ data(){
@@ -43,18 +42,6 @@ export default {
} }
}, },
methods: { methods: {
debounce(){
if (!this.searchStringTimeout){
this.searchStringTimeout = setTimeout(() => {
this.wireguardConfigurationStore.searchString = this.searchString;
}, 300)
}else{
clearTimeout(this.searchStringTimeout)
this.searchStringTimeout = setTimeout(() => {
this.wireguardConfigurationStore.searchString = this.searchString;
}, 300)
}
},
updateSort(sort){ updateSort(sort){
fetchPost("/api/updateDashboardConfigurationItem", { fetchPost("/api/updateDashboardConfigurationItem", {
section: "Server", section: "Server",

View File

@@ -3,13 +3,14 @@ import {GetLocale} from "@/utilities/locale.js";
import {computed, onMounted, ref, useTemplateRef} from "vue"; import {computed, onMounted, ref, useTemplateRef} from "vue";
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js"; import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
import LocaleText from "@/components/text/localeText.vue"; import LocaleText from "@/components/text/localeText.vue";
import {useRoute, useRouter} from "vue-router";
const searchBarPlaceholder = computed(() => { const searchBarPlaceholder = computed(() => {
return GetLocale("Search Peers...") return GetLocale("Search Peers...")
}) })
let searchStringTimeout = undefined let searchStringTimeout = undefined
const searchString = ref("")
const wireguardConfigurationStore = WireguardConfigurationsStore() const wireguardConfigurationStore = WireguardConfigurationsStore()
const searchString = ref(wireguardConfigurationStore.searchString)
const debounce = () => { const debounce = () => {
if (!searchStringTimeout){ if (!searchStringTimeout){
@@ -27,10 +28,18 @@ const debounce = () => {
const emits = defineEmits(['close']) const emits = defineEmits(['close'])
const input = useTemplateRef('searchBar') const input = useTemplateRef('searchBar')
const route = useRoute()
const router = useRouter()
if (route.query.peer){
searchString.value = route.query.peer
router.replace({ query: null })
}
onMounted(() => { onMounted(() => {
input.value.focus(); input.value.focus();
}) })
</script> </script>
<template> <template>

View File

@@ -29,17 +29,17 @@ export default {
} }
}, },
async mounted() { async mounted() {
if (!window.localStorage.getItem('ConfigurationListSort')){ // if (!window.localStorage.getItem('ConfigurationListSort')){
window.localStorage.setItem('ConfigurationListSort', JSON.stringify(this.currentSort)) // window.localStorage.setItem('ConfigurationListSort', JSON.stringify(this.currentSort))
}else{ // }else{
this.currentSort = JSON.parse(window.localStorage.getItem('ConfigurationListSort')) // this.currentSort = JSON.parse(window.localStorage.getItem('ConfigurationListSort'))
} // }
//
if (!window.localStorage.getItem('ConfigurationListDisplay')){ // if (!window.localStorage.getItem('ConfigurationListDisplay')){
window.localStorage.setItem('ConfigurationListDisplay', this.currentDisplay) // window.localStorage.setItem('ConfigurationListDisplay', this.currentDisplay)
}else{ // }else{
this.currentDisplay = window.localStorage.getItem('ConfigurationListDisplay') // this.currentDisplay = window.localStorage.getItem('ConfigurationListDisplay')
} // }
await this.wireguardConfigurationsStore.getConfigurations(); await this.wireguardConfigurationsStore.getConfigurations();
@@ -54,17 +54,8 @@ export default {
}, },
computed: { computed: {
configurations(){ configurations(){
return [...this.wireguardConfigurationsStore.Configurations] return this.wireguardConfigurationsStore.sortConfigurations
.filter(x => x.Name.toLowerCase().includes(this.searchKey) || x.PublicKey.includes(this.searchKey) || !this.searchKey) .filter(x => x.Name.toLowerCase().includes(this.searchKey) || x.PublicKey.includes(this.searchKey) || !this.searchKey)
.sort((a, b) => {
if (this.currentSort.order === 'desc') {
return this.dotNotation(a, this.currentSort.key) < this.dotNotation(b, this.currentSort.key) ?
1 : this.dotNotation(a, this.currentSort.key) > this.dotNotation(b, this.currentSort.key) ? -1 : 0;
} else {
return this.dotNotation(a, this.currentSort.key) > this.dotNotation(b, this.currentSort.key) ?
1 : this.dotNotation(a, this.currentSort.key) < this.dotNotation(b, this.currentSort.key) ? -1 : 0;
}
})
} }
}, },
methods: { methods: {
@@ -76,18 +67,16 @@ export default {
return result return result
}, },
updateSort(key){ updateSort(key){
if (this.currentSort.key === key){ if (this.wireguardConfigurationsStore.CurrentSort.key === key){
if (this.currentSort.order === 'asc') this.currentSort.order = 'desc' this.wireguardConfigurationsStore.CurrentSort.order =
else this.currentSort.order = 'asc' this.wireguardConfigurationsStore.CurrentSort.order === 'asc' ? 'desc' : 'asc'
}else{ }else{
this.currentSort.key = key this.wireguardConfigurationsStore.CurrentSort.key = key
} }
window.localStorage.setItem('ConfigurationListSort', JSON.stringify(this.currentSort))
}, },
updateDisplay(key){ updateDisplay(key){
if (this.currentDisplay !== key){ if (this.wireguardConfigurationsStore.CurrentDisplay !== key){
this.currentDisplay = key this.wireguardConfigurationsStore.CurrentDisplay = key
window.localStorage.setItem('ConfigurationListDisplay', this.currentDisplay)
} }
} }
} }
@@ -121,12 +110,12 @@ export default {
<div class="d-flex ms-auto ms-lg-0"> <div class="d-flex ms-auto ms-lg-0">
<a role="button" <a role="button"
@click="updateSort(sv)" @click="updateSort(sv)"
:class="{'bg-primary-subtle text-primary-emphasis': this.currentSort.key === sv}" :class="{'bg-primary-subtle text-primary-emphasis': this.wireguardConfigurationsStore.CurrentSort.key === sv}"
class="px-2 py-1 rounded-3" v-for="(s, sv) in this.sort"> class="px-2 py-1 rounded-3" v-for="(s, sv) in this.wireguardConfigurationsStore.SortOptions">
<small> <small>
<i class="bi me-2" <i class="bi me-2"
:class="[this.currentSort.order === 'asc' ? 'bi-sort-up' : 'bi-sort-down']" :class="[this.wireguardConfigurationsStore.CurrentSort.order === 'asc' ? 'bi-sort-up' : 'bi-sort-down']"
v-if="this.currentSort.key === sv"></i>{{s}} v-if="this.wireguardConfigurationsStore.CurrentSort.key === sv"></i>{{s}}
</small> </small>
</a> </a>
</div> </div>
@@ -139,7 +128,7 @@ export default {
<a role="button" <a role="button"
@click="updateDisplay(x.name)" @click="updateDisplay(x.name)"
v-for="x in [{name: 'List', key: 'list'}, {name: 'Grid', key: 'grid'}]" v-for="x in [{name: 'List', key: 'list'}, {name: 'Grid', key: 'grid'}]"
:class="{'bg-primary-subtle text-primary-emphasis': this.currentDisplay === x.name}" :class="{'bg-primary-subtle text-primary-emphasis': this.wireguardConfigurationsStore.CurrentDisplay === x.name}"
class="px-2 py-1 rounded-3"> class="px-2 py-1 rounded-3">
<small> <small>
<i class="bi me-2" :class="'bi-' + x.key"></i> <LocaleText :t="x.name"></LocaleText> <i class="bi me-2" :class="'bi-' + x.key"></i> <LocaleText :t="x.name"></LocaleText>
@@ -167,7 +156,7 @@ export default {
<LocaleText t="You don't have any WireGuard configurations yet. Please check the configuration folder or change it in Settings. By default the folder is /etc/wireguard."></LocaleText> <LocaleText t="You don't have any WireGuard configurations yet. Please check the configuration folder or change it in Settings. By default the folder is /etc/wireguard."></LocaleText>
</p> </p>
<ConfigurationCard <ConfigurationCard
:display="this.currentDisplay" :display="this.wireguardConfigurationsStore.CurrentDisplay"
v-for="(c, index) in configurations" v-for="(c, index) in configurations"
:delay="index*0.03 + 's'" :delay="index*0.03 + 's'"
v-else-if="this.configurationLoaded" v-else-if="this.configurationLoaded"

View File

@@ -74,14 +74,14 @@ export default {
</RouterLink></li> </RouterLink></li>
<li class="nav-item"> <li class="nav-item">
<RouterLink class="nav-link rounded-3" to="/settings" <RouterLink class="nav-link rounded-3" to="/settings"
exact-active-class="active"> active-class="active">
<i class="bi bi-gear me-2"></i> <i class="bi bi-gear me-2"></i>
<LocaleText t="Settings"></LocaleText> <LocaleText t="Settings"></LocaleText>
</RouterLink> </RouterLink>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<RouterLink class="nav-link rounded-3" to="/clients" <RouterLink class="nav-link rounded-3" to="/clients"
exact-active-class="active"> active-class="active">
<i class="bi bi-people me-2"></i> <i class="bi bi-people me-2"></i>
<LocaleText t="Clients"></LocaleText> <LocaleText t="Clients"></LocaleText>
</RouterLink> </RouterLink>
@@ -98,7 +98,7 @@ export default {
<LocaleText t="WireGuard Configurations"></LocaleText> <LocaleText t="WireGuard Configurations"></LocaleText>
</h6> </h6>
<ul class="nav flex-column px-2 gap-1"> <ul class="nav flex-column px-2 gap-1">
<li class="nav-item" v-for="c in this.wireguardConfigurationsStore.Configurations"> <li class="nav-item" v-for="c in this.wireguardConfigurationsStore.sortConfigurations">
<RouterLink :to="'/configuration/'+c.Name + '/peers'" class="nav-link nav-conf-link rounded-3" <RouterLink :to="'/configuration/'+c.Name + '/peers'" class="nav-link nav-conf-link rounded-3"
active-class="active" active-class="active"
> >

View File

@@ -9,6 +9,7 @@ import { createPinia } from 'pinia'
import App from './App.vue' import App from './App.vue'
import router from './router/router.js' import router from './router/router.js'
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js"; import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
let Locale; let Locale;
await fetch("/api/locale") await fetch("/api/locale")
.then(res => res.json()) .then(res => res.json())
@@ -19,11 +20,12 @@ await fetch("/api/locale")
const app = createApp(App) const app = createApp(App)
app.use(router) app.use(router)
const pinia = createPinia(); const pinia = createPinia();
pinia.use(piniaPluginPersistedstate)
pinia.use(({ store }) => { pinia.use(({ store }) => {
store.$router = markRaw(router) store.$router = markRaw(router)
}) })
app.use(pinia) app.use(pinia)
const store = DashboardConfigurationStore() const store = DashboardConfigurationStore()
store.Locale = Locale; store.Locale = Locale;

View File

@@ -5,9 +5,19 @@ import {GetLocale} from "@/utilities/locale.js";
export const WireguardConfigurationsStore = defineStore('WireguardConfigurationsStore', { export const WireguardConfigurationsStore = defineStore('WireguardConfigurationsStore', {
state: () => ({ state: () => ({
Configurations: undefined, Configurations: [],
searchString: "", searchString: "",
ConfigurationListInterval: undefined, ConfigurationListInterval: undefined,
SortOptions: {
Name: GetLocale("Name"),
Status: GetLocale("Status"),
'DataUsage.Total': GetLocale("Total Usage")
},
CurrentSort: {
key: "Name",
order: "asc"
},
CurrentDisplay: "List",
PeerScheduleJobs: { PeerScheduleJobs: {
dropdowns: { dropdowns: {
Field: [ Field: [
@@ -66,6 +76,19 @@ export const WireguardConfigurationsStore = defineStore('WireguardConfigurations
} }
} }
}), }),
getters: {
sortConfigurations(){
return [...this.Configurations].sort((a, b) => {
if (this.CurrentSort.order === 'desc') {
return this.dotNotation(a, this.CurrentSort.key) < this.dotNotation(b, this.CurrentSort.key) ?
1 : this.dotNotation(a, this.CurrentSort.key) > this.dotNotation(b, this.CurrentSort.key) ? -1 : 0;
} else {
return this.dotNotation(a, this.CurrentSort.key) > this.dotNotation(b, this.CurrentSort.key) ?
1 : this.dotNotation(a, this.CurrentSort.key) < this.dotNotation(b, this.CurrentSort.key) ? -1 : 0;
}
})
},
},
actions: { actions: {
async getConfigurations(){ async getConfigurations(){
await fetchGet("/api/getWireguardConfigurations", {}, (res) => { await fetchGet("/api/getWireguardConfigurations", {}, (res) => {
@@ -73,6 +96,14 @@ export const WireguardConfigurationsStore = defineStore('WireguardConfigurations
// this.Configurations = [] // this.Configurations = []
}); });
}, },
dotNotation(object, dotNotation){
let result = dotNotation.split('.').reduce((o, key) => o && o[key], object)
if (typeof result === "string"){
return result.toLowerCase()
}
return result
},
regexCheckIP(ip){ regexCheckIP(ip){
let regex = /((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))/; let regex = /((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))/;
return regex.test(ip) return regex.test(ip)
@@ -84,5 +115,10 @@ export const WireguardConfigurationsStore = defineStore('WireguardConfigurations
const reg = /^[A-Za-z0-9+/]{43}=?=?$/; const reg = /^[A-Za-z0-9+/]{43}=?=?$/;
return reg.test(key) return reg.test(key)
} }
},
persist: {
pick: [
"CurrentSort", "CurrentDisplay"
]
} }
}); });