package storage import ( "encoding/gob" "log" "os" "path" "time" ) func NewDirStore(path string) Store { store := &dir{ path: path, } return store } type dir struct { path string } type dirEntry struct { Password []byte ExpiresAt time.Time } func (store *dir) Create(password []byte, expiresAt time.Time) (string, error) { for range 1000 { id := generateId(24) path := store.getPath(id) file, err := os.OpenFile( path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, os.ModePerm, ) switch { case os.IsExist(err): continue case err != nil: return "", err } defer file.Close() entry := dirEntry{ Password: password, ExpiresAt: expiresAt, } err = gob.NewEncoder(file).Encode(&entry) if err != nil { log.Printf("%s", err) return "", err } return id, nil } return "", ErrFull } func (store *dir) Get(id string) ([]byte, error) { path := store.getPath(id) file, err := os.OpenFile( path, os.O_RDONLY, 0, ) switch { case os.IsNotExist(err): return nil, ErrNotFound case err != nil: return nil, err } defer file.Close() var entry dirEntry err = gob.NewDecoder(file).Decode(&entry) if err != nil { return nil, err } return entry.Password, nil } func (store *dir) Delete(id string) error { path := store.getPath(id) err := os.Remove(path) if err != nil { return nil } return nil } func (store *dir) ClearExpired() error { now := time.Now() entries, err := os.ReadDir(store.path) if err != nil { return err } for _, file := range entries { id := file.Name() path := store.getPath(id) file, err := os.OpenFile( path, os.O_RDONLY, 0, ) if err != nil { return err } defer file.Close() var entry dirEntry err = gob.NewDecoder(file).Decode(&entry) if err != nil { return err } if now.After(entry.ExpiresAt) { // Close file early as we need to delete it file.Close() err = store.Delete(id) if err != nil { return err } } } return nil } func (store *dir) getPath(id string) string { return path.Join(store.path, id) }