Add admin page for refetching internal data

This commit is contained in:
Martin Boehm 2023-03-26 21:08:51 +02:00
parent d7e548ed22
commit 7b068e085f
5 changed files with 157 additions and 1 deletions

View File

@ -4,7 +4,9 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"html/template"
"net/http" "net/http"
"path/filepath"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
@ -16,6 +18,7 @@ import (
// InternalServer is handle to internal http server // InternalServer is handle to internal http server
type InternalServer struct { type InternalServer struct {
htmlTemplates[InternalTemplateData]
https *http.Server https *http.Server
certFiles string certFiles string
db *db.RocksDB db *db.RocksDB
@ -41,6 +44,9 @@ func NewInternalServer(binding, certFiles string, db *db.RocksDB, chain bchain.B
Handler: serveMux, Handler: serveMux,
} }
s := &InternalServer{ s := &InternalServer{
htmlTemplates: htmlTemplates[InternalTemplateData]{
debug: true,
},
https: https, https: https,
certFiles: certFiles, certFiles: certFiles,
db: db, db: db,
@ -51,11 +57,18 @@ func NewInternalServer(binding, certFiles string, db *db.RocksDB, chain bchain.B
is: is, is: is,
api: api, 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.Handle(path+"favicon.ico", http.FileServer(http.Dir("./static/")))
serveMux.HandleFunc(path+"metrics", promhttp.Handler().ServeHTTP) serveMux.HandleFunc(path+"metrics", promhttp.Handler().ServeHTTP)
serveMux.HandleFunc(path, s.index) 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 return s, nil
} }
@ -97,3 +110,73 @@ func (s *InternalServer) index(w http.ResponseWriter, r *http.Request) {
w.Write(buf) 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
}

View 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>

View 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}}

View File

@ -0,0 +1,4 @@
{{define "specific"}}
<h1>Error</h1>
<h4>{{.Error.Text}}</h4>
{{end}}

View 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}}