Files
oc-discovery/daemons/node/indexer/handler.go

249 lines
6.4 KiB
Go
Raw Normal View History

2026-01-30 16:57:36 +01:00
package indexer
import (
2026-02-04 11:35:19 +01:00
"context"
2026-01-30 16:57:36 +01:00
"encoding/base64"
"encoding/json"
"errors"
2026-02-03 15:25:15 +01:00
"fmt"
2026-01-30 16:57:36 +01:00
"oc-discovery/daemons/node/common"
"time"
2026-02-03 15:25:15 +01:00
oclib "cloud.o-forge.io/core/oc-lib"
2026-01-30 16:57:36 +01:00
pp "cloud.o-forge.io/core/oc-lib/models/peer"
"cloud.o-forge.io/core/oc-lib/models/utils"
"cloud.o-forge.io/core/oc-lib/tools"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
)
type PeerRecord struct {
Name string `json:"name"`
DID string `json:"did"` // real PEER ID
PeerID string `json:"peer_id"`
PubKey []byte `json:"pub_key"`
APIUrl string `json:"api_url"`
StreamAddress string `json:"stream_address"`
NATSAddress string `json:"nats_address"`
WalletAddress string `json:"wallet_address"`
Signature []byte `json:"signature"`
ExpiryDate time.Time `json:"expiry_date"`
}
func (p *PeerRecord) Sign() error {
priv, err := tools.LoadKeyFromFilePrivate()
2026-01-30 16:57:36 +01:00
if err != nil {
return err
}
dht := PeerRecord{
Name: p.Name,
DID: p.DID,
PubKey: p.PubKey,
ExpiryDate: p.ExpiryDate,
}
payload, _ := json.Marshal(dht)
b, err := common.Sign(priv, payload)
p.Signature = b
return err
}
func (p *PeerRecord) Verify() (crypto.PubKey, error) {
pubKey, err := crypto.UnmarshalPublicKey(p.PubKey) // retrieve pub key in message
if err != nil {
2026-02-03 15:25:15 +01:00
fmt.Println("UnmarshalPublicKey")
2026-01-30 16:57:36 +01:00
return pubKey, err
}
dht := PeerRecord{
Name: p.Name,
DID: p.DID,
PubKey: p.PubKey,
ExpiryDate: p.ExpiryDate,
}
payload, _ := json.Marshal(dht)
if ok, _ := common.Verify(pubKey, payload, p.Signature); !ok { // verify minimal message was sign per pubKey
2026-02-03 15:25:15 +01:00
fmt.Println("Verify")
2026-01-30 16:57:36 +01:00
return pubKey, errors.New("invalid signature")
}
return pubKey, nil
}
func (pr *PeerRecord) ExtractPeer(ourkey string, key string, pubKey crypto.PubKey) (bool, *pp.Peer, error) {
2026-02-03 15:25:15 +01:00
pubBytes, err := crypto.MarshalPublicKey(pubKey)
if err != nil {
return false, nil, err
}
2026-01-30 16:57:36 +01:00
rel := pp.NONE
if ourkey == key { // at this point is PeerID is same as our... we are... thats our peer INFO
rel = pp.SELF
}
p := &pp.Peer{
AbstractObject: utils.AbstractObject{
UUID: pr.DID,
Name: pr.Name,
},
State: pp.ONLINE,
Relation: rel, // VERIFY.... it crush nothing
PeerID: pr.PeerID,
PublicKey: base64.StdEncoding.EncodeToString(pubBytes),
APIUrl: pr.APIUrl,
StreamAddress: pr.StreamAddress,
NATSAddress: pr.NATSAddress,
WalletAddress: pr.WalletAddress,
}
2026-02-03 15:25:15 +01:00
if time.Now().UTC().After(pr.ExpiryDate) { // is expired
2026-01-30 16:57:36 +01:00
p.State = pp.OFFLINE // then is considers OFFLINE
}
b, err := json.Marshal(p)
if err != nil {
return pp.SELF == p.Relation, nil, err
}
2026-02-05 15:36:22 +01:00
fmt.Println("SENDPEER SELF")
go tools.NewNATSCaller().SetNATSPub(tools.CREATE_PEER, tools.NATSResponse{
2026-01-30 16:57:36 +01:00
FromApp: "oc-discovery",
Datatype: tools.PEER,
Method: int(tools.CREATE_PEER),
Payload: b,
})
if p.State == pp.OFFLINE {
return pp.SELF == p.Relation, nil, errors.New("peer " + key + " is offline")
}
return pp.SELF == p.Relation, p, nil
}
type GetValue struct {
Key string `json:"key"`
PeerID peer.ID `json:"peer_id"`
2026-01-30 16:57:36 +01:00
}
type GetResponse struct {
2026-02-04 11:35:19 +01:00
Found bool `json:"found"`
Records map[string]PeerRecord `json:"records,omitempty"`
2026-01-30 16:57:36 +01:00
}
func (ix *IndexerService) genKey(did string) string {
return "/node/" + did
}
2026-01-30 16:57:36 +01:00
func (ix *IndexerService) initNodeHandler() {
ix.Host.SetStreamHandler(common.ProtocolHeartbeat, ix.HandleNodeHeartbeat)
ix.Host.SetStreamHandler(common.ProtocolPublish, ix.handleNodePublish)
ix.Host.SetStreamHandler(common.ProtocolGet, ix.handleNodeGet)
}
func (ix *IndexerService) handleNodePublish(s network.Stream) {
defer s.Close()
2026-02-03 15:25:15 +01:00
logger := oclib.GetLogger()
for {
var rec PeerRecord
if err := json.NewDecoder(s).Decode(&rec); err != nil {
logger.Err(err)
2026-02-03 15:25:15 +01:00
continue
}
rec2 := PeerRecord{
Name: rec.Name,
DID: rec.DID, // REAL PEER ID
PubKey: rec.PubKey,
PeerID: rec.PeerID,
}
if _, err := rec2.Verify(); err != nil {
logger.Err(err)
continue
}
if rec.PeerID == "" || rec.ExpiryDate.Before(time.Now().UTC()) { // already expired
logger.Err(errors.New(rec.PeerID + " is expired."))
2026-02-03 15:25:15 +01:00
continue
}
pid, err := peer.Decode(rec.PeerID)
if err != nil {
continue
}
2026-01-30 16:57:36 +01:00
2026-02-03 15:25:15 +01:00
ix.StreamMU.Lock()
2026-01-30 16:57:36 +01:00
if ix.StreamRecords[common.ProtocolHeartbeat] == nil {
ix.StreamRecords[common.ProtocolHeartbeat] = map[peer.ID]*common.StreamRecord[PeerRecord]{}
2026-01-30 16:57:36 +01:00
}
streams := ix.StreamRecords[common.ProtocolHeartbeat]
2026-02-03 15:25:15 +01:00
if srec, ok := streams[pid]; ok {
srec.DID = rec.DID
srec.Record = rec
2026-02-17 13:11:22 +01:00
srec.HeartbeatStream.UptimeTracker.LastSeen = time.Now().UTC()
2026-02-03 15:25:15 +01:00
} else {
2026-02-17 13:11:22 +01:00
ix.StreamMU.Unlock()
logger.Err(errors.New("no heartbeat"))
continue
2026-02-03 15:25:15 +01:00
}
2026-02-17 13:11:22 +01:00
ix.StreamMU.Unlock()
2026-02-03 15:25:15 +01:00
key := ix.genKey(rec.DID)
data, err := json.Marshal(rec)
if err != nil {
logger.Err(err)
continue
2026-02-04 11:35:19 +01:00
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
if err := ix.DHT.PutValue(ctx, key, data); err != nil {
logger.Err(err)
cancel()
continue
}
cancel()
break // response... so quit
}
2026-01-30 16:57:36 +01:00
}
func (ix *IndexerService) handleNodeGet(s network.Stream) {
defer s.Close()
2026-02-03 15:25:15 +01:00
logger := oclib.GetLogger()
for {
var req GetValue
if err := json.NewDecoder(s).Decode(&req); err != nil {
logger.Err(err)
continue
}
ix.StreamMU.Lock()
2026-01-30 16:57:36 +01:00
if ix.StreamRecords[common.ProtocolHeartbeat] == nil {
ix.StreamRecords[common.ProtocolHeartbeat] = map[peer.ID]*common.StreamRecord[PeerRecord]{}
2026-01-30 16:57:36 +01:00
}
2026-02-04 11:35:19 +01:00
resp := GetResponse{
Found: false,
Records: map[string]PeerRecord{},
}
streams := ix.StreamRecords[common.ProtocolHeartbeat]
key := ix.genKey(req.Key)
2026-02-03 15:25:15 +01:00
// simple lookup by PeerID (or DID)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
recBytes, err := ix.DHT.SearchValue(ctx, key)
if err != nil {
logger.Err(err).Msg("Failed to fetch PeerRecord from DHT")
cancel()
}
cancel()
for c := range recBytes {
var rec PeerRecord
if err := json.Unmarshal(c, &rec); err != nil || rec.PeerID != req.PeerID.String() {
2026-02-04 11:35:19 +01:00
continue
2026-02-03 15:25:15 +01:00
}
resp.Found = true
resp.Records[rec.PeerID] = rec
if srec, ok := streams[req.PeerID]; ok {
srec.DID = rec.DID
srec.Record = rec
srec.HeartbeatStream.UptimeTracker.LastSeen = time.Now().UTC()
2026-02-03 15:25:15 +01:00
}
}
2026-02-03 15:25:15 +01:00
// Not found
2026-02-04 11:35:19 +01:00
_ = json.NewEncoder(s).Encode(resp)
2026-02-03 15:25:15 +01:00
ix.StreamMU.Unlock()
break // response... so quit
}
2026-01-30 16:57:36 +01:00
}