mirror of
https://github.com/h44z/wg-portal.git
synced 2025-04-19 08:55:12 +00:00
Merge branch 'pr214'
This commit is contained in:
commit
e565e26c65
@ -54,7 +54,7 @@ func generateApi(basePath, apiPath, version string) error {
|
|||||||
OutputDir: filepath.Join(basePath, "core/assets/doc"),
|
OutputDir: filepath.Join(basePath, "core/assets/doc"),
|
||||||
OutputTypes: []string{"json", "yaml"},
|
OutputTypes: []string{"json", "yaml"},
|
||||||
ParseVendor: false,
|
ParseVendor: false,
|
||||||
ParseDependency: true,
|
ParseDependency: 3,
|
||||||
MarkdownFilesDir: "",
|
MarkdownFilesDir: "",
|
||||||
ParseInternal: true,
|
ParseInternal: true,
|
||||||
GeneratedTime: false,
|
GeneratedTime: false,
|
||||||
|
14
frontend/package-lock.json
generated
14
frontend/package-lock.json
generated
@ -1046,9 +1046,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.32",
|
"version": "8.4.35",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
|
||||||
"integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
|
"integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -1148,13 +1148,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "5.0.10",
|
"version": "5.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.1.4.tgz",
|
||||||
"integrity": "sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==",
|
"integrity": "sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.19.3",
|
"esbuild": "^0.19.3",
|
||||||
"postcss": "^8.4.32",
|
"postcss": "^8.4.35",
|
||||||
"rollup": "^4.2.0"
|
"rollup": "^4.2.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { RouterLink, RouterView } from 'vue-router';
|
import { RouterLink, RouterView } from 'vue-router';
|
||||||
import {computed, getCurrentInstance, onMounted, ref} from "vue";
|
import { computed, getCurrentInstance, onMounted, ref } from "vue";
|
||||||
import {authStore} from "./stores/auth";
|
import { authStore } from "./stores/auth";
|
||||||
import {securityStore} from "./stores/security";
|
import { securityStore } from "./stores/security";
|
||||||
import {settingsStore} from "@/stores/settings";
|
import { settingsStore } from "@/stores/settings";
|
||||||
|
|
||||||
const appGlobal = getCurrentInstance().appContext.config.globalProperties
|
const appGlobal = getCurrentInstance().appContext.config.globalProperties
|
||||||
const auth = authStore()
|
const auth = authStore()
|
||||||
@ -80,20 +80,16 @@ const currentYear = ref(new Date().getFullYear())
|
|||||||
|
|
||||||
<div class="navbar-nav d-flex justify-content-end">
|
<div class="navbar-nav d-flex justify-content-end">
|
||||||
<div v-if="auth.IsAuthenticated" class="nav-item dropdown">
|
<div v-if="auth.IsAuthenticated" class="nav-item dropdown">
|
||||||
<a aria-expanded="false" aria-haspopup="true" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#"
|
<a aria-expanded="false" aria-haspopup="true" class="nav-link dropdown-toggle" data-bs-toggle="dropdown"
|
||||||
role="button">{{ auth.User.Firstname }} {{ auth.User.Lastname }}</a>
|
href="#" role="button">{{ auth.User.Firstname }} {{ auth.User.Lastname }}</a>
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
<RouterLink :to="{ name: 'profile' }" class="dropdown-item"><i class="fas fa-user"></i> {{ $t('menu.profile') }}</RouterLink>
|
<RouterLink :to="{ name: 'profile' }" class="dropdown-item"><i class="fas fa-user"></i> {{ $t('menu.profile') }}</RouterLink>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
<a class="dropdown-item" href="#" @click.prevent="auth.Logout">
|
<a class="dropdown-item" href="#" @click.prevent="auth.Logout"><i class="fas fa-sign-out-alt"></i> {{ $t('menu.logout') }}</a>
|
||||||
<i class="fas fa-sign-out-alt"></i> {{ $t('menu.logout') }}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!auth.IsAuthenticated" class="nav-item">
|
<div v-if="!auth.IsAuthenticated" class="nav-item">
|
||||||
<RouterLink :to="{ name: 'login' }" class="nav-link">
|
<RouterLink :to="{ name: 'login' }" class="nav-link"><i class="fas fa-sign-in-alt fa-sm fa-fw me-2"></i>{{ $t('menu.login') }}</RouterLink>
|
||||||
<i class="fas fa-sign-in-alt fa-sm fa-fw me-2"></i>{{ $t('menu.login') }}
|
|
||||||
</RouterLink>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -111,18 +107,18 @@ const currentYear = ref(new Date().getFullYear())
|
|||||||
<div class="col-6 text-end">
|
<div class="col-6 text-end">
|
||||||
<div :aria-label="$t('menu.lang')" class="btn-group" role="group">
|
<div :aria-label="$t('menu.lang')" class="btn-group" role="group">
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<button aria-expanded="false" aria-haspopup="true" class="btn btn btn-secondary pe-0" data-bs-toggle="dropdown" type="button"><span :class="languageFlag" class="fi"></span></button>
|
<button aria-expanded="false" aria-haspopup="true" class="btn btn btn-secondary pe-0"
|
||||||
|
data-bs-toggle="dropdown" type="button"><span :class="languageFlag" class="fi"></span></button>
|
||||||
<div aria-labelledby="btnGroupDrop3" class="dropdown-menu" style="">
|
<div aria-labelledby="btnGroupDrop3" class="dropdown-menu" style="">
|
||||||
<a class="dropdown-item" href="#" @click.prevent="switchLanguage('en')"><span class="fi fi-us"></span> English</a>
|
<a class="dropdown-item" href="#" @click.prevent="switchLanguage('en')"><span class="fi fi-us"></span>English</a>
|
||||||
<a class="dropdown-item" href="#" @click.prevent="switchLanguage('de')"><span class="fi fi-de"></span> Deutsch</a>
|
<a class="dropdown-item" href="#" @click.prevent="switchLanguage('de')"><span class="fi fi-de"></span>Deutsch</a>
|
||||||
|
<a class="dropdown-item" href="#" @click.prevent="switchLanguage('ru')"><span class="fi fi-ru"></span>Русский</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer></template>
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,20 +1,22 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import Modal from "./Modal.vue";
|
import Modal from "./Modal.vue";
|
||||||
import {peerStore} from "@/stores/peers";
|
import { peerStore } from "@/stores/peers";
|
||||||
import {interfaceStore} from "@/stores/interfaces";
|
import { interfaceStore } from "@/stores/interfaces";
|
||||||
import {computed, ref, watch} from "vue";
|
import { computed, ref, watch } from "vue";
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { notify } from "@kyvg/vue3-notification";
|
import { notify } from "@kyvg/vue3-notification";
|
||||||
import Vue3TagsInput from "vue3-tags-input";
|
import Vue3TagsInput from "vue3-tags-input";
|
||||||
import { validateCIDR, validateIP, validateDomain } from '@/helpers/validators';
|
import { validateCIDR, validateIP, validateDomain } from '@/helpers/validators';
|
||||||
import isCidr from "is-cidr";
|
import isCidr from "is-cidr";
|
||||||
import {isIP} from 'is-ip';
|
import { isIP } from 'is-ip';
|
||||||
import { freshPeer, freshInterface } from '@/helpers/models';
|
import { freshPeer, freshInterface } from '@/helpers/models';
|
||||||
|
import { profileStore } from "@/stores/profile";
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const peers = peerStore()
|
const peers = peerStore()
|
||||||
const interfaces = interfaceStore()
|
const interfaces = interfaceStore()
|
||||||
|
const profile = profileStore()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
peerId: String,
|
peerId: String,
|
||||||
@ -24,7 +26,16 @@ const props = defineProps({
|
|||||||
const emit = defineEmits(['close'])
|
const emit = defineEmits(['close'])
|
||||||
|
|
||||||
const selectedPeer = computed(() => {
|
const selectedPeer = computed(() => {
|
||||||
return peers.Find(props.peerId)
|
let p = peers.Find(props.peerId)
|
||||||
|
|
||||||
|
if (!p) {
|
||||||
|
if (!!props.peerId || props.peerId.length) {
|
||||||
|
p = profile.peers.find((p) => p.Identifier === props.peerId)
|
||||||
|
} else {
|
||||||
|
p = freshPeer() // dummy peer to avoid 'undefined' exceptions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
const selectedInterface = computed(() => {
|
const selectedInterface = computed(() => {
|
||||||
@ -59,121 +70,119 @@ const formData = ref(freshPeer())
|
|||||||
// functions
|
// functions
|
||||||
|
|
||||||
watch(() => props.visible, async (newValue, oldValue) => {
|
watch(() => props.visible, async (newValue, oldValue) => {
|
||||||
if (oldValue === false && newValue === true) { // if modal is shown
|
if (oldValue === false && newValue === true) { // if modal is shown
|
||||||
console.log(selectedInterface.value)
|
if (!selectedPeer.value) {
|
||||||
console.log(selectedPeer.value)
|
await peers.PreparePeer(selectedInterface.value.Identifier)
|
||||||
if (!selectedPeer.value) {
|
|
||||||
await peers.PreparePeer(selectedInterface.value.Identifier)
|
|
||||||
|
|
||||||
formData.value.Identifier = peers.Prepared.Identifier
|
formData.value.Identifier = peers.Prepared.Identifier
|
||||||
formData.value.DisplayName = peers.Prepared.DisplayName
|
formData.value.DisplayName = peers.Prepared.DisplayName
|
||||||
formData.value.UserIdentifier = peers.Prepared.UserIdentifier
|
formData.value.UserIdentifier = peers.Prepared.UserIdentifier
|
||||||
formData.value.InterfaceIdentifier = peers.Prepared.InterfaceIdentifier
|
formData.value.InterfaceIdentifier = peers.Prepared.InterfaceIdentifier
|
||||||
formData.value.Disabled = peers.Prepared.Disabled
|
formData.value.Disabled = peers.Prepared.Disabled
|
||||||
formData.value.ExpiresAt = peers.Prepared.ExpiresAt
|
formData.value.ExpiresAt = peers.Prepared.ExpiresAt
|
||||||
formData.value.Notes = peers.Prepared.Notes
|
formData.value.Notes = peers.Prepared.Notes
|
||||||
|
|
||||||
formData.value.Endpoint = peers.Prepared.Endpoint
|
formData.value.Endpoint = peers.Prepared.Endpoint
|
||||||
formData.value.EndpointPublicKey = peers.Prepared.EndpointPublicKey
|
formData.value.EndpointPublicKey = peers.Prepared.EndpointPublicKey
|
||||||
formData.value.AllowedIPs = peers.Prepared.AllowedIPs
|
formData.value.AllowedIPs = peers.Prepared.AllowedIPs
|
||||||
formData.value.ExtraAllowedIPs = peers.Prepared.ExtraAllowedIPs
|
formData.value.ExtraAllowedIPs = peers.Prepared.ExtraAllowedIPs
|
||||||
formData.value.PresharedKey = peers.Prepared.PresharedKey
|
formData.value.PresharedKey = peers.Prepared.PresharedKey
|
||||||
formData.value.PersistentKeepalive = peers.Prepared.PersistentKeepalive
|
formData.value.PersistentKeepalive = peers.Prepared.PersistentKeepalive
|
||||||
|
|
||||||
formData.value.PrivateKey = peers.Prepared.PrivateKey
|
formData.value.PrivateKey = peers.Prepared.PrivateKey
|
||||||
formData.value.PublicKey = peers.Prepared.PublicKey
|
formData.value.PublicKey = peers.Prepared.PublicKey
|
||||||
|
|
||||||
formData.value.Mode = peers.Prepared.Mode
|
formData.value.Mode = peers.Prepared.Mode
|
||||||
|
|
||||||
formData.value.Addresses = peers.Prepared.Addresses
|
formData.value.Addresses = peers.Prepared.Addresses
|
||||||
formData.value.CheckAliveAddress = peers.Prepared.CheckAliveAddress
|
formData.value.CheckAliveAddress = peers.Prepared.CheckAliveAddress
|
||||||
formData.value.Dns = peers.Prepared.Dns
|
formData.value.Dns = peers.Prepared.Dns
|
||||||
formData.value.DnsSearch = peers.Prepared.DnsSearch
|
formData.value.DnsSearch = peers.Prepared.DnsSearch
|
||||||
formData.value.Mtu = peers.Prepared.Mtu
|
formData.value.Mtu = peers.Prepared.Mtu
|
||||||
formData.value.FirewallMark = peers.Prepared.FirewallMark
|
formData.value.FirewallMark = peers.Prepared.FirewallMark
|
||||||
formData.value.RoutingTable = peers.Prepared.RoutingTable
|
formData.value.RoutingTable = peers.Prepared.RoutingTable
|
||||||
|
|
||||||
formData.value.PreUp = peers.Prepared.PreUp
|
formData.value.PreUp = peers.Prepared.PreUp
|
||||||
formData.value.PostUp = peers.Prepared.PostUp
|
formData.value.PostUp = peers.Prepared.PostUp
|
||||||
formData.value.PreDown = peers.Prepared.PreDown
|
formData.value.PreDown = peers.Prepared.PreDown
|
||||||
formData.value.PostDown = peers.Prepared.PostDown
|
formData.value.PostDown = peers.Prepared.PostDown
|
||||||
|
|
||||||
} else { // fill existing data
|
} else { // fill existing data
|
||||||
formData.value.Identifier = selectedPeer.value.Identifier
|
formData.value.Identifier = selectedPeer.value.Identifier
|
||||||
formData.value.DisplayName = selectedPeer.value.DisplayName
|
formData.value.DisplayName = selectedPeer.value.DisplayName
|
||||||
formData.value.UserIdentifier = selectedPeer.value.UserIdentifier
|
formData.value.UserIdentifier = selectedPeer.value.UserIdentifier
|
||||||
formData.value.InterfaceIdentifier = selectedPeer.value.InterfaceIdentifier
|
formData.value.InterfaceIdentifier = selectedPeer.value.InterfaceIdentifier
|
||||||
formData.value.Disabled = selectedPeer.value.Disabled
|
formData.value.Disabled = selectedPeer.value.Disabled
|
||||||
formData.value.ExpiresAt = selectedPeer.value.ExpiresAt
|
formData.value.ExpiresAt = selectedPeer.value.ExpiresAt
|
||||||
formData.value.Notes = selectedPeer.value.Notes
|
formData.value.Notes = selectedPeer.value.Notes
|
||||||
|
|
||||||
formData.value.Endpoint = selectedPeer.value.Endpoint
|
formData.value.Endpoint = selectedPeer.value.Endpoint
|
||||||
formData.value.EndpointPublicKey = selectedPeer.value.EndpointPublicKey
|
formData.value.EndpointPublicKey = selectedPeer.value.EndpointPublicKey
|
||||||
formData.value.AllowedIPs = selectedPeer.value.AllowedIPs
|
formData.value.AllowedIPs = selectedPeer.value.AllowedIPs
|
||||||
formData.value.ExtraAllowedIPs = selectedPeer.value.ExtraAllowedIPs
|
formData.value.ExtraAllowedIPs = selectedPeer.value.ExtraAllowedIPs
|
||||||
formData.value.PresharedKey = selectedPeer.value.PresharedKey
|
formData.value.PresharedKey = selectedPeer.value.PresharedKey
|
||||||
formData.value.PersistentKeepalive = selectedPeer.value.PersistentKeepalive
|
formData.value.PersistentKeepalive = selectedPeer.value.PersistentKeepalive
|
||||||
|
|
||||||
formData.value.PrivateKey = selectedPeer.value.PrivateKey
|
formData.value.PrivateKey = selectedPeer.value.PrivateKey
|
||||||
formData.value.PublicKey = selectedPeer.value.PublicKey
|
formData.value.PublicKey = selectedPeer.value.PublicKey
|
||||||
|
|
||||||
formData.value.Mode = selectedPeer.value.Mode
|
formData.value.Mode = selectedPeer.value.Mode
|
||||||
|
|
||||||
formData.value.Addresses = selectedPeer.value.Addresses
|
formData.value.Addresses = selectedPeer.value.Addresses
|
||||||
formData.value.CheckAliveAddress = selectedPeer.value.CheckAliveAddress
|
formData.value.CheckAliveAddress = selectedPeer.value.CheckAliveAddress
|
||||||
formData.value.Dns = selectedPeer.value.Dns
|
formData.value.Dns = selectedPeer.value.Dns
|
||||||
formData.value.DnsSearch = selectedPeer.value.DnsSearch
|
formData.value.DnsSearch = selectedPeer.value.DnsSearch
|
||||||
formData.value.Mtu = selectedPeer.value.Mtu
|
formData.value.Mtu = selectedPeer.value.Mtu
|
||||||
formData.value.FirewallMark = selectedPeer.value.FirewallMark
|
formData.value.FirewallMark = selectedPeer.value.FirewallMark
|
||||||
formData.value.RoutingTable = selectedPeer.value.RoutingTable
|
formData.value.RoutingTable = selectedPeer.value.RoutingTable
|
||||||
|
|
||||||
formData.value.PreUp = selectedPeer.value.PreUp
|
formData.value.PreUp = selectedPeer.value.PreUp
|
||||||
formData.value.PostUp = selectedPeer.value.PostUp
|
formData.value.PostUp = selectedPeer.value.PostUp
|
||||||
formData.value.PreDown = selectedPeer.value.PreDown
|
formData.value.PreDown = selectedPeer.value.PreDown
|
||||||
formData.value.PostDown = selectedPeer.value.PostDown
|
formData.value.PostDown = selectedPeer.value.PostDown
|
||||||
|
|
||||||
if (!formData.value.Endpoint.Overridable ||
|
if (!formData.value.Endpoint.Overridable ||
|
||||||
!formData.value.EndpointPublicKey.Overridable ||
|
!formData.value.EndpointPublicKey.Overridable ||
|
||||||
!formData.value.AllowedIPs.Overridable ||
|
!formData.value.AllowedIPs.Overridable ||
|
||||||
!formData.value.PersistentKeepalive.Overridable ||
|
!formData.value.PersistentKeepalive.Overridable ||
|
||||||
!formData.value.Dns.Overridable ||
|
!formData.value.Dns.Overridable ||
|
||||||
!formData.value.DnsSearch.Overridable ||
|
!formData.value.DnsSearch.Overridable ||
|
||||||
!formData.value.Mtu.Overridable ||
|
!formData.value.Mtu.Overridable ||
|
||||||
!formData.value.FirewallMark.Overridable ||
|
!formData.value.FirewallMark.Overridable ||
|
||||||
!formData.value.RoutingTable.Overridable ||
|
!formData.value.RoutingTable.Overridable ||
|
||||||
!formData.value.PreUp.Overridable ||
|
!formData.value.PreUp.Overridable ||
|
||||||
!formData.value.PostUp.Overridable ||
|
!formData.value.PostUp.Overridable ||
|
||||||
!formData.value.PreDown.Overridable ||
|
!formData.value.PreDown.Overridable ||
|
||||||
!formData.value.PostDown.Overridable) {
|
!formData.value.PostDown.Overridable) {
|
||||||
formData.value.IgnoreGlobalSettings = true
|
formData.value.IgnoreGlobalSettings = true
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(() => formData.value.IgnoreGlobalSettings, async (newValue, oldValue) => {
|
watch(() => formData.value.IgnoreGlobalSettings, async (newValue, oldValue) => {
|
||||||
formData.value.Endpoint.Overridable = !newValue
|
formData.value.Endpoint.Overridable = !newValue
|
||||||
formData.value.EndpointPublicKey.Overridable = !newValue
|
formData.value.EndpointPublicKey.Overridable = !newValue
|
||||||
formData.value.AllowedIPs.Overridable = !newValue
|
formData.value.AllowedIPs.Overridable = !newValue
|
||||||
formData.value.PersistentKeepalive.Overridable = !newValue
|
formData.value.PersistentKeepalive.Overridable = !newValue
|
||||||
formData.value.Dns.Overridable = !newValue
|
formData.value.Dns.Overridable = !newValue
|
||||||
formData.value.DnsSearch.Overridable = !newValue
|
formData.value.DnsSearch.Overridable = !newValue
|
||||||
formData.value.Mtu.Overridable = !newValue
|
formData.value.Mtu.Overridable = !newValue
|
||||||
formData.value.FirewallMark.Overridable = !newValue
|
formData.value.FirewallMark.Overridable = !newValue
|
||||||
formData.value.RoutingTable.Overridable = !newValue
|
formData.value.RoutingTable.Overridable = !newValue
|
||||||
formData.value.PreUp.Overridable = !newValue
|
formData.value.PreUp.Overridable = !newValue
|
||||||
formData.value.PostUp.Overridable = !newValue
|
formData.value.PostUp.Overridable = !newValue
|
||||||
formData.value.PreDown.Overridable = !newValue
|
formData.value.PreDown.Overridable = !newValue
|
||||||
formData.value.PostDown.Overridable = !newValue
|
formData.value.PostDown.Overridable = !newValue
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(() => formData.value.Disabled, async (newValue, oldValue) => {
|
watch(() => formData.value.Disabled, async (newValue, oldValue) => {
|
||||||
if (oldValue && !newValue && formData.value.ExpiresAt) {
|
if (oldValue && !newValue && formData.value.ExpiresAt) {
|
||||||
formData.value.ExpiresAt = "" // reset expiry date
|
formData.value.ExpiresAt = "" // reset expiry date
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
@ -184,7 +193,7 @@ function close() {
|
|||||||
function handleChangeAddresses(tags) {
|
function handleChangeAddresses(tags) {
|
||||||
let validInput = true
|
let validInput = true
|
||||||
tags.forEach(tag => {
|
tags.forEach(tag => {
|
||||||
if(isCidr(tag) === 0) {
|
if (isCidr(tag) === 0) {
|
||||||
validInput = false
|
validInput = false
|
||||||
notify({
|
notify({
|
||||||
title: "Invalid CIDR",
|
title: "Invalid CIDR",
|
||||||
@ -193,7 +202,7 @@ function handleChangeAddresses(tags) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if(validInput) {
|
if (validInput) {
|
||||||
formData.value.Addresses = tags
|
formData.value.Addresses = tags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -201,7 +210,7 @@ function handleChangeAddresses(tags) {
|
|||||||
function handleChangeAllowedIPs(tags) {
|
function handleChangeAllowedIPs(tags) {
|
||||||
let validInput = true
|
let validInput = true
|
||||||
tags.forEach(tag => {
|
tags.forEach(tag => {
|
||||||
if(isCidr(tag) === 0) {
|
if (isCidr(tag) === 0) {
|
||||||
validInput = false
|
validInput = false
|
||||||
notify({
|
notify({
|
||||||
title: "Invalid CIDR",
|
title: "Invalid CIDR",
|
||||||
@ -210,7 +219,7 @@ function handleChangeAllowedIPs(tags) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if(validInput) {
|
if (validInput) {
|
||||||
formData.value.AllowedIPs.Value = tags
|
formData.value.AllowedIPs.Value = tags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,7 +227,7 @@ function handleChangeAllowedIPs(tags) {
|
|||||||
function handleChangeExtraAllowedIPs(tags) {
|
function handleChangeExtraAllowedIPs(tags) {
|
||||||
let validInput = true
|
let validInput = true
|
||||||
tags.forEach(tag => {
|
tags.forEach(tag => {
|
||||||
if(isCidr(tag) === 0) {
|
if (isCidr(tag) === 0) {
|
||||||
validInput = false
|
validInput = false
|
||||||
notify({
|
notify({
|
||||||
title: "Invalid CIDR",
|
title: "Invalid CIDR",
|
||||||
@ -227,7 +236,7 @@ function handleChangeExtraAllowedIPs(tags) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if(validInput) {
|
if (validInput) {
|
||||||
formData.value.ExtraAllowedIPs = tags
|
formData.value.ExtraAllowedIPs = tags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -235,7 +244,7 @@ function handleChangeExtraAllowedIPs(tags) {
|
|||||||
function handleChangeDns(tags) {
|
function handleChangeDns(tags) {
|
||||||
let validInput = true
|
let validInput = true
|
||||||
tags.forEach(tag => {
|
tags.forEach(tag => {
|
||||||
if(!isIP(tag)) {
|
if (!isIP(tag)) {
|
||||||
validInput = false
|
validInput = false
|
||||||
notify({
|
notify({
|
||||||
title: "Invalid IP",
|
title: "Invalid IP",
|
||||||
@ -244,7 +253,7 @@ function handleChangeDns(tags) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if(validInput) {
|
if (validInput) {
|
||||||
formData.value.Dns.Value = tags
|
formData.value.Dns.Value = tags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -255,14 +264,14 @@ function handleChangeDnsSearch(tags) {
|
|||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
try {
|
try {
|
||||||
if (props.peerId!=='#NEW#') {
|
if (props.peerId !== '#NEW#') {
|
||||||
await peers.UpdatePeer(selectedPeer.value.Identifier, formData.value)
|
await peers.UpdatePeer(selectedPeer.value.Identifier, formData.value)
|
||||||
} else {
|
} else {
|
||||||
await peers.CreatePeer(selectedInterface.value.Identifier, formData.value)
|
await peers.CreatePeer(selectedInterface.value.Identifier, formData.value)
|
||||||
}
|
}
|
||||||
close()
|
close()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
// console.log(e)
|
||||||
notify({
|
notify({
|
||||||
title: "Failed to save peer!",
|
title: "Failed to save peer!",
|
||||||
text: e.toString(),
|
text: e.toString(),
|
||||||
@ -276,7 +285,7 @@ async function del() {
|
|||||||
await peers.DeletePeer(selectedPeer.value.Identifier)
|
await peers.DeletePeer(selectedPeer.value.Identifier)
|
||||||
close()
|
close()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
// console.log(e)
|
||||||
notify({
|
notify({
|
||||||
title: "Failed to delete peer!",
|
title: "Failed to delete peer!",
|
||||||
text: e.toString(),
|
text: e.toString(),
|
||||||
@ -294,87 +303,86 @@ async function del() {
|
|||||||
<legend class="mt-4">{{ $t('modals.peer-edit.header-general') }}</legend>
|
<legend class="mt-4">{{ $t('modals.peer-edit.header-general') }}</legend>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label mt-4">{{ $t('modals.peer-edit.display-name.label') }}</label>
|
<label class="form-label mt-4">{{ $t('modals.peer-edit.display-name.label') }}</label>
|
||||||
<input type="text" class="form-control" :placeholder="$t('modals.peer-edit.display-name.placeholder')" v-model="formData.DisplayName">
|
<input type="text" class="form-control" :placeholder="$t('modals.peer-edit.display-name.placeholder')"
|
||||||
|
v-model="formData.DisplayName">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label mt-4">{{ $t('modals.peer-edit.linked-user.label') }}</label>
|
<label class="form-label mt-4">{{ $t('modals.peer-edit.linked-user.label') }}</label>
|
||||||
<input type="text" class="form-control" :placeholder="$t('modals.peer-edit.linked-user.placeholder')" v-model="formData.UserIdentifier">
|
<input type="text" class="form-control" :placeholder="$t('modals.peer-edit.linked-user.placeholder')"
|
||||||
|
v-model="formData.UserIdentifier">
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend class="mt-4">{{ $t('modals.peer-edit.header-crypto') }}</legend>
|
<legend class="mt-4">{{ $t('modals.peer-edit.header-crypto') }}</legend>
|
||||||
<div class="form-group" v-if="selectedInterface.Mode==='server'">
|
<div class="form-group" v-if="selectedInterface.Mode === 'server'">
|
||||||
<label class="form-label mt-4">{{ $t('modals.peer-edit.private-key.label') }}</label>
|
<label class="form-label mt-4">{{ $t('modals.peer-edit.private-key.label') }}</label>
|
||||||
<input type="email" class="form-control" :placeholder="$t('modals.peer-edit.private-key.placeholder')" required v-model="formData.PrivateKey">
|
<input type="email" class="form-control" :placeholder="$t('modals.peer-edit.private-key.placeholder')" required
|
||||||
|
v-model="formData.PrivateKey">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label mt-4">{{ $t('modals.peer-edit.public-key.label') }}</label>
|
<label class="form-label mt-4">{{ $t('modals.peer-edit.public-key.label') }}</label>
|
||||||
<input type="email" class="form-control" :placeholder="$t('modals.peer-edit.public-key.placeholder')" required v-model="formData.PublicKey">
|
<input type="email" class="form-control" :placeholder="$t('modals.peer-edit.public-key.placeholder')" required
|
||||||
|
v-model="formData.PublicKey">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label mt-4">{{ $t('modals.peer-edit.preshared-key.label') }}</label>
|
<label class="form-label mt-4">{{ $t('modals.peer-edit.preshared-key.label') }}</label>
|
||||||
<input type="email" class="form-control" :placeholder="$t('modals.peer-edit.preshared-key.placeholder')" v-model="formData.PresharedKey">
|
<input type="email" class="form-control" :placeholder="$t('modals.peer-edit.preshared-key.placeholder')"
|
||||||
|
v-model="formData.PresharedKey">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group" v-if="formData.Mode==='client'">
|
<div class="form-group" v-if="formData.Mode === 'client'">
|
||||||
<label class="form-label mt-4">{{ $t('modals.peer-edit.endpoint-public-key.label') }}</label>
|
<label class="form-label mt-4">{{ $t('modals.peer-edit.endpoint-public-key.label') }}</label>
|
||||||
<input type="text" class="form-control" :placeholder="$t('modals.peer-edit.endpoint-public-key.placeholder')" v-model="formData.EndpointPublicKey.Value">
|
<input type="text" class="form-control" :placeholder="$t('modals.peer-edit.endpoint-public-key.placeholder')"
|
||||||
|
v-model="formData.EndpointPublicKey.Value">
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend class="mt-4">{{ $t('modals.peer-edit.header-network') }}</legend>
|
<legend class="mt-4">{{ $t('modals.peer-edit.header-network') }}</legend>
|
||||||
<div class="form-group" v-if="selectedInterface.Mode==='client'">
|
<div class="form-group" v-if="selectedInterface.Mode === 'client'">
|
||||||
<label class="form-label mt-4">{{ $t('modals.peer-edit.endpoint.label') }}</label>
|
<label class="form-label mt-4">{{ $t('modals.peer-edit.endpoint.label') }}</label>
|
||||||
<input type="text" class="form-control" :placeholder="$t('modals.peer-edit.endpoint.placeholder')" v-model="formData.Endpoint.Value">
|
<input type="text" class="form-control" :placeholder="$t('modals.peer-edit.endpoint.placeholder')"
|
||||||
|
v-model="formData.Endpoint.Value">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label mt-4">{{ $t('modals.peer-edit.ip.label') }}</label>
|
<label class="form-label mt-4">{{ $t('modals.peer-edit.ip.label') }}</label>
|
||||||
<vue3-tags-input class="form-control" :tags="formData.Addresses"
|
<vue3-tags-input class="form-control" :tags="formData.Addresses"
|
||||||
:placeholder="$t('modals.peer-edit.ip.placeholder')"
|
:placeholder="$t('modals.peer-edit.ip.placeholder')" :add-tag-on-keys="[13, 188, 32, 9]"
|
||||||
:add-tag-on-keys="[13, 188, 32, 9]"
|
:validate="validateCIDR" @on-tags-changed="handleChangeAddresses" />
|
||||||
:validate="validateCIDR"
|
|
||||||
@on-tags-changed="handleChangeAddresses"/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label mt-4">{{ $t('modals.peer-edit.allowed-ip.label') }}</label>
|
<label class="form-label mt-4">{{ $t('modals.peer-edit.allowed-ip.label') }}</label>
|
||||||
<vue3-tags-input class="form-control" :tags="formData.AllowedIPs.Value"
|
<vue3-tags-input class="form-control" :tags="formData.AllowedIPs.Value"
|
||||||
:placeholder="$t('modals.peer-edit.allowed-ip.placeholder')"
|
:placeholder="$t('modals.peer-edit.allowed-ip.placeholder')" :add-tag-on-keys="[13, 188, 32, 9]"
|
||||||
:add-tag-on-keys="[13, 188, 32, 9]"
|
:validate="validateCIDR" @on-tags-changed="handleChangeAllowedIPs" />
|
||||||
:validate="validateCIDR"
|
|
||||||
@on-tags-changed="handleChangeAllowedIPs"/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label mt-4">{{ $t('modals.peer-edit.extra-allowed-ip.label') }}</label>
|
<label class="form-label mt-4">{{ $t('modals.peer-edit.extra-allowed-ip.label') }}</label>
|
||||||
<vue3-tags-input class="form-control" :tags="formData.ExtraAllowedIPs"
|
<vue3-tags-input class="form-control" :tags="formData.ExtraAllowedIPs"
|
||||||
:placeholder="$t('modals.peer-edit.extra-allowed-ip.placeholder')"
|
:placeholder="$t('modals.peer-edit.extra-allowed-ip.placeholder')" :add-tag-on-keys="[13, 188, 32, 9]"
|
||||||
:add-tag-on-keys="[13, 188, 32, 9]"
|
:validate="validateCIDR" @on-tags-changed="handleChangeExtraAllowedIPs" />
|
||||||
:validate="validateCIDR"
|
|
||||||
@on-tags-changed="handleChangeExtraAllowedIPs"/>
|
|
||||||
<small class="form-text text-muted">{{ $t('modals.peer-edit.extra-allowed-ip.description') }}</small>
|
<small class="form-text text-muted">{{ $t('modals.peer-edit.extra-allowed-ip.description') }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label mt-4">{{ $t('modals.peer-edit.dns.label') }}</label>
|
<label class="form-label mt-4">{{ $t('modals.peer-edit.dns.label') }}</label>
|
||||||
<vue3-tags-input class="form-control" :tags="formData.Dns.Value"
|
<vue3-tags-input class="form-control" :tags="formData.Dns.Value"
|
||||||
:placeholder="$t('modals.peer-edit.dns.placeholder')"
|
:placeholder="$t('modals.peer-edit.dns.placeholder')" :add-tag-on-keys="[13, 188, 32, 9]"
|
||||||
:add-tag-on-keys="[13, 188, 32, 9]"
|
:validate="validateIP" @on-tags-changed="handleChangeDns" />
|
||||||
:validate="validateIP"
|
|
||||||
@on-tags-changed="handleChangeDns"/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div hidden class="form-group">
|
||||||
<label class="form-label mt-4">{{ $t('modals.peer-edit.dns-search.label') }}</label>
|
<label class="form-label mt-4">{{ $t('modals.peer-edit.dns-search.label') }}</label>
|
||||||
<vue3-tags-input class="form-control" :tags="formData.DnsSearch.Value"
|
<vue3-tags-input class="form-control" :tags="formData.DnsSearch.Value"
|
||||||
:placeholder="$t('modals.peer-edit.dns-search.label')"
|
:placeholder="$t('modals.peer-edit.dns-search.label')" :add-tag-on-keys="[13, 188, 32, 9]"
|
||||||
:add-tag-on-keys="[13, 188, 32, 9]"
|
:validate="validateDomain" @on-tags-changed="handleChangeDnsSearch" />
|
||||||
:validate="validateDomain"
|
|
||||||
@on-tags-changed="handleChangeDnsSearch"/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label class="form-label mt-4">{{ $t('modals.peer-edit.keep-alive.label') }}</label>
|
<label class="form-label mt-4">{{ $t('modals.peer-edit.keep-alive.label') }}</label>
|
||||||
<input type="number" class="form-control" :placeholder="$t('modals.peer-edit.keep-alive.label')" v-model="formData.PersistentKeepalive.Value">
|
<input type="number" class="form-control" :placeholder="$t('modals.peer-edit.keep-alive.label')"
|
||||||
|
v-model="formData.PersistentKeepalive.Value">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label class="form-label mt-4">{{ $t('modals.peer-edit.mtu.label') }}</label>
|
<label class="form-label mt-4">{{ $t('modals.peer-edit.mtu.label') }}</label>
|
||||||
<input type="number" class="form-control" :placeholder="$t('modals.peer-edit.mtu.label')" v-model="formData.Mtu.Value">
|
<input type="number" class="form-control" :placeholder="$t('modals.peer-edit.mtu.label')"
|
||||||
|
v-model="formData.Mtu.Value">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@ -382,19 +390,23 @@ async function del() {
|
|||||||
<legend class="mt-4">{{ $t('modals.peer-edit.header-hooks') }}</legend>
|
<legend class="mt-4">{{ $t('modals.peer-edit.header-hooks') }}</legend>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label mt-4">{{ $t('modals.peer-edit.pre-up.label') }}</label>
|
<label class="form-label mt-4">{{ $t('modals.peer-edit.pre-up.label') }}</label>
|
||||||
<textarea v-model="formData.PreUp.Value" class="form-control" rows="2" :placeholder="$t('modals.peer-edit.pre-up.placeholder')"></textarea>
|
<textarea v-model="formData.PreUp.Value" class="form-control" rows="2"
|
||||||
|
:placeholder="$t('modals.peer-edit.pre-up.placeholder')"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label mt-4">{{ $t('modals.peer-edit.post-up.label') }}</label>
|
<label class="form-label mt-4">{{ $t('modals.peer-edit.post-up.label') }}</label>
|
||||||
<textarea v-model="formData.PostUp.Value" class="form-control" rows="2" :placeholder="$t('modals.peer-edit.post-up.placeholder')"></textarea>
|
<textarea v-model="formData.PostUp.Value" class="form-control" rows="2"
|
||||||
|
:placeholder="$t('modals.peer-edit.post-up.placeholder')"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label mt-4">{{ $t('modals.peer-edit.pre-down.label') }}</label>
|
<label class="form-label mt-4">{{ $t('modals.peer-edit.pre-down.label') }}</label>
|
||||||
<textarea v-model="formData.PreDown.Value" class="form-control" rows="2" :placeholder="$t('modals.peer-edit.pre-down.placeholder')"></textarea>
|
<textarea v-model="formData.PreDown.Value" class="form-control" rows="2"
|
||||||
|
:placeholder="$t('modals.peer-edit.pre-down.placeholder')"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label mt-4">{{ $t('modals.peer-edit.post-down.label') }}</label>
|
<label class="form-label mt-4">{{ $t('modals.peer-edit.post-down.label') }}</label>
|
||||||
<textarea v-model="formData.PostDown.Value" class="form-control" rows="2" :placeholder="$t('modals.peer-edit.post-down.placeholder')"></textarea>
|
<textarea v-model="formData.PostDown.Value" class="form-control" rows="2"
|
||||||
|
:placeholder="$t('modals.peer-edit.post-down.placeholder')"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
@ -403,7 +415,7 @@ async function del() {
|
|||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<div class="form-check form-switch">
|
<div class="form-check form-switch">
|
||||||
<input class="form-check-input" type="checkbox" v-model="formData.Disabled">
|
<input class="form-check-input" type="checkbox" v-model="formData.Disabled">
|
||||||
<label class="form-check-label" >{{ $t('modals.peer-edit.disabled.label') }}</label>
|
<label class="form-check-label">{{ $t('modals.peer-edit.disabled.label') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-switch">
|
<div class="form-check form-switch">
|
||||||
<input class="form-check-input" type="checkbox" v-model="formData.IgnoreGlobalSettings">
|
<input class="form-check-input" type="checkbox" v-model="formData.IgnoreGlobalSettings">
|
||||||
@ -412,14 +424,16 @@ async function del() {
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label class="form-label">{{ $t('modals.peer-edit.expires-at.label') }}</label>
|
<label class="form-label">{{ $t('modals.peer-edit.expires-at.label') }}</label>
|
||||||
<input type="date" pattern="\d{4}-\d{2}-\d{2}" class="form-control" min="2023-01-01" v-model="formData.ExpiresAt">
|
<input type="date" pattern="\d{4}-\d{2}-\d{2}" class="form-control" min="2023-01-01"
|
||||||
|
v-model="formData.ExpiresAt">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="flex-fill text-start">
|
<div class="flex-fill text-start">
|
||||||
<button v-if="props.peerId!=='#NEW#'" class="btn btn-danger me-1" type="button" @click.prevent="del">{{ $t('general.delete') }}</button>
|
<button v-if="props.peerId !== '#NEW#'" class="btn btn-danger me-1" type="button" @click.prevent="del">{{
|
||||||
|
$t('general.delete') }}</button>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-primary me-1" type="button" @click.prevent="save">{{ $t('general.save') }}</button>
|
<button class="btn btn-primary me-1" type="button" @click.prevent="save">{{ $t('general.save') }}</button>
|
||||||
<button class="btn btn-secondary" type="button" @click.prevent="close">{{ $t('general.close') }}</button>
|
<button class="btn btn-secondary" type="button" @click.prevent="close">{{ $t('general.close') }}</button>
|
||||||
@ -427,5 +441,4 @@ async function del() {
|
|||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,19 +1,23 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import Modal from "./Modal.vue";
|
import Modal from "./Modal.vue";
|
||||||
import {peerStore} from "@/stores/peers";
|
import { peerStore } from "@/stores/peers";
|
||||||
import {interfaceStore} from "@/stores/interfaces";
|
import { interfaceStore } from "@/stores/interfaces";
|
||||||
import {computed, ref, watch} from "vue";
|
import { computed, ref, watch } from "vue";
|
||||||
import {useI18n} from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import {freshInterface, freshPeer, freshStats} from '@/helpers/models';
|
import { freshInterface, freshPeer, freshStats } from '@/helpers/models';
|
||||||
import Prism from "vue-prism-component";
|
import Prism from "vue-prism-component";
|
||||||
import {notify} from "@kyvg/vue3-notification";
|
import { notify } from "@kyvg/vue3-notification";
|
||||||
import {settingsStore} from "@/stores/settings";
|
import { settingsStore } from "@/stores/settings";
|
||||||
|
import { profileStore } from "@/stores/profile";
|
||||||
|
import { base64_url_encode } from '@/helpers/encoding';
|
||||||
|
import { apiWrapper } from "@/helpers/fetch-wrapper";
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const settings = settingsStore()
|
const settings = settingsStore()
|
||||||
const peers = peerStore()
|
const peers = peerStore()
|
||||||
const interfaces = interfaceStore()
|
const interfaces = interfaceStore()
|
||||||
|
const profile = profileStore()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
peerId: String,
|
peerId: String,
|
||||||
@ -32,9 +36,12 @@ const selectedPeer = computed(() => {
|
|||||||
let p = peers.Find(props.peerId)
|
let p = peers.Find(props.peerId)
|
||||||
|
|
||||||
if (!p) {
|
if (!p) {
|
||||||
p = freshPeer() // dummy peer to avoid 'undefined' exceptions
|
if (!!props.peerId || props.peerId.length) {
|
||||||
|
p = profile.peers.find((p) => p.Identifier === props.peerId)
|
||||||
|
} else {
|
||||||
|
p = freshPeer() // dummy peer to avoid 'undefined' exceptions
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -42,9 +49,13 @@ const selectedStats = computed(() => {
|
|||||||
let s = peers.Statistics(props.peerId)
|
let s = peers.Statistics(props.peerId)
|
||||||
|
|
||||||
if (!s) {
|
if (!s) {
|
||||||
s = freshStats() // dummy peer to avoid 'undefined' exceptions
|
if (!!props.peerId || props.peerId.length) {
|
||||||
}
|
p = profile.Statistics(props.peerId)
|
||||||
|
} else {
|
||||||
|
s = freshStats() // dummy stats to avoid 'undefined' exceptions
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -54,7 +65,6 @@ const selectedInterface = computed(() => {
|
|||||||
if (!i) {
|
if (!i) {
|
||||||
i = freshInterface() // dummy interface to avoid 'undefined' exceptions
|
i = freshInterface() // dummy interface to avoid 'undefined' exceptions
|
||||||
}
|
}
|
||||||
|
|
||||||
return i
|
return i
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -70,11 +80,11 @@ const title = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
watch(() => props.visible, async (newValue, oldValue) => {
|
watch(() => props.visible, async (newValue, oldValue) => {
|
||||||
if (oldValue === false && newValue === true) { // if modal is shown
|
if (oldValue === false && newValue === true) { // if modal is shown
|
||||||
await peers.LoadPeerConfig(selectedPeer.value.Identifier)
|
await peers.LoadPeerConfig(selectedPeer.value.Identifier)
|
||||||
configString.value = peers.configuration
|
configString.value = peers.configuration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
function download() {
|
function download() {
|
||||||
@ -82,10 +92,10 @@ function download() {
|
|||||||
let filename = 'WireGuard-Tunnel.conf'
|
let filename = 'WireGuard-Tunnel.conf'
|
||||||
if (selectedPeer.value.DisplayName) {
|
if (selectedPeer.value.DisplayName) {
|
||||||
filename = selectedPeer.value.DisplayName
|
filename = selectedPeer.value.DisplayName
|
||||||
.replace(/ /g,"_")
|
.replace(/ /g, "_")
|
||||||
.replace(/[^a-zA-Z0-9-_]/g,"")
|
.replace(/[^a-zA-Z0-9-_]/g, "")
|
||||||
.substring(0, 16)
|
.substring(0, 16)
|
||||||
+ ".conf"
|
+ ".conf"
|
||||||
}
|
}
|
||||||
let text = configString.value
|
let text = configString.value
|
||||||
|
|
||||||
@ -110,6 +120,13 @@ function email() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ConfigQrUrl() {
|
||||||
|
if (props.peerId.length) {
|
||||||
|
return apiWrapper.url(`/peer/config-qr/${base64_url_encode(props.peerId)}`)
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -118,25 +135,30 @@ function email() {
|
|||||||
<div class="accordion" id="peerInformation">
|
<div class="accordion" id="peerInformation">
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<h2 class="accordion-header">
|
<h2 class="accordion-header">
|
||||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseDetails" aria-expanded="true" aria-controls="collapseDetails">
|
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseDetails"
|
||||||
|
aria-expanded="true" aria-controls="collapseDetails">
|
||||||
{{ $t('modals.peer-view.section-info') }}
|
{{ $t('modals.peer-view.section-info') }}
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<div id="collapseDetails" class="accordion-collapse collapse show" aria-labelledby="headingDetails" data-bs-parent="#peerInformation" style="">
|
<div id="collapseDetails" class="accordion-collapse collapse show" aria-labelledby="headingDetails"
|
||||||
|
data-bs-parent="#peerInformation" style="">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<ul>
|
<ul>
|
||||||
<li>{{ $t('modals.peer-view.identifier') }}: {{ selectedPeer.PublicKey }}</li>
|
<li>{{ $t('modals.peer-view.identifier') }}: {{ selectedPeer.PublicKey }}</li>
|
||||||
<li>{{ $t('modals.peer-view.ip') }}: <span v-for="ip in selectedPeer.Addresses" :key="ip" class="badge rounded-pill bg-light">{{ ip }}</span></li>
|
<li>{{ $t('modals.peer-view.ip') }}: <span v-for="ip in selectedPeer.Addresses" :key="ip"
|
||||||
|
class="badge rounded-pill bg-light">{{ ip }}</span></li>
|
||||||
<li>{{ $t('modals.peer-view.user') }}: {{ selectedPeer.UserIdentifier }}</li>
|
<li>{{ $t('modals.peer-view.user') }}: {{ selectedPeer.UserIdentifier }}</li>
|
||||||
<li v-if="selectedPeer.Notes">{{ $t('modals.peer-view.notes') }}: {{ selectedPeer.Notes }}</li>
|
<li v-if="selectedPeer.Notes">{{ $t('modals.peer-view.notes') }}: {{ selectedPeer.Notes }}</li>
|
||||||
<li v-if="selectedPeer.ExpiresAt">{{ $t('modals.peer-view.expiry-status') }}: {{ selectedPeer.ExpiresAt }}</li>
|
<li v-if="selectedPeer.ExpiresAt">{{ $t('modals.peer-view.expiry-status') }}: {{
|
||||||
<li v-if="selectedPeer.Disabled">{{ $t('modals.peer-view.disabled-status') }}: {{ selectedPeer.DisabledReason }}</li>
|
selectedPeer.ExpiresAt }}</li>
|
||||||
|
<li v-if="selectedPeer.Disabled">{{ $t('modals.peer-view.disabled-status') }}: {{
|
||||||
|
selectedPeer.DisabledReason }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<img class="config-qr-img" :src="peers.ConfigQrUrl(props.peerId)" loading="lazy" alt="Configuration QR Code">
|
<img class="config-qr-img" :src="ConfigQrUrl()" loading="lazy" alt="Configuration QR Code">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -144,16 +166,20 @@ function email() {
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<h2 class="accordion-header" id="headingStatus">
|
<h2 class="accordion-header" id="headingStatus">
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseStatus" aria-expanded="false" aria-controls="collapseStatus">
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#collapseStatus" aria-expanded="false" aria-controls="collapseStatus">
|
||||||
{{ $t('modals.peer-view.section-status') }}
|
{{ $t('modals.peer-view.section-status') }}
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<div id="collapseStatus" class="accordion-collapse collapse" aria-labelledby="headingStatus" data-bs-parent="#peerInformation" style="">
|
<div id="collapseStatus" class="accordion-collapse collapse" aria-labelledby="headingStatus"
|
||||||
|
data-bs-parent="#peerInformation" style="">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<h4>{{ $t('modals.peer-view.traffic') }}</h4>
|
<h4>{{ $t('modals.peer-view.traffic') }}</h4>
|
||||||
<p><i class="fas fa-long-arrow-alt-down" :title="$t('modals.peer-view.download')"></i> {{ selectedStats.BytesReceived }} Bytes / <i class="fas fa-long-arrow-alt-up" :title="$t('modals.peer-view.upload')"></i> {{ selectedStats.BytesTransmitted }} Bytes</p>
|
<p><i class="fas fa-long-arrow-alt-down" :title="$t('modals.peer-view.download')"></i> {{
|
||||||
|
selectedStats.BytesReceived }} Bytes / <i class="fas fa-long-arrow-alt-up"
|
||||||
|
:title="$t('modals.peer-view.upload')"></i> {{ selectedStats.BytesTransmitted }} Bytes</p>
|
||||||
<h4>{{ $t('modals.peer-view.connection-status') }}</h4>
|
<h4>{{ $t('modals.peer-view.connection-status') }}</h4>
|
||||||
<ul>
|
<ul>
|
||||||
<li>{{ $t('modals.peer-view.pingable') }}: {{ selectedStats.IsPingable }}</li>
|
<li>{{ $t('modals.peer-view.pingable') }}: {{ selectedStats.IsPingable }}</li>
|
||||||
@ -166,13 +192,15 @@ function email() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="selectedInterface.Mode==='server'" class="accordion-item">
|
<div v-if="selectedInterface.Mode === 'server'" class="accordion-item">
|
||||||
<h2 class="accordion-header" id="headingConfig">
|
<h2 class="accordion-header" id="headingConfig">
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseConfig" aria-expanded="false" aria-controls="collapseConfig">
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#collapseConfig" aria-expanded="false" aria-controls="collapseConfig">
|
||||||
{{ $t('modals.peer-view.section-config') }}
|
{{ $t('modals.peer-view.section-config') }}
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<div id="collapseConfig" class="accordion-collapse collapse" aria-labelledby="headingConfig" data-bs-parent="#peerInformation" style="">
|
<div id="collapseConfig" class="accordion-collapse collapse" aria-labelledby="headingConfig"
|
||||||
|
data-bs-parent="#peerInformation" style="">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
<Prism language="ini" :code="configString"></Prism>
|
<Prism language="ini" :code="configString"></Prism>
|
||||||
</div>
|
</div>
|
||||||
@ -182,18 +210,17 @@ function email() {
|
|||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="flex-fill text-start">
|
<div class="flex-fill text-start">
|
||||||
<button @click.prevent="download" type="button" class="btn btn-primary me-1">{{ $t('modals.peer-view.button-download') }}</button>
|
<button @click.prevent="download" type="button" class="btn btn-primary me-1">{{
|
||||||
<button @click.prevent="email" type="button" class="btn btn-primary me-1">{{ $t('modals.peer-view.button-email') }}</button>
|
$t('modals.peer-view.button-download') }}</button>
|
||||||
|
<button @click.prevent="email" hidden type="button" class="btn btn-primary me-1">{{
|
||||||
|
$t('modals.peer-view.button-email') }}</button>
|
||||||
</div>
|
</div>
|
||||||
<button @click.prevent="close" type="button" class="btn btn-secondary">{{ $t('general.close') }}</button>
|
<button @click.prevent="close" type="button" class="btn btn-secondary">{{ $t('general.close') }}</button>
|
||||||
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
</Modal>
|
</Modal></template>
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
<style>.config-qr-img {
|
||||||
.config-qr-img {
|
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}</style>
|
||||||
</style>
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// src/lang/index.js
|
// src/lang/index.js
|
||||||
import de from './translations/de.json';
|
import de from './translations/de.json';
|
||||||
|
import ru from './translations/ru.json';
|
||||||
import en from './translations/en.json';
|
import en from './translations/en.json';
|
||||||
import {createI18n} from "vue-i18n";
|
import {createI18n} from "vue-i18n";
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ const i18n = createI18n({
|
|||||||
fallbackLocale: "en", // set fallback locale
|
fallbackLocale: "en", // set fallback locale
|
||||||
messages: {
|
messages: {
|
||||||
"de": de,
|
"de": de,
|
||||||
|
"ru": ru,
|
||||||
"en": en
|
"en": en
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
489
frontend/src/lang/translations/ru.json
Normal file
489
frontend/src/lang/translations/ru.json
Normal file
@ -0,0 +1,489 @@
|
|||||||
|
{
|
||||||
|
"general": {
|
||||||
|
"pagination": {
|
||||||
|
"size": "Количество элементов",
|
||||||
|
"all": "Все (медленно)"
|
||||||
|
},
|
||||||
|
"search": {
|
||||||
|
"placeholder": "Поиск...",
|
||||||
|
"button": "Поиск"
|
||||||
|
},
|
||||||
|
"select-all": "Выбрать все",
|
||||||
|
"yes": "Да",
|
||||||
|
"no": "Нет",
|
||||||
|
"cancel": "Отмена",
|
||||||
|
"close": "Закрыть",
|
||||||
|
"save": "Сохранить",
|
||||||
|
"delete": "Удалить"
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"headline": "Пожалуйста, войдите в систему",
|
||||||
|
"username": {
|
||||||
|
"label": "Имя пользователя",
|
||||||
|
"placeholder": "Пожалуйста, введите ваше имя пользователя"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"label": "Пароль",
|
||||||
|
"placeholder": "Пожалуйста, введите ваш пароль"
|
||||||
|
},
|
||||||
|
"button": "Войти"
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"home": "Главная",
|
||||||
|
"interfaces": "Интерфейсы",
|
||||||
|
"users": "Пользователи",
|
||||||
|
"lang": "Сменить язык",
|
||||||
|
"profile": "Мой профиль",
|
||||||
|
"login": "Вход",
|
||||||
|
"logout": "Выход"
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"headline": "Портал VPN WireGuard®",
|
||||||
|
"info-headline": "Дополнительная информация",
|
||||||
|
"abstract": "WireGuard® - это чрезвычайно простой, но быстрый и современный VPN, использующий передовую криптографию. Он стремится быть быстрее, проще, компактнее и полезнее, чем IPsec, избегая при этом значительных сложностей. Он предназначен для значительного повышения производительности по сравнению с OpenVPN.",
|
||||||
|
"installation": {
|
||||||
|
"box-header": "Установка WireGuard",
|
||||||
|
"headline": "Установка",
|
||||||
|
"content": "Инструкции по установке клиентского программного обеспечения можно найти на официальном сайте WireGuard.",
|
||||||
|
"btn": "Открыть инструкции",
|
||||||
|
"button": "Открыть инструкции"
|
||||||
|
},
|
||||||
|
"about-wg": {
|
||||||
|
"box-header": "О WireGuard",
|
||||||
|
"headline": "О программе",
|
||||||
|
"content": "WireGuard® - это чрезвычайно простой, но быстрый и современный VPN, использующий передовую криптографию.",
|
||||||
|
"button": "Подробнее"
|
||||||
|
},
|
||||||
|
"about-portal": {
|
||||||
|
"box-header": "О портале WireGuard",
|
||||||
|
"headline": "Портал WireGuard",
|
||||||
|
"content": "Портал WireGuard - это простой веб-портал для настройки WireGuard.",
|
||||||
|
"button": "Подробнее"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"headline": "VPN Профили",
|
||||||
|
"abstract": "Вы можете получить доступ и загрузить свои личные конфигурации VPN через свой пользовательский профиль.",
|
||||||
|
"content": "Чтобы найти все сконфигурированные профили, нажмите на кнопку ниже.",
|
||||||
|
"button": "Открыть мой профиль"
|
||||||
|
},
|
||||||
|
"admin": {
|
||||||
|
"headline": "Административная зона",
|
||||||
|
"abstract": "В административной зоне вы можете управлять узлами и серверным интерфейсом WireGuard, а также пользователями, которым разрешен вход в портал WireGuard.",
|
||||||
|
"content": "",
|
||||||
|
"button-admin": "Открыть администрирование сервера",
|
||||||
|
"button-user": "Открыть администрирование пользователей"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"interfaces": {
|
||||||
|
"headline": "Администрирование интерфейсов",
|
||||||
|
"headline-peers": "Текущие VPN пиры",
|
||||||
|
"headline-endpoints": "Текущие конечные точки",
|
||||||
|
"no-interface": {
|
||||||
|
"default-selection": "Интерфейсы отсутствуют",
|
||||||
|
"headline": "Интерфейсы не найдены...",
|
||||||
|
"abstract": "Нажмите на кнопку со знаком плюса выше, чтобы создать новый интерфейс WireGuard."
|
||||||
|
},
|
||||||
|
"no-peer": {
|
||||||
|
"headline": "Пиры отсутствуют",
|
||||||
|
"abstract": "В настоящее время для выбранного интерфейса WireGuard нет доступных пиров."
|
||||||
|
},
|
||||||
|
"table-heading": {
|
||||||
|
"name": "Имя",
|
||||||
|
"user": "Пользователь",
|
||||||
|
"ip": "IP-адреса",
|
||||||
|
"endpoint": "Конечная точка",
|
||||||
|
"status": "Статус"
|
||||||
|
},
|
||||||
|
"interface": {
|
||||||
|
"headline": "Статус интерфейса для",
|
||||||
|
"mode": "режим",
|
||||||
|
"key": "Публичный ключ",
|
||||||
|
"endpoint": "Публичная конечная точка",
|
||||||
|
"port": "Порт прослушивания",
|
||||||
|
"peers": "Активные пиры",
|
||||||
|
"total-peers": "Всего пиров",
|
||||||
|
"endpoints": "Активные конечные точки",
|
||||||
|
"total-endpoints": "Всего конечных точек",
|
||||||
|
"ip": "IP-адрес",
|
||||||
|
"default-allowed-ip": "Разрешенные IP по умолчанию",
|
||||||
|
"dns": "DNS-серверы",
|
||||||
|
"mtu": "MTU",
|
||||||
|
"default-keep-alive": "Интервал поддержания активности по умолчанию",
|
||||||
|
"button-show-config": "Показать конфигурацию",
|
||||||
|
"button-download-config": "Скачать конфигурацию",
|
||||||
|
"button-store-config": "Сохранить конфигурацию для wg-quick",
|
||||||
|
"button-edit": "Редактировать интерфейс"
|
||||||
|
},
|
||||||
|
"button-add-interface": "Добавить интерфейс",
|
||||||
|
"button-add-peer": "Добавить пира",
|
||||||
|
"button-add-peers": "Добавить несколько пиров",
|
||||||
|
"button-show-peer": "Показать пира",
|
||||||
|
"button-edit-peer": "Редактировать пира",
|
||||||
|
"peer-disabled": "Пир отключен, причина:",
|
||||||
|
"peer-expiring": "Пир истекает в",
|
||||||
|
"peer-connected": "Подключено",
|
||||||
|
"peer-not-connected": "Не подключено",
|
||||||
|
"peer-handshake": "Последнее рукопожатие:"
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"headline": "Администрирование пользователей",
|
||||||
|
"table-heading": {
|
||||||
|
"id": "ID",
|
||||||
|
"email": "Электронная почта",
|
||||||
|
"firstname": "Имя",
|
||||||
|
"lastname": "Фамилия",
|
||||||
|
"source": "Источник",
|
||||||
|
"peers": "Пиры",
|
||||||
|
"admin": "Админ"
|
||||||
|
},
|
||||||
|
"no-user": {
|
||||||
|
"headline": "Пользователи отсутствуют",
|
||||||
|
"abstract": "В настоящее время в портале WireGuard не зарегистрировано ни одного пользователя."
|
||||||
|
},
|
||||||
|
"button-add-user": "Добавить пользователя",
|
||||||
|
"button-show-user": "Показать пользователя",
|
||||||
|
"button-edit-user": "Редактировать пользователя",
|
||||||
|
"user-disabled": "Пользователь отключен, причина:",
|
||||||
|
"user-locked": "Учетная запись заблокирована, причина:",
|
||||||
|
"admin": "Пользователь имеет права администратора",
|
||||||
|
"no-admin": "Пользователь не имеет прав администратора"
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
"headline": "Мои VPN пиры",
|
||||||
|
"table-heading": {
|
||||||
|
"name": "Имя",
|
||||||
|
"ip": "IP-адреса",
|
||||||
|
"stats": "Статус",
|
||||||
|
"interface": "Интерфейс сервера"
|
||||||
|
},
|
||||||
|
"no-peer": {
|
||||||
|
"headline": "Пиров нет",
|
||||||
|
"abstract": "В настоящее время у вашего профиля пользователя нет связанных пиров."
|
||||||
|
},
|
||||||
|
"peer-connected": "Подключено",
|
||||||
|
"button-add-peer": "Добавить пира",
|
||||||
|
"button-show-peer": "Показать пира",
|
||||||
|
"button-edit-peer": "Редактировать пира"
|
||||||
|
},
|
||||||
|
"modals": {
|
||||||
|
"user-view": {
|
||||||
|
"headline": "Учетная запись пользователя:",
|
||||||
|
"tab-user": "Информация",
|
||||||
|
"tab-peers": "Пиры",
|
||||||
|
"headline-info": "Информация о пользователе:",
|
||||||
|
"headline-notes": "Заметки:",
|
||||||
|
"email": "Электронная почта",
|
||||||
|
"firstname": "Имя",
|
||||||
|
"lastname": "Фамилия",
|
||||||
|
"phone": "Номер телефона",
|
||||||
|
"department": "Отдел",
|
||||||
|
"disabled": "Учетная запись отключена",
|
||||||
|
"locked": "Учетная запись заблокирована",
|
||||||
|
"no-peers": "У пользователя нет связанных пиров.",
|
||||||
|
"peers": {
|
||||||
|
"name": "Имя",
|
||||||
|
"interface": "Интерфейс",
|
||||||
|
"ip": "IP-адреса"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"user-edit": {
|
||||||
|
"headline-edit": "Редактировать пользователя:",
|
||||||
|
"headline-new": "Новый пользователь",
|
||||||
|
"header-general": "Общее",
|
||||||
|
"header-personal": "Информация о пользователе",
|
||||||
|
"header-notes": "Заметки",
|
||||||
|
"header-state": "Состояние",
|
||||||
|
"identifier": {
|
||||||
|
"label": "Идентификатор",
|
||||||
|
"placeholder": "Уникальный идентификатор пользователя"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"label": "Источник",
|
||||||
|
"placeholder": "Источник пользователя"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"label": "Пароль",
|
||||||
|
"placeholder": "Надежный пароль",
|
||||||
|
"description": "Оставьте это поле пустым, чтобы сохранить текущий пароль."
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"label": "Электронная почта",
|
||||||
|
"placeholder": "Адрес электронной почты"
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"label": "Телефон",
|
||||||
|
"placeholder": "Номер телефона"
|
||||||
|
},
|
||||||
|
"department": {
|
||||||
|
"label": "Отдел",
|
||||||
|
"placeholder": "Отдел"
|
||||||
|
},
|
||||||
|
"firstname": {
|
||||||
|
"label": "Имя",
|
||||||
|
"placeholder": "Имя"
|
||||||
|
},
|
||||||
|
"lastname": {
|
||||||
|
"label": "Фамилия",
|
||||||
|
"placeholder": "Фамилия"
|
||||||
|
},
|
||||||
|
"notes": {
|
||||||
|
"label": "Заметки",
|
||||||
|
"placeholder": ""
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"label": "Отключен (нет возможности подключения к WireGuard и входа в систему)"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"label": "Заблокирован (вход в систему невозможен, подключения WireGuard работают)"
|
||||||
|
},
|
||||||
|
"admin": {
|
||||||
|
"label": "Является администратором"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"interface-view": {
|
||||||
|
"headline": "Конфигурация интерфейса:"
|
||||||
|
},
|
||||||
|
"interface-edit": {
|
||||||
|
"headline-edit": "Редактировать интерфейс:",
|
||||||
|
"headline-new": "Новый интерфейс",
|
||||||
|
"tab-interface": "Интерфейс",
|
||||||
|
"tab-peerdef": "Настройки пира по умолчанию",
|
||||||
|
"header-general": "Общие",
|
||||||
|
"header-network": "Сеть",
|
||||||
|
"header-crypto": "Криптография",
|
||||||
|
"header-hooks": "Хуки интерфейса",
|
||||||
|
"header-peer-hooks": "Хуки",
|
||||||
|
"header-state": "Состояние",
|
||||||
|
"identifier": {
|
||||||
|
"label": "Идентификатор",
|
||||||
|
"placeholder": "Уникальный идентификатор интерфейса"
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"label": "Режим интерфейса",
|
||||||
|
"server": "Режим сервера",
|
||||||
|
"client": "Режим клиента",
|
||||||
|
"any": "Неизвестный режим"
|
||||||
|
},
|
||||||
|
"display-name": {
|
||||||
|
"label": "Отображаемое имя",
|
||||||
|
"placeholder": "Описательное имя для интерфейса"
|
||||||
|
},
|
||||||
|
"private-key": {
|
||||||
|
"label": "Приватный ключ",
|
||||||
|
"placeholder": "Приватный ключ"
|
||||||
|
},
|
||||||
|
"public-key": {
|
||||||
|
"label": "Публичный ключ",
|
||||||
|
"placeholder": "Публичный ключ"
|
||||||
|
},
|
||||||
|
"ip": {
|
||||||
|
"label": "IP-адреса",
|
||||||
|
"placeholder": "IP-адреса (в формате CIDR)"
|
||||||
|
},
|
||||||
|
"listen-port": {
|
||||||
|
"label": "Порт прослушивания",
|
||||||
|
"placeholder": "Порт для прослушивания"
|
||||||
|
},
|
||||||
|
"dns": {
|
||||||
|
"label": "DNS-сервер",
|
||||||
|
"placeholder": "Используемые DNS-серверы"
|
||||||
|
},
|
||||||
|
"dns-search": {
|
||||||
|
"label": "Поисковые домены DNS",
|
||||||
|
"placeholder": "Префиксы поиска DNS"
|
||||||
|
},
|
||||||
|
"mtu": {
|
||||||
|
"label": "MTU",
|
||||||
|
"placeholder": "MTU интерфейса (0 = использовать значение по умолчанию)"
|
||||||
|
},
|
||||||
|
"firewall-mark": {
|
||||||
|
"label": "Метка брандмауэра",
|
||||||
|
"placeholder": "Метка брандмауэра, применяемая к исходящему трафику (0 = автоматически)"
|
||||||
|
},
|
||||||
|
"routing-table": {
|
||||||
|
"label": "Таблица маршрутизации",
|
||||||
|
"placeholder": "ID таблицы маршрутизации",
|
||||||
|
"description": "Особые случаи: off = не управлять маршрутами, 0 = автоматически"
|
||||||
|
},
|
||||||
|
"pre-up": {
|
||||||
|
"label": "Pre-Up",
|
||||||
|
"placeholder": "Одна или несколько команд bash, разделенных ;"
|
||||||
|
},
|
||||||
|
"post-up": {
|
||||||
|
"label": "Post-Up",
|
||||||
|
"placeholder": "Одна или несколько команд bash, разделенных ;"
|
||||||
|
},
|
||||||
|
"pre-down": {
|
||||||
|
"label": "Pre-Down",
|
||||||
|
"placeholder": "Одна или несколько команд bash, разделенных ;"
|
||||||
|
},
|
||||||
|
"post-down": {
|
||||||
|
"label": "Post-Down",
|
||||||
|
"placeholder": "Одна или несколько команд bash, разделенных ;"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"label": "Интерфейс отключен"
|
||||||
|
},
|
||||||
|
"save-config": {
|
||||||
|
"label": "Автоматически сохранять конфигурацию wg-quick"
|
||||||
|
},
|
||||||
|
"defaults": {
|
||||||
|
"endpoint": {
|
||||||
|
"label": "Адрес конечной точки",
|
||||||
|
"placeholder": "Адрес конечной точки",
|
||||||
|
"description": "Адрес конечной точки, к которой будут подключаться пиры."
|
||||||
|
},
|
||||||
|
"networks": {
|
||||||
|
"label": "IP-сети",
|
||||||
|
"placeholder": "Сетевые адреса",
|
||||||
|
"description": "Пиры будут получать IP-адреса из этих подсетей."
|
||||||
|
},
|
||||||
|
"allowed-ip": {
|
||||||
|
"label": "Разрешенные IP-адреса",
|
||||||
|
"placeholder": "Разрешенные IP-адреса по умолчанию"
|
||||||
|
},
|
||||||
|
"mtu": {
|
||||||
|
"label": "MTU",
|
||||||
|
"placeholder": "MTU клиента (0 = использовать значение по умолчанию)"
|
||||||
|
},
|
||||||
|
"keep-alive": {
|
||||||
|
"label": "Интервал поддержания активности",
|
||||||
|
"placeholder": "Постоянное поддержание активности (0 = значение по умолчанию)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"button-apply-defaults": "Применить настройки пира по умолчанию"
|
||||||
|
},
|
||||||
|
"peer-view": {
|
||||||
|
"headline-peer": "Пир:",
|
||||||
|
"headline-endpoint": "Конечная точка:",
|
||||||
|
"section-info": "Информация о пире",
|
||||||
|
"section-status": "Текущий статус",
|
||||||
|
"section-config": "Конфигурация",
|
||||||
|
"identifier": "Идентификатор",
|
||||||
|
"ip": "IP-адреса",
|
||||||
|
"user": "Связанный пользователь",
|
||||||
|
"notes": "Заметки",
|
||||||
|
"expiry-status": "Истекает в",
|
||||||
|
"disabled-status": "Отключено в",
|
||||||
|
"traffic": "Трафик",
|
||||||
|
"connection-status": "Статус соединения",
|
||||||
|
"upload": "Загружено байт (от сервера к пиру)",
|
||||||
|
"download": "Скачано байт (от пира к серверу)",
|
||||||
|
"pingable": "Доступность пинга",
|
||||||
|
"handshake": "Последнее рукопожатие",
|
||||||
|
"connected-since": "Подключен с",
|
||||||
|
"endpoint": "Конечная точка",
|
||||||
|
"button-download": "Скачать конфигурацию",
|
||||||
|
"button-email": "Отправить конфигурацию по электронной почте"
|
||||||
|
},
|
||||||
|
"peer-edit": {
|
||||||
|
"headline-edit-peer": "Редактировать пира:",
|
||||||
|
"headline-edit-endpoint": "Редактировать конечную точку:",
|
||||||
|
"headline-new-peer": "Создать пира",
|
||||||
|
"headline-new-endpoint": "Создать конечную точку",
|
||||||
|
"header-general": "Общее",
|
||||||
|
"header-network": "Сеть",
|
||||||
|
"header-crypto": "Криптография",
|
||||||
|
"header-hooks": "Хуки (Выполняются на пире)",
|
||||||
|
"header-state": "Состояние",
|
||||||
|
"display-name": {
|
||||||
|
"label": "Отображаемое имя",
|
||||||
|
"placeholder": "Описательное имя для пира"
|
||||||
|
},
|
||||||
|
"linked-user": {
|
||||||
|
"label": "Связанный пользователь",
|
||||||
|
"placeholder": "Учетная запись пользователя, которой принадлежит этот пир"
|
||||||
|
},
|
||||||
|
"private-key": {
|
||||||
|
"label": "Приватный ключ",
|
||||||
|
"placeholder": "Приватный ключ"
|
||||||
|
},
|
||||||
|
"public-key": {
|
||||||
|
"label": "Публичный ключ",
|
||||||
|
"placeholder": "Публичный ключ"
|
||||||
|
},
|
||||||
|
"preshared-key": {
|
||||||
|
"label": "Предварительно разделяемый ключ",
|
||||||
|
"placeholder": "Необязательный предварительно разделяемый ключ"
|
||||||
|
},
|
||||||
|
"endpoint-public-key": {
|
||||||
|
"label": "Публичный ключ конечной точки",
|
||||||
|
"placeholder": "Публичный ключ удаленной конечной точки"
|
||||||
|
},
|
||||||
|
"endpoint": {
|
||||||
|
"label": "Адрес конечной точки",
|
||||||
|
"placeholder": "Адрес удаленной конечной точки"
|
||||||
|
},
|
||||||
|
"ip": {
|
||||||
|
"label": "IP-адреса",
|
||||||
|
"placeholder": "IP-адреса (в формате CIDR)"
|
||||||
|
},
|
||||||
|
"allowed-ip": {
|
||||||
|
"label": "Разрешенные IP-адреса",
|
||||||
|
"placeholder": "Разрешенные IP-адреса (в формате CIDR)"
|
||||||
|
},
|
||||||
|
"extra-allowed-ip": {
|
||||||
|
"label": "Дополнительно разрешенные IP-адреса",
|
||||||
|
"placeholder": "Дополнительные разрешенные IP-адреса (на стороне сервера)",
|
||||||
|
"description": "Эти IP-адреса будут добавлены в удаленный интерфейс WireGuard как разрешенные IP-адреса."
|
||||||
|
},
|
||||||
|
"dns": {
|
||||||
|
"label": "DNS Server",
|
||||||
|
"placeholder": "The DNS servers that should be used"
|
||||||
|
},
|
||||||
|
"dns-search": {
|
||||||
|
"label": "DNS Search Domains",
|
||||||
|
"placeholder": "DNS search prefixes"
|
||||||
|
},
|
||||||
|
"keep-alive": {
|
||||||
|
"label": "Keep Alive Interval",
|
||||||
|
"placeholder": "Persistent Keepalive (0 = default)"
|
||||||
|
},
|
||||||
|
"mtu": {
|
||||||
|
"label": "MTU",
|
||||||
|
"placeholder": "The client MTU (0 = keep default)"
|
||||||
|
},
|
||||||
|
"pre-up": {
|
||||||
|
"label": "Pre-Up",
|
||||||
|
"placeholder": "One or multiple bash commands separated by ;"
|
||||||
|
},
|
||||||
|
"post-up": {
|
||||||
|
"label": "Post-Up",
|
||||||
|
"placeholder": "One or multiple bash commands separated by ;"
|
||||||
|
},
|
||||||
|
"pre-down": {
|
||||||
|
"label": "Pre-Down",
|
||||||
|
"placeholder": "One or multiple bash commands separated by ;"
|
||||||
|
},
|
||||||
|
"post-down": {
|
||||||
|
"label": "Post-Down",
|
||||||
|
"placeholder": "One or multiple bash commands separated by ;"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"label": "Peer Disabled"
|
||||||
|
},
|
||||||
|
"ignore-global": {
|
||||||
|
"label": "Ignore global settings"
|
||||||
|
},
|
||||||
|
"expires-at": {
|
||||||
|
"label": "Expiry date"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"peer-multi-create": {
|
||||||
|
"headline-peer": "Create multiple peers",
|
||||||
|
"headline-endpoint": "Create multiple endpoints",
|
||||||
|
"identifiers": {
|
||||||
|
"label": "User Identifiers",
|
||||||
|
"placeholder": "User Identifiers",
|
||||||
|
"description": "A user identifier (the username) for which a peer should be created."
|
||||||
|
},
|
||||||
|
"prefix": {
|
||||||
|
"headline-peer": "Peer:",
|
||||||
|
"headline-endpoint": "Endpoint:",
|
||||||
|
"label": "Display Name Prefix",
|
||||||
|
"placeholder": "The prefix",
|
||||||
|
"description": "A prefix that is added to the peers display name."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {authStore} from "@/stores/auth";
|
import { authStore } from "@/stores/auth";
|
||||||
import {RouterLink} from "vue-router";
|
import { RouterLink } from "vue-router";
|
||||||
|
|
||||||
const auth = authStore()
|
const auth = authStore()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -29,7 +29,8 @@
|
|||||||
<hr class="my-4">
|
<hr class="my-4">
|
||||||
<p>{{ $t('home.admin.content') }}</p>
|
<p>{{ $t('home.admin.content') }}</p>
|
||||||
<p class="lead">
|
<p class="lead">
|
||||||
<RouterLink :to="{ name: 'interfaces' }" class="btn btn-primary btn-lg me-2">{{ $t('home.admin.button-admin') }}</RouterLink>
|
<RouterLink :to="{ name: 'interfaces' }" class="btn btn-primary btn-lg me-2">{{ $t('home.admin.button-admin') }}
|
||||||
|
</RouterLink>
|
||||||
<RouterLink :to="{ name: 'users' }" class="btn btn-primary btn-lg">{{ $t('home.admin.button-user') }}</RouterLink>
|
<RouterLink :to="{ name: 'users' }" class="btn btn-primary btn-lg">{{ $t('home.admin.button-user') }}</RouterLink>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import PeerViewModal from "../components/PeerViewModal.vue";
|
import PeerViewModal from "../components/PeerViewModal.vue";
|
||||||
|
|
||||||
import {onMounted, ref} from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import {profileStore} from "@/stores/profile";
|
import { profileStore } from "@/stores/profile";
|
||||||
import PeerEditModal from "@/components/PeerEditModal.vue";
|
import PeerEditModal from "@/components/PeerEditModal.vue";
|
||||||
import {settingsStore} from "@/stores/settings";
|
import { settingsStore } from "@/stores/settings";
|
||||||
|
|
||||||
const settings = settingsStore()
|
const settings = settingsStore()
|
||||||
const profile = profileStore()
|
const profile = profileStore()
|
||||||
@ -17,11 +17,12 @@ onMounted(async () => {
|
|||||||
await profile.LoadPeers()
|
await profile.LoadPeers()
|
||||||
await profile.LoadStats()
|
await profile.LoadStats()
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PeerViewModal :peerId="viewedPeerId" :visible="viewedPeerId!==''" @close="viewedPeerId=''"></PeerViewModal>
|
<PeerViewModal :peerId="viewedPeerId" :visible="viewedPeerId !== ''" @close="viewedPeerId = ''"></PeerViewModal>
|
||||||
<PeerEditModal :peerId="editPeerId" :visible="editPeerId!==''" @close="editPeerId=''"></PeerEditModal>
|
<PeerEditModal :peerId="editPeerId" :visible="editPeerId !== ''" @close="editPeerId = ''"></PeerEditModal>
|
||||||
|
|
||||||
<!-- Peer list -->
|
<!-- Peer list -->
|
||||||
<div class="mt-4 row">
|
<div class="mt-4 row">
|
||||||
@ -31,33 +32,38 @@ onMounted(async () => {
|
|||||||
<div class="col-12 col-lg-4 text-lg-end">
|
<div class="col-12 col-lg-4 text-lg-end">
|
||||||
<div class="form-group d-inline">
|
<div class="form-group d-inline">
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
<input v-model="profile.filter" class="form-control" :placeholder="$t('general.search.placeholder')" type="text" @keyup="profile.afterPageSizeChange">
|
<input v-model="profile.filter" class="form-control" :placeholder="$t('general.search.placeholder')" type="text"
|
||||||
<button class="input-group-text btn btn-primary" :title="$t('general.search.button')"><i class="fa-solid fa-search"></i></button>
|
@keyup="profile.afterPageSizeChange">
|
||||||
|
<button class="input-group-text btn btn-primary" :title="$t('general.search.button')"><i
|
||||||
|
class="fa-solid fa-search"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-lg-3 text-lg-end">
|
<div class="col-12 col-lg-3 text-lg-end">
|
||||||
<a v-if="settings.Setting('SelfProvisioning')" class="btn btn-primary ms-2" href="#" :title="$t('general.search.button-add-peer')" @click.prevent="editPeerId='#NEW#'"><i class="fa fa-plus me-1"></i><i class="fa fa-user"></i></a>
|
<a v-if="settings.Setting('SelfProvisioning')" class="btn btn-primary ms-2" href="#"
|
||||||
|
:title="$t('general.search.button-add-peer')" @click.prevent="editPeerId = '#NEW#'"><i
|
||||||
|
class="fa fa-plus me-1"></i><i class="fa fa-user"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-2 table-responsive">
|
<div class="mt-2 table-responsive">
|
||||||
<div v-if="profile.CountPeers===0">
|
<div v-if="profile.CountPeers === 0">
|
||||||
<h4>{{ $t('profile.no-peer.headline') }}</h4>
|
<h4>{{ $t('profile.no-peer.headline') }}</h4>
|
||||||
<p>{{ $t('profile.no-peer.abstract') }}</p>
|
<p>{{ $t('profile.no-peer.abstract') }}</p>
|
||||||
</div>
|
</div>
|
||||||
<table v-if="profile.CountPeers!==0" id="peerTable" class="table table-sm">
|
<table v-if="profile.CountPeers !== 0" id="peerTable" class="table table-sm">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">
|
<th scope="col">
|
||||||
<input id="flexCheckDefault" class="form-check-input" :title="$t('general.select-all')" type="checkbox" value="">
|
<input id="flexCheckDefault" class="form-check-input" :title="$t('general.select-all')" type="checkbox"
|
||||||
</th><!-- select -->
|
value="">
|
||||||
<th scope="col"></th><!-- status -->
|
</th><!-- select -->
|
||||||
<th scope="col">{{ $t('profile.table-heading.name') }}</th>
|
<th scope="col"></th><!-- status -->
|
||||||
<th scope="col">{{ $t('profile.table-heading.ip') }}</th>
|
<th scope="col">{{ $t('profile.table-heading.name') }}</th>
|
||||||
<th v-if="profile.hasStatistics" scope="col">{{ $t('profile.table-heading.stats') }}</th>
|
<th scope="col">{{ $t('profile.table-heading.ip') }}</th>
|
||||||
<th scope="col">{{ $t('profile.table-heading.interface') }}</th>
|
<th v-if="profile.hasStatistics" scope="col">{{ $t('profile.table-heading.stats') }}</th>
|
||||||
<th scope="col"></th><!-- Actions -->
|
<th scope="col">{{ $t('profile.table-heading.interface') }}</th>
|
||||||
</tr>
|
<th scope="col"></th><!-- Actions -->
|
||||||
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="peer in profile.FilteredAndPagedPeers" :key="peer.Identifier">
|
<tr v-for="peer in profile.FilteredAndPagedPeers" :key="peer.Identifier">
|
||||||
@ -65,25 +71,31 @@ onMounted(async () => {
|
|||||||
<input id="flexCheckDefault" class="form-check-input" type="checkbox" value="">
|
<input id="flexCheckDefault" class="form-check-input" type="checkbox" value="">
|
||||||
</th>
|
</th>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<span v-if="peer.Disabled" class="text-danger"><i class="fa fa-circle-xmark" :title="peer.DisabledReason"></i></span>
|
<span v-if="peer.Disabled" class="text-danger"><i class="fa fa-circle-xmark"
|
||||||
<span v-if="!peer.Disabled && peer.ExpiresAt" class="text-warning"><i class="fas fa-hourglass-end" :title="peer.ExpiresAt"></i></span>
|
:title="peer.DisabledReason"></i></span>
|
||||||
|
<span v-if="!peer.Disabled && peer.ExpiresAt" class="text-warning"><i class="fas fa-hourglass-end"
|
||||||
|
:title="peer.ExpiresAt"></i></span>
|
||||||
</td>
|
</td>
|
||||||
<td><span v-if="peer.DisplayName" :title="peer.Identifier">{{peer.DisplayName}}</span><span v-else :title="peer.Identifier">{{$filters.truncate(peer.Identifier, 10)}}</span></td>
|
<td><span v-if="peer.DisplayName" :title="peer.Identifier">{{ peer.DisplayName }}</span><span v-else
|
||||||
|
:title="peer.Identifier">{{ $filters.truncate(peer.Identifier, 10) }}</span></td>
|
||||||
<td>
|
<td>
|
||||||
<span v-for="ip in peer.Addresses" :key="ip" class="badge rounded-pill bg-light">{{ ip }}</span>
|
<span v-for="ip in peer.Addresses" :key="ip" class="badge rounded-pill bg-light">{{ ip }}</span>
|
||||||
</td>
|
</td>
|
||||||
<td v-if="profile.hasStatistics">
|
<td v-if="profile.hasStatistics">
|
||||||
<div v-if="profile.Statistics(peer.Identifier).IsConnected">
|
<div v-if="profile.Statistics(peer.Identifier).IsConnected">
|
||||||
<span class="badge rounded-pill bg-success"><i class="fa-solid fa-link"></i></span> <span :title="peers.Statistics(peer.Identifier).LastHandshake">{{ $t('profile.peer-connected') }}</span>
|
<span class="badge rounded-pill bg-success"><i class="fa-solid fa-link"></i></span>
|
||||||
|
<span :title="profile.Statistics(peer.Identifier).LastHandshake">{{ $t('profile.peer-connected') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<span class="badge rounded-pill bg-light"><i class="fa-solid fa-link-slash"></i></span>
|
<span class="badge rounded-pill bg-light"><i class="fa-solid fa-link-slash"></i></span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>{{peer.InterfaceIdentifier}}</td>
|
<td>{{ peer.InterfaceIdentifier }}</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<a href="#" :title="$t('profile.button-show-peer')" @click.prevent="viewedPeerId=peer.Identifier"><i class="fas fa-eye me-2"></i></a>
|
<a href="#" :title="$t('profile.button-show-peer')" @click.prevent="viewedPeerId = peer.Identifier"><i
|
||||||
<a href="#" :title="$t('profile.button-edit-peer')" @click.prevent="editPeerId=peer.Identifier"><i class="fas fa-cog"></i></a>
|
class="fas fa-eye me-2"></i></a>
|
||||||
|
<a href="#" :title="$t('profile.button-edit-peer')" @click.prevent="editPeerId = peer.Identifier"><i
|
||||||
|
class="fas fa-cog"></i></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -94,33 +106,34 @@ onMounted(async () => {
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<ul class="pagination pagination-sm">
|
<ul class="pagination pagination-sm">
|
||||||
<li :class="{disabled:profile.pageOffset===0}" class="page-item">
|
<li :class="{ disabled: profile.pageOffset === 0 }" class="page-item">
|
||||||
<a class="page-link" @click="profile.previousPage">«</a>
|
<a class="page-link" @click="profile.previousPage">«</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li v-for="page in profile.pages" :key="page" :class="{active:profile.currentPage===page}" class="page-item">
|
<li v-for="page in profile.pages" :key="page" :class="{ active: profile.currentPage === page }" class="page-item">
|
||||||
<a class="page-link" @click="profile.gotoPage(page)">{{page}}</a>
|
<a class="page-link" @click="profile.gotoPage(page)">{{ page }}</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li :class="{disabled:!profile.hasNextPage}" class="page-item">
|
<li :class="{ disabled: !profile.hasNextPage }" class="page-item">
|
||||||
<a class="page-link" @click="profile.nextPage">»</a>
|
<a class="page-link" @click="profile.nextPage">»</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-sm-6 col-form-label text-end" for="paginationSelector">{{ $t('general.pagination.size') }}:</label>
|
<label class="col-sm-6 col-form-label text-end" for="paginationSelector">
|
||||||
|
{{ $t('general.pagination.size')}}:
|
||||||
|
</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<select v-model.number="profile.pageSize" class="form-select" @click="profile.afterPageSizeChange()">
|
<select v-model.number="profile.pageSize" class="form-select" @click="profile.afterPageSizeChange()">
|
||||||
<option value="10">10</option>
|
<option value="10">10</option>
|
||||||
<option value="25">25</option>
|
<option value="25">25</option>
|
||||||
<option value="50">50</option>
|
<option value="50">50</option>
|
||||||
<option value="100">100</option>
|
<option value="100">100</option>
|
||||||
<option value="999999999">{{ $t('general.pagination.all') }}</option>
|
<option value="999999999">{{ $t('general.pagination.all') }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</div></template>
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -14,7 +14,7 @@
|
|||||||
let WGPORTAL_SITE_COMPANY_NAME="WireGuard Portal";
|
let WGPORTAL_SITE_COMPANY_NAME="WireGuard Portal";
|
||||||
</script>
|
</script>
|
||||||
<script src="/api/v0/config/frontend.js"></script>
|
<script src="/api/v0/config/frontend.js"></script>
|
||||||
<script type="module" crossorigin src="/app/assets/index-0ieWfcrp.js"></script>
|
<script type="module" crossorigin src="/app/assets/index--mSsjyvF.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/app/assets/index-OMIWgeM9.css">
|
<link rel="stylesheet" crossorigin href="/app/assets/index-OMIWgeM9.css">
|
||||||
</head>
|
</head>
|
||||||
<body class="d-flex flex-column min-vh-100">
|
<body class="d-flex flex-column min-vh-100">
|
||||||
|
@ -4,11 +4,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/h44z/wg-portal/internal/config"
|
"github.com/h44z/wg-portal/internal/config"
|
||||||
"github.com/h44z/wg-portal/internal/domain"
|
"github.com/h44z/wg-portal/internal/domain"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
evbus "github.com/vardius/message-bus"
|
evbus "github.com/vardius/message-bus"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type App struct {
|
type App struct {
|
||||||
@ -59,6 +60,7 @@ func New(cfg *config.Config, bus evbus.MessageBus, authenticator Authenticator,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) Startup(ctx context.Context) error {
|
func (a *App) Startup(ctx context.Context) error {
|
||||||
|
|
||||||
a.UserManager.StartBackgroundJobs(ctx)
|
a.UserManager.StartBackgroundJobs(ctx)
|
||||||
a.StatisticsCollector.StartBackgroundJobs(ctx)
|
a.StatisticsCollector.StartBackgroundJobs(ctx)
|
||||||
a.WireGuardManager.StartBackgroundJobs(ctx)
|
a.WireGuardManager.StartBackgroundJobs(ctx)
|
||||||
|
@ -4,11 +4,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/h44z/wg-portal/internal/app"
|
|
||||||
"math"
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/h44z/wg-portal/internal/app"
|
||||||
|
|
||||||
"github.com/h44z/wg-portal/internal"
|
"github.com/h44z/wg-portal/internal"
|
||||||
|
|
||||||
"github.com/go-ldap/ldap/v3"
|
"github.com/go-ldap/ldap/v3"
|
||||||
@ -87,7 +88,9 @@ func (m Manager) NewUser(ctx context.Context, user *domain.User) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m Manager) StartBackgroundJobs(ctx context.Context) {
|
func (m Manager) StartBackgroundJobs(ctx context.Context) {
|
||||||
|
|
||||||
go m.runLdapSynchronizationService(ctx)
|
go m.runLdapSynchronizationService(ctx)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Manager) GetUser(ctx context.Context, id domain.UserIdentifier) (*domain.User, error) {
|
func (m Manager) GetUser(ctx context.Context, id domain.UserIdentifier) (*domain.User, error) {
|
||||||
@ -322,7 +325,7 @@ func (m Manager) runLdapSynchronizationService(ctx context.Context) {
|
|||||||
if !ldapCfg.Synchronize {
|
if !ldapCfg.Synchronize {
|
||||||
continue // sync disabled
|
continue // sync disabled
|
||||||
}
|
}
|
||||||
|
//logrus.Tracef(&ldapCfg)
|
||||||
err := m.synchronizeLdapUsers(ctx, &ldapCfg)
|
err := m.synchronizeLdapUsers(ctx, &ldapCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("failed to synchronize LDAP users for %s: %v", ldapCfg.ProviderName, err)
|
logrus.Errorf("failed to synchronize LDAP users for %s: %v", ldapCfg.ProviderName, err)
|
||||||
@ -382,15 +385,20 @@ func (m Manager) updateLdapUsers(ctx context.Context, providerName string, rawUs
|
|||||||
return fmt.Errorf("find error for user id %s: %w", user.Identifier, err)
|
return fmt.Errorf("find error for user id %s: %w", user.Identifier, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
tctx = domain.SetUserInfo(tctx, domain.SystemAdminContextUserInfo())
|
||||||
|
|
||||||
if existingUser == nil {
|
if existingUser == nil {
|
||||||
err := m.NewUser(ctx, user)
|
err := m.NewUser(tctx, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("create error for user id %s: %w", user.Identifier, err)
|
return fmt.Errorf("create error for user id %s: %w", user.Identifier, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if existingUser != nil && existingUser.Source == domain.UserSourceLdap && userChangedInLdap(existingUser, user) {
|
if existingUser != nil && existingUser.Source == domain.UserSourceLdap && userChangedInLdap(existingUser, user) {
|
||||||
err := m.users.SaveUser(ctx, user.Identifier, func(u *domain.User) (*domain.User, error) {
|
|
||||||
|
err := m.users.SaveUser(tctx, user.Identifier, func(u *domain.User) (*domain.User, error) {
|
||||||
u.UpdatedAt = time.Now()
|
u.UpdatedAt = time.Now()
|
||||||
u.UpdatedBy = "ldap_sync"
|
u.UpdatedBy = "ldap_sync"
|
||||||
u.Email = user.Email
|
u.Email = user.Email
|
||||||
|
@ -3,9 +3,10 @@ package internal
|
|||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/go-ldap/ldap/v3"
|
"github.com/go-ldap/ldap/v3"
|
||||||
"github.com/h44z/wg-portal/internal/config"
|
"github.com/h44z/wg-portal/internal/config"
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user