add strfry backend.
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/fiatjaf/eventstore/mysql"
|
||||
"github.com/fiatjaf/eventstore/postgresql"
|
||||
"github.com/fiatjaf/eventstore/sqlite3"
|
||||
"github.com/fiatjaf/eventstore/strfry"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
@@ -52,6 +53,8 @@ var app = &cli.Command{
|
||||
case strings.HasPrefix(path, "https://"):
|
||||
// if we ever add something else that uses URLs we'll have to modify this
|
||||
typ = "elasticsearch"
|
||||
case strings.HasSuffix(path, ".conf"):
|
||||
typ = "strfry"
|
||||
default:
|
||||
// try to detect based on the form and names of disk files
|
||||
dbname, err := detect(path)
|
||||
@@ -102,6 +105,8 @@ var app = &cli.Command{
|
||||
}
|
||||
case "elasticsearch":
|
||||
db = &elasticsearch.ElasticsearchStorage{URL: path}
|
||||
case "strfry":
|
||||
db = &strfry.StrfryBackend{ConfigPath: path}
|
||||
case "":
|
||||
return fmt.Errorf("couldn't determine store type, you can use --type to specify it manually")
|
||||
default:
|
||||
|
||||
160
strfry/lib.go
Normal file
160
strfry/lib.go
Normal file
@@ -0,0 +1,160 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user