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