package store import ( "encoding/json" "fmt" "io/fs" "io/ioutil" "os" "path/filepath" "git.t-juice.club/torjus/ezshare/pb" "github.com/google/uuid" ) var _ FileStore = &FileSystemStore{} type FileSystemStore struct { dir string } func NewFileSystemStore(dir string) *FileSystemStore { return &FileSystemStore{dir: dir} } func (s *FileSystemStore) GetFile(id string) (*pb.File, error) { path := filepath.Join(s.dir, id) f, err := os.Open(path) if err != nil { if os.IsNotExist(os.ErrNotExist) { return nil, ErrNoSuchItem } return nil, fmt.Errorf("unable to open file: %w", err) } defer f.Close() fm, err := os.Open(fmt.Sprintf("%s.metadata", path)) if err != nil { // TODO: Different error if file not found return nil, fmt.Errorf("unable to open file: %w", err) } defer fm.Close() var file pb.File if file.Data, err = ioutil.ReadAll(f); err != nil { return nil, fmt.Errorf("unable to read file: %w", err) } decoder := json.NewDecoder(fm) if err := decoder.Decode(&file.Metadata); err != nil { return nil, fmt.Errorf("unable to read file metadata: %w", err) } return &file, nil } func (s *FileSystemStore) StoreFile(file *pb.File) (string, error) { id := uuid.Must(uuid.NewRandom()).String() file.FileId = id path := filepath.Join(s.dir, file.FileId) f, err := os.Create(path) if err != nil { return "", fmt.Errorf("unable to store file: %w", err) } defer f.Close() fm, err := os.Create(fmt.Sprintf("%s.metadata", path)) if err != nil { return "", fmt.Errorf("unable to store metadata: %w", err) } defer fm.Close() if _, err := f.Write(file.Data); err != nil { return "", fmt.Errorf("unable to write file to store: %w", err) } // TODO: Write metadata encoder := json.NewEncoder(fm) if err := encoder.Encode(file.Metadata); err != nil { return "", fmt.Errorf("unable to write file to store: %w", err) } return file.FileId, nil } func (s *FileSystemStore) DeleteFile(id string) error { path := filepath.Join(s.dir, id) metadataPath := fmt.Sprintf("%s.metadata", path) if err := os.Remove(path); err != nil { return fmt.Errorf("error deleting file: %w", err) } if err := os.Remove(metadataPath); err != nil { return fmt.Errorf("error deleting file metadata: %w", err) } return nil } func (s *FileSystemStore) ListFiles() ([]*pb.ListFilesResponse_ListFileInfo, error) { root := os.DirFS(s.dir) var response []*pb.ListFilesResponse_ListFileInfo err := fs.WalkDir(root, ".", func(path string, d fs.DirEntry, err error) error { id := filepath.Base(path) // Check that it matches length of uuid if len(id) != 36 { return nil } if _, err := uuid.Parse(id); err == nil { // Is valid uuid, try to open metadata-file f, err := root.Open(fmt.Sprintf("%s.metadata", path)) if err != nil { return err } defer f.Close() var fm pb.File_Metadata decoder := json.NewDecoder(f) if err := decoder.Decode(&fm); err != nil { return err } response = append(response, &pb.ListFilesResponse_ListFileInfo{FileId: id, Metadata: &fm}) } return nil }) if err != nil { return nil, err } return response, nil }