2024-10-30 09:09:18 +00:00
|
|
|
package storage
|
|
|
|
|
|
|
|
import (
|
2024-10-30 11:18:27 +00:00
|
|
|
"encoding/gob"
|
2024-10-30 09:09:18 +00:00
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2024-10-30 10:47:17 +00:00
|
|
|
func NewDirStore(path string) Store {
|
2024-10-30 09:09:18 +00:00
|
|
|
store := &dir{
|
2024-10-30 10:47:17 +00:00
|
|
|
path: path,
|
2024-10-30 09:09:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return store
|
|
|
|
}
|
|
|
|
|
|
|
|
type dir struct {
|
2024-10-30 10:47:17 +00:00
|
|
|
path string
|
2024-10-30 09:09:18 +00:00
|
|
|
}
|
|
|
|
|
2024-10-30 10:51:20 +00:00
|
|
|
type dirEntry struct {
|
2024-10-30 11:18:27 +00:00
|
|
|
Password []byte
|
|
|
|
ExpiresAt time.Time
|
2024-10-30 10:51:20 +00:00
|
|
|
}
|
|
|
|
|
2024-10-30 10:17:48 +00:00
|
|
|
func (store *dir) Create(password []byte, expiresAt time.Time) (string, error) {
|
2024-10-30 09:09:18 +00:00
|
|
|
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()
|
|
|
|
|
2024-10-30 10:51:20 +00:00
|
|
|
entry := dirEntry{
|
2024-10-30 09:09:18 +00:00
|
|
|
Password: password,
|
|
|
|
ExpiresAt: expiresAt,
|
|
|
|
}
|
|
|
|
|
2024-10-30 11:18:27 +00:00
|
|
|
err = gob.NewEncoder(file).Encode(&entry)
|
2024-10-30 09:09:18 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("%s", err)
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return id, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", ErrFull
|
|
|
|
}
|
|
|
|
|
2024-10-30 10:17:48 +00:00
|
|
|
func (store *dir) Get(id string) ([]byte, error) {
|
2024-10-30 09:09:18 +00:00
|
|
|
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()
|
|
|
|
|
2024-10-30 10:51:20 +00:00
|
|
|
var entry dirEntry
|
2024-10-30 11:18:27 +00:00
|
|
|
err = gob.NewDecoder(file).Decode(&entry)
|
2024-10-30 09:09:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return entry.Password, nil
|
|
|
|
}
|
|
|
|
|
2024-10-30 10:17:48 +00:00
|
|
|
func (store *dir) Delete(id string) error {
|
2024-10-30 09:09:18 +00:00
|
|
|
path := store.getPath(id)
|
2024-10-30 10:17:48 +00:00
|
|
|
err := os.Remove(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
2024-10-30 09:09:18 +00:00
|
|
|
}
|
|
|
|
|
2024-10-30 10:17:48 +00:00
|
|
|
return nil
|
2024-10-30 09:09:18 +00:00
|
|
|
}
|
|
|
|
|
2024-10-30 10:47:17 +00:00
|
|
|
func (store *dir) ClearExpired() error {
|
|
|
|
now := time.Now()
|
2024-10-30 09:09:18 +00:00
|
|
|
|
2024-10-30 10:47:17 +00:00
|
|
|
entries, err := os.ReadDir(store.path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-10-30 09:09:18 +00:00
|
|
|
|
2024-10-30 10:47:17 +00:00
|
|
|
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
|
|
|
|
}
|
2024-10-30 09:09:18 +00:00
|
|
|
|
2024-10-30 10:47:17 +00:00
|
|
|
defer file.Close()
|
2024-10-30 09:09:18 +00:00
|
|
|
|
2024-10-30 10:51:20 +00:00
|
|
|
var entry dirEntry
|
2024-10-30 11:18:27 +00:00
|
|
|
err = gob.NewDecoder(file).Decode(&entry)
|
2024-10-30 10:47:17 +00:00
|
|
|
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
|
2024-10-30 09:09:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-10-30 10:47:17 +00:00
|
|
|
|
|
|
|
return nil
|
2024-10-30 09:09:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (store *dir) getPath(id string) string {
|
|
|
|
return path.Join(store.path, id)
|
|
|
|
}
|