Prepared dashboard frontend
parent
0802cfdcb0
commit
6c76a7ce5f
|
@ -1,5 +1,6 @@
|
|||
version: '3'
|
||||
services:
|
||||
|
||||
dashboard-backend:
|
||||
build:
|
||||
context: back
|
||||
|
@ -12,3 +13,13 @@ services:
|
|||
- '/etc/ssl/private:/etc/ssl/private'
|
||||
- '/etc/wgapi:/etc/wgapi:ro'
|
||||
- '/var/log/wgapi:/var/log/wgapi'
|
||||
|
||||
dashboard-frontend:
|
||||
build:
|
||||
context: front
|
||||
dockerfile: Dockerfile.dashboard
|
||||
container_name: wgapi-dashboard-frontend
|
||||
volumes:
|
||||
- '/var/log/wgapi:/var/log/wgapi'
|
||||
ports:
|
||||
- '8081:80'
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
FROM httpd:2.4
|
||||
COPY ./dashboard.html /usr/local/apache2/htdocs/index.html
|
||||
COPY dashboard.js /usr/local/apache2/htdocs/dashboard.js
|
|
@ -1,9 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Wireguard Dashboard</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script src="index.js">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,34 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Wireguard Dashboard</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Use this console to edit your network-connected devices. </p>
|
||||
|
||||
<h2>Your peers</h2>
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th>Host</th><th>IPv4</th><th>IPv6</th><th></th>
|
||||
</tr></thead>
|
||||
<tbody data-bind="foreach:peers"><tr>
|
||||
<td data-bind="text:name"></td>
|
||||
<td data-bind="text:ipv4"></td>
|
||||
<td data-bind="text:ipv6"></td>
|
||||
<td><button style="float:right" data-bind="click:$parent.delPeer,disable:$data.isDeleting,text:deleteText">Delete</button></td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
|
||||
<h2>Add a peer</h2>
|
||||
<p>To add a new peer, type in a hostname and click add. The hostname must be 3-10 lowercase letters and numbers <code>/[a-z0-9]{3,10}/</code>. Keep it short for your own sake!</p>
|
||||
<div>
|
||||
<input type="text" data-bind="textInput:newPeerName,event:{keypress:addKeyPress}" placeholder="mypc1"></input>
|
||||
<button data-bind="click:addPeer,disable:isAdding,text:addText">Add</button>
|
||||
</div>
|
||||
<p>After clicking "Add", the new peer's config will appear below. Copy and paste it into your wireguard client and start the service. <b>This configuration will not be shown again!</b>If you lose the config, you will need to delete the peer and recreate it. </p>
|
||||
<hr>
|
||||
<pre data-bind="text:newConfigText"></pre>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.1/knockout-latest.js" integrity="sha512-2AL/VEauKkZqQU9BHgnv48OhXcJPx9vdzxN1JrKDVc4FPU/MEE/BZ6d9l0mP7VmvLsjtYwqiYQpDskK9dG8KBA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="/dashboard.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,145 @@
|
|||
const API_URL = 'http://ksn.gf4:8080'
|
||||
|
||||
function Peer(data) {
|
||||
this.name = ko.observable(data.name)
|
||||
this.ipv4 = ko.observable(data.ipv4)
|
||||
this.ipv6 = ko.observable(`:${data.ipv6.split(':').slice(-2).join(':')}`)
|
||||
this.isDeleting = ko.observable(false)
|
||||
this.deleteText = ko.computed(() => this.isDeleting()?'Deleting...':'Delete')
|
||||
}
|
||||
|
||||
function PeerList() {
|
||||
let self = this
|
||||
self.peers = ko.observableArray([])
|
||||
self.newPeerName = ko.observable('')
|
||||
self.newConfigText = ko.observable('')
|
||||
self.isAdding = ko.observable(false)
|
||||
self.addText = ko.computed(() => self.isAdding()?'Adding...':'Add')
|
||||
|
||||
// Initial loading
|
||||
self.getUser = async () => {
|
||||
let res; try {
|
||||
res = await fetch(`${API_URL}/`)
|
||||
} catch (err) {
|
||||
console.error(`Failed to GET ${API_URL}/`)
|
||||
if (err) console.error(err)
|
||||
}
|
||||
if (!res.ok) {
|
||||
console.log(`Got ${res.status} from GET ${API_URL}/`)
|
||||
alert('Failed to contact API and load peers list. Check your wireguard connection. ')
|
||||
} else {
|
||||
let user; try {
|
||||
user = await res.json()
|
||||
} catch (err) {
|
||||
console.error('Failed to parse JSON!')
|
||||
if (err) console.error(err)
|
||||
}
|
||||
self.peers(
|
||||
user.peers.sort(
|
||||
(a,b) => a.ipv4.split('.')[3] - b.ipv4.split('.')[3])
|
||||
.map( (i)=>new Peer(i))
|
||||
)
|
||||
self.token = user.token
|
||||
}
|
||||
}
|
||||
|
||||
self.addPeer = async () => {
|
||||
self.isAdding(true)
|
||||
const validName = self.newPeerName().trim().toLowerCase()
|
||||
if (validName.length === 0) {
|
||||
alert('Please enter a hostname.')
|
||||
self.isAdding(false)
|
||||
} else if (!/^([\-\_a-z0-9]{3,12})$/.test(validName)) {
|
||||
alert('Name must be 3-12 alphanumeric chars.')
|
||||
self.isAdding(false)
|
||||
} else if (self.peers().map((peer)=>peer.name()).includes(validName)) {
|
||||
alert(`You already have a peer named ${validName}!`)
|
||||
self.isAdding(false)
|
||||
} else {
|
||||
const url = `${API_URL}/?token=${self.token}`
|
||||
let res; try {
|
||||
res = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: validName,
|
||||
}),
|
||||
})
|
||||
} catch (err) {
|
||||
alert('Failed to contact server. Are you online?')
|
||||
if (err) console.error(err)
|
||||
self.isAdding(false)
|
||||
}
|
||||
let parsedRes; try {
|
||||
parsedRes = await res.text()
|
||||
} catch (err) {
|
||||
if (err) console.error(err)
|
||||
} finally { self.isAdding(false) }
|
||||
if (!res.ok) {
|
||||
alert(parsedRes)
|
||||
} else {
|
||||
self.newPeerName('')
|
||||
self.newConfigText(parsedRes)
|
||||
let interfaceLines = parsedRes.split('\n\n').filter(
|
||||
(paragraph) => paragraph.includes('[Interface]')
|
||||
)[0].split('\n')
|
||||
let addresses = interfaceLines.filter(
|
||||
(line) => line.includes('Address = ')
|
||||
)[0].split('=')[1].trim().split(', ')
|
||||
self.peers.push(new Peer({
|
||||
name: interfaceLines[0]
|
||||
.split('#')[1].trim().split('.')[0],
|
||||
ipv4: addresses.filter(
|
||||
(addr) => addr.includes('10.4.')
|
||||
)[0].split('/')[0],
|
||||
ipv6: addresses.filter(
|
||||
(addr) => addr.includes('fd69:1337:0:420:f4:f4:')
|
||||
)[0].split('/')[0],
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for user hitting enter key
|
||||
self.addKeyPress = (d,e) => {
|
||||
if (e.keyCode === 13) self.addPeer()
|
||||
return true
|
||||
}
|
||||
|
||||
self.delPeer = async (peer) => {
|
||||
const name = peer.name()
|
||||
if (confirm(`Are you sure you want to delete ${name}?`)) {
|
||||
peer.isDeleting(true)
|
||||
const url = `${API_URL}/?token=${self.token}`
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: name,
|
||||
}),
|
||||
})
|
||||
if (res.ok) self.peers.remove(peer)
|
||||
else {
|
||||
if (res.status===404) self.peers.remove(peer)
|
||||
try {
|
||||
alert(await res.text())
|
||||
} catch (err) {
|
||||
console.error(`Failed to parse DELETE response into text`)
|
||||
if (err) console.error(err)
|
||||
} finally { peer.isDeleting(false) }
|
||||
}
|
||||
} catch (err) {
|
||||
alert(`Failed to contact the server. Are you online?`)
|
||||
} finally { peer.isDeleting(false) }
|
||||
}
|
||||
}
|
||||
|
||||
self.getUser()
|
||||
}
|
||||
|
||||
ko.applyBindings(new PeerList())
|
|
@ -1,9 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Wireguard Dashboard</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script src="index.js">
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue