libs5-go/node/node.go

346 lines
7.9 KiB
Go
Raw Normal View History

package node
2024-01-06 11:33:46 +00:00
import (
"errors"
"fmt"
"git.lumeweb.com/LumeWeb/libs5-go/config"
"git.lumeweb.com/LumeWeb/libs5-go/encoding"
"git.lumeweb.com/LumeWeb/libs5-go/metadata"
"git.lumeweb.com/LumeWeb/libs5-go/protocol"
"git.lumeweb.com/LumeWeb/libs5-go/protocol/signed"
"git.lumeweb.com/LumeWeb/libs5-go/service"
"git.lumeweb.com/LumeWeb/libs5-go/storage"
2024-01-06 11:33:46 +00:00
"git.lumeweb.com/LumeWeb/libs5-go/structs"
"git.lumeweb.com/LumeWeb/libs5-go/types"
"git.lumeweb.com/LumeWeb/libs5-go/utils"
"github.com/go-resty/resty/v2"
"github.com/vmihailenco/msgpack/v5"
2024-01-06 11:33:46 +00:00
bolt "go.etcd.io/bbolt"
"go.uber.org/zap"
"time"
2024-01-06 11:33:46 +00:00
)
const cacheBucketName = "object-cache"
type Node struct {
nodeConfig *config.NodeConfig
metadataCache structs.Map
hashQueryRoutingTable structs.Map
services service.Services
httpClient *resty.Client
providerStore storage.ProviderStore
}
func (n *Node) NetworkId() string {
2024-01-07 14:02:39 +00:00
return n.nodeConfig.P2P.Network
}
func (n *Node) Services() service.Services {
return n.services
2024-01-06 11:33:46 +00:00
}
func NewNode(config *config.NodeConfig, services service.Services) *Node {
return &Node{
2024-01-06 11:33:46 +00:00
nodeConfig: config,
metadataCache: structs.NewMap(),
hashQueryRoutingTable: structs.NewMap(),
httpClient: resty.New(),
services: services, // Services are passed in, not created here
2024-01-06 11:33:46 +00:00
}
}
func (n *Node) HashQueryRoutingTable() structs.Map {
2024-01-06 11:33:46 +00:00
return n.hashQueryRoutingTable
}
func (n *Node) IsStarted() bool {
return n.services.IsStarted()
2024-01-06 11:33:46 +00:00
}
func (n *Node) Config() *config.NodeConfig {
2024-01-06 11:33:46 +00:00
return n.nodeConfig
}
func (n *Node) Logger() *zap.Logger {
2024-01-06 11:33:46 +00:00
if n.nodeConfig != nil {
return n.nodeConfig.Logger
}
return nil
}
func (n *Node) Db() *bolt.DB {
2024-01-06 11:33:46 +00:00
if n.nodeConfig != nil {
2024-01-06 18:15:45 +00:00
return n.nodeConfig.DB
2024-01-06 11:33:46 +00:00
}
return nil
}
func (n *Node) Start() error {
protocol.RegisterProtocols()
signed.RegisterSignedProtocols()
err :=
utils.CreateBucket(cacheBucketName, n.Db())
if err != nil {
return err
}
return n.services.Start()
}
func (n *Node) Stop() error {
return n.services.Stop()
}
func (n *Node) GetCachedStorageLocations(hash *encoding.Multihash, kinds []types.StorageLocationType) (map[string]storage.StorageLocation, error) {
locations := make(map[string]storage.StorageLocation)
2024-01-06 11:33:46 +00:00
locationMap, err := n.readStorageLocationsFromDB(hash)
2024-01-06 11:33:46 +00:00
if err != nil {
return nil, err
}
if len(locationMap) == 0 {
return make(map[string]storage.StorageLocation), nil
2024-01-06 11:33:46 +00:00
}
ts := time.Now().Unix()
for _, t := range kinds {
nodeMap, ok := (locationMap)[int(t)]
2024-01-06 11:33:46 +00:00
if !ok {
continue
}
for key, value := range nodeMap {
expiry, ok := value[3].(int64)
if !ok || expiry < ts {
continue
}
addressesInterface, ok := value[1].([]interface{})
2024-01-06 11:33:46 +00:00
if !ok {
continue
2024-01-06 11:33:46 +00:00
}
// Create a slice to hold the strings
addresses := make([]string, len(addressesInterface))
// Convert each element to string
for i, v := range addressesInterface {
str, ok := v.(string)
if !ok {
// Handle the error, maybe skip this element or set a default value
continue
}
addresses[i] = str
}
2024-01-06 11:33:46 +00:00
storageLocation := storage.NewStorageLocation(int(t), addresses, expiry)
if providerMessage, ok := value[4].([]byte); ok {
(storageLocation).SetProviderMessage(providerMessage)
2024-01-06 11:33:46 +00:00
}
locations[key] = storageLocation
2024-01-06 11:33:46 +00:00
}
}
return locations, nil
}
func (n *Node) readStorageLocationsFromDB(hash *encoding.Multihash) (storage.StorageLocationMap, error) {
var locationMap storage.StorageLocationMap
2024-01-06 11:33:46 +00:00
err := n.Db().View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(cacheBucketName)) // Replace with your actual bucket name
if b == nil {
return fmt.Errorf("bucket %s not found", cacheBucketName)
}
bytes := b.Get(hash.FullBytes())
if bytes == nil {
// If no data found, return an empty locationMap but no error
locationMap = storage.NewStorageLocationMap()
return nil
}
return msgpack.Unmarshal(bytes, &locationMap)
})
2024-01-06 11:33:46 +00:00
if err != nil {
return nil, err
}
return locationMap, nil
2024-01-06 11:33:46 +00:00
}
func (n *Node) AddStorageLocation(hash *encoding.Multihash, nodeId *encoding.NodeId, location storage.StorageLocation, message []byte) error {
2024-01-06 11:33:46 +00:00
// Read existing storage locations
locationDb, err := n.readStorageLocationsFromDB(hash)
2024-01-06 11:33:46 +00:00
if err != nil {
return err
}
nodeIdStr, err := nodeId.ToString()
if err != nil {
return err
}
2024-01-06 11:33:46 +00:00
// Get or create the inner map for the specific type
innerMap, exists := locationDb[location.Type()]
2024-01-06 11:33:46 +00:00
if !exists {
innerMap = make(storage.NodeStorage, 1)
innerMap[nodeIdStr] = make(storage.NodeDetailsStorage, 1)
2024-01-06 11:33:46 +00:00
}
// Create location map with new data
locationMap := make(map[int]interface{}, 3)
locationMap[1] = location.Parts()
locationMap[3] = location.Expiry()
2024-01-06 11:33:46 +00:00
locationMap[4] = message
// Update the inner map with the new location
innerMap[nodeIdStr] = locationMap
locationDb[location.Type()] = innerMap
2024-01-06 11:33:46 +00:00
// Serialize the updated map and store it in the database
packedBytes, err := msgpack.Marshal(locationDb)
2024-01-06 11:33:46 +00:00
if err != nil {
return err
}
err = n.Db().Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(cacheBucketName))
2024-01-06 11:33:46 +00:00
return b.Put(hash.FullBytes(), packedBytes)
})
2024-01-06 11:33:46 +00:00
if err != nil {
return err
}
return nil
}
2024-01-06 11:33:46 +00:00
func (n *Node) DownloadBytesByHash(hash *encoding.Multihash) ([]byte, error) {
// Initialize the download URI provider
dlUriProvider := storage.NewStorageLocationProvider(n, hash, types.StorageLocationTypeFull, types.StorageLocationTypeFile)
err := dlUriProvider.Start()
if err != nil {
return nil, err
}
2024-01-06 11:33:46 +00:00
retryCount := 0
for {
dlUri, err := dlUriProvider.Next()
if err != nil {
return nil, err
}
2024-01-09 16:01:25 +00:00
n.Logger().Debug("Trying to download from", zap.String("url", dlUri.Location().BytesURL()))
2024-01-06 11:33:46 +00:00
res, err := n.httpClient.R().Get(dlUri.Location().BytesURL())
2024-01-06 11:33:46 +00:00
if err != nil {
err := dlUriProvider.Downvote(dlUri)
if err != nil {
return nil, err
}
2024-01-06 11:33:46 +00:00
retryCount++
if retryCount > 32 {
return nil, errors.New("too many retries")
}
continue
}
bodyBytes := res.Body()
2024-01-06 11:33:46 +00:00
return bodyBytes, nil
2024-01-06 11:33:46 +00:00
}
}
func (n *Node) DownloadBytesByCID(cid *encoding.CID) (bytes []byte, err error) {
2024-01-09 21:18:49 +00:00
bytes, err = n.DownloadBytesByHash(&cid.Hash)
if err != nil {
return nil, err
}
return bytes, nil
}
func (n *Node) GetMetadataByCID(cid *encoding.CID) (md metadata.Metadata, err error) {
hashStr, err := cid.Hash.ToString()
if err != nil {
return nil, err
}
2024-01-06 11:33:46 +00:00
if n.metadataCache.Contains(hashStr) {
2024-01-09 16:01:57 +00:00
md, _ := n.metadataCache.Get(hashStr)
2024-01-06 11:33:46 +00:00
2024-01-09 16:01:57 +00:00
return md.(metadata.Metadata), nil
}
2024-01-06 11:33:46 +00:00
2024-01-09 16:01:57 +00:00
bytes, err := n.DownloadBytesByHash(&cid.Hash)
if err != nil {
return nil, err
}
2024-01-09 16:01:57 +00:00
switch cid.Type {
case types.CIDTypeMetadataMedia, types.CIDTypeBridge: // Both cases use the same deserialization method
md = metadata.NewEmptyMediaMetadata()
err = msgpack.Unmarshal(bytes, md)
if err != nil {
return nil, err
2024-01-06 11:33:46 +00:00
}
2024-01-09 16:01:57 +00:00
case types.CIDTypeMetadataWebapp:
md = metadata.NewEmptyWebAppMetadata()
2024-01-06 11:33:46 +00:00
err = msgpack.Unmarshal(bytes, md)
if err != nil {
return nil, err
}
case types.CIDTypeDirectory:
md = metadata.NewEmptyDirectoryMetadata()
2024-01-09 16:01:57 +00:00
err = msgpack.Unmarshal(bytes, md)
if err != nil {
return nil, err
}
default:
return nil, errors.New("unsupported metadata format")
2024-01-06 11:33:46 +00:00
}
2024-01-09 16:01:57 +00:00
n.metadataCache.Put(hashStr, md)
return md, nil
2024-01-06 11:33:46 +00:00
}
func (n *Node) WaitOnConnectedPeers() {
n.services.P2P().WaitOnConnectedPeers()
}
func (n *Node) SetProviderStore(store storage.ProviderStore) {
n.providerStore = store
}
func (n *Node) ProviderStore() storage.ProviderStore {
return n.providerStore
}
func DefaultNode(config *config.NodeConfig) *Node {
params := service.ServiceParams{
Logger: config.Logger,
Config: config,
Db: config.DB,
}
// Initialize services first
p2pService := service.NewP2P(params)
registryService := service.NewRegistry(params)
httpService := service.NewHTTP(params)
// Aggregate services
services := NewServices(ServicesParams{
P2P: p2pService,
Registry: registryService,
HTTP: httpService,
})
// Now create the node with the services
return NewNode(config, services)
}