mirror of
https://github.com/fiatjaf/nak.git
synced 2026-01-31 06:28:52 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c658c38f1 | ||
|
|
2a5ce3b249 | ||
|
|
c0b85af734 | ||
|
|
cb2247c9da | ||
|
|
686d960f62 | ||
|
|
af04838153 | ||
|
|
c6da13649d | ||
|
|
acd6227dd0 | ||
|
|
00fbda9af7 | ||
|
|
e838de9b72 | ||
|
|
6dfbed4413 | ||
|
|
0e283368ed | ||
|
|
38775e0d93 | ||
|
|
fabcad3f61 | ||
|
|
69e4895e48 |
5
.github/workflows/release-cli.yml
vendored
5
.github/workflows/release-cli.yml
vendored
@@ -47,6 +47,7 @@ jobs:
|
|||||||
md5sum: false
|
md5sum: false
|
||||||
sha256sum: false
|
sha256sum: false
|
||||||
compress_assets: false
|
compress_assets: false
|
||||||
|
|
||||||
smoke-test-linux-amd64:
|
smoke-test-linux-amd64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs:
|
||||||
@@ -102,7 +103,7 @@ jobs:
|
|||||||
# test NIP-49 key encryption/decryption
|
# test NIP-49 key encryption/decryption
|
||||||
echo "testing NIP-49 key encryption/decryption..."
|
echo "testing NIP-49 key encryption/decryption..."
|
||||||
ENCRYPTED_KEY=$(./nak key encrypt $SECRET_KEY "testpassword")
|
ENCRYPTED_KEY=$(./nak key encrypt $SECRET_KEY "testpassword")
|
||||||
echo "encrypted key: ${ENCRYPTED_KEY:0:20}..."
|
echo "encrypted key: ${ENCRYPTED_KEY: 0:20}..."
|
||||||
DECRYPTED_KEY=$(./nak key decrypt $ENCRYPTED_KEY "testpassword")
|
DECRYPTED_KEY=$(./nak key decrypt $ENCRYPTED_KEY "testpassword")
|
||||||
if [ "$DECRYPTED_KEY" != "$SECRET_KEY" ]; then
|
if [ "$DECRYPTED_KEY" != "$SECRET_KEY" ]; then
|
||||||
echo "nip-49 encryption/decryption test failed!"
|
echo "nip-49 encryption/decryption test failed!"
|
||||||
@@ -116,7 +117,7 @@ jobs:
|
|||||||
# test relay operations (with a public relay)
|
# test relay operations (with a public relay)
|
||||||
echo "testing publishing..."
|
echo "testing publishing..."
|
||||||
# publish a simple event to a public relay
|
# publish a simple event to a public relay
|
||||||
EVENT_JSON=$(./nak event --sec $SECRET_KEY -c "test from nak smoke test" nos.lol)
|
EVENT_JSON=$(./nak event --sec $SECRET_KEY -c "test from nak smoke test" nos.lol < /dev/null)
|
||||||
EVENT_ID=$(echo $EVENT_JSON | jq -r .id)
|
EVENT_ID=$(echo $EVENT_JSON | jq -r .id)
|
||||||
echo "published event ID: $EVENT_ID"
|
echo "published event ID: $EVENT_ID"
|
||||||
|
|
||||||
|
|||||||
52
blossom.go
52
blossom.go
@@ -229,12 +229,56 @@ if any of the files are not found the command will fail, otherwise it will succe
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "mirror",
|
Name: "mirror",
|
||||||
Usage: "",
|
Usage: "mirrors a from a server to another",
|
||||||
Description: ``,
|
Description: `examples:
|
||||||
|
mirroring a single blob:
|
||||||
|
nak blossom mirror https://nostr.download/5672be22e6da91c12b929a0f46b9e74de8b5366b9b19a645ff949c24052f9ad4 -s blossom.band
|
||||||
|
|
||||||
|
mirroring all blobs from a certain pubkey from one server to the other:
|
||||||
|
nak blossom list 78ce6faa72264387284e647ba6938995735ec8c7d5c5a65737e55130f026307d -s nostr.download | nak blossom mirror -s blossom.band`,
|
||||||
DisableSliceFlagSeparator: true,
|
DisableSliceFlagSeparator: true,
|
||||||
ArgsUsage: "",
|
|
||||||
Action: func(ctx context.Context, c *cli.Command) error {
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
|
client, err := getBlossomClient(ctx, c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var bd blossom.BlobDescriptor
|
||||||
|
if input := c.Args().First(); input != "" {
|
||||||
|
blobURL := input
|
||||||
|
if err := json.Unmarshal([]byte(input), &bd); err == nil {
|
||||||
|
blobURL = bd.URL
|
||||||
|
}
|
||||||
|
bd, err := client.MirrorBlob(ctx, blobURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
out, _ := json.Marshal(bd)
|
||||||
|
stdout(out)
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
for input := range getJsonsOrBlank() {
|
||||||
|
if input == "{}" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
blobURL := input
|
||||||
|
if err := json.Unmarshal([]byte(input), &bd); err == nil {
|
||||||
|
blobURL = bd.URL
|
||||||
|
}
|
||||||
|
bd, err := client.MirrorBlob(ctx, blobURL)
|
||||||
|
if err != nil {
|
||||||
|
ctx = lineProcessingError(ctx, "failed to mirror '%s': %w", blobURL, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out, _ := json.Marshal(bd)
|
||||||
|
stdout(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
exitIfLineProcessingError(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -329,6 +329,10 @@ var bunker = &cli.Command{
|
|||||||
|
|
||||||
// asking user for authorization
|
// asking user for authorization
|
||||||
signer.AuthorizeRequest = func(harmless bool, from nostr.PubKey, secret string) bool {
|
signer.AuthorizeRequest = func(harmless bool, from nostr.PubKey, secret string) bool {
|
||||||
|
if slices.Contains(config.AuthorizedKeys, from) || slices.Contains(authorizedSecrets, secret) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
if secret == newSecret {
|
if secret == newSecret {
|
||||||
// store this key
|
// store this key
|
||||||
config.AuthorizedKeys = appendUnique(config.AuthorizedKeys, from)
|
config.AuthorizedKeys = appendUnique(config.AuthorizedKeys, from)
|
||||||
@@ -343,9 +347,11 @@ var bunker = &cli.Command{
|
|||||||
if persist != nil {
|
if persist != nil {
|
||||||
persist()
|
persist()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return slices.Contains(config.AuthorizedKeys, from) || slices.Contains(authorizedSecrets, secret)
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for ie := range events {
|
for ie := range events {
|
||||||
|
|||||||
55
encode.go
55
encode.go
@@ -25,13 +25,6 @@ var encode = &cli.Command{
|
|||||||
"relays":["wss://nada.zero"],
|
"relays":["wss://nada.zero"],
|
||||||
"author":"ebb6ff85430705651b311ed51328767078fd790b14f02d22efba68d5513376bc"
|
"author":"ebb6ff85430705651b311ed51328767078fd790b14f02d22efba68d5513376bc"
|
||||||
} | nak encode`,
|
} | nak encode`,
|
||||||
Flags: []cli.Flag{
|
|
||||||
&cli.StringSliceFlag{
|
|
||||||
Name: "relay",
|
|
||||||
Aliases: []string{"r"},
|
|
||||||
Usage: "attach relay hints to naddr code",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
DisableSliceFlagSeparator: true,
|
DisableSliceFlagSeparator: true,
|
||||||
Action: func(ctx context.Context, c *cli.Command) error {
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
if c.Args().Len() != 0 {
|
if c.Args().Len() != 0 {
|
||||||
@@ -126,7 +119,12 @@ var encode = &cli.Command{
|
|||||||
&cli.StringSliceFlag{
|
&cli.StringSliceFlag{
|
||||||
Name: "relay",
|
Name: "relay",
|
||||||
Aliases: []string{"r"},
|
Aliases: []string{"r"},
|
||||||
Usage: "attach relay hints to nprofile code",
|
Usage: "attach relay hints to the code",
|
||||||
|
},
|
||||||
|
&BoolIntFlag{
|
||||||
|
Name: "outbox",
|
||||||
|
Usage: "automatically appends outbox relays to the code",
|
||||||
|
Value: 3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DisableSliceFlagSeparator: true,
|
DisableSliceFlagSeparator: true,
|
||||||
@@ -139,6 +137,13 @@ var encode = &cli.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
relays := c.StringSlice("relay")
|
relays := c.StringSlice("relay")
|
||||||
|
|
||||||
|
if getBoolInt(c, "outbox") > 0 {
|
||||||
|
for _, r := range sys.FetchOutboxRelays(ctx, pk, int(getBoolInt(c, "outbox"))) {
|
||||||
|
relays = appendUnique(relays, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := normalizeAndValidateRelayURLs(relays); err != nil {
|
if err := normalizeAndValidateRelayURLs(relays); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -159,6 +164,16 @@ var encode = &cli.Command{
|
|||||||
Aliases: []string{"a"},
|
Aliases: []string{"a"},
|
||||||
Usage: "attach an author pubkey as a hint to the nevent code",
|
Usage: "attach an author pubkey as a hint to the nevent code",
|
||||||
},
|
},
|
||||||
|
&cli.StringSliceFlag{
|
||||||
|
Name: "relay",
|
||||||
|
Aliases: []string{"r"},
|
||||||
|
Usage: "attach relay hints to the code",
|
||||||
|
},
|
||||||
|
&BoolIntFlag{
|
||||||
|
Name: "outbox",
|
||||||
|
Usage: "automatically appends outbox relays to the code",
|
||||||
|
Value: 3,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
DisableSliceFlagSeparator: true,
|
DisableSliceFlagSeparator: true,
|
||||||
Action: func(ctx context.Context, c *cli.Command) error {
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
@@ -171,6 +186,13 @@ var encode = &cli.Command{
|
|||||||
|
|
||||||
author := getPubKey(c, "author")
|
author := getPubKey(c, "author")
|
||||||
relays := c.StringSlice("relay")
|
relays := c.StringSlice("relay")
|
||||||
|
|
||||||
|
if getBoolInt(c, "outbox") > 0 && author != nostr.ZeroPK {
|
||||||
|
for _, r := range sys.FetchOutboxRelays(ctx, author, int(getBoolInt(c, "outbox"))) {
|
||||||
|
relays = appendUnique(relays, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := normalizeAndValidateRelayURLs(relays); err != nil {
|
if err := normalizeAndValidateRelayURLs(relays); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -204,6 +226,16 @@ var encode = &cli.Command{
|
|||||||
Usage: "kind of referred replaceable event",
|
Usage: "kind of referred replaceable event",
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
|
&cli.StringSliceFlag{
|
||||||
|
Name: "relay",
|
||||||
|
Aliases: []string{"r"},
|
||||||
|
Usage: "attach relay hints to the code",
|
||||||
|
},
|
||||||
|
&BoolIntFlag{
|
||||||
|
Name: "outbox",
|
||||||
|
Usage: "automatically appends outbox relays to the code",
|
||||||
|
Value: 3,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
DisableSliceFlagSeparator: true,
|
DisableSliceFlagSeparator: true,
|
||||||
Action: func(ctx context.Context, c *cli.Command) error {
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
@@ -224,6 +256,13 @@ var encode = &cli.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
relays := c.StringSlice("relay")
|
relays := c.StringSlice("relay")
|
||||||
|
|
||||||
|
if getBoolInt(c, "outbox") > 0 {
|
||||||
|
for _, r := range sys.FetchOutboxRelays(ctx, pubkey, int(getBoolInt(c, "outbox"))) {
|
||||||
|
relays = appendUnique(relays, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := normalizeAndValidateRelayURLs(relays); err != nil {
|
if err := normalizeAndValidateRelayURLs(relays); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
3
event.go
3
event.go
@@ -145,7 +145,7 @@ example:
|
|||||||
if relayUrls := c.Args().Slice(); len(relayUrls) > 0 {
|
if relayUrls := c.Args().Slice(); len(relayUrls) > 0 {
|
||||||
relays = connectToAllRelays(ctx, c, relayUrls, nil,
|
relays = connectToAllRelays(ctx, c, relayUrls, nil,
|
||||||
nostr.PoolOptions{
|
nostr.PoolOptions{
|
||||||
AuthHandler: func(ctx context.Context, authEvent *nostr.Event) error {
|
AuthRequiredHandler: func(ctx context.Context, authEvent *nostr.Event) error {
|
||||||
return authSigner(ctx, c, func(s string, args ...any) {}, authEvent)
|
return authSigner(ctx, c, func(s string, args ...any) {}, authEvent)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -155,6 +155,7 @@ example:
|
|||||||
os.Exit(3)
|
os.Exit(3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kr, sec, err := gatherKeyerFromArguments(ctx, c)
|
kr, sec, err := gatherKeyerFromArguments(ctx, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
56
flags.go
56
flags.go
@@ -11,6 +11,62 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
BoolIntFlag = cli.FlagBase[int, struct{}, boolIntValue]
|
||||||
|
)
|
||||||
|
|
||||||
|
type boolIntValue struct {
|
||||||
|
int int
|
||||||
|
defaultWhenSet int
|
||||||
|
hasDefault bool
|
||||||
|
hasBeenSet bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ cli.ValueCreator[int, struct{}] = boolIntValue{}
|
||||||
|
|
||||||
|
func (t boolIntValue) Create(val int, p *int, c struct{}) cli.Value {
|
||||||
|
*p = val
|
||||||
|
|
||||||
|
return &boolIntValue{
|
||||||
|
defaultWhenSet: val,
|
||||||
|
hasDefault: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t boolIntValue) IsBoolFlag() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t boolIntValue) ToString(b int) string { return "<<>>" }
|
||||||
|
|
||||||
|
func (t *boolIntValue) Set(value string) error {
|
||||||
|
t.hasBeenSet = true
|
||||||
|
if value == "true" {
|
||||||
|
if t.hasDefault {
|
||||||
|
t.int = t.defaultWhenSet
|
||||||
|
} else {
|
||||||
|
t.int = 1
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
t.int, err = strconv.Atoi(value)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *boolIntValue) String() string { return fmt.Sprintf("%#v", t.int) }
|
||||||
|
func (t *boolIntValue) Value() int { return t.int }
|
||||||
|
func (t *boolIntValue) Get() any { return t.int }
|
||||||
|
|
||||||
|
func getBoolInt(cmd *cli.Command, name string) int {
|
||||||
|
return cmd.Value(name).(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
type NaturalTimeFlag = cli.FlagBase[nostr.Timestamp, struct{}, naturalTimeValue]
|
type NaturalTimeFlag = cli.FlagBase[nostr.Timestamp, struct{}, naturalTimeValue]
|
||||||
|
|
||||||
type naturalTimeValue struct {
|
type naturalTimeValue struct {
|
||||||
|
|||||||
2
fs.go
2
fs.go
@@ -1,4 +1,4 @@
|
|||||||
//go:build !windows && !openbsd
|
//go:build !windows && !openbsd && !cgofuse
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
118
fs_cgo.go
Normal file
118
fs_cgo.go
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
//go:build cgofuse && !windows && !openbsd
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"fiatjaf.com/nostr"
|
||||||
|
"fiatjaf.com/nostr/keyer"
|
||||||
|
"github.com/fatih/color"
|
||||||
|
nostrfs "github.com/fiatjaf/nak/nostrfs_cgo"
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
|
"github.com/winfsp/cgofuse/fuse"
|
||||||
|
)
|
||||||
|
|
||||||
|
var fsCmd = &cli.Command{
|
||||||
|
Name: "fs",
|
||||||
|
Usage: "mount a FUSE filesystem that exposes Nostr events as files.",
|
||||||
|
Description: `(experimental)`,
|
||||||
|
ArgsUsage: "<mountpoint>",
|
||||||
|
Flags: append(defaultKeyFlags,
|
||||||
|
&PubKeyFlag{
|
||||||
|
Name: "pubkey",
|
||||||
|
Usage: "public key from where to to prepopulate directories",
|
||||||
|
},
|
||||||
|
&cli.DurationFlag{
|
||||||
|
Name: "auto-publish-notes",
|
||||||
|
Usage: "delay after which new notes will be auto-published, set to -1 to not publish.",
|
||||||
|
Value: time.Second * 30,
|
||||||
|
},
|
||||||
|
&cli.DurationFlag{
|
||||||
|
Name: "auto-publish-articles",
|
||||||
|
Usage: "delay after which edited articles will be auto-published.",
|
||||||
|
Value: time.Hour * 24 * 365 * 2,
|
||||||
|
DefaultText: "basically infinite",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
DisableSliceFlagSeparator: true,
|
||||||
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
|
mountpoint := c.Args().First()
|
||||||
|
if mountpoint == "" {
|
||||||
|
return fmt.Errorf("must be called with a directory path to serve as the mountpoint as an argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
var kr nostr.User
|
||||||
|
if signer, _, err := gatherKeyerFromArguments(ctx, c); err == nil {
|
||||||
|
kr = signer
|
||||||
|
} else {
|
||||||
|
kr = keyer.NewReadOnlyUser(getPubKey(c, "pubkey"))
|
||||||
|
}
|
||||||
|
|
||||||
|
apnt := c.Duration("auto-publish-notes")
|
||||||
|
if apnt < 0 {
|
||||||
|
apnt = time.Hour * 24 * 365 * 3
|
||||||
|
}
|
||||||
|
apat := c.Duration("auto-publish-articles")
|
||||||
|
if apat < 0 {
|
||||||
|
apat = time.Hour * 24 * 365 * 3
|
||||||
|
}
|
||||||
|
|
||||||
|
root := nostrfs.NewNostrRoot(
|
||||||
|
context.WithValue(
|
||||||
|
context.WithValue(
|
||||||
|
ctx,
|
||||||
|
"log", log,
|
||||||
|
),
|
||||||
|
"logverbose", logverbose,
|
||||||
|
),
|
||||||
|
sys,
|
||||||
|
kr,
|
||||||
|
mountpoint,
|
||||||
|
nostrfs.Options{
|
||||||
|
AutoPublishNotesTimeout: apnt,
|
||||||
|
AutoPublishArticlesTimeout: apat,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// create the server
|
||||||
|
log("- mounting at %s... ", color.HiCyanString(mountpoint))
|
||||||
|
|
||||||
|
// create cgofuse host
|
||||||
|
host := fuse.NewFileSystemHost(root)
|
||||||
|
host.SetCapReaddirPlus(true)
|
||||||
|
host.SetUseIno(true)
|
||||||
|
|
||||||
|
// mount the filesystem
|
||||||
|
mountArgs := []string{"-s", mountpoint}
|
||||||
|
if isVerbose {
|
||||||
|
mountArgs = append([]string{"-d"}, mountArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
host.Mount("", mountArgs)
|
||||||
|
}()
|
||||||
|
|
||||||
|
log("ok.\n")
|
||||||
|
|
||||||
|
// setup signal handling for clean unmount
|
||||||
|
ch := make(chan os.Signal, 1)
|
||||||
|
chErr := make(chan error)
|
||||||
|
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
go func() {
|
||||||
|
<-ch
|
||||||
|
log("- unmounting... ")
|
||||||
|
// cgofuse doesn't have explicit unmount, it unmounts on process exit
|
||||||
|
log("ok\n")
|
||||||
|
chErr <- nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
// wait for signals
|
||||||
|
return <-chErr
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build windows || openbsd
|
//go:build openbsd
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -15,6 +15,6 @@ var fsCmd = &cli.Command{
|
|||||||
Description: `doesn't work on Windows and OpenBSD.`,
|
Description: `doesn't work on Windows and OpenBSD.`,
|
||||||
DisableSliceFlagSeparator: true,
|
DisableSliceFlagSeparator: true,
|
||||||
Action: func(ctx context.Context, c *cli.Command) error {
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
return fmt.Errorf("this doesn't work on Windows and OpenBSD.")
|
return fmt.Errorf("this doesn't work on OpenBSD.")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
140
fs_windows.go
Normal file
140
fs_windows.go
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"fiatjaf.com/nostr"
|
||||||
|
"fiatjaf.com/nostr/keyer"
|
||||||
|
"github.com/fatih/color"
|
||||||
|
nostrfs "github.com/fiatjaf/nak/nostrfs_cgo"
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
|
"github.com/winfsp/cgofuse/fuse"
|
||||||
|
)
|
||||||
|
|
||||||
|
var fsCmd = &cli.Command{
|
||||||
|
Name: "fs",
|
||||||
|
Usage: "mount a FUSE filesystem that exposes Nostr events as files.",
|
||||||
|
Description: `(experimental)`,
|
||||||
|
ArgsUsage: "<mountpoint>",
|
||||||
|
Flags: append(defaultKeyFlags,
|
||||||
|
&PubKeyFlag{
|
||||||
|
Name: "pubkey",
|
||||||
|
Usage: "public key from where to to prepopulate directories",
|
||||||
|
},
|
||||||
|
&cli.DurationFlag{
|
||||||
|
Name: "auto-publish-notes",
|
||||||
|
Usage: "delay after which new notes will be auto-published, set to -1 to not publish.",
|
||||||
|
Value: time.Second * 30,
|
||||||
|
},
|
||||||
|
&cli.DurationFlag{
|
||||||
|
Name: "auto-publish-articles",
|
||||||
|
Usage: "delay after which edited articles will be auto-published.",
|
||||||
|
Value: time.Hour * 24 * 365 * 2,
|
||||||
|
DefaultText: "basically infinite",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
DisableSliceFlagSeparator: true,
|
||||||
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
|
mountpoint := c.Args().First()
|
||||||
|
if mountpoint == "" {
|
||||||
|
return fmt.Errorf("must be called with a directory path to serve as the mountpoint as an argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
var kr nostr.User
|
||||||
|
if signer, _, err := gatherKeyerFromArguments(ctx, c); err == nil {
|
||||||
|
kr = signer
|
||||||
|
} else {
|
||||||
|
kr = keyer.NewReadOnlyUser(getPubKey(c, "pubkey"))
|
||||||
|
}
|
||||||
|
|
||||||
|
apnt := c.Duration("auto-publish-notes")
|
||||||
|
if apnt < 0 {
|
||||||
|
apnt = time.Hour * 24 * 365 * 3
|
||||||
|
}
|
||||||
|
apat := c.Duration("auto-publish-articles")
|
||||||
|
if apat < 0 {
|
||||||
|
apat = time.Hour * 24 * 365 * 3
|
||||||
|
}
|
||||||
|
|
||||||
|
root := nostrfs.NewNostrRoot(
|
||||||
|
context.WithValue(
|
||||||
|
context.WithValue(
|
||||||
|
ctx,
|
||||||
|
"log", log,
|
||||||
|
),
|
||||||
|
"logverbose", logverbose,
|
||||||
|
),
|
||||||
|
sys,
|
||||||
|
kr,
|
||||||
|
mountpoint,
|
||||||
|
nostrfs.Options{
|
||||||
|
AutoPublishNotesTimeout: apnt,
|
||||||
|
AutoPublishArticlesTimeout: apat,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// create the server
|
||||||
|
log("- mounting at %s... ", color.HiCyanString(mountpoint))
|
||||||
|
|
||||||
|
// create cgofuse host
|
||||||
|
host := fuse.NewFileSystemHost(root)
|
||||||
|
host.SetCapReaddirPlus(true)
|
||||||
|
host.SetUseIno(true)
|
||||||
|
|
||||||
|
// mount the filesystem - Windows/WinFsp version
|
||||||
|
// based on rclone cmount implementation
|
||||||
|
mountArgs := []string{
|
||||||
|
"-o", "uid=-1",
|
||||||
|
"-o", "gid=-1",
|
||||||
|
"--FileSystemName=nak",
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if mountpoint is a drive letter or directory
|
||||||
|
isDriveLetter := len(mountpoint) == 2 && mountpoint[1] == ':'
|
||||||
|
|
||||||
|
if !isDriveLetter {
|
||||||
|
// winFsp primarily supports drive letters on Windows
|
||||||
|
// directory mounting may not work reliably
|
||||||
|
log("WARNING: directory mounting may not work on Windows (WinFsp limitation)\n")
|
||||||
|
log(" consider using a drive letter instead (e.g., 'nak fs Z:')\n")
|
||||||
|
|
||||||
|
// for directory mounts, follow rclone's approach:
|
||||||
|
// 1. check that mountpoint doesn't already exist
|
||||||
|
if _, err := os.Stat(mountpoint); err == nil {
|
||||||
|
return fmt.Errorf("mountpoint path already exists: %s (must not exist before mounting)", mountpoint)
|
||||||
|
} else if !os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("failed to check mountpoint: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. check that parent directory exists
|
||||||
|
parent := filepath.Join(mountpoint, "..")
|
||||||
|
if _, err := os.Stat(parent); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("parent of mountpoint directory does not exist: %s", parent)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("failed to check parent directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. use network mode for directory mounts
|
||||||
|
mountArgs = append(mountArgs, "--VolumePrefix=\\nak\\"+filepath.Base(mountpoint))
|
||||||
|
}
|
||||||
|
|
||||||
|
if isVerbose {
|
||||||
|
mountArgs = append(mountArgs, "-o", "debug")
|
||||||
|
}
|
||||||
|
mountArgs = append(mountArgs, mountpoint)
|
||||||
|
|
||||||
|
log("ok.\n")
|
||||||
|
|
||||||
|
if !host.Mount("", mountArgs) {
|
||||||
|
return fmt.Errorf("failed to mount filesystem")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
11
go.mod
11
go.mod
@@ -3,8 +3,7 @@ module github.com/fiatjaf/nak
|
|||||||
go 1.25
|
go 1.25
|
||||||
|
|
||||||
require (
|
require (
|
||||||
fiatjaf.com/lib v0.3.1
|
fiatjaf.com/nostr v0.0.0-20260118173002-57d595a5b4c7
|
||||||
fiatjaf.com/nostr v0.0.0-20251230181913-e52ffa631bd6
|
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||||
github.com/bep/debounce v1.2.1
|
github.com/bep/debounce v1.2.1
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.3.6
|
github.com/btcsuite/btcd/btcec/v2 v2.3.6
|
||||||
@@ -12,7 +11,6 @@ require (
|
|||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0
|
||||||
github.com/fatih/color v1.16.0
|
github.com/fatih/color v1.16.0
|
||||||
github.com/hanwen/go-fuse/v2 v2.7.2
|
|
||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/liamg/magic v0.0.1
|
github.com/liamg/magic v0.0.1
|
||||||
github.com/mailru/easyjson v0.9.1
|
github.com/mailru/easyjson v0.9.1
|
||||||
@@ -24,11 +22,17 @@ require (
|
|||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1
|
github.com/puzpuzpuz/xsync/v3 v3.5.1
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/urfave/cli/v3 v3.0.0-beta1
|
github.com/urfave/cli/v3 v3.0.0-beta1
|
||||||
|
github.com/winfsp/cgofuse v1.6.0
|
||||||
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6
|
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6
|
||||||
golang.org/x/sync v0.18.0
|
golang.org/x/sync v0.18.0
|
||||||
golang.org/x/term v0.32.0
|
golang.org/x/term v0.32.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
fiatjaf.com/lib v0.3.2
|
||||||
|
github.com/hanwen/go-fuse/v2 v2.9.0
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/FastFilter/xorfilter v0.2.1 // indirect
|
github.com/FastFilter/xorfilter v0.2.1 // indirect
|
||||||
github.com/ImVexed/fasturl v0.0.0-20230304231329-4e41488060f3 // indirect
|
github.com/ImVexed/fasturl v0.0.0-20230304231329-4e41488060f3 // indirect
|
||||||
@@ -69,7 +73,6 @@ require (
|
|||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
github.com/klauspost/compress v1.18.0 // indirect
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
github.com/magefile/mage v1.14.0 // indirect
|
github.com/magefile/mage v1.14.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
|
|||||||
16
go.sum
16
go.sum
@@ -1,7 +1,9 @@
|
|||||||
fiatjaf.com/lib v0.3.1 h1:/oFQwNtFRfV+ukmOCxfBEAuayoLwXp4wu2/fz5iHpwA=
|
fiatjaf.com/lib v0.3.2 h1:RBS41z70d8Rp8e2nemQsbPY1NLLnEGShiY2c+Bom3+Q=
|
||||||
fiatjaf.com/lib v0.3.1/go.mod h1:Ycqq3+mJ9jAWu7XjbQI1cVr+OFgnHn79dQR5oTII47g=
|
fiatjaf.com/lib v0.3.2/go.mod h1:UlHaZvPHj25PtKLh9GjZkUHRmQ2xZ8Jkoa4VRaLeeQ8=
|
||||||
fiatjaf.com/nostr v0.0.0-20251230181913-e52ffa631bd6 h1:yH+cU9ZNgUdMCRa5eS3pmqTPP/QdZtSmQAIrN/U5nEc=
|
fiatjaf.com/nostr v0.0.0-20251230181913-e52ffa631bd6 h1:yH+cU9ZNgUdMCRa5eS3pmqTPP/QdZtSmQAIrN/U5nEc=
|
||||||
fiatjaf.com/nostr v0.0.0-20251230181913-e52ffa631bd6/go.mod h1:ue7yw0zHfZj23Ml2kVSdBx0ENEaZiuvGxs/8VEN93FU=
|
fiatjaf.com/nostr v0.0.0-20251230181913-e52ffa631bd6/go.mod h1:ue7yw0zHfZj23Ml2kVSdBx0ENEaZiuvGxs/8VEN93FU=
|
||||||
|
fiatjaf.com/nostr v0.0.0-20260118173002-57d595a5b4c7 h1:CkMr8zFLfoOO59+oNlBXXrga00lTKyl2A4fUXAJQ7fY=
|
||||||
|
fiatjaf.com/nostr v0.0.0-20260118173002-57d595a5b4c7/go.mod h1:ue7yw0zHfZj23Ml2kVSdBx0ENEaZiuvGxs/8VEN93FU=
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
|
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/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
|
||||||
github.com/FastFilter/xorfilter v0.2.1 h1:lbdeLG9BdpquK64ZsleBS8B4xO/QW1IM0gMzF7KaBKc=
|
github.com/FastFilter/xorfilter v0.2.1 h1:lbdeLG9BdpquK64ZsleBS8B4xO/QW1IM0gMzF7KaBKc=
|
||||||
@@ -144,8 +146,8 @@ github.com/hablullah/go-hijri v1.0.2 h1:drT/MZpSZJQXo7jftf5fthArShcaMtsal0Zf/dnm
|
|||||||
github.com/hablullah/go-hijri v1.0.2/go.mod h1:OS5qyYLDjORXzK4O1adFw9Q5WfhOcMdAKglDkcTxgWQ=
|
github.com/hablullah/go-hijri v1.0.2/go.mod h1:OS5qyYLDjORXzK4O1adFw9Q5WfhOcMdAKglDkcTxgWQ=
|
||||||
github.com/hablullah/go-juliandays v1.0.0 h1:A8YM7wIj16SzlKT0SRJc9CD29iiaUzpBLzh5hr0/5p0=
|
github.com/hablullah/go-juliandays v1.0.0 h1:A8YM7wIj16SzlKT0SRJc9CD29iiaUzpBLzh5hr0/5p0=
|
||||||
github.com/hablullah/go-juliandays v1.0.0/go.mod h1:0JOYq4oFOuDja+oospuc61YoX+uNEn7Z6uHYTbBzdGc=
|
github.com/hablullah/go-juliandays v1.0.0/go.mod h1:0JOYq4oFOuDja+oospuc61YoX+uNEn7Z6uHYTbBzdGc=
|
||||||
github.com/hanwen/go-fuse/v2 v2.7.2 h1:SbJP1sUP+n1UF8NXBA14BuojmTez+mDgOk0bC057HQw=
|
github.com/hanwen/go-fuse/v2 v2.9.0 h1:0AOGUkHtbOVeyGLr0tXupiid1Vg7QB7M6YUcdmVdC58=
|
||||||
github.com/hanwen/go-fuse/v2 v2.7.2/go.mod h1:ugNaD/iv5JYyS1Rcvi57Wz7/vrLQJo10mmketmoef48=
|
github.com/hanwen/go-fuse/v2 v2.9.0/go.mod h1:yE6D2PqWwm3CbYRxFXV9xUd8Md5d6NG0WBs5spCswmI=
|
||||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||||
@@ -200,8 +202,8 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1f
|
|||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
||||||
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
|
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
|
||||||
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
|
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
@@ -269,6 +271,8 @@ github.com/wasilibs/go-re2 v1.3.0 h1:LFhBNzoStM3wMie6rN2slD1cuYH2CGiHpvNL3UtcsMw
|
|||||||
github.com/wasilibs/go-re2 v1.3.0/go.mod h1:AafrCXVvGRJJOImMajgJ2M7rVmWyisVK7sFshbxnVrg=
|
github.com/wasilibs/go-re2 v1.3.0/go.mod h1:AafrCXVvGRJJOImMajgJ2M7rVmWyisVK7sFshbxnVrg=
|
||||||
github.com/wasilibs/nottinygc v0.4.0 h1:h1TJMihMC4neN6Zq+WKpLxgd9xCFMw7O9ETLwY2exJQ=
|
github.com/wasilibs/nottinygc v0.4.0 h1:h1TJMihMC4neN6Zq+WKpLxgd9xCFMw7O9ETLwY2exJQ=
|
||||||
github.com/wasilibs/nottinygc v0.4.0/go.mod h1:oDcIotskuYNMpqMF23l7Z8uzD4TC0WXHK8jetlB3HIo=
|
github.com/wasilibs/nottinygc v0.4.0/go.mod h1:oDcIotskuYNMpqMF23l7Z8uzD4TC0WXHK8jetlB3HIo=
|
||||||
|
github.com/winfsp/cgofuse v1.6.0 h1:re3W+HTd0hj4fISPBqfsrwyvPFpzqhDu8doJ9nOPDB0=
|
||||||
|
github.com/winfsp/cgofuse v1.6.0/go.mod h1:uxjoF2jEYT3+x+vC2KJddEGdk/LU8pRowXmyVMHSV5I=
|
||||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||||
|
|||||||
12
helpers.go
12
helpers.go
@@ -46,8 +46,14 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func isPiped() bool {
|
func isPiped() bool {
|
||||||
stat, _ := os.Stdin.Stat()
|
stat, err := os.Stdin.Stat()
|
||||||
return stat.Mode()&os.ModeCharDevice == 0
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := stat.Mode()
|
||||||
|
is := mode&os.ModeCharDevice == 0
|
||||||
|
return is
|
||||||
}
|
}
|
||||||
|
|
||||||
func getJsonsOrBlank() iter.Seq[string] {
|
func getJsonsOrBlank() iter.Seq[string] {
|
||||||
@@ -76,7 +82,7 @@ func getJsonsOrBlank() iter.Seq[string] {
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
if !hasStdin && !isPiped() {
|
if !hasStdin {
|
||||||
yield("{}")
|
yield("{}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
key.go
13
key.go
@@ -279,12 +279,13 @@ func getSecretKeysFromStdinLinesOrSlice(ctx context.Context, _ *cli.Command, key
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sk = data.(nostr.SecretKey)
|
sk = data.(nostr.SecretKey)
|
||||||
}
|
} else {
|
||||||
|
var err error
|
||||||
sk, err := nostr.SecretKeyFromHex(sec)
|
sk, err = nostr.SecretKeyFromHex(sec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx = lineProcessingError(ctx, "invalid hex key: %s", err)
|
ctx = lineProcessingError(ctx, "invalid hex key: %s", err)
|
||||||
continue
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ch <- sk
|
ch <- sk
|
||||||
|
|||||||
1177
nostrfs_cgo/root.go
Normal file
1177
nostrfs_cgo/root.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -153,7 +153,7 @@ example:
|
|||||||
relayUrls = nostr.AppendUnique(relayUrls, c.Args().Slice()...)
|
relayUrls = nostr.AppendUnique(relayUrls, c.Args().Slice()...)
|
||||||
relays := connectToAllRelays(ctx, c, relayUrls, nil,
|
relays := connectToAllRelays(ctx, c, relayUrls, nil,
|
||||||
nostr.PoolOptions{
|
nostr.PoolOptions{
|
||||||
AuthHandler: func(ctx context.Context, authEvent *nostr.Event) error {
|
AuthRequiredHandler: func(ctx context.Context, authEvent *nostr.Event) error {
|
||||||
return authSigner(ctx, c, func(s string, args ...any) {}, authEvent)
|
return authSigner(ctx, c, func(s string, args ...any) {}, authEvent)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
2
req.go
2
req.go
@@ -138,7 +138,7 @@ example:
|
|||||||
relayUrls,
|
relayUrls,
|
||||||
forcePreAuthSigner,
|
forcePreAuthSigner,
|
||||||
nostr.PoolOptions{
|
nostr.PoolOptions{
|
||||||
AuthHandler: func(ctx context.Context, authEvent *nostr.Event) error {
|
AuthRequiredHandler: func(ctx context.Context, authEvent *nostr.Event) error {
|
||||||
return authSigner(ctx, c, func(s string, args ...any) {
|
return authSigner(ctx, c, func(s string, args ...any) {
|
||||||
if strings.HasPrefix(s, "authenticating as") {
|
if strings.HasPrefix(s, "authenticating as") {
|
||||||
cleanUrl, _ := strings.CutPrefix(
|
cleanUrl, _ := strings.CutPrefix(
|
||||||
|
|||||||
Reference in New Issue
Block a user