Add voting wallet status to admin page

This commit is contained in:
jholdstock 2020-07-06 10:43:46 +01:00 committed by David Hill
parent da410b5060
commit 1c351d02ec
6 changed files with 143 additions and 7 deletions

View File

@ -155,3 +155,12 @@ func (c *WalletRPC) AddTicketForVoting(votingWIF, blockHash, txHex string) error
func (c *WalletRPC) SetVoteChoice(agenda, choice, ticketHash string) error {
return c.Call(c.ctx, "setvotechoice", nil, agenda, choice, ticketHash)
}
func (c *WalletRPC) GetBestBlockHeight() (int64, error) {
var block dcrdtypes.GetBestBlockResult
err := c.Call(c.ctx, "getbestblock", &block)
if err != nil {
return 0, err
}
return block.Height, nil
}

View File

@ -3,14 +3,64 @@ package webapi
import (
"net/http"
"github.com/decred/vspd/rpc"
"github.com/gin-gonic/gin"
"github.com/gorilla/sessions"
)
// WalletStatus describes the current status of a single voting wallet.
type WalletStatus struct {
Connected bool
InfoError bool
DaemonConnected bool
VoteVersion uint32
Unlocked bool
Voting bool
BestBlockError bool
BestBlockHeight int64
}
func walletStatus(c *gin.Context) map[string]WalletStatus {
walletClients := c.MustGet("WalletClients").([]*rpc.WalletRPC)
failedWalletClients := c.MustGet("FailedWalletClients").([]string)
walletStatus := make(map[string]WalletStatus)
for _, v := range walletClients {
ws := WalletStatus{Connected: true}
walletInfo, err := v.WalletInfo()
if err != nil {
log.Errorf("dcrwallet.WalletInfo error (wallet=%s): %v", v.String(), err)
ws.InfoError = true
} else {
ws.DaemonConnected = walletInfo.DaemonConnected
ws.VoteVersion = walletInfo.VoteVersion
ws.Unlocked = walletInfo.Unlocked
ws.Voting = walletInfo.Voting
}
height, err := v.GetBestBlockHeight()
if err != nil {
log.Errorf("dcrwallet.GetBestBlockHeight error (wallet=%s): %v", v.String(), err)
ws.BestBlockError = true
} else {
ws.BestBlockHeight = height
}
walletStatus[v.String()] = ws
}
for _, v := range failedWalletClients {
ws := WalletStatus{Connected: false}
walletStatus[v] = ws
}
return walletStatus
}
// adminPage is the handler for "GET /admin".
func adminPage(c *gin.Context) {
c.HTML(http.StatusOK, "admin.html", gin.H{
"VspStats": getVSPStats(),
"VspStats": getVSPStats(),
"WalletStatus": walletStatus(c),
})
}
@ -32,7 +82,8 @@ func ticketSearch(c *gin.Context) {
"Found": found,
"Ticket": ticket,
},
"VspStats": getVSPStats(),
"VspStats": getVSPStats(),
"WalletStatus": walletStatus(c),
})
}

View File

@ -105,6 +105,7 @@ func withWalletClients(wallets rpc.WalletConnect) gin.HandlerFunc {
len(failedConnections), len(clients))
}
c.Set("WalletClients", clients)
c.Set("FailedWalletClients", failedConnections)
}
}

View File

@ -103,7 +103,8 @@ footer .code {
.block__content th {
font-weight: normal;
padding-right: 15px;
text-align: right;
color: #495057;
background-color: #e9ecef;
}
.block__content table td {
@ -115,3 +116,22 @@ footer .code {
vertical-align: top;
white-space: nowrap;
}
td.status-good{
background-color: #C4ECCA;
}
td.status-bad{
background-color: #FEB8A5;
}
.ticket-table th {
text-align: right;
}
.ticket-table td {
text-align: left;
}
.status-table th,
.status-table td {
text-align: center;
}

View File

@ -22,14 +22,70 @@
</div>
<div class="container">
<div class="row">
<div class="col-12 p-3">
<div class="block__content">
<h1>Voting Wallet Status</h1>
<table class="table status-table mb-0" width="100%">
<tr>
<th>URL</th>
<th>Best Block Height</th>
<th>Daemon Connected</th>
<th>Unlocked</th>
<th>Voting</th>
<th>Vote Version</th>
</tr>
{{ range $host, $status := .WalletStatus }}
<tr>
<td>{{ $host }}</td>
{{ if $status.Connected }}
{{ if $status.BestBlockError }}
<td class="status-bad">Error</td>
{{ else }}
<td>{{ $status.BestBlockHeight }}</td>
{{ end }}
{{ if $status.InfoError }}
<td class="status-bad" colspan="4">Error</td>
{{ else }}
<td class="{{ if $status.DaemonConnected }}status-good{{else}}status-bad{{end}}"
>{{ $status.DaemonConnected }}</td>
<td class="{{ if $status.Unlocked }}status-good{{else}}status-bad{{end}}"
>{{ $status.Unlocked }}</td>
<td class="{{ if $status.Voting }}status-good{{else}}status-bad{{end}}"
>{{ $status.Voting }}</td>
<td>{{ $status.VoteVersion }}</td>
{{ end }}
{{else}}
<td class="status-bad" colspan="4">Cannot connect to wallet</td>
{{end}}
</tr>
{{end}}
</table>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-12 p-3">
<div class="block__content">
<h1>Ticket Search</h1>
<form class="my-2" action="/admin/ticket" method="post">
<input type="text" name="hash" size="64" minlength="64" maxlength="64" required placeholder="Ticket hash" autocomplete="off">
<button class="ml-3 btn btn-primary" type="submit">Search</button>
@ -38,7 +94,7 @@
{{ with .SearchResult }}
{{ if .Found }}
{{ with .Ticket }}
<table class="table mt-4 mb-0">
<table class="table ticket-table mt-4 mb-0">
<tr>
<th>Hash</th>
<td>{{ .Hash }}</td>
@ -104,7 +160,6 @@
</div>
</div>
</div>
{{ template "footer" . }}

View File

@ -205,7 +205,7 @@ func router(debugMode bool, cookieSecret []byte, dcrd rpc.DcrdConnect, wallets r
login.POST("", adminLogin)
admin := router.Group("/admin").Use(
withSession(cookieStore), requireAdmin(),
withWalletClients(wallets), withSession(cookieStore), requireAdmin(),
)
admin.GET("", adminPage)
admin.POST("/ticket", ticketSearch)