Private GIT

Skip to content
Snippets Groups Projects
Commit 3c155f1c authored by Christoph Haas's avatar Christoph Haas
Browse files

improve formatting and layout

parent 31dd494d
Branches
Tags
No related merge requests found
...@@ -41,6 +41,6 @@ OAUTH2_REDIRECT_URL=https://wg-gen-web-demo.127-0-0-1.fr ...@@ -41,6 +41,6 @@ OAUTH2_REDIRECT_URL=https://wg-gen-web-demo.127-0-0-1.fr
OAUTH2_PROVIDER_NAME=fake OAUTH2_PROVIDER_NAME=fake
# https://github.com/jamescun/wg-api integration, user and password (basic auth) are optional # https://github.com/jamescun/wg-api integration, user and password (basic auth) are optional
WG_STATS_API=https://wg.example.digital/wg-api WG_STATS_API=
WG_STATS_API_USER= WG_STATS_API_USER=
WG_STATS_API_PASS= WG_STATS_API_PASS=
\ No newline at end of file
...@@ -23,7 +23,7 @@ func readInterfaceStatus(c *gin.Context) { ...@@ -23,7 +23,7 @@ func readInterfaceStatus(c *gin.Context) {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"err": err, "err": err,
}).Error("failed to read interface status") }).Error("failed to read interface status")
c.AbortWithStatus(http.StatusInternalServerError) c.AbortWithStatusJSON(http.StatusInternalServerError, err.Error())
return return
} }
...@@ -36,7 +36,7 @@ func readClientStatus(c *gin.Context) { ...@@ -36,7 +36,7 @@ func readClientStatus(c *gin.Context) {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"err": err, "err": err,
}).Error("failed to read client status") }).Error("failed to read client status")
c.AbortWithStatus(http.StatusInternalServerError) c.AbortWithStatusJSON(http.StatusInternalServerError, err.Error())
return return
} }
......
...@@ -3,6 +3,7 @@ package core ...@@ -3,6 +3,7 @@ package core
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os" "os"
...@@ -34,6 +35,10 @@ type apiResponse struct { ...@@ -34,6 +35,10 @@ type apiResponse struct {
func fetchWireGuardAPI(reqData apiRequest) (*apiResponse, error) { func fetchWireGuardAPI(reqData apiRequest) (*apiResponse, error) {
apiUrl := os.Getenv("WG_STATS_API") apiUrl := os.Getenv("WG_STATS_API")
if apiUrl == "" {
return nil, errors.New("Status API integration not configured")
}
apiClient := http.Client{ apiClient := http.Client{
Timeout: time.Second * 2, // Timeout after 2 seconds Timeout: time.Second * 2, // Timeout after 2 seconds
} }
...@@ -135,6 +140,8 @@ func ReadClientStatus() ([]*model.ClientStatus, error) { ...@@ -135,6 +140,8 @@ func ReadClientStatus() ([]*model.ClientStatus, error) {
for i, peerIP := range peerIPs { for i, peerIP := range peerIPs {
peerAddresses[i] = peerIP.(string) peerAddresses[i] = peerIP.(string)
} }
peerHandshakeRelative := time.Since(peerHandshake)
peerActive := peerHandshakeRelative.Minutes() < 3 // TODO: we need a better detection... ping for example?
newClientStatus := &model.ClientStatus{ newClientStatus := &model.ClientStatus{
PublicKey: peer["public_key"].(string), PublicKey: peer["public_key"].(string),
...@@ -142,10 +149,11 @@ func ReadClientStatus() ([]*model.ClientStatus, error) { ...@@ -142,10 +149,11 @@ func ReadClientStatus() ([]*model.ClientStatus, error) {
ProtocolVersion: int(peer["protocol_version"].(float64)), ProtocolVersion: int(peer["protocol_version"].(float64)),
Name: "UNKNOWN", Name: "UNKNOWN",
Email: "UNKNOWN", Email: "UNKNOWN",
Connected: false, Connected: peerActive,
AllowedIPs: peerAddresses, AllowedIPs: peerAddresses,
Endpoint: peer["endpoint"].(string), Endpoint: peer["endpoint"].(string),
LastHandshake: peerHandshake, LastHandshake: peerHandshake,
LastHandshakeRelative: peerHandshakeRelative,
ReceivedBytes: int(peer["receive_bytes"].(float64)), ReceivedBytes: int(peer["receive_bytes"].(float64)),
TransmittedBytes: int(peer["transmit_bytes"].(float64)), TransmittedBytes: int(peer["transmit_bytes"].(float64)),
} }
......
package model package model
import ( import (
"encoding/json"
"fmt"
"time" "time"
) )
...@@ -15,10 +17,46 @@ type ClientStatus struct { ...@@ -15,10 +17,46 @@ type ClientStatus struct {
AllowedIPs []string `json:"allowedIPs"` AllowedIPs []string `json:"allowedIPs"`
Endpoint string `json:"endpoint"` Endpoint string `json:"endpoint"`
LastHandshake time.Time `json:"lastHandshake"` LastHandshake time.Time `json:"lastHandshake"`
LastHandshakeRelative time.Duration `json:"lastHandshakeRelative"`
ReceivedBytes int `json:"receivedBytes"` ReceivedBytes int `json:"receivedBytes"`
TransmittedBytes int `json:"transmittedBytes"` TransmittedBytes int `json:"transmittedBytes"`
} }
func (c *ClientStatus) MarshalJSON() ([]byte, error) {
duration := fmt.Sprintf("%v ago", c.LastHandshakeRelative)
if c.LastHandshakeRelative.Hours() > 5208 { // 24*7*31 = approx one month
duration = "more than a month ago"
}
return json.Marshal(&struct {
PublicKey string `json:"publicKey"`
HasPresharedKey bool `json:"hasPresharedKey"`
ProtocolVersion int `json:"protocolVersion"`
Name string `json:"name"`
Email string `json:"email"`
Connected bool `json:"connected"`
AllowedIPs []string `json:"allowedIPs"`
Endpoint string `json:"endpoint"`
LastHandshake time.Time `json:"lastHandshake"`
LastHandshakeRelative string `json:"lastHandshakeRelative"`
ReceivedBytes int `json:"receivedBytes"`
TransmittedBytes int `json:"transmittedBytes"`
}{
PublicKey: c.PublicKey,
HasPresharedKey: c.HasPresharedKey,
ProtocolVersion: c.ProtocolVersion,
Name: c.Name,
Email: c.Email,
Connected: c.Connected,
AllowedIPs: c.AllowedIPs,
Endpoint: c.Endpoint,
LastHandshake: c.LastHandshake,
LastHandshakeRelative: duration,
ReceivedBytes: c.ReceivedBytes,
TransmittedBytes: c.TransmittedBytes,
})
}
// InterfaceStatus structure // InterfaceStatus structure
type InterfaceStatus struct { type InterfaceStatus struct {
Name string `json:"name"` Name string `json:"name"`
......
...@@ -44,9 +44,19 @@ ...@@ -44,9 +44,19 @@
:items="clients" :items="clients"
:search="search" :search="search"
> >
<template v-slot:item.address="{ item }"> <template v-slot:item.connected="{ item }">
<v-icon left v-if="item.connected" color="success">mdi-lan-connect</v-icon>
<v-icon left v-else>mdi-lan-disconnect</v-icon>
</template>
<template v-slot:item.receivedBytes="{ item }">
{{ humanFileSize(item.receivedBytes) }}
</template>
<template v-slot:item.transmittedBytes="{ item }">
{{ humanFileSize(item.transmittedBytes) }}
</template>
<template v-slot:item.allowedIPs="{ item }">
<v-chip <v-chip
v-for="(ip, i) in item.address" v-for="(ip, i) in item.allowedIPs"
:key="i" :key="i"
color="indigo" color="indigo"
text-color="white" text-color="white"
...@@ -55,63 +65,11 @@ ...@@ -55,63 +65,11 @@
{{ ip }} {{ ip }}
</v-chip> </v-chip>
</template> </template>
<template v-slot:item.tags="{ item }"> <template v-slot:item.lastHandshake="{ item }">
<v-chip
v-for="(tag, i) in item.tags"
:key="i"
color="blue-grey"
text-color="white"
>
<v-icon left>mdi-tag</v-icon>
{{ tag }}
</v-chip>
</template>
<template v-slot:item.created="{ item }">
<v-row>
<p>At {{ item.created | formatDate }} by {{ item.createdBy }}</p>
</v-row>
</template>
<template v-slot:item.updated="{ item }">
<v-row> <v-row>
<p>At {{ item.updated | formatDate }} by {{ item.updatedBy }}</p> <p>{{ item.lastHandshake | formatDate }} ({{ item.lastHandshakeRelative }})</p>
</v-row> </v-row>
</template> </template>
<template v-slot:item.action="{ item }">
<v-row>
<v-icon
class="pr-1 pl-1"
@click.stop="startUpdate(item)"
>
mdi-square-edit-outline
</v-icon>
<v-icon
class="pr-1 pl-1"
@click.stop="forceFileDownload(item)"
>
mdi-cloud-download-outline
</v-icon>
<v-icon
class="pr-1 pl-1"
@click.stop="email(item)"
>
mdi-email-send-outline
</v-icon>
<v-icon
class="pr-1 pl-1"
@click="remove(item)"
>
mdi-trash-can-outline
</v-icon>
<v-switch
dark
class="pr-1 pl-1"
color="success"
v-model="item.enable"
v-on:change="update(item)"
/>
</v-row>
</template>
</v-data-table> </v-data-table>
</v-card> </v-card>
</v-col> </v-col>
...@@ -170,6 +128,28 @@ ...@@ -170,6 +128,28 @@
reload() { reload() {
this.readStatus() this.readStatus()
}, },
humanFileSize(bytes, si=false, dp=1) {
const thresh = si ? 1000 : 1024;
if (Math.abs(bytes) < thresh) {
return bytes + ' B';
}
const units = si
? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
let u = -1;
const r = 10**dp;
do {
bytes /= thresh;
++u;
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
return bytes.toFixed(dp) + ' ' + units[u];
}
} }
}; };
</script> </script>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment