Add HandleDelete and GetSerialsFromFilter methods, integrate admin keys handling, and enhance constraints API. Include a new CLI convert tool for key translation.
This commit is contained in:
@@ -31,6 +31,7 @@ type C struct {
|
||||
DBLogLevel string `env:"ORLY_DB_LOG_LEVEL" default:"info" usage:"database log level: fatal error warn info debug trace"`
|
||||
Pprof string `env:"ORLY_PPROF" usage:"enable pprof in modes: cpu,memory,allocation"`
|
||||
IPWhitelist []string `env:"ORLY_IP_WHITELIST" usage:"comma-separated list of IP addresses to allow access from, matches on prefixes to allow private subnets, eg 10.0.0 = 10.0.0.0/8"`
|
||||
Admins []string `env:"ORLY_ADMINS" usage:"comma-separated list of admin npubs"`
|
||||
}
|
||||
|
||||
// New creates and initializes a new configuration object for the relay
|
||||
|
||||
@@ -11,9 +11,7 @@ import (
|
||||
// HandleClose processes a CLOSE envelope by unmarshalling the request,
|
||||
// validates the presence of an <id> field, and signals cancellation for
|
||||
// the associated listener through the server's publisher mechanism.
|
||||
func (l *Listener) HandleClose(
|
||||
req []byte,
|
||||
) (err error) {
|
||||
func (l *Listener) HandleClose(req []byte) (err error) {
|
||||
var rem []byte
|
||||
env := closeenvelope.New()
|
||||
if rem, err = env.Unmarshal(req); chk.E(err) {
|
||||
|
||||
180
app/handle-delete.go
Normal file
180
app/handle-delete.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
database "database.orly"
|
||||
"database.orly/indexes/types"
|
||||
"encoders.orly/envelopes/eventenvelope"
|
||||
"encoders.orly/event"
|
||||
"encoders.orly/filter"
|
||||
"encoders.orly/hex"
|
||||
"encoders.orly/ints"
|
||||
"encoders.orly/kind"
|
||||
"encoders.orly/tag"
|
||||
"encoders.orly/tag/atag"
|
||||
"lol.mleku.dev/chk"
|
||||
"lol.mleku.dev/log"
|
||||
utils "utils.orly"
|
||||
)
|
||||
|
||||
func (l *Listener) GetSerialsFromFilter(f *filter.F) (
|
||||
sers types.Uint40s, err error,
|
||||
) {
|
||||
var idxs []database.Range
|
||||
if idxs, err = database.GetIndexesFromFilter(f); chk.E(err) {
|
||||
return
|
||||
}
|
||||
for _, idx := range idxs {
|
||||
var s types.Uint40s
|
||||
if s, err = l.GetSerialsByRange(idx); chk.E(err) {
|
||||
continue
|
||||
}
|
||||
sers = append(sers, s...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (l *Listener) HandleDelete(env *eventenvelope.Submission) {
|
||||
log.T.C(
|
||||
func() string {
|
||||
return fmt.Sprintf(
|
||||
"delete event\n%s", env.E.Serialize(),
|
||||
)
|
||||
},
|
||||
)
|
||||
var ownerDelete bool
|
||||
for _, pk := range l.Admins {
|
||||
if utils.FastEqual(pk, env.E.Pubkey) {
|
||||
ownerDelete = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// process the tags in the delete event
|
||||
var err error
|
||||
for _, t := range *env.E.Tags {
|
||||
// first search for a tags, as these are the simplest to process
|
||||
if utils.FastEqual(t.Key(), []byte("a")) {
|
||||
at := new(atag.T)
|
||||
if _, err = at.Unmarshal(t.Value()); chk.E(err) {
|
||||
continue
|
||||
}
|
||||
if ownerDelete || utils.FastEqual(env.E.Pubkey, at.Pubkey) {
|
||||
// find the event and delete it
|
||||
f := &filter.F{
|
||||
Authors: tag.NewFromBytesSlice(at.Pubkey),
|
||||
Kinds: kind.NewS(at.Kind),
|
||||
}
|
||||
if len(at.DTag) > 0 {
|
||||
f.Tags = tag.NewS(
|
||||
tag.NewFromAny("d", at.DTag),
|
||||
)
|
||||
}
|
||||
var sers types.Uint40s
|
||||
if sers, err = l.GetSerialsFromFilter(f); chk.E(err) {
|
||||
continue
|
||||
}
|
||||
// if found, delete them
|
||||
if len(sers) > 0 {
|
||||
for _, s := range sers {
|
||||
var ev *event.E
|
||||
if ev, err = l.FetchEventBySerial(s); chk.E(err) {
|
||||
continue
|
||||
}
|
||||
if !(kind.IsReplaceable(ev.Kind) && len(at.DTag) == 0) {
|
||||
// skip a tags with no dtag if the kind is not
|
||||
// replaceable.
|
||||
continue
|
||||
}
|
||||
if err = l.DeleteEventBySerial(
|
||||
l.Ctx, s, ev,
|
||||
); chk.E(err) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
// if e tags are found, delete them if the author is signer, or one of
|
||||
// the owners is signer
|
||||
if utils.FastEqual(t.Key(), []byte("e")) {
|
||||
var dst []byte
|
||||
if _, err = hex.DecBytes(dst, t.Value()); chk.E(err) {
|
||||
continue
|
||||
}
|
||||
f := &filter.F{
|
||||
Ids: tag.NewFromBytesSlice(dst),
|
||||
}
|
||||
var sers types.Uint40s
|
||||
if sers, err = l.GetSerialsFromFilter(f); chk.E(err) {
|
||||
continue
|
||||
}
|
||||
// if found, delete them
|
||||
if len(sers) > 0 {
|
||||
// there should be only one event per serial, so we can just
|
||||
// delete them all
|
||||
for _, s := range sers {
|
||||
var ev *event.E
|
||||
if ev, err = l.FetchEventBySerial(s); chk.E(err) {
|
||||
continue
|
||||
}
|
||||
// check that the author is the same as the signer of the
|
||||
// delete, for the k tag case the author is the signer of
|
||||
// the event.
|
||||
if !utils.FastEqual(env.E.Pubkey, ev.Pubkey) {
|
||||
continue
|
||||
}
|
||||
// exclude delete events
|
||||
if ev.Kind == kind.EventDeletion.K {
|
||||
continue
|
||||
}
|
||||
if err = l.DeleteEventBySerial(l.Ctx, s, ev); chk.E(err) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
// if k tags are found, check they are replaceable
|
||||
if utils.FastEqual(t.Key(), []byte("k")) {
|
||||
ki := ints.New(0)
|
||||
if _, err = ki.Unmarshal(t.Value()); chk.E(err) {
|
||||
continue
|
||||
}
|
||||
kn := ki.Uint16()
|
||||
// skip events that are delete events or that are not replaceable
|
||||
if !kind.IsReplaceable(kn) || kn != kind.EventDeletion.K {
|
||||
continue
|
||||
}
|
||||
f := &filter.F{
|
||||
Authors: tag.NewFromBytesSlice(env.E.Pubkey),
|
||||
Kinds: kind.NewS(kind.New(kn)),
|
||||
}
|
||||
var sers types.Uint40s
|
||||
if sers, err = l.GetSerialsFromFilter(f); chk.E(err) {
|
||||
continue
|
||||
}
|
||||
// if found, delete them
|
||||
if len(sers) > 0 {
|
||||
// there should be only one event per serial because replaces
|
||||
// delete old ones, so we can just delete them all
|
||||
for _, s := range sers {
|
||||
var ev *event.E
|
||||
if ev, err = l.FetchEventBySerial(s); chk.E(err) {
|
||||
continue
|
||||
}
|
||||
// check that the author is the same as the signer of the
|
||||
// delete, for the k tag case the author is the signer of
|
||||
// the event.
|
||||
if !utils.FastEqual(env.E.Pubkey, ev.Pubkey) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -12,9 +11,7 @@ import (
|
||||
utils "utils.orly"
|
||||
)
|
||||
|
||||
func (l *Listener) HandleEvent(c context.Context, msg []byte) (
|
||||
err error,
|
||||
) {
|
||||
func (l *Listener) HandleEvent(msg []byte) (err error) {
|
||||
// decode the envelope
|
||||
env := eventenvelope.NewSubmission()
|
||||
if msg, err = env.Unmarshal(msg); chk.E(err) {
|
||||
@@ -57,24 +54,22 @@ func (l *Listener) HandleEvent(c context.Context, msg []byte) (
|
||||
}
|
||||
// if the event is a delete, process the delete
|
||||
if env.E.Kind == kind.EventDeletion.K {
|
||||
|
||||
}
|
||||
// check if the event was deleted
|
||||
//
|
||||
// todo: the list of admin pubkeys should go in the second parameter when it
|
||||
// is implemented to enable admins to delete events of other users.
|
||||
if err = l.CheckForDeleted(env.E, nil); err != nil {
|
||||
if strings.HasPrefix(err.Error(), "blocked:") {
|
||||
errStr := err.Error()[len("blocked: "):len(err.Error())]
|
||||
if err = Ok.Error(
|
||||
l, env, errStr,
|
||||
); chk.E(err) {
|
||||
return
|
||||
l.HandleDelete(env)
|
||||
} else {
|
||||
// check if the event was deleted
|
||||
if err = l.CheckForDeleted(env.E, l.Admins); err != nil {
|
||||
if strings.HasPrefix(err.Error(), "blocked:") {
|
||||
errStr := err.Error()[len("blocked: "):len(err.Error())]
|
||||
if err = Ok.Error(
|
||||
l, env, errStr,
|
||||
); chk.E(err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// store the event
|
||||
if _, _, err = l.SaveEvent(c, env.E); chk.E(err) {
|
||||
if _, _, err = l.SaveEvent(l.Ctx, env.E); chk.E(err) {
|
||||
return
|
||||
}
|
||||
l.publishers.Deliver(env.E)
|
||||
|
||||
@@ -29,10 +29,10 @@ func (l *Listener) HandleMessage(msg []byte, remote string) {
|
||||
switch t {
|
||||
case eventenvelope.L:
|
||||
log.D.F("eventenvelope: %s", rem)
|
||||
err = l.HandleEvent(l.ctx, rem)
|
||||
err = l.HandleEvent(rem)
|
||||
case reqenvelope.L:
|
||||
log.D.F("reqenvelope: %s", rem)
|
||||
err = l.HandleReq(l.ctx, rem)
|
||||
err = l.HandleReq(rem)
|
||||
case closeenvelope.L:
|
||||
log.D.F("closeenvelope: %s", rem)
|
||||
err = l.HandleClose(rem)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"encoders.orly/envelopes/closedenvelope"
|
||||
@@ -18,7 +17,7 @@ import (
|
||||
"utils.orly/pointers"
|
||||
)
|
||||
|
||||
func (l *Listener) HandleReq(c context.Context, msg []byte) (
|
||||
func (l *Listener) HandleReq(msg []byte) (
|
||||
err error,
|
||||
) {
|
||||
var rem []byte
|
||||
@@ -36,7 +35,7 @@ func (l *Listener) HandleReq(c context.Context, msg []byte) (
|
||||
continue
|
||||
}
|
||||
}
|
||||
if events, err = l.QueryEvents(c, f); chk.E(err) {
|
||||
if events, err = l.QueryEvents(l.Ctx, f); chk.E(err) {
|
||||
if errors.Is(err, badger.ErrDBClosed) {
|
||||
return
|
||||
}
|
||||
|
||||
13
app/main.go
13
app/main.go
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
database "database.orly"
|
||||
"encoders.orly/bech32encoding"
|
||||
"lol.mleku.dev/chk"
|
||||
"lol.mleku.dev/log"
|
||||
"next.orly.dev/app/config"
|
||||
@@ -23,12 +24,24 @@ func Run(
|
||||
close(quit)
|
||||
}
|
||||
}()
|
||||
// get the admins
|
||||
var err error
|
||||
var adminKeys [][]byte
|
||||
for _, admin := range cfg.Admins {
|
||||
var pk []byte
|
||||
if pk, err = bech32encoding.NpubOrHexToPublicKeyBinary(admin); chk.E(err) {
|
||||
continue
|
||||
}
|
||||
adminKeys = append(adminKeys, pk)
|
||||
}
|
||||
|
||||
// start listener
|
||||
l := &Server{
|
||||
Ctx: ctx,
|
||||
Config: cfg,
|
||||
D: db,
|
||||
publishers: publish.New(NewPublisher(ctx)),
|
||||
Admins: adminKeys,
|
||||
}
|
||||
addr := fmt.Sprintf("%s:%d", cfg.Listen, cfg.Port)
|
||||
log.I.F("starting listener on http://%s", addr)
|
||||
|
||||
@@ -12,12 +12,13 @@ import (
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
mux *http.ServeMux
|
||||
Config *config.C
|
||||
Ctx context.Context
|
||||
remote string
|
||||
*database.D
|
||||
mux *http.ServeMux
|
||||
Config *config.C
|
||||
Ctx context.Context
|
||||
remote string
|
||||
publishers *publish.S
|
||||
Admins [][]byte
|
||||
*database.D
|
||||
}
|
||||
|
||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user