Add admin page for refetching internal data
This commit is contained in:
parent
d7e548ed22
commit
7b068e085f
@ -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
|
||||||
|
}
|
||||||
|
|||||||
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