mnd/console/server: endpoint with index.html added

Change-Id: Ic5154feaa995bf5c26c851024079f7e82612f306
This commit is contained in:
crawter 2021-01-21 18:52:19 +02:00 committed by Yehor Butko
parent 24d60384c5
commit 135d846aff
4 changed files with 83 additions and 55 deletions

View File

@ -133,32 +133,15 @@ func (controller *Nodes) Get(w http.ResponseWriter, r *http.Request) {
node, err := controller.service.Get(ctx, nodeID)
if err != nil {
controller.log.Error("get node not found error", zap.Error(err))
controller.serveError(w, http.StatusNotFound, ErrNodes.Wrap(err))
return
}
if err = json.NewEncoder(w).Encode(node); err != nil {
controller.log.Error("failed to write json response", zap.Error(err))
return
}
}
// List handles retrieving list of nodes.
func (controller *Nodes) List(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
defer mon.Task()(&ctx)(&err)
w.Header().Add("Content-Type", "application/json")
list, err := controller.service.List(ctx)
if err != nil {
controller.log.Error("list nodes internal error", zap.Error(err))
if nodes.ErrNoNode.Has(err) {
controller.serveError(w, http.StatusNotFound, ErrNodes.Wrap(err))
return
}
controller.serveError(w, http.StatusInternalServerError, ErrNodes.Wrap(err))
return
}
if err = json.NewEncoder(w).Encode(list); err != nil {
if err = json.NewEncoder(w).Encode(node); err != nil {
controller.log.Error("failed to write json response", zap.Error(err))
return
}

View File

@ -5,8 +5,10 @@ package server
import (
"context"
"html/template"
"net"
"net/http"
"path/filepath"
"github.com/gorilla/mux"
"github.com/zeebo/errs"
@ -39,6 +41,8 @@ type Server struct {
listener net.Listener
http http.Server
index *template.Template
}
// NewServer returns new instance of Multinode Dashboard http server.
@ -51,13 +55,14 @@ func NewServer(log *zap.Logger, config Config, nodes *nodes.Service, listener ne
}
router := mux.NewRouter()
fs := http.FileServer(http.Dir(server.config.StaticDir))
apiRouter := router.PathPrefix("/api/v0").Subrouter()
apiRouter.NotFoundHandler = controllers.NewNotFound(server.log)
nodesController := controllers.NewNodes(server.log, server.nodes)
nodesRouter := apiRouter.PathPrefix("/nodes").Subrouter()
nodesRouter.HandleFunc("", nodesController.Add).Methods(http.MethodPost)
nodesRouter.HandleFunc("", nodesController.List).Methods(http.MethodGet)
nodesRouter.HandleFunc("/infos", nodesController.ListInfos).Methods(http.MethodGet)
nodesRouter.HandleFunc("/infos/{satelliteID}", nodesController.ListInfosSatellite).Methods(http.MethodGet)
nodesRouter.HandleFunc("/trusted-satellites", nodesController.TrustedSatellites).Methods(http.MethodGet)
@ -65,6 +70,11 @@ func NewServer(log *zap.Logger, config Config, nodes *nodes.Service, listener ne
nodesRouter.HandleFunc("/{id}", nodesController.UpdateName).Methods(http.MethodPatch)
nodesRouter.HandleFunc("/{id}", nodesController.Delete).Methods(http.MethodDelete)
if server.config.StaticDir != "" {
router.PathPrefix("/static/").Handler(http.StripPrefix("/static", fs))
router.PathPrefix("/").HandlerFunc(server.appHandler)
}
server.http = http.Server{
Handler: router,
}
@ -72,10 +82,33 @@ func NewServer(log *zap.Logger, config Config, nodes *nodes.Service, listener ne
return &server, nil
}
// appHandler is web app http handler function.
func (server *Server) appHandler(w http.ResponseWriter, r *http.Request) {
header := w.Header()
header.Set("Content-Type", "text/html; charset=UTF-8")
header.Set("X-Content-Type-Options", "nosniff")
header.Set("Referrer-Policy", "same-origin")
if server.index == nil {
server.log.Error("index template is not set")
return
}
if err := server.index.Execute(w, nil); err != nil {
server.log.Error("index template could not be executed", zap.Error(Error.Wrap(err)))
return
}
}
// Run starts the server that host webapp and api endpoints.
func (server *Server) Run(ctx context.Context) (err error) {
ctx, cancel := context.WithCancel(ctx)
err = server.initializeTemplates()
if err != nil {
return Error.Wrap(err)
}
ctx, cancel := context.WithCancel(ctx)
var group errgroup.Group
group.Go(func() error {
@ -94,3 +127,13 @@ func (server *Server) Run(ctx context.Context) (err error) {
func (server *Server) Close() error {
return Error.Wrap(server.http.Close())
}
// initializeTemplates is used to initialize all templates.
func (server *Server) initializeTemplates() (err error) {
server.index, err = template.ParseFiles(filepath.Join(server.config.StaticDir, "dist", "index.html"))
if err != nil {
server.log.Error("dist folder is not generated. use 'npm run build' command", zap.Error(err))
}
return err
}

View File

@ -33,32 +33,33 @@ var ErrNoNode = errs.Class("no such node")
// Node is a representation of storagenode, that SNO could add to the Multinode Dashboard.
type Node struct {
ID storj.NodeID
ID storj.NodeID `json:"id"`
// APISecret is a secret issued by storagenode, that will be main auth mechanism in MND <-> SNO api.
APISecret []byte
PublicAddress string
Name string
APISecret []byte `json:"apiSecret"`
PublicAddress string `json:"publicAddress"`
Name string `json:"name"`
}
// NodeInfo contains basic node internal state.
type NodeInfo struct {
ID storj.NodeID
Name string
Version string
LastContact time.Time
DiskSpaceUsed int64
DiskSpaceLeft int64
BandwidthUsed int64
TotalEarned int64
ID storj.NodeID `json:"id"`
Name string `json:"name"`
Version string `json:"version"`
LastContact time.Time `json:"lastContact"`
DiskSpaceUsed int64 `json:"diskSpaceUsed"`
DiskSpaceLeft int64 `json:"diskSpaceLeft"`
BandwidthUsed int64 `json:"bandwidthUsed"`
TotalEarned int64 `json:"totalEarned"`
}
// NodeInfoSatellite contains satellite specific node internal state.
type NodeInfoSatellite struct {
ID storj.NodeID
Name string
Version string
LastContact time.Time
OnlineScore float64
AuditScore float64
SuspensionScore float64
ID storj.NodeID `json:"id"`
Name string `json:"name"`
Version string `json:"version"`
LastContact time.Time `json:"lastContact"`
OnlineScore float64 `json:"onlineScore"`
AuditScore float64 `json:"auditScore"`
SuspensionScore float64 `json:"suspensionScore"`
TotalEarned int64 `json:"totalEarned"`
}

View File

@ -66,18 +66,6 @@ func (service *Service) Get(ctx context.Context, id storj.NodeID) (_ Node, err e
}
// List retrieves list of all added nodes.
func (service *Service) List(ctx context.Context) (_ []Node, err error) {
defer mon.Task()(&ctx)(&err)
nodes, err := service.nodes.List(ctx)
if err != nil {
return nil, Error.Wrap(err)
}
return nodes, nil
}
// Remove removes node from the system.
func (service *Service) Remove(ctx context.Context, id storj.NodeID) (err error) {
defer mon.Task()(&ctx)(&err)
@ -90,6 +78,9 @@ func (service *Service) ListInfos(ctx context.Context) (_ []NodeInfo, err error)
nodes, err := service.nodes.List(ctx)
if err != nil {
if ErrNoNode.Has(err) {
return []NodeInfo{}, nil
}
return nil, Error.Wrap(err)
}
@ -172,6 +163,9 @@ func (service *Service) ListInfosSatellite(ctx context.Context, satelliteID stor
nodes, err := service.nodes.List(ctx)
if err != nil {
if ErrNoNode.Has(err) {
return []NodeInfoSatellite{}, nil
}
return nil, Error.Wrap(err)
}
@ -191,6 +185,7 @@ func (service *Service) ListInfosSatellite(ctx context.Context, satelliteID stor
}()
nodeClient := multinodepb.NewDRPCNodeClient(conn)
payoutClient := multinodepb.NewDRPCPayoutClient(conn)
header := &multinodepb.RequestHeader{
ApiKey: node.APISecret,
@ -214,6 +209,11 @@ func (service *Service) ListInfosSatellite(ctx context.Context, satelliteID stor
return NodeInfoSatellite{}, Error.Wrap(err)
}
earned, err := payoutClient.Earned(ctx, &multinodepb.EarnedRequest{Header: header})
if err != nil {
return NodeInfoSatellite{}, Error.Wrap(err)
}
return NodeInfoSatellite{
ID: node.ID,
Name: node.Name,
@ -222,6 +222,7 @@ func (service *Service) ListInfosSatellite(ctx context.Context, satelliteID stor
OnlineScore: rep.Online.Score,
AuditScore: rep.Audit.Score,
SuspensionScore: rep.Audit.SuspensionScore,
TotalEarned: earned.Total,
}, nil
}()
if err != nil {