Merge 'main' branch.

Change-Id: I6e8162d1a6caf75e89c9f9c9f9522730aebf83ae
This commit is contained in:
Michał Niewrzał 2021-01-11 10:26:58 +01:00
commit ec88d21a3c
103 changed files with 4983 additions and 1687 deletions

View File

@ -73,6 +73,11 @@ build-storagenode-npm:
##@ Simulator
# Allow the caller to set GATEWAYPATH if desired. This controls where the new
# go module is created to install the specific gateway version.
ifndef GATEWAYPATH
GATEWAYPATH=.build/gateway-tmp
endif
.PHONY: install-sim
install-sim: ## install storj-sim
@echo "Running ${@}"
@ -86,9 +91,9 @@ install-sim: ## install storj-sim
storj.io/storj/cmd/certificates
## install exact version of storj/gateway
mkdir -p .build/gateway-tmp
-cd .build/gateway-tmp && go mod init gatewaybuild
cd .build/gateway-tmp && GO111MODULE=on go get storj.io/gateway@multipart-upload
mkdir -p ${GATEWAYPATH}
-cd ${GATEWAYPATH} && go mod init gatewaybuild
cd ${GATEWAYPATH} && GO111MODULE=on go get storj.io/gateway@multipart-upload
##@ Test

View File

@ -133,7 +133,7 @@ func init() {
process.Bind(dashboardCmd, &dashboardCfg, defaults, cfgstruct.ConfDir(defaultDiagDir))
process.Bind(gracefulExitInitCmd, &diagCfg, defaults, cfgstruct.ConfDir(defaultDiagDir))
process.Bind(gracefulExitStatusCmd, &diagCfg, defaults, cfgstruct.ConfDir(defaultDiagDir))
process.Bind(issueAPITokenCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(issueAPITokenCmd, &diagCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
}
func cmdRun(cmd *cobra.Command, args []string) (err error) {
@ -312,14 +312,14 @@ func cmdIssue(cmd *cobra.Command, args []string) (err error) {
err = errs.Combine(err, db.Close())
}()
service := apikeys.NewService(db.Secret())
service := apikeys.NewService(db.APIKeys())
apiKey, err := service.Issue(ctx)
if err != nil {
return errs.New("Error while trying to issue new api key: %v", err)
}
fmt.Print(apiKey.Secret.String())
fmt.Println(apiKey.Secret.String())
return
}

View File

@ -26,6 +26,7 @@ type Flags struct {
Identities int
IsDev bool
FailFast bool
OnlyEnv bool // only do things necessary for loading env vars
@ -64,7 +65,8 @@ func main() {
rootCmd.PersistentFlags().IntVarP(&flags.Identities, "identities", "", 10, "number of identities to create")
rootCmd.PersistentFlags().BoolVarP(&printCommands, "print-commands", "x", false, "print commands as they are run")
rootCmd.PersistentFlags().BoolVarP(&flags.IsDev, "dev", "", false, "use configuration values tuned for development")
rootCmd.PersistentFlags().BoolVarP(&flags.IsDev, "dev", "", true, "use configuration values tuned for development")
rootCmd.PersistentFlags().BoolVarP(&flags.FailFast, "failfast", "", true, "stop all processes when one of the processes fails")
rootCmd.PersistentFlags().StringVarP(&flags.Postgres, "postgres", "", os.Getenv("STORJ_SIM_POSTGRES"), "connection string for postgres (defaults to STORJ_SIM_POSTGRES)")
rootCmd.PersistentFlags().StringVarP(&flags.Redis, "redis", "", os.Getenv("STORJ_SIM_REDIS"), "connection string for redis e.g. 127.0.0.1:6379 (defaults to STORJ_SIM_REDIS)")

View File

@ -169,8 +169,14 @@ func networkTest(flags *Flags, command string, args []string) error {
ctx, cancel := NewCLIContext(context.Background())
var group errgroup.Group
processes.Start(ctx, &group, "run")
var group *errgroup.Group
if processes.FailFast {
group, ctx = errgroup.WithContext(ctx)
} else {
group = &errgroup.Group{}
}
processes.Start(ctx, group, "run")
for _, process := range processes.List {
process.Status.Started.Wait(ctx)
@ -218,14 +224,19 @@ func newNetwork(flags *Flags) (*Processes, error) {
common := []string{"--metrics.app-suffix", "sim", "--log.level", "debug", "--config-dir", dir}
if flags.IsDev {
common = append(common, "--defaults", "dev")
} else {
common = append(common, "--defaults", "release")
}
for command, args := range all {
all[command] = append(append(common, command), args...)
full := append([]string{}, common...)
full = append(full, command)
full = append(full, args...)
all[command] = full
}
return all
}
processes := NewProcesses(flags.Directory)
processes := NewProcesses(flags.Directory, flags.FailFast)
var host = flags.Host
versioncontrol := processes.New(Info{
@ -315,13 +326,17 @@ func newNetwork(flags *Flags) (*Processes, error) {
apiProcess.Arguments = withCommon(apiProcess.Directory, Arguments{
"setup": {
"--identity-dir", apiProcess.Directory,
"--console.address", net.JoinHostPort(host, port(satellitePeer, i, publicHTTP)),
"--console.static-dir", filepath.Join(storjRoot, "web/satellite/"),
"--console.auth-token-secret", "my-suppa-secret-key",
"--console.open-registration-enabled",
"--console.rate-limit.burst", "100",
"--marketing.base-url", "",
"--marketing.address", net.JoinHostPort(host, port(satellitePeer, i, privateHTTP)),
"--marketing.static-dir", filepath.Join(storjRoot, "web/marketing/"),
"--server.address", apiProcess.Address,
"--server.private-address", net.JoinHostPort(host, port(satellitePeer, i, privateRPC)),
@ -635,7 +650,7 @@ func newNetwork(flags *Flags) (*Processes, error) {
}
func identitySetup(network *Processes) (*Processes, error) {
processes := NewProcesses(network.Directory)
processes := NewProcesses(network.Directory, network.FailFast)
for _, process := range network.List {
if process.Info.Executable == "gateway" || process.Info.Executable == "redis-server" {

View File

@ -11,6 +11,7 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"syscall"
@ -29,25 +30,33 @@ type Processes struct {
Directory string
List []*Process
FailFast bool
MaxStartupWait time.Duration
}
const storjSimMaxLineLen = 10000
// NewProcesses returns a group of processes.
func NewProcesses(dir string) *Processes {
func NewProcesses(dir string, failfast bool) *Processes {
return &Processes{
Output: NewPrefixWriter("sim", storjSimMaxLineLen, os.Stdout),
Directory: dir,
List: nil,
FailFast: failfast,
MaxStartupWait: time.Minute,
}
}
// Exec executes a command on all processes.
func (processes *Processes) Exec(ctx context.Context, command string) error {
var group errgroup.Group
processes.Start(ctx, &group, command)
var group *errgroup.Group
if processes.FailFast {
group, ctx = errgroup.WithContext(ctx)
} else {
group = &errgroup.Group{}
}
processes.Start(ctx, group, command)
return group.Wait()
}
@ -221,7 +230,7 @@ func (process *Process) Exec(ctx context.Context, command string) (err error) {
executable := process.Executable
// use executable inside the directory, if it exists
localExecutable := filepath.Join(process.Directory, executable)
localExecutable := exe(filepath.Join(process.Directory, executable))
if _, err := os.Lstat(localExecutable); !os.IsNotExist(err) {
executable = localExecutable
}
@ -335,3 +344,10 @@ func tryConnect(address string) bool {
// Close closes process resources.
func (process *Process) Close() error { return nil }
func exe(name string) string {
if runtime.GOOS == "windows" {
return name + ".exe"
}
return name
}

View File

@ -132,7 +132,7 @@ func parseAccess(access string) (sa string, apiKey string, ea string, err error)
}
func accessRegister(cmd *cobra.Command, args []string) (err error) {
access, err := getAccessFromArgZeroOrConfig(inspectCfg, args)
access, err := getAccessFromArgZeroOrConfig(registerCfg.AccessConfig, args)
if err != nil {
return errs.New("no access specified: %w", err)
}
@ -168,7 +168,7 @@ func accessRegister(cmd *cobra.Command, args []string) (err error) {
func getAccessFromArgZeroOrConfig(config AccessConfig, args []string) (access *uplink.Access, err error) {
if len(args) != 0 {
access, err = inspectCfg.GetNamedAccess(args[0])
access, err = config.GetNamedAccess(args[0])
if err != nil {
return nil, err
}
@ -177,7 +177,7 @@ func getAccessFromArgZeroOrConfig(config AccessConfig, args []string) (access *u
}
return uplink.ParseAccess(args[0])
}
return inspectCfg.GetAccess()
return config.GetAccess()
}
// RegisterAccess registers an access grant with a Gateway Authorization Service.

2
go.mod
View File

@ -46,7 +46,7 @@ require (
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e
google.golang.org/api v0.20.0 // indirect
google.golang.org/protobuf v1.25.0 // indirect
storj.io/common v0.0.0-20201210184814-6206aefd1d48
storj.io/common v0.0.0-20210104180112-e8500e1c37a0
storj.io/drpc v0.0.16
storj.io/monkit-jaeger v0.0.0-20200518165323-80778fc3f91b
storj.io/private v0.0.0-20201126162939-6fbb1e924f51

2
go.sum
View File

@ -810,6 +810,8 @@ storj.io/common v0.0.0-20200424175742-65ac59022f4f/go.mod h1:pZyXiIE7bGETIRXtfs0
storj.io/common v0.0.0-20201026135900-1aaeec90670b/go.mod h1:GqdmNf3fLm2UZX/7Zr0BLFCJ4gFjgm6eHrk/fnmr5jQ=
storj.io/common v0.0.0-20201210184814-6206aefd1d48 h1:bxIYHG96eFQNsEazsICfiEHjFwo1YqqbXkGfg72d2mg=
storj.io/common v0.0.0-20201210184814-6206aefd1d48/go.mod h1:6sepaQTRLuygvA+GNPzdgRPOB1+wFfjde76KBWofbMY=
storj.io/common v0.0.0-20210104180112-e8500e1c37a0 h1:3EisqXNx2mjd1g+oBSlz1z5s3X7UFc+pXst2aNN/1m8=
storj.io/common v0.0.0-20210104180112-e8500e1c37a0/go.mod h1:GhZn7vlakLMJBMePwaMvaNUS45FhqMTVWzAn7dZxLOg=
storj.io/drpc v0.0.11/go.mod h1:TiFc2obNjL9/3isMW1Rpxjy8V9uE0B2HMeMFGiiI7Iw=
storj.io/drpc v0.0.14/go.mod h1:82nfl+6YwRwF6UG31cEWWUqv/FaKvP5SGqUvoqTxCMA=
storj.io/drpc v0.0.16 h1:9sxypc5lKi/0D69cR21BR0S21+IvXfON8L5nXMVNTwQ=

View File

@ -13,6 +13,7 @@ import (
"storj.io/common/storj"
"storj.io/storj/multinode/nodes"
"storj.io/storj/private/multinodeauth"
)
var (
@ -59,13 +60,13 @@ func (controller *Nodes) Add(w http.ResponseWriter, r *http.Request) {
return
}
apiSecret, err := nodes.APISecretFromBase64(payload.APISecret)
apiSecret, err := multinodeauth.SecretFromBase64(payload.APISecret)
if err != nil {
controller.serveError(w, http.StatusBadRequest, ErrNodes.Wrap(err))
return
}
if err = controller.service.Add(ctx, id, apiSecret, payload.PublicAddress); err != nil {
if err = controller.service.Add(ctx, id, apiSecret[:], payload.PublicAddress); err != nil {
// TODO: add more error checks in future, like bad payload if address is invalid or unauthorized if secret invalid.
controller.log.Error("add node internal error", zap.Error(err))
controller.serveError(w, http.StatusInternalServerError, ErrNodes.Wrap(err))
@ -150,14 +151,14 @@ func (controller *Nodes) List(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
nodes, err := controller.service.List(ctx)
list, err := controller.service.List(ctx)
if err != nil {
controller.log.Error("list nodes internal error", zap.Error(err))
controller.serveError(w, http.StatusInternalServerError, ErrNodes.Wrap(err))
return
}
if err = json.NewEncoder(w).Encode(nodes); err != nil {
if err = json.NewEncoder(w).Encode(list); err != nil {
controller.log.Error("failed to write json response", zap.Error(err))
return
}
@ -193,6 +194,75 @@ func (controller *Nodes) Delete(w http.ResponseWriter, r *http.Request) {
}
}
// ListInfos handles node basic info list retrieval.
func (controller *Nodes) ListInfos(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
defer mon.Task()(&ctx)(&err)
w.Header().Add("Content-Type", "application/json")
infos, err := controller.service.ListInfos(ctx)
if err != nil {
controller.log.Error("list node infos internal error", zap.Error(err))
controller.serveError(w, http.StatusInternalServerError, ErrNodes.Wrap(err))
return
}
if err = json.NewEncoder(w).Encode(infos); err != nil {
controller.log.Error("failed to write json response", zap.Error(err))
return
}
}
// ListInfosSatellite handles node satellite specific info list retrieval.
func (controller *Nodes) ListInfosSatellite(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
defer mon.Task()(&ctx)(&err)
w.Header().Add("Content-Type", "application/json")
vars := mux.Vars(r)
satelliteID, err := storj.NodeIDFromString(vars["satelliteID"])
if err != nil {
controller.serveError(w, http.StatusBadRequest, ErrNodes.Wrap(err))
return
}
infos, err := controller.service.ListInfosSatellite(ctx, satelliteID)
if err != nil {
controller.log.Error("list node satellite infos internal error", zap.Error(err))
controller.serveError(w, http.StatusInternalServerError, ErrNodes.Wrap(err))
return
}
if err = json.NewEncoder(w).Encode(infos); err != nil {
controller.log.Error("failed to write json response", zap.Error(err))
return
}
}
// TrustedSatellites handles retrieval of unique trusted satellites node urls list.
func (controller *Nodes) TrustedSatellites(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
defer mon.Task()(&ctx)(&err)
nodeURLs, err := controller.service.TrustedSatellites(ctx)
if err != nil {
controller.log.Error("list node trusted satellites internal error", zap.Error(err))
controller.serveError(w, http.StatusInternalServerError, ErrNodes.Wrap(err))
return
}
if err = json.NewEncoder(w).Encode(nodeURLs); err != nil {
controller.log.Error("failed to write json response", zap.Error(err))
return
}
}
// serveError set http statuses and send json error.
func (controller *Nodes) serveError(w http.ResponseWriter, status int, err error) {
w.WriteHeader(status)

View File

@ -58,6 +58,9 @@ func NewServer(log *zap.Logger, config Config, nodes *nodes.Service, listener ne
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)
nodesRouter.HandleFunc("/{id}", nodesController.Get).Methods(http.MethodGet)
nodesRouter.HandleFunc("/{id}", nodesController.UpdateName).Methods(http.MethodPatch)
nodesRouter.HandleFunc("/{id}", nodesController.Delete).Methods(http.MethodDelete)

View File

@ -5,15 +5,13 @@ package nodes
import (
"context"
"encoding/base64"
"time"
"github.com/zeebo/errs"
"storj.io/common/storj"
)
// TODO: should this file be placed outside of console in nodes package?
// DB exposes needed by MND NodesDB functionality.
//
// architecture: Database
@ -42,7 +40,24 @@ type Node struct {
Name string
}
// APISecretFromBase64 decodes API secret from base 64 string.
func APISecretFromBase64(s string) ([]byte, error) {
return base64.URLEncoding.DecodeString(s)
// 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
}
// 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
}

View File

@ -4,13 +4,16 @@
package nodes
import (
"bytes"
"context"
"github.com/spacemonkeygo/monkit/v3"
"github.com/zeebo/errs"
"go.uber.org/zap"
"storj.io/common/rpc"
"storj.io/common/storj"
"storj.io/storj/private/multinodepb"
)
var (
@ -25,13 +28,15 @@ var (
// architecture: Service
type Service struct {
log *zap.Logger
dialer rpc.Dialer
nodes DB
}
// NewService creates new instance of Service.
func NewService(log *zap.Logger, nodes DB) *Service {
func NewService(log *zap.Logger, dialer rpc.Dialer, nodes DB) *Service {
return &Service{
log: log,
dialer: dialer,
nodes: nodes,
}
}
@ -78,3 +83,227 @@ func (service *Service) Remove(ctx context.Context, id storj.NodeID) (err error)
defer mon.Task()(&ctx)(&err)
return Error.Wrap(service.nodes.Remove(ctx, id))
}
// ListInfos queries node basic info from all nodes via rpc.
func (service *Service) ListInfos(ctx context.Context) (_ []NodeInfo, err error) {
defer mon.Task()(&ctx)(&err)
nodes, err := service.nodes.List(ctx)
if err != nil {
return nil, Error.Wrap(err)
}
var infos []NodeInfo
for _, node := range nodes {
info, err := func() (_ NodeInfo, err error) {
conn, err := service.dialer.DialNodeURL(ctx, storj.NodeURL{
ID: node.ID,
Address: node.PublicAddress,
})
if err != nil {
return NodeInfo{}, Error.Wrap(err)
}
defer func() {
err = errs.Combine(err, conn.Close())
}()
nodeClient := multinodepb.NewDRPCNodeClient(conn)
storageClient := multinodepb.NewDRPCStorageClient(conn)
bandwidthClient := multinodepb.NewDRPCBandwidthClient(conn)
header := &multinodepb.RequestHeader{
ApiKey: node.APISecret,
}
nodeVersion, err := nodeClient.Version(ctx, &multinodepb.VersionRequest{Header: header})
if err != nil {
return NodeInfo{}, Error.Wrap(err)
}
lastContact, err := nodeClient.LastContact(ctx, &multinodepb.LastContactRequest{Header: header})
if err != nil {
return NodeInfo{}, Error.Wrap(err)
}
diskSpace, err := storageClient.DiskSpace(ctx, &multinodepb.DiskSpaceRequest{Header: header})
if err != nil {
return NodeInfo{}, Error.Wrap(err)
}
bandwidthSummaryRequest := &multinodepb.BandwidthMonthSummaryRequest{
Header: header,
}
bandwidthSummary, err := bandwidthClient.MonthSummary(ctx, bandwidthSummaryRequest)
if err != nil {
return NodeInfo{}, Error.Wrap(err)
}
return NodeInfo{
ID: node.ID,
Name: node.Name,
Version: nodeVersion.Version,
LastContact: lastContact.LastContact,
DiskSpaceUsed: diskSpace.GetUsedPieces() + diskSpace.GetUsedTrash(),
DiskSpaceLeft: diskSpace.GetAvailable(),
BandwidthUsed: bandwidthSummary.GetUsed(),
}, nil
}()
if err != nil {
return nil, Error.Wrap(err)
}
infos = append(infos, info)
}
return infos, nil
}
// ListInfosSatellite queries node satellite specific info from all nodes via rpc.
func (service *Service) ListInfosSatellite(ctx context.Context, satelliteID storj.NodeID) (_ []NodeInfoSatellite, err error) {
defer mon.Task()(&ctx)(&err)
nodes, err := service.nodes.List(ctx)
if err != nil {
return nil, Error.Wrap(err)
}
var infos []NodeInfoSatellite
for _, node := range nodes {
info, err := func() (_ NodeInfoSatellite, err error) {
conn, err := service.dialer.DialNodeURL(ctx, storj.NodeURL{
ID: node.ID,
Address: node.PublicAddress,
})
if err != nil {
return NodeInfoSatellite{}, Error.Wrap(err)
}
defer func() {
err = errs.Combine(err, conn.Close())
}()
nodeClient := multinodepb.NewDRPCNodeClient(conn)
header := &multinodepb.RequestHeader{
ApiKey: node.APISecret,
}
nodeVersion, err := nodeClient.Version(ctx, &multinodepb.VersionRequest{Header: header})
if err != nil {
return NodeInfoSatellite{}, Error.Wrap(err)
}
lastContact, err := nodeClient.LastContact(ctx, &multinodepb.LastContactRequest{Header: header})
if err != nil {
return NodeInfoSatellite{}, Error.Wrap(err)
}
rep, err := nodeClient.Reputation(ctx, &multinodepb.ReputationRequest{
Header: header,
SatelliteId: satelliteID,
})
if err != nil {
return NodeInfoSatellite{}, Error.Wrap(err)
}
return NodeInfoSatellite{
ID: node.ID,
Name: node.Name,
Version: nodeVersion.Version,
LastContact: lastContact.LastContact,
OnlineScore: rep.Online.Score,
AuditScore: rep.Audit.Score,
SuspensionScore: rep.Audit.SuspensionScore,
}, nil
}()
if err != nil {
return nil, Error.Wrap(err)
}
infos = append(infos, info)
}
return infos, nil
}
// TrustedSatellites returns list of unique trusted satellites node urls.
func (service *Service) TrustedSatellites(ctx context.Context) (_ storj.NodeURLs, err error) {
defer mon.Task()(&ctx)(&err)
nodes, err := service.nodes.List(ctx)
if err != nil {
return nil, Error.Wrap(err)
}
var trustedSatellites storj.NodeURLs
for _, node := range nodes {
nodeURLs, err := service.trustedSatellites(ctx, node)
if err != nil {
return nil, Error.Wrap(err)
}
trustedSatellites = appendUniqueNodeURLs(trustedSatellites, nodeURLs)
}
return trustedSatellites, nil
}
// trustedSatellites retrieves list of trusted satellites node urls for a node.
func (service *Service) trustedSatellites(ctx context.Context, node Node) (_ storj.NodeURLs, err error) {
defer mon.Task()(&ctx)(&err)
conn, err := service.dialer.DialNodeURL(ctx, storj.NodeURL{
ID: node.ID,
Address: node.PublicAddress,
})
if err != nil {
return nil, Error.Wrap(err)
}
defer func() {
err = errs.Combine(err, conn.Close())
}()
nodeClient := multinodepb.NewDRPCNodeClient(conn)
header := &multinodepb.RequestHeader{
ApiKey: node.APISecret,
}
resp, err := nodeClient.TrustedSatellites(ctx, &multinodepb.TrustedSatellitesRequest{Header: header})
if err != nil {
return nil, Error.Wrap(err)
}
var nodeURLs storj.NodeURLs
for _, url := range resp.TrustedSatellites {
nodeURLs = append(nodeURLs, storj.NodeURL{
ID: url.NodeId,
Address: url.GetAddress(),
})
}
return nodeURLs, nil
}
// appendUniqueNodeURLs appends unique node urls from incoming slice.
func appendUniqueNodeURLs(slice storj.NodeURLs, nodeURLs storj.NodeURLs) storj.NodeURLs {
for _, nodeURL := range nodeURLs {
slice = appendUniqueNodeURL(slice, nodeURL)
}
return slice
}
// appendUniqueNodeURL appends node url if it is unique.
func appendUniqueNodeURL(slice storj.NodeURLs, nodeURL storj.NodeURL) storj.NodeURLs {
for _, existing := range slice {
if bytes.Equal(existing.ID.Bytes(), nodeURL.ID.Bytes()) {
return slice
}
}
slice = append(slice, nodeURL)
return slice
}

View File

@ -12,6 +12,8 @@ import (
"golang.org/x/sync/errgroup"
"storj.io/common/identity"
"storj.io/common/peertls/tlsopts"
"storj.io/common/rpc"
"storj.io/private/debug"
"storj.io/storj/multinode/console"
"storj.io/storj/multinode/console/server"
@ -55,6 +57,8 @@ type Peer struct {
Identity *identity.FullIdentity
DB DB
Dialer rpc.Dialer
// contains logic of nodes domain.
Nodes struct {
Service *nodes.Service
@ -78,9 +82,22 @@ func New(log *zap.Logger, full *identity.FullIdentity, config Config, db DB) (_
Servers: lifecycle.NewGroup(log.Named("servers")),
}
tlsConfig := tlsopts.Config{
UsePeerCAWhitelist: false,
PeerIDVersions: "0",
}
tlsOptions, err := tlsopts.NewOptions(peer.Identity, tlsConfig, nil)
if err != nil {
return nil, err
}
peer.Dialer = rpc.NewDefaultDialer(tlsOptions)
{ // nodes setup
peer.Nodes.Service = nodes.NewService(
peer.Log.Named("nodes:service"),
peer.Dialer,
peer.DB.Nodes(),
)
}

View File

@ -1,579 +0,0 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: diskspace.proto
package multinodepb
import (
context "context"
fmt "fmt"
math "math"
time "time"
proto "github.com/gogo/protobuf/proto"
drpc "storj.io/drpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
var _ = time.Kitchen
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
type GetDiskSpaceRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetDiskSpaceRequest) Reset() { *m = GetDiskSpaceRequest{} }
func (m *GetDiskSpaceRequest) String() string { return proto.CompactTextString(m) }
func (*GetDiskSpaceRequest) ProtoMessage() {}
func (*GetDiskSpaceRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_9d1904cedee84a32, []int{0}
}
func (m *GetDiskSpaceRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetDiskSpaceRequest.Unmarshal(m, b)
}
func (m *GetDiskSpaceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetDiskSpaceRequest.Marshal(b, m, deterministic)
}
func (m *GetDiskSpaceRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetDiskSpaceRequest.Merge(m, src)
}
func (m *GetDiskSpaceRequest) XXX_Size() int {
return xxx_messageInfo_GetDiskSpaceRequest.Size(m)
}
func (m *GetDiskSpaceRequest) XXX_DiscardUnknown() {
xxx_messageInfo_GetDiskSpaceRequest.DiscardUnknown(m)
}
var xxx_messageInfo_GetDiskSpaceRequest proto.InternalMessageInfo
type GetDiskSpaceResponse struct {
DiskSpace *DiskSpace `protobuf:"bytes,1,opt,name=disk_space,json=diskSpace,proto3" json:"disk_space,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetDiskSpaceResponse) Reset() { *m = GetDiskSpaceResponse{} }
func (m *GetDiskSpaceResponse) String() string { return proto.CompactTextString(m) }
func (*GetDiskSpaceResponse) ProtoMessage() {}
func (*GetDiskSpaceResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_9d1904cedee84a32, []int{1}
}
func (m *GetDiskSpaceResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetDiskSpaceResponse.Unmarshal(m, b)
}
func (m *GetDiskSpaceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetDiskSpaceResponse.Marshal(b, m, deterministic)
}
func (m *GetDiskSpaceResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetDiskSpaceResponse.Merge(m, src)
}
func (m *GetDiskSpaceResponse) XXX_Size() int {
return xxx_messageInfo_GetDiskSpaceResponse.Size(m)
}
func (m *GetDiskSpaceResponse) XXX_DiscardUnknown() {
xxx_messageInfo_GetDiskSpaceResponse.DiscardUnknown(m)
}
var xxx_messageInfo_GetDiskSpaceResponse proto.InternalMessageInfo
func (m *GetDiskSpaceResponse) GetDiskSpace() *DiskSpace {
if m != nil {
return m.DiskSpace
}
return nil
}
// DiskSpace stores all info about storagenode disk space.
type DiskSpace struct {
Used int64 `protobuf:"varint,1,opt,name=used,proto3" json:"used,omitempty"`
Available int64 `protobuf:"varint,2,opt,name=available,proto3" json:"available,omitempty"`
Trash int64 `protobuf:"varint,3,opt,name=trash,proto3" json:"trash,omitempty"`
Overused int64 `protobuf:"varint,4,opt,name=overused,proto3" json:"overused,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DiskSpace) Reset() { *m = DiskSpace{} }
func (m *DiskSpace) String() string { return proto.CompactTextString(m) }
func (*DiskSpace) ProtoMessage() {}
func (*DiskSpace) Descriptor() ([]byte, []int) {
return fileDescriptor_9d1904cedee84a32, []int{2}
}
func (m *DiskSpace) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DiskSpace.Unmarshal(m, b)
}
func (m *DiskSpace) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_DiskSpace.Marshal(b, m, deterministic)
}
func (m *DiskSpace) XXX_Merge(src proto.Message) {
xxx_messageInfo_DiskSpace.Merge(m, src)
}
func (m *DiskSpace) XXX_Size() int {
return xxx_messageInfo_DiskSpace.Size(m)
}
func (m *DiskSpace) XXX_DiscardUnknown() {
xxx_messageInfo_DiskSpace.DiscardUnknown(m)
}
var xxx_messageInfo_DiskSpace proto.InternalMessageInfo
func (m *DiskSpace) GetUsed() int64 {
if m != nil {
return m.Used
}
return 0
}
func (m *DiskSpace) GetAvailable() int64 {
if m != nil {
return m.Available
}
return 0
}
func (m *DiskSpace) GetTrash() int64 {
if m != nil {
return m.Trash
}
return 0
}
func (m *DiskSpace) GetOverused() int64 {
if m != nil {
return m.Overused
}
return 0
}
type DailyStorageUsageRequest struct {
From time.Time `protobuf:"bytes,1,opt,name=from,proto3,stdtime" json:"from"`
To time.Time `protobuf:"bytes,2,opt,name=to,proto3,stdtime" json:"to"`
SatelliteId NodeID `protobuf:"bytes,3,opt,name=satellite_id,json=satelliteId,proto3,customtype=NodeID" json:"satellite_id"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DailyStorageUsageRequest) Reset() { *m = DailyStorageUsageRequest{} }
func (m *DailyStorageUsageRequest) String() string { return proto.CompactTextString(m) }
func (*DailyStorageUsageRequest) ProtoMessage() {}
func (*DailyStorageUsageRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_9d1904cedee84a32, []int{3}
}
func (m *DailyStorageUsageRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DailyStorageUsageRequest.Unmarshal(m, b)
}
func (m *DailyStorageUsageRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_DailyStorageUsageRequest.Marshal(b, m, deterministic)
}
func (m *DailyStorageUsageRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_DailyStorageUsageRequest.Merge(m, src)
}
func (m *DailyStorageUsageRequest) XXX_Size() int {
return xxx_messageInfo_DailyStorageUsageRequest.Size(m)
}
func (m *DailyStorageUsageRequest) XXX_DiscardUnknown() {
xxx_messageInfo_DailyStorageUsageRequest.DiscardUnknown(m)
}
var xxx_messageInfo_DailyStorageUsageRequest proto.InternalMessageInfo
func (m *DailyStorageUsageRequest) GetFrom() time.Time {
if m != nil {
return m.From
}
return time.Time{}
}
func (m *DailyStorageUsageRequest) GetTo() time.Time {
if m != nil {
return m.To
}
return time.Time{}
}
type DailyStorageUsageResponse struct {
NodeId []byte `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"`
DailyStorageUsage []*DailyStorageUsageResponse_StorageUsage `protobuf:"bytes,2,rep,name=daily_storage_usage,json=dailyStorageUsage,proto3" json:"daily_storage_usage,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DailyStorageUsageResponse) Reset() { *m = DailyStorageUsageResponse{} }
func (m *DailyStorageUsageResponse) String() string { return proto.CompactTextString(m) }
func (*DailyStorageUsageResponse) ProtoMessage() {}
func (*DailyStorageUsageResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_9d1904cedee84a32, []int{4}
}
func (m *DailyStorageUsageResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DailyStorageUsageResponse.Unmarshal(m, b)
}
func (m *DailyStorageUsageResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_DailyStorageUsageResponse.Marshal(b, m, deterministic)
}
func (m *DailyStorageUsageResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_DailyStorageUsageResponse.Merge(m, src)
}
func (m *DailyStorageUsageResponse) XXX_Size() int {
return xxx_messageInfo_DailyStorageUsageResponse.Size(m)
}
func (m *DailyStorageUsageResponse) XXX_DiscardUnknown() {
xxx_messageInfo_DailyStorageUsageResponse.DiscardUnknown(m)
}
var xxx_messageInfo_DailyStorageUsageResponse proto.InternalMessageInfo
func (m *DailyStorageUsageResponse) GetNodeId() []byte {
if m != nil {
return m.NodeId
}
return nil
}
func (m *DailyStorageUsageResponse) GetDailyStorageUsage() []*DailyStorageUsageResponse_StorageUsage {
if m != nil {
return m.DailyStorageUsage
}
return nil
}
type DailyStorageUsageResponse_StorageUsage struct {
AtRestTotal float64 `protobuf:"fixed64,1,opt,name=at_rest_total,json=atRestTotal,proto3" json:"at_rest_total,omitempty"`
Timestamp time.Time `protobuf:"bytes,2,opt,name=timestamp,proto3,stdtime" json:"timestamp"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DailyStorageUsageResponse_StorageUsage) Reset() {
*m = DailyStorageUsageResponse_StorageUsage{}
}
func (m *DailyStorageUsageResponse_StorageUsage) String() string { return proto.CompactTextString(m) }
func (*DailyStorageUsageResponse_StorageUsage) ProtoMessage() {}
func (*DailyStorageUsageResponse_StorageUsage) Descriptor() ([]byte, []int) {
return fileDescriptor_9d1904cedee84a32, []int{4, 0}
}
func (m *DailyStorageUsageResponse_StorageUsage) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DailyStorageUsageResponse_StorageUsage.Unmarshal(m, b)
}
func (m *DailyStorageUsageResponse_StorageUsage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_DailyStorageUsageResponse_StorageUsage.Marshal(b, m, deterministic)
}
func (m *DailyStorageUsageResponse_StorageUsage) XXX_Merge(src proto.Message) {
xxx_messageInfo_DailyStorageUsageResponse_StorageUsage.Merge(m, src)
}
func (m *DailyStorageUsageResponse_StorageUsage) XXX_Size() int {
return xxx_messageInfo_DailyStorageUsageResponse_StorageUsage.Size(m)
}
func (m *DailyStorageUsageResponse_StorageUsage) XXX_DiscardUnknown() {
xxx_messageInfo_DailyStorageUsageResponse_StorageUsage.DiscardUnknown(m)
}
var xxx_messageInfo_DailyStorageUsageResponse_StorageUsage proto.InternalMessageInfo
func (m *DailyStorageUsageResponse_StorageUsage) GetAtRestTotal() float64 {
if m != nil {
return m.AtRestTotal
}
return 0
}
func (m *DailyStorageUsageResponse_StorageUsage) GetTimestamp() time.Time {
if m != nil {
return m.Timestamp
}
return time.Time{}
}
type SatelliteSummaryRequest struct {
From time.Time `protobuf:"bytes,1,opt,name=from,proto3,stdtime" json:"from"`
To time.Time `protobuf:"bytes,2,opt,name=to,proto3,stdtime" json:"to"`
SatelliteId NodeID `protobuf:"bytes,3,opt,name=satellite_id,json=satelliteId,proto3,customtype=NodeID" json:"satellite_id"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SatelliteSummaryRequest) Reset() { *m = SatelliteSummaryRequest{} }
func (m *SatelliteSummaryRequest) String() string { return proto.CompactTextString(m) }
func (*SatelliteSummaryRequest) ProtoMessage() {}
func (*SatelliteSummaryRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_9d1904cedee84a32, []int{5}
}
func (m *SatelliteSummaryRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SatelliteSummaryRequest.Unmarshal(m, b)
}
func (m *SatelliteSummaryRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SatelliteSummaryRequest.Marshal(b, m, deterministic)
}
func (m *SatelliteSummaryRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_SatelliteSummaryRequest.Merge(m, src)
}
func (m *SatelliteSummaryRequest) XXX_Size() int {
return xxx_messageInfo_SatelliteSummaryRequest.Size(m)
}
func (m *SatelliteSummaryRequest) XXX_DiscardUnknown() {
xxx_messageInfo_SatelliteSummaryRequest.DiscardUnknown(m)
}
var xxx_messageInfo_SatelliteSummaryRequest proto.InternalMessageInfo
func (m *SatelliteSummaryRequest) GetFrom() time.Time {
if m != nil {
return m.From
}
return time.Time{}
}
func (m *SatelliteSummaryRequest) GetTo() time.Time {
if m != nil {
return m.To
}
return time.Time{}
}
type SatelliteSummaryResponse struct {
StorageUsage float64 `protobuf:"fixed64,1,opt,name=storage_usage,json=storageUsage,proto3" json:"storage_usage,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SatelliteSummaryResponse) Reset() { *m = SatelliteSummaryResponse{} }
func (m *SatelliteSummaryResponse) String() string { return proto.CompactTextString(m) }
func (*SatelliteSummaryResponse) ProtoMessage() {}
func (*SatelliteSummaryResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_9d1904cedee84a32, []int{6}
}
func (m *SatelliteSummaryResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SatelliteSummaryResponse.Unmarshal(m, b)
}
func (m *SatelliteSummaryResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SatelliteSummaryResponse.Marshal(b, m, deterministic)
}
func (m *SatelliteSummaryResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_SatelliteSummaryResponse.Merge(m, src)
}
func (m *SatelliteSummaryResponse) XXX_Size() int {
return xxx_messageInfo_SatelliteSummaryResponse.Size(m)
}
func (m *SatelliteSummaryResponse) XXX_DiscardUnknown() {
xxx_messageInfo_SatelliteSummaryResponse.DiscardUnknown(m)
}
var xxx_messageInfo_SatelliteSummaryResponse proto.InternalMessageInfo
func (m *SatelliteSummaryResponse) GetStorageUsage() float64 {
if m != nil {
return m.StorageUsage
}
return 0
}
func init() {
proto.RegisterType((*GetDiskSpaceRequest)(nil), "diskspace.GetDiskSpaceRequest")
proto.RegisterType((*GetDiskSpaceResponse)(nil), "diskspace.GetDiskSpaceResponse")
proto.RegisterType((*DiskSpace)(nil), "diskspace.DiskSpace")
proto.RegisterType((*DailyStorageUsageRequest)(nil), "diskspace.DailyStorageUsageRequest")
proto.RegisterType((*DailyStorageUsageResponse)(nil), "diskspace.DailyStorageUsageResponse")
proto.RegisterType((*DailyStorageUsageResponse_StorageUsage)(nil), "diskspace.DailyStorageUsageResponse.StorageUsage")
proto.RegisterType((*SatelliteSummaryRequest)(nil), "diskspace.SatelliteSummaryRequest")
proto.RegisterType((*SatelliteSummaryResponse)(nil), "diskspace.SatelliteSummaryResponse")
}
func init() { proto.RegisterFile("diskspace.proto", fileDescriptor_9d1904cedee84a32) }
var fileDescriptor_9d1904cedee84a32 = []byte{
// 528 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x53, 0xc1, 0x6e, 0xd3, 0x40,
0x10, 0xc5, 0x4e, 0x08, 0xcd, 0xc4, 0x01, 0xba, 0x0d, 0xaa, 0xb1, 0x2a, 0x52, 0x39, 0x1c, 0x7a,
0x72, 0xd4, 0x94, 0x03, 0x37, 0xa4, 0x28, 0x12, 0x8a, 0x90, 0x40, 0x72, 0xca, 0x05, 0x24, 0xac,
0x0d, 0xde, 0x9a, 0xa5, 0x76, 0xd7, 0x78, 0xc7, 0x91, 0xfa, 0x15, 0xf0, 0x03, 0xfc, 0x09, 0xdc,
0xf9, 0x06, 0x0e, 0xe5, 0x57, 0xd0, 0xae, 0x13, 0xdb, 0x69, 0x9b, 0xaa, 0x1c, 0xb9, 0xed, 0xcc,
0xce, 0x9b, 0x7d, 0xfb, 0xde, 0x0c, 0x3c, 0x08, 0xb9, 0x3c, 0x95, 0x29, 0xfd, 0xc8, 0xbc, 0x34,
0x13, 0x28, 0x48, 0xbb, 0x4c, 0x38, 0x10, 0x89, 0x48, 0x14, 0x69, 0xa7, 0x1f, 0x09, 0x11, 0xc5,
0x6c, 0xa8, 0xa3, 0x79, 0x7e, 0x32, 0x44, 0x9e, 0x30, 0x89, 0x34, 0x49, 0x8b, 0x02, 0xf7, 0x11,
0xec, 0xbc, 0x64, 0x38, 0xe1, 0xf2, 0x74, 0xa6, 0xc0, 0x3e, 0xfb, 0x92, 0x33, 0x89, 0xee, 0x2b,
0xe8, 0xad, 0xa7, 0x65, 0x2a, 0xce, 0x24, 0x23, 0x47, 0x00, 0xea, 0xa1, 0x40, 0xbf, 0x64, 0x1b,
0xfb, 0xc6, 0x41, 0x67, 0xd4, 0xf3, 0x2a, 0x32, 0x15, 0x42, 0x13, 0xd2, 0x47, 0x57, 0x40, 0xbb,
0xcc, 0x13, 0x02, 0xcd, 0x5c, 0xb2, 0x50, 0x63, 0x1b, 0xbe, 0x3e, 0x93, 0x3d, 0x68, 0xd3, 0x05,
0xe5, 0x31, 0x9d, 0xc7, 0xcc, 0x36, 0xf5, 0x45, 0x95, 0x20, 0x3d, 0xb8, 0x8b, 0x19, 0x95, 0x9f,
0xec, 0x86, 0xbe, 0x29, 0x02, 0xe2, 0xc0, 0x96, 0x58, 0xb0, 0x4c, 0xf7, 0x6a, 0xea, 0x8b, 0x32,
0x76, 0x7f, 0x1a, 0x60, 0x4f, 0x28, 0x8f, 0xcf, 0x67, 0x28, 0x32, 0x1a, 0xb1, 0xb7, 0x92, 0x46,
0xab, 0xaf, 0x91, 0xe7, 0xd0, 0x3c, 0xc9, 0x44, 0xb2, 0x24, 0xef, 0x78, 0x85, 0x42, 0xde, 0x4a,
0x21, 0xef, 0x78, 0xa5, 0xd0, 0x78, 0xeb, 0xd7, 0x45, 0xff, 0xce, 0xb7, 0x3f, 0x7d, 0xc3, 0xd7,
0x08, 0xf2, 0x0c, 0x4c, 0x14, 0x9a, 0xdf, 0x6d, 0x71, 0x26, 0x0a, 0x72, 0x08, 0x96, 0xa4, 0xc8,
0xe2, 0x98, 0x23, 0x0b, 0x78, 0xa8, 0x7f, 0x61, 0x8d, 0xef, 0xab, 0x9a, 0xdf, 0x17, 0xfd, 0xd6,
0x6b, 0x11, 0xb2, 0xe9, 0xc4, 0xef, 0x94, 0x35, 0xd3, 0xd0, 0xfd, 0x6a, 0xc2, 0xe3, 0x6b, 0xf8,
0x2f, 0x3d, 0xd8, 0x85, 0x7b, 0x67, 0x22, 0xd4, 0xbd, 0xd4, 0x1f, 0x2c, 0xbf, 0xa5, 0xc2, 0x69,
0x48, 0x28, 0xec, 0x84, 0x0a, 0x15, 0xc8, 0x02, 0x16, 0xe4, 0x0a, 0x67, 0x9b, 0xfb, 0x8d, 0x83,
0xce, 0xe8, 0xb0, 0xee, 0xd2, 0xa6, 0xde, 0xde, 0x5a, 0x72, 0x3b, 0xbc, 0x5c, 0xe7, 0x2c, 0xc0,
0xaa, 0xc7, 0xc4, 0x85, 0x2e, 0xc5, 0x20, 0x63, 0x12, 0x03, 0x14, 0x48, 0x63, 0xcd, 0xc8, 0xf0,
0x3b, 0x14, 0x7d, 0x26, 0xf1, 0x58, 0xa5, 0xc8, 0x18, 0xda, 0xe5, 0xd4, 0xfd, 0x93, 0x7a, 0x15,
0xcc, 0xfd, 0x61, 0xc0, 0xee, 0x6c, 0xa5, 0xd0, 0x2c, 0x4f, 0x12, 0x9a, 0x9d, 0xff, 0x47, 0x86,
0xbe, 0x00, 0xfb, 0x2a, 0xfb, 0xa5, 0x9d, 0x03, 0xe8, 0xae, 0xfb, 0x55, 0x48, 0x68, 0xc9, 0x9a,
0xce, 0xa3, 0xef, 0x26, 0x74, 0x55, 0xe3, 0x6a, 0x8f, 0xde, 0x80, 0x55, 0xdf, 0x50, 0xf2, 0xa4,
0xe6, 0xef, 0x35, 0x1b, 0xed, 0xf4, 0x37, 0xde, 0x2f, 0x79, 0x7c, 0x80, 0xed, 0x2b, 0x73, 0x41,
0x06, 0x37, 0x4f, 0x4d, 0xd1, 0xfa, 0xe9, 0x6d, 0x46, 0x8b, 0xbc, 0x87, 0x87, 0x97, 0x35, 0x20,
0x6e, 0x0d, 0xb9, 0xc1, 0x5e, 0x67, 0x70, 0x63, 0x4d, 0xd1, 0x7c, 0xbc, 0xf7, 0xce, 0x51, 0x7a,
0x7d, 0xf6, 0xb8, 0x18, 0xea, 0xc3, 0x30, 0xc9, 0x63, 0xe4, 0x6a, 0x31, 0xd2, 0xf9, 0xbc, 0xa5,
0x3d, 0x3d, 0xfa, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xb3, 0xd3, 0x33, 0x3c, 0x36, 0x05, 0x00, 0x00,
}
// --- DRPC BEGIN ---
type DRPCNodeDiskSpaceClient interface {
DRPCConn() drpc.Conn
GetDiskSpace(ctx context.Context, in *GetDiskSpaceRequest) (*GetDiskSpaceResponse, error)
DailyStorageUsage(ctx context.Context, in *DailyStorageUsageRequest) (*DailyStorageUsageResponse, error)
SatelliteSummary(ctx context.Context, in *SatelliteSummaryRequest) (*SatelliteSummaryResponse, error)
}
type drpcNodeDiskSpaceClient struct {
cc drpc.Conn
}
func NewDRPCNodeDiskSpaceClient(cc drpc.Conn) DRPCNodeDiskSpaceClient {
return &drpcNodeDiskSpaceClient{cc}
}
func (c *drpcNodeDiskSpaceClient) DRPCConn() drpc.Conn { return c.cc }
func (c *drpcNodeDiskSpaceClient) GetDiskSpace(ctx context.Context, in *GetDiskSpaceRequest) (*GetDiskSpaceResponse, error) {
out := new(GetDiskSpaceResponse)
err := c.cc.Invoke(ctx, "/diskspace.NodeDiskSpace/GetDiskSpace", in, out)
if err != nil {
return nil, err
}
return out, nil
}
func (c *drpcNodeDiskSpaceClient) DailyStorageUsage(ctx context.Context, in *DailyStorageUsageRequest) (*DailyStorageUsageResponse, error) {
out := new(DailyStorageUsageResponse)
err := c.cc.Invoke(ctx, "/diskspace.NodeDiskSpace/DailyStorageUsage", in, out)
if err != nil {
return nil, err
}
return out, nil
}
func (c *drpcNodeDiskSpaceClient) SatelliteSummary(ctx context.Context, in *SatelliteSummaryRequest) (*SatelliteSummaryResponse, error) {
out := new(SatelliteSummaryResponse)
err := c.cc.Invoke(ctx, "/diskspace.NodeDiskSpace/SatelliteSummary", in, out)
if err != nil {
return nil, err
}
return out, nil
}
type DRPCNodeDiskSpaceServer interface {
GetDiskSpace(context.Context, *GetDiskSpaceRequest) (*GetDiskSpaceResponse, error)
DailyStorageUsage(context.Context, *DailyStorageUsageRequest) (*DailyStorageUsageResponse, error)
SatelliteSummary(context.Context, *SatelliteSummaryRequest) (*SatelliteSummaryResponse, error)
}
type DRPCNodeDiskSpaceDescription struct{}
func (DRPCNodeDiskSpaceDescription) NumMethods() int { return 3 }
func (DRPCNodeDiskSpaceDescription) Method(n int) (string, drpc.Receiver, interface{}, bool) {
switch n {
case 0:
return "/diskspace.NodeDiskSpace/GetDiskSpace",
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCNodeDiskSpaceServer).
GetDiskSpace(
ctx,
in1.(*GetDiskSpaceRequest),
)
}, DRPCNodeDiskSpaceServer.GetDiskSpace, true
case 1:
return "/diskspace.NodeDiskSpace/DailyStorageUsage",
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCNodeDiskSpaceServer).
DailyStorageUsage(
ctx,
in1.(*DailyStorageUsageRequest),
)
}, DRPCNodeDiskSpaceServer.DailyStorageUsage, true
case 2:
return "/diskspace.NodeDiskSpace/SatelliteSummary",
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCNodeDiskSpaceServer).
SatelliteSummary(
ctx,
in1.(*SatelliteSummaryRequest),
)
}, DRPCNodeDiskSpaceServer.SatelliteSummary, true
default:
return "", nil, nil, false
}
}
func DRPCRegisterNodeDiskSpace(mux drpc.Mux, impl DRPCNodeDiskSpaceServer) error {
return mux.Register(impl, DRPCNodeDiskSpaceDescription{})
}
type DRPCNodeDiskSpace_GetDiskSpaceStream interface {
drpc.Stream
SendAndClose(*GetDiskSpaceResponse) error
}
type drpcNodeDiskSpaceGetDiskSpaceStream struct {
drpc.Stream
}
func (x *drpcNodeDiskSpaceGetDiskSpaceStream) SendAndClose(m *GetDiskSpaceResponse) error {
if err := x.MsgSend(m); err != nil {
return err
}
return x.CloseSend()
}
type DRPCNodeDiskSpace_DailyStorageUsageStream interface {
drpc.Stream
SendAndClose(*DailyStorageUsageResponse) error
}
type drpcNodeDiskSpaceDailyStorageUsageStream struct {
drpc.Stream
}
func (x *drpcNodeDiskSpaceDailyStorageUsageStream) SendAndClose(m *DailyStorageUsageResponse) error {
if err := x.MsgSend(m); err != nil {
return err
}
return x.CloseSend()
}
type DRPCNodeDiskSpace_SatelliteSummaryStream interface {
drpc.Stream
SendAndClose(*SatelliteSummaryResponse) error
}
type drpcNodeDiskSpaceSatelliteSummaryStream struct {
drpc.Stream
}
func (x *drpcNodeDiskSpaceSatelliteSummaryStream) SendAndClose(m *SatelliteSummaryResponse) error {
if err := x.MsgSend(m); err != nil {
return err
}
return x.CloseSend()
}
// --- DRPC END ---

View File

@ -1,56 +0,0 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
syntax = "proto3";
option go_package = "storj.io/storj/multinodepb";
package diskspace;
import "gogo.proto";
import "google/protobuf/timestamp.proto";
service NodeDiskSpace {
rpc GetDiskSpace(GetDiskSpaceRequest) returns (GetDiskSpaceResponse);
rpc DailyStorageUsage(DailyStorageUsageRequest) returns (DailyStorageUsageResponse);
rpc SatelliteSummary(SatelliteSummaryRequest) returns (SatelliteSummaryResponse);
}
message GetDiskSpaceRequest {}
message GetDiskSpaceResponse {
DiskSpace disk_space = 1;
}
// DiskSpace stores all info about storagenode disk space.
message DiskSpace {
int64 used = 1;
int64 available = 2;
int64 trash = 3;
int64 overused = 4;
}
message DailyStorageUsageRequest {
google.protobuf.Timestamp from = 1 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
google.protobuf.Timestamp to = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
bytes satellite_id = 3 [(gogoproto.customtype) = "NodeID", (gogoproto.nullable) = false];
}
message DailyStorageUsageResponse {
message StorageUsage {
double at_rest_total = 1;
google.protobuf.Timestamp timestamp = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
}
bytes node_id = 1;
repeated StorageUsage daily_storage_usage = 2;
}
message SatelliteSummaryRequest {
google.protobuf.Timestamp from = 1 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
google.protobuf.Timestamp to = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
bytes satellite_id = 3 [(gogoproto.customtype) = "NodeID", (gogoproto.nullable) = false];
}
message SatelliteSummaryResponse {
double storage_usage = 1;
}

View File

@ -1,466 +0,0 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: reputation.proto
package multinodepb
import (
context "context"
fmt "fmt"
math "math"
time "time"
proto "github.com/gogo/protobuf/proto"
drpc "storj.io/drpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
var _ = time.Kitchen
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
type ReputationStats struct {
TotalCount int64 `protobuf:"varint,1,opt,name=total_count,json=totalCount,proto3" json:"total_count,omitempty"`
SuccessCount int64 `protobuf:"varint,2,opt,name=success_count,json=successCount,proto3" json:"success_count,omitempty"`
ReputationAlpha float64 `protobuf:"fixed64,3,opt,name=reputation_alpha,json=reputationAlpha,proto3" json:"reputation_alpha,omitempty"`
ReputationBeta float64 `protobuf:"fixed64,4,opt,name=reputation_beta,json=reputationBeta,proto3" json:"reputation_beta,omitempty"`
ReputationScore float64 `protobuf:"fixed64,5,opt,name=reputation_score,json=reputationScore,proto3" json:"reputation_score,omitempty"`
UnknownReputationAlpha float64 `protobuf:"fixed64,6,opt,name=unknown_reputation_alpha,json=unknownReputationAlpha,proto3" json:"unknown_reputation_alpha,omitempty"`
UnknownReputationBeta float64 `protobuf:"fixed64,7,opt,name=unknown_reputation_beta,json=unknownReputationBeta,proto3" json:"unknown_reputation_beta,omitempty"`
UnknownReputationScore float64 `protobuf:"fixed64,8,opt,name=unknown_reputation_score,json=unknownReputationScore,proto3" json:"unknown_reputation_score,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ReputationStats) Reset() { *m = ReputationStats{} }
func (m *ReputationStats) String() string { return proto.CompactTextString(m) }
func (*ReputationStats) ProtoMessage() {}
func (*ReputationStats) Descriptor() ([]byte, []int) {
return fileDescriptor_b35a2508345eddf0, []int{0}
}
func (m *ReputationStats) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ReputationStats.Unmarshal(m, b)
}
func (m *ReputationStats) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ReputationStats.Marshal(b, m, deterministic)
}
func (m *ReputationStats) XXX_Merge(src proto.Message) {
xxx_messageInfo_ReputationStats.Merge(m, src)
}
func (m *ReputationStats) XXX_Size() int {
return xxx_messageInfo_ReputationStats.Size(m)
}
func (m *ReputationStats) XXX_DiscardUnknown() {
xxx_messageInfo_ReputationStats.DiscardUnknown(m)
}
var xxx_messageInfo_ReputationStats proto.InternalMessageInfo
func (m *ReputationStats) GetTotalCount() int64 {
if m != nil {
return m.TotalCount
}
return 0
}
func (m *ReputationStats) GetSuccessCount() int64 {
if m != nil {
return m.SuccessCount
}
return 0
}
func (m *ReputationStats) GetReputationAlpha() float64 {
if m != nil {
return m.ReputationAlpha
}
return 0
}
func (m *ReputationStats) GetReputationBeta() float64 {
if m != nil {
return m.ReputationBeta
}
return 0
}
func (m *ReputationStats) GetReputationScore() float64 {
if m != nil {
return m.ReputationScore
}
return 0
}
func (m *ReputationStats) GetUnknownReputationAlpha() float64 {
if m != nil {
return m.UnknownReputationAlpha
}
return 0
}
func (m *ReputationStats) GetUnknownReputationBeta() float64 {
if m != nil {
return m.UnknownReputationBeta
}
return 0
}
func (m *ReputationStats) GetUnknownReputationScore() float64 {
if m != nil {
return m.UnknownReputationScore
}
return 0
}
type GetBySatelliteIDRequest struct {
SatelliteId NodeID `protobuf:"bytes,1,opt,name=satellite_id,json=satelliteId,proto3,customtype=NodeID" json:"satellite_id"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetBySatelliteIDRequest) Reset() { *m = GetBySatelliteIDRequest{} }
func (m *GetBySatelliteIDRequest) String() string { return proto.CompactTextString(m) }
func (*GetBySatelliteIDRequest) ProtoMessage() {}
func (*GetBySatelliteIDRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_b35a2508345eddf0, []int{1}
}
func (m *GetBySatelliteIDRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetBySatelliteIDRequest.Unmarshal(m, b)
}
func (m *GetBySatelliteIDRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetBySatelliteIDRequest.Marshal(b, m, deterministic)
}
func (m *GetBySatelliteIDRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetBySatelliteIDRequest.Merge(m, src)
}
func (m *GetBySatelliteIDRequest) XXX_Size() int {
return xxx_messageInfo_GetBySatelliteIDRequest.Size(m)
}
func (m *GetBySatelliteIDRequest) XXX_DiscardUnknown() {
xxx_messageInfo_GetBySatelliteIDRequest.DiscardUnknown(m)
}
var xxx_messageInfo_GetBySatelliteIDRequest proto.InternalMessageInfo
type GetBySatelliteIDResponse struct {
AuditCheck *ReputationStats `protobuf:"bytes,1,opt,name=audit_check,json=auditCheck,proto3" json:"audit_check,omitempty"`
Disqualified *time.Time `protobuf:"bytes,2,opt,name=disqualified,proto3,stdtime" json:"disqualified,omitempty"`
Suspended *time.Time `protobuf:"bytes,3,opt,name=suspended,proto3,stdtime" json:"suspended,omitempty"`
JoinedAt time.Time `protobuf:"bytes,4,opt,name=joined_at,json=joinedAt,proto3,stdtime" json:"joined_at"`
OfflineSuspended *time.Time `protobuf:"bytes,5,opt,name=offline_suspended,json=offlineSuspended,proto3,stdtime" json:"offline_suspended,omitempty"`
OnlineScore float64 `protobuf:"fixed64,6,opt,name=online_score,json=onlineScore,proto3" json:"online_score,omitempty"`
OfflineUnderReview *time.Time `protobuf:"bytes,7,opt,name=offline_under_review,json=offlineUnderReview,proto3,stdtime" json:"offline_under_review,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetBySatelliteIDResponse) Reset() { *m = GetBySatelliteIDResponse{} }
func (m *GetBySatelliteIDResponse) String() string { return proto.CompactTextString(m) }
func (*GetBySatelliteIDResponse) ProtoMessage() {}
func (*GetBySatelliteIDResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_b35a2508345eddf0, []int{2}
}
func (m *GetBySatelliteIDResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetBySatelliteIDResponse.Unmarshal(m, b)
}
func (m *GetBySatelliteIDResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetBySatelliteIDResponse.Marshal(b, m, deterministic)
}
func (m *GetBySatelliteIDResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetBySatelliteIDResponse.Merge(m, src)
}
func (m *GetBySatelliteIDResponse) XXX_Size() int {
return xxx_messageInfo_GetBySatelliteIDResponse.Size(m)
}
func (m *GetBySatelliteIDResponse) XXX_DiscardUnknown() {
xxx_messageInfo_GetBySatelliteIDResponse.DiscardUnknown(m)
}
var xxx_messageInfo_GetBySatelliteIDResponse proto.InternalMessageInfo
func (m *GetBySatelliteIDResponse) GetAuditCheck() *ReputationStats {
if m != nil {
return m.AuditCheck
}
return nil
}
func (m *GetBySatelliteIDResponse) GetDisqualified() *time.Time {
if m != nil {
return m.Disqualified
}
return nil
}
func (m *GetBySatelliteIDResponse) GetSuspended() *time.Time {
if m != nil {
return m.Suspended
}
return nil
}
func (m *GetBySatelliteIDResponse) GetJoinedAt() time.Time {
if m != nil {
return m.JoinedAt
}
return time.Time{}
}
func (m *GetBySatelliteIDResponse) GetOfflineSuspended() *time.Time {
if m != nil {
return m.OfflineSuspended
}
return nil
}
func (m *GetBySatelliteIDResponse) GetOnlineScore() float64 {
if m != nil {
return m.OnlineScore
}
return 0
}
func (m *GetBySatelliteIDResponse) GetOfflineUnderReview() *time.Time {
if m != nil {
return m.OfflineUnderReview
}
return nil
}
type AllRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AllRequest) Reset() { *m = AllRequest{} }
func (m *AllRequest) String() string { return proto.CompactTextString(m) }
func (*AllRequest) ProtoMessage() {}
func (*AllRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_b35a2508345eddf0, []int{3}
}
func (m *AllRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AllRequest.Unmarshal(m, b)
}
func (m *AllRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AllRequest.Marshal(b, m, deterministic)
}
func (m *AllRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AllRequest.Merge(m, src)
}
func (m *AllRequest) XXX_Size() int {
return xxx_messageInfo_AllRequest.Size(m)
}
func (m *AllRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AllRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AllRequest proto.InternalMessageInfo
type AllResponse struct {
Reputation []*GetBySatelliteIDResponse `protobuf:"bytes,1,rep,name=reputation,proto3" json:"reputation,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AllResponse) Reset() { *m = AllResponse{} }
func (m *AllResponse) String() string { return proto.CompactTextString(m) }
func (*AllResponse) ProtoMessage() {}
func (*AllResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_b35a2508345eddf0, []int{4}
}
func (m *AllResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AllResponse.Unmarshal(m, b)
}
func (m *AllResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AllResponse.Marshal(b, m, deterministic)
}
func (m *AllResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AllResponse.Merge(m, src)
}
func (m *AllResponse) XXX_Size() int {
return xxx_messageInfo_AllResponse.Size(m)
}
func (m *AllResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AllResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AllResponse proto.InternalMessageInfo
func (m *AllResponse) GetReputation() []*GetBySatelliteIDResponse {
if m != nil {
return m.Reputation
}
return nil
}
func init() {
proto.RegisterType((*ReputationStats)(nil), "reputation.ReputationStats")
proto.RegisterType((*GetBySatelliteIDRequest)(nil), "reputation.GetBySatelliteIDRequest")
proto.RegisterType((*GetBySatelliteIDResponse)(nil), "reputation.GetBySatelliteIDResponse")
proto.RegisterType((*AllRequest)(nil), "reputation.AllRequest")
proto.RegisterType((*AllResponse)(nil), "reputation.AllResponse")
}
func init() { proto.RegisterFile("reputation.proto", fileDescriptor_b35a2508345eddf0) }
var fileDescriptor_b35a2508345eddf0 = []byte{
// 584 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xc1, 0x6e, 0xd3, 0x40,
0x14, 0xac, 0x31, 0x2d, 0xed, 0xb3, 0x69, 0xcb, 0x0a, 0x5a, 0xcb, 0x20, 0xb9, 0xa4, 0x48, 0x84,
0x8b, 0x23, 0x82, 0x54, 0x71, 0xe0, 0x12, 0xb7, 0x12, 0x54, 0x42, 0x48, 0x38, 0xc0, 0x01, 0x09,
0x59, 0x1b, 0x7b, 0x93, 0x6e, 0xbb, 0xd9, 0x75, 0xbd, 0x6b, 0x2a, 0xae, 0x7c, 0x01, 0xff, 0xc0,
0xcf, 0xf4, 0x1b, 0x38, 0x84, 0xcf, 0xe0, 0x8a, 0xbc, 0x76, 0x62, 0x93, 0xb4, 0x10, 0x6e, 0xde,
0xf1, 0xcc, 0xbc, 0xb1, 0xdf, 0x68, 0x61, 0x3b, 0x23, 0x69, 0xae, 0xb0, 0xa2, 0x82, 0xfb, 0x69,
0x26, 0x94, 0x40, 0x50, 0x23, 0x2e, 0x8c, 0xc4, 0x48, 0x94, 0xb8, 0xeb, 0x8d, 0x84, 0x18, 0x31,
0xd2, 0xd1, 0xa7, 0x41, 0x3e, 0xec, 0x28, 0x3a, 0x26, 0x52, 0xe1, 0x71, 0x5a, 0x12, 0x5a, 0x5f,
0x4d, 0xd8, 0x0a, 0x67, 0xda, 0xbe, 0xc2, 0x4a, 0x22, 0x0f, 0x2c, 0x25, 0x14, 0x66, 0x51, 0x2c,
0x72, 0xae, 0x1c, 0x63, 0xcf, 0x68, 0x9b, 0x21, 0x68, 0xe8, 0xb0, 0x40, 0xd0, 0x3e, 0xdc, 0x96,
0x79, 0x1c, 0x13, 0x29, 0x2b, 0xca, 0x0d, 0x4d, 0xb1, 0x2b, 0xb0, 0x24, 0x3d, 0x69, 0xc6, 0x8c,
0x30, 0x4b, 0x4f, 0xb0, 0x63, 0xee, 0x19, 0x6d, 0x23, 0xdc, 0xaa, 0xf1, 0x5e, 0x01, 0xa3, 0xc7,
0xd0, 0x80, 0xa2, 0x01, 0x51, 0xd8, 0xb9, 0xa9, 0x99, 0x9b, 0x35, 0x1c, 0x10, 0x85, 0xe7, 0x3c,
0x65, 0x2c, 0x32, 0xe2, 0xac, 0xce, 0x7b, 0xf6, 0x0b, 0x18, 0x3d, 0x07, 0x27, 0xe7, 0x67, 0x5c,
0x5c, 0xf0, 0x68, 0x21, 0xc6, 0x9a, 0x96, 0xec, 0x54, 0xef, 0xc3, 0xb9, 0x34, 0x07, 0xb0, 0x7b,
0x85, 0x52, 0xa7, 0xba, 0xa5, 0x85, 0xf7, 0x16, 0x84, 0x3a, 0xdc, 0xd5, 0x13, 0xcb, 0x90, 0xeb,
0xd7, 0x4c, 0xd4, 0x59, 0x5b, 0xaf, 0x61, 0xf7, 0x25, 0x51, 0xc1, 0x97, 0x3e, 0x56, 0x84, 0x31,
0xaa, 0xc8, 0xf1, 0x51, 0x48, 0xce, 0x73, 0x22, 0x15, 0x7a, 0x0a, 0xb6, 0x9c, 0xa2, 0x11, 0x4d,
0xf4, 0x32, 0xec, 0x60, 0xf3, 0x72, 0xe2, 0xad, 0xfc, 0x98, 0x78, 0x6b, 0x6f, 0x44, 0x52, 0x90,
0xad, 0x19, 0xe7, 0x38, 0x69, 0xfd, 0x32, 0xc1, 0x59, 0xb4, 0x93, 0xa9, 0xe0, 0x92, 0xa0, 0x17,
0x60, 0xe1, 0x3c, 0xa1, 0x2a, 0x8a, 0x4f, 0x48, 0x7c, 0xa6, 0xed, 0xac, 0xee, 0x7d, 0xbf, 0x51,
0xa8, 0xb9, 0x36, 0x84, 0xa0, 0xf9, 0x87, 0x05, 0x1d, 0xbd, 0x02, 0x3b, 0xa1, 0xf2, 0x3c, 0xc7,
0x8c, 0x0e, 0x29, 0x49, 0xf4, 0xde, 0xad, 0xae, 0xeb, 0x97, 0x2d, 0xf3, 0xa7, 0x2d, 0xf3, 0xdf,
0x4d, 0x5b, 0x16, 0xac, 0x5f, 0x4e, 0x3c, 0xe3, 0xdb, 0x4f, 0xcf, 0x08, 0xff, 0x50, 0xa2, 0x00,
0x36, 0x64, 0x2e, 0x53, 0xc2, 0x13, 0x92, 0xe8, 0x5a, 0x2c, 0x6b, 0x53, 0xcb, 0x50, 0x0f, 0x36,
0x4e, 0x05, 0xe5, 0x24, 0x89, 0xb0, 0xd2, 0x85, 0xf9, 0xb7, 0xc7, 0x8a, 0xf6, 0x58, 0x2f, 0x65,
0x3d, 0x85, 0xde, 0xc2, 0x1d, 0x31, 0x1c, 0x32, 0xca, 0x49, 0x54, 0xc7, 0x59, 0xfd, 0x8f, 0x38,
0xdb, 0x95, 0xbc, 0x3f, 0x4b, 0xf5, 0x10, 0x6c, 0xc1, 0x4b, 0x47, 0xbd, 0xfa, 0xb2, 0x6c, 0x56,
0x89, 0x95, 0xdd, 0xfc, 0x00, 0x77, 0xa7, 0x53, 0x73, 0x9e, 0x90, 0x2c, 0xca, 0xc8, 0x67, 0x4a,
0x2e, 0x74, 0xbd, 0x96, 0x1d, 0x8c, 0x2a, 0x87, 0xf7, 0x85, 0x41, 0xa8, 0xf5, 0x2d, 0x1b, 0xa0,
0xc7, 0x58, 0x55, 0x9d, 0x56, 0x1f, 0x2c, 0x7d, 0xaa, 0x36, 0x7f, 0x04, 0x8d, 0x4b, 0xc2, 0x31,
0xf6, 0xcc, 0xb6, 0xd5, 0x7d, 0xd4, 0x5c, 0xfc, 0x75, 0x9d, 0x09, 0x1b, 0xba, 0xee, 0x77, 0x03,
0xa0, 0x6e, 0x08, 0xfa, 0x04, 0xdb, 0xf3, 0x32, 0xb4, 0xff, 0x77, 0x53, 0x1d, 0xce, 0x5d, 0x6a,
0x32, 0x3a, 0x00, 0xb3, 0xc7, 0x18, 0xda, 0x69, 0x92, 0xeb, 0x2f, 0x74, 0x77, 0x17, 0xf0, 0x52,
0x17, 0x3c, 0xf8, 0xe8, 0x4a, 0x25, 0xb2, 0x53, 0x9f, 0x8a, 0x8e, 0x7e, 0xe8, 0x8c, 0x73, 0xa6,
0x28, 0x17, 0x09, 0x49, 0x07, 0x83, 0x35, 0xfd, 0x63, 0x9f, 0xfd, 0x0e, 0x00, 0x00, 0xff, 0xff,
0x13, 0x5c, 0xf1, 0xa4, 0x47, 0x05, 0x00, 0x00,
}
// --- DRPC BEGIN ---
type DRPCReputationClient interface {
DRPCConn() drpc.Conn
GetBySatelliteID(ctx context.Context, in *GetBySatelliteIDRequest) (*GetBySatelliteIDResponse, error)
All(ctx context.Context, in *AllRequest) (*AllResponse, error)
}
type drpcReputationClient struct {
cc drpc.Conn
}
func NewDRPCReputationClient(cc drpc.Conn) DRPCReputationClient {
return &drpcReputationClient{cc}
}
func (c *drpcReputationClient) DRPCConn() drpc.Conn { return c.cc }
func (c *drpcReputationClient) GetBySatelliteID(ctx context.Context, in *GetBySatelliteIDRequest) (*GetBySatelliteIDResponse, error) {
out := new(GetBySatelliteIDResponse)
err := c.cc.Invoke(ctx, "/reputation.Reputation/GetBySatelliteID", in, out)
if err != nil {
return nil, err
}
return out, nil
}
func (c *drpcReputationClient) All(ctx context.Context, in *AllRequest) (*AllResponse, error) {
out := new(AllResponse)
err := c.cc.Invoke(ctx, "/reputation.Reputation/All", in, out)
if err != nil {
return nil, err
}
return out, nil
}
type DRPCReputationServer interface {
GetBySatelliteID(context.Context, *GetBySatelliteIDRequest) (*GetBySatelliteIDResponse, error)
All(context.Context, *AllRequest) (*AllResponse, error)
}
type DRPCReputationDescription struct{}
func (DRPCReputationDescription) NumMethods() int { return 2 }
func (DRPCReputationDescription) Method(n int) (string, drpc.Receiver, interface{}, bool) {
switch n {
case 0:
return "/reputation.Reputation/GetBySatelliteID",
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCReputationServer).
GetBySatelliteID(
ctx,
in1.(*GetBySatelliteIDRequest),
)
}, DRPCReputationServer.GetBySatelliteID, true
case 1:
return "/reputation.Reputation/All",
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCReputationServer).
All(
ctx,
in1.(*AllRequest),
)
}, DRPCReputationServer.All, true
default:
return "", nil, nil, false
}
}
func DRPCRegisterReputation(mux drpc.Mux, impl DRPCReputationServer) error {
return mux.Register(impl, DRPCReputationDescription{})
}
type DRPCReputation_GetBySatelliteIDStream interface {
drpc.Stream
SendAndClose(*GetBySatelliteIDResponse) error
}
type drpcReputationGetBySatelliteIDStream struct {
drpc.Stream
}
func (x *drpcReputationGetBySatelliteIDStream) SendAndClose(m *GetBySatelliteIDResponse) error {
if err := x.MsgSend(m); err != nil {
return err
}
return x.CloseSend()
}
type DRPCReputation_AllStream interface {
drpc.Stream
SendAndClose(*AllResponse) error
}
type drpcReputationAllStream struct {
drpc.Stream
}
func (x *drpcReputationAllStream) SendAndClose(m *AllResponse) error {
if err := x.MsgSend(m); err != nil {
return err
}
return x.CloseSend()
}
// --- DRPC END ---

View File

@ -1,46 +0,0 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
syntax = "proto3";
option go_package = "storj.io/storj/multinodepb";
package reputation;
import "gogo.proto";
import "google/protobuf/timestamp.proto";
service Reputation {
rpc GetBySatelliteID(GetBySatelliteIDRequest) returns (GetBySatelliteIDResponse);
rpc All(AllRequest) returns (AllResponse);
}
message ReputationStats {
int64 total_count = 1;
int64 success_count = 2;
double reputation_alpha = 3;
double reputation_beta = 4;
double reputation_score = 5;
double unknown_reputation_alpha = 6;
double unknown_reputation_beta = 7;
double unknown_reputation_score = 8;
}
message GetBySatelliteIDRequest {
bytes satellite_id = 1 [(gogoproto.customtype) = "NodeID", (gogoproto.nullable) = false];
}
message GetBySatelliteIDResponse {
ReputationStats audit_check = 1;
google.protobuf.Timestamp disqualified = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = true];
google.protobuf.Timestamp suspended = 3 [(gogoproto.stdtime) = true, (gogoproto.nullable) = true];
google.protobuf.Timestamp joined_at = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
google.protobuf.Timestamp offline_suspended = 5 [(gogoproto.stdtime) = true, (gogoproto.nullable) = true];
double online_score = 6;
google.protobuf.Timestamp offline_under_review = 7 [(gogoproto.stdtime) = true, (gogoproto.nullable) = true];
}
message AllRequest {}
message AllResponse {
repeated GetBySatelliteIDResponse reputation = 1;
}

View File

@ -1,202 +0,0 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: status.proto
package multinodepb
import (
context "context"
fmt "fmt"
math "math"
time "time"
proto "github.com/gogo/protobuf/proto"
drpc "storj.io/drpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
var _ = time.Kitchen
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
type GetRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetRequest) Reset() { *m = GetRequest{} }
func (m *GetRequest) String() string { return proto.CompactTextString(m) }
func (*GetRequest) ProtoMessage() {}
func (*GetRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_dfe4fce6682daf5b, []int{0}
}
func (m *GetRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetRequest.Unmarshal(m, b)
}
func (m *GetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetRequest.Marshal(b, m, deterministic)
}
func (m *GetRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetRequest.Merge(m, src)
}
func (m *GetRequest) XXX_Size() int {
return xxx_messageInfo_GetRequest.Size(m)
}
func (m *GetRequest) XXX_DiscardUnknown() {
xxx_messageInfo_GetRequest.DiscardUnknown(m)
}
var xxx_messageInfo_GetRequest proto.InternalMessageInfo
type GetResponse struct {
StartedAt time.Time `protobuf:"bytes,1,opt,name=started_at,json=startedAt,proto3,stdtime" json:"started_at"`
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetResponse) Reset() { *m = GetResponse{} }
func (m *GetResponse) String() string { return proto.CompactTextString(m) }
func (*GetResponse) ProtoMessage() {}
func (*GetResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_dfe4fce6682daf5b, []int{1}
}
func (m *GetResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetResponse.Unmarshal(m, b)
}
func (m *GetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetResponse.Marshal(b, m, deterministic)
}
func (m *GetResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetResponse.Merge(m, src)
}
func (m *GetResponse) XXX_Size() int {
return xxx_messageInfo_GetResponse.Size(m)
}
func (m *GetResponse) XXX_DiscardUnknown() {
xxx_messageInfo_GetResponse.DiscardUnknown(m)
}
var xxx_messageInfo_GetResponse proto.InternalMessageInfo
func (m *GetResponse) GetStartedAt() time.Time {
if m != nil {
return m.StartedAt
}
return time.Time{}
}
func (m *GetResponse) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
func init() {
proto.RegisterType((*GetRequest)(nil), "status.GetRequest")
proto.RegisterType((*GetResponse)(nil), "status.GetResponse")
}
func init() { proto.RegisterFile("status.proto", fileDescriptor_dfe4fce6682daf5b) }
var fileDescriptor_dfe4fce6682daf5b = []byte{
// 225 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x8f, 0xb1, 0x4e, 0xc3, 0x30,
0x10, 0x86, 0x09, 0x48, 0x81, 0x5e, 0x3b, 0x99, 0x25, 0xb2, 0x90, 0x52, 0x75, 0xea, 0xe4, 0x48,
0x65, 0x61, 0xa5, 0x0c, 0xdd, 0x03, 0x13, 0x0b, 0x4a, 0xd4, 0xc3, 0x32, 0x4a, 0x72, 0xc6, 0x77,
0xe6, 0x39, 0x78, 0x2c, 0x9e, 0x02, 0x5e, 0x05, 0xc9, 0x6e, 0x84, 0xd8, 0xee, 0xfb, 0x75, 0xf7,
0xeb, 0x3b, 0x58, 0xb1, 0x74, 0x12, 0xd9, 0xf8, 0x40, 0x42, 0xaa, 0xcc, 0xa4, 0xc1, 0x92, 0xa5,
0x9c, 0xe9, 0xda, 0x12, 0xd9, 0x01, 0x9b, 0x44, 0x7d, 0x7c, 0x6d, 0xc4, 0x8d, 0xc8, 0xd2, 0x8d,
0x3e, 0x2f, 0x6c, 0x56, 0x00, 0x07, 0x94, 0x16, 0xdf, 0x23, 0xb2, 0x6c, 0x06, 0x58, 0x26, 0x62,
0x4f, 0x13, 0xa3, 0x7a, 0x00, 0x60, 0xe9, 0x82, 0xe0, 0xf1, 0xa5, 0x93, 0xaa, 0x58, 0x17, 0xdb,
0xe5, 0x4e, 0x9b, 0x5c, 0x69, 0xe6, 0x4a, 0xf3, 0x34, 0x57, 0xee, 0xaf, 0xbe, 0xbe, 0xeb, 0xb3,
0xcf, 0x9f, 0xba, 0x68, 0x17, 0xa7, 0xbb, 0x7b, 0x51, 0x15, 0x5c, 0x7e, 0x60, 0x60, 0x47, 0x53,
0x75, 0xbe, 0x2e, 0xb6, 0x8b, 0x76, 0xc6, 0xdd, 0x1d, 0x94, 0x8f, 0x49, 0x59, 0x19, 0xb8, 0x38,
0xa0, 0x28, 0x65, 0x4e, 0x0f, 0xfd, 0x29, 0xe9, 0xeb, 0x7f, 0x59, 0x16, 0xdb, 0xdf, 0x3c, 0x6b,
0x16, 0x0a, 0x6f, 0xc6, 0x51, 0x93, 0x86, 0x66, 0x8c, 0x83, 0xb8, 0x89, 0x8e, 0xe8, 0xfb, 0xbe,
0x4c, 0x6a, 0xb7, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x81, 0x06, 0x90, 0x45, 0x1f, 0x01, 0x00,
0x00,
}
// --- DRPC BEGIN ---
type DRPCStatusClient interface {
DRPCConn() drpc.Conn
Get(ctx context.Context, in *GetRequest) (*GetResponse, error)
}
type drpcStatusClient struct {
cc drpc.Conn
}
func NewDRPCStatusClient(cc drpc.Conn) DRPCStatusClient {
return &drpcStatusClient{cc}
}
func (c *drpcStatusClient) DRPCConn() drpc.Conn { return c.cc }
func (c *drpcStatusClient) Get(ctx context.Context, in *GetRequest) (*GetResponse, error) {
out := new(GetResponse)
err := c.cc.Invoke(ctx, "/status.Status/Get", in, out)
if err != nil {
return nil, err
}
return out, nil
}
type DRPCStatusServer interface {
Get(context.Context, *GetRequest) (*GetResponse, error)
}
type DRPCStatusDescription struct{}
func (DRPCStatusDescription) NumMethods() int { return 1 }
func (DRPCStatusDescription) Method(n int) (string, drpc.Receiver, interface{}, bool) {
switch n {
case 0:
return "/status.Status/Get",
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCStatusServer).
Get(
ctx,
in1.(*GetRequest),
)
}, DRPCStatusServer.Get, true
default:
return "", nil, nil, false
}
}
func DRPCRegisterStatus(mux drpc.Mux, impl DRPCStatusServer) error {
return mux.Register(impl, DRPCStatusDescription{})
}
type DRPCStatus_GetStream interface {
drpc.Stream
SendAndClose(*GetResponse) error
}
type drpcStatusGetStream struct {
drpc.Stream
}
func (x *drpcStatusGetStream) SendAndClose(m *GetResponse) error {
if err := x.MsgSend(m); err != nil {
return err
}
return x.CloseSend()
}
// --- DRPC END ---

View File

@ -1,21 +0,0 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
syntax = "proto3";
option go_package = "storj.io/storj/multinodepb";
package status;
import "gogo.proto";
import "google/protobuf/timestamp.proto";
service Status {
rpc Get(GetRequest) returns (GetResponse);
}
message GetRequest {}
message GetResponse {
google.protobuf.Timestamp started_at = 1 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
string version = 2; // must be semver formatted
}

View File

@ -0,0 +1,61 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package multinodeauth
import (
"bytes"
"crypto/rand"
"encoding/base64"
"github.com/zeebo/errs"
)
// Secret crypto random 32 bytes array for multinode auth.
type Secret [32]byte
// NewSecret creates new multinode auth secret.
func NewSecret() (Secret, error) {
var b [32]byte
_, err := rand.Read(b[:])
if err != nil {
return b, errs.New("error creating multinode auth secret")
}
return b, nil
}
// String implements Stringer.
func (secret Secret) String() string {
return base64.URLEncoding.EncodeToString(secret[:])
}
// IsZero returns if secret is not set.
func (secret Secret) IsZero() bool {
var zero Secret
// this doesn't need to be constant-time, because we're explicitly testing
// against a hardcoded, well-known value
return bytes.Equal(secret[:], zero[:])
}
// SecretFromBase64 creates new secret from base64 string.
func SecretFromBase64(s string) (Secret, error) {
b, err := base64.URLEncoding.DecodeString(s)
if err != nil {
return Secret{}, err
}
return SecretFromBytes(b)
}
// SecretFromBytes creates secret from bytes slice.
func SecretFromBytes(b []byte) (Secret, error) {
if len(b) != 32 {
return Secret{}, errs.New("invalid secret")
}
var secret Secret
copy(secret[:], b)
return secret, nil
}

View File

@ -1,7 +1,7 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
// Package multinodepb contains protobuf definitions for Storj peers.
// Package multinodepb contains protobuf definitions for storagenode multinode dashboard.
package multinodepb
//go:generate go run gen.go

View File

@ -16,7 +16,7 @@ import (
)
var (
mainpkg = flag.String("pkg", "storj.io/storj/multinodepb", "main package name")
mainpkg = flag.String("pkg", "storj.io/storj/private/multinodepb", "main package name")
protoc = flag.String("protoc", "protoc", "protoc compiler")
)
@ -63,7 +63,7 @@ func main() {
protofiles = ignore(protofiles)
overrideImports := ",Mgoogle/protobuf/timestamp.proto=storj.io/storj/multinodepb"
overrideImports := ",Mgoogle/protobuf/timestamp.proto=" + *mainpkg
args := []string{
"--lint_out=.",
"--drpc_out=plugins=drpc,paths=source_relative" + overrideImports + ":.",
@ -75,7 +75,9 @@ func main() {
cmd := exec.Command(*protoc, args...)
fmt.Println(strings.Join(cmd.Args, " "))
out, err := cmd.CombinedOutput()
if len(out) > 0 {
fmt.Println(string(out))
}
check(err)
}
@ -90,7 +92,9 @@ func main() {
{
// format code to get rid of extra imports
out, err := exec.Command("goimports", "-local", "storj.io", "-w", ".").CombinedOutput()
if len(out) > 0 {
fmt.Println(string(out))
}
check(err)
}
}

View File

@ -139,5 +139,4 @@ extend google.protobuf.FieldOptions {
optional bool stdduration = 65011;
optional bool wktpointer = 65012;
optional bool compare = 65013;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,97 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
syntax = "proto3";
option go_package = "storj.io/storj/private/multinodepb";
package multinode;
import "gogo.proto";
import "google/protobuf/timestamp.proto";
message RequestHeader {
bytes api_key = 1;
}
service Storage {
rpc DiskSpace(DiskSpaceRequest) returns (DiskSpaceResponse);
}
message DiskSpaceRequest {
RequestHeader header = 1;
}
message DiskSpaceResponse {
int64 allocated = 1;
int64 used_pieces = 2;
int64 used_trash = 3;
int64 free = 4;
int64 available = 5;
int64 overused = 6;
}
service Bandwidth {
rpc MonthSummary(BandwidthMonthSummaryRequest) returns (BandwidthMonthSummaryResponse);
}
message BandwidthMonthSummaryRequest {
RequestHeader header = 1;
}
message BandwidthMonthSummaryResponse {
int64 used = 1;
}
service Node {
rpc Version(VersionRequest) returns (VersionResponse);
rpc LastContact(LastContactRequest) returns (LastContactResponse);
rpc Reputation(ReputationRequest) returns (ReputationResponse);
rpc TrustedSatellites(TrustedSatellitesRequest) returns (TrustedSatellitesResponse);
}
message VersionRequest {
RequestHeader header = 1;
}
message VersionResponse {
string version = 1; // must be semver formatted
}
message LastContactRequest {
RequestHeader header = 1;
}
message LastContactResponse {
google.protobuf.Timestamp last_contact = 1 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
}
message ReputationRequest {
RequestHeader header = 1;
bytes satellite_id = 2 [(gogoproto.customtype) = "NodeID", (gogoproto.nullable) = false];
}
message ReputationResponse {
message Online {
double score = 1;
}
message Audit {
double score = 1;
double suspension_score = 2;
}
Online online = 1;
Audit audit = 2;
}
message TrustedSatellitesRequest {
RequestHeader header = 1;
}
message TrustedSatellitesResponse {
message NodeURL {
bytes node_id = 1 [(gogoproto.customtype) = "NodeID", (gogoproto.nullable) = false];
string address = 2;
}
repeated NodeURL trusted_satellites = 1;
}

View File

@ -335,6 +335,22 @@ func (client *Uplink) DeleteBucket(ctx context.Context, satellite *Satellite, bu
return nil
}
// ListBuckets returns a list of all buckets in a project.
func (client *Uplink) ListBuckets(ctx context.Context, satellite *Satellite) ([]*uplink.Bucket, error) {
var buckets = []*uplink.Bucket{}
project, err := client.GetProject(ctx, satellite)
if err != nil {
return buckets, err
}
defer func() { err = errs.Combine(err, project.Close()) }()
iter := project.ListBuckets(ctx, &uplink.ListBucketsOptions{})
for iter.Next() {
buckets = append(buckets, iter.Item())
}
return buckets, iter.Err()
}
// GetProject returns a uplink.Project which allows interactions with a specific project.
func (client *Uplink) GetProject(ctx context.Context, satellite *Satellite) (*uplink.Project, error) {
access := client.Access[satellite.ID()]

View File

@ -23,6 +23,7 @@ import (
"storj.io/common/testcontext"
"storj.io/common/testrand"
"storj.io/storj/private/testplanet"
"storj.io/storj/satellite/internalpb"
"storj.io/uplink/private/ecclient"
"storj.io/uplink/private/eestream"
)
@ -132,8 +133,8 @@ func newAddressedOrderLimit(ctx context.Context, action pb.PieceAction, satellit
key := satellite.Config.Orders.EncryptionKeys.Default
encrypted, err := key.EncryptMetadata(
serialNumber,
&pb.OrderLimitMetadata{
ProjectBucketPrefix: []byte("testprojectid/testbucketname"),
&internalpb.OrderLimitMetadata{
CompactProjectBucketPrefix: []byte("0000111122223333testbucketname"),
},
)
if err != nil {

View File

@ -269,7 +269,7 @@ func NewAPI(log *zap.Logger, full *identity.FullIdentity, db DB,
{ // setup contact service
c := config.Contact
if c.ExternalAddress == "" {
c.ExternalAddress = peer.Addr()
c.ExternalAddress = peer.Server.Addr().String()
}
pbVersion, err := versionInfo.Proto()
@ -338,10 +338,6 @@ func NewAPI(log *zap.Logger, full *identity.FullIdentity, db DB,
peer.Orders.DB,
peer.DB.Buckets(),
config.Orders,
&pb.NodeAddress{
Transport: pb.NodeTransport_TCP_TLS_GRPC,
Address: config.Contact.ExternalAddress,
},
)
if err != nil {
return nil, errs.Combine(err, peer.Close())
@ -721,7 +717,9 @@ func (peer *API) Close() error {
func (peer *API) ID() storj.NodeID { return peer.Identity.ID }
// Addr returns the public address.
func (peer *API) Addr() string { return peer.Server.Addr().String() }
func (peer *API) Addr() string {
return peer.Contact.Service.Local().Node.Address.Address
}
// URL returns the storj.NodeURL.
func (peer *API) URL() storj.NodeURL {

View File

@ -11,6 +11,7 @@ import (
"storj.io/common/testcontext"
"storj.io/storj/private/testplanet"
console "storj.io/storj/satellite/console/consolewasm"
"storj.io/uplink"
)
// TestGenerateAccessGrant confirms that the access grant produced by the wasm access code
@ -38,3 +39,44 @@ func TestGenerateAccessGrant(t *testing.T) {
require.Equal(t, wasmAccessString, uplinkCliAccessString)
})
}
// TestDefaultAccess confirms that you can perform basic uplink operations with
// the default access grant created from wasm code.
func TestDefaultAccess(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1, StorageNodeCount: 10, UplinkCount: 1,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
satellitePeer := planet.Satellites[0]
satelliteNodeURL := satellitePeer.NodeURL().String()
uplinkPeer := planet.Uplinks[0]
APIKey := uplinkPeer.APIKey[satellitePeer.ID()]
projectID := uplinkPeer.Projects[0].ID.String()
require.Equal(t, 1, len(uplinkPeer.Projects))
passphrase := "supersecretpassphrase"
testbucket1 := "buckettest1"
testfilename := "file.txt"
testdata := []byte("fun data")
// Create an access with the console access grant code that allows full access.
access, err := console.GenAccessGrant(satelliteNodeURL, APIKey.Serialize(), passphrase, projectID)
require.NoError(t, err)
newAccess, err := uplink.ParseAccess(access)
require.NoError(t, err)
uplinkPeer.Access[satellitePeer.ID()] = newAccess
// Confirm that we can create a bucket, upload/download/delete an object, and delete the bucket with the new access.
require.NoError(t, uplinkPeer.CreateBucket(ctx, satellitePeer, testbucket1))
err = uplinkPeer.Upload(ctx, satellitePeer, testbucket1, testfilename, testdata)
require.NoError(t, err)
data, err := uplinkPeer.Download(ctx, satellitePeer, testbucket1, testfilename)
require.NoError(t, err)
require.Equal(t, data, testdata)
buckets, err := uplinkPeer.ListBuckets(ctx, satellitePeer)
require.NoError(t, err)
require.Equal(t, len(buckets), 1)
err = uplinkPeer.DeleteObject(ctx, satellitePeer, testbucket1, testfilename)
require.NoError(t, err)
require.NoError(t, uplinkPeer.DeleteBucket(ctx, satellitePeer, testbucket1))
})
}

View File

@ -97,3 +97,53 @@ func TestSetPermissionWithBuckets(t *testing.T) {
require.True(t, errs2.IsRPC(err, rpcstatus.PermissionDenied))
})
}
func TestSetPermissionUplinkOperations(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1, StorageNodeCount: 10, UplinkCount: 1,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
satellitePeer := planet.Satellites[0]
satelliteNodeURL := satellitePeer.NodeURL().String()
uplinkPeer := planet.Uplinks[0]
APIKey := uplinkPeer.APIKey[satellitePeer.ID()]
apiKeyString := APIKey.Serialize()
projectID := uplinkPeer.Projects[0].ID.String()
require.Equal(t, 1, len(uplinkPeer.Projects))
allPermission := console.Permission{
AllowDownload: true,
AllowUpload: true,
AllowList: true,
AllowDelete: true,
NotBefore: time.Now().Add(-24 * time.Hour),
NotAfter: time.Now().Add(48 * time.Hour),
}
restrictedKey, err := console.SetPermission(apiKeyString, []string{}, allPermission)
require.NoError(t, err)
passphrase := "supersecretpassphrase"
restrictedAccessGrant, err := console.GenAccessGrant(satelliteNodeURL, restrictedKey.Serialize(), passphrase, projectID)
require.NoError(t, err)
restrictedAccess, err := uplink.ParseAccess(restrictedAccessGrant)
require.NoError(t, err)
uplinkPeer.APIKey[satellitePeer.ID()] = restrictedKey
uplinkPeer.Access[satellitePeer.ID()] = restrictedAccess
testbucket1 := "buckettest1"
testfilename := "file.txt"
testdata := []byte("fun data")
// Confirm that we can create a bucket, upload/download/delete an object, and delete the bucket with the new restricted access.
require.NoError(t, uplinkPeer.CreateBucket(ctx, satellitePeer, testbucket1))
err = uplinkPeer.Upload(ctx, satellitePeer, testbucket1, testfilename, testdata)
require.NoError(t, err)
data, err := uplinkPeer.Download(ctx, satellitePeer, testbucket1, testfilename)
require.NoError(t, err)
require.Equal(t, data, testdata)
buckets, err := uplinkPeer.ListBuckets(ctx, satellitePeer)
require.NoError(t, err)
require.Equal(t, len(buckets), 1)
err = uplinkPeer.DeleteObject(ctx, satellitePeer, testbucket1, testfilename)
require.NoError(t, err)
require.NoError(t, uplinkPeer.DeleteBucket(ctx, satellitePeer, testbucket1))
})
}

View File

@ -59,7 +59,7 @@ var (
// Config contains configuration for console web server.
type Config struct {
Address string `help:"server address of the graphql api gateway and frontend app" devDefault:"127.0.0.1:8081" releaseDefault:":10100"`
Address string `help:"server address of the graphql api gateway and frontend app" devDefault:"" releaseDefault:":10100"`
StaticDir string `help:"path to static resources" default:""`
ExternalAddress string `help:"external endpoint of the satellite if hosted" default:""`

View File

@ -252,10 +252,6 @@ func New(log *zap.Logger, full *identity.FullIdentity, db DB,
peer.Orders.DB,
peer.DB.Buckets(),
config.Orders,
&pb.NodeAddress{
Transport: pb.NodeTransport_TCP_TLS_GRPC,
Address: config.Contact.ExternalAddress,
},
)
if err != nil {
return nil, errs.Combine(err, peer.Close())

View File

@ -0,0 +1,100 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: ordersmeta.proto
package internalpb
import (
fmt "fmt"
math "math"
proto "github.com/gogo/protobuf/proto"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
// OrderLimitMetadata is used to transmit meta information about an order limit.
// This data will be encrypted.
type OrderLimitMetadata struct {
BucketId []byte `protobuf:"bytes,1,opt,name=bucket_id,json=bucketId,proto3" json:"bucket_id,omitempty"`
ProjectBucketPrefix []byte `protobuf:"bytes,2,opt,name=project_bucket_prefix,json=projectBucketPrefix,proto3" json:"project_bucket_prefix,omitempty"`
CompactProjectBucketPrefix []byte `protobuf:"bytes,3,opt,name=compact_project_bucket_prefix,json=compactProjectBucketPrefix,proto3" json:"compact_project_bucket_prefix,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *OrderLimitMetadata) Reset() { *m = OrderLimitMetadata{} }
func (m *OrderLimitMetadata) String() string { return proto.CompactTextString(m) }
func (*OrderLimitMetadata) ProtoMessage() {}
func (*OrderLimitMetadata) Descriptor() ([]byte, []int) {
return fileDescriptor_e00ef1afe54bb544, []int{0}
}
func (m *OrderLimitMetadata) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_OrderLimitMetadata.Unmarshal(m, b)
}
func (m *OrderLimitMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_OrderLimitMetadata.Marshal(b, m, deterministic)
}
func (m *OrderLimitMetadata) XXX_Merge(src proto.Message) {
xxx_messageInfo_OrderLimitMetadata.Merge(m, src)
}
func (m *OrderLimitMetadata) XXX_Size() int {
return xxx_messageInfo_OrderLimitMetadata.Size(m)
}
func (m *OrderLimitMetadata) XXX_DiscardUnknown() {
xxx_messageInfo_OrderLimitMetadata.DiscardUnknown(m)
}
var xxx_messageInfo_OrderLimitMetadata proto.InternalMessageInfo
func (m *OrderLimitMetadata) GetBucketId() []byte {
if m != nil {
return m.BucketId
}
return nil
}
func (m *OrderLimitMetadata) GetProjectBucketPrefix() []byte {
if m != nil {
return m.ProjectBucketPrefix
}
return nil
}
func (m *OrderLimitMetadata) GetCompactProjectBucketPrefix() []byte {
if m != nil {
return m.CompactProjectBucketPrefix
}
return nil
}
func init() {
proto.RegisterType((*OrderLimitMetadata)(nil), "satellite.ordersmeta.OrderLimitMetadata")
}
func init() { proto.RegisterFile("ordersmeta.proto", fileDescriptor_e00ef1afe54bb544) }
var fileDescriptor_e00ef1afe54bb544 = []byte{
// 192 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x2f, 0x4a, 0x49,
0x2d, 0x2a, 0xce, 0x4d, 0x2d, 0x49, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x29, 0x4e,
0x2c, 0x49, 0xcd, 0xc9, 0xc9, 0x2c, 0x49, 0xd5, 0x43, 0xc8, 0x29, 0xad, 0x60, 0xe4, 0x12, 0xf2,
0x07, 0x71, 0x7d, 0x32, 0x73, 0x33, 0x4b, 0x7c, 0x53, 0x4b, 0x12, 0x53, 0x12, 0x4b, 0x12, 0x85,
0xa4, 0xb9, 0x38, 0x93, 0x4a, 0x93, 0xb3, 0x53, 0x4b, 0xe2, 0x33, 0x53, 0x24, 0x18, 0x15, 0x18,
0x35, 0x78, 0x82, 0x38, 0x20, 0x02, 0x9e, 0x29, 0x42, 0x46, 0x5c, 0xa2, 0x05, 0x45, 0xf9, 0x59,
0xa9, 0xc9, 0x25, 0xf1, 0x50, 0x45, 0x05, 0x45, 0xa9, 0x69, 0x99, 0x15, 0x12, 0x4c, 0x60, 0x85,
0xc2, 0x50, 0x49, 0x27, 0xb0, 0x5c, 0x00, 0x58, 0x4a, 0xc8, 0x91, 0x4b, 0x36, 0x39, 0x3f, 0xb7,
0x20, 0x31, 0x19, 0xa4, 0x18, 0x9b, 0x5e, 0x66, 0xb0, 0x5e, 0x29, 0xa8, 0xa2, 0x00, 0x4c, 0x23,
0x9c, 0x54, 0xa3, 0x94, 0x8b, 0x4b, 0xf2, 0x8b, 0xb2, 0xf4, 0x32, 0xf3, 0xf5, 0xc1, 0x0c, 0x7d,
0xb8, 0x8f, 0xf4, 0x33, 0xf3, 0x4a, 0x52, 0x8b, 0xf2, 0x12, 0x73, 0x0a, 0x92, 0x92, 0xd8, 0xc0,
0xde, 0x35, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x00, 0xcc, 0x15, 0xb0, 0x02, 0x01, 0x00, 0x00,
}

View File

@ -0,0 +1,15 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
syntax = "proto3";
option go_package = "storj.io/storj/satellite/internalpb";
package satellite.ordersmeta;
// OrderLimitMetadata is used to transmit meta information about an order limit.
// This data will be encrypted.
message OrderLimitMetadata {
bytes bucket_id = 1;
bytes project_bucket_prefix = 2;
bytes compact_project_bucket_prefix = 3;
}

View File

@ -68,11 +68,31 @@ func (loc BucketLocation) Verify() error {
return nil
}
// ParseCompactBucketPrefix parses BucketPrefix.
func ParseCompactBucketPrefix(compactPrefix []byte) (BucketLocation, error) {
if len(compactPrefix) < 16 {
return BucketLocation{}, Error.New("invalid prefix %q", compactPrefix)
}
var loc BucketLocation
copy(loc.ProjectID[:], compactPrefix)
loc.BucketName = string(compactPrefix[16:])
return loc, nil
}
// Prefix converts bucket location into bucket prefix.
func (loc BucketLocation) Prefix() BucketPrefix {
return BucketPrefix(loc.ProjectID.String() + "/" + loc.BucketName)
}
// CompactPrefix converts bucket location into bucket prefix with compact project ID.
func (loc BucketLocation) CompactPrefix() []byte {
xs := make([]byte, 0, 16+len(loc.BucketName))
xs = append(xs, loc.ProjectID[:]...)
xs = append(xs, []byte(loc.BucketName)...)
return xs
}
// ObjectKey is an encrypted object key encoded using Path Component Encoding.
// It is not ascii safe.
type ObjectKey string

View File

@ -13,6 +13,7 @@ import (
"storj.io/common/pb"
"storj.io/common/storj"
"storj.io/storj/satellite/internalpb"
)
// ErrEncryptionKey is error class used for keys.
@ -82,7 +83,7 @@ func (key *EncryptionKey) Decrypt(ciphertext []byte, nonce storj.SerialNumber) (
}
// EncryptMetadata encrypts order limit metadata.
func (key *EncryptionKey) EncryptMetadata(serial storj.SerialNumber, metadata *pb.OrderLimitMetadata) ([]byte, error) {
func (key *EncryptionKey) EncryptMetadata(serial storj.SerialNumber, metadata *internalpb.OrderLimitMetadata) ([]byte, error) {
marshaled, err := pb.Marshal(metadata)
if err != nil {
return nil, ErrEncryptionKey.Wrap(err)
@ -91,13 +92,13 @@ func (key *EncryptionKey) EncryptMetadata(serial storj.SerialNumber, metadata *p
}
// DecryptMetadata decrypts order limit metadata.
func (key *EncryptionKey) DecryptMetadata(serial storj.SerialNumber, encrypted []byte) (*pb.OrderLimitMetadata, error) {
func (key *EncryptionKey) DecryptMetadata(serial storj.SerialNumber, encrypted []byte) (*internalpb.OrderLimitMetadata, error) {
decrypted, err := key.Decrypt(encrypted, serial)
if err != nil {
return nil, ErrEncryptionKey.Wrap(err)
}
metadata := &pb.OrderLimitMetadata{}
metadata := &internalpb.OrderLimitMetadata{}
err = pb.Unmarshal(decrypted, metadata)
if err != nil {
return nil, ErrEncryptionKey.Wrap(err)

View File

@ -649,14 +649,29 @@ func (endpoint *Endpoint) SettlementWithWindowFinal(stream pb.DRPCOrders_Settlem
mon.Event("bucketinfo_from_orders_metadata_error_1")
continue
}
bucketInfo, err := metabase.ParseBucketPrefix(
metabase.BucketPrefix(metadata.GetProjectBucketPrefix()),
)
var bucketInfo metabase.BucketLocation
switch {
case len(metadata.CompactProjectBucketPrefix) > 0:
bucketInfo, err = metabase.ParseCompactBucketPrefix(metadata.GetCompactProjectBucketPrefix())
if err != nil {
log.Debug("decrypt order: ParseBucketPrefix", zap.Error(err))
mon.Event("bucketinfo_from_orders_metadata_error_2")
log.Debug("decrypt order: ParseCompactBucketPrefix", zap.Error(err))
mon.Event("bucketinfo_from_orders_metadata_error_compact")
continue
}
case len(metadata.ProjectBucketPrefix) > 0:
bucketInfo, err = metabase.ParseBucketPrefix(metabase.BucketPrefix(metadata.GetProjectBucketPrefix()))
if err != nil {
log.Debug("decrypt order: ParseBucketPrefix", zap.Error(err))
mon.Event("bucketinfo_from_orders_metadata_error_uncompact")
continue
}
default:
log.Debug("decrypt order: project bucket prefix missing", zap.Error(err))
mon.Event("bucketinfo_from_orders_metadata_error_default")
continue
}
if bucketInfo.BucketName == "" || bucketInfo.ProjectID.IsZero() {
log.Info("decrypt order: bucketName or projectID not set",
zap.String("bucketName", bucketInfo.BucketName),

View File

@ -20,6 +20,8 @@ import (
"storj.io/common/testrand"
"storj.io/storj/private/testplanet"
"storj.io/storj/satellite"
"storj.io/storj/satellite/internalpb"
"storj.io/storj/satellite/metainfo/metabase"
"storj.io/storj/satellite/orders"
)
@ -52,7 +54,10 @@ func TestSettlementWithWindowEndpointManyOrders(t *testing.T) {
now := time.Now()
projectID := testrand.UUID()
bucketname := "testbucket"
bucketID := storj.JoinPaths(projectID.String(), bucketname)
bucketLocation := metabase.BucketLocation{
ProjectID: projectID,
BucketName: bucketname,
}
key := satellite.Config.Orders.EncryptionKeys.Default
// stop any async flushes because we want to be sure when some values are
@ -84,8 +89,8 @@ func TestSettlementWithWindowEndpointManyOrders(t *testing.T) {
serialNumber1 := testrand.SerialNumber()
encrypted1, err := key.EncryptMetadata(
serialNumber1,
&pb.OrderLimitMetadata{
ProjectBucketPrefix: []byte(bucketID),
&internalpb.OrderLimitMetadata{
CompactProjectBucketPrefix: bucketLocation.CompactPrefix(),
},
)
require.NoError(t, err)
@ -93,8 +98,8 @@ func TestSettlementWithWindowEndpointManyOrders(t *testing.T) {
serialNumber2 := testrand.SerialNumber()
encrypted2, err := key.EncryptMetadata(
serialNumber2,
&pb.OrderLimitMetadata{
ProjectBucketPrefix: []byte(bucketID),
&internalpb.OrderLimitMetadata{
CompactProjectBucketPrefix: bucketLocation.CompactPrefix(),
},
)
require.NoError(t, err)
@ -210,7 +215,10 @@ func TestSettlementWithWindowEndpointSingleOrder(t *testing.T) {
now := time.Now()
projectID := testrand.UUID()
bucketname := "testbucket"
bucketID := storj.JoinPaths(projectID.String(), bucketname)
bucketLocation := metabase.BucketLocation{
ProjectID: projectID,
BucketName: bucketname,
}
key := satellite.Config.Orders.EncryptionKeys.Default
// stop any async flushes because we want to be sure when some values are
@ -231,8 +239,8 @@ func TestSettlementWithWindowEndpointSingleOrder(t *testing.T) {
serialNumber := testrand.SerialNumber()
encrypted, err := key.EncryptMetadata(
serialNumber,
&pb.OrderLimitMetadata{
ProjectBucketPrefix: []byte(bucketID),
&internalpb.OrderLimitMetadata{
CompactProjectBucketPrefix: bucketLocation.CompactPrefix(),
},
)
require.NoError(t, err)
@ -333,7 +341,10 @@ func TestSettlementWithWindowEndpointErrors(t *testing.T) {
now := time.Now()
projectID := testrand.UUID()
bucketname := "testbucket"
bucketID := storj.JoinPaths(projectID.String(), bucketname)
bucketLocation := metabase.BucketLocation{
ProjectID: projectID,
BucketName: bucketname,
}
// stop any async flushes because we want to be sure when some values are
// written to avoid races
@ -351,11 +362,11 @@ func TestSettlementWithWindowEndpointErrors(t *testing.T) {
// create serial number to use in test
serialNumber1 := testrand.SerialNumber()
err = ordersDB.CreateSerialInfo(ctx, serialNumber1, []byte(bucketID), now.AddDate(1, 0, 10))
err = ordersDB.CreateSerialInfo(ctx, serialNumber1, []byte(bucketLocation.Prefix()), now.AddDate(1, 0, 10))
require.NoError(t, err)
serialNumber2 := testrand.SerialNumber()
err = ordersDB.CreateSerialInfo(ctx, serialNumber2, []byte(bucketID), now.AddDate(1, 0, 10))
err = ordersDB.CreateSerialInfo(ctx, serialNumber2, []byte(bucketLocation.Prefix()), now.AddDate(1, 0, 10))
require.NoError(t, err)
piecePublicKey1, piecePrivateKey1, err := storj.NewPieceKey()
@ -366,8 +377,8 @@ func TestSettlementWithWindowEndpointErrors(t *testing.T) {
key := satellite.Config.Orders.EncryptionKeys.Default
encrypted, err := key.EncryptMetadata(
serialNumber1,
&pb.OrderLimitMetadata{
ProjectBucketPrefix: []byte(bucketID),
&internalpb.OrderLimitMetadata{
CompactProjectBucketPrefix: bucketLocation.CompactPrefix(),
},
)
require.NoError(t, err)
@ -468,8 +479,10 @@ func TestSettlementEndpointSingleOrder(t *testing.T) {
now := time.Now()
projectID := testrand.UUID()
bucketname := "testbucket"
bucketID := storj.JoinPaths(projectID.String(), bucketname)
bucketLocation := metabase.BucketLocation{
ProjectID: projectID,
BucketName: bucketname,
}
// stop any async flushes because we want to be sure when some values are
// written to avoid races
satellite.Orders.Chore.Loop.Pause()
@ -486,7 +499,7 @@ func TestSettlementEndpointSingleOrder(t *testing.T) {
// create serial number to use in test
serialNumber := testrand.SerialNumber()
err = ordersDB.CreateSerialInfo(ctx, serialNumber, []byte(bucketID), now.AddDate(1, 0, 10))
err = ordersDB.CreateSerialInfo(ctx, serialNumber, []byte(bucketLocation.Prefix()), now.AddDate(1, 0, 10))
require.NoError(t, err)
piecePublicKey, piecePrivateKey, err := storj.NewPieceKey()
@ -494,8 +507,8 @@ func TestSettlementEndpointSingleOrder(t *testing.T) {
key := satellite.Config.Orders.EncryptionKeys.Default
encrypted, err := key.EncryptMetadata(
serialNumber,
&pb.OrderLimitMetadata{
ProjectBucketPrefix: []byte(bucketID),
&internalpb.OrderLimitMetadata{
CompactProjectBucketPrefix: bucketLocation.CompactPrefix(),
},
)
require.NoError(t, err)

View File

@ -17,6 +17,7 @@ import (
"storj.io/common/signing"
"storj.io/common/storj"
"storj.io/common/uuid"
"storj.io/storj/satellite/internalpb"
"storj.io/storj/satellite/metainfo/metabase"
"storj.io/storj/satellite/overlay"
"storj.io/uplink/private/eestream"
@ -60,7 +61,6 @@ type Service struct {
encryptionKeys EncryptionKeys
satelliteAddress *pb.NodeAddress
orderExpiration time.Duration
rngMu sync.Mutex
@ -72,7 +72,6 @@ func NewService(
log *zap.Logger, satellite signing.Signer, overlay *overlay.Service,
orders DB, buckets BucketsDB,
config Config,
satelliteAddress *pb.NodeAddress,
) (*Service, error) {
if config.EncryptionKeys.Default.IsZero() {
return nil, Error.New("encryption keys must be specified to include encrypted metadata")
@ -87,7 +86,6 @@ func NewService(
encryptionKeys: config.EncryptionKeys,
satelliteAddress: satelliteAddress,
orderExpiration: config.Expiration,
rng: mathrand.New(mathrand.NewSource(time.Now().UnixNano())),
@ -583,7 +581,7 @@ func (service *Service) UpdatePutInlineOrder(ctx context.Context, bucket metabas
}
// DecryptOrderMetadata decrypts the order metadata.
func (service *Service) DecryptOrderMetadata(ctx context.Context, order *pb.OrderLimit) (_ *pb.OrderLimitMetadata, err error) {
func (service *Service) DecryptOrderMetadata(ctx context.Context, order *pb.OrderLimit) (_ *internalpb.OrderLimitMetadata, err error) {
defer mon.Task()(&ctx)(&err)
var orderKeyID EncryptionKeyID

View File

@ -54,9 +54,7 @@ func TestOrderLimitsEncryptedMetadata(t *testing.T) {
require.Error(t, err)
actualOrderMetadata, err := satellitePeer.Orders.Service.DecryptOrderMetadata(ctx, orderLimit1)
require.NoError(t, err)
actualBucketInfo, err := metabase.ParseBucketPrefix(
metabase.BucketPrefix(actualOrderMetadata.GetProjectBucketPrefix()),
)
actualBucketInfo, err := metabase.ParseCompactBucketPrefix(actualOrderMetadata.GetCompactProjectBucketPrefix())
require.NoError(t, err)
require.Equal(t, bucketName, actualBucketInfo.BucketName)
require.Equal(t, projectID, actualBucketInfo.ProjectID)

View File

@ -14,6 +14,7 @@ import (
"storj.io/common/pb"
"storj.io/common/signing"
"storj.io/common/storj"
"storj.io/storj/satellite/internalpb"
"storj.io/storj/satellite/metainfo/metabase"
)
@ -148,8 +149,8 @@ func (signer *Signer) Sign(ctx context.Context, node storj.NodeURL, pieceNum int
encrypted, err := encryptionKey.EncryptMetadata(
signer.Serial,
&pb.OrderLimitMetadata{
ProjectBucketPrefix: []byte(signer.Bucket.Prefix()),
&internalpb.OrderLimitMetadata{
CompactProjectBucketPrefix: signer.Bucket.CompactPrefix(),
},
)
if err != nil {
@ -173,7 +174,7 @@ func (signer *Signer) Sign(ctx context.Context, node storj.NodeURL, pieceNum int
OrderCreation: signer.OrderCreation,
OrderExpiration: signer.OrderExpiration,
SatelliteAddress: signer.Service.satelliteAddress,
SatelliteAddress: nil,
EncryptedMetadataKeyId: signer.EncryptedMetadataKeyID,
EncryptedMetadata: signer.EncryptedMetadata,

View File

@ -41,7 +41,7 @@ func TestSigner_EncryptedMetadata(t *testing.T) {
project, err := uplink.GetProject(ctx, satellite)
require.NoError(t, err)
bucketName := "testbucket"
bucketName := "123456789012345678901234567890123456789012345678901234567890123"
bucketLocation := metabase.BucketLocation{
ProjectID: uplink.Projects[0].ID,
BucketName: bucketName,
@ -70,7 +70,7 @@ func TestSigner_EncryptedMetadata(t *testing.T) {
metadata, err := ekeys.Default.DecryptMetadata(addressedLimit.Limit.SerialNumber, addressedLimit.Limit.EncryptedMetadata)
require.NoError(t, err)
bucketInfo, err := metabase.ParseBucketPrefix(metabase.BucketPrefix(metadata.ProjectBucketPrefix))
bucketInfo, err := metabase.ParseCompactBucketPrefix(metadata.CompactProjectBucketPrefix)
require.NoError(t, err)
require.Equal(t, bucketInfo.BucketName, bucketName)
require.Equal(t, bucketInfo.ProjectID, uplink.Projects[0].ID)
@ -96,11 +96,13 @@ func TestSigner_EncryptedMetadata_UploadDownload(t *testing.T) {
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
satellite, uplink := planet.Satellites[0], planet.Uplinks[0]
const bucket = "123456789012345678901234567890123456789012345678901234567890123"
testdata := testrand.Bytes(8 * memory.KiB)
err := uplink.Upload(ctx, satellite, "testbucket", "data", testdata)
err := uplink.Upload(ctx, satellite, bucket, "data", testdata)
require.NoError(t, err)
downdata, err := uplink.Download(ctx, satellite, "testbucket", "data")
downdata, err := uplink.Download(ctx, satellite, bucket, "data")
require.NoError(t, err)
require.Equal(t, testdata, downdata)

View File

@ -99,10 +99,6 @@ type DB interface {
SuspendNodeUnknownAudit(ctx context.Context, nodeID storj.NodeID, suspendedAt time.Time) (err error)
// UnsuspendNodeUnknownAudit unsuspends a storage node for unknown audits.
UnsuspendNodeUnknownAudit(ctx context.Context, nodeID storj.NodeID) (err error)
// SuspendNodeOfflineAudit suspends a storage node for offline audits.
SuspendNodeOfflineAudit(ctx context.Context, nodeID storj.NodeID, suspendedAt time.Time) (err error)
// UnsuspendNodeOfflineAudit unsuspends a storage node for offline audits.
UnsuspendNodeOfflineAudit(ctx context.Context, nodeID storj.NodeID) (err error)
// TestVetNode directly sets a node's vetted_at timestamp to make testing easier
TestVetNode(ctx context.Context, nodeID storj.NodeID) (vettedTime *time.Time, err error)

View File

@ -463,15 +463,11 @@ func TestKnownReliable(t *testing.T) {
require.NoError(t, err)
require.False(t, service.IsOnline(node))
// Suspend storage node #2 for unknown audits
// Suspend storage node #2
err = satellite.DB.OverlayCache().SuspendNodeUnknownAudit(ctx, planet.StorageNodes[2].ID(), time.Now())
require.NoError(t, err)
// Suspend storage node #3 for offline audits
err = satellite.DB.OverlayCache().SuspendNodeOfflineAudit(ctx, planet.StorageNodes[3].ID(), time.Now())
require.NoError(t, err)
// Check that only storage nodes #4 is reliable
// Check that only storage nodes #3 and #4 are reliable
result, err := service.KnownReliable(ctx, []storj.NodeID{
planet.StorageNodes[0].ID(),
planet.StorageNodes[1].ID(),
@ -480,10 +476,11 @@ func TestKnownReliable(t *testing.T) {
planet.StorageNodes[4].ID(),
})
require.NoError(t, err)
require.Len(t, result, 1)
require.Len(t, result, 2)
// Sort the storage nodes for predictable checks
expectedReliable := []storj.NodeURL{
planet.StorageNodes[3].NodeURL(),
planet.StorageNodes[4].NodeURL(),
}
sort.Slice(expectedReliable, func(i, j int) bool { return expectedReliable[i].ID.Less(expectedReliable[j].ID) })

View File

@ -377,7 +377,7 @@ func (obs *checkerObserver) RemoteSegment(ctx context.Context, segment *metainfo
Path: key,
LostPieces: missingPieces,
InsertedTime: time.Now().UTC(),
}, float64(numHealthy))
}, segmentHealth)
if err != nil {
obs.log.Error("error adding injured segment to queue", zap.Error(err))
return nil

View File

@ -14,7 +14,6 @@ import (
"golang.org/x/sync/errgroup"
"storj.io/common/identity"
"storj.io/common/pb"
"storj.io/common/peertls/extensions"
"storj.io/common/peertls/tlsopts"
"storj.io/common/rpc"
@ -162,10 +161,6 @@ func NewRepairer(log *zap.Logger, full *identity.FullIdentity,
peer.Orders.DB,
bucketsDB,
config.Orders,
&pb.NodeAddress{
Transport: pb.NodeTransport_TCP_TLS_GRPC,
Address: config.Contact.ExternalAddress,
},
)
if err != nil {
return nil, errs.Combine(err, peer.Close())

View File

@ -181,7 +181,6 @@ func nodeSelectionCondition(ctx context.Context, criteria *overlay.NodeCriteria,
var conds conditions
conds.add(`disqualified IS NULL`)
conds.add(`unknown_audit_suspended IS NULL`)
conds.add(`offline_suspended IS NULL`)
conds.add(`exit_initiated_at IS NULL`)
conds.add(`type = ?`, int(pb.NodeType_STORAGE))

View File

@ -62,7 +62,6 @@ func (cache *overlaycache) selectAllStorageNodesUpload(ctx context.Context, sele
FROM nodes ` + asOf + `
WHERE disqualified IS NULL
AND unknown_audit_suspended IS NULL
AND offline_suspended IS NULL
AND exit_initiated_at IS NULL
AND type = $1
AND free_disk >= $2
@ -313,7 +312,6 @@ func (cache *overlaycache) knownUnreliableOrOffline(ctx context.Context, criteri
WHERE id = any($1::bytea[])
AND disqualified IS NULL
AND unknown_audit_suspended IS NULL
AND offline_suspended IS NULL
AND exit_finished_at IS NULL
AND last_contact_success > $2
`), pgutil.NodeIDArray(nodeIDs), time.Now().Add(-criteria.OnlineWindow),
@ -370,7 +368,6 @@ func (cache *overlaycache) knownReliable(ctx context.Context, onlineWindow time.
WHERE id = any($1::bytea[])
AND disqualified IS NULL
AND unknown_audit_suspended IS NULL
AND offline_suspended IS NULL
AND exit_finished_at IS NULL
AND last_contact_success > $2
`), pgutil.NodeIDArray(nodeIDs), time.Now().Add(-onlineWindow),
@ -419,7 +416,6 @@ func (cache *overlaycache) reliable(ctx context.Context, criteria *overlay.NodeC
SELECT id FROM nodes `+asOf+`
WHERE disqualified IS NULL
AND unknown_audit_suspended IS NULL
AND offline_suspended IS NULL
AND exit_finished_at IS NULL
AND last_contact_success > ?
`), time.Now().Add(-criteria.OnlineWindow))
@ -749,38 +745,6 @@ func (cache *overlaycache) UnsuspendNodeUnknownAudit(ctx context.Context, nodeID
return nil
}
// SuspendNodeOfflineAudit suspends a storage node for offline audits.
func (cache *overlaycache) SuspendNodeOfflineAudit(ctx context.Context, nodeID storj.NodeID, suspendedAt time.Time) (err error) {
defer mon.Task()(&ctx)(&err)
updateFields := dbx.Node_Update_Fields{}
updateFields.OfflineSuspended = dbx.Node_OfflineSuspended(suspendedAt.UTC())
dbNode, err := cache.db.Update_Node_By_Id(ctx, dbx.Node_Id(nodeID.Bytes()), updateFields)
if err != nil {
return err
}
if dbNode == nil {
return errs.New("unable to get node by ID: %v", nodeID)
}
return nil
}
// UnsuspendNodeOfflineAudit unsuspends a storage node for offline audits.
func (cache *overlaycache) UnsuspendNodeOfflineAudit(ctx context.Context, nodeID storj.NodeID) (err error) {
defer mon.Task()(&ctx)(&err)
updateFields := dbx.Node_Update_Fields{}
updateFields.OfflineSuspended = dbx.Node_OfflineSuspended_Null()
dbNode, err := cache.db.Update_Node_By_Id(ctx, dbx.Node_Id(nodeID.Bytes()), updateFields)
if err != nil {
return err
}
if dbNode == nil {
return errs.New("unable to get node by ID: %v", nodeID)
}
return nil
}
// AllPieceCounts returns a map of node IDs to piece counts from the db.
// NB: a valid, partial piece map can be returned even if node ID parsing error(s) are returned.
func (cache *overlaycache) AllPieceCounts(ctx context.Context) (_ map[storj.NodeID]int, err error) {

View File

@ -4,78 +4,35 @@
package apikeys
import (
"bytes"
"context"
"crypto/rand"
"encoding/base64"
"time"
"github.com/zeebo/errs"
"storj.io/storj/private/multinodeauth"
)
// ErrNoSecret represents errors from the apikey database.
var ErrNoSecret = errs.Class("no apikey error")
// ErrNoAPIKey represents no api key error.
var ErrNoAPIKey = errs.Class("no api key error")
// DB is interface for working with apikey tokens.
// DB is interface for working with api keys.
//
// architecture: Database
type DB interface {
// Store stores apikey token into db.
Store(ctx context.Context, secret APIKey) error
// Store stores api key into db.
Store(ctx context.Context, apiKey APIKey) error
// Check checks if unique apikey exists in db by token.
Check(ctx context.Context, token Secret) error
// Check checks if api key exists in db by secret.
Check(ctx context.Context, secret multinodeauth.Secret) error
// Revoke removes token from db.
Revoke(ctx context.Context, token Secret) error
// Revoke removes api key from db.
Revoke(ctx context.Context, secret multinodeauth.Secret) error
}
// Secret stores token of storagenode APIkey.
type Secret [32]byte
// APIKey describing apikey model in the database.
// APIKey describing api key in the database.
type APIKey struct {
// Secret is PK of the table and keeps unique value sno apikey token
Secret Secret
// APIKeys is PK of the table and keeps unique value sno api key.
Secret multinodeauth.Secret
CreatedAt time.Time `json:"createdAt"`
}
// NewSecret creates new apikey secret.
func NewSecret() (Secret, error) {
var b [32]byte
_, err := rand.Read(b[:])
if err != nil {
return b, errs.New("error creating apikey token")
}
return b, nil
}
// String implements Stringer.
func (secret Secret) String() string {
return base64.URLEncoding.EncodeToString(secret[:])
}
// IsZero returns if the apikey token is not set.
func (secret Secret) IsZero() bool {
var zero Secret
// this doesn't need to be constant-time, because we're explicitly testing
// against a hardcoded, well-known value
return bytes.Equal(secret[:], zero[:])
}
// TokenSecretFromBase64 creates new apikey token from base64 string.
func TokenSecretFromBase64(s string) (Secret, error) {
var token Secret
b, err := base64.URLEncoding.DecodeString(s)
if err != nil {
return token, err
}
copy(token[:], b)
return token, nil
}

View File

@ -10,42 +10,42 @@ import (
"github.com/zeebo/assert"
"storj.io/common/testcontext"
"storj.io/storj/private/multinodeauth"
"storj.io/storj/storagenode"
"storj.io/storj/storagenode/apikeys"
"storj.io/storj/storagenode/storagenodedb/storagenodedbtest"
)
func TestSecretDB(t *testing.T) {
func TestAPIKeysDB(t *testing.T) {
storagenodedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db storagenode.DB) {
secrets := db.Secret()
token, err := apikeys.NewSecret()
apiKeys := db.APIKeys()
secret, err := multinodeauth.NewSecret()
assert.NoError(t, err)
token2, err := apikeys.NewSecret()
secret2, err := multinodeauth.NewSecret()
assert.NoError(t, err)
t.Run("Test StoreSecret", func(t *testing.T) {
err := secrets.Store(ctx, apikeys.APIKey{
Secret: token,
t.Run("Store", func(t *testing.T) {
err := apiKeys.Store(ctx, apikeys.APIKey{
Secret: secret,
CreatedAt: time.Now().UTC(),
})
assert.NoError(t, err)
})
t.Run("Test CheckSecret", func(t *testing.T) {
err := secrets.Check(ctx, token)
t.Run("Check", func(t *testing.T) {
err := apiKeys.Check(ctx, secret)
assert.NoError(t, err)
err = secrets.Check(ctx, token2)
err = apiKeys.Check(ctx, secret2)
assert.Error(t, err)
})
t.Run("Test RevokeSecret", func(t *testing.T) {
err = secrets.Revoke(ctx, token)
t.Run("Revoke", func(t *testing.T) {
err = apiKeys.Revoke(ctx, secret)
assert.NoError(t, err)
err = secrets.Check(ctx, token)
err = apiKeys.Check(ctx, secret)
assert.Error(t, err)
})
})
}

View File

@ -9,6 +9,8 @@ import (
"github.com/spacemonkeygo/monkit/v3"
"github.com/zeebo/errs"
"storj.io/storj/private/multinodeauth"
)
var (
@ -33,7 +35,7 @@ func NewService(db DB) *Service {
// Issue generates new api key and stores it into db.
func (service *Service) Issue(ctx context.Context) (apiKey APIKey, err error) {
defer mon.Task()(&ctx)(&err)
secret, err := NewSecret()
secret, err := multinodeauth.NewSecret()
if err != nil {
return APIKey{}, ErrService.Wrap(err)
}
@ -50,14 +52,14 @@ func (service *Service) Issue(ctx context.Context) (apiKey APIKey, err error) {
}
// Check returns error if api key does not exists.
func (service *Service) Check(ctx context.Context, secret Secret) (err error) {
func (service *Service) Check(ctx context.Context, secret multinodeauth.Secret) (err error) {
defer mon.Task()(&ctx)(&err)
return service.store.Check(ctx, secret)
}
// Remove revokes apikey, deletes it from db.
func (service *Service) Remove(ctx context.Context, secret Secret) (err error) {
func (service *Service) Remove(ctx context.Context, secret multinodeauth.Secret) (err error) {
defer mon.Task()(&ctx)(&err)
return ErrService.Wrap(service.store.Revoke(ctx, secret))

View File

@ -244,6 +244,7 @@ type Satellite struct {
Audit reputation.Metric `json:"audit"`
Uptime reputation.Metric `json:"uptime"`
OnlineScore float64 `json:"onlineScore"`
AuditHistory reputation.AuditHistory `json:"auditHistory"`
PriceModel PriceModel `json:"priceModel"`
NodeJoinedAt time.Time `json:"nodeJoinedAt"`
}
@ -317,6 +318,7 @@ func (s *Service) GetSatelliteData(ctx context.Context, satelliteID storj.NodeID
Audit: rep.Audit,
Uptime: rep.Uptime,
OnlineScore: rep.OnlineScore,
AuditHistory: reputation.GetAuditHistoryFromPB(rep.AuditHistory),
PriceModel: satellitePricing,
NodeJoinedAt: rep.JoinedAt,
}, nil

View File

@ -27,6 +27,16 @@ var (
Error = errs.Class("piecestore monitor")
)
// DiskSpace consolidates monitored disk space statistics.
type DiskSpace struct {
Allocated int64
UsedForPieces int64
UsedForTrash int64
Free int64
Available int64
Overused int64
}
// Config defines parameters for storage node disk and bandwidth usage monitoring.
type Config struct {
Interval time.Duration `help:"how frequently Kademlia bucket should be refreshed with node stats" default:"1h0m0s"`
@ -81,9 +91,9 @@ func (service *Service) Run(ctx context.Context) (err error) {
}
freeDiskSpace := storageStatus.DiskFree
totalUsed, err := service.usedSpace(ctx)
totalUsed, err := service.store.SpaceUsedForPiecesAndTrash(ctx)
if err != nil {
return err
return Error.Wrap(err)
}
// check your hard drive is big enough
@ -184,21 +194,13 @@ func (service *Service) updateNodeInformation(ctx context.Context) (err error) {
return nil
}
func (service *Service) usedSpace(ctx context.Context) (_ int64, err error) {
defer mon.Task()(&ctx)(&err)
usedSpace, err := service.store.SpaceUsedForPiecesAndTrash(ctx)
if err != nil {
return 0, err
}
return usedSpace, nil
}
// AvailableSpace returns available disk space for upload.
func (service *Service) AvailableSpace(ctx context.Context) (_ int64, err error) {
defer mon.Task()(&ctx)(&err)
usedSpace, err := service.usedSpace(ctx)
usedSpace, err := service.store.SpaceUsedForPiecesAndTrash(ctx)
if err != nil {
return 0, Error.Wrap(err)
return 0, err
}
freeSpaceForStorj := service.allocatedDiskSpace - usedSpace
@ -217,3 +219,41 @@ func (service *Service) AvailableSpace(ctx context.Context) (_ int64, err error)
return freeSpaceForStorj, nil
}
// DiskSpace returns consolidated disk space state info.
func (service *Service) DiskSpace(ctx context.Context) (_ DiskSpace, err error) {
defer mon.Task()(&ctx)(&err)
usedForPieces, _, err := service.store.SpaceUsedForPieces(ctx)
if err != nil {
return DiskSpace{}, Error.Wrap(err)
}
usedForTrash, err := service.store.SpaceUsedForTrash(ctx)
if err != nil {
return DiskSpace{}, Error.Wrap(err)
}
storageStatus, err := service.store.StorageStatus(ctx)
if err != nil {
return DiskSpace{}, Error.Wrap(err)
}
overused := int64(0)
available := service.allocatedDiskSpace - (usedForPieces + usedForTrash)
if available < 0 {
overused = -available
}
if storageStatus.DiskFree < available {
available = storageStatus.DiskFree
}
return DiskSpace{
Allocated: service.allocatedDiskSpace,
UsedForPieces: usedForPieces,
UsedForTrash: usedForTrash,
Free: storageStatus.DiskFree,
Available: available,
Overused: overused,
}, nil
}

View File

@ -0,0 +1,26 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package multinode
import (
"context"
"storj.io/storj/private/multinodeauth"
"storj.io/storj/private/multinodepb"
"storj.io/storj/storagenode/apikeys"
)
// authenticate checks if request header contains valid api key.
func authenticate(ctx context.Context, apiKeys *apikeys.Service, header *multinodepb.RequestHeader) error {
secret, err := multinodeauth.SecretFromBytes(header.GetApiKey())
if err != nil {
return err
}
if err = apiKeys.Check(ctx, secret); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,54 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package multinode
import (
"context"
"time"
"go.uber.org/zap"
"storj.io/common/rpc/rpcstatus"
"storj.io/storj/private/multinodepb"
"storj.io/storj/storagenode/apikeys"
"storj.io/storj/storagenode/bandwidth"
)
var _ multinodepb.DRPCBandwidthServer = (*BandwidthEndpoint)(nil)
// BandwidthEndpoint implements multinode bandwidth endpoint.
//
// architecture: Endpoint
type BandwidthEndpoint struct {
log *zap.Logger
apiKeys *apikeys.Service
db bandwidth.DB
}
// NewBandwidthEndpoint creates new multinode bandwidth endpoint.
func NewBandwidthEndpoint(log *zap.Logger, apiKeys *apikeys.Service, db bandwidth.DB) *BandwidthEndpoint {
return &BandwidthEndpoint{
log: log,
apiKeys: apiKeys,
db: db,
}
}
// MonthSummary returns bandwidth used current month.
func (bandwidth *BandwidthEndpoint) MonthSummary(ctx context.Context, req *multinodepb.BandwidthMonthSummaryRequest) (_ *multinodepb.BandwidthMonthSummaryResponse, err error) {
defer mon.Task()(&ctx)(&err)
if err = authenticate(ctx, bandwidth.apiKeys, req.GetHeader()); err != nil {
return nil, rpcstatus.Wrap(rpcstatus.Unauthenticated, err)
}
used, err := bandwidth.db.MonthSummary(ctx, time.Now())
if err != nil {
return nil, rpcstatus.Wrap(rpcstatus.Internal, err)
}
return &multinodepb.BandwidthMonthSummaryResponse{
Used: used,
}, nil
}

View File

@ -0,0 +1,12 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package multinode
import (
"github.com/spacemonkeygo/monkit/v3"
)
var (
mon = monkit.Package()
)

View File

@ -0,0 +1,120 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package multinode
import (
"context"
"go.uber.org/zap"
"storj.io/common/rpc/rpcstatus"
"storj.io/private/version"
"storj.io/storj/private/multinodepb"
"storj.io/storj/storagenode/apikeys"
"storj.io/storj/storagenode/contact"
"storj.io/storj/storagenode/reputation"
"storj.io/storj/storagenode/trust"
)
var _ multinodepb.DRPCNodeServer = (*NodeEndpoint)(nil)
// NodeEndpoint implements multinode node endpoint.
//
// architecture: Endpoint
type NodeEndpoint struct {
log *zap.Logger
apiKeys *apikeys.Service
version version.Info
contact *contact.PingStats
reputation reputation.DB
trust *trust.Pool
}
// NewNodeEndpoint creates new multinode node endpoint.
func NewNodeEndpoint(log *zap.Logger, apiKeys *apikeys.Service, version version.Info, contact *contact.PingStats, reputation reputation.DB, trust *trust.Pool) *NodeEndpoint {
return &NodeEndpoint{
log: log,
apiKeys: apiKeys,
version: version,
contact: contact,
reputation: reputation,
trust: trust,
}
}
// Version returns node current version.
func (node *NodeEndpoint) Version(ctx context.Context, req *multinodepb.VersionRequest) (_ *multinodepb.VersionResponse, err error) {
defer mon.Task()(&ctx)(&err)
if err = authenticate(ctx, node.apiKeys, req.GetHeader()); err != nil {
return nil, rpcstatus.Wrap(rpcstatus.Unauthenticated, err)
}
return &multinodepb.VersionResponse{
Version: node.version.Version.String(),
}, nil
}
// LastContact returns timestamp when node was last in contact with satellite.
func (node *NodeEndpoint) LastContact(ctx context.Context, req *multinodepb.LastContactRequest) (_ *multinodepb.LastContactResponse, err error) {
defer mon.Task()(&ctx)(&err)
if err = authenticate(ctx, node.apiKeys, req.GetHeader()); err != nil {
return nil, rpcstatus.Wrap(rpcstatus.Unauthenticated, err)
}
return &multinodepb.LastContactResponse{
LastContact: node.contact.WhenLastPinged(),
}, nil
}
// Reputation returns reputation for specific satellite.
func (node *NodeEndpoint) Reputation(ctx context.Context, req *multinodepb.ReputationRequest) (_ *multinodepb.ReputationResponse, err error) {
defer mon.Task()(&ctx)(&err)
if err = authenticate(ctx, node.apiKeys, req.GetHeader()); err != nil {
return nil, rpcstatus.Wrap(rpcstatus.Unauthenticated, err)
}
rep, err := node.reputation.Get(ctx, req.SatelliteId)
if err != nil {
return nil, rpcstatus.Wrap(rpcstatus.Internal, err)
}
return &multinodepb.ReputationResponse{
Online: &multinodepb.ReputationResponse_Online{
Score: rep.OnlineScore,
},
Audit: &multinodepb.ReputationResponse_Audit{
Score: rep.Audit.Score,
SuspensionScore: rep.Audit.UnknownScore,
},
}, nil
}
// TrustedSatellites returns list of trusted satellites node urls.
func (node *NodeEndpoint) TrustedSatellites(ctx context.Context, req *multinodepb.TrustedSatellitesRequest) (_ *multinodepb.TrustedSatellitesResponse, err error) {
defer mon.Task()(&ctx)(&err)
if err = authenticate(ctx, node.apiKeys, req.GetHeader()); err != nil {
return nil, rpcstatus.Wrap(rpcstatus.Unauthenticated, err)
}
response := new(multinodepb.TrustedSatellitesResponse)
satellites := node.trust.GetSatellites(ctx)
for _, satellite := range satellites {
nodeURL, err := node.trust.GetNodeURL(ctx, satellite)
if err != nil {
return nil, rpcstatus.Wrap(rpcstatus.Internal, err)
}
response.TrustedSatellites = append(response.TrustedSatellites, &multinodepb.TrustedSatellitesResponse_NodeURL{
NodeId: nodeURL.ID,
Address: nodeURL.Address,
})
}
return response, nil
}

View File

@ -0,0 +1,59 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package multinode
import (
"context"
"go.uber.org/zap"
"storj.io/common/rpc/rpcstatus"
"storj.io/storj/private/multinodepb"
"storj.io/storj/storagenode/apikeys"
"storj.io/storj/storagenode/monitor"
)
var _ multinodepb.DRPCStorageServer = (*StorageEndpoint)(nil)
// StorageEndpoint implements multinode storage endpoint.
//
// architecture: Endpoint
type StorageEndpoint struct {
log *zap.Logger
apiKeys *apikeys.Service
monitor *monitor.Service
}
// NewStorageEndpoint creates new multinode storage endpoint.
func NewStorageEndpoint(log *zap.Logger, apiKeys *apikeys.Service, monitor *monitor.Service) *StorageEndpoint {
return &StorageEndpoint{
log: log,
apiKeys: apiKeys,
monitor: monitor,
}
}
// DiskSpace returns disk space state.
func (storage *StorageEndpoint) DiskSpace(ctx context.Context, req *multinodepb.DiskSpaceRequest) (_ *multinodepb.DiskSpaceResponse, err error) {
defer mon.Task()(&ctx)(&err)
if err = authenticate(ctx, storage.apiKeys, req.GetHeader()); err != nil {
return nil, rpcstatus.Wrap(rpcstatus.Unauthenticated, err)
}
diskSpace, err := storage.monitor.DiskSpace(ctx)
if err != nil {
storage.log.Error("disk space internal error", zap.Error(err))
return nil, rpcstatus.Wrap(rpcstatus.Internal, err)
}
return &multinodepb.DiskSpaceResponse{
Allocated: diskSpace.Allocated,
UsedPieces: diskSpace.UsedForPieces,
UsedTrash: diskSpace.UsedForTrash,
Free: diskSpace.Free,
Available: diskSpace.Available,
Overused: diskSpace.Overused,
}, nil
}

View File

@ -28,6 +28,7 @@ import (
"storj.io/private/version"
"storj.io/storj/pkg/server"
"storj.io/storj/private/lifecycle"
"storj.io/storj/private/multinodepb"
"storj.io/storj/private/version/checker"
"storj.io/storj/storage"
"storj.io/storj/storage/filestore"
@ -42,6 +43,7 @@ import (
"storj.io/storj/storagenode/inspector"
"storj.io/storj/storagenode/internalpb"
"storj.io/storj/storagenode/monitor"
"storj.io/storj/storagenode/multinode"
"storj.io/storj/storagenode/nodestats"
"storj.io/storj/storagenode/notifications"
"storj.io/storj/storagenode/orders"
@ -88,7 +90,7 @@ type DB interface {
Notifications() notifications.DB
Payout() payout.DB
Pricing() pricing.DB
Secret() apikeys.DB
APIKeys() apikeys.DB
Preflight(ctx context.Context) error
}
@ -279,6 +281,12 @@ type Peer struct {
Bandwidth *bandwidth.Service
Reputation *reputation.Service
Multinode struct {
Storage *multinode.StorageEndpoint
Bandwidth *multinode.BandwidthEndpoint
Node *multinode.NodeEndpoint
}
}
// New creates a new Storage Node.
@ -769,6 +777,39 @@ func New(log *zap.Logger, full *identity.FullIdentity, db DB, revocationDB exten
peer.Debug.Server.Panel.Add(
debug.Cycle("Bandwidth", peer.Bandwidth.Loop))
{ // setup multinode endpoints
// TODO: add to peer?
apiKeys := apikeys.NewService(peer.DB.APIKeys())
peer.Multinode.Storage = multinode.NewStorageEndpoint(
peer.Log.Named("multinode:storage-endpoint"),
apiKeys,
peer.Storage2.Monitor)
peer.Multinode.Bandwidth = multinode.NewBandwidthEndpoint(
peer.Log.Named("multinode:bandwidth-endpoint"),
apiKeys,
peer.DB.Bandwidth())
peer.Multinode.Node = multinode.NewNodeEndpoint(
peer.Log.Named("multinode:node-endpoint"),
apiKeys,
peer.Version.Service.Info,
peer.Contact.PingStats,
peer.DB.Reputation(),
peer.Storage2.Trust)
if err = multinodepb.DRPCRegisterStorage(peer.Server.DRPC(), peer.Multinode.Storage); err != nil {
return nil, errs.Combine(err, peer.Close())
}
if err = multinodepb.DRPCRegisterBandwidth(peer.Server.DRPC(), peer.Multinode.Bandwidth); err != nil {
return nil, errs.Combine(err, peer.Close())
}
if err = multinodepb.DRPCRegisterNode(peer.Server.DRPC(), peer.Multinode.Node); err != nil {
return nil, errs.Combine(err, peer.Close())
}
}
return peer, nil
}

View File

@ -53,3 +53,33 @@ type Metric struct {
Score float64 `json:"score"`
UnknownScore float64 `json:"unknownScore"`
}
// AuditHistory encapsulates storagenode audit history.
type AuditHistory struct {
Score float64 `json:"score"`
Windows []AuditHistoryWindow `json:"windows"`
}
// AuditHistoryWindow encapsulates storagenode audit history window.
type AuditHistoryWindow struct {
WindowStart time.Time `json:"windowStart"`
TotalCount int32 `json:"totalCount"`
OnlineCount int32 `json:"onlineCount"`
}
// GetAuditHistoryFromPB creates the AuditHistory json struct from a protobuf.
func GetAuditHistoryFromPB(auditHistoryPB *pb.AuditHistory) AuditHistory {
ah := AuditHistory{}
if auditHistoryPB == nil {
return ah
}
ah.Score = auditHistoryPB.Score
for _, window := range auditHistoryPB.Windows {
ah.Windows = append(ah.Windows, AuditHistoryWindow{
WindowStart: window.WindowStart,
TotalCount: window.TotalCount,
OnlineCount: window.OnlineCount,
})
}
return ah
}

View File

@ -10,25 +10,26 @@ import (
"github.com/zeebo/errs"
"storj.io/storj/private/multinodeauth"
"storj.io/storj/storagenode/apikeys"
)
// ensures that secretDB implements apikeys.DB interface.
var _ apikeys.DB = (*secretDB)(nil)
// ensures that apiKeysDB implements apikeys.DB interface.
var _ apikeys.DB = (*apiKeysDB)(nil)
// ErrSecret represents errors from the apikey database.
var ErrSecret = errs.Class("apikey db error")
// ErrAPIKeysDB represents errors from the api keys database.
var ErrAPIKeysDB = errs.Class("apikeys db error")
// SecretDBName represents the database name.
const SecretDBName = "secret"
// APIKeysDBName represents the database name.
const APIKeysDBName = "secret"
// secretDB works with node apikey DB.
type secretDB struct {
// apiKeysDB works with node api keys DB.
type apiKeysDB struct {
dbContainerImpl
}
// Store stores api key into database.
func (db *secretDB) Store(ctx context.Context, secret apikeys.APIKey) (err error) {
func (db *apiKeysDB) Store(ctx context.Context, apiKey apikeys.APIKey) (err error) {
defer mon.Task()(&ctx)(&err)
query := `INSERT INTO secret (
@ -37,15 +38,15 @@ func (db *secretDB) Store(ctx context.Context, secret apikeys.APIKey) (err error
) VALUES(?,?)`
_, err = db.ExecContext(ctx, query,
secret.Secret[:],
secret.CreatedAt,
apiKey.Secret[:],
apiKey.CreatedAt,
)
return ErrSecret.Wrap(err)
return ErrAPIKeysDB.Wrap(err)
}
// Check checks if apikey exists in db by token.
func (db *secretDB) Check(ctx context.Context, token apikeys.Secret) (err error) {
// Check checks if api key exists in db by secret.
func (db *apiKeysDB) Check(ctx context.Context, secret multinodeauth.Secret) (err error) {
defer mon.Task()(&ctx)(&err)
var bytes []uint8
@ -53,7 +54,7 @@ func (db *secretDB) Check(ctx context.Context, token apikeys.Secret) (err error)
rowStub := db.QueryRowContext(ctx,
`SELECT token, created_at FROM secret WHERE token = ?`,
token[:],
secret[:],
)
err = rowStub.Scan(
@ -62,21 +63,21 @@ func (db *secretDB) Check(ctx context.Context, token apikeys.Secret) (err error)
)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return apikeys.ErrNoSecret.Wrap(err)
return apikeys.ErrNoAPIKey.Wrap(err)
}
return ErrSecret.Wrap(err)
return ErrAPIKeysDB.Wrap(err)
}
return nil
}
// Revoke removes api key from db.
func (db *secretDB) Revoke(ctx context.Context, secret apikeys.Secret) (err error) {
func (db *apiKeysDB) Revoke(ctx context.Context, secret multinodeauth.Secret) (err error) {
defer mon.Task()(&ctx)(&err)
query := `DELETE FROM secret WHERE token = ?`
_, err = db.ExecContext(ctx, query, secret[:])
return ErrSecret.Wrap(err)
return ErrAPIKeysDB.Wrap(err)
}

View File

@ -107,7 +107,7 @@ type DB struct {
notificationsDB *notificationDB
payoutDB *payoutDB
pricingDB *pricingDB
secretDB *secretDB
apiKeysDB *apiKeysDB
SQLDBs map[string]DBContainer
}
@ -134,7 +134,7 @@ func OpenNew(ctx context.Context, log *zap.Logger, config Config) (*DB, error) {
notificationsDB := &notificationDB{}
payoutDB := &payoutDB{}
pricingDB := &pricingDB{}
secretDB := &secretDB{}
apiKeysDB := &apiKeysDB{}
db := &DB{
log: log,
@ -157,7 +157,7 @@ func OpenNew(ctx context.Context, log *zap.Logger, config Config) (*DB, error) {
notificationsDB: notificationsDB,
payoutDB: payoutDB,
pricingDB: pricingDB,
secretDB: secretDB,
apiKeysDB: apiKeysDB,
SQLDBs: map[string]DBContainer{
DeprecatedInfoDBName: deprecatedInfoDB,
@ -173,7 +173,7 @@ func OpenNew(ctx context.Context, log *zap.Logger, config Config) (*DB, error) {
NotificationsDBName: notificationsDB,
HeldAmountDBName: payoutDB,
PricingDBName: pricingDB,
SecretDBName: secretDB,
APIKeysDBName: apiKeysDB,
},
}
@ -202,7 +202,7 @@ func OpenExisting(ctx context.Context, log *zap.Logger, config Config) (*DB, err
notificationsDB := &notificationDB{}
payoutDB := &payoutDB{}
pricingDB := &pricingDB{}
secretDB := &secretDB{}
apiKeysDB := &apiKeysDB{}
db := &DB{
log: log,
@ -225,7 +225,7 @@ func OpenExisting(ctx context.Context, log *zap.Logger, config Config) (*DB, err
notificationsDB: notificationsDB,
payoutDB: payoutDB,
pricingDB: pricingDB,
secretDB: secretDB,
apiKeysDB: apiKeysDB,
SQLDBs: map[string]DBContainer{
DeprecatedInfoDBName: deprecatedInfoDB,
@ -241,7 +241,7 @@ func OpenExisting(ctx context.Context, log *zap.Logger, config Config) (*DB, err
NotificationsDBName: notificationsDB,
HeldAmountDBName: payoutDB,
PricingDBName: pricingDB,
SecretDBName: secretDB,
APIKeysDBName: apiKeysDB,
},
}
@ -274,7 +274,7 @@ func (db *DB) openDatabases(ctx context.Context) error {
NotificationsDBName,
HeldAmountDBName,
PricingDBName,
SecretDBName,
APIKeysDBName,
}
for _, dbName := range dbs {
@ -543,9 +543,9 @@ func (db *DB) Pricing() pricing.DB {
return db.pricingDB
}
// Secret returns instance of the Secret database.
func (db *DB) Secret() apikeys.DB {
return db.secretDB
// APIKeys returns instance of the APIKeys database.
func (db *DB) APIKeys() apikeys.DB {
return db.apiKeysDB
}
// RawDatabases are required for testing purposes.
@ -1812,11 +1812,11 @@ func (db *DB) Migration(ctx context.Context) *migrate.Migration {
}),
},
{
DB: &db.secretDB.DB,
DB: &db.apiKeysDB.DB,
Description: "Create secret table",
Version: 46,
CreateDB: func(ctx context.Context, log *zap.Logger) error {
if err := db.openDatabase(ctx, SecretDBName); err != nil {
if err := db.openDatabase(ctx, APIKeysDBName); err != nil {
return ErrDatabase.Wrap(err)
}

View File

@ -21,7 +21,7 @@ var v46 = MultiDBState{
storagenodedb.NotificationsDBName: v43.DBStates[storagenodedb.NotificationsDBName],
storagenodedb.HeldAmountDBName: v43.DBStates[storagenodedb.HeldAmountDBName],
storagenodedb.PricingDBName: v43.DBStates[storagenodedb.PricingDBName],
storagenodedb.SecretDBName: &DBState{
storagenodedb.APIKeysDBName: &DBState{
SQL: `
-- table to hold storagenode secret token
CREATE TABLE secret (

View File

@ -51,6 +51,6 @@ var v47 = MultiDBState{
storagenodedb.NotificationsDBName: v46.DBStates[storagenodedb.NotificationsDBName],
storagenodedb.HeldAmountDBName: v46.DBStates[storagenodedb.HeldAmountDBName],
storagenodedb.PricingDBName: v46.DBStates[storagenodedb.PricingDBName],
storagenodedb.SecretDBName: v46.DBStates[storagenodedb.SecretDBName],
storagenodedb.APIKeysDBName: v46.DBStates[storagenodedb.APIKeysDBName],
},
}

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,8 @@
"@vue/cli-service": "4.5.9",
"babel-core": "6.26.3",
"core-js": "3.8.1",
"node-sass": "4.14.1",
"sass-loader": "8.0.0",
"stylelint": "13.8.0",
"stylelint-config-standard": "20.0.0",
"stylelint-scss": "3.18.0",
@ -31,6 +33,7 @@
"tslint-consistent-codestyle": "1.16.0",
"tslint-loader": "3.5.4",
"typescript": "3.7.4",
"vue-svg-loader": "0.16.0",
"vue-template-compiler": "2.6.11",
"vue-tslint": "0.3.2",
"vue-tslint-loader": "3.5.6",
@ -61,7 +64,7 @@
"rule-empty-line-before": "always-multi-line",
"selector-pseudo-element-colon-notation": "single",
"selector-pseudo-class-parentheses-space-inside": "never",
"selector-max-type": 1,
"selector-max-type": 3,
"font-family-no-missing-generic-family-keyword": true,
"at-rule-no-unknown": null,
"scss/at-rule-no-unknown": true,

View File

@ -14,3 +14,50 @@ import { Component, Vue } from 'vue-property-decorator';
export default class App extends Vue {
}
</script>
<style lang="scss">
@import 'static/styles/variables';
body {
margin: 0 !important;
position: relative;
overflow-y: hidden;
}
p,
h1,
h2,
h3,
h4 {
margin: 0;
}
#app {
width: 100vw;
height: 100vh;
}
@font-face {
font-display: swap;
font-family: 'font_regular';
src: url('../../static/fonts/font_regular.ttf');
}
@font-face {
font-display: swap;
font-family: 'font_medium';
src: url('../../static/fonts/font_medium.ttf');
}
@font-face {
font-display: swap;
font-family: 'font_semiBold';
src: url('../../static/fonts/font_semiBold.ttf');
}
@font-face {
font-display: swap;
font-family: 'font_bold';
src: url('../../static/fonts/font_bold.ttf');
}
</style>

View File

@ -0,0 +1,172 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="input-container">
<div v-if="!isOptional" class="label-container">
<div class="label-container__main">
<div v-if="error">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="20" height="20" rx="10" fill="#EB5757"/>
<path d="M10.0012 11.7364C10.612 11.7364 11.1117 11.204 11.1117 10.5532V5.81218C11.1117 5.75302 11.108 5.68991 11.1006 5.63074C11.0192 5.06672 10.5565 4.62891 10.0012 4.62891C9.39037 4.62891 8.89062 5.16138 8.89062 5.81218V10.5492C8.89062 11.204 9.39037 11.7364 10.0012 11.7364Z" fill="white"/>
<path d="M10.0001 12.8906C9.13977 12.8906 8.44531 13.5851 8.44531 14.4454C8.44531 15.3057 9.13977 16.0002 10.0001 16.0002C10.8604 16.0002 11.5548 15.3057 11.5548 14.4454C11.5583 13.5851 10.8638 12.8906 10.0001 12.8906Z" fill="white"/>
</svg>
</div>
<h3 v-if="!error" class="label-container__main__label">{{label}}</h3>
<h3 v-if="!error" class="label-container__main__label add-label">{{additionalLabel}}</h3>
<h3 class="label-container__main__error" v-if="error">{{error}}</h3>
</div>
<h3 v-if="isLimitShown" class="label-container__limit">{{currentLimit}}/{{maxSymbols}}</h3>
</div>
<div v-if="isOptional" class="optional-label-container">
<h3 class="label-container__label">{{label}}</h3>
<h4 class="optional-label-container__optional">Optional</h4>
</div>
<textarea
class="headered-textarea"
v-if="isMultiline"
:id="this.label"
:placeholder="this.placeholder"
:style="style.inputStyle"
:rows="5"
:cols="40"
wrap="hard"
@input="onInput"
@change="onInput"
v-model="value">
</textarea>
<input
class="headered-input"
v-if="!isMultiline"
:id="this.label"
:placeholder="this.placeholder"
:type="[isPassword ? 'password': 'text']"
@input="onInput"
@change="onInput"
v-model="value"
:style="style.inputStyle"
/>
</div>
</template>
<script lang="ts">
import { Component, Prop } from 'vue-property-decorator';
import HeaderlessInput from './HeaderlessInput.vue';
// Custom input component with labeled header.
@Component
export default class HeaderedInput extends HeaderlessInput {
@Prop({default: ''})
private readonly initValue: string;
@Prop({default: ''})
private readonly additionalLabel: string;
@Prop({default: 0})
private readonly currentLimit: number;
@Prop({default: false})
private readonly isOptional: boolean;
@Prop({default: false})
private readonly isLimitShown: boolean;
@Prop({default: false})
private readonly isMultiline: boolean;
public value: string;
public constructor() {
super();
this.value = this.initValue;
}
}
</script>
<style scoped lang="scss">
.input-container {
display: flex;
flex-direction: column;
align-items: flex-start;
margin-top: 10px;
font-family: 'font_regular', sans-serif;
}
.label-container {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
&__main {
display: flex;
justify-content: flex-start;
align-items: center;
&__label {
font-family: 'font_regular', sans-serif;
font-size: 16px;
line-height: 21px;
color: var(--c-gray);
margin-bottom: 8px;
}
&__error {
font-size: 16px;
line-height: 21px;
color: var(--c-error);
margin-left: 10px;
}
}
&__limit {
font-size: 16px;
line-height: 21px;
color: rgba(56, 75, 101, 0.4);
}
}
.optional-label-container {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
&__optional {
font-size: 16px;
line-height: 21px;
color: #afb7c1;
}
}
.headered-input,
.headered-textarea {
font-size: 16px;
line-height: 21px;
resize: none;
height: 48px;
width: 100%;
text-indent: 20px;
outline: none;
box-shadow: none;
font-family: 'font_regular', sans-serif;
border: 1px solid var(--c-gray--light);
border-radius: var(--br-input);
color: #354049;
caret-color: var(--c-primary);
box-sizing: border-box;
&::placeholder {
color: var(--c-placeholder);
}
}
.headered-textarea {
padding: 15px 22px;
text-indent: 0;
line-height: 26px;
}
.add-label {
margin-left: 5px;
color: rgba(56, 75, 101, 0.4);
}
</style>

View File

@ -0,0 +1,267 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="input-wrap">
<div class="label-container">
<div class="icon" v-if="error">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="20" height="20" rx="10" fill="#EB5757"/>
<path d="M10.0012 11.7364C10.612 11.7364 11.1117 11.204 11.1117 10.5532V5.81218C11.1117 5.75302 11.108 5.68991 11.1006 5.63074C11.0192 5.06672 10.5565 4.62891 10.0012 4.62891C9.39037 4.62891 8.89062 5.16138 8.89062 5.81218V10.5492C8.89062 11.204 9.39037 11.7364 10.0012 11.7364Z" fill="white"/>
<path d="M10.0001 12.8906C9.13977 12.8906 8.44531 13.5851 8.44531 14.4454C8.44531 15.3057 9.13977 16.0002 10.0001 16.0002C10.8604 16.0002 11.5548 15.3057 11.5548 14.4454C11.5583 13.5851 10.8638 12.8906 10.0001 12.8906Z" fill="white"/>
</svg>
</div>
<p class="label-container__label" v-if="isLabelShown" :style="style.labelStyle">{{label}}</p>
<p class="label-container__error" v-if="error" :style="style.errorStyle">{{error}}</p>
</div>
<div
class="headerless-input-container"
:style="style.inputStyle"
>
<input
class="headerless-input"
:class="{'inputError' : error, 'password': isPassword}"
@input="onInput"
@change="onInput"
v-model="value"
:placeholder="placeholder"
:type="type"
@focus="showPasswordStrength"
@blur="hidePasswordStrength"
/>
<!--2 conditions of eye image (crossed or not) -->
<div
class="input-wrap__image icon"
v-if="isPasswordHiddenState"
@click="changeVision"
>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path class="input-wrap__image__path" d="M10 4C4.70642 4 1 10 1 10C1 10 3.6999 16 10 16C16.3527 16 19 10 19 10C19 10 15.3472 4 10 4ZM10 13.8176C7.93537 13.8176 6.2946 12.1271 6.2946 10C6.2946 7.87285 7.93537 6.18239 10 6.18239C12.0646 6.18239 13.7054 7.87285 13.7054 10C13.7054 12.1271 12.0646 13.8176 10 13.8176Z" fill="#AFB7C1"/>
<path d="M11.6116 9.96328C11.6116 10.8473 10.8956 11.5633 10.0116 11.5633C9.12763 11.5633 8.41162 10.8473 8.41162 9.96328C8.41162 9.07929 9.12763 8.36328 10.0116 8.36328C10.8956 8.36328 11.6116 9.07929 11.6116 9.96328Z" fill="#AFB7C1"/>
</svg>
</div>
<div
class="input-wrap__image icon"
v-if="isPasswordShownState"
@click="changeVision"
>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path class="input-wrap__image__path" d="M10 4C4.70642 4 1 10 1 10C1 10 3.6999 16 10 16C16.3527 16 19 10 19 10C19 10 15.3472 4 10 4ZM10 13.8176C7.93537 13.8176 6.2946 12.1271 6.2946 10C6.2946 7.87285 7.93537 6.18239 10 6.18239C12.0646 6.18239 13.7054 7.87285 13.7054 10C13.7054 12.1271 12.0646 13.8176 10 13.8176Z" fill="#AFB7C1"/>
<path d="M11.6121 9.96328C11.6121 10.8473 10.8961 11.5633 10.0121 11.5633C9.12812 11.5633 8.41211 10.8473 8.41211 9.96328C8.41211 9.07929 9.12812 8.36328 10.0121 8.36328C10.8961 8.36328 11.6121 9.07929 11.6121 9.96328Z" fill="#AFB7C1"/>
<mask id="path-3-inside-1" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 16.5L16 1L16.8155 1.57875L5.81551 17.0787L5 16.5Z"/>
</mask>
<path class="input-wrap__image__path" fill-rule="evenodd" clip-rule="evenodd" d="M5 16.5L16 1L16.8155 1.57875L5.81551 17.0787L5 16.5Z" fill="white"/>
<path class="input-wrap__image__path" d="M16 1L16.5787 0.184493L15.7632 -0.394254L15.1845 0.421253L16 1ZM5 16.5L4.18449 15.9213L3.60575 16.7368L4.42125 17.3155L5 16.5ZM16.8155 1.57875L17.631 2.15749L18.2098 1.34199L17.3943 0.76324L16.8155 1.57875ZM5.81551 17.0787L5.23676 17.8943L6.05227 18.473L6.63101 17.6575L5.81551 17.0787ZM15.1845 0.421253L4.18449 15.9213L5.81551 17.0787L16.8155 1.57875L15.1845 0.421253ZM17.3943 0.76324L16.5787 0.184493L15.4213 1.81551L16.2368 2.39425L17.3943 0.76324ZM6.63101 17.6575L17.631 2.15749L16 1L5 16.5L6.63101 17.6575ZM4.42125 17.3155L5.23676 17.8943L6.39425 16.2632L5.57875 15.6845L4.42125 17.3155Z" fill="white" mask="url(#path-3-inside-1)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 17.5L16 2L16.8155 2.57875L5.81551 18.0787L5 17.5Z" fill="#AFB7C1"/>
</svg>
</div>
<!-- end of image-->
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
/**
* Custom input component for login page.
*/
@Component
export default class HeaderlessInput extends Vue {
private readonly textType: string = 'text';
private readonly passwordType: string = 'password';
private type: string = this.textType;
private isPasswordShown: boolean = false;
protected value: string = '';
@Prop({default: ''})
protected readonly label: string;
@Prop({default: 'default'})
protected readonly placeholder: string;
@Prop({default: false})
protected readonly isPassword: boolean;
@Prop({default: '48px'})
protected readonly height: string;
@Prop({default: '100%'})
protected readonly width: string;
@Prop({default: ''})
protected readonly error: string;
@Prop({default: Number.MAX_SAFE_INTEGER})
protected readonly maxSymbols: number;
@Prop({default: false})
private readonly isWhite: boolean;
public constructor() {
super();
this.type = this.isPassword ? this.passwordType : this.textType;
}
/**
* Used to set default value from parent component.
* @param value
*/
public setValue(value: string): void {
this.value = value;
}
public showPasswordStrength(): void {
this.$emit('showPasswordStrength');
}
public hidePasswordStrength(): void {
this.$emit('hidePasswordStrength');
}
/**
* triggers on input.
*/
// @ts-ignore
public onInput({ target }): void {
if (!target || target.value) return;
if (target.value.length > this.maxSymbols) {
this.value = target.value.slice(0, this.maxSymbols);
} else {
this.value = target.value;
}
this.$emit('setData', this.value);
}
/**
* Triggers input type between text and password to show/hide symbols.
*/
public changeVision(): void {
this.isPasswordShown = !this.isPasswordShown;
this.type = this.isPasswordShown ? this.textType : this.passwordType;
}
public get isLabelShown(): boolean {
return !!(!this.error && this.label);
}
public get isPasswordHiddenState(): boolean {
return this.isPassword && !this.isPasswordShown;
}
public get isPasswordShownState(): boolean {
return this.isPassword && this.isPasswordShown;
}
/**
* Returns style objects depends on props.
*/
protected get style(): object {
return {
inputStyle: {
width: this.width,
height: this.height,
},
labelStyle: {
color: this.isWhite ? 'white' : '#354049',
},
errorStyle: {
color: this.isWhite ? 'white' : '#FF5560',
},
};
}
}
</script>
<style scoped lang="scss">
.input-wrap {
position: relative;
width: 100%;
font-family: 'font_regular', sans-serif;
&__image {
position: absolute;
right: 25px;
top: 50%;
transform: translateY(-50%);
z-index: 20;
cursor: pointer;
&:hover .input-wrap__image__path {
fill: var(--c-primary) !important;
}
}
}
.label-container {
display: flex;
justify-content: flex-start;
align-items: flex-end;
padding-bottom: 8px;
flex-direction: row;
height: auto;
&__label {
font-size: 16px;
line-height: 21px;
color: var(--c-label);
margin-bottom: 0;
}
&__add-label {
margin-left: 5px;
font-size: 16px;
line-height: 21px;
color: rgba(56, 75, 101, 0.4);
}
&__error {
font-size: 16px;
margin: 18px 0 0 10px;
}
}
.headerless-input-container {
position: relative;
box-sizing: border-box;
height: 46px;
}
.headerless-input {
font-size: 16px;
line-height: 21px;
resize: none;
padding: 0 30px 0 0;
width: 100%;
height: 100%;
text-indent: 20px;
border: 1px solid rgba(56, 75, 101, 0.4);
border-radius: 6px;
}
.headerless-input::placeholder {
color: #384b65;
opacity: 0.4;
}
.inputError::placeholder {
color: #eb5757;
opacity: 0.4;
}
.error {
color: #ff5560;
margin-left: 10px;
}
.password {
width: calc(100% - 75px) !important;
padding-right: 75px;
}
.icon {
width: 20px;
height: 20px;
}
</style>

View File

@ -0,0 +1,146 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<!-- if isDisabled check onPress in parent element -->
<div
class="container"
:class="containerClassName"
:style="style"
@click="onPress"
>
<svg v-if="withPlus" class="plus" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M10 4.1665V15.8332" stroke="white" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.16797 10H15.8346" stroke="white" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<span class="label">{{ label }}</span>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
/**
* Custom button component with label.
*/
@Component
export default class VButton extends Vue {
@Prop({default: 'Default'})
private readonly label: string;
@Prop({default: 'inherit'})
private readonly width: string;
@Prop({default: '48px'})
private readonly height: string;
@Prop({default: false})
private readonly isWhite: boolean;
@Prop({default: false})
private readonly isTransparent: boolean;
@Prop({default: false})
private readonly isDeletion: boolean;
@Prop({default: false})
private readonly isBlueWhite: boolean;
@Prop({default: false})
private isDisabled: boolean;
@Prop({default: false})
private withPlus: boolean;
@Prop({default: false})
private inactive: boolean;
@Prop({default: () => { return; }})
private readonly onPress: Function;
public get style(): Object {
return { width: this.width, height: this.height };
}
public get containerClassName(): string {
let className: string = `${this.inactive ? 'inactive' : ''}`;
switch (true) {
case this.isDisabled:
className = 'disabled';
break;
case this.isWhite:
className = 'white';
break;
case this.isTransparent:
className = 'transparent';
break;
case this.isDeletion:
className = 'red';
}
return className;
}
}
</script>
<style lang="scss" scoped>
.container {
display: flex;
align-items: center;
justify-content: center;
background-color: var(--c-primary);
border-radius: var(--br-button);
cursor: pointer;
.label {
font-family: 'font_medium', sans-serif;
font-size: 16px;
color: var(--c-button-label);
margin: 0;
}
&:hover {
background-color: #004199;
&.white {
box-shadow: none !important;
background-color: var(--c-button-white-hover) !important;
border-color: transparent;
}
&.red {
box-shadow: none !important;
background-color: var(--c-button-red-hover);
}
}
}
.red {
background-color: var(--c-button-red);
}
.plus {
margin-right: 10px;
}
.white {
background-color: var(--c-button-white);
border: var(--b-button-white);
.label {
color: var(--c-button-white--label);
}
.plus {
path {
stroke: var(--c-title);
}
}
}
.disabled {
background-color: var(--c-button-disabled);
pointer-events: none !important;
.label {
color: #acb0bc !important;
}
}
.inactive {
opacity: 0.5;
pointer-events: none !important;
}
</style>

View File

@ -0,0 +1,142 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div
class="dropdown"
@click.self="toggleOptions"
:class="{ active: areOptionsShown }"
>
<span class="label">{{ selectedOption.label }}</span>
<div class="dropdown__selection" v-show="areOptionsShown">
<div class="dropdown__selection__overflow-container">
<div v-for="option in allOptions" :key="option.label" class="dropdown__selection__option" @click="onOptionClick(option)">
<span class="dropdown__selection__option__label">{{ option.label }}</span>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
export class Option {
public constructor(
public label: string = '',
public onClick: Function = () => { return; },
) {}
}
@Component
export default class VDropdown extends Vue {
@Prop({default: 'All'})
private readonly allLabel: string;
@Prop({default: () => { return; }})
private readonly onAllClick: Function;
@Prop({default: []})
private readonly options: Option[];
public areOptionsShown: boolean = false;
@Watch('options')
public allOptions: Option[] = [ new Option(this.allLabel, this.onAllClick), ...this.options ];
@Watch('options')
public selectedOption: Option = this.allOptions[0];
public toggleOptions(): void {
this.areOptionsShown = !this.areOptionsShown;
}
public closeOptions(): void {
this.areOptionsShown = false;
}
/**
* Fires on option click.
* Calls callback and changes selection.
* @param option
*/
public async onOptionClick(option: Option): Promise<void> {
await option.onClick();
this.selectedOption = option;
this.closeOptions();
}
}
</script>
<style lang="scss">
.dropdown {
box-sizing: border-box;
width: 300px;
height: 40px;
background: transparent;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
border: 1px solid var(--c-gray--light);
border-radius: 6px;
font-size: 16px;
color: var(--c-title);
cursor: pointer;
font-family: 'font_medium', sans-serif;
&:hover {
border-color: var(--c-gray);
}
&.active {
border-color: var(--c-primary);
}
&__selection {
position: absolute;
top: 52px;
left: 0;
width: 300px;
border: 1px solid var(--c-gray--light);
border-radius: 6px;
overflow: hidden;
&__overflow-container {
overflow: overlay;
overflow-x: hidden;
height: 160px;
}
&__option {
display: flex;
align-items: center;
justify-content: flex-start;
padding: 0 16px;
height: 40px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
width: 100% !important;
cursor: pointer;
border-bottom: 1px solid var(--c-gray--light);
&:hover {
background: var(--c-background);
}
}
}
}
::-webkit-scrollbar {
width: 3px;
}
::-webkit-scrollbar-track {
box-shadow: inset 0 0 5px transparent;
}
::-webkit-scrollbar-thumb {
background: var(--c-gray--light);
border-radius: 6px;
height: 5px;
}
</style>

View File

@ -0,0 +1,92 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div
class="modal-wrap"
@click.self="$emit('close')"
>
<div class="modal">
<div class="modal__header">
<slot name="header"></slot>
</div>
<div class="modal__body">
<slot name="body"></slot>
</div>
<div class="modal__footer">
<slot name="footer"></slot>
</div>
<div class="modal__cross" @click="$emit('close')">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M24 8L8 24" stroke="#676F84" stroke-width="2.66667" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 8L24 24" stroke="#676F84" stroke-width="2.66667" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
@Component
export default class VModal extends Vue {
}
</script>
<style lang="scss">
.modal-wrap {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(37, 42, 50, 0.7);
z-index: 1000;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
font-family: 'font_regular', sans-serif;
.modal {
position: relative;
background: white;
padding: 80px 97px;
height: auto;
z-index: 1001;
border-radius: 16px;
&__header {
font-family: 'font_bold', sans-serif;
font-size: 32px;
color: var(--c-title);
display: flex;
justify-content: center;
}
&__body {
margin-top: 46px;
}
&__footer {
margin-top: 32px;
}
&__cross {
position: absolute;
top: 32px;
right: 32px;
display: flex;
align-items: center;
justify-content: center;
width: 32px;
max-width: 32px;
height: 32px;
max-height: 32px;
cursor: pointer;
}
}
}
</style>

View File

@ -0,0 +1,82 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="add-new-node">
<v-button :with-plus="true" label="New Node" :on-press="openModal" width="152px" />
<v-modal v-if="isAddNewNodeModalShown" @close="closeModal">
<h2 slot="header">Add New Node</h2>
<div class="add-new-node__body" slot="body">
<headered-input
class="add-new-node__body__input"
label="Public IP Address"
/>
<headered-input
class="add-new-node__body__input"
label="API Key"
/>
<headered-input
class="add-new-node__body__input"
label="Displayed Name"
/>
</div>
<div class="add-new-node__footer" slot="footer">
<v-button label="Cancel" :is-white="true" width="205px" :on-press="closeModal" />
<v-button label="Continue" width="205px" />
</div>
</v-modal>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HeaderedInput from '@/app/components/common/HeaderedInput.vue';
import VButton from '@/app/components/common/VButton.vue';
import VModal from '@/app/components/common/VModal.vue';
@Component({
components: {
VButton,
HeaderedInput,
VModal,
},
})
export default class AddNewNode extends Vue {
public isAddNewNodeModalShown: boolean = false;
public openModal(): void {
this.isAddNewNodeModalShown = true;
}
public closeModal(): void {
this.isAddNewNodeModalShown = false;
}
}
</script>
<style lang="scss">
.add-new-node {
h2 {
margin: 0;
font-size: 32px;
}
&__body {
width: 441px;
&__input:not(:first-of-type) {
margin-top: 20px;
}
}
&__footer {
width: 441px;
display: flex;
align-items: center;
justify-content: space-between;
}
}
</style>

View File

@ -0,0 +1,125 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="navigation-area">
<storj-logo class="navigation-area__logo" />
<router-link
:aria-label="navItem.name"
class="navigation-area__item-container"
v-for="navItem in navigation"
:key="navItem.name"
:to="navItem.path"
>
<div class="navigation-area__item-container__link">
<component :is="navItem.icon"></component>
<p class="navigation-area__item-container__link__title">{{ navItem.name }}</p>
</div>
</router-link>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import MyNodesIcon from '@/../static/images/icons/navigation/nodes.svg';
import NotificationIcon from '@/../static/images/icons/navigation/notifications.svg';
import PayoutsIcon from '@/../static/images/icons/navigation/payouts.svg';
import ReputationIcon from '@/../static/images/icons/navigation/reputation.svg';
import TrafficIcon from '@/../static/images/icons/navigation/traffic.svg';
import StorjLogo from '@/../static/images/Logo.svg';
export class NavigationLink {
constructor(
public name: string = '',
public path: string = '',
public icon: Vue = new Vue(),
) {}
}
@Component({
components: {
StorjLogo,
MyNodesIcon,
PayoutsIcon,
ReputationIcon,
TrafficIcon,
NotificationIcon,
},
})
export default class NavigationArea extends Vue {
/**
* Array of navigation links with icons.
*/
// TODO: add actual routes
public readonly navigation: NavigationLink[] = [
new NavigationLink('My Nodes', '/my-nodes', MyNodesIcon),
new NavigationLink('Payouts', '/payouts', PayoutsIcon),
new NavigationLink('Bandwidth & Disk', '/traffic', TrafficIcon),
new NavigationLink('Reputation', '/reputation', ReputationIcon),
new NavigationLink('Notifications', '/notifications', NotificationIcon),
];
}
</script>
<style scoped lang="scss">
.navigation-area {
box-sizing: border-box;
padding: 30px 24px;
height: 100vh;
display: flex;
flex-direction: column;
align-items: flex-start;
border-right: 1px solid var(--c-gray--light);
background: var(--c-block-gray);
&__logo {
margin-bottom: 62px;
}
&__item-container {
flex: 0 0 auto;
padding: 10px;
width: calc(100% - 20px);
display: flex;
justify-content: flex-start;
align-items: center;
margin-bottom: 20px;
text-decoration: none;
path {
fill: var(--c-label);
}
&__link {
display: flex;
justify-content: flex-start;
align-items: center;
&__title {
font-family: 'font_semiBold', sans-serif;
font-size: 16px;
line-height: 23px;
margin: 0 0 0 15px;
white-space: nowrap;
color: var(--c-label);
}
}
&.router-link-active,
&:hover {
background: #e7e9eb;
border-radius: 6px;
.navigation-area__item-container__link__title {
color: var(--c-title);
}
path {
fill: var(--c-title) !important;
opacity: 1;
}
}
}
}
</style>

View File

@ -0,0 +1,101 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<table class="nodes-table" v-if="nodes.length" border="0" cellpadding="0" cellspacing="0">
<thead>
<tr>
<th class="align-left">NODE</th>
<th>DISK SPACE USED</th>
<th>DISK SPACE LEFT</th>
<th>BANDWIDTH USED</th>
<th>EARNED</th>
<th>VERSION</th>
<th>STATUS</th>
</tr>
</thead>
<tbody>
<tr v-for="node in nodes" :key="node.id">
<th class="align-left">{{ node.name }}</th>
<th>{{ node.diskSpaceUsed | bytesToBase10String }}</th>
<th>{{ node.diskSpaceLeft | bytesToBase10String }}</th>
<th>{{ node.bandwidthUsed | bytesToBase10String }}</th>
<th>{{ node.earned | centsToDollars }}</th>
<th>{{ node.version }}</th>
<th :class="node.status">{{ node.status }}</th>
</tr>
</tbody>
</table>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { Node } from '@/nodes';
@Component
export default class NodesTable extends Vue {
public nodes: Node[] = [];
}
</script>
<style scoped lang="scss">
.nodes-table {
width: 100%;
border: 1px solid var(--c-gray--light);
border-radius: var(--br-table);
font-family: 'font_semiBold', sans-serif;
overflow: hidden;
th {
box-sizing: border-box;
padding: 0 20px;
max-width: 250px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
thead {
background: var(--c-block-gray);
tr {
height: 40px;
font-size: 12px;
color: var(--c-gray);
border-radius: var(--br-table);
text-align: right;
}
}
tbody {
tr {
height: 56px;
text-align: right;
font-size: 16px;
color: var(--c-line);
&:nth-of-type(even) {
background: var(--c-block-gray);
}
th:not(:first-of-type) {
font-family: 'font_medium', sans-serif;
}
}
.online {
color: var(--c-success);
}
.offline {
color: var(--c-error);
}
}
.align-left {
text-align: left;
}
}
</style>

View File

@ -4,7 +4,10 @@
import Router, { RouterMode } from 'vue-router';
import { Component } from 'vue-router/types/router';
import AddFirstNode from '@/app/views/AddFirstNode.vue';
import Dashboard from '@/app/views/Dashboard.vue';
import MyNodes from '@/app/views/MyNodes.vue';
import WelcomeScreen from '@/app/views/WelcomeScreen.vue';
/**
* Metadata holds arbitrary information to routes like transition names, who can access the route, etc.
@ -20,7 +23,7 @@ export class Route {
public readonly path: string;
public readonly name: string;
public readonly component: Component;
public readonly children?: Route[];
public children?: Route[];
public readonly meta?: Metadata;
/**
@ -31,24 +34,39 @@ export class Route {
* @param children - all nested components of current route.
* @param meta - arbitrary information to routes like transition names, who can access the route, etc.
*/
public constructor(path: string, name: string, component: Component, children: Route[] | undefined = undefined, meta: Metadata | undefined = undefined) {
public constructor(path: string, name: string, component: Component, meta: Metadata | undefined = undefined) {
this.path = path;
this.name = name;
this.component = component;
this.children = children;
this.meta = meta;
}
/**
* Adds children routes to route.
*/
public addChildren(children: Route[]): Route {
this.children = children;
return this;
}
}
/**
* Config contains configuration of all available routes for a Multinode Dashboard router.
*/
export class Config {
public static Root: Route = new Route('/', 'Root', Dashboard, undefined, {requiresAuth: true});
public static Root: Route = new Route('/', 'Root', Dashboard, {requiresAuth: true});
public static Welcome: Route = new Route('/welcome', 'Welcome', WelcomeScreen);
public static AddFirstNode: Route = new Route('/add-first-node', 'AddFirstNode', AddFirstNode);
public static MyNodes: Route = new Route('/my-nodes', 'MyNodes', MyNodes);
public static mode: RouterMode = 'history';
public static routes: Route[] = [
Config.Root,
Config.Root.addChildren([
Config.MyNodes,
]),
Config.Welcome,
Config.AddFirstNode,
];
}

View File

@ -0,0 +1,15 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
/**
* Size class contains currency related functionality such as convertation.
*/
export class Currency {
/**
* dollarsFromCents converts cents to dollars with prefix.
* @param cents count
*/
public static dollarsFromCents(cents: number): string {
return `$${(cents / 100).toFixed(2)}`;
}
}

View File

@ -0,0 +1,44 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
/**
* Base10 sizes.
*/
enum SizeBreakpoints {
KB = 1e3,
MB = 1e6,
GB = 1e9,
TB = 1e12,
PB = 1e15,
EB = 1e18,
}
/**
* Size class contains size related functionality such as convertation.
*/
export class Size {
/**
* Base10String converts size to a string using base-10 prefixes.
* @param size in bytes
*/
public static toBase10String(size: number): string {
const decimals = 2;
const _size = Math.abs(size);
switch (true) {
case _size >= SizeBreakpoints.EB * 2 / 3:
return `${parseFloat((size / SizeBreakpoints.EB).toFixed(decimals))}EB`;
case _size >= SizeBreakpoints.PB * 2 / 3:
return `${parseFloat((size / SizeBreakpoints.PB).toFixed(decimals))}PB`;
case _size >= SizeBreakpoints.TB * 2 / 3:
return `${parseFloat((size / SizeBreakpoints.TB).toFixed(decimals))}TB`;
case _size >= SizeBreakpoints.MB * 2 / 3:
return `${parseFloat((size / SizeBreakpoints.MB).toFixed(decimals))}MB`;
case _size >= SizeBreakpoints.KB * 2 / 3:
return `${parseFloat((size / SizeBreakpoints.KB).toFixed(decimals))}KB`;
default:
return `${size}B`;
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -2,14 +2,68 @@
// See LICENSE for copying information.
<template>
<div>
<div class="dashboard-area">
<div class="dashboard-area__navigation-area">
<navigation-area />
</div>
<div class="dashboard-area__right-area">
<div class="dashboard-area__right-area__header">
<add-new-node />
</div>
<div class="dashboard-area__right-area__content">
<router-view />
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
@Component
import AddNewNode from '@/app/components/modals/AddNewNode.vue';
import NavigationArea from '@/app/components/navigation/NavigationArea.vue';
@Component({
components: {
AddNewNode,
NavigationArea,
},
})
export default class Dashboard extends Vue {}
</script>
<style lang="scss" scoped>
.dashboard-area {
display: flex;
&__navigation-area {
width: 280px;
}
&__right-area {
position: relative;
flex: 1;
&__header {
width: 100%;
height: 80px;
padding: 0 60px;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: flex-end;
border: 1px solid var(--c-gray--light);
background: var(--c-block-gray);
}
&__content {
position: absolute;
box-sizing: border-box;
height: calc(100vh - 80px);
top: 80px;
left: 0;
width: 100%;
}
}
}
</style>

View File

@ -0,0 +1,36 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="my-nodes">
<h1 class="my-nodes__title">My Nodes</h1>
<nodes-table />
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import NodesTable from '@/app/components/tables/NodesTable.vue';
@Component({
components: { NodesTable },
})
export default class MyNodes extends Vue {}
</script>
<style lang="scss" scoped>
.my-nodes {
box-sizing: border-box;
padding: 60px;
height: 100%;
overflow-y: auto;
&__title {
font-family: 'font_bold', sans-serif;
font-size: 32px;
color: var(--c-title);
margin-bottom: 36px;
}
}
</style>

File diff suppressed because one or more lines are too long

View File

@ -7,11 +7,27 @@ import Router from 'vue-router';
import App from '@/app/App.vue';
import { router } from '@/app/router';
import { store } from '@/app/store';
import { Currency } from '@/app/utils/currency';
import { Size } from '@/app/utils/size';
Vue.config.productionTip = false;
Vue.use(Router);
/**
* centsToDollars is a Vue filter that converts amount of cents in dollars string.
*/
Vue.filter('centsToDollars', (cents: number): string => {
return Currency.dollarsFromCents(cents);
});
/**
* Converts bytes to base-10 size.
*/
Vue.filter('bytesToBase10String', (amountInBytes: number): string => {
return Size.toBase10String(amountInBytes);
});
const app = new Vue({
router,
store,

View File

@ -2,9 +2,9 @@
// See LICENSE for copying information.
/**
* Node is a representation of storagenode, that SNO could add to the Multinode Dashboard.
* NodeToAdd is a representation of storagenode, that SNO could add to the Multinode Dashboard.
*/
export class Node {
export class NodeToAdd {
public id: string; // TODO: create ts analog of storj.NodeID;
/**
* apiSecret is a secret issued by storagenode, that will be main auth mechanism in MND <-> SNO api.
@ -13,3 +13,28 @@ export class Node {
public publicAddress: string;
public name: string;
}
/**
* Describes node online statuses.
*/
export enum NodeStatus {
Online = 'online',
Offline = 'offline',
}
// TODO: refactor this
/**
* Node holds all information of node for the Multinode Dashboard.
*/
export class Node {
public constructor(
public id: string = '',
public name: string = '',
public diskSpaceUsed: number = 0,
public diskSpaceLeft: number = 0,
public bandwidthUsed: number = 0,
public earned: number = 0,
public version: string = '',
public status: NodeStatus = NodeStatus.Offline,
) {}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.5 2.50065C2.5 1.58018 3.24619 0.833984 4.16667 0.833984H15.8333C16.7538 0.833984 17.5 1.58018 17.5 2.50065V17.5006C17.5 18.4211 16.7538 19.1673 15.8333 19.1673H4.16667C3.24619 19.1673 2.5 18.4211 2.5 17.5006V2.50065ZM6.25 6.66732C6.71025 6.66732 7.08333 6.29423 7.08333 5.83398C7.08333 5.37373 6.71025 5.00065 6.25 5.00065C5.78975 5.00065 5.41667 5.37373 5.41667 5.83398C5.41667 6.29423 5.78975 6.66732 6.25 6.66732ZM7.91667 5.83398C7.91667 5.37375 8.28976 5.00065 8.75 5.00065H13.75C14.2102 5.00065 14.5833 5.37375 14.5833 5.83398C14.5833 6.29422 14.2102 6.66732 13.75 6.66732H8.75C8.28976 6.66732 7.91667 6.29422 7.91667 5.83398ZM8.75 9.16732C8.28976 9.16732 7.91667 9.54041 7.91667 10.0007C7.91667 10.4609 8.28976 10.834 8.75 10.834H13.75C14.2102 10.834 14.5833 10.4609 14.5833 10.0007C14.5833 9.54041 14.2102 9.16732 13.75 9.16732H8.75ZM8.75 13.334C8.28976 13.334 7.91667 13.7071 7.91667 14.1673C7.91667 14.6276 8.28976 15.0006 8.75 15.0006H13.75C14.2102 15.0006 14.5833 14.6276 14.5833 14.1673C14.5833 13.7071 14.2102 13.334 13.75 13.334H8.75ZM6.25 10.834C6.71025 10.834 7.08333 10.4609 7.08333 10.0007C7.08333 9.5404 6.71025 9.16732 6.25 9.16732C5.78975 9.16732 5.41667 9.5404 5.41667 10.0007C5.41667 10.4609 5.78975 10.834 6.25 10.834ZM6.25 15.0006C6.71025 15.0006 7.08333 14.6276 7.08333 14.1673C7.08333 13.7071 6.71025 13.334 6.25 13.334C5.78975 13.334 5.41667 13.7071 5.41667 14.1673C5.41667 14.6276 5.78975 15.0006 6.25 15.0006Z" fill="#586474"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 20C11.1 20 12 19.1 12 18H8C8 19.1 8.9 20 10 20ZM16 14V9C16 5.93 14.37 3.36 11.5 2.68V2C11.5 1.17 10.83 0.5 10 0.5C9.17 0.5 8.5 1.17 8.5 2V2.68C5.64 3.36 4 5.92 4 9V14L2 16V17H18V16L16 14ZM14 15H6V9C6 6.52 7.51 4.5 10 4.5C12.49 4.5 14 6.52 14 9V15Z" fill="#586474"/>
</svg>

After

Width:  |  Height:  |  Size: 382 B

View File

@ -0,0 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 10H16V14H14V10Z" fill="#586474"/>
<path d="M17.2 5.50039V3.70039C17.2 2.70769 16.3927 1.90039 15.4 1.90039H3.7C2.2114 1.90039 1 3.11179 1 4.60039V15.4004C1 17.3813 2.6146 18.1004 3.7 18.1004H17.2C18.1927 18.1004 19 17.2931 19 16.3004V7.30039C19 6.30769 18.1927 5.50039 17.2 5.50039ZM3.7 3.70039H15.4V5.50039H3.7C3.46827 5.49002 3.24946 5.39067 3.08915 5.22303C2.92883 5.05538 2.83936 4.83235 2.83936 4.60039C2.83936 4.36843 2.92883 4.1454 3.08915 3.97775C3.24946 3.81011 3.46827 3.71076 3.7 3.70039ZM17.2 16.3004H3.7108C3.295 16.2896 2.8 16.1249 2.8 15.4004V7.13389C3.0826 7.23559 3.3823 7.30039 3.7 7.30039H17.2V16.3004Z" fill="#586474"/>
</svg>

After

Width:  |  Height:  |  Size: 756 B

View File

@ -0,0 +1,3 @@
<svg width="20" height="18" viewBox="0 0 20 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.996 0.250003C10.3133 0.249188 10.6035 0.428639 10.7446 0.712863L13.1308 5.52073L18.4523 6.29668C18.7658 6.34239 19.0262 6.56186 19.1244 6.86304C19.2225 7.16422 19.1414 7.49502 18.9151 7.71666L15.0444 11.5074L15.9636 16.7734C16.0183 17.0871 15.8895 17.4047 15.6315 17.5915C15.3736 17.7784 15.0317 17.8019 14.7507 17.6521L9.99818 15.119L5.24675 17.652C4.96552 17.802 4.62341 17.7784 4.36543 17.5912C4.10745 17.4041 3.97879 17.0862 4.03398 16.7724L4.95973 11.5074L1.08283 7.71717C0.856142 7.49555 0.774837 7.1645 0.87307 6.86308C0.971303 6.56165 1.23206 6.34208 1.5458 6.29658L6.8962 5.52074L9.24979 0.716704C9.38939 0.431758 9.67869 0.250818 9.996 0.250003ZM10.003 2.96758L8.1994 6.64896C8.07832 6.89612 7.84301 7.06755 7.57064 7.10704L3.45354 7.70405L6.43915 10.6229C6.63585 10.8152 6.72498 11.0922 6.67734 11.3631L5.97158 15.3769L9.60611 13.4393C9.85112 13.3087 10.1451 13.3087 10.3901 13.4393L14.0282 15.3784L13.3272 11.3621C13.28 11.0917 13.369 10.8154 13.5651 10.6234L16.5469 7.70314L12.4582 7.10695C12.1876 7.06749 11.9535 6.89777 11.832 6.6528L10.003 2.96758Z" fill="#586474"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.1595 5.95509C6.21995 6.23073 5.41536 7.00873 5.41536 8.13094C5.41536 8.92561 5.77129 9.55151 6.3613 9.94129C6.90384 10.2997 7.57734 10.4172 8.20591 10.4172L11.6654 10.4175L11.7852 10.4172C12.4138 10.4172 13.088 10.2997 13.6315 9.94223C14.2224 9.5536 14.582 8.92806 14.582 8.13094C14.582 7.37062 14.2809 6.75139 13.7476 6.34748C13.4693 6.13672 13.1547 6.00459 12.8355 5.92898C12.7176 5.41471 12.4741 4.95056 12.1066 4.57903C11.5663 4.0327 10.8214 3.75081 9.9986 3.75081C9.18143 3.75081 8.43641 4.02397 7.89429 4.57032C7.51798 4.94955 7.27357 5.42597 7.1595 5.95509ZM8.73903 8.75052C8.74653 8.75052 8.754 8.75062 8.76145 8.75081H11.2209C11.2283 8.75062 11.2358 8.75052 11.2433 8.75052H11.7852C12.241 8.75052 12.5485 8.65969 12.7157 8.54975C12.8354 8.47098 12.9153 8.37007 12.9153 8.13094C12.9153 7.844 12.8215 7.73679 12.7413 7.67608C12.6285 7.59064 12.4177 7.51441 12.1037 7.51148L12.0849 7.51169C11.6247 7.51169 11.2516 7.13859 11.2516 6.67836C11.2516 6.24477 11.1107 5.94219 10.9216 5.75103C10.7338 5.56108 10.4355 5.41748 9.9986 5.41748C9.55609 5.41748 9.26072 5.55947 9.07736 5.74425C8.89451 5.92852 8.75297 6.22612 8.75116 6.6697L8.7512 6.67806C8.7512 6.73474 8.74554 6.7901 8.73475 6.84361C8.65809 7.22469 8.32146 7.51169 7.91781 7.51169L7.90041 7.51151C7.34512 7.51902 7.08203 7.88433 7.08203 8.13094C7.08203 8.37251 7.1625 8.47307 7.27999 8.55068C7.44495 8.65966 7.75005 8.75052 8.20591 8.75052H8.73903Z" fill="#131D3A"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.94915 1.91213C3.03015 1.52628 3.37046 1.25 3.76471 1.25H16.2573C16.6523 1.25 16.993 1.52729 17.0732 1.91404L19.1462 11.9058C19.1588 11.9632 19.1654 12.0229 19.1654 12.0841V17.5008C19.1654 17.9611 18.7923 18.3341 18.332 18.3341H1.66536C1.20513 18.3341 0.832031 17.9611 0.832031 17.5008V12.0841C0.832031 12.0208 0.83909 11.9592 0.852463 11.8999L2.94915 1.91213ZM15.5791 2.91667L17.3082 11.2508H2.69172L4.44127 2.91667H15.5791ZM15.832 14.7925C15.832 15.3678 15.3657 15.8341 14.7904 15.8341C14.2151 15.8341 13.7487 15.3678 13.7487 14.7925C13.7487 14.2172 14.2151 13.7508 14.7904 13.7508C15.3657 13.7508 15.832 14.2172 15.832 14.7925Z" fill="#131D3A"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,35 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
:root {
// colors
--c-title: #131d3a;
--c-gray: #74777e;
--c-label: #586474;
--c-placeholder: #a2aebe;
--c-background: #f4f6f9;
--c-line: #131d3a;
--c-gray--light: #e1e3e6;
--c-block-gray: #f8f8f9;
--c-primary: #0059d0;
--c-error: #eb5757;
--c-success: #04b978;
--c-warning: #f4b740;
--c-button-label: white;
--c-button-common: #2683ff;
--c-button-common-hover: #196cda;
--c-button-white: transparent;
--c-button-white--label: #354049;
--c-button-white-hover: #ededed;
--c-button-red: #ff4f4d;
--c-button-red-hover: #de3e3d;
--c-button-disabled: #dadde5;
// borders
--b-button-white: 1px solid #afb7c1;
// border radiuses
--br-button: 6px;
--br-input: 6px;
--br-table: 6px;
}

View File

@ -29,5 +29,16 @@ module.exports = {
args[0].template = './index.html';
return args
});
const svgRule = config.module.rule('svg');
svgRule.uses.clear();
svgRule
.use('babel-loader')
.loader('babel-loader')
.end()
.use('vue-svg-loader')
.loader('vue-svg-loader');
}
};

View File

@ -8,6 +8,17 @@
Use this token to create your access grant in the CLI tool.
</p>
<div class="cli-container__token-area">
<p class="cli-container__token-area__label">Satellite Address</p>
<div class="cli-container__token-area__container">
<p ref="addressContainer" class="cli-container__token-area__container__token" @click="selectAddress">{{ satelliteAddress }}</p>
<VButton
class="cli-container__token-area__container__button"
label="Copy"
width="66px"
height="30px"
:on-press="onCopyAddressClick"
/>
</div>
<p class="cli-container__token-area__label">Token</p>
<div class="cli-container__token-area__container">
<p class="cli-container__token-area__container__token">{{ restrictedKey }}</p>
@ -16,7 +27,7 @@
label="Copy"
width="66px"
height="30px"
:on-press="onCopyClick"
:on-press="onCopyTokenClick"
/>
</div>
</div>

View File

@ -44,7 +44,7 @@
flex-direction: column;
align-items: flex-start;
width: 100%;
margin-bottom: 50px;
margin-bottom: 30px;
&__label {
font-family: 'font_bold', sans-serif;
@ -61,6 +61,7 @@
width: calc(100% - 30px);
border: 1px solid rgba(56, 75, 101, 0.4);
border-radius: 6px;
margin-bottom: 20px;
&__token {
font-size: 16px;

Some files were not shown because too many files have changed in this diff Show More