Add admin page for refetching internal data
This commit is contained in:
parent
d7e548ed22
commit
7b068e085f
@ -4,7 +4,9 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
@ -16,6 +18,7 @@ import (
|
||||
|
||||
// InternalServer is handle to internal http server
|
||||
type InternalServer struct {
|
||||
htmlTemplates[InternalTemplateData]
|
||||
https *http.Server
|
||||
certFiles string
|
||||
db *db.RocksDB
|
||||
@ -41,6 +44,9 @@ func NewInternalServer(binding, certFiles string, db *db.RocksDB, chain bchain.B
|
||||
Handler: serveMux,
|
||||
}
|
||||
s := &InternalServer{
|
||||
htmlTemplates: htmlTemplates[InternalTemplateData]{
|
||||
debug: true,
|
||||
},
|
||||
https: https,
|
||||
certFiles: certFiles,
|
||||
db: db,
|
||||
@ -51,11 +57,18 @@ func NewInternalServer(binding, certFiles string, db *db.RocksDB, chain bchain.B
|
||||
is: is,
|
||||
api: api,
|
||||
}
|
||||
s.htmlTemplates.newTemplateData = s.newTemplateData
|
||||
s.htmlTemplates.newTemplateDataWithError = s.newTemplateDataWithError
|
||||
s.htmlTemplates.parseTemplates = s.parseTemplates
|
||||
s.templates = s.parseTemplates()
|
||||
|
||||
serveMux.Handle(path+"favicon.ico", http.FileServer(http.Dir("./static/")))
|
||||
serveMux.HandleFunc(path+"metrics", promhttp.Handler().ServeHTTP)
|
||||
serveMux.HandleFunc(path, s.index)
|
||||
|
||||
serveMux.HandleFunc(path+"admin", s.htmlTemplateHandler(s.adminIndex))
|
||||
if s.chainParser.GetChainType() == bchain.ChainEthereumType {
|
||||
serveMux.HandleFunc(path+"admin/internal-data-errors", s.htmlTemplateHandler(s.internalDataErrors))
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@ -97,3 +110,73 @@ func (s *InternalServer) index(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
w.Write(buf)
|
||||
}
|
||||
|
||||
const (
|
||||
adminIndexTpl = iota + errorInternalTpl + 1
|
||||
adminInternalErrorsTpl
|
||||
|
||||
internalTplCount
|
||||
)
|
||||
|
||||
// InternalTemplateData is used to transfer data to the templates
|
||||
type InternalTemplateData struct {
|
||||
CoinName string
|
||||
CoinShortcut string
|
||||
CoinLabel string
|
||||
ChainType bchain.ChainType
|
||||
Error *api.APIError
|
||||
InternalDataErrors []db.BlockInternalDataError
|
||||
InInternalDataRetry bool
|
||||
}
|
||||
|
||||
func (s *InternalServer) newTemplateData(r *http.Request) *InternalTemplateData {
|
||||
t := &InternalTemplateData{
|
||||
CoinName: s.is.Coin,
|
||||
CoinShortcut: s.is.CoinShortcut,
|
||||
CoinLabel: s.is.CoinLabel,
|
||||
ChainType: s.chainParser.GetChainType(),
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (s *InternalServer) newTemplateDataWithError(error *api.APIError, r *http.Request) *InternalTemplateData {
|
||||
td := s.newTemplateData(r)
|
||||
td.Error = error
|
||||
return td
|
||||
}
|
||||
|
||||
func (s *InternalServer) parseTemplates() []*template.Template {
|
||||
templateFuncMap := template.FuncMap{
|
||||
"formatUint32": formatUint32,
|
||||
}
|
||||
createTemplate := func(filenames ...string) *template.Template {
|
||||
if len(filenames) == 0 {
|
||||
panic("Missing templates")
|
||||
}
|
||||
return template.Must(template.New(filepath.Base(filenames[0])).Funcs(templateFuncMap).ParseFiles(filenames...))
|
||||
}
|
||||
t := make([]*template.Template, internalTplCount)
|
||||
t[errorTpl] = createTemplate("./static/internal_templates/error.html", "./static/internal_templates/base.html")
|
||||
t[errorInternalTpl] = createTemplate("./static/internal_templates/error.html", "./static/internal_templates/base.html")
|
||||
t[adminIndexTpl] = createTemplate("./static/internal_templates/index.html", "./static/internal_templates/base.html")
|
||||
t[adminInternalErrorsTpl] = createTemplate("./static/internal_templates/block_internal_data_errors.html", "./static/internal_templates/base.html")
|
||||
return t
|
||||
}
|
||||
|
||||
func (s *InternalServer) adminIndex(w http.ResponseWriter, r *http.Request) (tpl, *InternalTemplateData, error) {
|
||||
data := s.newTemplateData(r)
|
||||
return adminIndexTpl, data, nil
|
||||
}
|
||||
|
||||
func (s *InternalServer) internalDataErrors(w http.ResponseWriter, r *http.Request) (tpl, *InternalTemplateData, error) {
|
||||
if r.Method == http.MethodPost {
|
||||
|
||||
}
|
||||
data := s.newTemplateData(r)
|
||||
internalErrors, err := s.db.GetBlockInternalDataErrorsEthereumType()
|
||||
if err != nil {
|
||||
return errorTpl, nil, err
|
||||
}
|
||||
data.InternalDataErrors = internalErrors
|
||||
return adminInternalErrorsTpl, data, nil
|
||||
}
|
||||
|
||||
28
static/internal_templates/base.html
Normal file
28
static/internal_templates/base.html
Normal file
@ -0,0 +1,28 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,shrink-to-fit=no">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
|
||||
<title>Blockbook {{.CoinLabel}} Internal Admin</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header id="header">
|
||||
<nav class="navbar">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/admin" title="Home">
|
||||
<h1>Blockbook {{.CoinLabel}} Internal Admin</h1>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<main id="wrap">
|
||||
<div class="container">
|
||||
{{- template "specific" . -}}
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
34
static/internal_templates/block_internal_data_errors.html
Normal file
34
static/internal_templates/block_internal_data_errors.html
Normal file
@ -0,0 +1,34 @@
|
||||
{{define "specific"}}
|
||||
<div class="row g-0">
|
||||
<h3 class="col-md-11">Blocks with errors from fetching internal data</h3>
|
||||
<div class="col-md-1 justify-content-right">
|
||||
{{if .InInternalDataRetry}}Fetching...{{else}}
|
||||
<form method="POST" action="/admin/internal-data-errors">
|
||||
<button type="submit" class="btn btn-outline-secondary">Retry fetch</button>
|
||||
</form>
|
||||
{{end}}
|
||||
</div>
|
||||
</row>
|
||||
<div>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Height</th>
|
||||
<th class="col-md-6">Hash</th>
|
||||
<th class="col-md-1 text-end">Retries</span></th>
|
||||
<th>Error Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range $e := .InternalDataErrors}}
|
||||
<tr>
|
||||
<td>{{formatUint32 $e.Height}}</a></td>
|
||||
<td class="ellipsis">{{$e.Hash}}</td>
|
||||
<td class="text-end">{{$e.Retries}}</td>
|
||||
<td>{{$e.ErrorMessage}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
||||
4
static/internal_templates/error.html
Normal file
4
static/internal_templates/error.html
Normal file
@ -0,0 +1,4 @@
|
||||
{{define "specific"}}
|
||||
<h1>Error</h1>
|
||||
<h4>{{.Error.Text}}</h4>
|
||||
{{end}}
|
||||
7
static/internal_templates/index.html
Normal file
7
static/internal_templates/index.html
Normal file
@ -0,0 +1,7 @@
|
||||
{{define "specific"}}
|
||||
{{if eq .ChainType 1}}
|
||||
<div class="row">
|
||||
<div class="col"><a href="/admin/internal-data-errors">Internal Data Errors</a></div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
Loading…
Reference in New Issue
Block a user