package nostrfs import ( "context" "path/filepath" "syscall" "time" "fiatjaf.com/nostr" "fiatjaf.com/nostr/nip05" "fiatjaf.com/nostr/nip19" "fiatjaf.com/nostr/sdk" "github.com/hanwen/go-fuse/v2/fs" "github.com/hanwen/go-fuse/v2/fuse" ) type Options struct { AutoPublishNotesTimeout time.Duration AutoPublishArticlesTimeout time.Duration } type NostrRoot struct { fs.Inode ctx context.Context wd string sys *sdk.System rootPubKey nostr.PubKey signer nostr.Signer opts Options } var _ = (fs.NodeOnAdder)((*NostrRoot)(nil)) func NewNostrRoot(ctx context.Context, sys *sdk.System, user nostr.User, mountpoint string, o Options) *NostrRoot { pubkey, _ := user.GetPublicKey(ctx) abs, _ := filepath.Abs(mountpoint) var signer nostr.Signer if user != nil { signer, _ = user.(nostr.Signer) } return &NostrRoot{ ctx: ctx, sys: sys, rootPubKey: pubkey, signer: signer, wd: abs, opts: o, } } func (r *NostrRoot) OnAdd(_ context.Context) { if r.rootPubKey == nostr.ZeroPK { return } go func() { time.Sleep(time.Millisecond * 100) // add our contacts fl := r.sys.FetchFollowList(r.ctx, r.rootPubKey) for _, f := range fl.Items { pointer := nostr.ProfilePointer{PublicKey: f.Pubkey, Relays: []string{f.Relay}} r.AddChild( nip19.EncodeNpub(f.Pubkey), r.CreateNpubDir(r, pointer, nil), true, ) } // add ourselves npub := nip19.EncodeNpub(r.rootPubKey) if r.GetChild(npub) == nil { pointer := nostr.ProfilePointer{PublicKey: r.rootPubKey} r.AddChild( npub, r.CreateNpubDir(r, pointer, r.signer), true, ) } // add a link to ourselves r.AddChild("@me", r.NewPersistentInode( r.ctx, &fs.MemSymlink{Data: []byte(r.wd + "/" + npub)}, fs.StableAttr{Mode: syscall.S_IFLNK}, ), true) }() } func (r *NostrRoot) Lookup(_ context.Context, name string, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) { out.SetEntryTimeout(time.Minute * 5) child := r.GetChild(name) if child != nil { return child, fs.OK } if pp, err := nip05.QueryIdentifier(r.ctx, name); err == nil { return r.NewPersistentInode( r.ctx, &fs.MemSymlink{Data: []byte(r.wd + "/" + nip19.EncodePointer(*pp))}, fs.StableAttr{Mode: syscall.S_IFLNK}, ), fs.OK } pointer, err := nip19.ToPointer(name) if err != nil { return nil, syscall.ENOENT } switch p := pointer.(type) { case nostr.ProfilePointer: npubdir := r.CreateNpubDir(r, p, nil) return npubdir, fs.OK case nostr.EventPointer: eventdir, err := r.FetchAndCreateEventDir(r, p) if err != nil { return nil, syscall.ENOENT } return eventdir, fs.OK default: return nil, syscall.ENOENT } }