diff --git a/go.mod b/go.mod index 08f598e..6777509 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/fiatjaf/nak go 1.25 require ( - fiatjaf.com/nostr v0.0.0-20260919022302-cf8167ebdb95 + fiatjaf.com/nostr v0.0.0-20260928115942-58e4c715304e github.com/AlecAivazis/survey/v2 v2.3.7 github.com/bep/debounce v1.2.1 github.com/btcsuite/btcd/btcec/v2 v2.3.6 diff --git a/go.sum b/go.sum index 8e1e90e..d1e1972 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,7 @@ fiatjaf.com/lib v0.3.7 h1:mXZOn7NrUcjSdy4oNvwQyAmes7Ueb+Zr5hjqMIe2dxI= fiatjaf.com/lib v0.3.7/go.mod h1:UlHaZvPHj25PtKLh9GjZkUHRmQ2xZ8Jkoa4VRaLeeQ8= -fiatjaf.com/nostr v0.0.0-20260827104008-28e324b57f7e h1:BuWR1BP/CBzM+ZbLoUKuV6KlktlDZEyD9N/pdxqJJyE= -fiatjaf.com/nostr v0.0.0-20260827104008-28e324b57f7e/go.mod h1:b1EIUDnd133Ie8Pg8O/biaKdFyCMz28aD4n64g1GqvM= -fiatjaf.com/nostr v0.0.0-20260914174524-1ce6fccff3c3 h1:xzGlJR7EupVzxD5XGP/y4Z/i5qAxaBH3TzvCPyemLZM= -fiatjaf.com/nostr v0.0.0-20260914174524-1ce6fccff3c3/go.mod h1:b1EIUDnd133Ie8Pg8O/biaKdFyCMz28aD4n64g1GqvM= -fiatjaf.com/nostr v0.0.0-20260916003959-33dfb12f7374 h1:Ms85MGJyOpKsG6y4llGwa/9ro3OtZBcmdXtJwOa/Qg4= -fiatjaf.com/nostr v0.0.0-20260916003959-33dfb12f7374/go.mod h1:b1EIUDnd133Ie8Pg8O/biaKdFyCMz28aD4n64g1GqvM= -fiatjaf.com/nostr v0.0.0-20260916040958-27e395a0f6e7 h1:UKC6fhAmyoDQTQ7FH6gTF31WSprVgLYUiLiV8BRPEbU= -fiatjaf.com/nostr v0.0.0-20260916040958-27e395a0f6e7/go.mod h1:b1EIUDnd133Ie8Pg8O/biaKdFyCMz28aD4n64g1GqvM= -fiatjaf.com/nostr v0.0.0-20260919022302-cf8167ebdb95 h1:E59biPECxSHqdfjGFwQ+UMoZSnZfK9tjrpXuLVes6gY= -fiatjaf.com/nostr v0.0.0-20260919022302-cf8167ebdb95/go.mod h1:b1EIUDnd133Ie8Pg8O/biaKdFyCMz28aD4n64g1GqvM= +fiatjaf.com/nostr v0.0.0-20260928115942-58e4c715304e h1:ZHPQsQzd0rGdOq/vCMUOy9FfrzlmAxvQ+V+4ucJ1WXY= +fiatjaf.com/nostr v0.0.0-20260928115942-58e4c715304e/go.mod h1:b1EIUDnd133Ie8Pg8O/biaKdFyCMz28aD4n64g1GqvM= github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= github.com/FastFilter/xorfilter v0.2.1 h1:lbdeLG9BdpquK64ZsleBS8B4xO/QW1IM0gMzF7KaBKc= diff --git a/nsite.go b/nsite.go index b6e19df..c00a6d6 100644 --- a/nsite.go +++ b/nsite.go @@ -246,6 +246,13 @@ var nsite = &cli.Command{ Usage: "downloads all files from a published nsite", ArgsUsage: " [directory]", DisableSliceFlagSeparator: true, + Flags: []cli.Flag{ + &cli.StringSliceFlag{ + Name: "server", + Aliases: []string{"s"}, + Usage: "extra blossom server hostname or URL to try, can be given multiple times", + }, + }, Action: func(ctx context.Context, c *cli.Command) error { input := c.Args().First() if input == "" { @@ -265,23 +272,45 @@ var nsite = &cli.Command{ return fmt.Errorf("failed to stat output directory %s: %w", outputDir, err) } - pk, identifier, isRoot, err := nip5a.DecodeSiteURL(input) - if err != nil { + var filter nostr.Filter + var relays []string + var readOnlySigner nostr.Signer = keyer.NewReadOnlySigner(nostr.ZeroPK) + + if strings.HasPrefix(input, "nostr:") { + input = input[6:] + } + + if strings.HasPrefix(input, "nevent1") || strings.HasPrefix(input, "naddr1") { + ptr, err := nip19.ToPointer(input) + if err != nil { + return err + } + filter = ptr.AsFilter() + + if pk := ptr.AssociatedPublicKey(); pk != nostr.ZeroPK { + relays = sys.FetchWriteRelays(ctx, pk) + readOnlySigner = keyer.NewReadOnlySigner(pk) + } + relays = append(relays, ptr.RelayHints()...) + } else if pk, identifier, isRoot, err := nip5a.DecodeSiteURL(input); err == nil { + filter = nostr.Filter{ + Authors: []nostr.PubKey{pk}, + Limit: 1, + } + if isRoot { + filter.Kinds = []nostr.Kind{nostr.KindNsiteRoot} + } else { + filter.Kinds = []nostr.Kind{nostr.KindNsiteNamed} + filter.Tags = nostr.TagMap{"d": []string{identifier}} + } + + relays = sys.FetchWriteRelays(ctx, pk) + readOnlySigner = keyer.NewReadOnlySigner(pk) + } else { return err } - filter := nostr.Filter{ - Authors: []nostr.PubKey{pk}, - Limit: 1, - } - if isRoot { - filter.Kinds = []nostr.Kind{nostr.KindNsiteRoot} - } else { - filter.Kinds = []nostr.Kind{nostr.KindNsiteNamed} - filter.Tags = nostr.TagMap{"d": []string{identifier}} - } - - res := sys.Pool.QuerySingle(ctx, sys.FetchWriteRelays(ctx, pk), filter, nostr.SubscriptionOptions{ + res := sys.Pool.QuerySingle(ctx, relays, filter, nostr.SubscriptionOptions{ Label: "nak-nsite", }) if res == nil { @@ -297,15 +326,18 @@ var nsite = &cli.Command{ if len(blossomServers) == 0 { servers := sys.FetchBlossomServerList(ctx, res.Event.PubKey) if len(servers.Items) == 0 { - return fmt.Errorf("no blossom servers advertised in manifest or kind:10063") - } - blossomServers = make([]string, len(servers.Items)) - for i, s := range servers.Items { - blossomServers[i] = s.Value() + blossomServers = []string{} + } else { + blossomServers = make([]string, len(servers.Items)) + for i, s := range servers.Items { + blossomServers[i] = s.Value() + } } } - - signer := keyer.NewReadOnlySigner(pk) + blossomServers = nostr.AppendUnique(blossomServers, c.StringSlice("server")...) + if len(blossomServers) == 0 { + return fmt.Errorf("no blossom servers advertised in manifest or kind:10063") + } for path, hash := range mnf.Paths { relPath := strings.TrimPrefix(path, "/") @@ -319,12 +351,13 @@ var nsite = &cli.Command{ var downloadErr error for _, server := range blossomServers { - client := blossom.NewClient(server, signer) + client := blossom.NewClient(server, readOnlySigner) data, err := client.Download(ctx, hash) if err != nil { - downloadErr = err + downloadErr = errors.Join(downloadErr, fmt.Errorf("%s: %w", server, err)) continue } + if err := os.WriteFile(fullPath, data, 0o644); err != nil { return fmt.Errorf("failed to write %s: %w", fullPath, err) }