diff --git a/pkg/core/core.go b/pkg/core/core.go index 5ef055c..1eb0e85 100644 --- a/pkg/core/core.go +++ b/pkg/core/core.go @@ -38,11 +38,6 @@ func NewCore() *Core { panic(err) } - err = c.fs.MkdirAll(filesystem.DirName, 0o755) - if err != nil { - panic(err) - } - return &c } @@ -104,13 +99,11 @@ func (c *Core) resetCore() { // Loads, if exists, or creates new repository with the given name. func (c *Core) initCalendarRepo(name string) (*gogit.Repository, error) { - repoPath := c.fs.Join(filesystem.DirName, name) - - if err := c.fs.MkdirAll(repoPath, 0o755); err != nil { + if err := c.fs.MkdirAll(name, 0o755); err != nil { return nil, fmt.Errorf("create repo dir: %w", err) } - repoFS, err := c.fs.Chroot(repoPath) + repoFS, err := c.fs.Chroot(name) if err != nil { return nil, fmt.Errorf("chroot repo dir: %w", err) } diff --git a/pkg/core/core_calendars.go b/pkg/core/core_calendars.go index 4996108..32d57bc 100644 --- a/pkg/core/core_calendars.go +++ b/pkg/core/core_calendars.go @@ -10,7 +10,6 @@ import ( "strings" "github.com/git-calendar/core/pkg/encryption" - "github.com/git-calendar/core/pkg/filesystem" gogitutil "github.com/go-git/go-billy/v5/util" gogit "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" @@ -29,7 +28,7 @@ func (c *Core) CreateCalendar(name, password string) error { if len(password) != 0 { key = encryption.DeriveKey(password, []byte(name)) - keyFile, err := c.fs.Create(c.fs.Join(filesystem.DirName, fmt.Sprintf("%s.key", name))) + keyFile, err := c.fs.Create(fmt.Sprintf("%s.key", name)) if err != nil { return fmt.Errorf("failed to create key file: %w", err) } @@ -61,7 +60,7 @@ func (c *Core) LoadCalendars() error { c.resetCore() // load repositories - entries, err := c.fs.ReadDir(filesystem.DirName) + entries, err := c.fs.ReadDir(".") if err != nil { return fmt.Errorf("failed to list all directories in root: %w", err) } @@ -79,7 +78,7 @@ func (c *Core) LoadCalendars() error { } var key []byte = nil - keyFile, err := c.fs.Open(c.fs.Join(filesystem.DirName, fmt.Sprintf("%s.key", name))) + keyFile, err := c.fs.Open(fmt.Sprintf("%s.key", name)) if err == nil { key, err = io.ReadAll(keyFile) if err != nil { @@ -147,11 +146,10 @@ func (c *Core) CloneCalendar(repoUrl url.URL, password string) error { } // make sure that the repo dir is created - repoPath := c.fs.Join(filesystem.DirName, calendarName) - if err := c.fs.MkdirAll(repoPath, 0o755); err != nil { + if err := c.fs.MkdirAll(calendarName, 0o755); err != nil { return fmt.Errorf("create repo dir: %w", err) } - repoFS, err := c.fs.Chroot(repoPath) + repoFS, err := c.fs.Chroot(calendarName) if err != nil { return fmt.Errorf("chroot repo dir: %w", err) } @@ -186,7 +184,7 @@ func (c *Core) CloneCalendar(repoUrl url.URL, password string) error { if len(password) != 0 { key = encryption.DeriveKey(password, []byte(calendarName)) - keyFile, err := c.fs.Create(c.fs.Join(filesystem.DirName, fmt.Sprintf("%s.key", calendarName))) + keyFile, err := c.fs.Create(fmt.Sprintf("%s.key", calendarName)) if err != nil { return fmt.Errorf("failed to create key file: %w", err) } @@ -211,12 +209,12 @@ func (c *Core) RemoveCalendar(name string) error { delete(c.calendars, name) // remove dir from filesystem - if err := gogitutil.RemoveAll(c.fs, c.fs.Join(filesystem.DirName, name)); err != nil { + if err := gogitutil.RemoveAll(c.fs, name); err != nil { return fmt.Errorf("failed to remove repo directory: %w", err) } // try to remove encryption key - _ = c.fs.Remove(c.fs.Join(filesystem.DirName, fmt.Sprintf("%s.key", name))) + _ = c.fs.Remove(fmt.Sprintf("%s.key", name)) // TODO: This is the lazy way. // LoadCalendars does full erase and load again for events map and tree. It also deletes all the repos, and reloads them from disk. diff --git a/pkg/core/core_events.go b/pkg/core/core_events.go index 1a3b015..efc95c6 100644 --- a/pkg/core/core_events.go +++ b/pkg/core/core_events.go @@ -8,7 +8,6 @@ import ( "slices" "time" - "github.com/git-calendar/core/pkg/filesystem" gogit "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing/object" "github.com/google/uuid" @@ -381,7 +380,7 @@ func (c *Core) saveAndCommitEvent(event *Event, commitMsg string) error { } // ensure events directory exists - dirPath := c.fs.Join(filesystem.DirName, event.Calendar, EventsDirName) + dirPath := c.fs.Join(event.Calendar, EventsDirName) if err := c.fs.MkdirAll(dirPath, 0o755); err != nil { return fmt.Errorf("failed mkdir events: %w", err) } @@ -437,7 +436,7 @@ func (c *Core) deleteAndCommitEvent(eventId uuid.UUID, commitMsg string) error { filename := fmt.Sprintf("%s.json", eventId) // -------- remove from disk -------- - filePath := c.fs.Join(filesystem.DirName, event.Calendar, EventsDirName, filename) + filePath := c.fs.Join(event.Calendar, EventsDirName, filename) if err := c.fs.Remove(filePath); err != nil { // TODO maybe continue, to clean the git from this file return fmt.Errorf("failed to remove file from disk: %w", err) diff --git a/pkg/filesystem/fs_classic.go b/pkg/filesystem/classic.go similarity index 50% rename from pkg/filesystem/fs_classic.go rename to pkg/filesystem/classic.go index c4c7715..38a741a 100644 --- a/pkg/filesystem/fs_classic.go +++ b/pkg/filesystem/classic.go @@ -4,8 +4,10 @@ package filesystem import ( "os" + "path/filepath" "github.com/go-git/go-billy/v5" + "github.com/go-git/go-billy/v5/helper/chroot" "github.com/go-git/go-billy/v5/osfs" ) @@ -21,5 +23,16 @@ func GetFS() (billy.Filesystem, error) { return nil, err } - return osfs.New(home), nil + // ensure the directory exists on the real filesystem + rootPath := filepath.Join(home, DirName) + if err := os.MkdirAll(rootPath, 0o755); err != nil { + return nil, err + } + + // base filesystem rooted at home + base := osfs.New(home) + + // chroot into DirName + scoped := chroot.New(base, DirName) + return scoped, nil } diff --git a/pkg/filesystem/wasm.go b/pkg/filesystem/wasm.go new file mode 100644 index 0000000..c5245af --- /dev/null +++ b/pkg/filesystem/wasm.go @@ -0,0 +1,38 @@ +//go:build js && wasm + +package filesystem + +import ( + "errors" + "fmt" + "syscall/js" + + "github.com/git-calendar/core/pkg/opfs" + "github.com/go-git/go-billy/v5" +) + +const DirName = "git-calendar-data" + +func GetFS() (billy.Filesystem, error) { + // gets the handle from js window.opfsRootHandle + rootHandle := js.Global().Get("opfsRootHandle") + if rootHandle.IsUndefined() { + return nil, errors.New("opfsRootHandle not initialized") + } + + // create OPFS rooted at / + fs := opfs.New(rootHandle) + + // ensure directory exists + if err := fs.MkdirAll(DirName, 0o755); err != nil { + return nil, fmt.Errorf("failed to create dir: %w", err) + } + + // chroot into the directory + chrooted, err := fs.Chroot(DirName) + if err != nil { + return nil, fmt.Errorf("failed to chroot: %w", err) + } + + return chrooted, nil +} diff --git a/pkg/filesystem/fs_file_wasm.go b/pkg/opfs/file.go similarity index 98% rename from pkg/filesystem/fs_file_wasm.go rename to pkg/opfs/file.go index 9f28262..e35ec71 100644 --- a/pkg/filesystem/fs_file_wasm.go +++ b/pkg/opfs/file.go @@ -1,6 +1,6 @@ //go:build js && wasm -package filesystem +package opfs import ( "fmt" @@ -214,7 +214,7 @@ func (f *OPFSFile) openAccess() error { var err error // https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle/createSyncAccessHandle - f.inode.access, err = await(f.inode.handle.Call("createSyncAccessHandle")) // returns Promise + f.inode.access, err = Await(f.inode.handle.Call("createSyncAccessHandle")) // returns Promise return err } diff --git a/pkg/filesystem/fs_file_info_wasm.go b/pkg/opfs/file_info.go similarity index 98% rename from pkg/filesystem/fs_file_info_wasm.go rename to pkg/opfs/file_info.go index f864fcd..f4b5e9e 100644 --- a/pkg/filesystem/fs_file_info_wasm.go +++ b/pkg/opfs/file_info.go @@ -1,6 +1,6 @@ //go:build js && wasm -package filesystem +package opfs import ( "io/fs" diff --git a/pkg/filesystem/fs_wasm.go b/pkg/opfs/fs.go similarity index 92% rename from pkg/filesystem/fs_wasm.go rename to pkg/opfs/fs.go index 046b404..32243a0 100644 --- a/pkg/filesystem/fs_wasm.go +++ b/pkg/opfs/fs.go @@ -1,9 +1,8 @@ //go:build js && wasm -package filesystem +package opfs import ( - "errors" "fmt" "io" "io/fs" @@ -19,27 +18,20 @@ import ( "github.com/go-git/go-billy/v5/helper/chroot" ) -const DirName = "git-calendar-data" - -func GetFS() (billy.Filesystem, error) { - rootHandle := js.Global().Get("opfsRootHandle") - if rootHandle.IsUndefined() { - return nil, errors.New("opfsRootHandle not initialized") - } - - return &OPFS{ - root: rootHandle, - }, nil -} - // Origin private file system // // https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system type OPFS struct { - root js.Value // FileSystemDirectoryHandle + RootHandle js.Value // FileSystemDirectoryHandle } -var _ billy.Filesystem = (*OPFS)(nil) // makes sure that it implements all the interface methods, it wont compile without it +var _ billy.Filesystem = (*OPFS)(nil) // makes sure that it implements all the interface methods, it won't compile without it + +func New(baseDirHandle js.Value) *OPFS { + return &OPFS{ + RootHandle: baseDirHandle, + } +} func (fs *OPFS) MkdirAll(path string, perm fs.FileMode) error { // OPFS ignores permissions (perm) @@ -93,7 +85,7 @@ func (fs *OPFS) OpenFile(fullPath string, flag int, perm os.FileMode) (billy.Fil } // https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle/getFileHandle - handle, err := await(dirHandle.Call("getFileHandle", fileName, map[string]any{"create": create})) // returns Promise + handle, err := Await(dirHandle.Call("getFileHandle", fileName, map[string]any{"create": create})) // returns Promise if err != nil { if strings.Contains(err.Error(), "NotFoundError") { return nil, os.ErrNotExist @@ -149,7 +141,7 @@ func (fs *OPFS) Remove(path string) error { // OPFS FileSystemDirectoryHandle provides a native removeEntry method // https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle/removeEntry // a non-empty directory will not be removed - _, err = await(dirHandle.Call("removeEntry", name)) + _, err = Await(dirHandle.Call("removeEntry", name)) if err == nil { return nil // removed ok } @@ -226,7 +218,7 @@ func (fs *OPFS) ReadDir(path string) (infos []os.FileInfo, err error) { // the JS AsyncIterator has a .next() -> {done, value} for { // get one entry - result, err := await(itValue.Call("next")) // {done, value} + result, err := Await(itValue.Call("next")) // {done, value} if err != nil { return nil, err } @@ -310,10 +302,10 @@ func (fs *OPFS) Stat(path string) (os.FileInfo, error) { // try as file first // https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle/getFileHandle - handle, err := await(parentDirHandle.Call("getFileHandle", name)) + handle, err := Await(parentDirHandle.Call("getFileHandle", name)) if err == nil { // https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle/getFile - file, err := await(handle.Call("getFile")) // returns Promise + file, err := Await(handle.Call("getFile")) // returns Promise if err != nil { return nil, err } @@ -327,7 +319,7 @@ func (fs *OPFS) Stat(path string) (os.FileInfo, error) { } // if file failed, try as directory - _, err = await(parentDirHandle.Call("getDirectoryHandle", name)) + _, err = Await(parentDirHandle.Call("getDirectoryHandle", name)) if err == nil { return &OPFSFileInfo{ name: name, @@ -375,13 +367,13 @@ func (fs *OPFS) applyFlags(f *OPFSFile, flag int) error { func (fs *OPFS) getDirectoryHandle(path string, create bool) (js.Value, error) { parts := strings.Split(path, "/") - dir := fs.root + dir := fs.RootHandle for _, part := range parts { if part == "" || part == "." { continue } - d, err := await(dir.Call("getDirectoryHandle", part, map[string]any{"create": create})) + d, err := Await(dir.Call("getDirectoryHandle", part, map[string]any{"create": create})) if err != nil { return js.Undefined(), err } @@ -406,7 +398,7 @@ func (fs *OPFS) split(fullPath string) (string, string) { // }); // // But instead of "something", we pass the value/error to Go. -func await(p js.Value) (js.Value, error) { +func Await(p js.Value) (js.Value, error) { // create channel for each callback valCh := make(chan js.Value, 1) errCh := make(chan error, 1) diff --git a/pkg/filesystem/fs_inode_wasm.go b/pkg/opfs/inode.go similarity index 98% rename from pkg/filesystem/fs_inode_wasm.go rename to pkg/opfs/inode.go index 373049c..7ae8c35 100644 --- a/pkg/filesystem/fs_inode_wasm.go +++ b/pkg/opfs/inode.go @@ -1,6 +1,6 @@ //go:build js && wasm -package filesystem +package opfs import ( "path/filepath" diff --git a/pkg/filesystem/fs_inode_wasm_test.go b/pkg/opfs/inode_test.go similarity index 97% rename from pkg/filesystem/fs_inode_wasm_test.go rename to pkg/opfs/inode_test.go index 1f6926d..d7fef83 100644 --- a/pkg/filesystem/fs_inode_wasm_test.go +++ b/pkg/opfs/inode_test.go @@ -1,6 +1,6 @@ //go:build js && wasm -package filesystem +package opfs import "testing"