Fix pluralization and add background fetch of metadata and relay lists

- Updated log message to use correct plurals for `owners`, `pubkey`, and related variables
- Added background fetching of profile metadata, relay list metadata, and DM relays list using `SpiderFetch` in the goroutine
- Modified `server.go` to import `"orly.dev/pkg/protocol/socketapi"` correctly and initialize `listeners` with the updated constructor
This commit is contained in:
2025-07-20 23:57:41 +01:00
parent 4bbbbb1bb6
commit 90c9198ebe
7 changed files with 169 additions and 151 deletions

View File

@@ -6,6 +6,7 @@ import (
"fmt"
"net"
"net/http"
"orly.dev/pkg/protocol/socketapi"
"strconv"
"time"
@@ -15,7 +16,6 @@ import (
"orly.dev/pkg/app/relay/publish"
"orly.dev/pkg/interfaces/relay"
"orly.dev/pkg/protocol/servemux"
"orly.dev/pkg/protocol/socketapi"
"orly.dev/pkg/utils/chk"
"orly.dev/pkg/utils/context"
"orly.dev/pkg/utils/log"
@@ -90,15 +90,15 @@ func NewServer(sp *ServerParams, opts ...options.O) (s *Server, err error) {
}
serveMux := servemux.NewServeMux()
s = &Server{
Ctx: sp.Ctx,
Cancel: sp.Cancel,
relay: sp.Rl,
mux: serveMux,
options: op,
listeners: publish.New(socketapi.New()),
C: sp.C,
Lists: new(Lists),
Ctx: sp.Ctx,
Cancel: sp.Cancel,
relay: sp.Rl,
mux: serveMux,
options: op,
C: sp.C,
Lists: new(Lists),
}
s.listeners = publish.New(socketapi.New(s))
go func() {
if err := s.relay.Init(); chk.E(err) {
s.Shutdown()

View File

@@ -44,90 +44,92 @@ func (s *Server) Spider(noFetch ...bool) (err error) {
// there is no OwnersPubkeys, so there is nothing to do.
return
}
dontFetch := false
if len(noFetch) > 0 && noFetch[0] {
dontFetch = true
}
log.I.F("getting ownersFollowed")
var ownersFollowed [][]byte
if ownersFollowed, err = s.SpiderFetch(
kind.FollowList, dontFetch, ownersPubkeys...,
); chk.E(err) {
return
}
log.I.F("getting followedFollows")
var followedFollows [][]byte
if followedFollows, err = s.SpiderFetch(
kind.FollowList, dontFetch, ownersFollowed...,
); chk.E(err) {
return
}
log.I.F("getting ownersMuted")
var ownersMuted [][]byte
if ownersMuted, err = s.SpiderFetch(
kind.MuteList, dontFetch, ownersPubkeys...,
); chk.E(err) {
return
}
// remove the ownersFollowed and ownersMuted items from the followedFollows
// list
filteredFollows := make([][]byte, 0, len(followedFollows))
for _, follow := range followedFollows {
found := false
for _, owner := range ownersFollowed {
if bytes.Equal(follow, owner) {
found = true
break
go func() {
dontFetch := false
if len(noFetch) > 0 && noFetch[0] {
dontFetch = true
}
log.I.F("getting ownersFollowed")
var ownersFollowed [][]byte
if ownersFollowed, err = s.SpiderFetch(
kind.FollowList, dontFetch, ownersPubkeys...,
); chk.E(err) {
return
}
log.I.F("getting followedFollows")
var followedFollows [][]byte
if followedFollows, err = s.SpiderFetch(
kind.FollowList, dontFetch, ownersFollowed...,
); chk.E(err) {
return
}
log.I.F("getting ownersMuted")
var ownersMuted [][]byte
if ownersMuted, err = s.SpiderFetch(
kind.MuteList, dontFetch, ownersPubkeys...,
); chk.E(err) {
return
}
// remove the ownersFollowed and ownersMuted items from the followedFollows
// list
filteredFollows := make([][]byte, 0, len(followedFollows))
for _, follow := range followedFollows {
found := false
for _, owner := range ownersFollowed {
if bytes.Equal(follow, owner) {
found = true
break
}
}
for _, owner := range ownersMuted {
if bytes.Equal(follow, owner) {
found = true
break
}
}
if !found {
filteredFollows = append(filteredFollows, follow)
}
}
for _, owner := range ownersMuted {
if bytes.Equal(follow, owner) {
found = true
break
}
followedFollows = filteredFollows
own := "owner"
if len(ownersPubkeys) > 1 {
own = "owners"
}
if !found {
filteredFollows = append(filteredFollows, follow)
fol := "pubkey"
if len(ownersFollowed) > 1 {
fol = "pubkeys"
}
}
followedFollows = filteredFollows
own := "owner"
if len(ownersPubkeys) > 1 {
own = "owners"
}
fol := "pubkey"
if len(ownersFollowed) > 1 {
fol = "pubkeys"
}
folfol := "pubkey"
if len(followedFollows) > 1 {
folfol = "pubkeys"
}
mut := "pubkey"
if len(ownersMuted) > 1 {
mut = "pubkeys"
}
log.T.F(
"found %d %s with a total of %d followed %s and %d followed's follows %s, and excluding %d owner muted %s",
len(ownersPubkeys), own,
len(ownersFollowed), fol,
len(followedFollows), folfol,
len(ownersMuted), mut,
)
// add the owners
ownersFollowed = append(ownersFollowed, ownersPubkeys...)
s.SetOwnersPubkeys(ownersPubkeys)
s.SetOwnersFollowed(ownersFollowed)
s.SetFollowedFollows(followedFollows)
s.SetOwnersMuted(ownersMuted)
// lastly, update users profile metadata and relay lists in the background
if !dontFetch {
go func() {
everyone := append(ownersFollowed, followedFollows...)
s.SpiderFetch(kind.ProfileMetadata, false, everyone...)
s.SpiderFetch(kind.RelayListMetadata, false, everyone...)
s.SpiderFetch(kind.DMRelaysList, false, everyone...)
}()
}
folfol := "pubkey"
if len(followedFollows) > 1 {
folfol = "pubkeys"
}
mut := "pubkey"
if len(ownersMuted) > 1 {
mut = "pubkeys"
}
log.T.F(
"found %d %s with a total of %d followed %s and %d followed's follows %s, and excluding %d owner muted %s",
len(ownersPubkeys), own,
len(ownersFollowed), fol,
len(followedFollows), folfol,
len(ownersMuted), mut,
)
// add the owners
ownersFollowed = append(ownersFollowed, ownersPubkeys...)
s.SetOwnersPubkeys(ownersPubkeys)
s.SetOwnersFollowed(ownersFollowed)
s.SetFollowedFollows(followedFollows)
s.SetOwnersMuted(ownersMuted)
// lastly, update users profile metadata and relay lists in the background
if !dontFetch {
go func() {
everyone := append(ownersFollowed, followedFollows...)
s.SpiderFetch(kind.ProfileMetadata, false, everyone...)
s.SpiderFetch(kind.RelayListMetadata, false, everyone...)
s.SpiderFetch(kind.DMRelaysList, false, everyone...)
}()
}
}()
return
}

View File

@@ -16,7 +16,6 @@ import (
"orly.dev/pkg/utils/chk"
"orly.dev/pkg/utils/context"
"orly.dev/pkg/utils/errorf"
"orly.dev/pkg/utils/log"
"sort"
)
@@ -38,7 +37,6 @@ func (d *D) SaveEvent(c context.T, ev *event.E) (kc, vc int, err error) {
DTag: t.Value(),
}
at := a.Marshal(nil)
log.I.S(at)
if idxs, err = GetIndexesFromFilter(
&filter.F{
Authors: tag.New(ev.Pubkey),

View File

@@ -0,0 +1,45 @@
package auth
import (
"bytes"
"orly.dev/pkg/encoders/event"
"orly.dev/pkg/encoders/hex"
"orly.dev/pkg/encoders/tag"
)
func CheckPrivilege(authedPubkey []byte, ev *event.E) (privileged bool) {
if ev.Kind.IsPrivileged() {
if len(authedPubkey) == 0 {
// this is a shortcut because none of the following
// tests would return true.
return
}
// authed users when auth is required must be present in the
// event if it is privileged.
authedIsAuthor := bytes.Equal(ev.Pubkey, authedPubkey)
// if the authed pubkey matches the event author, it is
// allowed.
if !authedIsAuthor {
// check whether one of the p (mention) tags is
// present designating the authed pubkey, as this means
// the author wants the designated pubkey to be able to
// access the event. this is the case for nip-4, nip-44
// DMs, and gift-wraps. The query would usually have
// been for precisely a p tag with their pubkey.
eTags := ev.Tags.GetAll(tag.New("p"))
var hexAuthedKey []byte
hex.EncAppend(hexAuthedKey, authedPubkey)
var authedIsMentioned bool
for _, e := range eTags.ToSliceOfTags() {
if bytes.Equal(e.Value(), hexAuthedKey) {
authedIsMentioned = true
break
}
}
if !authedIsMentioned {
return
}
}
}
return
}

View File

@@ -26,6 +26,7 @@ import (
// corresponding handler method, generates a notice for errors or unknown types,
// logs the notice, and writes it back to the listener if required.
func (a *A) HandleMessage(msg []byte) {
log.T.F("received message:\n%s", string(msg))
var notice []byte
var err error
var t string

View File

@@ -1,7 +1,6 @@
package socketapi
import (
"bytes"
"errors"
"github.com/dgraph-io/badger/v4"
"orly.dev/pkg/encoders/envelopes/closedenvelope"
@@ -9,9 +8,8 @@ import (
"orly.dev/pkg/encoders/envelopes/eventenvelope"
"orly.dev/pkg/encoders/envelopes/reqenvelope"
"orly.dev/pkg/encoders/event"
"orly.dev/pkg/encoders/hex"
"orly.dev/pkg/encoders/tag"
"orly.dev/pkg/interfaces/server"
"orly.dev/pkg/protocol/auth"
"orly.dev/pkg/utils/chk"
"orly.dev/pkg/utils/context"
"orly.dev/pkg/utils/log"
@@ -91,39 +89,12 @@ func (a *A) HandleReq(
if srv.AuthRequired() {
var tmp event.S
for _, ev := range events {
if ev.Kind.IsPrivileged() {
authedPubkey := a.Listener.AuthedPubkey()
if len(authedPubkey) == 0 {
// this is a shortcut because none of the following
// tests would return true.
continue
}
// authed users when auth is required must be present in the
// event if it is privileged.
authedIsAuthor := bytes.Equal(ev.Pubkey, authedPubkey)
// if the authed pubkey matches the event author, it is
// allowed.
if !authedIsAuthor {
// check whether one of the p (mention) tags is
// present designating the authed pubkey, as this means
// the author wants the designated pubkey to be able to
// access the event. this is the case for nip-4, nip-44
// DMs, and gift-wraps. The query would usually have
// been for precisely a p tag with their pubkey.
eTags := ev.Tags.GetAll(tag.New("p"))
var hexAuthedKey []byte
hex.EncAppend(hexAuthedKey, authedPubkey)
var authedIsMentioned bool
for _, e := range eTags.ToSliceOfTags() {
if bytes.Equal(e.Value(), hexAuthedKey) {
authedIsMentioned = true
break
}
}
if !authedIsMentioned {
continue
}
}
if auth.CheckPrivilege(a.Listener.AuthedPubkey(), ev) {
log.W.F(
"not privileged %0x ev pubkey %0x",
a.Listener.AuthedPubkey(), ev.Pubkey,
)
continue
}
tmp = append(tmp, ev)
}

View File

@@ -5,6 +5,8 @@ import (
"orly.dev/pkg/encoders/event"
"orly.dev/pkg/encoders/filters"
"orly.dev/pkg/interfaces/publisher"
"orly.dev/pkg/interfaces/server"
"orly.dev/pkg/protocol/auth"
"orly.dev/pkg/protocol/ws"
"orly.dev/pkg/utils/chk"
"orly.dev/pkg/utils/log"
@@ -57,11 +59,13 @@ type S struct {
Mx sync.Mutex
// Map is the map of subscribers and subscriptions from the websocket api.
Map
// Server is an interface to the server.
Server server.I
}
var _ publisher.I = &S{}
func New() (publisher *S) { return &S{Map: make(Map)} }
func New(s server.I) (publisher *S) { return &S{Map: make(Map), Server: s} }
func (p *S) Type() (typeName string) { return Type }
@@ -98,6 +102,7 @@ func (p *S) Receive(msg publisher.Message) {
return
}
p.Mx.Lock()
defer p.Mx.Unlock()
if subs, ok := p.Map[m.Listener]; !ok {
subs = make(map[string]*filters.T)
subs[m.Id] = m.Filters
@@ -112,33 +117,25 @@ func (p *S) Receive(msg publisher.Message) {
"added subscription %s for %s", m.Id, m.Listener.RealRemote(),
)
}
p.Mx.Unlock()
}
}
// Deliver sends an event to all subscribers whose filters match the event
// Deliver processes and distributes an event to all matching subscribers based on their filter configurations.
//
// # Parameters
//
// - ev (*event.E): The event to deliver to matching subscribers
// - ev (*event.E): The event to be delivered to subscribed clients.
//
// # Expected behaviour
//
// # Locks the mutex to synchronize access to subscriber data
//
// # Iterates over all websocket connections and their associated subscriptions
//
// # Checks if each subscription's filter matches the event being delivered
//
// # Creates an event envelope result for matching subscriptions
//
// # Writes the result to the corresponding websocket connection
//
// Logs details about event delivery and any errors encountered
// Delivers the event to all subscribers whose filters match the event. It
// applies authentication checks if required by the server, and skips delivery
// for unauthenticated users when events are privileged.
func (p *S) Deliver(ev *event.E) {
log.T.F("delivering event %0x to subscribers", ev.Id)
var err error
p.Mx.Lock()
defer p.Mx.Unlock()
for w, subs := range p.Map {
log.I.F("%v %s", subs, w.RealRemote())
for id, subscriber := range subs {
@@ -149,17 +146,21 @@ func (p *S) Deliver(ev *event.E) {
if !subscriber.Match(ev) {
continue
}
var res *eventenvelope.Result
if res, err = eventenvelope.NewResultWith(id, ev); chk.E(err) {
continue
if p.Server.AuthRequired() {
if auth.CheckPrivilege(w.AuthedPubkey(), ev) {
continue
}
var res *eventenvelope.Result
if res, err = eventenvelope.NewResultWith(id, ev); chk.E(err) {
continue
}
if err = res.Write(w); chk.E(err) {
continue
}
log.T.F("dispatched event %0x to subscription %s", ev.Id, id)
}
if err = res.Write(w); chk.E(err) {
continue
}
log.T.F("dispatched event %0x to subscription %s", ev.Id, id)
}
}
p.Mx.Unlock()
}
// removeSubscriberId removes a specific subscription from a subscriber