188 lines
5.8 KiB
Go
188 lines
5.8 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"html/template"
|
|
"net/http"
|
|
"path/filepath"
|
|
|
|
"github.com/golang/glog"
|
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
"github.com/trezor/blockbook/api"
|
|
"github.com/trezor/blockbook/bchain"
|
|
"github.com/trezor/blockbook/common"
|
|
"github.com/trezor/blockbook/db"
|
|
"github.com/trezor/blockbook/fiat"
|
|
)
|
|
|
|
// InternalServer is handle to internal http server
|
|
type InternalServer struct {
|
|
htmlTemplates[InternalTemplateData]
|
|
https *http.Server
|
|
certFiles string
|
|
db *db.RocksDB
|
|
txCache *db.TxCache
|
|
chain bchain.BlockChain
|
|
chainParser bchain.BlockChainParser
|
|
mempool bchain.Mempool
|
|
is *common.InternalState
|
|
api *api.Worker
|
|
}
|
|
|
|
// NewInternalServer creates new internal http interface to blockbook and returns its handle
|
|
func NewInternalServer(binding, certFiles string, db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, metrics *common.Metrics, is *common.InternalState, fiatRates *fiat.FiatRates) (*InternalServer, error) {
|
|
api, err := api.NewWorker(db, chain, mempool, txCache, metrics, is, fiatRates)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
addr, path := splitBinding(binding)
|
|
serveMux := http.NewServeMux()
|
|
https := &http.Server{
|
|
Addr: addr,
|
|
Handler: serveMux,
|
|
}
|
|
s := &InternalServer{
|
|
htmlTemplates: htmlTemplates[InternalTemplateData]{
|
|
debug: true,
|
|
},
|
|
https: https,
|
|
certFiles: certFiles,
|
|
db: db,
|
|
txCache: txCache,
|
|
chain: chain,
|
|
chainParser: chain.GetChainParser(),
|
|
mempool: mempool,
|
|
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
|
|
}
|
|
|
|
// Run starts the server
|
|
func (s *InternalServer) Run() error {
|
|
if s.certFiles == "" {
|
|
glog.Info("internal server: starting to listen on http://", s.https.Addr)
|
|
return s.https.ListenAndServe()
|
|
}
|
|
glog.Info("internal server: starting to listen on https://", s.https.Addr)
|
|
return s.https.ListenAndServeTLS(fmt.Sprint(s.certFiles, ".crt"), fmt.Sprint(s.certFiles, ".key"))
|
|
}
|
|
|
|
// Close closes the server
|
|
func (s *InternalServer) Close() error {
|
|
glog.Infof("internal server: closing")
|
|
return s.https.Close()
|
|
}
|
|
|
|
// Shutdown shuts down the server
|
|
func (s *InternalServer) Shutdown(ctx context.Context) error {
|
|
glog.Infof("internal server: shutdown")
|
|
return s.https.Shutdown(ctx)
|
|
}
|
|
|
|
func (s *InternalServer) index(w http.ResponseWriter, r *http.Request) {
|
|
si, err := s.api.GetSystemInfo(true)
|
|
if err != nil {
|
|
glog.Error(err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
buf, err := json.MarshalIndent(si, "", " ")
|
|
if err != nil {
|
|
glog.Error(err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
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
|
|
RefetchingInternalData 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 {
|
|
err := s.api.RefetchInternalData()
|
|
if err != nil {
|
|
return errorTpl, nil, err
|
|
}
|
|
}
|
|
data := s.newTemplateData(r)
|
|
internalErrors, err := s.db.GetBlockInternalDataErrorsEthereumType()
|
|
if err != nil {
|
|
return errorTpl, nil, err
|
|
}
|
|
data.InternalDataErrors = internalErrors
|
|
data.RefetchingInternalData = s.api.IsRefetchingInternalData()
|
|
return adminInternalErrorsTpl, data, nil
|
|
}
|