Files
eventstore/strfry/lib.go
fiatjaf 92588ac842
Some checks failed
build cli / make-release (push) Has been cancelled
build cli / build-linux (push) Has been cancelled
add strfry backend.
2024-07-19 22:18:56 -03:00

161 lines
3.3 KiB
Go

package strfry
import (
"bytes"
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/fiatjaf/eventstore"
"github.com/mailru/easyjson"
"github.com/nbd-wtf/go-nostr"
)
var _ eventstore.Store = (*StrfryBackend)(nil)
type StrfryBackend struct {
ConfigPath string
ExecutablePath string
}
func (s *StrfryBackend) Init() error {
if s.ExecutablePath == "" {
configPath := filepath.Dir(s.ConfigPath)
os.Setenv("PATH", configPath+":"+os.Getenv("PATH"))
exe, err := exec.LookPath("strfry")
if err != nil {
return fmt.Errorf("failed to find strfry executable: %w (better provide it manually)", err)
}
s.ExecutablePath = exe
}
return nil
}
func (_ StrfryBackend) Close() {}
func (s StrfryBackend) QueryEvents(ctx context.Context, filter nostr.Filter) (chan *nostr.Event, error) {
stdout, err := s.baseStrfryScan(ctx, filter)
if err != nil {
return nil, err
}
ch := make(chan *nostr.Event)
go func() {
defer close(ch)
for {
line, err := stdout.ReadBytes('\n')
if err != nil {
break
}
evt := &nostr.Event{}
easyjson.Unmarshal(line, evt)
if evt.ID == "" {
continue
}
ch <- evt
}
}()
return ch, nil
}
func (s StrfryBackend) SaveEvent(ctx context.Context, evt *nostr.Event) error {
args := make([]string, 0, 4)
if s.ConfigPath != "" {
args = append(args, "--config="+s.ConfigPath)
}
args = append(args, "import")
args = append(args, "--show-rejected")
args = append(args, "--no-verify")
cmd := exec.CommandContext(ctx, s.ExecutablePath, args...)
var stderr bytes.Buffer
cmd.Stderr = &stderr
// event is sent on stdin
j, _ := easyjson.Marshal(evt)
cmd.Stdin = bytes.NewBuffer(j)
err := cmd.Run()
if err != nil {
return fmt.Errorf(
"%s %s failed: %w, (%s)",
s.ExecutablePath, strings.Join(args, " "), err, stderr.String(),
)
}
return nil
}
func (s StrfryBackend) DeleteEvent(ctx context.Context, evt *nostr.Event) error {
args := make([]string, 0, 3)
if s.ConfigPath != "" {
args = append(args, "--config="+s.ConfigPath)
}
args = append(args, "delete")
args = append(args, "--filter={\"ids\":[\""+evt.ID+"\"]}")
cmd := exec.CommandContext(ctx, s.ExecutablePath, args...)
var stderr bytes.Buffer
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
return fmt.Errorf(
"%s %s failed: %w, (%s)",
s.ExecutablePath, strings.Join(args, " "), err, stderr.String(),
)
}
return nil
}
func (s StrfryBackend) CountEvents(ctx context.Context, filter nostr.Filter) (int64, error) {
stdout, err := s.baseStrfryScan(ctx, filter)
if err != nil {
return 0, err
}
var count int64
for {
_, err := stdout.ReadBytes('\n')
if err != nil {
break
}
count++
}
return count, nil
}
func (s StrfryBackend) baseStrfryScan(ctx context.Context, filter nostr.Filter) (*bytes.Buffer, error) {
args := make([]string, 0, 3)
if s.ConfigPath != "" {
args = append(args, "--config="+s.ConfigPath)
}
args = append(args, "scan")
args = append(args, filter.String())
cmd := exec.CommandContext(ctx, s.ExecutablePath, args...)
var stdout bytes.Buffer
cmd.Stdout = &stdout
var stderr bytes.Buffer
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
return nil, fmt.Errorf(
"%s %s failed: %w, (%s)",
s.ExecutablePath, strings.Join(args, " "), err, stderr.String(),
)
}
return &stdout, nil
}