mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2024-11-22 15:20:09 +01:00
Finished restrict & allow access of peers
This commit is contained in:
parent
c7ca20b45a
commit
9a280e99ad
@ -238,6 +238,12 @@ class WireguardConfiguration:
|
||||
self.Status = self.Name in psutil.net_if_addrs().keys()
|
||||
return self.Status
|
||||
|
||||
def __getRestrictedPeers(self):
|
||||
self.RestrictedPeers = []
|
||||
restricted = cursor.execute("SELECT * FROM %s_restrict_access" % self.Name).fetchall()
|
||||
for i in restricted:
|
||||
self.RestrictedPeers.append(Peer(i, self))
|
||||
|
||||
def __getPeers(self):
|
||||
self.Peers = []
|
||||
with open(os.path.join(WG_CONF_PATH, f'{self.Name}.conf'), 'r') as configFile:
|
||||
@ -309,6 +315,55 @@ class WireguardConfiguration:
|
||||
return True, i
|
||||
return False, None
|
||||
|
||||
def allowAccessPeers(self, listOfPublicKeys):
|
||||
# numOfAllowedPeers = 0
|
||||
# numOfFailedToAllowPeers = 0
|
||||
for i in listOfPublicKeys:
|
||||
p = cursor.execute("SELECT * FROM %s_restrict_access WHERE id = ?" % self.Name, (i,)).fetchone()
|
||||
if p is not None:
|
||||
cursor.execute("INSERT INTO %s SELECT * FROM %s_restrict_access WHERE id = ?"
|
||||
% (self.Name, self.Name,), (p['id'],))
|
||||
cursor.execute("DELETE FROM %s_restrict_access WHERE id = ?"
|
||||
% self.Name, (p['id'],))
|
||||
subprocess.check_output(f"wg set {self.Name} peer {p['id']} allowed-ips {p['allowed_ip']}",
|
||||
shell=True, stderr=subprocess.STDOUT)
|
||||
else:
|
||||
return ResponseObject(False, "Failed to allow access of peer " + i)
|
||||
if not self.__wgSave():
|
||||
return ResponseObject(False, "Failed to save configuration through WireGuard")
|
||||
|
||||
self.__getPeers()
|
||||
return ResponseObject(True, "Allow access successfully!")
|
||||
|
||||
def restrictPeers(self, listOfPublicKeys):
|
||||
numOfRestrictedPeers = 0
|
||||
numOfFailedToRestrictPeers = 0
|
||||
for p in listOfPublicKeys:
|
||||
found, pf = self.searchPeer(p)
|
||||
if found:
|
||||
try:
|
||||
subprocess.check_output(f"wg set {self.Name} peer {pf.id} remove",
|
||||
shell=True, stderr=subprocess.STDOUT)
|
||||
cursor.execute("INSERT INTO %s_restrict_access SELECT * FROM %s WHERE id = ?" %
|
||||
(self.Name, self.Name,), (pf.id,))
|
||||
cursor.execute("UPDATE %s_restrict_access SET status = 'stopped' WHERE id = ?" %
|
||||
(self.Name,), (pf.id,))
|
||||
cursor.execute("DELETE FROM %s WHERE id = ?" % self.Name, (pf.id,))
|
||||
numOfRestrictedPeers += 1
|
||||
except Exception as e:
|
||||
numOfFailedToRestrictPeers += 1
|
||||
|
||||
if not self.__wgSave():
|
||||
return ResponseObject(False, "Failed to save configuration through WireGuard")
|
||||
|
||||
self.__getPeers()
|
||||
|
||||
if numOfRestrictedPeers == len(listOfPublicKeys):
|
||||
return ResponseObject(True, f"Restricted {numOfRestrictedPeers} peer(s)")
|
||||
return ResponseObject(False,
|
||||
f"Restricted {numOfRestrictedPeers} peer(s) successfully. Failed to restrict {numOfFailedToRestrictPeers} peer(s)")
|
||||
pass
|
||||
|
||||
def deletePeers(self, listOfPublicKeys):
|
||||
numOfDeletedPeers = 0
|
||||
numOfFailedToDeletePeers = 0
|
||||
@ -468,6 +523,10 @@ class WireguardConfiguration:
|
||||
self.__getPeers()
|
||||
return self.Peers
|
||||
|
||||
def getRestrictedPeersList(self) -> list:
|
||||
self.__getRestrictedPeers()
|
||||
return self.RestrictedPeers
|
||||
|
||||
def toJson(self):
|
||||
self.Status = self.getStatus()
|
||||
return {
|
||||
@ -1047,6 +1106,30 @@ def API_deletePeers(configName: str) -> ResponseObject:
|
||||
return ResponseObject(False, "Configuration does not exist")
|
||||
|
||||
|
||||
@app.route('/api/restrictPeers/<configName>', methods=['POST'])
|
||||
def API_restrictPeers(configName: str) -> ResponseObject:
|
||||
data = request.get_json()
|
||||
peers = data['peers']
|
||||
if configName in WireguardConfigurations.keys():
|
||||
if len(peers) == 0:
|
||||
return ResponseObject(False, "Please specify more than one peer")
|
||||
configuration = WireguardConfigurations.get(configName)
|
||||
return configuration.restrictPeers(peers)
|
||||
return ResponseObject(False, "Configuration does not exist")
|
||||
|
||||
|
||||
@app.route('/api/allowAccessPeers/<configName>', methods=['POST'])
|
||||
def API_allowAccessPeers(configName: str) -> ResponseObject:
|
||||
data = request.get_json()
|
||||
peers = data['peers']
|
||||
if configName in WireguardConfigurations.keys():
|
||||
if len(peers) == 0:
|
||||
return ResponseObject(False, "Please specify more than one peer")
|
||||
configuration = WireguardConfigurations.get(configName)
|
||||
return configuration.allowAccessPeers(peers)
|
||||
return ResponseObject(False, "Configuration does not exist")
|
||||
|
||||
|
||||
@app.route('/api/addPeers/<configName>', methods=['POST'])
|
||||
def API_addPeers(configName):
|
||||
data = request.get_json()
|
||||
@ -1184,7 +1267,8 @@ def API_getConfigurationInfo():
|
||||
return ResponseObject(False, "Please provide configuration name")
|
||||
return ResponseObject(data={
|
||||
"configurationInfo": WireguardConfigurations[configurationName],
|
||||
"configurationPeers": WireguardConfigurations[configurationName].getPeersList()
|
||||
"configurationPeers": WireguardConfigurations[configurationName].getPeersList(),
|
||||
"configurationRestrictedPeers": WireguardConfigurations[configurationName].getRestrictedPeersList()
|
||||
})
|
||||
|
||||
|
||||
|
@ -34,22 +34,31 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card shadow-sm rounded-3">
|
||||
<div class="card-header bg-transparent d-flex align-items-center gap-2 border-0">
|
||||
<div class="dot ms-0" :class="{active: Peer.status === 'running'}"></div>
|
||||
<div style="font-size: 0.8rem" class="ms-auto d-flex gap-2">
|
||||
<span class="text-primary">
|
||||
<i class="bi bi-arrow-down"></i><strong>
|
||||
{{(Peer.cumu_receive + Peer.total_receive).toFixed(4)}}</strong> GB
|
||||
</span>
|
||||
<span class="text-success">
|
||||
<i class="bi bi-arrow-up"></i><strong>
|
||||
{{(Peer.cumu_sent + Peer.total_sent).toFixed(4)}}</strong> GB
|
||||
</span>
|
||||
<span class="text-secondary" v-if="Peer.latest_handshake !== 'No Handshake'">
|
||||
<i class="bi bi-arrows-angle-contract"></i>
|
||||
{{getLatestHandshake}} ago
|
||||
</span>
|
||||
<div class="card shadow-sm rounded-3"
|
||||
:class="{'border-warning': Peer.restricted}"
|
||||
>
|
||||
<div>
|
||||
<div v-if="!Peer.restricted" class="card-header bg-transparent d-flex align-items-center gap-2 border-0">
|
||||
<div class="dot ms-0" :class="{active: Peer.status === 'running'}"></div>
|
||||
<div style="font-size: 0.8rem" class="ms-auto d-flex gap-2">
|
||||
<span class="text-primary">
|
||||
<i class="bi bi-arrow-down"></i><strong>
|
||||
{{(Peer.cumu_receive + Peer.total_receive).toFixed(4)}}</strong> GB
|
||||
</span>
|
||||
<span class="text-success">
|
||||
<i class="bi bi-arrow-up"></i><strong>
|
||||
{{(Peer.cumu_sent + Peer.total_sent).toFixed(4)}}</strong> GB
|
||||
</span>
|
||||
<span class="text-secondary" v-if="Peer.latest_handshake !== 'No Handshake'">
|
||||
<i class="bi bi-arrows-angle-contract"></i>
|
||||
{{getLatestHandshake}} ago
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="border-0 card-header bg-transparent text-warning fw-bold"
|
||||
style="font-size: 0.8rem">
|
||||
<i class="bi-lock-fill me-2"></i>
|
||||
Access Restricted
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body pt-1" style="font-size: 0.9rem">
|
||||
|
@ -118,15 +118,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// console.log('mounted')
|
||||
// this.loading = true;
|
||||
// let id = this.$route.params.id;
|
||||
// this.configurationInfo = [];
|
||||
// this.configurationPeers = [];
|
||||
// if (id){
|
||||
// this.getPeers(id)
|
||||
// this.setInterval();
|
||||
// }
|
||||
|
||||
},
|
||||
watch: {
|
||||
'$route': {
|
||||
@ -176,6 +168,13 @@ export default {
|
||||
}, (res) => {
|
||||
this.configurationInfo = res.data.configurationInfo;
|
||||
this.configurationPeers = res.data.configurationPeers;
|
||||
this.configurationPeers.forEach(x => {
|
||||
x.restricted = false;
|
||||
})
|
||||
res.data.configurationRestrictedPeers.forEach(x => {
|
||||
x.restricted = true;
|
||||
this.configurationPeers.push(x)
|
||||
})
|
||||
this.loading = false;
|
||||
if (this.configurationPeers.length > 0){
|
||||
const sent = this.configurationPeers.map(x => x.total_sent + x.cumu_sent).reduce((x,y) => x + y).toFixed(4);
|
||||
@ -336,8 +335,23 @@ export default {
|
||||
keys: ["name", "id", "allowed_ip"]
|
||||
});
|
||||
|
||||
const result = this.wireguardConfigurationStore.searchString ? fuse.search(this.wireguardConfigurationStore.searchString).map(x => x.item) : this.configurationPeers;
|
||||
|
||||
const result = this.wireguardConfigurationStore.searchString ?
|
||||
fuse.search(this.wireguardConfigurationStore.searchString).map(x => x.item) : this.configurationPeers;
|
||||
|
||||
if (this.dashboardConfigurationStore.Configuration.Server.dashboard_sort === "restricted"){
|
||||
return result.slice().sort((a, b) => {
|
||||
if ( a[this.dashboardConfigurationStore.Configuration.Server.dashboard_sort]
|
||||
< b[this.dashboardConfigurationStore.Configuration.Server.dashboard_sort] ){
|
||||
return 1;
|
||||
}
|
||||
if ( a[this.dashboardConfigurationStore.Configuration.Server.dashboard_sort]
|
||||
> b[this.dashboardConfigurationStore.Configuration.Server.dashboard_sort]){
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
return result.slice().sort((a, b) => {
|
||||
if ( a[this.dashboardConfigurationStore.Configuration.Server.dashboard_sort]
|
||||
< b[this.dashboardConfigurationStore.Configuration.Server.dashboard_sort] ){
|
||||
@ -460,7 +474,7 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row gx-2 gy-2 mb-5">
|
||||
<div class="row gx-2 gy-2 mb-3">
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="card rounded-3 bg-transparent shadow-sm" style="height: 270px">
|
||||
<div class="card-header bg-transparent border-0"><small class="text-muted">Peers Total Data Usage</small></div>
|
||||
@ -498,14 +512,9 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<div class="d-flex align-items-center gap-3 mb-2 ">
|
||||
<h3>Peers</h3>
|
||||
|
||||
<RouterLink
|
||||
to="create"
|
||||
class="text-decoration-none ms-auto btn btn-primary rounded-3">
|
||||
<i class="bi bi-plus-circle-fill me-2"></i>Peers</RouterLink>
|
||||
</div>
|
||||
<!-- <div class="d-flex align-items-center gap-3 mb-2">-->
|
||||
<!-- <h3>Peers</h3>-->
|
||||
<!-- </div>-->
|
||||
<PeerSearch></PeerSearch>
|
||||
<TransitionGroup name="list" tag="div" class="row gx-2 gy-2 z-0">
|
||||
<div class="col-12 col-lg-6 col-xl-4"
|
||||
|
@ -18,7 +18,8 @@ export default {
|
||||
sort: {
|
||||
status: "Status",
|
||||
name: "Name",
|
||||
allowed_ip: "Allowed IP"
|
||||
allowed_ip: "Allowed IP",
|
||||
restricted: "Restricted"
|
||||
},
|
||||
interval: {
|
||||
'5000': '5 Seconds',
|
||||
@ -59,47 +60,59 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="d-flex gap-2 mb-3 z-3">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-secondary btn-sm dropdown-toggle rounded-3" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-filter-circle me-2"></i>
|
||||
Sort
|
||||
</button>
|
||||
<ul class="dropdown-menu mt-2 shadow rounded-3">
|
||||
<li v-for="(value, key) in this.sort">
|
||||
<a class="dropdown-item d-flex" role="button" @click="this.updateSort(key)">
|
||||
<span class="me-auto">{{value}}</span>
|
||||
<i class="bi bi-check text-primary"
|
||||
v-if="store.Configuration.Server.dashboard_sort === key"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-secondary btn-sm dropdown-toggle rounded-3" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-arrow-repeat me-2"></i>Refresh Interval
|
||||
</button>
|
||||
<ul class="dropdown-menu shadow mt-2 rounded-3">
|
||||
<li v-for="(value, key) in this.interval">
|
||||
<a class="dropdown-item d-flex" role="button" @click="updateRefreshInterval(key)">
|
||||
<span class="me-auto">{{value}}</span>
|
||||
<i class="bi bi-check text-primary"
|
||||
v-if="store.Configuration.Server.dashboard_refresh_interval === key"></i>
|
||||
</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- <button class="btn btn-outline-secondary btn-sm rounded-3" type="button"-->
|
||||
<!-- @click="this.store.Peers.Selecting = !this.store.Peers.Selecting"-->
|
||||
<!-- >-->
|
||||
<!-- <i class="bi bi-app-indicator me-2"></i>-->
|
||||
<!-- Select-->
|
||||
<!-- </button>-->
|
||||
|
||||
<div class="ms-auto d-flex align-items-center">
|
||||
<label class="d-flex me-2 text-muted" for="searchPeers"><i class="bi bi-search me-1"></i></label>
|
||||
<input class="form-control form-control-sm rounded-3"
|
||||
id="searchPeers"
|
||||
v-model="this.wireguardConfigurationStore.searchString">
|
||||
<div>
|
||||
<div class="d-flex gap-2 mb-3 z-3">
|
||||
<h4 class="mb-0">Peers</h4>
|
||||
<RouterLink
|
||||
to="create"
|
||||
class=" ms-auto text-decoration-none btn btn-primary rounded-3 btn-sm">
|
||||
<i class="bi bi-plus-lg me-2"></i>Peers
|
||||
</RouterLink>
|
||||
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-secondary btn-sm dropdown-toggle rounded-3" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-filter-circle me-2"></i>
|
||||
Sort
|
||||
</button>
|
||||
<ul class="dropdown-menu mt-2 shadow rounded-3">
|
||||
<li v-for="(value, key) in this.sort">
|
||||
<a class="dropdown-item d-flex" role="button" @click="this.updateSort(key)">
|
||||
<span class="me-auto">{{value}}</span>
|
||||
<i class="bi bi-check text-primary"
|
||||
v-if="store.Configuration.Server.dashboard_sort === key"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-secondary btn-sm dropdown-toggle rounded-3" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-arrow-repeat me-2"></i>Refresh Interval
|
||||
</button>
|
||||
<ul class="dropdown-menu shadow mt-2 rounded-3">
|
||||
<li v-for="(value, key) in this.interval">
|
||||
<a class="dropdown-item d-flex" role="button" @click="updateRefreshInterval(key)">
|
||||
<span class="me-auto">{{value}}</span>
|
||||
<i class="bi bi-check text-primary"
|
||||
v-if="store.Configuration.Server.dashboard_refresh_interval === key"></i>
|
||||
</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- <button class="btn btn-outline-secondary btn-sm rounded-3" type="button"-->
|
||||
<!-- @click="this.store.Peers.Selecting = !this.store.Peers.Selecting"-->
|
||||
<!-- >-->
|
||||
<!-- <i class="bi bi-app-indicator me-2"></i>-->
|
||||
<!-- Select-->
|
||||
<!-- </button>-->
|
||||
|
||||
<div class="d-flex align-items-center">
|
||||
<!-- <label class="d-flex me-2 text-muted" for="searchPeers"><i class="bi bi-search me-1"></i></label>-->
|
||||
<input class="form-control form-control-sm rounded-3"
|
||||
placeholder="Search..."
|
||||
id="searchPeers"
|
||||
v-model="this.wireguardConfigurationStore.searchString">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -13,7 +13,9 @@ export default {
|
||||
},
|
||||
data(){
|
||||
return{
|
||||
deleteBtnDisabled: false
|
||||
deleteBtnDisabled: false,
|
||||
restrictBtnDisabled: false,
|
||||
allowAccessBtnDisabled: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -55,6 +57,26 @@ export default {
|
||||
this.$emit("refresh")
|
||||
this.deleteBtnDisabled = false
|
||||
})
|
||||
},
|
||||
restrictPeer(){
|
||||
this.restrictBtnDisabled = true
|
||||
fetchPost(`/api/restrictPeers/${this.$route.params.id}`, {
|
||||
peers: [this.Peer.id]
|
||||
}, (res) => {
|
||||
this.dashboardStore.newMessage("Server", res.message, res.status ? "success":"danger")
|
||||
this.$emit("refresh")
|
||||
this.restrictBtnDisabled = false
|
||||
})
|
||||
},
|
||||
allowAccessPeer(){
|
||||
this.allowAccessBtnDisabled = true
|
||||
fetchPost(`/api/allowAccessPeers/${this.$route.params.id}`, {
|
||||
peers: [this.Peer.id]
|
||||
}, (res) => {
|
||||
this.dashboardStore.newMessage("Server", res.message, res.status ? "success":"danger")
|
||||
this.$emit("refresh")
|
||||
this.allowAccessBtnDisabled = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -62,61 +84,75 @@ export default {
|
||||
|
||||
<template>
|
||||
<ul class="dropdown-menu mt-2 shadow-lg d-block rounded-3" style="max-width: 200px">
|
||||
<template v-if="!this.Peer.private_key">
|
||||
<li>
|
||||
<small class="w-100 dropdown-item text-muted"
|
||||
style="white-space: break-spaces; font-size: 0.7rem"
|
||||
>Download & QR Code is not available due to no <code>private key</code>
|
||||
set for this peer
|
||||
</small>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
</template>
|
||||
|
||||
<li>
|
||||
<a class="dropdown-item d-flex" role="button"
|
||||
@click="this.$emit('setting')"
|
||||
>
|
||||
<i class="me-auto bi bi-pen"></i> Edit
|
||||
</a>
|
||||
</li>
|
||||
<template v-if="this.Peer.private_key">
|
||||
<li>
|
||||
<a class="dropdown-item d-flex" role="button" @click="this.downloadPeer()">
|
||||
<i class="me-auto bi bi-download"></i> Download
|
||||
</a>
|
||||
</li>
|
||||
<template v-if="!this.Peer.restricted">
|
||||
<template v-if="!this.Peer.private_key">
|
||||
<li>
|
||||
<small class="w-100 dropdown-item text-muted"
|
||||
style="white-space: break-spaces; font-size: 0.7rem"
|
||||
>Download & QR Code is not available due to no <code>private key</code>
|
||||
set for this peer
|
||||
</small>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
</template>
|
||||
<li>
|
||||
<a class="dropdown-item d-flex" role="button"
|
||||
@click="this.downloadQRCode()"
|
||||
@click="this.$emit('setting')"
|
||||
>
|
||||
<i class="me-auto bi bi-qr-code"></i> QR Code
|
||||
<i class="me-auto bi bi-pen"></i> Edit
|
||||
</a>
|
||||
</li>
|
||||
<template v-if="this.Peer.private_key">
|
||||
<li>
|
||||
<a class="dropdown-item d-flex" role="button" @click="this.downloadPeer()">
|
||||
<i class="me-auto bi bi-download"></i> Download
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item d-flex" role="button"
|
||||
@click="this.downloadQRCode()"
|
||||
>
|
||||
<i class="me-auto bi bi-qr-code"></i> QR Code
|
||||
</a>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<a class="dropdown-item d-flex text-warning"
|
||||
@click="this.restrictPeer()"
|
||||
:class="{disabled: this.restrictBtnDisabled}"
|
||||
role="button">
|
||||
<i class="me-auto bi bi-lock"></i> {{!this.restrictBtnDisabled ? "Restrict Access":"Restricting..."}}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item d-flex fw-bold text-danger"
|
||||
@click="this.deletePeer()"
|
||||
:class="{disabled: this.deleteBtnDisabled}"
|
||||
role="button">
|
||||
<i class="me-auto bi bi-trash"></i> {{!this.deleteBtnDisabled ? "Delete":"Deleting..."}}
|
||||
</a>
|
||||
</li>
|
||||
</template>
|
||||
<template v-else>
|
||||
<li>
|
||||
<a class="dropdown-item d-flex text-warning"
|
||||
@click="this.allowAccessPeer()"
|
||||
:class="{disabled: this.restrictBtnDisabled}"
|
||||
role="button">
|
||||
<i class="me-auto bi bi-unlock"></i>
|
||||
{{!this.allowAccessBtnDisabled ? "Allow Access":"Allowing..."}}
|
||||
</a>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<a class="dropdown-item d-flex text-warning"
|
||||
|
||||
role="button">
|
||||
<i class="me-auto bi bi-lock"></i> Lock
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item d-flex fw-bold text-danger"
|
||||
@click="this.deletePeer()"
|
||||
:class="{disabled: this.deleteBtnDisabled}"
|
||||
role="button">
|
||||
<i class="me-auto bi bi-trash"></i> {{!this.deleteBtnDisabled ? "Delete":"Deleting..."}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.dropdown-menu{
|
||||
right: 1rem;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.dropdown-item.disabled, .dropdown-item:disabled{
|
||||
|
@ -0,0 +1,23 @@
|
||||
<script>
|
||||
export default {
|
||||
name: "restrictedPeers"
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="mb-4">
|
||||
<RouterLink to="peers" is="div" class="d-flex align-items-center gap-4 text-decoration-none">
|
||||
<h3 class="mb-0 text-body">
|
||||
<i class="bi bi-chevron-left"></i>
|
||||
</h3>
|
||||
<h3 class="text-body mb-0">Restricted Peers</h3>
|
||||
</RouterLink>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -14,6 +14,7 @@ import Configuration from "@/views/configuration.vue";
|
||||
import PeerSettings from "@/components/configurationComponents/peerSettings.vue";
|
||||
import PeerList from "@/components/configurationComponents/peerList.vue";
|
||||
import PeerCreate from "@/components/configurationComponents/peerCreate.vue";
|
||||
import RestrictedPeers from "@/components/configurationComponents/restrictedPeers.vue";
|
||||
|
||||
const checkAuth = async () => {
|
||||
let result = false
|
||||
|
Loading…
Reference in New Issue
Block a user