disable file

This commit is contained in:
celogeek 2022-05-14 17:47:55 +02:00
parent 2d1fdc1314
commit 44f0cf68be
Signed by: celogeek
GPG Key ID: E6B7BDCFC446233A
2 changed files with 210 additions and 223 deletions

View File

@ -35,7 +35,6 @@ func (s *Service) Migrate() {
tx.AutoMigrate(&Account{}) tx.AutoMigrate(&Account{})
tx.AutoMigrate(&Session{}) tx.AutoMigrate(&Session{})
tx.AutoMigrate(&File{}) tx.AutoMigrate(&File{})
tx.AutoMigrate(&FileChunk{})
} }
func (s *Service) DBConnect() { func (s *Service) DBConnect() {

View File

@ -1,19 +1,7 @@
package photosapi package photosapi
import ( import (
"bytes"
"errors" "errors"
"fmt"
"io"
"mime"
"net/http"
"path/filepath"
"strings"
"github.com/dsoprea/go-exif/v3"
"github.com/gin-gonic/gin"
"github.com/jackc/pgconn"
"gorm.io/gorm"
) )
// Error // Error
@ -29,252 +17,252 @@ var (
ErrStoreMissingName = errors.New("name required") ErrStoreMissingName = errors.New("name required")
) )
// Service // // Service
var CHUNK_SIZE int64 = 4 << 20 // var CHUNK_SIZE int64 = 4 << 20
type FileRequest struct { // type FileRequest struct {
Name string `json:"name"` // Name string `json:"name"`
Checksum string `json:"checksum"` // Checksum string `json:"checksum"`
Chunks []string `json:"chunks"` // Chunks []string `json:"chunks"`
} // }
func (s *Service) FileCreate(c *gin.Context) { // func (s *Service) FileCreate(c *gin.Context) {
file := &FileRequest{} // file := &FileRequest{}
if c.BindJSON(file) != nil { // if c.BindJSON(file) != nil {
return // return
} // }
if len(file.Name) < 1 { // if len(file.Name) < 1 {
c.AbortWithError(http.StatusBadRequest, ErrStoreMissingName) // c.AbortWithError(http.StatusBadRequest, ErrStoreMissingName)
return // return
} // }
if len(file.Checksum) != 40 { // if len(file.Checksum) != 40 {
c.AbortWithError(http.StatusBadRequest, ErrStoreBadChecksum) // c.AbortWithError(http.StatusBadRequest, ErrStoreBadChecksum)
return // return
} // }
if len(file.Chunks) == 0 { // if len(file.Chunks) == 0 {
c.AbortWithError(http.StatusBadRequest, ErrStoreMissingChunks) // c.AbortWithError(http.StatusBadRequest, ErrStoreMissingChunks)
return // return
} // }
for _, chunk := range file.Chunks { // for _, chunk := range file.Chunks {
if len(chunk) != 40 { // if len(chunk) != 40 {
c.AbortWithError(http.StatusBadRequest, ErrStoreBadChecksum) // c.AbortWithError(http.StatusBadRequest, ErrStoreBadChecksum)
return // return
} // }
} // }
r, rs, err := s.Store.Combine(file.Chunks) // r, rs, err := s.Store.Combine(file.Chunks)
if err != nil { // if err != nil {
if strings.HasPrefix(err.Error(), "chunk") && strings.HasSuffix(err.Error(), "doesn't exists") { // if strings.HasPrefix(err.Error(), "chunk") && strings.HasSuffix(err.Error(), "doesn't exists") {
c.AbortWithError(http.StatusBadRequest, err) // c.AbortWithError(http.StatusBadRequest, err)
} else { // } else {
c.AbortWithError(http.StatusInternalServerError, err) // c.AbortWithError(http.StatusInternalServerError, err)
} // }
return // return
} // }
if r != file.Checksum { // if r != file.Checksum {
c.AbortWithError(http.StatusExpectationFailed, ErrStoreMismatchChecksum) // c.AbortWithError(http.StatusExpectationFailed, ErrStoreMismatchChecksum)
return // return
} // }
sess := s.CurrentSession(c) // sess := s.CurrentSession(c)
f := &File{ // f := &File{
Name: file.Name, // Name: file.Name,
Checksum: file.Checksum, // Checksum: file.Checksum,
Size: rs, // Size: rs,
AuthorId: &sess.AccountId, // AuthorId: &sess.AccountId,
} // }
err = s.DB.Transaction(func(tx *gorm.DB) error { // err = s.DB.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(f).Error; err != nil { // if err := tx.Create(f).Error; err != nil {
return err // return err
} // }
for i, chunk := range file.Chunks { // for i, chunk := range file.Chunks {
fc := &FileChunk{ // fc := &FileChunk{
FileId: f.ID, // FileId: f.ID,
Part: uint32(i + 1), // Part: uint32(i + 1),
Checksum: chunk, // Checksum: chunk,
} // }
if err := tx.Create(fc).Error; err != nil { // if err := tx.Create(fc).Error; err != nil {
return err // return err
} // }
} // }
return nil // return nil
}) // })
if nerr, ok := err.(*pgconn.PgError); ok { // if nerr, ok := err.(*pgconn.PgError); ok {
if nerr.Code == "23505" && nerr.Detail == fmt.Sprintf("Key (checksum)=(%s) already exists.", file.Checksum) { // if nerr.Code == "23505" && nerr.Detail == fmt.Sprintf("Key (checksum)=(%s) already exists.", file.Checksum) {
err = nil // err = nil
} // }
} // }
if err != nil { // if err != nil {
c.AbortWithError(http.StatusInternalServerError, err) // c.AbortWithError(http.StatusInternalServerError, err)
return // return
} // }
c.JSON(http.StatusOK, gin.H{ // c.JSON(http.StatusOK, gin.H{
"sum": file.Checksum, // "sum": file.Checksum,
"nbChunks": len(file.Chunks), // "nbChunks": len(file.Chunks),
"size": rs, // "size": rs,
}) // })
} // }
func (s *Service) FileCreateChunk(c *gin.Context) { // func (s *Service) FileCreateChunk(c *gin.Context) {
if c.Request.ContentLength > CHUNK_SIZE { // if c.Request.ContentLength > CHUNK_SIZE {
c.AbortWithError(http.StatusBadRequest, ErrStoreBadChunkSize) // c.AbortWithError(http.StatusBadRequest, ErrStoreBadChunkSize)
return // return
} // }
b := bytes.NewBuffer([]byte{}) // b := bytes.NewBuffer([]byte{})
io.Copy(b, c.Request.Body) // io.Copy(b, c.Request.Body)
sess := s.CurrentSession(c) // sess := s.CurrentSession(c)
chunk := s.Store.NewChunk(b.Bytes()) // chunk := s.Store.NewChunk(b.Bytes())
if err := chunk.Save(sess.Account.Login); err != nil { // if err := chunk.Save(sess.Account.Login); err != nil {
if errors.Is(err, ErrStoreChunkAlreadyExists) { // if errors.Is(err, ErrStoreChunkAlreadyExists) {
c.JSON(http.StatusOK, gin.H{ // c.JSON(http.StatusOK, gin.H{
"checksum": chunk.Sum, // "checksum": chunk.Sum,
}) // })
} else { // } else {
c.AbortWithError(http.StatusBadRequest, err) // c.AbortWithError(http.StatusBadRequest, err)
} // }
return // return
} // }
c.JSON(http.StatusOK, gin.H{ // c.JSON(http.StatusOK, gin.H{
"checksum": chunk.Sum, // "checksum": chunk.Sum,
}) // })
} // }
func (s *Service) FileChunkExists(c *gin.Context) { // func (s *Service) FileChunkExists(c *gin.Context) {
checksum := c.Param("checksum") // checksum := c.Param("checksum")
if len(checksum) != 40 { // if len(checksum) != 40 {
c.AbortWithError(http.StatusBadRequest, ErrStoreBadChecksum) // c.AbortWithError(http.StatusBadRequest, ErrStoreBadChecksum)
return // return
} // }
if s.Store.Chunk(checksum).FileExists() { // if s.Store.Chunk(checksum).FileExists() {
c.Status(http.StatusOK) // c.Status(http.StatusOK)
} else { // } else {
c.Status(http.StatusNotFound) // c.Status(http.StatusNotFound)
} // }
} // }
func (s *Service) FileExists(c *gin.Context) { // func (s *Service) FileExists(c *gin.Context) {
checksum := c.Param("checksum") // checksum := c.Param("checksum")
if len(checksum) != 40 { // if len(checksum) != 40 {
c.AbortWithError(http.StatusBadRequest, ErrStoreBadChecksum) // c.AbortWithError(http.StatusBadRequest, ErrStoreBadChecksum)
return // return
} // }
var fileExists int64 // var fileExists int64
if err := s.DB.Model(&File{}).Where("checksum = ?", checksum).Count(&fileExists).Error; err != nil { // if err := s.DB.Model(&File{}).Where("checksum = ?", checksum).Count(&fileExists).Error; err != nil {
c.AbortWithError(http.StatusInternalServerError, err) // c.AbortWithError(http.StatusInternalServerError, err)
return // return
} // }
if fileExists > 0 { // if fileExists > 0 {
c.Status(http.StatusOK) // c.Status(http.StatusOK)
} else { // } else {
c.Status(http.StatusNotFound) // c.Status(http.StatusNotFound)
} // }
} // }
func (s *Service) FileGet(c *gin.Context) { // func (s *Service) FileGet(c *gin.Context) {
checksum := c.Param("checksum") // checksum := c.Param("checksum")
if len(checksum) != 40 { // if len(checksum) != 40 {
c.AbortWithError(http.StatusBadRequest, ErrStoreBadChecksum) // c.AbortWithError(http.StatusBadRequest, ErrStoreBadChecksum)
return // return
} // }
if checksum == c.GetHeader("If-None-Match") { // if checksum == c.GetHeader("If-None-Match") {
c.Status(http.StatusNotModified) // c.Status(http.StatusNotModified)
return // return
} // }
f := &File{} // f := &File{}
if err := s.DB.Debug().Preload("Chunks").Where("checksum = ?", checksum).First(&f).Error; err != nil { // if err := s.DB.Debug().Preload("Chunks").Where("checksum = ?", checksum).First(&f).Error; err != nil {
c.AbortWithError(http.StatusBadRequest, err) // c.AbortWithError(http.StatusBadRequest, err)
return // return
} // }
chunks := make([]string, len(f.Chunks)) // chunks := make([]string, len(f.Chunks))
for _, fc := range f.Chunks { // for _, fc := range f.Chunks {
chunks[fc.Part-1] = fc.Checksum // chunks[fc.Part-1] = fc.Checksum
} // }
reader, err := s.Store.NewStoreReader(chunks) // reader, err := s.Store.NewStoreReader(chunks)
if err != nil { // if err != nil {
c.AbortWithError(http.StatusInternalServerError, err) // c.AbortWithError(http.StatusInternalServerError, err)
return // return
} // }
defer reader.Close() // defer reader.Close()
c.DataFromReader( // c.DataFromReader(
http.StatusOK, // http.StatusOK,
reader.Size, // reader.Size,
mime.TypeByExtension(filepath.Ext(f.Name)), // mime.TypeByExtension(filepath.Ext(f.Name)),
reader, // reader,
map[string]string{ // map[string]string{
"Content-Disposition": fmt.Sprintf("inline; filename=\"%s\"", f.Name), // "Content-Disposition": fmt.Sprintf("inline; filename=\"%s\"", f.Name),
"ETag": f.Checksum, // "ETag": f.Checksum,
}, // },
) // )
} // }
func (s *Service) FileAnalyze(c *gin.Context) { // func (s *Service) FileAnalyze(c *gin.Context) {
checksum := c.Param("checksum") // checksum := c.Param("checksum")
if len(checksum) != 40 { // if len(checksum) != 40 {
c.AbortWithError(http.StatusBadRequest, ErrStoreBadChecksum) // c.AbortWithError(http.StatusBadRequest, ErrStoreBadChecksum)
return // return
} // }
f := &File{} // f := &File{}
if err := s.DB.Debug().Preload("Chunks").Where("checksum = ?", checksum).First(&f).Error; err != nil { // if err := s.DB.Debug().Preload("Chunks").Where("checksum = ?", checksum).First(&f).Error; err != nil {
c.AbortWithError(http.StatusBadRequest, err) // c.AbortWithError(http.StatusBadRequest, err)
return // return
} // }
chunks := make([]string, len(f.Chunks)) // chunks := make([]string, len(f.Chunks))
for _, fc := range f.Chunks { // for _, fc := range f.Chunks {
chunks[fc.Part-1] = fc.Checksum // chunks[fc.Part-1] = fc.Checksum
} // }
reader, err := s.Store.NewStoreReader(chunks) // reader, err := s.Store.NewStoreReader(chunks)
if err != nil { // if err != nil {
c.AbortWithError(http.StatusInternalServerError, err) // c.AbortWithError(http.StatusInternalServerError, err)
return // return
} // }
defer reader.Close() // defer reader.Close()
rawExif, err := exif.SearchAndExtractExifWithReader(reader) // rawExif, err := exif.SearchAndExtractExifWithReader(reader)
if err != nil { // if err != nil {
c.AbortWithError(http.StatusInternalServerError, err) // c.AbortWithError(http.StatusInternalServerError, err)
return // return
} // }
entries, _, err := exif.GetFlatExifDataUniversalSearch(rawExif, nil, true) // entries, _, err := exif.GetFlatExifDataUniversalSearch(rawExif, nil, true)
if err != nil { // if err != nil {
c.AbortWithError(http.StatusInternalServerError, err) // c.AbortWithError(http.StatusInternalServerError, err)
return // return
} // }
c.JSON(http.StatusOK, gin.H{ // c.JSON(http.StatusOK, gin.H{
"exif": entries, // "exif": entries,
}) // })
} // }
func (s *Service) FileInit() { func (s *Service) FileInit() {
file := s.Gin.Group("/file") file := s.Gin.Group("/file")
file.Use(s.RequireSession) file.Use(s.RequireSession)
file.POST("", s.FileCreate) // file.POST("", s.FileCreate)
file.HEAD("/:checksum", s.FileExists) // file.HEAD("/:checksum", s.FileExists)
file.GET("/:checksum", s.FileGet) // file.GET("/:checksum", s.FileGet)
file.POST("/chunk", s.FileCreateChunk) // file.POST("/chunk", s.FileCreateChunk)
file.HEAD("/chunk/:checksum", s.FileChunkExists) // file.HEAD("/chunk/:checksum", s.FileChunkExists)
file.GET("/analyze/:checksum", s.FileAnalyze) // file.GET("/analyze/:checksum", s.FileAnalyze)
} }