Fixed the issue with PlainText (Complete Anon) posting Need to fix device sign on issues. Need to make it so that the non-signed in devices can only see their equalivant level of posts. (i.e. plaintext, public-encrypted, private-encrypted)
152 lines
3.2 KiB
Go
152 lines
3.2 KiB
Go
package api
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// SimpleFSStore is a minimal FS-backed implementation of BlobStore.
|
|
// Layout under root:
|
|
//
|
|
// <root>/<hash> - content
|
|
// <root>/<hash>.priv - presence means "private"
|
|
type SimpleFSStore struct {
|
|
root string
|
|
}
|
|
|
|
func NewSimpleFSStore(root string) *SimpleFSStore {
|
|
return &SimpleFSStore{root: root}
|
|
}
|
|
|
|
func (fs *SimpleFSStore) ensureRoot() error {
|
|
return os.MkdirAll(fs.root, 0o755)
|
|
}
|
|
|
|
func (fs *SimpleFSStore) pathFor(hash string) string {
|
|
return filepath.Join(fs.root, hash)
|
|
}
|
|
func (fs *SimpleFSStore) privPathFor(hash string) string {
|
|
return filepath.Join(fs.root, hash+".priv")
|
|
}
|
|
|
|
// Get implements BlobStore.Get
|
|
func (fs *SimpleFSStore) Get(hash string) (io.ReadCloser, int64, error) {
|
|
if err := fs.ensureRoot(); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
f, err := os.Open(fs.pathFor(hash))
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
st, err := f.Stat()
|
|
if err != nil {
|
|
_ = f.Close()
|
|
return nil, 0, err
|
|
}
|
|
return f, st.Size(), nil
|
|
}
|
|
|
|
// Put implements BlobStore.Put
|
|
func (fs *SimpleFSStore) Put(r io.Reader, private bool) (string, int64, time.Time, error) {
|
|
if err := fs.ensureRoot(); err != nil {
|
|
return "", 0, time.Time{}, err
|
|
}
|
|
tmp, err := os.CreateTemp(fs.root, "put-*")
|
|
if err != nil {
|
|
return "", 0, time.Time{}, err
|
|
}
|
|
defer func() {
|
|
_ = tmp.Close()
|
|
_ = os.Remove(tmp.Name())
|
|
}()
|
|
|
|
h := sha256.New()
|
|
w := io.MultiWriter(tmp, h)
|
|
|
|
n, err := io.Copy(w, r)
|
|
if err != nil {
|
|
return "", 0, time.Time{}, err
|
|
}
|
|
hash := hex.EncodeToString(h.Sum(nil))
|
|
|
|
final := fs.pathFor(hash)
|
|
if err := os.Rename(tmp.Name(), final); err != nil {
|
|
return "", 0, time.Time{}, err
|
|
}
|
|
|
|
if private {
|
|
if err := os.WriteFile(fs.privPathFor(hash), nil, 0o600); err != nil {
|
|
_ = os.Remove(final)
|
|
return "", 0, time.Time{}, err
|
|
}
|
|
}
|
|
|
|
st, err := os.Stat(final)
|
|
if err != nil {
|
|
return "", 0, time.Time{}, err
|
|
}
|
|
return hash, n, st.ModTime().UTC(), nil
|
|
}
|
|
|
|
// Delete implements BlobStore.Delete
|
|
func (fs *SimpleFSStore) Delete(hash string) error {
|
|
if err := fs.ensureRoot(); err != nil {
|
|
return err
|
|
}
|
|
_ = os.Remove(fs.privPathFor(hash))
|
|
return os.Remove(fs.pathFor(hash))
|
|
}
|
|
|
|
// Walk implements BlobStore.Walk
|
|
func (fs *SimpleFSStore) Walk(fn func(hash string, bytes int64, private bool, storedAt time.Time) error) (int, error) {
|
|
if err := fs.ensureRoot(); err != nil {
|
|
return 0, err
|
|
}
|
|
ents, err := os.ReadDir(fs.root)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
count := 0
|
|
for _, e := range ents {
|
|
if e.IsDir() {
|
|
continue
|
|
}
|
|
name := e.Name()
|
|
// skip sidecars and non-64-hex filenames
|
|
if strings.HasSuffix(name, ".priv") || len(name) != 64 || !isHex(name) {
|
|
continue
|
|
}
|
|
full := fs.pathFor(name)
|
|
st, err := os.Stat(full)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
private := false
|
|
if _, err := os.Stat(fs.privPathFor(name)); err == nil {
|
|
private = true
|
|
}
|
|
if err := fn(name, st.Size(), private, st.ModTime().UTC()); err != nil {
|
|
return count, err
|
|
}
|
|
count++
|
|
}
|
|
return count, nil
|
|
}
|
|
|
|
func isHex(s string) bool {
|
|
for i := 0; i < len(s); i++ {
|
|
c := s[i]
|
|
if !((c >= '0' && c <= '9') ||
|
|
(c >= 'a' && c <= 'f') ||
|
|
(c >= 'A' && c <= 'F')) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|