multinode,web/multinode: use go:embed for assets
Go can now directly embed files without relying on external tools. This makes code use go:embed and avoid the external tooling. go:embed requires files to be present in the embedded directory, hence we need to add .keep to "dist" folder. We also add one to public/.keep, such that it won't be deleted when building multinode. Change-Id: I53ac3d5ac76e44f740d95221acf0da99fc256d42
This commit is contained in:
parent
fb7454ed9c
commit
28c9403702
5
Makefile
5
Makefile
@ -175,11 +175,6 @@ multinode-console:
|
||||
-u $(shell id -u):$(shell id -g) \
|
||||
node:${NODE_VERSION} \
|
||||
/bin/bash -c "npm ci && npm run build"
|
||||
# embed web assets into go
|
||||
go-bindata -prefix web/multinode/ -fs -o multinode/console/consoleassets/bindata.resource.go -pkg consoleassets web/multinode/dist/... web/multinode/static/...
|
||||
# configure existing go code to know about the new assets
|
||||
/usr/bin/env echo -e '\nfunc init() { FileSystem = AssetFile() }' >> multinode/console/consoleassets/bindata.resource.go
|
||||
gofmt -w -s multinode/console/consoleassets/bindata.resource.go
|
||||
|
||||
.PHONY: satellite-admin-ui
|
||||
satellite-admin-ui:
|
||||
|
@ -1,13 +0,0 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package consoleassets
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// FileSystem is nil by default, but when our Makefile generates
|
||||
// embedded resources via go-bindata, it will also drop an init method
|
||||
// that sets this to not nil.
|
||||
var FileSystem http.FileSystem
|
@ -6,8 +6,8 @@ package server
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
@ -54,7 +54,7 @@ type Server struct {
|
||||
log *zap.Logger
|
||||
listener net.Listener
|
||||
http http.Server
|
||||
assets http.FileSystem
|
||||
assets fs.FS
|
||||
|
||||
nodes *nodes.Service
|
||||
payouts *payouts.Service
|
||||
@ -62,12 +62,10 @@ type Server struct {
|
||||
bandwidth *bandwidth.Service
|
||||
storage *storage.Service
|
||||
reputation *reputation.Service
|
||||
|
||||
index *template.Template
|
||||
}
|
||||
|
||||
// NewServer returns new instance of Multinode Dashboard http server.
|
||||
func NewServer(log *zap.Logger, listener net.Listener, assets http.FileSystem, services Services) (*Server, error) {
|
||||
func NewServer(log *zap.Logger, listener net.Listener, assets fs.FS, services Services) (*Server, error) {
|
||||
server := Server{
|
||||
log: log,
|
||||
listener: listener,
|
||||
@ -134,11 +132,8 @@ func NewServer(log *zap.Logger, listener net.Listener, assets http.FileSystem, s
|
||||
reputationRouter := apiRouter.PathPrefix("/reputation").Subrouter()
|
||||
reputationRouter.HandleFunc("/satellites/{satelliteID}", reputationController.Stats)
|
||||
|
||||
if server.assets != nil {
|
||||
fs := http.FileServer(server.assets)
|
||||
router.PathPrefix("/static/").Handler(http.StripPrefix("/static", fs))
|
||||
router.PathPrefix("/").HandlerFunc(server.appHandler)
|
||||
}
|
||||
router.PathPrefix("/static").Handler(http.FileServer(http.FS(server.assets)))
|
||||
router.PathPrefix("/").HandlerFunc(server.appHandler)
|
||||
|
||||
server.http = http.Server{
|
||||
Handler: router,
|
||||
@ -155,24 +150,18 @@ func (server *Server) appHandler(w http.ResponseWriter, r *http.Request) {
|
||||
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")
|
||||
f, err := server.assets.Open("index.html")
|
||||
if err != nil {
|
||||
http.Error(w, `web/multinode unbuilt, run "npm install && npm run build" in web/multinode.`, http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
defer func() { _ = f.Close() }()
|
||||
|
||||
if err := server.index.Execute(w, nil); err != nil {
|
||||
server.log.Error("index template could not be executed", zap.Error(Error.Wrap(err)))
|
||||
return
|
||||
}
|
||||
_, _ = io.Copy(w, f)
|
||||
}
|
||||
|
||||
// Run starts the server that host webapp and api endpoints.
|
||||
func (server *Server) Run(ctx context.Context) (err error) {
|
||||
err = server.initializeTemplates()
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
var group errgroup.Group
|
||||
|
||||
@ -196,23 +185,3 @@ 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) {
|
||||
f, err := server.assets.Open("/dist/index.html")
|
||||
if err != nil {
|
||||
server.log.Error("dist folder is not generated. use 'npm run build' command", zap.Error(err))
|
||||
return nil
|
||||
}
|
||||
b, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
||||
server.index, err = template.New("index.html").Parse(string(b))
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -5,8 +5,10 @@ package multinode
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spacemonkeygo/monkit/v3"
|
||||
"go.uber.org/zap"
|
||||
@ -17,7 +19,6 @@ import (
|
||||
"storj.io/common/rpc"
|
||||
"storj.io/private/debug"
|
||||
"storj.io/storj/multinode/bandwidth"
|
||||
"storj.io/storj/multinode/console/consoleassets"
|
||||
"storj.io/storj/multinode/console/server"
|
||||
"storj.io/storj/multinode/nodes"
|
||||
"storj.io/storj/multinode/operators"
|
||||
@ -25,6 +26,7 @@ import (
|
||||
"storj.io/storj/multinode/reputation"
|
||||
"storj.io/storj/multinode/storage"
|
||||
"storj.io/storj/private/lifecycle"
|
||||
multinodeweb "storj.io/storj/web/multinode"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -175,10 +177,14 @@ func New(log *zap.Logger, full *identity.FullIdentity, config Config, db DB) (_
|
||||
return nil, err
|
||||
}
|
||||
|
||||
assets := consoleassets.FileSystem
|
||||
var assets fs.FS
|
||||
assets = multinodeweb.Assets
|
||||
// Use a custom directory instead of the embedded assets.
|
||||
if config.Console.StaticDir != "" {
|
||||
// a specific directory has been configured. use it
|
||||
assets = http.Dir(config.Console.StaticDir)
|
||||
// HACKFIX: Previous setups specify the directory for web/multinode,
|
||||
// instead of the actual built data. This is for backwards compatibility.
|
||||
distDir := filepath.Join(config.Console.StaticDir, "dist")
|
||||
assets = os.DirFS(distDir)
|
||||
}
|
||||
|
||||
peer.Console.Endpoint, err = server.NewServer(
|
||||
|
23
web/multinode/assets.go
Normal file
23
web/multinode/assets.go
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (C) 2022 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package multinodeweb
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
)
|
||||
|
||||
//go:embed dist/*
|
||||
var assets embed.FS
|
||||
|
||||
// Assets contains either the built multinode from web/multinode/dist directory
|
||||
// or it is empty.
|
||||
var Assets = func() fs.FS {
|
||||
dist, err := fs.Sub(assets, "dist")
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("invalid embedding: %w", err))
|
||||
}
|
||||
return dist
|
||||
}()
|
0
web/multinode/dist/.keep
vendored
Normal file
0
web/multinode/dist/.keep
vendored
Normal file
0
web/multinode/public/.keep
Normal file
0
web/multinode/public/.keep
Normal file
@ -4,17 +4,14 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
publicPath: '/static/dist',
|
||||
productionSourceMap: false,
|
||||
parallel: true,
|
||||
lintOnSave: false, // disables eslint for builds
|
||||
assetsDir: "static",
|
||||
configureWebpack: {
|
||||
plugins: [],
|
||||
},
|
||||
chainWebpack: config => {
|
||||
config.output.chunkFilename('js/vendors_[hash].js');
|
||||
config.output.filename('js/app_[hash].js');
|
||||
|
||||
config.resolve.alias
|
||||
.set('@', path.resolve('src'));
|
||||
|
||||
@ -22,7 +19,6 @@ module.exports = {
|
||||
.plugin('html')
|
||||
.tap(args => {
|
||||
args[0].template = './index.html';
|
||||
|
||||
return args;
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user