Discover Catalog Per

This commit is contained in:
mr
2026-01-27 15:39:53 +01:00
parent 50ff7ebf89
commit 0d3446ca5b
16 changed files with 1448 additions and 155 deletions

208
infrastructure/subscribe.go Normal file
View File

@@ -0,0 +1,208 @@
package infrastructure
import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"slices"
"time"
oclib "cloud.o-forge.io/core/oc-lib"
"cloud.o-forge.io/core/oc-lib/models/peer"
"cloud.o-forge.io/core/oc-lib/tools"
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/libp2p/go-libp2p/core/crypto"
)
func (ps *PubSubService) InitSubscribeEvents(ctx context.Context) error {
ourPeerID, err := oclib.GenerateNodeID()
if err != nil {
return err
}
// subscribe :
if err := ps.subscribeEvents(ctx, nil, SEARCH, "", -1); err != nil { // we subscribe at our proprer deductible search adresse.
return err
}
if err := ps.subscribeEvents(ctx, nil, SEARCH, ourPeerID, -1); err != nil { // we subscribe at our proprer deductible search adresse.
return err
}
if err := ps.PartnersSubscribeEvents(ctx); err != nil {
return nil
}
return nil
}
func (ps *PubSubService) SearchSubscribeEvents(
ctx context.Context,
priv crypto.PrivKey,
dt tools.DataType,
id string,
payload []byte,
) error {
return ps.subscribeEvents(ctx, &dt, CREATE, id, 10)
}
func (ps *PubSubService) PartnersSubscribeEvents(
ctx context.Context,
) error {
access := oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.PEER), "", "", []string{}, nil)
peers := access.Search(nil, fmt.Sprintf("%v", peer.PARTNER.EnumIndex()), false)
for _, p := range peers.Data {
loadedPeer := access.LoadOne(p.GetID())
rp := loadedPeer.ToPeer()
if rp == nil {
continue
}
if err := ps.PartnerSubscribeEvents(ctx, rp.PeerID); err != nil {
return err
}
}
return nil
}
func (ps *PubSubService) PartnerSubscribeEvents(
ctx context.Context,
peerID string,
) error {
if peerID == "" {
return errors.New("should discover a particular peer")
}
if err := ps.subscribeEvents(ctx, nil, CREATE, peerID, -1); err != nil {
return err
}
if err := ps.subscribeEvents(ctx, nil, UPDATE, peerID, -1); err != nil {
return err
}
if err := ps.subscribeEvents(ctx, nil, DELETE, peerID, -1); err != nil {
return err
}
return nil
}
// generic function to subscribe to DHT flow of event
func (ps *PubSubService) subscribeEvents(
ctx context.Context,
dt *tools.DataType,
action PubSubAction,
peerID string,
timeout int,
) error {
// define a name app.action#peerID
name := "oc-catalog." + action.String() + "#" + peerID
if dt != nil { // if a datatype is precised then : app.action.datatype#peerID
name = "oc-catalog." + action.String() + "." + (*dt).String() + "#" + peerID
}
topic, err := ps.PS.Join(name) // find out the topic
if err != nil {
return err
}
sub, err := topic.Subscribe() // then subscribe to it
if err != nil {
return err
}
ps.mutex.Lock() // add safely in cache your subscription.
ps.Subscription = append(ps.Subscription, name)
ps.mutex.Unlock()
// launch loop waiting for results.
go ps.waitResults(ctx, sub, name, timeout)
return nil
}
func (ps *PubSubService) waitResults(ctx context.Context, sub *pubsub.Subscription, topicName string, timeout int) {
logger := oclib.GetLogger()
defer ctx.Done()
for {
ps.mutex.Lock() // check safely if cache is actually notified subscribed to topic
if !slices.Contains(ps.Subscription, topicName) { // if not kill the loop.
break
}
ps.mutex.Unlock()
// if still subscribed -> wait for new message
var cancel context.CancelFunc
if timeout != -1 {
ctx, cancel = context.WithTimeout(ctx, time.Duration(timeout)*time.Second)
defer cancel()
}
msg, err := sub.Next(ctx)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
// timeout hit, no message before deadline kill subsciption.
ps.mutex.Lock()
subs := []string{}
for _, ss := range ps.Subscription {
if ss != topicName {
subs = append(subs, ss)
}
}
ps.Subscription = subs
ps.mutex.Unlock()
return
}
continue
}
var evt Event
if err := json.Unmarshal(msg.Data, &evt); err != nil { // map to event
continue
}
access := oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.PEER), "", "", []string{}, nil)
peers := access.Search(nil, evt.From, false)
if len(peers.Data) > 0 { // then we check if the peer is friendly or not : Partner or None...
if err := ps.processEventPeerKnown(ctx, peers, evt, topicName); err != nil {
logger.Err(err)
}
} else {
tools.NewNATSCaller().SetNATSPub(tools.PEER_DISCOVERY, map[string]string{
"peer_id": evt.From,
})
time.Sleep(30 * time.Second)
peers = access.Search(nil, evt.From, false)
if len(peers.Data) > 0 { // if found... ok... if not found ignore
if err := ps.processEventPeerKnown(ctx, peers, evt, topicName); err != nil {
logger.Err(err)
}
}
}
}
}
func (ps *PubSubService) processEventPeerKnown(ctx context.Context, peers oclib.LibDataShallow, event Event, topicName string) error {
if err := ps.verifyPeer(peers, event); err != nil {
return err
}
ps.handleEvent(ctx, topicName, event)
return nil
}
func (pc *PubSubService) verifyPeer(peers oclib.LibDataShallow, event Event) error {
access := oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.PEER), "", "", []string{}, nil)
loadedPeer := access.LoadOne(peers.Data[0].GetID())
rp := loadedPeer.ToPeer()
if rp == nil || rp.Relation == peer.BLACKLIST { // if peer is blacklisted... quit...
return errors.New("peer is blacklisted")
}
pubKey, err := PubKeyFromString(rp.PublicKey) // extract pubkey from pubkey str
if err != nil {
return errors.New("pubkey is malformed")
}
data, _ := json.Marshal(event.rawEvent()) // extract byte from raw event excluding signature.
if ok, _ := pubKey.Verify(data, event.Signature); !ok { // then verify if pubkey sign this message...
return errors.New("check signature failed")
}
return nil
}
func PubKeyFromString(s string) (crypto.PubKey, error) {
data, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return nil, err
}
return crypto.UnmarshalPublicKey(data)
}