dashboard / erock/pico / chore(pobj.fs): feature parity with minio #66 rss

accepted · opened on 2025-04-18T17:16:07Z by erock
Help
checkout latest patchset:
ssh pr.pico.sh print pr-66 | git am -3
checkout any patchset in a patch request:
ssh pr.pico.sh print ps-X | git am -3
add changes to patch request:
git format-patch main --stdout | ssh pr.pico.sh pr add 66
add review to patch request:
git format-patch main --stdout | ssh pr.pico.sh pr add --review 66
accept PR:
ssh pr.pico.sh pr accept 66
close PR:
ssh pr.pico.sh pr close 66

Logs

erock created pr with ps-133 on 2025-04-18T17:16:07Z
erock changed pr name on 2025-04-18T17:18:11Z {"name":"chore(pobj.fs): feature parity with minio"}
erock added ps-134 on 2025-04-21T18:44:44Z
erock changed status on 2025-04-22T22:17:41Z {"status":"accepted"}

Patchsets

ps-133 by erock on 2025-04-18T17:16:07Z
Range Diff ↕ rd-134
1: a3f9ef2 = 1: a3f9ef2 chore: rm UserMetadata
2: ccd89b0 = 2: ccd89b0 refactor(shared): mime type pkg
3: 8621e35 = 3: 8621e35 chore(pobj.fs): provide metadata
-: ------- > 4: 831b150 chore(storage.fs): add tests
ps-134 by erock on 2025-04-21T18:44:44Z

chore: rm UserMetadata

pkg/pobj/storage/fs.go link
+0 -1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
diff --git a/pkg/pobj/storage/fs.go b/pkg/pobj/storage/fs.go
index ab6b3c4..21e3329 100644
--- a/pkg/pobj/storage/fs.go
+++ b/pkg/pobj/storage/fs.go
@@ -97,7 +97,6 @@ func (s *StorageFS) GetObject(bucket Bucket, fpath string) (utils.ReadAndReaderA
 	objInfo := &ObjectInfo{
 		LastModified: time.Time{},
 		Metadata:     nil,
-		UserMetadata: map[string]string{},
 	}
 
 	dat, err := os.Open(filepath.Join(bucket.Path, fpath))
pkg/pobj/storage/memory.go link
+0 -1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
diff --git a/pkg/pobj/storage/memory.go b/pkg/pobj/storage/memory.go
index 1fa43c9..5542aee 100644
--- a/pkg/pobj/storage/memory.go
+++ b/pkg/pobj/storage/memory.go
@@ -87,7 +87,6 @@ func (s *StorageMemory) GetObject(bucket Bucket, fpath string) (utils.ReadAndRea
 	objInfo := &ObjectInfo{
 		LastModified: time.Time{},
 		Metadata:     nil,
-		UserMetadata: map[string]string{},
 	}
 
 	dat, ok := s.storage[bucket.Path][fpath]
pkg/pobj/storage/minio.go link
+0 -1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
diff --git a/pkg/pobj/storage/minio.go b/pkg/pobj/storage/minio.go
index 3e3509b..d7f310e 100644
--- a/pkg/pobj/storage/minio.go
+++ b/pkg/pobj/storage/minio.go
@@ -200,7 +200,6 @@ func (s *StorageMinio) GetObject(bucket Bucket, fpath string) (utils.ReadAndRead
 	objInfo.LastModified = info.LastModified
 	objInfo.ETag = info.ETag
 	objInfo.Metadata = info.Metadata
-	objInfo.UserMetadata = info.UserMetadata
 	objInfo.Size = info.Size
 
 	if mtime, ok := info.UserMetadata["Mtime"]; ok {
pkg/pobj/storage/storage.go link
+0 -1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
diff --git a/pkg/pobj/storage/storage.go b/pkg/pobj/storage/storage.go
index 6054e8b..f33d6e0 100644
--- a/pkg/pobj/storage/storage.go
+++ b/pkg/pobj/storage/storage.go
@@ -33,5 +33,4 @@ type ObjectInfo struct {
 	LastModified time.Time
 	ETag         string
 	Metadata     http.Header
-	UserMetadata map[string]string
 }

refactor(shared): mime type pkg

pkg/apps/pgs/calc_route.go link
+2 -2
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
diff --git a/pkg/apps/pgs/calc_route.go b/pkg/apps/pgs/calc_route.go
index 2387e4c..351139b 100644
--- a/pkg/apps/pgs/calc_route.go
+++ b/pkg/apps/pgs/calc_route.go
@@ -10,7 +10,7 @@ import (
 
 	"github.com/picosh/pico/pkg/send/utils"
 	"github.com/picosh/pico/pkg/shared"
-	"github.com/picosh/pico/pkg/shared/storage"
+	"github.com/picosh/pico/pkg/shared/mime"
 )
 
 type HttpReply struct {
@@ -23,7 +23,7 @@ func expandRoute(projectName, fp string, status int) []*HttpReply {
 	if fp == "" {
 		fp = "/"
 	}
-	mimeType := storage.GetMimeType(fp)
+	mimeType := mime.GetMimeType(fp)
 	fname := filepath.Base(fp)
 	fdir := filepath.Dir(fp)
 	fext := filepath.Ext(fp)
pkg/shared/mime/mime.go link
+82 -0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
diff --git a/pkg/shared/mime/mime.go b/pkg/shared/mime/mime.go
new file mode 100644
index 0000000..e5ee81f
--- /dev/null
+++ b/pkg/shared/mime/mime.go
@@ -0,0 +1,82 @@
+package mime
+
+import "path/filepath"
+
+func GetMimeType(fpath string) string {
+	ext := filepath.Ext(fpath)
+	if ext == ".svg" {
+		return "image/svg+xml"
+	} else if ext == ".css" {
+		return "text/css"
+	} else if ext == ".js" {
+		return "text/javascript"
+	} else if ext == ".ico" {
+		return "image/x-icon"
+	} else if ext == ".pdf" {
+		return "application/pdf"
+	} else if ext == ".html" || ext == ".htm" {
+		return "text/html"
+	} else if ext == ".jpg" || ext == ".jpeg" {
+		return "image/jpeg"
+	} else if ext == ".png" {
+		return "image/png"
+	} else if ext == ".gif" {
+		return "image/gif"
+	} else if ext == ".webp" {
+		return "image/webp"
+	} else if ext == ".otf" {
+		return "font/otf"
+	} else if ext == ".woff" {
+		return "font/woff"
+	} else if ext == ".woff2" {
+		return "font/woff2"
+	} else if ext == ".ttf" {
+		return "font/ttf"
+	} else if ext == ".md" {
+		return "text/markdown; charset=UTF-8"
+	} else if ext == ".json" || ext == ".map" {
+		return "application/json"
+	} else if ext == ".rss" {
+		return "application/rss+xml"
+	} else if ext == ".atom" {
+		return "application/atom+xml"
+	} else if ext == ".webmanifest" {
+		return "application/manifest+json"
+	} else if ext == ".xml" {
+		return "application/xml"
+	} else if ext == ".xsl" {
+		return "application/xml"
+	} else if ext == ".avif" {
+		return "image/avif"
+	} else if ext == ".heif" {
+		return "image/heif"
+	} else if ext == ".heic" {
+		return "image/heif"
+	} else if ext == ".opus" {
+		return "audio/opus"
+	} else if ext == ".wav" {
+		return "audio/wav"
+	} else if ext == ".mp3" {
+		return "audio/mpeg"
+	} else if ext == ".mp4" {
+		return "video/mp4"
+	} else if ext == ".mpeg" {
+		return "video/mpeg"
+	} else if ext == ".wasm" {
+		return "application/wasm"
+	} else if ext == ".opml" {
+		return "text/x-opml"
+	} else if ext == ".eot" {
+		return "application/vnd.ms-fontobject"
+	} else if ext == ".yml" || ext == ".yaml" {
+		return "text/x-yaml"
+	} else if ext == ".zip" {
+		return "application/zip"
+	} else if ext == ".rar" {
+		return "application/vnd.rar"
+	} else if ext == ".txt" {
+		return "text/plain"
+	}
+
+	return "text/plain"
+}
pkg/shared/storage/fs.go link
+2 -1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
diff --git a/pkg/shared/storage/fs.go b/pkg/shared/storage/fs.go
index 04068df..0c75fcc 100644
--- a/pkg/shared/storage/fs.go
+++ b/pkg/shared/storage/fs.go
@@ -9,6 +9,7 @@ import (
 	"strings"
 
 	sst "github.com/picosh/pico/pkg/pobj/storage"
+	"github.com/picosh/pico/pkg/shared/mime"
 )
 
 type StorageFS struct {
@@ -28,7 +29,7 @@ func (s *StorageFS) ServeObject(bucket sst.Bucket, fpath string, opts *ImgProces
 	var rc io.ReadCloser
 	info := &sst.ObjectInfo{}
 	var err error
-	mimeType := GetMimeType(fpath)
+	mimeType := mime.GetMimeType(fpath)
 	if !strings.HasPrefix(mimeType, "image/") || opts == nil || os.Getenv("IMGPROXY_URL") == "" {
 		rc, info, err = s.GetObject(bucket, fpath)
 		if info.Metadata == nil {
pkg/shared/storage/memory.go link
+2 -1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
diff --git a/pkg/shared/storage/memory.go b/pkg/shared/storage/memory.go
index 350fa13..7c3df6e 100644
--- a/pkg/shared/storage/memory.go
+++ b/pkg/shared/storage/memory.go
@@ -6,6 +6,7 @@ import (
 	"time"
 
 	sst "github.com/picosh/pico/pkg/pobj/storage"
+	"github.com/picosh/pico/pkg/shared/mime"
 )
 
 type StorageMemory struct {
@@ -32,7 +33,7 @@ func (s *StorageMemory) ServeObject(bucket sst.Bucket, fpath string, opts *ImgPr
 	if info.ETag == "" {
 		info.ETag = "static-etag-for-testing-purposes"
 	}
-	mimeType := GetMimeType(fpath)
+	mimeType := mime.GetMimeType(fpath)
 	info.Metadata.Set("content-type", mimeType)
 	return obj, info, err
 }
pkg/shared/storage/minio.go link
+2 -1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
diff --git a/pkg/shared/storage/minio.go b/pkg/shared/storage/minio.go
index 8d242a5..5826fe6 100644
--- a/pkg/shared/storage/minio.go
+++ b/pkg/shared/storage/minio.go
@@ -9,6 +9,7 @@ import (
 	"strings"
 
 	sst "github.com/picosh/pico/pkg/pobj/storage"
+	"github.com/picosh/pico/pkg/shared/mime"
 )
 
 type StorageMinio struct {
@@ -27,7 +28,7 @@ func (s *StorageMinio) ServeObject(bucket sst.Bucket, fpath string, opts *ImgPro
 	var rc io.ReadCloser
 	info := &sst.ObjectInfo{}
 	var err error
-	mimeType := GetMimeType(fpath)
+	mimeType := mime.GetMimeType(fpath)
 	if !strings.HasPrefix(mimeType, "image/") || opts == nil || os.Getenv("IMGPROXY_URL") == "" {
 		rc, info, err = s.GetObject(bucket, fpath)
 		if info.Metadata == nil {
pkg/shared/storage/proxy.go link
+0 -80
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
diff --git a/pkg/shared/storage/proxy.go b/pkg/shared/storage/proxy.go
index a3a2c51..811cc25 100644
--- a/pkg/shared/storage/proxy.go
+++ b/pkg/shared/storage/proxy.go
@@ -10,7 +10,6 @@ import (
 	"log/slog"
 	"net/http"
 	"os"
-	"path/filepath"
 	"strconv"
 	"strings"
 	"time"
@@ -18,85 +17,6 @@ import (
 	"github.com/picosh/pico/pkg/pobj/storage"
 )
 
-func GetMimeType(fpath string) string {
-	ext := filepath.Ext(fpath)
-	if ext == ".svg" {
-		return "image/svg+xml"
-	} else if ext == ".css" {
-		return "text/css"
-	} else if ext == ".js" {
-		return "text/javascript"
-	} else if ext == ".ico" {
-		return "image/x-icon"
-	} else if ext == ".pdf" {
-		return "application/pdf"
-	} else if ext == ".html" || ext == ".htm" {
-		return "text/html"
-	} else if ext == ".jpg" || ext == ".jpeg" {
-		return "image/jpeg"
-	} else if ext == ".png" {
-		return "image/png"
-	} else if ext == ".gif" {
-		return "image/gif"
-	} else if ext == ".webp" {
-		return "image/webp"
-	} else if ext == ".otf" {
-		return "font/otf"
-	} else if ext == ".woff" {
-		return "font/woff"
-	} else if ext == ".woff2" {
-		return "font/woff2"
-	} else if ext == ".ttf" {
-		return "font/ttf"
-	} else if ext == ".md" {
-		return "text/markdown; charset=UTF-8"
-	} else if ext == ".json" || ext == ".map" {
-		return "application/json"
-	} else if ext == ".rss" {
-		return "application/rss+xml"
-	} else if ext == ".atom" {
-		return "application/atom+xml"
-	} else if ext == ".webmanifest" {
-		return "application/manifest+json"
-	} else if ext == ".xml" {
-		return "application/xml"
-	} else if ext == ".xsl" {
-		return "application/xml"
-	} else if ext == ".avif" {
-		return "image/avif"
-	} else if ext == ".heif" {
-		return "image/heif"
-	} else if ext == ".heic" {
-		return "image/heif"
-	} else if ext == ".opus" {
-		return "audio/opus"
-	} else if ext == ".wav" {
-		return "audio/wav"
-	} else if ext == ".mp3" {
-		return "audio/mpeg"
-	} else if ext == ".mp4" {
-		return "video/mp4"
-	} else if ext == ".mpeg" {
-		return "video/mpeg"
-	} else if ext == ".wasm" {
-		return "application/wasm"
-	} else if ext == ".opml" {
-		return "text/x-opml"
-	} else if ext == ".eot" {
-		return "application/vnd.ms-fontobject"
-	} else if ext == ".yml" || ext == ".yaml" {
-		return "text/x-yaml"
-	} else if ext == ".zip" {
-		return "application/zip"
-	} else if ext == ".rar" {
-		return "application/vnd.rar"
-	} else if ext == ".txt" {
-		return "text/plain"
-	}
-
-	return "text/plain"
-}
-
 func UriToImgProcessOpts(uri string) (*ImgProcessOpts, error) {
 	opts := &ImgProcessOpts{}
 	parts := strings.Split(uri, "/")

chore(pobj.fs): provide metadata

fix(pobj.fs): attempt to delete empty folder
pkg/pobj/storage/fs.go link
+8 -1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
diff --git a/pkg/pobj/storage/fs.go b/pkg/pobj/storage/fs.go
index 21e3329..15ddd76 100644
--- a/pkg/pobj/storage/fs.go
+++ b/pkg/pobj/storage/fs.go
@@ -5,6 +5,7 @@ import (
 	"io"
 	"io/fs"
 	"log/slog"
+	"net/http"
 	"os"
 	"path"
 	"path/filepath"
@@ -12,6 +13,7 @@ import (
 	"time"
 
 	"github.com/picosh/pico/pkg/send/utils"
+	"github.com/picosh/pico/pkg/shared/mime"
 )
 
 // https://stackoverflow.com/a/32482941
@@ -96,7 +98,7 @@ func (s *StorageFS) DeleteBucket(bucket Bucket) error {
 func (s *StorageFS) GetObject(bucket Bucket, fpath string) (utils.ReadAndReaderAtCloser, *ObjectInfo, error) {
 	objInfo := &ObjectInfo{
 		LastModified: time.Time{},
-		Metadata:     nil,
+		Metadata:     make(http.Header),
 	}
 
 	dat, err := os.Open(filepath.Join(bucket.Path, fpath))
@@ -111,6 +113,7 @@ func (s *StorageFS) GetObject(bucket Bucket, fpath string) (utils.ReadAndReaderA
 
 	objInfo.Size = info.Size()
 	objInfo.LastModified = info.ModTime()
+	objInfo.Metadata.Set("content-type", mime.GetMimeType(fpath))
 	return dat, objInfo, nil
 }
 
@@ -147,6 +150,10 @@ func (s *StorageFS) DeleteObject(bucket Bucket, fpath string) error {
 		return err
 	}
 
+	// try to remove dir if it is empty
+	dir := filepath.Dir(loc)
+	_ = os.Remove(dir)
+
 	return nil
 }