From 2666b76b44f30d45f3e25e238ce9c47e2a8f1ed1 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 17 Aug 2025 23:40:34 +0300 Subject: [PATCH] feat (api): migrate to UUIDs. Not fully debugged, may contain bugs --- apps/api/censor.go | 4 +- apps/api/dev_server.go | 153 +++++++++++++------------ apps/api/reaper/main.go | 3 +- apps/api/schemas/db.go | 142 +++++++++++++++++++---- apps/book-reaper/go.mod | 1 + apps/book-reaper/go.sum | 2 + apps/book-reaper/internal/app/app.go | 3 +- apps/book-reaper/pkg/reaper/main.go | 3 +- apps/book-reaper/pkg/reaper/schemas.go | 116 +++++++++++++++---- 9 files changed, 306 insertions(+), 121 deletions(-) diff --git a/apps/api/censor.go b/apps/api/censor.go index 530c291..caade51 100644 --- a/apps/api/censor.go +++ b/apps/api/censor.go @@ -39,10 +39,10 @@ func GenWhitelist(db *gorm.DB, authorsFilePath string) { continue } if firstLine { - whitelist.WriteString(strconv.FormatUint(book.ID, 10)) + whitelist.WriteString(book.ID.String()) firstLine = false } else { - whitelist.WriteString("\n" + strconv.FormatUint(book.ID, 10)) + whitelist.WriteString("\n" + book.ID.String()) } } } diff --git a/apps/api/dev_server.go b/apps/api/dev_server.go index 079804f..2308d73 100644 --- a/apps/api/dev_server.go +++ b/apps/api/dev_server.go @@ -11,6 +11,7 @@ import ( "encoding/pem" "encoding/xml" "errors" + "fmt" "image" "image/color" "image/draw" @@ -52,11 +53,11 @@ type Search struct { Books []APIBook `json:"books"` } type Person struct { - Id int `json:"id"` - Name string `json:"name"` - FirstName string `json:"firstName"` - MiddleName *string `json:"middleName"` - LastName *string `json:"lastName"` + Id uuid.UUID `json:"id"` + Name string `json:"name"` + FirstName string `json:"firstName"` + MiddleName *string `json:"middleName"` + LastName *string `json:"lastName"` } type Books struct { Title string `json:"title"` @@ -80,8 +81,8 @@ type Books struct { Filetype string `json:"filetype"` } type ShortAPICollection struct { - ID uint `json:"id"` - Name string `json:"name"` + ID uuid.UUID `json:"id"` + Name string `json:"name"` } func (ShortAPICollection) TableName() string { @@ -159,12 +160,12 @@ func AuthMiddleware(publicKey *rsa.PublicKey) gin.HandlerFunc { ctx.AbortWithStatus(401) return } - userIdUint, err := strconv.ParseUint(userId, 10, 64) + userIdUUID, err := uuid.Parse(userId) if err != nil { ctx.AbortWithStatus(401) return } - ctx.Set("id", uint(userIdUint)) + ctx.Set("id", userIdUUID.String()) ctx.Set("scopes", claims.Scopes) ctx.Next() } @@ -202,12 +203,12 @@ type Claims struct { jwt.RegisteredClaims } -func GenerateJWT(scopes string, userId uint64, priv *rsa.PrivateKey) (string, error) { +func GenerateJWT(scopes string, userId uuid.UUID, priv *rsa.PrivateKey) (string, error) { claims := &Claims{ Scopes: scopes, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 72)), - Subject: strconv.FormatUint(userId, 10), + Subject: userId.String(), }, } token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) @@ -304,22 +305,22 @@ func main() { &schemas.Permission{}, ) log.Println("create indexes") - db.Exec("CREATE VIRTUAL TABLE IF NOT EXISTS books_indices USING fts5(title, content='books', content_rowid='id');") + db.Exec("CREATE VIRTUAL TABLE IF NOT EXISTS books_indices USING fts5(book_id, title);") db.Exec(` CREATE TRIGGER IF NOT EXISTS books_ai AFTER INSERT ON books BEGIN - INSERT INTO books_indices(rowid, title) VALUES (new.id, new.title); + INSERT INTO books_indices(book_id, title) VALUES (new.id, new.title); END; `) db.Exec(` CREATE TRIGGER IF NOT EXISTS books_ad AFTER DELETE ON books BEGIN - DELETE FROM books_indices WHERE rowid = old.id; + DELETE FROM books_indices WHERE book_id = old.id; END; `) db.Exec(` CREATE TRIGGER IF NOT EXISTS books_au AFTER UPDATE ON books BEGIN - UPDATE books_indices SET title = new.title WHERE rowid = old.id; + UPDATE books_indices SET title = new.title WHERE book_id = old.id; END; `) log.Println("migrated!") @@ -357,10 +358,9 @@ func main() { var books []APIBook q, isTitle := c.GetQuery("q") author, isAuthor := c.GetQuery("author") - collectionStr, isCollection := c.GetQuery("collection") + collection, isCollection := c.GetQuery("collection") limitStr, _ := c.GetQuery("limit") limit, _ := strconv.ParseInt(limitStr, 10, 64) - collection, _ := strconv.ParseInt(collectionStr, 10, 64) offsetStr, _ := c.GetQuery("offset") offset, _ := strconv.ParseInt(offsetStr, 10, 64) _, _ = author, q @@ -370,10 +370,12 @@ func main() { Table("books"). Joins("JOIN collection_books ON collection_books.book_id = books.id"). Joins("JOIN collections ON collections.id = collection_books.collection_id") - if len(strings.Split(collectionStr, "-")) > 1 { // if collection token + if strings.Split(collection, "-")[0] == "pub" { // if collection token + fmt.Println("collection link") query = query. - Where("collections.link = ?", collectionStr) + Where("collections.link = ?", collection) } else { + fmt.Println("collection") query = query. Where("collections.id = ?", collection) } @@ -391,7 +393,7 @@ func main() { query = db. Find(&BooksIndex{}). Where("books_indices.title MATCH ?", q+"*"). - Joins("JOIN books ON books.id=books_indices.rowid"). + Joins("JOIN books ON books.id=books_indices.book_id"). Order("rank") } else if isAuthor && !isTitle { query = db. @@ -430,12 +432,11 @@ func main() { }) }) sec.GET("/author/:id", func(ctx *gin.Context) { - authorID, err := strconv.Atoi(ctx.Param("id")) + authorID, err := uuid.Parse(ctx.Param("id")) if err != nil { ctx.AbortWithError(400, err) return } - _ = authorID var authorObj schemas.Author db.Find(&authorObj, "id = ?", authorID) authorName := personToString(authorObj.FirstName, authorObj.MiddleName, authorObj.LastName) @@ -458,7 +459,7 @@ func main() { var authors []Person for _, author := range book.Authors { authors = append(authors, Person{ - Id: int(author.ID), + Id: author.ID, Name: personToString(author.FirstName, author.MiddleName, author.LastName), FirstName: author.FirstName, MiddleName: author.MiddleName, @@ -473,7 +474,7 @@ func main() { translators = &newSlice } *translators = append(*translators, Person{ - Id: int(translator.ID), + Id: translator.ID, Name: personToString(translator.FirstName, translator.MiddleName, translator.LastName), }) } @@ -492,7 +493,7 @@ func main() { genreName = *genre.Name } *genres = append(*genres, Person{ - Id: int(genre.ID), + Id: genre.ID, Name: genreName, }) } @@ -760,7 +761,7 @@ func main() { ctx.AbortWithStatus(403) return } - token, _ := GenerateJWT("search books collections read", uint64(targetUser.ID), priv) + token, _ := GenerateJWT("search books collections read", targetUser.ID, priv) ctx.SetCookie("token", token, 60*60*72, "/", "", false, false) }) sec.GET("/ping", func(ctx *gin.Context) { @@ -875,10 +876,11 @@ func main() { ctx.Data(200, "image/webp", decodedImage) }) sec.GET("/user", func(ctx *gin.Context) { - userId := ctx.MustGet("id").(uint) - if userId == 0 { + userId := ctx.MustGet("id").(string) + userIdUUID := uuid.MustParse(userId) + if userIdUUID == uuid.Nil { ctx.JSON(200, APIUser{ - ID: 0, + ID: uuid.Nil, Username: "guest", Name: "Гость", }) @@ -889,31 +891,33 @@ func main() { ctx.JSON(200, user) }) sec.POST("/user/collection", func(ctx *gin.Context) { - userId := ctx.MustGet("id").(uint) + userId := ctx.MustGet("id").(string) + userIdUUID := uuid.MustParse(userId) var requestBody struct { Link string } ctx.BindJSON(&requestBody) var collection schemas.Collection db.Where("link = ?", requestBody.Link).Take(&collection) - if collection.ID == 0 { + if collection.ID == uuid.Nil { ctx.AbortWithStatus(404) return } db. - Model(&schemas.Permission{ID: userId}). + Model(&schemas.Permission{ID: userIdUUID}). Association("ViewCollections"). Append(&collection) ctx.Status(204) }) sec.DELETE("/user/collection/:id", func(ctx *gin.Context) { // no 404 error, hahaha wtf - userId := ctx.MustGet("id").(uint) + userId := ctx.MustGet("id").(string) + userIdUUID := uuid.MustParse(userId) collId := ctx.Param("id") var collection schemas.Collection - db.Find(&collection, collId) + db.Find(&collection, "id = ?", collId) db. - Model(&schemas.Permission{ID: userId}). + Model(&schemas.Permission{ID: userIdUUID}). Association("ViewCollections"). Delete(&collection) ctx.Status(204) @@ -962,15 +966,16 @@ func main() { // } // }) sec.GET("/collection", func(ctx *gin.Context) { - userId := ctx.MustGet("id") + userId := ctx.MustGet("id").(string) + userIdUUID := uuid.MustParse(userId) collections := []struct { - ID uint `json:"id"` - Name string `json:"name"` + ID uuid.UUID `json:"id"` + Name string `json:"name"` }{} db. - Model(&schemas.Permission{ID: userId.(uint)}). + Model(&schemas.Permission{ID: userIdUUID}). Association("ViewCollections"). Find(&collections) // db.Model(&schemas.Collection{}).Find(&collections, &schemas.Collection{UserID: userId.(uint)}) @@ -978,7 +983,7 @@ func main() { }) sec.GET("/collection/:id", func(ctx *gin.Context) { isToken := false - if len(strings.Split(ctx.Param("id"), "-")) > 1 { + if strings.Split(ctx.Param("id"), "-")[0] == "pub" { isToken = true } collection := APICollection{} @@ -992,7 +997,7 @@ func main() { res = db. Table("collections"). // what if delete this string?.. Joins("JOIN view_collections ON view_collections.collection_id = collections.id"). - Where("view_collections.permission_id = ?", ctx.MustGet("id").(uint)). + Where("view_collections.permission_id = ?", ctx.MustGet("id").(string)). Where("id = ?", ctx.Param("id")). Preload("Creator"). Find(&collection) @@ -1004,6 +1009,8 @@ func main() { ctx.JSON(200, collection) }) sec.PUT("/collection", func(ctx *gin.Context) { + userId := ctx.MustGet("id").(string) + userIdUUID := uuid.MustParse(userId) var requestBody struct { Name string } @@ -1015,14 +1022,14 @@ func main() { linkUUID := uuid.New() newCollection := schemas.Collection{ Name: requestBody.Name, - Creator: schemas.User{ID: ctx.MustGet("id").(uint)}, + Creator: schemas.User{ID: userIdUUID}, Link: linkUUID.String(), Created: time.Now(), Modified: time.Now(), } db.Create(&newCollection) db. - Model(&schemas.Permission{ID: ctx.MustGet("id").(uint)}). + Model(&schemas.Permission{ID: userIdUUID}). Association("ViewCollections"). Append(&newCollection) ctx.JSON(200, gin.H{"id": newCollection.ID}) @@ -1031,13 +1038,13 @@ func main() { var requestBody struct { BookID string `json:"book_id"` } - collectionId, err := strconv.ParseUint(ctx.Param("id"), 10, 64) + collectionId, err := uuid.Parse(ctx.Param("id")) if err != nil { ctx.AbortWithError(400, err) return } ctx.BindJSON(&requestBody) - bookID, err := strconv.ParseUint(requestBody.BookID, 10, 64) + bookID, err := uuid.Parse(requestBody.BookID) if err != nil { ctx.AbortWithError(400, err) return @@ -1055,11 +1062,11 @@ func main() { return } collectionBookAssociation := db.Model(&schemas.Collection{ - ID: uint(collectionId), + ID: collectionId, }).Association("Books") var associatedBook schemas.Book collectionBookAssociation.Find(&associatedBook, bookID) - if associatedBook.ID != 0 { + if associatedBook.ID != uuid.Nil { // если книга уже есть в ассоциациях - удаляем err := collectionBookAssociation.Delete(associatedBook) log.Println(err) @@ -1079,32 +1086,36 @@ func main() { }) sec.DELETE("/collection/:id", func(ctx *gin.Context) { - collectionId, err := strconv.ParseUint(ctx.Param("id"), 10, 64) + userId := ctx.MustGet("id").(string) + userIdUUID := uuid.MustParse(userId) + collectionId, err := uuid.Parse(ctx.Param("id")) if err != nil { ctx.AbortWithError(400, err) return } var collection schemas.Collection db.Take(&collection, collectionId) - if collection.UserID == 0 { + if collection.UserID == uuid.Nil { ctx.AbortWithStatusJSON(404, gin.H{"err": "collection not found"}) return } - if collection.UserID != ctx.MustGet("id").(uint) { + if collection.UserID != userIdUUID { ctx.AbortWithStatus(403) return } db.Model(&schemas.Collection{ - ID: uint(collectionId), + ID: collectionId, }).Association("Books").Clear() db. - Model(&schemas.Permission{ID: ctx.MustGet("id").(uint)}). + Model(&schemas.Permission{ID: userIdUUID}). Association("ViewCollections"). - Delete(&schemas.Collection{ID: uint(collectionId)}) + Delete(&schemas.Collection{ID: collectionId}) db.Delete(&schemas.Collection{}, collectionId) ctx.Status(204) }) sec.POST("/book/upload", func(ctx *gin.Context) { + userId := ctx.MustGet("id").(string) + userIdUUID := uuid.MustParse(userId) form, err := ctx.MultipartForm() if err != nil { ctx.String(400, "Форму ты сломал: %s", err.Error()) @@ -1119,7 +1130,7 @@ func main() { fileReader, _ := file.Open() fileData, _ := io.ReadAll(fileReader) contentType := file.Header.Get("Content-Type") - var bookID uint64 + var bookID uuid.UUID bookUUID := uuid.NewString() switch contentType { case "application/pdf": @@ -1205,26 +1216,26 @@ func main() { } var collection schemas.Collection - res := db.Where(&schemas.Collection{Name: "Загрузки", UserID: ctx.MustGet("id").(uint)}).First(&collection) + res := db.Where(&schemas.Collection{Name: "Загрузки", UserID: userIdUUID}).First(&collection) if res.RowsAffected == 0 { linkUUID := uuid.New() newCollection := schemas.Collection{ Name: "Загрузки", - Creator: schemas.User{ID: ctx.MustGet("id").(uint)}, + Creator: schemas.User{ID: userIdUUID}, Link: linkUUID.String(), Created: time.Now(), Modified: time.Now(), } db.Create(&newCollection) db. - Model(&schemas.Permission{ID: ctx.MustGet("id").(uint)}). + Model(&schemas.Permission{ID: userIdUUID}). Association("ViewCollections"). Append(&newCollection) - db.Where(&schemas.Collection{Name: "Загрузки", UserID: ctx.MustGet("id").(uint)}).First(&collection) + db.Where(&schemas.Collection{Name: "Загрузки", UserID: userIdUUID}).First(&collection) } collectionBookAssociation := db.Model(&schemas.Collection{ - ID: uint(collection.ID), + ID: collection.ID, }).Association("Books") // если книги еще нет в ассоциациях - создаем err = collectionBookAssociation.Append(&schemas.Book{ID: bookID}) @@ -1269,11 +1280,11 @@ func main() { } type APIAuthor struct { - ID uint64 `json:"id"` - Key string `json:"key"` - FirstName string `json:"firstName"` - MiddleName string `json:"middleName"` - LastName string `json:"lastName"` + ID uuid.UUID `json:"id"` + Key string `json:"key"` + FirstName string `json:"firstName"` + MiddleName string `json:"middleName"` + LastName string `json:"lastName"` } func (APIAuthor) TableName() string { @@ -1281,7 +1292,7 @@ func (APIAuthor) TableName() string { } type APIBook struct { - ID uint `json:"id"` + ID uuid.UUID `json:"id"` Title string `json:"title"` Authors []APIAuthor `gorm:"many2many:BookAuthor;joinForeignKey:book_id;joinReferences:author_id" json:"authors"` Filetype string `json:"filetype"` @@ -1292,7 +1303,7 @@ func (APIBook) TableName() string { } type APIUser struct { - ID uint64 `gorm:"primaryKey" json:"id"` + ID uuid.UUID `gorm:"primaryKey" json:"id"` Name string `json:"name"` Username string `json:"username"` Collections []ShortAPICollection `json:"collections" gorm:"ForeignKey:id;"` @@ -1304,11 +1315,11 @@ func (APIUser) TableName() string { } type APICollection struct { - ID uint `gorm:"primaryKey" json:"id"` - Link string `gorm:"uniqueIndex" json:"link"` - Name string `json:"name"` - UserID uint `json:"user_id"` - Creator APIUser `gorm:"foreignKey:UserID" json:"creator"` + ID uuid.UUID `gorm:"primaryKey" json:"id"` + Link string `gorm:"uniqueIndex" json:"link"` + Name string `json:"name"` + UserID uuid.UUID `json:"user_id"` + Creator APIUser `gorm:"foreignKey:UserID" json:"creator"` //Books []APIBook `gorm:"many2many:CollectionBook;joinReferences:book_id;joinForeignKey:collection_id" json:"books"` Created time.Time `json:"created"` Modified time.Time `json:"modified"` diff --git a/apps/api/reaper/main.go b/apps/api/reaper/main.go index 539b41d..fdc7571 100644 --- a/apps/api/reaper/main.go +++ b/apps/api/reaper/main.go @@ -5,6 +5,7 @@ import ( "io" "mi6e4ka/yabl-api/schemas" + "github.com/google/uuid" "golang.org/x/net/html/charset" "gorm.io/gorm" ) @@ -66,7 +67,7 @@ func nilCheck(nilString *string) string { return *nilString } -func FB2toDB(tx *gorm.DB, book FB2) uint64 { +func FB2toDB(tx *gorm.DB, book FB2) uuid.UUID { var genres *[]schemas.Genre //book.Genres = nil if book.Genres != nil { diff --git a/apps/api/schemas/db.go b/apps/api/schemas/db.go index 939c021..a4ac715 100644 --- a/apps/api/schemas/db.go +++ b/apps/api/schemas/db.go @@ -1,23 +1,42 @@ package schemas -import "time" +import ( + "time" + + "github.com/google/uuid" + "gorm.io/gorm" +) type Language struct { - ID uint `gorm:",unique;autoIncrement:true"` + ID uuid.UUID Code string `gorm:"primaryKey;index:,unique"` ISO *string } +func (b *Language) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +} + type Genre struct { - ID uint `gorm:",unique;autoIncrement:true"` + ID uuid.UUID RawTag string `gorm:"index:,unique"` Tag *string Name *string } +func (b *Genre) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +} + type Author struct { - ID uint `gorm:"primaryKey;index:,unique;autoIncrement:true"` - Key string `gorm:",unique"` + ID uuid.UUID `gorm:"primaryKey;index:,unique;autoIncrement:true"` + Key string `gorm:",unique"` FirstName string MiddleName *string LastName *string @@ -26,9 +45,16 @@ type Author struct { Books []Book `gorm:"many2many:BookAuthor"` } +func (b *Author) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +} + type User struct { - ID uint `gorm:"primaryKey"` - Username string `gorm:"autoIncrement:false;index:,unique"` + ID uuid.UUID `gorm:"primaryKey"` + Username string `gorm:"autoIncrement:false;index:,unique"` Avatar *string Name *string Admin bool @@ -42,26 +68,54 @@ type User struct { BookShelf []ReaderBook } +func (b *User) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +} + type Translator struct { - ID uint `gorm:"primaryKey;index:,unique;autoIncrement:true"` - Key string `gorm:",unique"` + ID uuid.UUID `gorm:"primaryKey;index:,unique"` + Key string `gorm:",unique"` FirstName string MiddleName *string LastName *string } +func (b *Translator) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +} + type Sequence struct { - ID uint `gorm:",unique;autoIncrement:true"` - Name string `gorm:"primaryKey;index:,unique"` + ID uuid.UUID `gorm:",unique"` + Name string `gorm:"primaryKey;index:,unique"` +} + +func (b *Sequence) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return } type Publisher struct { - ID uint `gorm:",unique;autoIncrement:true"` - Name string `gorm:"primaryKey;index:,unique"` + ID uuid.UUID `gorm:",unique;autoIncrement:true"` + Name string `gorm:"primaryKey;index:,unique"` +} + +func (b *Publisher) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return } type Book struct { - ID uint64 `gorm:"primaryKey"` + ID uuid.UUID `gorm:"primaryKey"` Title string Authors []Author `gorm:"many2many:BookAuthor;References:ID"` Lang *string @@ -97,42 +151,72 @@ type Book struct { ExternalCover *string } +func (b *Book) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +} + // type Filetype struct { // ID uint `gorm:"primaryKey"` // Filetype string `gorm:"uniqueIndex"` // Name string // } type ReaderBook struct { - ID uint `gorm:"primaryKey"` - UserID uint - BookID uint + ID uuid.UUID `gorm:"primaryKey"` + UserID uuid.UUID + BookID uuid.UUID Book Book `gorm:"foreignKey:BookID"` Progress float64 LastRead time.Time } + +func (b *ReaderBook) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +} + type Collection struct { - ID uint `gorm:"primaryKey"` - Link string `gorm:"uniqueIndex"` + ID uuid.UUID `gorm:"primaryKey"` + Link string `gorm:"uniqueIndex"` Name string - UserID uint + UserID uuid.UUID Creator User `gorm:"foreignKey:UserID"` Books []Book `gorm:"many2many:CollectionBook;"` Created time.Time Modified time.Time } +func (b *Collection) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +} + type Share struct { - ID uint `gorm:"primaryKey"` - Token string `gorm:"uniqueIndex"` + ID uuid.UUID `gorm:"primaryKey"` + Token string `gorm:"uniqueIndex"` CreatedAt time.Time ExpiredAt *time.Time - BookID *uint + BookID *uuid.UUID Book *Book - UserID uint + UserID uuid.UUID User User } + +func (b *Share) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +} + type Permission struct { - ID uint `gorm:"primaryKey"` + ID uuid.UUID `gorm:"primaryKey"` User User `gorm:"foreignKey:ID"` EditBooks []Book `gorm:"many2many:EditableBook;"` BooksWhitelist *[]Book `gorm:"many2many:WhitelistedBook;"` @@ -143,6 +227,14 @@ type Permission struct { CanUpload bool CanCreateCollections bool } + +func (b *Permission) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +} + type Settings struct { LibPath string ReadOnly bool diff --git a/apps/book-reaper/go.mod b/apps/book-reaper/go.mod index 566c6cf..3fb0670 100644 --- a/apps/book-reaper/go.mod +++ b/apps/book-reaper/go.mod @@ -3,6 +3,7 @@ module book-tools go 1.24.4 require ( + github.com/google/uuid v1.6.0 github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/joho/godotenv v1.5.1 diff --git a/apps/book-reaper/go.sum b/apps/book-reaper/go.sum index 6b450a3..b89e17f 100644 --- a/apps/book-reaper/go.sum +++ b/apps/book-reaper/go.sum @@ -1,3 +1,5 @@ +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= diff --git a/apps/book-reaper/internal/app/app.go b/apps/book-reaper/internal/app/app.go index 8333a2e..c40189a 100644 --- a/apps/book-reaper/internal/app/app.go +++ b/apps/book-reaper/internal/app/app.go @@ -13,6 +13,7 @@ import ( "sync/atomic" "time" + "github.com/google/uuid" "gorm.io/gorm" ) @@ -63,7 +64,7 @@ func (a *App) Run() error { log.Printf("Failed add book to transaction: %v", tx.Error) return } - if bookID > 0 { + if bookID != uuid.Nil { addedBooks++ } } diff --git a/apps/book-reaper/pkg/reaper/main.go b/apps/book-reaper/pkg/reaper/main.go index d42ae8d..727a1ac 100644 --- a/apps/book-reaper/pkg/reaper/main.go +++ b/apps/book-reaper/pkg/reaper/main.go @@ -4,6 +4,7 @@ import ( "encoding/xml" "io" + "github.com/google/uuid" "golang.org/x/net/html/charset" "gorm.io/gorm" ) @@ -65,7 +66,7 @@ func nilCheck(nilString *string) string { return *nilString } -func FB2toDB(tx *gorm.DB, book FB2) uint64 { +func FB2toDB(tx *gorm.DB, book FB2) uuid.UUID { var genres *[]Genre //book.Genres = nil if book.Genres != nil { diff --git a/apps/book-reaper/pkg/reaper/schemas.go b/apps/book-reaper/pkg/reaper/schemas.go index f6eac50..37d574c 100644 --- a/apps/book-reaper/pkg/reaper/schemas.go +++ b/apps/book-reaper/pkg/reaper/schemas.go @@ -1,23 +1,42 @@ package reaper -import "time" +import ( + "time" + + "github.com/google/uuid" + "gorm.io/gorm" +) type Language struct { - ID uint `gorm:",unique;autoIncrement:true"` + ID uuid.UUID Code string `gorm:"primaryKey;index:,unique"` ISO *string } +func (b *Language) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +} + type Genre struct { - ID uint `gorm:",unique;autoIncrement:true"` + ID uuid.UUID RawTag string `gorm:"index:,unique"` Tag *string Name *string } +func (b *Genre) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +} + type Author struct { - ID uint `gorm:"primaryKey;index:,unique;autoIncrement:true"` - Key string `gorm:",unique"` + ID uuid.UUID `gorm:"primaryKey;index:,unique;autoIncrement:true"` + Key string `gorm:",unique"` FirstName string MiddleName *string LastName *string @@ -26,9 +45,16 @@ type Author struct { Books []Book `gorm:"many2many:BookAuthor"` } +func (b *Author) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +} + type User struct { - ID uint `gorm:"primaryKey"` - Username string `gorm:"autoIncrement:false;index:,unique"` + ID uuid.UUID `gorm:"primaryKey"` + Username string `gorm:"autoIncrement:false;index:,unique"` Avatar *string Name *string Admin bool @@ -42,26 +68,54 @@ type User struct { BookShelf []ReaderBook } +func (b *User) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +} + type Translator struct { - ID uint `gorm:"primaryKey;index:,unique;autoIncrement:true"` - Key string `gorm:",unique"` + ID uuid.UUID `gorm:"primaryKey;index:,unique"` + Key string `gorm:",unique"` FirstName string MiddleName *string LastName *string } +func (b *Translator) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +} + type Sequence struct { - ID uint `gorm:",unique;autoIncrement:true"` - Name string `gorm:"primaryKey;index:,unique"` + ID uuid.UUID `gorm:",unique"` + Name string `gorm:"primaryKey;index:,unique"` +} + +func (b *Sequence) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return } type Publisher struct { - ID uint `gorm:",unique;autoIncrement:true"` - Name string `gorm:"primaryKey;index:,unique"` + ID uuid.UUID `gorm:",unique;autoIncrement:true"` + Name string `gorm:"primaryKey;index:,unique"` +} + +func (b *Publisher) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return } type Book struct { - ID uint64 `gorm:"primaryKey"` + ID uuid.UUID `gorm:"primaryKey"` Title string Authors []Author `gorm:"many2many:BookAuthor;References:ID"` Lang *string @@ -97,26 +151,48 @@ type Book struct { ExternalCover *string } +func (b *Book) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +} + // type Filetype struct { // ID uint `gorm:"primaryKey"` // Filetype string `gorm:"uniqueIndex"` // Name string // } type ReaderBook struct { - ID uint `gorm:"primaryKey"` - UserID uint - BookID uint + ID uuid.UUID `gorm:"primaryKey"` + UserID uuid.UUID + BookID uuid.UUID Book Book `gorm:"foreignKey:BookID"` Progress float64 LastRead time.Time } + +func (b *ReaderBook) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +} + type Collection struct { - ID uint `gorm:"primaryKey"` - Link string `gorm:"uniqueIndex"` + ID uuid.UUID `gorm:"primaryKey"` + Link string `gorm:"uniqueIndex"` Name string - UserID uint + UserID uuid.UUID Creator User `gorm:"foreignKey:UserID"` Books []Book `gorm:"many2many:CollectionBook;"` Created time.Time Modified time.Time } + +func (b *Collection) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +}