diff --git a/cmd/photos-api-cli/upload.go b/cmd/photos-api-cli/upload.go index f7f7355..efd516b 100644 --- a/cmd/photos-api-cli/upload.go +++ b/cmd/photos-api-cli/upload.go @@ -12,7 +12,7 @@ import ( "github.com/go-resty/resty/v2" "github.com/schollz/progressbar/v3" - "gitlab.celogeek.com/photos/api/internal/photos/api" + photosapi "gitlab.celogeek.com/photos/api/internal/photos/api" ) type UploadCommand struct { @@ -82,8 +82,8 @@ func (c *UploadCommand) FileUpload(sum string) error { if err != nil { return err } - nbChunks := st.Size() / api.CHUNK_SIZE - if st.Size()%api.CHUNK_SIZE > 0 { + nbChunks := st.Size() / photosapi.CHUNK_SIZE + if st.Size()%photosapi.CHUNK_SIZE > 0 { nbChunks++ } @@ -105,7 +105,7 @@ func (c *UploadCommand) FileUpload(sum string) error { for w := uint32(0); w < c.Workers; w++ { go func(w uint32) { defer wg.Done() - b := make([]byte, api.CHUNK_SIZE) + b := make([]byte, photosapi.CHUNK_SIZE) for { mu.Lock() part := i diff --git a/cmd/photos-api/main.go b/cmd/photos-api/main.go index 2da9956..3ed65d5 100644 --- a/cmd/photos-api/main.go +++ b/cmd/photos-api/main.go @@ -6,7 +6,7 @@ import ( "log" "gitlab.celogeek.com/photos/api/internal/env" - "gitlab.celogeek.com/photos/api/internal/photos/api" + photosapi "gitlab.celogeek.com/photos/api/internal/photos/api" ) var ( @@ -23,8 +23,8 @@ var ( ) func main() { - config := &api.ServiceConfig{ - DB: &api.DBConfig{}, + config := &photosapi.ServiceConfig{ + DB: &photosapi.DBConfig{}, } flag.StringVar(&config.Listen, "listen", listen, "Listen address") @@ -52,7 +52,7 @@ func main() { return } - p := api.New(config) + p := photosapi.New(config) if err := p.Run(); err != nil { log.Fatal(err) diff --git a/internal/photos/api/account.go b/internal/photos/api/account.go index 512516f..6054640 100644 --- a/internal/photos/api/account.go +++ b/internal/photos/api/account.go @@ -1,16 +1,56 @@ -package api +package photosapi import ( + "crypto" + "encoding/base64" "errors" "net/http" + "time" "github.com/gin-gonic/gin" - photoserrors "gitlab.celogeek.com/photos/api/internal/photos/errors" - "gitlab.celogeek.com/photos/api/internal/photos/models" "gopkg.in/validator.v2" "gorm.io/gorm" ) +// Errors +var ( + ErrAccountExists = errors.New("account exists") + ErrAccountAuth = errors.New("login or password incorrect") +) + +// Model +type Account struct { + ID uint32 `gorm:"primary_key" json:"-"` + Login string `gorm:"unique;size:64;not null" json:"login"` + Password string `gorm:"-" json:"-"` + EncryptedPassword string `gorm:"size:44;not null" json:"-"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"-"` +} + +func (a *Account) BeforeCreate(tx *gorm.DB) error { + if a.EncryptedPassword == "" { + a.EncryptPassword() + } + return nil +} + +func (a *Account) EncryptPassword() { + sha1 := crypto.SHA256.New() + sha1.Write([]byte(a.Password)) + a.EncryptedPassword = base64.StdEncoding.EncodeToString(sha1.Sum(nil)) +} + +func NewAccount(login string, password string) *Account { + a := &Account{ + Login: login, + Password: password, + } + a.EncryptPassword() + return a +} + +// Service type SignupOrLoginRequest struct { Login string `validate:"min=3,max=40,regexp=^[a-zA-Z0-9]*$"` Password string `validate:"min=8,max=40"` @@ -29,15 +69,15 @@ func (s *Service) Signup(c *gin.Context) { } var accountExists int64 - if err := s.DB.Model(&models.Account{}).Where("login = ?", account.Login).Count(&accountExists).Error; err != nil { + if err := s.DB.Model(&Account{}).Where("login = ?", account.Login).Count(&accountExists).Error; err != nil { s.Error(c, http.StatusInternalServerError, err) return } if accountExists > 0 { - s.Error(c, http.StatusConflict, photoserrors.ErrAccountExists) + s.Error(c, http.StatusConflict, ErrAccountExists) return } - if err := s.DB.Create(models.NewAccount(account.Login, account.Password)).Error; err != nil { + if err := s.DB.Create(NewAccount(account.Login, account.Password)).Error; err != nil { s.Error(c, http.StatusConflict, err) return } @@ -55,10 +95,10 @@ func (s *Service) Login(c *gin.Context) { return } - session, err := models.NewSession(s.DB, account.Login, account.Password) + session, err := NewSession(s.DB, account.Login, account.Password) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - s.Error(c, http.StatusNotFound, photoserrors.ErrAccountAuth) + s.Error(c, http.StatusNotFound, ErrAccountAuth) } else { s.Error(c, http.StatusInternalServerError, err) } @@ -72,13 +112,13 @@ func (s *Service) Login(c *gin.Context) { } func (s *Service) Logout(c *gin.Context) { - res := s.DB.Where("token = ?", c.GetString("token")).Delete(&models.Session{}) + res := s.DB.Where("token = ?", c.GetString("token")).Delete(&Session{}) if res.Error != nil { s.Error(c, http.StatusInternalServerError, res.Error) return } if res.RowsAffected == 0 { - s.Error(c, http.StatusNotFound, photoserrors.ErrSessionNotFound) + s.Error(c, http.StatusNotFound, ErrSessionNotFound) return } c.JSON(http.StatusOK, gin.H{ diff --git a/internal/photos/api/check_body.go b/internal/photos/api/check_body.go index 6ae2ea2..4a63fd5 100644 --- a/internal/photos/api/check_body.go +++ b/internal/photos/api/check_body.go @@ -1,15 +1,19 @@ -package api +package photosapi import ( + "errors" "net/http" "github.com/gin-gonic/gin" - photoserrors "gitlab.celogeek.com/photos/api/internal/photos/errors" +) + +var ( + ErrReqMissingBody = errors.New("missing body") ) func (s *Service) RequireBody(c *gin.Context) { if c.Request.Method == "POST" && c.Request.ContentLength == 0 { - s.Error(c, http.StatusBadRequest, photoserrors.ErrReqMissingBody) + s.Error(c, http.StatusBadRequest, ErrReqMissingBody) return } } diff --git a/internal/photos/api/db.go b/internal/photos/api/db.go index 3e7f40f..4ebab3a 100644 --- a/internal/photos/api/db.go +++ b/internal/photos/api/db.go @@ -1,4 +1,4 @@ -package api +package photosapi import ( "fmt" @@ -6,7 +6,6 @@ import ( "os" "time" - "gitlab.celogeek.com/photos/api/internal/photos/models" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" @@ -33,10 +32,10 @@ func (d *DBConfig) DSN() string { func (s *Service) Migrate() { tx := s.DB - tx.AutoMigrate(&models.Account{}) - tx.AutoMigrate(&models.Session{}) - tx.AutoMigrate(&models.File{}) - tx.AutoMigrate(&models.FileChunk{}) + tx.AutoMigrate(&Account{}) + tx.AutoMigrate(&Session{}) + tx.AutoMigrate(&File{}) + tx.AutoMigrate(&FileChunk{}) } func (s *Service) DBConnect() { diff --git a/internal/photos/api/dump.go b/internal/photos/api/dump.go index e45bbac..94ee214 100644 --- a/internal/photos/api/dump.go +++ b/internal/photos/api/dump.go @@ -1,4 +1,4 @@ -package api +package photosapi import ( "bytes" diff --git a/internal/photos/api/errors.go b/internal/photos/api/errors.go index 92bc1f5..674f3d5 100644 --- a/internal/photos/api/errors.go +++ b/internal/photos/api/errors.go @@ -1,4 +1,4 @@ -package api +package photosapi import ( "github.com/gin-gonic/gin" diff --git a/internal/photos/api/file.go b/internal/photos/api/file.go index d9430fa..0db0bc8 100644 --- a/internal/photos/api/file.go +++ b/internal/photos/api/file.go @@ -1,4 +1,4 @@ -package api +package photosapi import ( "bytes" @@ -9,47 +9,82 @@ import ( "net/http" "path/filepath" "strings" + "time" "github.com/dsoprea/go-exif/v3" "github.com/gin-gonic/gin" "github.com/jackc/pgconn" - photoserrors "gitlab.celogeek.com/photos/api/internal/photos/errors" - "gitlab.celogeek.com/photos/api/internal/photos/models" "gorm.io/gorm" ) +// Error +var ( + // Store + ErrStoreBadChecksum = errors.New("checksum should be sha1 in hex format") + ErrStoreBadChunkSize = errors.New("part file size should be 1MB max") + ErrStoreMissingChunks = errors.New("part checksum missing") + ErrStoreWrongChecksum = errors.New("wrong checksum") + ErrStoreMismatchChecksum = errors.New("part files doesn't match the original checksum") + ErrStoreAlreadyExists = errors.New("original file already exists") + ErrStoreChunkAlreadyExists = errors.New("chunk file already exists") + ErrStoreMissingName = errors.New("name required") +) + +// Model +type File struct { + ID uint32 `gorm:"primary_key" json:"id"` + Name string `gorm:"not null" json:"name"` + Checksum string `gorm:"unique;size:44;not null"` + Size uint64 `gorm:"not null"` + Author *Account `gorm:"constraint:OnDelete:SET NULL,OnUpdate:CASCADE" json:"author"` + AuthorId *uint32 `json:"-"` + Chunks []*FileChunk `gorm:"constraint:OnDelete:CASCADE,OnUpdate:CASCADE"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type FileChunk struct { + FileId uint32 + File *File `gorm:"constraint:OnDelete:CASCADE,OnUpdate:CASCADE"` + Part uint32 + Checksum string `gorm:"unique;size:44;not null"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +// Service var CHUNK_SIZE int64 = 4 << 20 -type File struct { +type FileRequest struct { Name string `json:"name"` Checksum string `json:"checksum"` Chunks []string `json:"chunks"` } func (s *Service) FileCreate(c *gin.Context) { - file := &File{} + file := &FileRequest{} if err := c.ShouldBindJSON(file); err != nil { s.Error(c, http.StatusInternalServerError, err) return } if len(file.Name) < 1 { - s.Error(c, http.StatusBadRequest, photoserrors.ErrStoreMissingName) + s.Error(c, http.StatusBadRequest, ErrStoreMissingName) return } if len(file.Checksum) != 40 { - s.Error(c, http.StatusBadRequest, photoserrors.ErrStoreBadChecksum) + s.Error(c, http.StatusBadRequest, ErrStoreBadChecksum) return } if len(file.Chunks) == 0 { - s.Error(c, http.StatusBadRequest, photoserrors.ErrStoreMissingChunks) + s.Error(c, http.StatusBadRequest, ErrStoreMissingChunks) return } for _, chunk := range file.Chunks { if len(chunk) != 40 { - s.Error(c, http.StatusBadRequest, photoserrors.ErrStoreBadChecksum) + s.Error(c, http.StatusBadRequest, ErrStoreBadChecksum) return } } @@ -65,13 +100,13 @@ func (s *Service) FileCreate(c *gin.Context) { } if r != file.Checksum { - s.Error(c, http.StatusExpectationFailed, photoserrors.ErrStoreMismatchChecksum) + s.Error(c, http.StatusExpectationFailed, ErrStoreMismatchChecksum) return } sess := s.CurrentSession(c) - f := &models.File{ + f := &File{ Name: file.Name, Checksum: file.Checksum, Size: rs, @@ -83,7 +118,7 @@ func (s *Service) FileCreate(c *gin.Context) { return err } for i, chunk := range file.Chunks { - fc := &models.FileChunk{ + fc := &FileChunk{ FileId: f.ID, Part: uint32(i + 1), Checksum: chunk, @@ -116,7 +151,7 @@ func (s *Service) FileCreate(c *gin.Context) { func (s *Service) FileCreateChunk(c *gin.Context) { if c.Request.ContentLength > CHUNK_SIZE { - s.Error(c, http.StatusBadRequest, photoserrors.ErrStoreBadChunkSize) + s.Error(c, http.StatusBadRequest, ErrStoreBadChunkSize) return } @@ -127,8 +162,8 @@ func (s *Service) FileCreateChunk(c *gin.Context) { sess := s.CurrentSession(c) chunk := s.Store.NewChunk(b.Bytes()) - if err := chunk.Save(sess); err != nil { - if errors.Is(err, photoserrors.ErrStoreChunkAlreadyExists) { + if err := chunk.Save(sess.Account.Login); err != nil { + if errors.Is(err, ErrStoreChunkAlreadyExists) { c.JSON(http.StatusOK, gin.H{ "status": "success", "checksum": chunk.Sum, @@ -148,7 +183,7 @@ func (s *Service) FileCreateChunk(c *gin.Context) { func (s *Service) FileChunkExists(c *gin.Context) { checksum := c.Param("checksum") if len(checksum) != 40 { - s.Error(c, http.StatusBadRequest, photoserrors.ErrStoreBadChecksum) + s.Error(c, http.StatusBadRequest, ErrStoreBadChecksum) return } @@ -162,12 +197,12 @@ func (s *Service) FileChunkExists(c *gin.Context) { func (s *Service) FileExists(c *gin.Context) { checksum := c.Param("checksum") if len(checksum) != 40 { - s.Error(c, http.StatusBadRequest, photoserrors.ErrStoreBadChecksum) + s.Error(c, http.StatusBadRequest, ErrStoreBadChecksum) return } var fileExists int64 - if err := s.DB.Model(&models.File{}).Where("checksum = ?", checksum).Count(&fileExists).Error; err != nil { + if err := s.DB.Model(&File{}).Where("checksum = ?", checksum).Count(&fileExists).Error; err != nil { s.Error(c, http.StatusInternalServerError, err) return } @@ -182,7 +217,7 @@ func (s *Service) FileExists(c *gin.Context) { func (s *Service) FileGet(c *gin.Context) { checksum := c.Param("checksum") if len(checksum) != 40 { - s.Error(c, http.StatusBadRequest, photoserrors.ErrStoreBadChecksum) + s.Error(c, http.StatusBadRequest, ErrStoreBadChecksum) return } if checksum == c.GetHeader("If-None-Match") { @@ -190,7 +225,7 @@ func (s *Service) FileGet(c *gin.Context) { return } - f := &models.File{} + f := &File{} if err := s.DB.Debug().Preload("Chunks").Where("checksum = ?", checksum).First(&f).Error; err != nil { s.Error(c, http.StatusBadRequest, err) return @@ -223,11 +258,11 @@ func (s *Service) FileGet(c *gin.Context) { func (s *Service) FileAnalyze(c *gin.Context) { checksum := c.Param("checksum") if len(checksum) != 40 { - s.Error(c, http.StatusBadRequest, photoserrors.ErrStoreBadChecksum) + s.Error(c, http.StatusBadRequest, ErrStoreBadChecksum) return } - f := &models.File{} + f := &File{} if err := s.DB.Debug().Preload("Chunks").Where("checksum = ?", checksum).First(&f).Error; err != nil { s.Error(c, http.StatusBadRequest, err) return diff --git a/internal/photos/api/logger.go b/internal/photos/api/logger.go index 20c3123..8622d5c 100644 --- a/internal/photos/api/logger.go +++ b/internal/photos/api/logger.go @@ -1,4 +1,4 @@ -package api +package photosapi import ( "fmt" diff --git a/internal/photos/api/main.go b/internal/photos/api/main.go index 05da661..1dac4be 100644 --- a/internal/photos/api/main.go +++ b/internal/photos/api/main.go @@ -1,7 +1,8 @@ -package api +package photosapi import ( "context" + "errors" "math/rand" "net/http" "os" @@ -9,16 +10,20 @@ import ( "time" "github.com/gin-gonic/gin" - photoserrors "gitlab.celogeek.com/photos/api/internal/photos/errors" - "gitlab.celogeek.com/photos/api/internal/photos/store" + photosstore "gitlab.celogeek.com/photos/api/internal/photos/store" "gorm.io/gorm" ) +var ( + ErrReqNotFound = errors.New("this route doesn't exists") + ErrStorePathNotADirectory = errors.New("store path is not a directory") +) + type Service struct { Gin *gin.Engine DB *gorm.DB Config *ServiceConfig - Store *store.Store + Store *photosstore.Store Storage *Storage LogOk *Logger LogErr *Logger @@ -34,7 +39,7 @@ func New(config *ServiceConfig) *Service { return &Service{ Gin: gin.New(), Config: config, - Store: &store.Store{Path: config.StorePath}, + Store: &photosstore.Store{Path: config.StorePath}, Storage: &Storage{Path: config.StorePath}, LogOk: &Logger{os.Stdout, "Photos"}, LogErr: &Logger{os.Stderr, "Photos"}, @@ -54,7 +59,7 @@ func (s *Service) SetupRoutes() { s.UploadInit() s.Gin.NoRoute(func(c *gin.Context) { - s.Error(c, http.StatusNotFound, photoserrors.ErrReqNotFound) + s.Error(c, http.StatusNotFound, ErrReqNotFound) }) } @@ -64,7 +69,7 @@ func (s *Service) PrepareStore() { s.LogErr.Fatal("Store", err) } if !d.IsDir() { - s.LogErr.Fatal("Store", photoserrors.ErrStorePathNotADirectory) + s.LogErr.Fatal("Store", ErrStorePathNotADirectory) } } diff --git a/internal/photos/api/me.go b/internal/photos/api/me.go index ace2b0f..774d8d5 100644 --- a/internal/photos/api/me.go +++ b/internal/photos/api/me.go @@ -1,4 +1,4 @@ -package api +package photosapi import ( "net/http" diff --git a/internal/photos/api/recovery.go b/internal/photos/api/recovery.go index bedebab..db2ce6d 100644 --- a/internal/photos/api/recovery.go +++ b/internal/photos/api/recovery.go @@ -1,17 +1,23 @@ -package api +package photosapi import ( + "errors" "net/http" "github.com/gin-gonic/gin" - photoserrors "gitlab.celogeek.com/photos/api/internal/photos/errors" ) +// Errors +var ( + ErrUnexpected = errors.New("an unexpected error occur") +) + +// Service func (s *Service) Recovery(c *gin.Context) { defer func() { if err := recover(); err != nil { s.LogErr.Print("PANIC", err) - s.Error(c, http.StatusInternalServerError, photoserrors.ErrUnexpected) + s.Error(c, http.StatusInternalServerError, ErrUnexpected) } }() c.Next() diff --git a/internal/photos/api/session.go b/internal/photos/api/session.go index 4db4547..53ff28e 100644 --- a/internal/photos/api/session.go +++ b/internal/photos/api/session.go @@ -1,4 +1,4 @@ -package api +package photosapi import ( "errors" @@ -7,25 +7,68 @@ import ( "time" "github.com/gin-gonic/gin" - photoserrors "gitlab.celogeek.com/photos/api/internal/photos/errors" - "gitlab.celogeek.com/photos/api/internal/photos/models" + "github.com/google/uuid" "gorm.io/gorm" ) +// Errors +var ( + ErrSessionNotFound = errors.New("session not found") + ErrSessionInvalid = errors.New("session invalid") + ErrTokenMissing = errors.New("token missing") +) + +// Model +type Session struct { + ID uint32 `gorm:"primary_key"` + Token string `gorm:"size:36"` + Account *Account `gorm:"constraint:OnDelete:CASCADE,OnUpdate:CASCADE"` + AccountId uint32 `gorm:"not null"` + CreatedAt time.Time + UpdatedAt time.Time +} + +func (s *Session) BeforeCreate(tx *gorm.DB) error { + uuid, err := uuid.NewRandom() + if err != nil { + return err + } + s.Token = uuid.String() + return nil +} + +func NewSession(tx *gorm.DB, login string, password string) (*Session, error) { + account := NewAccount(login, password) + if err := tx.Where( + "login = ? and encrypted_password = ?", + account.Login, + account.EncryptedPassword, + ).First(account).Error; err != nil { + return nil, err + } + + session := &Session{Account: account} + if err := tx.Create(session).Error; err != nil { + return nil, err + } + return session, nil +} + +// Service func (s *Service) RequireAuthToken(c *gin.Context) { tokenAuth := c.GetHeader("Authorization") tokenCookie, _ := c.Cookie("photoapitoken") if tokenAuth != "" { if !strings.HasPrefix(tokenAuth, "Private ") { - s.Error(c, http.StatusForbidden, photoserrors.ErrTokenMissing) + s.Error(c, http.StatusForbidden, ErrTokenMissing) } else { c.Set("token", tokenAuth[8:]) } } else if tokenCookie != "" { c.Set("token", tokenCookie) } else { - s.Error(c, http.StatusForbidden, photoserrors.ErrTokenMissing) + s.Error(c, http.StatusForbidden, ErrTokenMissing) } } @@ -35,17 +78,17 @@ func (s *Service) RequireSession(c *gin.Context) { return } - sess := &models.Session{} + sess := &Session{} if err := s.DB.Preload("Account").Where("token = ?", c.GetString("token")).First(sess).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - s.Error(c, http.StatusForbidden, photoserrors.ErrSessionNotFound) + s.Error(c, http.StatusForbidden, ErrSessionNotFound) } else { s.Error(c, http.StatusForbidden, err) } return } if sess.Account == nil { - s.Error(c, http.StatusInternalServerError, photoserrors.ErrSessionInvalid) + s.Error(c, http.StatusInternalServerError, ErrSessionInvalid) return } s.DB.Select("updated_at").Save(sess) @@ -53,15 +96,15 @@ func (s *Service) RequireSession(c *gin.Context) { c.Set("session", sess) } -func (s *Service) CurrentSession(c *gin.Context) *models.Session { - return c.MustGet("session").(*models.Session) +func (s *Service) CurrentSession(c *gin.Context) *Session { + return c.MustGet("session").(*Session) } func (s *Service) SessionCleaner() { for range time.Tick(time.Minute) { t := time.Now().UTC().Add(-3 * time.Hour).Truncate(time.Minute) s.LogOk.Printf("Session", "Cleaning old session < %s", t) - if err := s.DB.Where("updated_at < ?", t).Delete(&models.Session{}).Error; err != nil { + if err := s.DB.Where("updated_at < ?", t).Delete(&Session{}).Error; err != nil { s.LogErr.Printf("Session", "Cleaning failed: %s", err) } } diff --git a/internal/photos/api/storage.go b/internal/photos/api/storage.go index 1ec2197..13d31cc 100644 --- a/internal/photos/api/storage.go +++ b/internal/photos/api/storage.go @@ -1,4 +1,4 @@ -package api +package photosapi import ( "fmt" diff --git a/internal/photos/api/upload.go b/internal/photos/api/upload.go index 26592c1..5880efc 100644 --- a/internal/photos/api/upload.go +++ b/internal/photos/api/upload.go @@ -1,4 +1,4 @@ -package api +package photosapi import ( "crypto/sha256" diff --git a/internal/photos/errors/accounts.go b/internal/photos/errors/accounts.go deleted file mode 100644 index e1a277e..0000000 --- a/internal/photos/errors/accounts.go +++ /dev/null @@ -1,9 +0,0 @@ -package photoserrors - -import "errors" - -var ( - // Account - ErrAccountExists = errors.New("account exists") - ErrAccountAuth = errors.New("login or password incorrect") -) diff --git a/internal/photos/errors/albums.go b/internal/photos/errors/albums.go deleted file mode 100644 index 1cf28b4..0000000 --- a/internal/photos/errors/albums.go +++ /dev/null @@ -1,9 +0,0 @@ -package photoserrors - -import "errors" - -var ( - - // Album - ErrAlbumDontExists = errors.New("album doesn't exists") -) diff --git a/internal/photos/errors/panic.go b/internal/photos/errors/panic.go deleted file mode 100644 index 082b5a0..0000000 --- a/internal/photos/errors/panic.go +++ /dev/null @@ -1,9 +0,0 @@ -package photoserrors - -import "errors" - -var ( - - // Panic - ErrUnexpected = errors.New("an unexpected error occur") -) diff --git a/internal/photos/errors/requests.go b/internal/photos/errors/requests.go deleted file mode 100644 index 5ff6913..0000000 --- a/internal/photos/errors/requests.go +++ /dev/null @@ -1,9 +0,0 @@ -package photoserrors - -import "errors" - -var ( - // Request - ErrReqMissingBody = errors.New("missing body") - ErrReqNotFound = errors.New("this route doesn't exists") -) diff --git a/internal/photos/errors/session.go b/internal/photos/errors/session.go deleted file mode 100644 index 353f868..0000000 --- a/internal/photos/errors/session.go +++ /dev/null @@ -1,10 +0,0 @@ -package photoserrors - -import "errors" - -var ( - // Session - ErrSessionNotFound = errors.New("session not found") - ErrSessionInvalid = errors.New("session invalid") - ErrTokenMissing = errors.New("token missing") -) diff --git a/internal/photos/errors/store.go b/internal/photos/errors/store.go deleted file mode 100644 index 22ec2a1..0000000 --- a/internal/photos/errors/store.go +++ /dev/null @@ -1,16 +0,0 @@ -package photoserrors - -import "errors" - -var ( - // Store - ErrStorePathNotADirectory = errors.New("store path is not a directory") - ErrStoreBadChecksum = errors.New("checksum should be sha1 in hex format") - ErrStoreBadChunkSize = errors.New("part file size should be 1MB max") - ErrStoreMissingChunks = errors.New("part checksum missing") - ErrStoreWrongChecksum = errors.New("wrong checksum") - ErrStoreMismatchChecksum = errors.New("part files doesn't match the original checksum") - ErrStoreAlreadyExists = errors.New("original file already exists") - ErrStoreChunkAlreadyExists = errors.New("chunk file already exists") - ErrStoreMissingName = errors.New("name required") -) diff --git a/internal/photos/models/account.go b/internal/photos/models/account.go deleted file mode 100644 index f7b0f6f..0000000 --- a/internal/photos/models/account.go +++ /dev/null @@ -1,40 +0,0 @@ -package models - -import ( - "crypto" - "encoding/base64" - "time" - - "gorm.io/gorm" -) - -type Account struct { - ID uint32 `gorm:"primary_key" json:"-"` - Login string `gorm:"unique;size:64;not null" json:"login"` - Password string `gorm:"-" json:"-"` - EncryptedPassword string `gorm:"size:44;not null" json:"-"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"-"` -} - -func (a *Account) BeforeCreate(tx *gorm.DB) error { - if a.EncryptedPassword == "" { - a.EncryptPassword() - } - return nil -} - -func (a *Account) EncryptPassword() { - sha1 := crypto.SHA256.New() - sha1.Write([]byte(a.Password)) - a.EncryptedPassword = base64.StdEncoding.EncodeToString(sha1.Sum(nil)) -} - -func NewAccount(login string, password string) *Account { - a := &Account{ - Login: login, - Password: password, - } - a.EncryptPassword() - return a -} diff --git a/internal/photos/models/file.go b/internal/photos/models/file.go deleted file mode 100644 index aa2ac4b..0000000 --- a/internal/photos/models/file.go +++ /dev/null @@ -1,15 +0,0 @@ -package models - -import "time" - -type File struct { - ID uint32 `gorm:"primary_key" json:"id"` - Name string `gorm:"not null" json:"name"` - Checksum string `gorm:"unique;size:44;not null"` - Size uint64 `gorm:"not null"` - Author *Account `gorm:"constraint:OnDelete:SET NULL,OnUpdate:CASCADE" json:"author"` - AuthorId *uint32 `json:"-"` - Chunks []*FileChunk `gorm:"constraint:OnDelete:CASCADE,OnUpdate:CASCADE"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} diff --git a/internal/photos/models/filechunk.go b/internal/photos/models/filechunk.go deleted file mode 100644 index 92009a9..0000000 --- a/internal/photos/models/filechunk.go +++ /dev/null @@ -1,12 +0,0 @@ -package models - -import "time" - -type FileChunk struct { - FileId uint32 - File *File `gorm:"constraint:OnDelete:CASCADE,OnUpdate:CASCADE"` - Part uint32 - Checksum string `gorm:"unique;size:44;not null"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} diff --git a/internal/photos/models/session.go b/internal/photos/models/session.go deleted file mode 100644 index c81f957..0000000 --- a/internal/photos/models/session.go +++ /dev/null @@ -1,43 +0,0 @@ -package models - -import ( - "time" - - "github.com/google/uuid" - "gorm.io/gorm" -) - -type Session struct { - ID uint32 `gorm:"primary_key"` - Token string `gorm:"size:36"` - Account *Account `gorm:"constraint:OnDelete:CASCADE,OnUpdate:CASCADE"` - AccountId uint32 `gorm:"not null"` - CreatedAt time.Time - UpdatedAt time.Time -} - -func (s *Session) BeforeCreate(tx *gorm.DB) error { - uuid, err := uuid.NewRandom() - if err != nil { - return err - } - s.Token = uuid.String() - return nil -} - -func NewSession(tx *gorm.DB, login string, password string) (*Session, error) { - account := NewAccount(login, password) - if err := tx.Where( - "login = ? and encrypted_password = ?", - account.Login, - account.EncryptedPassword, - ).First(account).Error; err != nil { - return nil, err - } - - session := &Session{Account: account} - if err := tx.Create(session).Error; err != nil { - return nil, err - } - return session, nil -} diff --git a/internal/photos/store/core.go b/internal/photos/store/core.go index 1748e16..ea86001 100644 --- a/internal/photos/store/core.go +++ b/internal/photos/store/core.go @@ -1,4 +1,4 @@ -package store +package photosstore import ( "crypto/sha1" @@ -11,8 +11,10 @@ import ( "time" "github.com/gin-gonic/gin" - photoserrors "gitlab.celogeek.com/photos/api/internal/photos/errors" - "gitlab.celogeek.com/photos/api/internal/photos/models" +) + +var ( + ErrStoreChunkAlreadyExists = errors.New("chunk file already exists") ) type Store struct { @@ -78,9 +80,9 @@ func (c *Chunk) Mkdir() error { return os.MkdirAll(c.Dir(), 0755) } -func (c *Chunk) Save(sess *models.Session) error { +func (c *Chunk) Save(login string) error { if c.FileExists() { - return photoserrors.ErrStoreChunkAlreadyExists + return ErrStoreChunkAlreadyExists } if err := c.Mkdir(); err != nil { @@ -98,7 +100,7 @@ func (c *Chunk) Save(sess *models.Session) error { enc := json.NewEncoder(meta) enc.SetIndent("", " ") return enc.Encode(gin.H{ - "author": sess.Account.Login, + "author": login, "date": time.Now().UTC().Format("2006-01-02 15:04:05"), "checksum": c.Sum, "size": len(c.Bytes), diff --git a/internal/photos/store/reader.go b/internal/photos/store/reader.go index 2961851..772ef4e 100644 --- a/internal/photos/store/reader.go +++ b/internal/photos/store/reader.go @@ -1,10 +1,13 @@ -package store +package photosstore import ( + "errors" "io" "os" +) - photoserrors "gitlab.celogeek.com/photos/api/internal/photos/errors" +var ( + ErrStoreMissingChunks = errors.New("part checksum missing") ) type StoreReaderChunk struct { @@ -26,7 +29,7 @@ func (s *Store) NewStoreReader(chunks []string) (*StoreReader, error) { name := c.Filename() size := c.Size() if size < 0 { - return nil, photoserrors.ErrStoreMissingChunks + return nil, ErrStoreMissingChunks } sr.chunks[i] = StoreReaderChunk{name, size} sr.Size += size