diff --git a/main.go b/main.go index a3a79b9..decb367 100644 --- a/main.go +++ b/main.go @@ -55,6 +55,7 @@ var app = &cli.Command{ syncCmd, spell, profile, + validateCmd, }, Version: version, Flags: []cli.Flag{ diff --git a/validate.go b/validate.go new file mode 100644 index 0000000..586df22 --- /dev/null +++ b/validate.go @@ -0,0 +1,79 @@ +package main + +import ( + "context" + "fmt" + "strings" + + "fiatjaf.com/nostr" + "fiatjaf.com/nostr/schema" + "github.com/urfave/cli/v3" +) + +var validateCmd = &cli.Command{ + Name: "validate", + Usage: "validates events against the provided RoK YAML schema", + Description: `takes a URL to a YAML schema in the same format as that of https://github.com/nostr-protocol/registry-of-kinds (defaults to that one) and validates the event tags and content against it, according to its kind. + +example: +nak event -k 1 -p not_a_pubkey | nak validate +>> schema validation failed: tag[0][1]: invalid pubkey value 'not_a_pubkey' at tag 'p', index 1: pubkey should be 64-char hex, got 'not_a_pubkey' +`, + DisableSliceFlagSeparator: true, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "schema", + Usage: "url to download the YAML schema from, or path to the file", + Value: "https://raw.githubusercontent.com/nostr-protocol/registry-of-kinds/refs/heads/master/schema.yaml", + TakesFile: true, + }, + }, + Action: func(ctx context.Context, c *cli.Command) error { + var validator schema.Validator + + if schemaURL := c.String("schema"); strings.HasPrefix(schemaURL, "http") { + var err error + validator, err = schema.NewValidatorFromURL(schemaURL) + if err != nil { + return fmt.Errorf("failed to instantiate validator from '%s': %w", schemaURL, err) + } + } else { + var err error + validator, err = schema.NewValidatorFromFile(schemaURL) + if err != nil { + return fmt.Errorf("failed to instantiate validator from %s: %w", schemaURL, err) + } + } + + handleEvent := func(stdinEvent string) error { + evt := nostr.Event{} + if err := json.Unmarshal([]byte(stdinEvent), &evt); err != nil { + return fmt.Errorf("invalid event JSON: %w", err) + } + + if err := validator.ValidateEvent(evt); err != nil { + return fmt.Errorf("schema validation failed: %w", err) + } + + return nil + } + + for stdinEvent := range getJsonsOrBlank() { + if stdinEvent == "" { + for _, arg := range c.Args().Slice() { + if err := handleEvent(arg); err != nil { + ctx = lineProcessingError(ctx, "%s", err) + } + } + continue + } + + if err := handleEvent(stdinEvent); err != nil { + ctx = lineProcessingError(ctx, "%s", err) + } + } + + exitIfLineProcessingError(ctx) + return nil + }, +}