From f5068c6189c0a0d0ddc5a6b7fbbe92b277b6e741 Mon Sep 17 00:00:00 2001 From: Abhishek Shroff Date: Sat, 3 Aug 2024 08:48:15 +0530 Subject: [PATCH] Merge fileInfo and file into resource --- server/internal/app/core/file.go | 77 ----------------------- server/internal/app/core/resource.go | 76 ++++++++++++++++++++++ server/internal/app/core/silo.go | 35 ++++++----- server/internal/handler_webdav/handler.go | 2 +- server/internal/webdav/file.go | 6 +- server/internal/webdav/prop.go | 16 ++--- server/internal/webdav/serve_resource.go | 16 ++--- server/internal/webdav/webdav.go | 4 +- 8 files changed, 116 insertions(+), 116 deletions(-) delete mode 100644 server/internal/app/core/file.go create mode 100644 server/internal/app/core/resource.go diff --git a/server/internal/app/core/file.go b/server/internal/app/core/file.go deleted file mode 100644 index 2bd069bb..00000000 --- a/server/internal/app/core/file.go +++ /dev/null @@ -1,77 +0,0 @@ -package core - -import ( - "context" - "fmt" - "io" - "mime" - "path/filepath" - "time" - - "github.com/google/uuid" -) - -type FileInfo interface { - ID() uuid.UUID - Name() string - Size() int64 - ModTime() time.Time - IsDir() bool - ETag() string - ContentType() string -} - -type File interface { - FileInfo - OpenRead(ctx context.Context, start, length int64) (io.ReadCloser, error) - ReadDir(ctx context.Context) ([]FileInfo, error) -} - -type fileInfo struct { - id uuid.UUID - name string - size int64 - collection bool - modTime time.Time - etag string -} - -type file struct { - fileInfo - silo Silo -} - -func (f fileInfo) ID() uuid.UUID { return f.id } - -func (f fileInfo) Name() string { return f.name } - -func (f fileInfo) Size() int64 { return f.size } - -func (f fileInfo) ModTime() time.Time { return f.modTime } - -func (f fileInfo) IsDir() bool { return f.collection } - -func (f fileInfo) ETag() string { - if f.etag != "" { - return f.etag - } - // The Apache http 2.4 web server by default concatenates the - // modification time and size of a file. - return fmt.Sprintf(`"%x%x"`, f.modTime.UnixMilli(), f.size) -} - -func (f fileInfo) ContentType() string { - mimeType := mime.TypeByExtension(filepath.Ext(f.name)) - if mimeType != "" { - return mimeType - } - return "application/octet-stream" -} - -func (f file) OpenRead(ctx context.Context, start, length int64) (io.ReadCloser, error) { - return f.silo.OpenRead(ctx, f.id, start, length) -} - -func (f file) ReadDir(ctx context.Context) ([]FileInfo, error) { - return f.silo.ReadDir(ctx, f.id, false, false) -} diff --git a/server/internal/app/core/resource.go b/server/internal/app/core/resource.go new file mode 100644 index 00000000..6490beda --- /dev/null +++ b/server/internal/app/core/resource.go @@ -0,0 +1,76 @@ +package core + +import ( + "context" + "fmt" + "io" + "mime" + "path/filepath" + "time" + + "github.com/google/uuid" +) + +type ResourceInfo interface { + ID() uuid.UUID + Name() string + Size() int64 + ModTime() time.Time + IsDir() bool + ETag() string + ContentType() string +} + +type Resource interface { + ResourceInfo + OpenRead(ctx context.Context, start, length int64) (io.ReadCloser, error) + ReadDir(ctx context.Context) ([]Resource, error) +} + +type resource struct { + silo Silo + id uuid.UUID + parentID *uuid.UUID + name string + size int64 + collection bool + modTime time.Time + etag string +} + +func (r resource) ID() uuid.UUID { return r.id } + +func (r resource) ParentID() *uuid.UUID { return r.parentID } + +func (r resource) Name() string { return r.name } + +func (r resource) Size() int64 { return r.size } + +func (r resource) ModTime() time.Time { return r.modTime } + +func (r resource) IsDir() bool { return r.collection } + +func (r resource) ETag() string { + if r.etag != "" { + return r.etag + } + // The Apache http 2.4 web server by default concatenates the + // modification time and size of a file. + return fmt.Sprintf(`"%x%x"`, r.modTime.UnixMilli(), r.size) +} + +func (r resource) ContentType() string { + mimeType := mime.TypeByExtension(filepath.Ext(r.name)) + if mimeType != "" { + return mimeType + } + return "application/octet-stream" +} + +func (f resource) OpenRead(ctx context.Context, start, length int64) (io.ReadCloser, error) { + return f.silo.OpenRead(ctx, f.id, start, length) +} + +func (f resource) ReadDir(ctx context.Context) ([]Resource, error) { + return f.silo.ReadDir(ctx, f.id, false, false) +} diff --git a/server/internal/app/core/silo.go b/server/internal/app/core/silo.go index 20650d4e..054b624c 100644 --- a/server/internal/app/core/silo.go +++ b/server/internal/app/core/silo.go @@ -20,9 +20,9 @@ type Silo interface { StorageName() string OpenRead(ctx context.Context, id uuid.UUID, start, length int64) (io.ReadCloser, error) OpenWrite(ctx context.Context, id uuid.UUID) (io.WriteCloser, error) - ReadDir(ctx context.Context, id uuid.UUID, includeRoot bool, recursive bool) ([]FileInfo, error) + ReadDir(ctx context.Context, id uuid.UUID, includeRoot bool, recursive bool) ([]Resource, error) DeleteRecursive(ctx context.Context, id uuid.UUID, hardDelete bool) error - ResourceByPath(ctx context.Context, path string) (File, error) + ResourceByPath(ctx context.Context, path string) (Resource, error) Move(ctx context.Context, id uuid.UUID, parent uuid.UUID, name string) error CreateResource(ctx context.Context, id uuid.UUID, parent uuid.UUID, name string, dir bool) error } @@ -75,20 +75,22 @@ func (s *silo) OpenWrite(ctx context.Context, id uuid.UUID) (io.WriteCloser, err }) } -func (s *silo) ReadDir(ctx context.Context, id uuid.UUID, includeRoot bool, recursive bool) ([]FileInfo, error) { +func (s *silo) ReadDir(ctx context.Context, id uuid.UUID, includeRoot bool, recursive bool) ([]Resource, error) { children, err := s.db.Queries().ReadDir(ctx, sql.ReadDirParams{ID: id, IncludeRoot: includeRoot, Recursive: recursive}) if err != nil { return nil, err } - result := make([]FileInfo, len(children)) + result := make([]Resource, len(children)) for i, c := range children { - result[i] = fileInfo{ + result[i] = resource{ + silo: s, + id: c.ID, + parentID: &id, name: c.Name, size: int64(c.Size.Int32), modTime: c.Modified.Time, collection: c.Dir, - id: c.ID, etag: c.Etag.String, } } @@ -138,7 +140,7 @@ func (s *silo) Move(ctx context.Context, id uuid.UUID, parent uuid.UUID, name st return s.db.Queries().Rename(ctx, sql.RenameParams{ID: id, Parent: parent, Name: name}) } -func (s *silo) ResourceByPath(ctx context.Context, path string) (File, error) { +func (s *silo) ResourceByPath(ctx context.Context, path string) (Resource, error) { path = strings.Trim(path, "/") segments := strings.Split(path, "/") if path == "" { @@ -153,15 +155,14 @@ func (s *silo) ResourceByPath(ctx context.Context, path string) (File, error) { //TODO: Permissions checks - return file{ - fileInfo: fileInfo{ - id: res.ID, - name: res.Name, - size: int64(res.Size.Int32), - collection: res.Dir, - modTime: res.Modified.Time, - etag: res.Etag.String, - }, - silo: s, + return resource{ + silo: s, + id: res.ID, + parentID: res.Parent, + name: res.Name, + size: int64(res.Size.Int32), + collection: res.Dir, + modTime: res.Modified.Time, + etag: res.Etag.String, }, nil } diff --git a/server/internal/handler_webdav/handler.go b/server/internal/handler_webdav/handler.go index 6c1cd84c..3cc8548d 100644 --- a/server/internal/handler_webdav/handler.go +++ b/server/internal/handler_webdav/handler.go @@ -77,7 +77,7 @@ type adapter struct { silo core.Silo } -func (a adapter) Stat(ctx context.Context, name string) (core.File, error) { +func (a adapter) Stat(ctx context.Context, name string) (core.Resource, error) { return a.silo.ResourceByPath(ctx, name) } diff --git a/server/internal/webdav/file.go b/server/internal/webdav/file.go index 270acfef..18f03291 100644 --- a/server/internal/webdav/file.go +++ b/server/internal/webdav/file.go @@ -19,7 +19,7 @@ import ( // in a file path are separated by slash ('/', U+002F) characters, regardless // of host operating system convention. type FileSystem interface { - Stat(ctx context.Context, name string) (core.File, error) + Stat(ctx context.Context, name string) (core.Resource, error) Mkdir(ctx context.Context, name string) error RemoveAll(ctx context.Context, name string) error Rename(ctx context.Context, oldName, newName string) error @@ -146,14 +146,14 @@ func copyFiles(ctx context.Context, fs FileSystem, src, dst string, overwrite bo return http.StatusNoContent, nil } -type WalkFunc func(path string, info core.FileInfo, err error) error +type WalkFunc func(path string, info core.Resource, err error) error // walkFS traverses filesystem fs starting at name up to depth levels. // // Allowed values for depth are 0, 1 or infiniteDepth. For each visited node, // walkFS calls walkFn. If a visited file system node is a directory and // walkFn returns filepath.SkipDir, walkFS will skip traversal of this node. -func walkFS(ctx context.Context, fs FileSystem, depth int, name string, info core.FileInfo, walkFn WalkFunc) error { +func walkFS(ctx context.Context, fs FileSystem, depth int, name string, info core.Resource, walkFn WalkFunc) error { // This implementation is based on Walk's code in the standard path/filepath package. err := walkFn(name, info, nil) if err != nil { diff --git a/server/internal/webdav/prop.go b/server/internal/webdav/prop.go index 81210cb3..cde76e27 100644 --- a/server/internal/webdav/prop.go +++ b/server/internal/webdav/prop.go @@ -70,7 +70,7 @@ func makePropstats(x, y Propstat) []Propstat { var liveProps = map[xml.Name]struct { // findFn implements the propfind function of this property. If nil, // it indicates a hidden property. - findFn func(core.FileInfo) string + findFn func(core.ResourceInfo) string // dir is true if the property applies to directories. dir bool }{ @@ -265,34 +265,34 @@ func escapeXML(s string) string { return s } -func findResourceType(fi core.FileInfo) string { +func findResourceType(fi core.ResourceInfo) string { if fi.IsDir() { return `` } return "" } -func findDisplayName(fi core.FileInfo) string { +func findDisplayName(fi core.ResourceInfo) string { return escapeXML(fi.Name()) } -func findContentLength(fi core.FileInfo) string { +func findContentLength(fi core.ResourceInfo) string { return strconv.FormatInt(fi.Size(), 10) } -func findLastModified(fi core.FileInfo) string { +func findLastModified(fi core.ResourceInfo) string { return fi.ModTime().UTC().Format(http.TimeFormat) } -func findContentType(fi core.FileInfo) string { +func findContentType(fi core.ResourceInfo) string { return fi.ContentType() } -func findETag(fi core.FileInfo) string { +func findETag(fi core.ResourceInfo) string { return fi.ETag() } -func findSupportedLock(fi core.FileInfo) string { +func findSupportedLock(fi core.ResourceInfo) string { return `` + `` + `` + diff --git a/server/internal/webdav/serve_resource.go b/server/internal/webdav/serve_resource.go index 0a53316e..701eaa9f 100644 --- a/server/internal/webdav/serve_resource.go +++ b/server/internal/webdav/serve_resource.go @@ -25,7 +25,7 @@ var htmlReplacer = strings.NewReplacer( "'", "'", ) -func serveCollection(w http.ResponseWriter, r *http.Request, file core.File) { +func serveCollection(w http.ResponseWriter, r *http.Request, file core.Resource) { if !strings.HasSuffix(r.URL.Path, "/") { http.Redirect(w, r, r.URL.String()+"/", http.StatusMovedPermanently) return @@ -59,7 +59,7 @@ func serveCollection(w http.ResponseWriter, r *http.Request, file core.File) { fmt.Fprintf(w, "\n") } -func serveResource(w http.ResponseWriter, r *http.Request, file core.File) { +func serveResource(w http.ResponseWriter, r *http.Request, file core.Resource) { w.Header().Set("Etag", file.ETag()) w.Header().Set("Last-Modified", file.ModTime().Format(http.TimeFormat)) w.Header().Set("Content-Type", file.ContentType()) @@ -206,7 +206,7 @@ func parseRange(s string, size int64) ([]httpRange, error) { // checkPreconditions evaluates request preconditions and reports whether a precondition // resulted in sending StatusNotModified or StatusPreconditionFailed. -func checkPreconditions(w http.ResponseWriter, r *http.Request, ri core.FileInfo) (done bool, rangeHeader string) { +func checkPreconditions(w http.ResponseWriter, r *http.Request, ri core.ResourceInfo) (done bool, rangeHeader string) { // This function carefully follows RFC 7232 section 6. ch := checkIfMatch(r, ri) if ch == condNone { @@ -289,7 +289,7 @@ const ( condFalse ) -func checkIfMatch(r *http.Request, ri core.FileInfo) condResult { +func checkIfMatch(r *http.Request, ri core.ResourceInfo) condResult { im := r.Header.Get("If-Match") if im == "" { return condNone @@ -319,7 +319,7 @@ func checkIfMatch(r *http.Request, ri core.FileInfo) condResult { return condFalse } -func checkIfUnmodifiedSince(r *http.Request, ri core.FileInfo) condResult { +func checkIfUnmodifiedSince(r *http.Request, ri core.ResourceInfo) condResult { ius := r.Header.Get("If-Unmodified-Since") if ius == "" || isZeroTime(ri.ModTime()) { return condNone @@ -338,7 +338,7 @@ func checkIfUnmodifiedSince(r *http.Request, ri core.FileInfo) condResult { return condFalse } -func checkIfNoneMatch(r *http.Request, ri core.FileInfo) condResult { +func checkIfNoneMatch(r *http.Request, ri core.ResourceInfo) condResult { inm := r.Header.Get("If-None-Match") if inm == "" { return condNone @@ -368,7 +368,7 @@ func checkIfNoneMatch(r *http.Request, ri core.FileInfo) condResult { return condTrue } -func checkIfModifiedSince(r *http.Request, ri core.FileInfo) condResult { +func checkIfModifiedSince(r *http.Request, ri core.ResourceInfo) condResult { if r.Method != "GET" && r.Method != "HEAD" { return condNone } @@ -389,7 +389,7 @@ func checkIfModifiedSince(r *http.Request, ri core.FileInfo) condResult { return condTrue } -func checkIfRange(r *http.Request, ri core.FileInfo) condResult { +func checkIfRange(r *http.Request, ri core.ResourceInfo) condResult { if r.Method != "GET" && r.Method != "HEAD" { return condNone } diff --git a/server/internal/webdav/webdav.go b/server/internal/webdav/webdav.go index 2725dcae..a863eee6 100644 --- a/server/internal/webdav/webdav.go +++ b/server/internal/webdav/webdav.go @@ -524,7 +524,7 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status mw := multistatusWriter{w: w} - walkFn := func(reqPath string, info core.FileInfo, err error) error { + walkFn := func(reqPath string, info core.Resource, err error) error { if err != nil { return handlePropfindError(err, info) } @@ -625,7 +625,7 @@ func makePropstatResponse(href string, pstats []Propstat) *response { return &resp } -func handlePropfindError(err error, info core.FileInfo) error { +func handlePropfindError(err error, info core.ResourceInfo) error { var skipResp error = nil if info.IsDir() { skipResp = filepath.SkipDir