reorganize package by logic

This commit is contained in:
celogeek 2022-05-02 09:35:49 +02:00
parent 4fbe92cdcf
commit 656440c4b9
Signed by: celogeek
GPG Key ID: E6B7BDCFC446233A
27 changed files with 222 additions and 257 deletions

View File

@ -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

View File

@ -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)

View File

@ -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{

View File

@ -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
}
}

View File

@ -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() {

View File

@ -1,4 +1,4 @@
package api
package photosapi
import (
"bytes"

View File

@ -1,4 +1,4 @@
package api
package photosapi
import (
"github.com/gin-gonic/gin"

View File

@ -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

View File

@ -1,4 +1,4 @@
package api
package photosapi
import (
"fmt"

View File

@ -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)
}
}

View File

@ -1,4 +1,4 @@
package api
package photosapi
import (
"net/http"

View File

@ -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()

View File

@ -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)
}
}

View File

@ -1,4 +1,4 @@
package api
package photosapi
import (
"fmt"

View File

@ -1,4 +1,4 @@
package api
package photosapi
import (
"crypto/sha256"

View File

@ -1,9 +0,0 @@
package photoserrors
import "errors"
var (
// Account
ErrAccountExists = errors.New("account exists")
ErrAccountAuth = errors.New("login or password incorrect")
)

View File

@ -1,9 +0,0 @@
package photoserrors
import "errors"
var (
// Album
ErrAlbumDontExists = errors.New("album doesn't exists")
)

View File

@ -1,9 +0,0 @@
package photoserrors
import "errors"
var (
// Panic
ErrUnexpected = errors.New("an unexpected error occur")
)

View File

@ -1,9 +0,0 @@
package photoserrors
import "errors"
var (
// Request
ErrReqMissingBody = errors.New("missing body")
ErrReqNotFound = errors.New("this route doesn't exists")
)

View File

@ -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")
)

View File

@ -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")
)

View File

@ -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
}

View File

@ -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"`
}

View File

@ -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"`
}

View File

@ -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
}

View File

@ -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),

View File

@ -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