@ -15,7 +15,6 @@ import (
"lol.mleku.dev/log"
"lol.mleku.dev/log"
"next.orly.dev/app/config"
"next.orly.dev/app/config"
"next.orly.dev/pkg/database"
"next.orly.dev/pkg/database"
"next.orly.dev/pkg/database/indexes/types"
"git.mleku.dev/mleku/nostr/encoders/bech32encoding"
"git.mleku.dev/mleku/nostr/encoders/bech32encoding"
"git.mleku.dev/mleku/nostr/encoders/envelopes"
"git.mleku.dev/mleku/nostr/encoders/envelopes"
"git.mleku.dev/mleku/nostr/encoders/envelopes/eoseenvelope"
"git.mleku.dev/mleku/nostr/encoders/envelopes/eoseenvelope"
@ -104,7 +103,8 @@ func (f *Follows) Configure(cfg ...any) (err error) {
newOwnersSet [ hex . EncodeToString ( own ) ] = struct { } { }
newOwnersSet [ hex . EncodeToString ( own ) ] = struct { } { }
}
}
// find admin follow lists (database I/O happens here, but no lock held)
// parse admin pubkeys
var adminBinaries [ ] [ ] byte
for _ , admin := range f . cfg . Admins {
for _ , admin := range f . cfg . Admins {
var adm [ ] byte
var adm [ ] byte
if a , e := bech32encoding . NpubOrHexToPublicKeyBinary ( admin ) ; chk . E ( e ) {
if a , e := bech32encoding . NpubOrHexToPublicKeyBinary ( admin ) ; chk . E ( e ) {
@ -114,29 +114,27 @@ func (f *Follows) Configure(cfg ...any) (err error) {
}
}
newAdmins = append ( newAdmins , adm )
newAdmins = append ( newAdmins , adm )
newAdminsSet [ hex . EncodeToString ( adm ) ] = struct { } { }
newAdminsSet [ hex . EncodeToString ( adm ) ] = struct { } { }
adminBinaries = append ( adminBinaries , adm )
}
// Batch query all admin follow lists in a single DB call
// Kind 3 is replaceable, so QueryEvents returns only the latest per author
if len ( adminBinaries ) > 0 {
ctx := f . Ctx
if ctx == nil {
ctx = context . Background ( )
}
fl := & filter . F {
fl := & filter . F {
Authors : tag . NewFromAny ( adm ) ,
Authors : tag . NewFromBytesSlice ( adminBinaries ... ) ,
Kinds : kind . NewS ( kind . New ( kind . FollowList . K ) ) ,
Kinds : kind . NewS ( kind . New ( kind . FollowList . K ) ) ,
Limit : values . ToUintPointer ( uint ( len ( adminBinaries ) ) ) ,
}
}
var idxs [ ] database . Range
var evs event . S
if idxs , err = database . GetIndexesFromFilter ( fl ) ; chk . E ( err ) {
if evs , err = f . db . QueryEvents ( ctx , fl ) ; err != nil {
return
log . W . F ( "follows ACL: error querying admin follow lists: %v" , err )
}
err = nil // Don't fail Configure on query error
var sers types . Uint40s
for _ , idx := range idxs {
var s types . Uint40s
if s , err = f . db . GetSerialsByRange ( idx ) ; chk . E ( err ) {
continue
}
sers = append ( sers , s ... )
}
if len ( sers ) > 0 {
for _ , s := range sers {
var ev * event . E
if ev , err = f . db . FetchEventBySerial ( s ) ; chk . E ( err ) {
continue
}
}
for _ , ev := range evs {
for _ , v := range ev . Tags . GetAll ( [ ] byte ( "p" ) ) {
for _ , v := range ev . Tags . GetAll ( [ ] byte ( "p" ) ) {
// ValueHex() automatically handles both binary and hex storage formats
// ValueHex() automatically handles both binary and hex storage formats
if b , e := hex . DecodeString ( string ( v . ValueHex ( ) ) ) ; chk . E ( e ) {
if b , e := hex . DecodeString ( string ( v . ValueHex ( ) ) ) ; chk . E ( e ) {
@ -151,7 +149,6 @@ func (f *Follows) Configure(cfg ...any) (err error) {
}
}
}
}
}
}
}
// Now acquire the lock ONLY for the quick swap operation
// Now acquire the lock ONLY for the quick swap operation
f . followsMx . Lock ( )
f . followsMx . Lock ( )
@ -294,29 +291,23 @@ func (f *Follows) adminRelays() (urls []string) {
}
}
}
}
// First, try to get relay URLs from admin kind 10002 events
// Batch query all admin relay list events in a single DB call
for _ , adm := range admins {
// Kind 10002 is replaceable, so QueryEvents returns only the latest per author
if len ( admins ) > 0 {
ctx := f . Ctx
if ctx == nil {
ctx = context . Background ( )
}
fl := & filter . F {
fl := & filter . F {
Authors : tag . NewFromAny ( adm ) ,
Authors : tag . NewFromBytesSlice ( admins ... ) ,
Kinds : kind . NewS ( kind . New ( kind . RelayListMetadata . K ) ) ,
Kinds : kind . NewS ( kind . New ( kind . RelayListMetadata . K ) ) ,
Limit : values . ToUintPointer ( uint ( len ( admins ) ) ) ,
}
}
idxs , err := database . GetIndexesFromFilter ( fl )
evs , qerr := f . db . QueryEvents ( ctx , fl )
if chk . E ( err ) {
if qerr != nil {
continue
log . W . F ( "follows ACL: error querying admin relay lists: %v" , qerr )
}
var sers types . Uint40s
for _ , idx := range idxs {
s , err := f . db . GetSerialsByRange ( idx )
if chk . E ( err ) {
continue
}
sers = append ( sers , s ... )
}
for _ , s := range sers {
ev , err := f . db . FetchEventBySerial ( s )
if chk . E ( err ) || ev == nil {
continue
}
}
for _ , ev := range evs {
for _ , v := range ev . Tags . GetAll ( [ ] byte ( "r" ) ) {
for _ , v := range ev . Tags . GetAll ( [ ] byte ( "r" ) ) {
u := string ( v . Value ( ) )
u := string ( v . Value ( ) )
n := string ( normalize . URL ( u ) )
n := string ( normalize . URL ( u ) )