const API_URL = 'https://wg-admin-backend.myhost.mytld' const TLD = 'mytld' function Peer(data) { this.domain = data.domain this.pubkey = data.pubkey this.ipv4 = data.ipv4 this.ipv6 = data.ipv6 this.cantDelete = data.cantDelete this.isDeleting = ko.observable(false) this.deleteText = ko.computed(() => this.isDeleting()?'Deleting...':'Delete') } function User(data) { this.name = data.name this.number = data.number this.isDeleting = ko.observable(false) } function PeerList() { let self = this self.peers = ko.observableArray([]) self.newPeerHostname = ko.observable('') self.newPeerUser = ko.observable('') self.newConfigText = ko.observable('') self.isAddingPeer = ko.observable(false) self.addPeerText = ko.computed(() => self.isAddingPeer()?'Adding...':'Add') self.users = ko.observableArray([]) self.newUserHostname = ko.observable('') self.newUsername = ko.observable('') self.isAddingUser = ko.observable(false) self.addUserText = ko.computed(() => self.isAddingUser()?'Adding...':'Add') self.userToDelete = ko.observable('') self.isDeletingUser = ko.observable(false) self.deleteUserText = ko.computed(() => self.isDeletingUser()?'Deleting...':'Delete') self.isLoaded = ko.observable(false) // Initial loading self.getUsers = async () => { let res; try { res = await fetch(`${API_URL}/peer/`) } catch (err) { if (err) console.error(err) alert('Failed to contact API and load peers list. Check your wireguard connection. ') } if (!res.ok) { if (res.status) console.log(res.status) 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]) .sort( (a,b) => a.ipv4.split('.')[2] - b.ipv4.split('.')[2]) .map( (i)=>new Peer(i)) ) self.users([...new Map( user.peers.filter( (u) => u.ipv4.split('.')[2]!=='0' ).map((u)=>new User({ name: u.domain.split('.')[1], number: u.ipv4.split('.')[2], })).map(i=>[i['number'],i]) ).values()]) self.token = user.token self.isLoaded(true) } } self.addPeer = async () => { self.isAddingPeer(true) const validHostname = self.newPeerHostname().trim().toLowerCase() if (validHostname.length === 0) { alert('Please enter a hostname.') self.isAddingPeer(false) } else if (!/^([\-\_a-z0-9]{3,12})$/.test(validHostname)) { alert('Name must be 3-12 alphanumeric chars.') self.isAddingPeer(false) } else if (self.peers().map((peer)=>peer.domain.slice(0,peer.domain.lastIndexOf('.'))).includes(`${validHostname}.${self.newPeerUser().name}`)) { alert(`There is already a peer named ${validHostname}.${self.newPeerUser().name}!`) self.isAddingPeer(false) } else { const url = `${API_URL}/peer?t=${self.token}&host=${validHostname}&user=${self.newPeerUser().name}&num=${self.newPeerUser().number}` let res; try { res = await fetch(url, {method: 'POST'}) } catch (err) { alert('Failed to contact server. Are you online?') if (err) console.error(err) self.isAddingPeer(false) } let parsedRes; try { parsedRes = await res.text() } catch (err) { if (err) console.error(err) } finally { self.isAddingPeer(false) } if (!res.ok) { alert(res.status) } else { self.newPeerHostname('') self.peers.push(new Peer({ domain:`${validHostname}.${self.newPeerUser().name}.${TLD}`, cantDelete: true, })) self.newConfigText(parsedRes) new QRCode(document.getElementById('qrcode'),parsedRes) } } } // Listen for user hitting enter key self.addPeerKeyPress = (d,e) => { if (e.keyCode === 13) self.addPeer() return true } self.delPeer = async (peer) => { if (confirm(`Are you sure you want to delete ${peer.domain}?`)) { peer.isDeleting(true) const url = `${API_URL}/peer?t=${self.token}&pubkey=${peer.pubkey}` try { const res = await fetch(url, {method: 'DELETE'}) 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.addUser = async () => { self.isAddingUser(true) const validHostname = self.newUserHostname().trim().toLowerCase() const validUsername = self.newUsername().trim().toLowerCase() if (validHostname.length === 0) { alert('Please enter a hostname.') self.isAddingUser(false) } else if (validUsername.length === 0) { alert('Please enter a username.') self.isAddingUser(false) } else if (!/^([\-\_a-z0-9]{3,12})$/.test(validHostname)) { alert('Hostname must be 3-12 alphanumeric chars.') self.isAddingUser(false) } else if (!/^([\-\_a-z0-9]{3,12})$/.test(validUsername)) { alert('Username must be 3-12 alphanumeric chars.') self.isAddingUser(false) } else if (self.users().map((user)=>user.name).includes(validUsername)) { alert(`There is already a user named ${validUsername}!`) self.isAddingUser(false) } else { const url = `${API_URL}/user?t=${self.token}&host=${validHostname}&user=${validUsername}` console.log(url) let res; try { res = await fetch(url, {method: 'POST'}) } catch (err) { alert('Failed to contact server. Are you online?') if (err) console.error(err) self.isAddingUser(false) } let parsedRes; try { parsedRes = await res.text() } catch (err) { if (err) console.error(err) } finally { self.isAddingUser(false) } if (!res.ok) { alert(res.status) } else { self.newUserHostname('') self.newUsername('') self.peers.push(new Peer({ domain:`${validHostname}.${validUsername}.${TLD}`, cantDelete: true, })) self.newConfigText(parsedRes) new QRCode(document.getElementById('qrcode'),parsedRes) } } } // Listen for user hitting enter key self.addUserKeyPress = (d,e) => { if (e.keyCode === 13) self.addUser() return true } self.delUser = async () => { user = self.userToDelete() if (confirm(`Are you sure you want to delete ${user.name}?`)) { self.isDeletingUser(true) const url = `${API_URL}/user?t=${self.token}&user=${user.name}&un=${user.number}` try { const res = await fetch(url, {method: 'DELETE'}) if (res.ok) { self.users.remove(user) self.peers().forEach( (peer)=> { if ( peer.domain.split('.')[1] === user.name) self.peers.remove(peer) } ) try { alert(await res.text()) } catch (err) { alert('success') if (err) console.error(err) } finally { self.isDeletingUser(false) } } } catch (err) { if (err) console.error(err) alert(`Failed to contact the server. Are you online?`) } finally { self.isDeletingUser(false) } } } self.getUsers() } ko.applyBindings(new PeerList())