package store import ( "crypto/sha1" "encoding/hex" "encoding/json" "errors" "fmt" "os" "path/filepath" "time" "github.com/gin-gonic/gin" photoserrors "gitlab.celogeek.com/photos/api/internal/photos/errors" "gitlab.celogeek.com/photos/api/internal/photos/models" ) type Store struct { Path string } type Chunk struct { *Store Sum string Bytes []byte } func (s *Store) NewChunk(b []byte) *Chunk { sum := sha1.New() sum.Write(b) sumString := hex.EncodeToString(sum.Sum(nil)) return &Chunk{s, sumString, b} } func (s *Store) LoadChunk(sum string) (*Chunk, error) { c := s.Chunk(sum) if !c.FileExists() { return nil, fmt.Errorf("chunk %s doesn't exists", sum) } b, err := os.ReadFile(c.Filename()) if err != nil { return nil, err } c.Bytes = b return c, nil } func (s *Store) Chunk(sum string) *Chunk { return &Chunk{s, sum, nil} } func (c *Chunk) Dir() string { return filepath.Join(c.Path, "storage", c.Sum[0:1], c.Sum[1:2]) } func (c *Chunk) Filename() string { return filepath.Join(c.Dir(), c.Sum) } func (c *Chunk) Size() int64 { st, err := os.Stat(c.Filename()) if err != nil { return -1 } return st.Size() } func (c *Chunk) FileExists() bool { fs, err := os.Stat(c.Filename()) if errors.Is(err, os.ErrNotExist) { return false } return !fs.IsDir() } func (c *Chunk) Mkdir() error { return os.MkdirAll(c.Dir(), 0755) } func (c *Chunk) Save(sess *models.Session) error { if c.FileExists() { return photoserrors.ErrStoreChunkAlreadyExists } if err := c.Mkdir(); err != nil { return err } if err := os.WriteFile(c.Filename(), c.Bytes, 0666); err != nil { return err } meta, err := os.Create(c.Filename() + ".json") if err != nil { return err } enc := json.NewEncoder(meta) enc.SetIndent("", " ") return enc.Encode(gin.H{ "author": sess.Account.Login, "date": time.Now().UTC().Format("2006-01-02 15:04:05"), "checksum": c.Sum, "size": len(c.Bytes), }) } func (s *Store) Combine(sumb []string) (string, uint64, error) { sum := sha1.New() size := uint64(0) for _, sb := range sumb { c, err := s.LoadChunk(sb) if err != nil { return "", 0, err } sum.Write(c.Bytes) size += uint64(len(c.Bytes)) } return hex.EncodeToString(sum.Sum(nil)), size, nil }