globular-mvc
Version:
Generic template to create web-application that made use of globular as backend and materialize as css (wrap in web-component's)
749 lines (618 loc) • 28.7 kB
JavaScript
import '@polymer/iron-icons/iron-icons.js';
import "@polymer/iron-icons/hardware-icons";
import * as GlobularWebClient from "globular-web-client";
import { Model } from '../Model';
import { AcceptPeerRqst, AddPeerActionsRqst, DeletePeerRqst, GetPeerApprovalStateRqst, GetPeersRqst, Peer, RegisterPeerRqst, RemovePeerActionRqst } from 'globular-web-client/resource/resource_pb';
import { getAllPeersInfo } from 'globular-web-client/api';
import { ApplicationView } from '../ApplicationView';
import { SearchableList } from './List.js'
import { GetAllActionsRequest } from 'globular-web-client/services_manager/services_manager_pb';
export function getPeerById(id, callback, errorCallback){
let p_ = null
getAllPeers(Model.globular, peers=>{
peers.forEach(p=>{
if(p.getMac() == id){
p_ = p
}
})
if(p_ != null){
callback(p_)
return
}
errorCallback("no peer found with id " + id)
}, errorCallback)
}
export function getAllPeers(globule, callback, errorCallback) {
let rqst = new GetPeersRqst
rqst.setQuery("{}")
let peers = [];
let stream = globule.resourceService.getPeers(rqst, { domain: Model.domain,address: Model.address, application: Model.application, token: localStorage.getItem("user_token") });
// Get the stream and set event on it...
stream.on("data", (rsp) => {
peers = peers.concat(rsp.getPeersList());
});
stream.on("status", (status) => {
if (status.code == 0) {
callback(peers);
} else {
errorCallback({ message: status.details });
}
});
}
export class PeersManager extends HTMLElement {
// attributes.
// Create the applicaiton view.
constructor() {
super()
// Set the shadow dom.
this.attachShadow({ mode: 'open' });
// Innitialisation of the layout.
this.shadowRoot.innerHTML = `
<style>
paper-card{
background-color: var(--palette-background-paper);
color: var(--palette-text-primary);
}
#create-peer-btn{
top: -42px;
right: 0px;
position: absolute;
}
#container{
display: flex;
flex-direction: column;
position: relative;
}
.card-content {
min-width: 728px;
padding: 0px;
font-size: 1rem;
}
(max-width: 800px) {
.card-content{
min-width: 580px;
}
}
(max-width: 600px) {
.card-content{
min-width: 380px;
}
}
#create-peer-card{
width: 400px;
right: 0px;
position: absolute;
background-color: var(--palette-background-paper);
}
</style>
<div id="container">
<paper-card>
<div class="card-content">
</div>
</paper-card>
<paper-icon-button icon="add" id="create-peer-btn"></paper-icon-button>
</div>
`
// give the focus to the input.
let content = this.shadowRoot.querySelector(".card-content")
// give the focus to the input.
let container = this.shadowRoot.querySelector("#container")
let displayPeers = () => {
content.innerHTML = ""
// Here I will get the list of all peers.
getAllPeersInfo(Model.globular,
(peers) => {
// sort by status
peers.sort((a, b) => {
a.getState() - b.getState()
})
peers.forEach(p => {
let panel = new PeerPanel(p)
content.appendChild(panel)
})
}, err => {
ApplicationView.displayMessage(err, 3000)
})
}
// Here I will display the create peer card.
this.shadowRoot.querySelector("#create-peer-btn").onclick = () => {
let panel = this.shadowRoot.querySelector("#create-peer-card")
if (panel != null) {
setTimeout(() => {
panel.querySelector("paper-input").focus()
}, 100)
return
}
let html = `
<paper-card id="create-peer-card">
<div style="display: flex; align-items: center;">
<div style="flex-grow: 1; padding: 5px;">
Send a connection request to peer at domain
</div>
<paper-icon-button id="cancel-btn" icon="close"></paper-icon-button>
</div>
<div style="display: flex; flex-direction: column; padding: 10px;">
<paper-input id="peer-address-input" title="connect with peer at address" label="address"></paper-input>
<paper-button id="create-peer-connection-btn" style="align-self: end;">Create</paper-button>
</div>
</paper-card>
`
// The query selector.
container.appendChild(document.createRange().createContextualFragment(html))
panel = this.shadowRoot.querySelector("#create-peer-card")
// Now the actions.
let closeBtn = panel.querySelector("#cancel-btn")
closeBtn.onclick = () => {
panel.parentNode.removeChild(panel)
}
let createPeerConnectionBtn = panel.querySelector("#create-peer-connection-btn")
createPeerConnectionBtn.onclick = () => {
// the hostname.domain:port
let address = panel.querySelector("#peer-address-input").value
// Here The peers must be both https or the peer at url must be https
let url = location.protocol + "//" + address + "/config"
let globule = new GlobularWebClient.Globular(url, () => {
let peer = new Peer
peer.setDomain(globule.domain)
peer.setProtocol(globule.config.Protocol)
peer.setPorthttp(globule.config.PortHttp)
peer.setPorthttps(globule.config.PortHttps)
let rqst = new RegisterPeerRqst
rqst.setPeer(peer)
Model.globular.resourceService.registerPeer(rqst, { domain: Model.domain, address: Model.address, application: Model.application, token: localStorage.getItem("user_token") })
.then(() => {
if (location.protocol == "https:") {
if (peer.getProtocol() == "https") {
Model.globules.set("https://" + peer.getDomain() + ":" + peer.getPorthttps(), globule)
} else {
ApplicationView.displayMessage("fail to access peer with http protocol from https " + peer.getDomain(), 3000)
}
} else {
// Set http address
Model.globules.set("http://" + peer.getDomain() + ":" + peer.getPorthttp(), globule)
}
// Also keep the given address from the input.
Model.globules.set(url, globule)
panel.parentNode.removeChild(panel)
})
.catch(err => {
console.log(err)
ApplicationView.displayMessage(err, 3000)
})
}, (err) => {
ApplicationView.displayMessage(err, 3000)
})
}
let input = panel.querySelector("paper-input")
setTimeout(() => {
input.focus()
}, 100)
}
// call once
displayPeers()
Model.eventHub.subscribe("update_peers_evt", uuid => { }, evt => {
displayPeers()
}, false)
Model.eventHub.subscribe("delete_peer_evt", uuid => { }, evt => {
displayPeers()
}, false)
}
// The connection callback.
connectedCallback() {
}
}
customElements.define('globular-peer-manager', PeersManager)
/**
* Display each peers
*/
export class PeerPanel extends HTMLElement {
// attributes.
// Create the applicaiton view.
constructor(peer) {
super()
// Set the shadow dom.
this.attachShadow({ mode: 'open' });
// Keep group informations.
this.peer = peer;
let address = peer.getDomain()
if (peer.getProtocol() == "https") {
address += ":" + peer.getPorthttps()
} else {
address += ":" + peer.getPorthttp()
}
// Innitialisation of the layout.
this.shadowRoot.innerHTML = `
<style>
#container{
display: flex;
flex-direction: column;
align-items: center;
background-color: var(--palette-background-paper);
}
.header{
display: flex;
align-items: center;
width: 100%;
transition: background 0.2s ease,padding 0.8s linear;
background-color: var(--palette-background-paper);
}
.header:hover{
-webkit-filter: invert(10%);
filter: invert(10%);
}
.title{
padding-left: 10px;
flex-grow: 1;
color: var(--palette-text-primary);
}
a:visited {
color: var(--palette-text-primary);
background-color: transparent;
text-decoration: none;
}
.state{
padding-right: 20px;
padding-left: 10px;
}
.state-pending{
color: var(--palette-error-main);
}
.state-rejected .state-deleted{
color: var(--palette-secondary-main);
}
.state-unreachable{
color: var(--palette-error-dark);
}
.state-accepted{
color: var(--palette-success-main);
}
.card-content{
overflow-y: auto;
min-width: 400px;
max-height: 260px;
overflow-y: auto;
}
.peer-card-content {
display: table;
border-spacing: 30px 10px;
border-collapse: separate;
}
.peer-card-content .row {
display: table-row;
}
.peer-card-content .cell {
display: table-cell;
}
#actions-lst{
padding-left: 30px;
padding-right: 30px;
padding-bottom: 10px;
}
#content {
margin: 10px;
background-color: var(--palette-background-paper);
color: var(--palette-text-primary);
}
</style>
<div id="container">
<div class="header">
<iron-icon style="padding: 10px;" icon="hardware:computer"></iron-icon>
<a class="title">${this.peer.getHostname()}</a>
<span id="approval-state-span" class="state"></span>
<div style="display: flex; width: 32px; height: 32px; justify-content: center; align-items: center;position: relative;">
<iron-icon id="hide-btn" icon="unfold-less" style="flex-grow: 1; --iron-icon-fill-color:var(--palette-text-primary);" icon="unfold-more"></iron-icon>
<paper-ripple class="circle" recenters=""></paper-ripple>
</div>
</div>
<iron-collapse id="collapse-panel" style="width: 100%;" >
<div id="content" style="display: flex; flex-direction: column;">
<div class="peer-card-content">
<div class="row">
<div class="cell label">Mac Address</div>
<div class="cell value">${this.peer.getMac()}</div>
</div>
<div class="row">
<div class="cell label">address</div>
<div class="cell value">${address}</div>
</div>
<div class="row">
<div class="cell label">domain</div>
<div class="cell value">${this.peer.getDomain()}</div>
</div>
<div class="row">
<div class="cell label">public IP</div>
<div class="cell value">${this.peer.getExternalIpAddress()}</div>
</div>
<div class="row">
<div class="cell label">local IP</div>
<div class="cell value">${this.peer.getLocalIpAddress()}</div>
</div>
</div>
<div id="actions-lst">
</div>
<div style="display: flex; justify-content: end;">
<paper-button title="accept a peer." id="accept-peer-btn">
Accept
</paper-button>
<paper-button title="keep a peer in a black list until it be deleted." id="reject-peer-btn" >
Reject
</paper-button>
<paper-button title="delete a peer from the peer list, it also delete keys and permissions associated with that peer." id="delete-peer-btn" >
Delete
</paper-button>
</div>
</div>
</iron-collapse>
</div>
`
let lnk = this.shadowRoot.querySelector(".title")
if (peer.getProtocol() == "https") {
if (peer.getPorthttps() != 443) {
lnk.href = peer.getProtocol() + '://' + peer.getDomain() + ":" + peer.getPorthttps() + "/console"
} else {
lnk.href = peer.getProtocol() + '://' + peer.getDomain() + "/console"
}
} else {
if (peer.getPorthttp() != 80) {
lnk.href = peer.getProtocol() + '://' + peer.getDomain() + ":" + peer.getPorthttp() + "/console"
} else {
lnk.href = peer.getProtocol() + '://' + peer.getDomain() + "/console"
}
}
lnk.target = "_blank"
let content = this.shadowRoot.querySelector("#collapse-panel")
this.hideBtn = this.shadowRoot.querySelector("#hide-btn")
let deleteBtn = this.shadowRoot.querySelector("#delete-peer-btn")
deleteBtn.onclick = () => {
this.onDeletePeer(peer)
}
let rejectBtn = this.shadowRoot.querySelector("#reject-peer-btn")
rejectBtn.onclick = () => {
this.onRejectPeer(peer)
}
let acceptBtn = this.shadowRoot.querySelector("#accept-peer-btn")
acceptBtn.onclick = () => {
this.onAcceptPeer(peer)
}
// get the remote state for this peers.
this.getRemoteState((remote_state) => {
let state = 0
let stateSpan = this.shadowRoot.querySelector("#approval-state-span")
if (remote_state != 0) {
if (remote_state == -1) {
state = "unreachable"
stateSpan.innerHTML = "unreachable"
stateSpan.className = "state state-unreachable"
} else {
if (this.peer.getState() == 1 && remote_state == 1) {
state = 1
}
if (this.peer.getState() == 2 || remote_state == 2) {
state = 2
}
stateSpan.innerHTML = this.getState(state)
if (this.peer.getState() == 0) {
stateSpan.innerHTML += " require your approbation"
}
}
} else {
stateSpan.innerHTML += " wait for approbation"
}
stateSpan.className = "state state-" + this.getState(state)
// Here I will set the button
if (remote_state == -1) {
deleteBtn.style.display = "block"
rejectBtn.style.display = "none"
acceptBtn.style.display = "none"
} else if (this.peer.getState() == 0) {
deleteBtn.style.display = "none"
rejectBtn.style.display = "block"
acceptBtn.style.display = "block"
} else if (this.peer.getState() == 1) {
deleteBtn.style.display = "block"
rejectBtn.style.display = "none"
acceptBtn.style.display = "none"
} else if (this.peer.getState() == 2) {
deleteBtn.style.display = "block"
rejectBtn.style.display = "none"
acceptBtn.style.display = "none"
}
})
// Here I will create the searchable actions list.
let actionsList = new SearchableList("Actions", this.peer.getActionsList(),
(action) => {
// remove action...
let removeActionRqst = new RemovePeerActionRqst
removeActionRqst.setAction(action)
removeActionRqst.setRoleid(peer.getId())
Model.globular.resourceService.removePeerAction(removeActionRqst, { domain: Model.domain, address: Model.address, application: Model.application, token: localStorage.getItem("user_token") })
.then(rsp => {
actionsList.removeItem(action)
ApplicationView.displayMessage("Action " + action + " was removed from peer " + peer.getId(), 3000)
}).catch(err => {
ApplicationView.displayMessage(err, 3000)
})
},
(action) => {
ApplicationView.displayMessage("Action " + action + " was added to peer " + peer.getId(), 3000)
},
(actions) => {
// Now I will get the list of all actions install on the server.
let getAllActionsRqst = new GetAllActionsRequest
Model.globular.servicesManagerService.getAllActions(getAllActionsRqst, { domain: Model.domain, address: Model.address, application: Model.application, token: localStorage.getItem("user_token") })
.then(rsp => {
let actions_ = rsp.getActionsList()
actions.forEach(a => {
actions_.splice(actions_.indexOf(a), 1);
});
// sort the array.
actions_.sort()
let html = `
<style>
#add-peer-action-panel{
position: absolute;
right: 0px;
z-index: 1;
}
paper-card{
background-color: var(--palette-background-paper);
color: var(--palette-text-primary);
}
</style>
<paper-card id="add-peer-action-panel">
<div style="display: flex; align-items: center;">
<div style="flex-grow: 1; padding: 5px;">
Add Action
</div>
<paper-icon-button id="cancel-btn" icon="close"></paper-icon-button>
</div>
<div class="card-content">
</div>
</paper-card>
`
let headerDiv = actionsList.getHeader()
let panel = headerDiv.querySelector("#add-peer-action-panel")
if (panel == undefined) {
headerDiv.appendChild(document.createRange().createContextualFragment(html))
panel = headerDiv.querySelector("#add-peer-action-panel")
panel.style.top = (headerDiv.offsetHeight / 2) + 14 + "px";
let closeBtn = panel.querySelector("#cancel-btn")
closeBtn.onclick = () => {
panel.parentNode.removeChild(panel)
}
actions_.forEach(a => {
let html = `
<div class="item-div" style="">
<span style="flex-grow: 1;">${a}</span>
<paper-icon-button id="add-action-btn" icon="add" peer="button" tabindex="0" aria-disabled="false"></paper-icon-button>
</div>
`
let content = panel.querySelector(".card-content")
content.appendChild(document.createRange().createContextualFragment(html))
let actionDiv = content.children[content.children.length - 1]
let actionAddBtn = actionDiv.children[1]
actionAddBtn.onclick = () => {
let rqst = new AddPeerActionsRqst
rqst.setPeerid(peer.getId())
rqst.setActionsList([a])
Model.globular.resourceService.addPeerAction(rqst, { domain: Model.domain, address: Model.address, application: Model.application, token: localStorage.getItem("user_token") })
.then(rsp => {
actionDiv.parentNode.removeChild(actionDiv)
actionsList.appendItem(a)
// call the onadditem.
actionsList.onadditem(a)
}).catch(err => {
ApplicationView.displayMessage(err, 3000)
})
}
})
}
}).catch(err => {
ApplicationView.displayMessage(err, 3000)
})
})
this.shadowRoot.querySelector("#actions-lst").appendChild(actionsList)
// give the focus to the input.
this.hideBtn.onclick = () => {
let button = this.shadowRoot.querySelector("#hide-btn")
if (button && content) {
if (!content.opened) {
button.icon = "unfold-more"
} else {
button.icon = "unfold-less"
}
content.toggle();
}
}
}
getRemoteState(callback) {
let rqst = new GetPeerApprovalStateRqst
let address = this.peer.getDomain()
if (this.peer.getProtocol() == "https") {
address += ":" + this.peer.getPorthttps()
} else {
address += ":" + this.peer.getPorthttp()
}
rqst.setRemotePeerAddress(address)
Model.globular.resourceService.getPeerApprovalState(rqst, { domain: Model.domain, address: Model.address, application: Model.application, token: localStorage.getItem("user_token") })
.then(rsp => {
callback(rsp.getState())
})
.catch((err) => {
console.log("fail to get peer approval state with error ", err)
callback(-1)
})
}
getState(state) {
if (state == 0) {
return "pending"
} else if (state == 1) {
return "accepted"
} else if (state == 2) {
return "rejected"
}
return ""
}
onAcceptPeer(peer) {
let rqst = new AcceptPeerRqst
rqst.setPeer(peer)
Model.globular.resourceService.acceptPeer(rqst, { domain: Model.domain, address: Model.address, application: Model.application, token: localStorage.getItem("user_token") })
.then(rsp => {
console.log(rsp)
}).catch(err => ApplicationView.displayMessage(err, 3000))
}
onRejectPeer(peer) {
console.log("reject peer ", peer)
}
onDeletePeer(peer) {
let toast = ApplicationView.displayMessage(
`
<style>
#yes-no-contact-delete-box{
display: flex;
flex-direction: column;
}
#yes-no-contact-delete-box globular-contact-card{
padding-bottom: 10px;
}
#yes-no-contact-delete-box div{
display: flex;
padding-bottom: 10px;
}
</style>
<div id="yes-no-contact-delete-box">
<div>Your about to delete the peer ${peer.getHostname()}</div>
<div>Is it what you want to do? </div>
<div style="justify-content: flex-end;">
<paper-button id="yes-delete-contact">Yes</paper-button>
<paper-button id="no-delete-contact">No</paper-button>
</div>
</div>
`,
15000 // 15 sec...
);
let yesBtn = document.querySelector("#yes-delete-contact")
let noBtn = document.querySelector("#no-delete-contact")
// On yes
yesBtn.onclick = () => {
let rqst = new DeletePeerRqst
rqst.setPeer(peer)
Model.globular.resourceService.deletePeer(rqst, { domain: Model.domain, address: Model.address, application: Model.application, token: localStorage.getItem("user_token") }).then((rsp) => {
ApplicationView.displayMessage(
"<iron-icon icon='communication:message' style='margin-right: 10px;'></iron-icon><div>Peer with hostname " +
peer.getHostname() +
" was deleted!</div>",
3000
);
toast.dismiss();
}).catch(e => {
ApplicationView.displayMessage(e, 3000)
toast.dismiss();
})
}
noBtn.onclick = () => {
toast.dismiss();
}
}
}
customElements.define('globular-peer-panel', PeerPanel)