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 }