Skip to content

Commit 1ac6d31

Browse files
committed
Add filestore
1 parent 5ba2601 commit 1ac6d31

3 files changed

Lines changed: 140 additions & 0 deletions

File tree

httpcache.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
1515
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
1616
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
17+
"github.com/golevi/cache-handler/stores/filestore"
1718
"github.com/golevi/cache-handler/stores/redisstore"
1819
"github.com/prometheus/client_golang/prometheus"
1920
"github.com/prometheus/client_golang/prometheus/promauto"
@@ -271,6 +272,8 @@ func (c *Cache) Provision(ctx caddy.Context) error {
271272
switch c.Config.Type {
272273
case "redis":
273274
c.Store = redisstore.NewRedisStore(c.Host)
275+
case "file":
276+
c.Store = filestore.NewFileStore()
274277
}
275278

276279
return nil

stores/filestore/filestore.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package filestore
2+
3+
import (
4+
"crypto/sha256"
5+
"encoding/gob"
6+
"encoding/hex"
7+
"log"
8+
"os"
9+
"strings"
10+
"time"
11+
12+
"github.com/caddyserver/caddy/v2"
13+
)
14+
15+
// FileStore uses the local filesystem.
16+
type FileStore struct {
17+
Level int
18+
Dir string
19+
}
20+
21+
type fileCache struct {
22+
Data []byte
23+
Created time.Time
24+
Expires time.Duration
25+
}
26+
27+
// NewFileStore uses `caddy.AppDataDir()` to get a location to store the cached
28+
// files.
29+
func NewFileStore() FileStore {
30+
f := FileStore{
31+
Level: 2,
32+
Dir: caddy.AppDataDir() + "/cache",
33+
}
34+
35+
return f
36+
}
37+
38+
// Get value from file.
39+
func (f FileStore) Get(key string) (interface{}, error) {
40+
key = hashKey(key)
41+
42+
path := f.path(key)
43+
filepath := path + key
44+
log.Println(filepath)
45+
46+
file, err := os.Open(filepath)
47+
if err != nil {
48+
return nil, err
49+
}
50+
defer file.Close()
51+
52+
fc := fileCache{}
53+
dec := gob.NewDecoder(file)
54+
err = dec.Decode(&fc)
55+
if err != nil {
56+
return nil, err
57+
}
58+
59+
if time.Now().Sub(fc.Created) > fc.Expires {
60+
os.Remove(filepath)
61+
}
62+
63+
return fc.Data, nil
64+
}
65+
66+
// Has checks if the key exists.
67+
func (f FileStore) Has(key string) bool {
68+
key = hashKey(key)
69+
path := f.path(key)
70+
filepath := path + key
71+
72+
if _, err := os.Stat(filepath); os.IsNotExist(err) {
73+
return false
74+
}
75+
76+
return true
77+
}
78+
79+
// Put the value in a file.
80+
func (f FileStore) Put(key string, value interface{}, expiration time.Duration) {
81+
key = hashKey(key)
82+
83+
fc := fileCache{
84+
Data: value.([]byte),
85+
Created: time.Now(),
86+
Expires: expiration,
87+
}
88+
89+
path := f.path(key)
90+
91+
if _, err := os.Stat(path); os.IsNotExist(err) {
92+
os.MkdirAll(path, 0700)
93+
}
94+
95+
filepath := path + key
96+
file, _ := os.Create(filepath)
97+
98+
defer file.Close()
99+
100+
enc := gob.NewEncoder(file)
101+
enc.Encode(fc)
102+
}
103+
104+
func (f FileStore) path(key string) string {
105+
s := strings.Split(key, "")
106+
folders := ""
107+
for i, d := range s {
108+
folders += d + "/"
109+
if i >= f.Level-1 {
110+
break
111+
}
112+
}
113+
return f.Dir + "/" + folders
114+
}
115+
116+
func hashKey(input string) string {
117+
h := sha256.New()
118+
h.Write([]byte(input))
119+
o := h.Sum(nil)
120+
121+
return hex.EncodeToString(o)
122+
}

stores/filestore/filestore_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package filestore
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestPath(t *testing.T) {
8+
key := "abcdefg"
9+
fs := NewFileStore()
10+
11+
out := fs.path(key)
12+
if out != "" {
13+
t.Error(out)
14+
}
15+
}

0 commit comments

Comments
 (0)