2023-02-12 23:13:04 +01:00
< script setup >
import PeerViewModal from "../components/PeerViewModal.vue" ;
import PeerEditModal from "../components/PeerEditModal.vue" ;
2023-07-07 15:36:07 +02:00
import PeerMultiCreateModal from "../components/PeerMultiCreateModal.vue" ;
2023-02-12 23:13:04 +01:00
import InterfaceEditModal from "../components/InterfaceEditModal.vue" ;
2023-06-14 23:03:24 +02:00
import InterfaceViewModal from "../components/InterfaceViewModal.vue" ;
2023-02-12 23:13:04 +01:00
import { onMounted , ref } from "vue" ;
import { peerStore } from "../stores/peers" ;
import { interfaceStore } from "../stores/interfaces" ;
2023-07-20 23:17:32 +02:00
import { notify } from "@kyvg/vue3-notification" ;
2023-02-12 23:13:04 +01:00
const interfaces = interfaceStore ( )
const peers = peerStore ( )
const viewedPeerId = ref ( "" )
const editPeerId = ref ( "" )
2023-07-07 15:36:07 +02:00
const multiCreatePeerId = ref ( "" )
2023-02-12 23:13:04 +01:00
const editInterfaceId = ref ( "" )
2023-06-14 23:03:24 +02:00
const viewedInterfaceId = ref ( "" )
function calculateInterfaceName ( id , name ) {
let result = id
if ( name ) {
result += ' (' + name + ')'
}
return result
}
2023-02-12 23:13:04 +01:00
2023-07-04 22:55:11 +02:00
async function download ( ) {
await interfaces . LoadInterfaceConfig ( interfaces . GetSelected . Identifier )
// credit: https://www.bitdegree.org/learn/javascript-download
let filename = interfaces . GetSelected . Identifier + ".conf"
let text = interfaces . configuration
let element = document . createElement ( 'a' )
element . setAttribute ( 'href' , 'data:text/plain;charset=utf-8,' + encodeURIComponent ( text ) )
element . setAttribute ( 'download' , filename )
element . style . display = 'none'
document . body . appendChild ( element )
element . click ( )
document . body . removeChild ( element )
}
2023-07-20 23:17:32 +02:00
async function saveConfig ( ) {
try {
await interfaces . SaveConfiguration ( interfaces . GetSelected . Identifier )
notify ( {
title : "Interface configuration persisted to file" ,
text : "The interface configuration has been written to the wg-quick configuration file." ,
type : 'success' ,
} )
} catch ( e ) {
console . log ( e )
notify ( {
title : "Backend Connection Failure" ,
text : "Failed to persist interface configuration file!" ,
type : 'error' ,
} )
}
}
2023-02-12 23:13:04 +01:00
onMounted ( async ( ) => {
await interfaces . LoadInterfaces ( )
2023-07-18 16:05:06 +02:00
await peers . LoadPeers ( undefined ) // use default interface
await peers . LoadStats ( undefined ) // use default interface
2023-02-12 23:13:04 +01:00
} )
< / script >
< template >
< PeerViewModal :peerId = "viewedPeerId" :visible = "viewedPeerId!==''" @close ="viewedPeerId=''" > < / PeerViewModal >
< PeerEditModal :peerId = "editPeerId" :visible = "editPeerId!==''" @close ="editPeerId=''" > < / PeerEditModal >
2023-07-07 15:36:07 +02:00
< PeerMultiCreateModal :visible = "multiCreatePeerId!==''" @close ="multiCreatePeerId=''" > < / PeerMultiCreateModal >
2023-02-12 23:13:04 +01:00
< InterfaceEditModal :interfaceId = "editInterfaceId" :visible = "editInterfaceId!==''" @close ="editInterfaceId=''" > < / InterfaceEditModal >
2023-06-14 23:03:24 +02:00
< InterfaceViewModal :interfaceId = "viewedInterfaceId" :visible = "viewedInterfaceId!==''" @close ="viewedInterfaceId=''" > < / InterfaceViewModal >
2023-02-12 23:13:04 +01:00
<!-- Headline and interface selector -- >
< div class = "page-header row" >
< div class = "col-12 col-lg-8" >
< h1 > { { $t ( 'interfaces.h1' ) } } < / h1 >
< / div >
< div class = "col-12 col-lg-4 text-end" >
< div class = "form-group" >
< / div >
< div class = "form-group" >
< div class = "input-group mb-3" >
< button class = "input-group-text btn btn-primary" title = "Add new interface" @click.prevent ="editInterfaceId='#NEW#'" >
< i class = "fa-solid fa-plus-circle" > < / i >
< / button >
< select v-model = "interfaces.selected" :disabled="interfaces.Count===0" class="form-select" @change="peers.LoadPeers()" >
< option v-if = "interfaces.Count===0" value="nothing" > {{ $ t ( ' interfaces.notAvailable ' ) }} < / option >
2023-06-14 23:03:24 +02:00
< option v-for = "iface in interfaces.All" :key="iface.Identifier" :value="iface.Identifier" > {{ calculateInterfaceName ( iface.Identifier , iface.DisplayName ) }} < / option >
2023-02-12 23:13:04 +01:00
< / select >
< / div >
< / div >
< / div >
< / div >
<!-- No interfaces information -- >
< div v-if = "interfaces.Count===0" class="row" >
< div class = "col-lg-12" >
< div class = "mt-5" >
< h4 > { { $t ( 'interfaces.noInterface.h1' ) } } < / h4 >
< p > { { $t ( 'interfaces.noInterface.message' ) } } < / p >
< / div >
< / div >
< / div >
<!-- Interface overview -- >
< div v-if = "interfaces.Count!==0" class="row" >
< div class = "col-lg-12" >
< div class = "card border-secondary mb-4" style = "min-height: 15rem;" >
< div class = "card-header" >
< div class = "row" >
< div class = "col-12 col-lg-8" >
{ { $t ( 'interfaces.statusBox.h1' ) } } < strong > { { interfaces . GetSelected . Identifier } } < / strong > ( { { interfaces . GetSelected . Mode } } { { $t ( 'interfaces.statusBox.mode' ) } } )
2023-07-20 23:17:32 +02:00
< span v-if = "interfaces.GetSelected.Disabled" class="text-danger"><i class="fa fa-circle-xmark" :title="interfaces.GetSelected.DisabledReason" > < / i > < / span >
2023-02-12 23:13:04 +01:00
< / div >
< div class = "col-12 col-lg-4 text-lg-end" >
2023-06-14 23:03:24 +02:00
< a class = "btn-link" href = "#" title = "Show interface configuration" @click.prevent ="viewedInterfaceId=interfaces.GetSelected.Identifier" > < i class = "fas fa-eye" > < / i > < / a >
2023-07-04 22:55:11 +02:00
< a class = "ms-5 btn-link" href = "#" title = "Download interface configuration" @click.prevent ="download" > < i class = "fas fa-download" > < / i > < / a >
2023-07-20 23:17:32 +02:00
< a class = "ms-5 btn-link" href = "#" title = "Write interface configuration file" @click.prevent ="saveConfig" > < i class = "fas fa-save" > < / i > < / a >
2023-02-12 23:13:04 +01:00
< a class = "ms-5 btn-link" href = "#" title = "Edit interface settings" @click.prevent ="editInterfaceId=interfaces.GetSelected.Identifier" > < i class = "fas fa-cog" > < / i > < / a >
< / div >
< / div >
< / div >
< div class = "card-body d-flex flex-column" >
< div v-if = "interfaces.GetSelected.Mode==='server'" class="row" >
< div class = "col-sm-6" >
< table class = "table table-sm table-borderless device-status-table" >
< tbody >
< tr >
< td > { { $t ( 'interfaces.statusBox.key' ) } } : < / td >
< td > { { interfaces . GetSelected . PublicKey } } < / td >
< / tr >
< tr >
< td > { { $t ( 'interfaces.statusBox.endpoint' ) } } : < / td >
< td > { { interfaces . GetSelected . PeerDefEndpoint } } < / td >
< / tr >
< tr >
< td > { { $t ( 'interfaces.statusBox.port' ) } } : < / td >
< td > { { interfaces . GetSelected . ListenPort } } < / td >
< / tr >
< tr >
< td > { { $t ( 'interfaces.statusBox.peers' ) } } : < / td >
< td > { { interfaces . GetSelected . EnabledPeers } } < / td >
< / tr >
< tr >
< td > { { $t ( 'interfaces.statusBox.totalPeers' ) } } : < / td >
< td > { { interfaces . GetSelected . TotalPeers } } < / td >
< / tr >
< / tbody >
< / table >
< / div >
< div class = "col-sm-6" >
< table class = "table table-sm table-borderless device-status-table" >
< tbody >
< tr >
< td > { { $t ( 'interfaces.statusBox.ip' ) } } : < / td >
< td > < span class = "badge bg-light me-1" v-for ="addr in interfaces.GetSelected.Addresses" :key ="addr" > {{ addr }} < / span > < / td >
< / tr >
< tr >
< td > { { $t ( 'interfaces.statusBox.dnsServers' ) } } : < / td >
< td > < span class = "badge bg-light me-1" v-for ="addr in interfaces.GetSelected.Dns" :key ="addr" > {{ addr }} < / span > < / td >
< / tr >
< tr >
< td > { { $t ( 'interfaces.statusBox.mtu' ) } } : < / td >
< td > { { interfaces . GetSelected . Mtu } } < / td >
< / tr >
< tr >
2023-07-20 23:17:32 +02:00
< td > { { $t ( 'interfaces.statusBox.interval' ) } } : < / td >
2023-02-12 23:13:04 +01:00
< td > { { interfaces . GetSelected . PeerDefPersistentKeepalive } } < / td >
< / tr >
< tr >
< td > { { $t ( 'interfaces.statusBox.allowedIP' ) } } : < / td >
< td > < span class = "badge bg-light me-1" v-for ="addr in interfaces.GetSelected.PeerDefAllowedIPs" :key ="addr" > {{ addr }} < / span > < / td >
< / tr >
< / tbody >
< / table >
< / div >
< / div >
< div v-if = "interfaces.GetSelected.Mode==='client'" class="row" >
< div class = "col-sm-6" >
< table class = "table table-sm table-borderless device-status-table" >
< tbody >
< tr >
< td > { { $t ( 'interfaces.statusBox.key' ) } } : < / td >
< td > { { interfaces . GetSelected . PublicKey } } < / td >
< / tr >
< tr >
< td > { { $t ( 'interfaces.statusBox.endpoint' ) } } : < / td >
< td > { { interfaces . GetSelected . InterfacePeers } } < / td >
< / tr >
< tr >
< td > { { $t ( 'interfaces.statusBox.totalPeers' ) } } : < / td >
< td > { { interfaces . GetSelected . TotalPeers } } < / td >
< / tr >
< / tbody >
< / table >
< / div >
< div class = "col-sm-6" >
< table class = "table table-sm table-borderless device-status-table" >
< tbody >
< tr >
< td > { { $t ( 'interfaces.statusBox.ip' ) } } : < / td >
2023-06-14 23:03:24 +02:00
< td > < span class = "badge bg-light me-1" v-for ="addr in interfaces.GetSelected.Addresses" :key ="addr" > {{ addr }} < / span > < / td >
2023-02-12 23:13:04 +01:00
< / tr >
< tr >
< td > { { $t ( 'interfaces.statusBox.dnsServers' ) } } : < / td >
2023-06-14 23:03:24 +02:00
< td > < span class = "badge bg-light me-1" v-for ="addr in interfaces.GetSelected.Dns" :key ="addr" > {{ addr }} < / span > < / td >
2023-02-12 23:13:04 +01:00
< / tr >
< tr >
< td > { { $t ( 'interfaces.statusBox.mtu' ) } } : < / td >
< td > { { interfaces . GetSelected . Mtu } } < / td >
< / tr >
< / tbody >
< / table >
< / div >
< / div >
< div v-if = "interfaces.GetSelected.Mode==='any'" class="row" >
< div class = "col-sm-6" >
< table class = "table table-sm table-borderless device-status-table" >
< tbody >
< tr >
< td > { { $t ( 'interfaces.statusBox.key' ) } } : < / td >
< td > { { interfaces . GetSelected . PublicKey } } < / td >
< / tr >
< tr >
< td > { { $t ( 'interfaces.statusBox.endpoint' ) } } : < / td >
< td > { { interfaces . GetSelected . PeerDefEndpoint } } < / td >
< / tr >
< tr >
< td > { { $t ( 'interfaces.statusBox.port' ) } } : < / td >
< td > { { interfaces . GetSelected . ListenPort } } < / td >
< / tr >
< tr >
< td > { { $t ( 'interfaces.statusBox.peers' ) } } : < / td >
< td > { { interfaces . GetSelected . EnabledPeers } } < / td >
< / tr >
< tr >
< td > { { $t ( 'interfaces.statusBox.totalPeers' ) } } : < / td >
< td > { { interfaces . GetSelected . TotalPeers } } < / td >
< / tr >
< / tbody >
< / table >
< / div >
< div class = "col-sm-6" >
< table class = "table table-sm table-borderless device-status-table" >
< tbody >
< tr >
< td > { { $t ( 'interfaces.statusBox.ip' ) } } : < / td >
2023-06-14 23:03:24 +02:00
< td > < span class = "badge bg-light me-1" v-for ="addr in interfaces.GetSelected.Addresses" :key ="addr" > {{ addr }} < / span > < / td >
2023-02-12 23:13:04 +01:00
< / tr >
< tr >
< td > { { $t ( 'interfaces.statusBox.allowedIP' ) } } : < / td >
2023-06-14 23:03:24 +02:00
< td > < span class = "badge bg-light me-1" v-for ="addr in interfaces.GetSelected.PeerDefAllowedIPs" :key ="addr" > {{ addr }} < / span > < / td >
2023-02-12 23:13:04 +01:00
< / tr >
< tr >
< td > { { $t ( 'interfaces.statusBox.dnsServers' ) } } : < / td >
2023-06-14 23:03:24 +02:00
< td > < span class = "badge bg-light me-1" v-for ="addr in interfaces.GetSelected.PeerDefDns" :key ="addr" > {{ addr }} < / span > < / td >
2023-02-12 23:13:04 +01:00
< / tr >
< tr >
< td > { { $t ( 'interfaces.statusBox.mtu' ) } } : < / td >
< td > { { interfaces . GetSelected . Mtu } } < / td >
< / tr >
< tr >
2023-07-20 23:17:32 +02:00
< td > { { $t ( 'interfaces.statusBox.interval' ) } } : < / td >
2023-02-12 23:13:04 +01:00
< td > { { interfaces . GetSelected . PeerDefPersistentKeepalive } } < / td >
< / tr >
< / tbody >
< / table >
< / div >
< / div >
< / div >
< / div >
< / div >
< / div >
<!-- Peer list -- >
< div v-if = "interfaces.Count!==0" class="mt-4 row" >
< div class = "col-12 col-lg-5" >
< h2 v-if = "interfaces.GetSelected.Mode==='server'" class="mt-2" > {{ $ t ( ' interfaces.h2 ' ) }} < / h2 >
< h2 v -else class = "mt-2" > { { $t ( 'interfaces.h2-client' ) } } < / h2 >
< / div >
< div class = "col-12 col-lg-4 text-lg-end" >
< div class = "form-group d-inline" >
< div class = "input-group mb-3" >
< input v-model = "peers.filter" class="form-control" placeholder="Search..." type="text" @keyup="peers.afterPageSizeChange" >
< button class = "input-group-text btn btn-primary" title = "Search" > < i class = "fa-solid fa-search" > < / i > < / button >
< / div >
< / div >
< / div >
< div class = "col-12 col-lg-3 text-lg-end" >
2023-07-07 21:24:02 +02:00
<!-- a v - if = "interfaces.GetSelected.Mode==='server' && peers.Count!==0" class = "btn btn-primary" href = "#" title = "Send mail to all peers" > < i class = "fa fa-paper-plane" > < / i > < / a-- >
2023-07-07 15:36:07 +02:00
< a class = "btn btn-primary ms-2" href = "#" title = "Add multiple peers" @click.prevent ="multiCreatePeerId='#NEW#'" > < i class = "fa fa-plus me-1" > < / i > < i class = "fa fa-users" > < / i > < / a >
2023-02-12 23:13:04 +01:00
< a class = "btn btn-primary ms-2" href = "#" title = "Add a peer" @click.prevent ="editPeerId='#NEW#'" > < i class = "fa fa-plus me-1" > < / i > < i class = "fa fa-user" > < / i > < / a >
< / div >
< / div >
< div v-if = "interfaces.Count!==0" class="mt-2 table-responsive" >
< div v-if = "peers.Count===0" >
< h4 > { { $t ( 'interfaces.noPeerSelect.h4' ) } } < / h4 >
< p > { { $t ( 'interfaces.noPeerSelect.message' ) } } < / p >
< / div >
< table v-if = "peers.Count!==0" id="peerTable" class="table table-sm" >
< thead >
< tr >
< th scope = "col" >
< input id = "flexCheckDefault" class = "form-check-input" title = "Select all" type = "checkbox" value = "" >
< / th > <!-- select -- >
2023-07-20 23:17:32 +02:00
< th scope = "col" > < / th > <!-- status -- >
< th scope = "col" > { { $t ( 'interfaces.tableHeadings.name' ) } } < / th >
< th scope = "col" > { { $t ( 'interfaces.tableHeadings.user' ) } } < / th >
< th scope = "col" > { { $t ( 'interfaces.tableHeadings.ip' ) } } < / th >
< th v-if = "interfaces.GetSelected.Mode==='client'" scope="col" > {{ $ t ( ' interfaces.tableHeadings.endpoint ' ) }} < / th >
< th v-if = "peers.hasStatistics" scope="col" > {{ $ t ( ' interfaces.tableHeadings.stats ' ) }} < / th >
2023-02-12 23:13:04 +01:00
< th scope = "col" > < / th > <!-- Actions -- >
< / tr >
< / thead >
< tbody >
< tr v-for = "peer in peers.FilteredAndPaged" :key="peer.Identifier" >
< th scope = "row" >
< input id = "flexCheckDefault" class = "form-check-input" type = "checkbox" value = "" >
< / th >
2023-07-20 23:17:32 +02:00
< 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 && peer.ExpiresAt" class="text-warning"><i class="fas fa-hourglass-end expiring-peer" :title="peer.ExpiresAt" > < / i > < / 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 >
2023-02-12 23:13:04 +01:00
< td > { { peer . UserIdentifier } } < / td >
< td >
2023-06-24 01:35:25 +02:00
< span v-for = "ip in peer.Addresses" :key="ip" class="badge bg-light me-1" > {{ ip }} < / span >
2023-02-12 23:13:04 +01:00
< / td >
2023-06-23 19:24:59 +02:00
< td v-if = "interfaces.GetSelected.Mode==='client'" > {{ peer.Endpoint.Value }} < / td >
2023-07-18 16:05:06 +02:00
< td v-if = "peers.hasStatistics" >
< div v-if = "peers.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" > Connected < / span >
< / div >
< div v-else >
< span class = "badge rounded-pill bg-light" > < i class = "fa-solid fa-link-slash" > < / i > < / span >
< / div >
< / td >
2023-02-12 23:13:04 +01:00
< td class = "text-center" >
< a href = "#" title = "Show peer" @click.prevent ="viewedPeerId=peer.Identifier" > < i class = "fas fa-eye me-2" > < / i > < / a >
< a href = "#" title = "Edit peer" @click.prevent ="editPeerId=peer.Identifier" > < i class = "fas fa-cog" > < / i > < / a >
< / td >
< / tr >
< / tbody >
< / table >
< / div >
< hr v-if = "interfaces.Count!==0" >
< div v-if = "interfaces.Count!==0" class="mt-3" >
< div class = "row" >
< div class = "col-6" >
< ul class = "pagination pagination-sm" >
< li :class = "{disabled:peers.pageOffset===0}" class = "page-item" >
< a class = "page-link" @click ="peers.previousPage" > & laquo ; < / a >
< / li >
< li v-for = "page in peers.pages" :key="page" :class="{active:peers.currentPage===page}" class="page-item" >
< a class = "page-link" @click ="peers.gotoPage(page)" > {{ page }} < / a >
< / li >
< li :class = "{disabled:!peers.hasNextPage}" class = "page-item" >
< a class = "page-link" @click ="peers.nextPage" > & raquo ; < / a >
< / li >
< / ul >
< / div >
< div class = "col-6" >
< div class = "form-group row" >
2023-07-20 23:17:32 +02:00
< label class = "col-sm-6 col-form-label text-end" for = "paginationSelector" > { { $t ( 'general.pagination.size' ) } } : < / label >
2023-02-12 23:13:04 +01:00
< div class = "col-sm-6" >
< select v -model .number = " peers.pageSize " class = "form-select" @click ="peers.afterPageSizeChange()" >
< option value = "10" > 10 < / option >
< option value = "25" > 25 < / option >
< option value = "50" > 50 < / option >
< option value = "100" > 100 < / option >
2023-07-20 23:17:32 +02:00
< option value = "999999999" > { { $t ( 'general.pagination.all' ) } } < / option >
2023-02-12 23:13:04 +01:00
< / select >
< / div >
< / div >
< / div >
< / div >
< / div >
< / template >