-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstore.go
204 lines (172 loc) · 5.93 KB
/
store.go
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
package main
import (
"crypto/sha1"
"encoding/hex"
"errors"
"fmt"
"io"
"os"
"strings"
)
// getDefaultRootFolder returns the default storage directory.
func getDefaultRootFolder() (string, error) {
defaultRootFolderName := "Storage"
dir, err := os.Getwd()
if err != nil {
return "", fmt.Errorf("failed to get current working directory: %w", err)
}
return fmt.Sprintf("%s/%s", dir, defaultRootFolderName), nil
}
// CASPathTransformFunc transforms a key into a PathKey using SHA1 hashing.
func CASPathTransformFunc(key string) PathKey {
// Implementing key transformation using SHA1.
hash := sha1.Sum([]byte(key))
hashString := hex.EncodeToString(hash[:]) // Convert to slice [:]
blockSize := 5 // Depth for block
sliceLength := len(hashString) / blockSize
paths := make([]string, sliceLength)
for i := 0; i < sliceLength; i++ {
from, to := i*blockSize, (i+1)*blockSize
paths[i] = hashString[from:to]
}
return PathKey{
PathName: strings.Join(paths, "/"),
FileName: hashString,
}
}
// PathKey represents the transformed path and file name.
type PathKey struct {
PathName string
FileName string
}
// FirstPathName returns the first segment of the path name.
func (p PathKey) FirstPathName() string {
paths := strings.Split(p.PathName, "/")
if len(paths) == 0 {
return ""
}
return paths[0]
}
// PathTransformFunc defines a function type for transforming keys into PathKeys.
type PathTransformFunc func(key string) PathKey
// FullPath constructs the full path for the file.
func (p PathKey) FullPath() string {
return fmt.Sprintf("%s/%s", p.PathName, p.FileName)
}
// StoreOpts contains options for creating a Store.
type StoreOpts struct {
Root string // Root directory for storing files.
PathTransformFunc PathTransformFunc // Function to transform keys into PathKeys.
}
// DefaultPathTransformFunc is the default transformation function.
var DefaultPathTransformFunc = func(key string) PathKey {
return PathKey{
PathName: key,
FileName: key,
}
}
// Store is responsible for managing file storage.
type Store struct {
StoreOpts
}
// NewStore creates a new Store instance with the provided options.
func NewStore(storeOpts StoreOpts) *Store {
if storeOpts.PathTransformFunc == nil {
storeOpts.PathTransformFunc = DefaultPathTransformFunc
}
if len(storeOpts.Root) == 0 {
var err error
storeOpts.Root, err = getDefaultRootFolder()
if err != nil {
return nil // Handle error appropriately
}
}
return &Store{
StoreOpts: storeOpts,
}
}
// HasKey checks if a key exists in the store.
func (s *Store) HasKey(key string) bool {
pathKey := s.PathTransformFunc(key)
fullPath := pathKey.FullPath()
fullPathWithRoot := fmt.Sprintf("%s/%s", s.Root, fullPath)
_, err := os.Stat(fullPathWithRoot)
return !errors.Is(err, os.ErrNotExist)
}
// Clear removes all files in the store's root directory.
func (s *Store) Clear() error {
return os.RemoveAll(s.Root)
}
// Delete removes the file associated with the given key.
func (s *Store) Delete(key string) error {
path := s.PathTransformFunc(key)
fullPath := path.FullPath()
firstPath := path.FirstPathName()
firstPathWithRoot := fmt.Sprintf("%s/%s", s.Root, firstPath)
defer func() {
fmt.Printf("Deleting: %s\n", fullPath)
}()
return os.RemoveAll(firstPathWithRoot)
}
// Read retrieves the file associated with the given key.
func (s *Store) Read(key string) (int64, io.Reader, error) {
return s.readStream(key)
}
// readStream opens a stream for reading the file associated with the key.
func (s *Store) readStream(key string) (int64, io.ReadCloser, error) {
pathKey := s.PathTransformFunc(key)
pathAndFilename := pathKey.FullPath()
fullPathWithRoot := fmt.Sprintf("%s/%s", s.Root, pathAndFilename)
fi, err := os.Stat(fullPathWithRoot)
if err != nil {
return 0, nil, fmt.Errorf("error stating file %s: %w", fullPathWithRoot, err)
}
file, err := os.Open(fullPathWithRoot)
if err != nil {
return 0, nil, fmt.Errorf("error opening file %s: %w", fullPathWithRoot, err)
}
return fi.Size(), file, nil
}
// Write stores the content from the provided reader associated with the key.
func (s *Store) Write(key string, r io.Reader) (int64, error) {
return s.writeStream(key, r)
}
// WriteDecrypt stores the content from the provided reader associated with the key after decryption.
func (s *Store) WriteDecrypt(encKey []byte, key string, r io.Reader) (int64, error) {
pathKey := s.PathTransformFunc(key)
pathNameWithRoot := fmt.Sprintf("%s/%s", s.Root, pathKey.PathName)
if err := os.MkdirAll(pathNameWithRoot, os.ModePerm); err != nil {
return 0, fmt.Errorf("error creating directory %s: %w", pathNameWithRoot, err)
}
pathAndFilename := pathKey.FullPath()
fullPathAndFilenameWithRoot := fmt.Sprintf("%s/%s", s.Root, pathAndFilename)
f, err := os.Create(fullPathAndFilenameWithRoot)
if err != nil {
return 0, fmt.Errorf("error creating file %s: %w", fullPathAndFilenameWithRoot, err)
}
n, err := copyDecrypt(encKey, r, f)
if err != nil {
return 0, fmt.Errorf("error copying decrypted content to file: %w", err)
}
fmt.Printf("Written (%d) bytes to disk: %s\n", n, fullPathAndFilenameWithRoot)
return int64(n), nil
}
// writeStream writes the content from the provided reader associated with the key.
func (s *Store) writeStream(key string, r io.Reader) (int64, error) {
pathKey := s.PathTransformFunc(key)
pathNameWithRoot := fmt.Sprintf("%s/%s", s.Root, pathKey.PathName)
if err := os.MkdirAll(pathNameWithRoot, os.ModePerm); err != nil {
return 0, fmt.Errorf("error creating directory %s: %w", pathNameWithRoot, err)
}
pathAndFilename := pathKey.FullPath()
fullPathAndFilenameWithRoot := fmt.Sprintf("%s/%s", s.Root, pathAndFilename)
f, err := os.Create(fullPathAndFilenameWithRoot)
if err != nil {
return 0, fmt.Errorf("error creating file %s: %w", fullPathAndFilenameWithRoot, err)
}
n, err := io.Copy(f, r)
if err != nil {
return 0, fmt.Errorf("error writing to file %s: %w", fullPathAndFilenameWithRoot, err)
}
return n, nil
}