github.com/cryptohub-digital/blockbook@v0.3.5-0.20240403155730-99ab40b9104c/server/internal.go (about)

     1  package server
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"html/template"
     8  	"net/http"
     9  	"path/filepath"
    10  
    11  	"github.com/cryptohub-digital/blockbook/api"
    12  	"github.com/cryptohub-digital/blockbook/bchain"
    13  	"github.com/cryptohub-digital/blockbook/common"
    14  	"github.com/cryptohub-digital/blockbook/db"
    15  	"github.com/cryptohub-digital/blockbook/fiat"
    16  	"github.com/golang/glog"
    17  	"github.com/prometheus/client_golang/prometheus/promhttp"
    18  )
    19  
    20  // InternalServer is handle to internal http server
    21  type InternalServer struct {
    22  	htmlTemplates[InternalTemplateData]
    23  	https       *http.Server
    24  	certFiles   string
    25  	db          *db.RocksDB
    26  	txCache     *db.TxCache
    27  	chain       bchain.BlockChain
    28  	chainParser bchain.BlockChainParser
    29  	mempool     bchain.Mempool
    30  	is          *common.InternalState
    31  	api         *api.Worker
    32  }
    33  
    34  // NewInternalServer creates new internal http interface to blockbook and returns its handle
    35  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) {
    36  	api, err := api.NewWorker(db, chain, mempool, txCache, metrics, is, fiatRates)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  
    41  	addr, path := splitBinding(binding)
    42  	serveMux := http.NewServeMux()
    43  	https := &http.Server{
    44  		Addr:    addr,
    45  		Handler: serveMux,
    46  	}
    47  	s := &InternalServer{
    48  		htmlTemplates: htmlTemplates[InternalTemplateData]{
    49  			debug: true,
    50  		},
    51  		https:       https,
    52  		certFiles:   certFiles,
    53  		db:          db,
    54  		txCache:     txCache,
    55  		chain:       chain,
    56  		chainParser: chain.GetChainParser(),
    57  		mempool:     mempool,
    58  		is:          is,
    59  		api:         api,
    60  	}
    61  	s.htmlTemplates.newTemplateData = s.newTemplateData
    62  	s.htmlTemplates.newTemplateDataWithError = s.newTemplateDataWithError
    63  	s.htmlTemplates.parseTemplates = s.parseTemplates
    64  	s.templates = s.parseTemplates()
    65  
    66  	serveMux.Handle(path+"favicon.ico", http.FileServer(http.Dir("./static/")))
    67  	serveMux.HandleFunc(path+"metrics", promhttp.Handler().ServeHTTP)
    68  	serveMux.HandleFunc(path, s.index)
    69  	serveMux.HandleFunc(path+"admin", s.htmlTemplateHandler(s.adminIndex))
    70  	if s.chainParser.GetChainType() == bchain.ChainEthereumType {
    71  		serveMux.HandleFunc(path+"admin/internal-data-errors", s.htmlTemplateHandler(s.internalDataErrors))
    72  	}
    73  	return s, nil
    74  }
    75  
    76  // Run starts the server
    77  func (s *InternalServer) Run() error {
    78  	if s.certFiles == "" {
    79  		glog.Info("internal server: starting to listen on http://", s.https.Addr)
    80  		return s.https.ListenAndServe()
    81  	}
    82  	glog.Info("internal server: starting to listen on https://", s.https.Addr)
    83  	return s.https.ListenAndServeTLS(fmt.Sprint(s.certFiles, ".crt"), fmt.Sprint(s.certFiles, ".key"))
    84  }
    85  
    86  // Close closes the server
    87  func (s *InternalServer) Close() error {
    88  	glog.Infof("internal server: closing")
    89  	return s.https.Close()
    90  }
    91  
    92  // Shutdown shuts down the server
    93  func (s *InternalServer) Shutdown(ctx context.Context) error {
    94  	glog.Infof("internal server: shutdown")
    95  	return s.https.Shutdown(ctx)
    96  }
    97  
    98  func (s *InternalServer) index(w http.ResponseWriter, r *http.Request) {
    99  	si, err := s.api.GetSystemInfo(true)
   100  	if err != nil {
   101  		glog.Error(err)
   102  		w.WriteHeader(http.StatusInternalServerError)
   103  		return
   104  	}
   105  	buf, err := json.MarshalIndent(si, "", "    ")
   106  	if err != nil {
   107  		glog.Error(err)
   108  		w.WriteHeader(http.StatusInternalServerError)
   109  		return
   110  	}
   111  
   112  	w.Write(buf)
   113  }
   114  
   115  const (
   116  	adminIndexTpl = iota + errorInternalTpl + 1
   117  	adminInternalErrorsTpl
   118  
   119  	internalTplCount
   120  )
   121  
   122  // InternalTemplateData is used to transfer data to the templates
   123  type InternalTemplateData struct {
   124  	CoinName               string
   125  	CoinShortcut           string
   126  	CoinLabel              string
   127  	ChainType              bchain.ChainType
   128  	Error                  *api.APIError
   129  	InternalDataErrors     []db.BlockInternalDataError
   130  	RefetchingInternalData bool
   131  }
   132  
   133  func (s *InternalServer) newTemplateData(r *http.Request) *InternalTemplateData {
   134  	t := &InternalTemplateData{
   135  		CoinName:     s.is.Coin,
   136  		CoinShortcut: s.is.CoinShortcut,
   137  		CoinLabel:    s.is.CoinLabel,
   138  		ChainType:    s.chainParser.GetChainType(),
   139  	}
   140  	return t
   141  }
   142  
   143  func (s *InternalServer) newTemplateDataWithError(error *api.APIError, r *http.Request) *InternalTemplateData {
   144  	td := s.newTemplateData(r)
   145  	td.Error = error
   146  	return td
   147  }
   148  
   149  func (s *InternalServer) parseTemplates() []*template.Template {
   150  	templateFuncMap := template.FuncMap{
   151  		"formatUint32": formatUint32,
   152  	}
   153  	createTemplate := func(filenames ...string) *template.Template {
   154  		if len(filenames) == 0 {
   155  			panic("Missing templates")
   156  		}
   157  		return template.Must(template.New(filepath.Base(filenames[0])).Funcs(templateFuncMap).ParseFiles(filenames...))
   158  	}
   159  	t := make([]*template.Template, internalTplCount)
   160  	t[errorTpl] = createTemplate("./static/internal_templates/error.html", "./static/internal_templates/base.html")
   161  	t[errorInternalTpl] = createTemplate("./static/internal_templates/error.html", "./static/internal_templates/base.html")
   162  	t[adminIndexTpl] = createTemplate("./static/internal_templates/index.html", "./static/internal_templates/base.html")
   163  	t[adminInternalErrorsTpl] = createTemplate("./static/internal_templates/block_internal_data_errors.html", "./static/internal_templates/base.html")
   164  	return t
   165  }
   166  
   167  func (s *InternalServer) adminIndex(w http.ResponseWriter, r *http.Request) (tpl, *InternalTemplateData, error) {
   168  	data := s.newTemplateData(r)
   169  	return adminIndexTpl, data, nil
   170  }
   171  
   172  func (s *InternalServer) internalDataErrors(w http.ResponseWriter, r *http.Request) (tpl, *InternalTemplateData, error) {
   173  	if r.Method == http.MethodPost {
   174  		err := s.api.RefetchInternalData()
   175  		if err != nil {
   176  			return errorTpl, nil, err
   177  		}
   178  	}
   179  	data := s.newTemplateData(r)
   180  	internalErrors, err := s.db.GetBlockInternalDataErrorsEthereumType()
   181  	if err != nil {
   182  		return errorTpl, nil, err
   183  	}
   184  	data.InternalDataErrors = internalErrors
   185  	data.RefetchingInternalData = s.api.IsRefetchingInternalData()
   186  	return adminInternalErrorsTpl, data, nil
   187  }