Files
nostrlib/eventstore/cmd/eventstore/main.go

131 lines
3.2 KiB
Go

package main
import (
"bufio"
"context"
"encoding/json"
"fmt"
"log"
"os"
"strings"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/eventstore"
"fiatjaf.com/nostr/eventstore/badger"
"fiatjaf.com/nostr/eventstore/lmdb"
"fiatjaf.com/nostr/eventstore/slicestore"
"github.com/urfave/cli/v3"
)
var db eventstore.Store
var app = &cli.Command{
Name: "eventstore",
Usage: "a CLI for all the eventstore backends",
UsageText: "eventstore -d ./data/sqlite <query|save|delete> ...",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "store",
Aliases: []string{"d"},
Usage: "path to the database file or directory or database connection uri",
Required: true,
},
&cli.StringFlag{
Name: "type",
Aliases: []string{"t"},
Usage: "store type ('lmdb', 'badger', 'mmm')",
},
},
Before: func(ctx context.Context, c *cli.Command) (context.Context, error) {
path := strings.Trim(c.String("store"), "/")
typ := c.String("type")
if typ != "" {
// bypass automatic detection
// this also works for creating disk databases from scratch
} else {
// try to detect based on url scheme
switch {
case strings.HasPrefix(path, "postgres://"), strings.HasPrefix(path, "postgresql://"):
typ = "postgres"
case strings.HasPrefix(path, "mysql://"):
typ = "mysql"
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, ".jsonl"):
typ = "file"
default:
// try to detect based on the form and names of disk files
dbname, err := detect(path)
if err != nil {
if os.IsNotExist(err) {
return ctx, fmt.Errorf(
"'%s' does not exist, to create a store there specify the --type argument", path)
}
return ctx, fmt.Errorf("failed to detect store type: %w", err)
}
typ = dbname
}
}
switch typ {
case "lmdb":
db = &lmdb.LMDBBackend{Path: path}
case "badger":
db = &badger.BadgerBackend{Path: path}
case "mmm":
var err error
if db, err = doMmmInit(path); err != nil {
return ctx, err
}
case "file":
db = &slicestore.SliceStore{}
// run this after we've called db.Init()
defer func() {
f, err := os.Open(path)
if err != nil {
log.Printf("failed to file at '%s': %s\n", path, err)
os.Exit(3)
}
scanner := bufio.NewScanner(f)
scanner.Buffer(make([]byte, 16*1024*1024), 256*1024*1024)
i := 0
for scanner.Scan() {
var evt nostr.Event
if err := json.Unmarshal(scanner.Bytes(), &evt); err != nil {
log.Printf("invalid event read at line %d: %s (`%s`)\n", i, err, scanner.Text())
}
db.SaveEvent(evt)
i++
}
}()
case "":
return ctx, fmt.Errorf("couldn't determine store type, you can use --type to specify it manually")
default:
return ctx, fmt.Errorf("'%s' store type is not supported by this CLI", typ)
}
if err := db.Init(); err != nil {
return ctx, err
}
return ctx, nil
},
Commands: []*cli.Command{
queryOrSave,
query,
save,
delete_,
neg,
},
DefaultCommand: "query-or-save",
}
func main() {
if err := app.Run(context.Background(), os.Args); err != nil {
fmt.Println(err)
os.Exit(1)
}
}