feat (api): migrate to UUIDs. Not fully debugged, may contain bugs

This commit is contained in:
Michael 2025-08-17 23:40:34 +03:00
parent 7ff759947a
commit 2666b76b44
9 changed files with 306 additions and 121 deletions

View File

@ -39,10 +39,10 @@ func GenWhitelist(db *gorm.DB, authorsFilePath string) {
continue continue
} }
if firstLine { if firstLine {
whitelist.WriteString(strconv.FormatUint(book.ID, 10)) whitelist.WriteString(book.ID.String())
firstLine = false firstLine = false
} else { } else {
whitelist.WriteString("\n" + strconv.FormatUint(book.ID, 10)) whitelist.WriteString("\n" + book.ID.String())
} }
} }
} }

View File

@ -11,6 +11,7 @@ import (
"encoding/pem" "encoding/pem"
"encoding/xml" "encoding/xml"
"errors" "errors"
"fmt"
"image" "image"
"image/color" "image/color"
"image/draw" "image/draw"
@ -52,11 +53,11 @@ type Search struct {
Books []APIBook `json:"books"` Books []APIBook `json:"books"`
} }
type Person struct { type Person struct {
Id int `json:"id"` Id uuid.UUID `json:"id"`
Name string `json:"name"` Name string `json:"name"`
FirstName string `json:"firstName"` FirstName string `json:"firstName"`
MiddleName *string `json:"middleName"` MiddleName *string `json:"middleName"`
LastName *string `json:"lastName"` LastName *string `json:"lastName"`
} }
type Books struct { type Books struct {
Title string `json:"title"` Title string `json:"title"`
@ -80,8 +81,8 @@ type Books struct {
Filetype string `json:"filetype"` Filetype string `json:"filetype"`
} }
type ShortAPICollection struct { type ShortAPICollection struct {
ID uint `json:"id"` ID uuid.UUID `json:"id"`
Name string `json:"name"` Name string `json:"name"`
} }
func (ShortAPICollection) TableName() string { func (ShortAPICollection) TableName() string {
@ -159,12 +160,12 @@ func AuthMiddleware(publicKey *rsa.PublicKey) gin.HandlerFunc {
ctx.AbortWithStatus(401) ctx.AbortWithStatus(401)
return return
} }
userIdUint, err := strconv.ParseUint(userId, 10, 64) userIdUUID, err := uuid.Parse(userId)
if err != nil { if err != nil {
ctx.AbortWithStatus(401) ctx.AbortWithStatus(401)
return return
} }
ctx.Set("id", uint(userIdUint)) ctx.Set("id", userIdUUID.String())
ctx.Set("scopes", claims.Scopes) ctx.Set("scopes", claims.Scopes)
ctx.Next() ctx.Next()
} }
@ -202,12 +203,12 @@ type Claims struct {
jwt.RegisteredClaims 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{ claims := &Claims{
Scopes: scopes, Scopes: scopes,
RegisteredClaims: jwt.RegisteredClaims{ RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 72)), ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 72)),
Subject: strconv.FormatUint(userId, 10), Subject: userId.String(),
}, },
} }
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
@ -304,22 +305,22 @@ func main() {
&schemas.Permission{}, &schemas.Permission{},
) )
log.Println("create indexes") 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(` db.Exec(`
CREATE TRIGGER IF NOT EXISTS books_ai AFTER INSERT ON books BEGIN 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; END;
`) `)
db.Exec(` db.Exec(`
CREATE TRIGGER IF NOT EXISTS books_ad AFTER DELETE ON books BEGIN 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; END;
`) `)
db.Exec(` db.Exec(`
CREATE TRIGGER IF NOT EXISTS books_au AFTER UPDATE ON books BEGIN 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; END;
`) `)
log.Println("migrated!") log.Println("migrated!")
@ -357,10 +358,9 @@ func main() {
var books []APIBook var books []APIBook
q, isTitle := c.GetQuery("q") q, isTitle := c.GetQuery("q")
author, isAuthor := c.GetQuery("author") author, isAuthor := c.GetQuery("author")
collectionStr, isCollection := c.GetQuery("collection") collection, isCollection := c.GetQuery("collection")
limitStr, _ := c.GetQuery("limit") limitStr, _ := c.GetQuery("limit")
limit, _ := strconv.ParseInt(limitStr, 10, 64) limit, _ := strconv.ParseInt(limitStr, 10, 64)
collection, _ := strconv.ParseInt(collectionStr, 10, 64)
offsetStr, _ := c.GetQuery("offset") offsetStr, _ := c.GetQuery("offset")
offset, _ := strconv.ParseInt(offsetStr, 10, 64) offset, _ := strconv.ParseInt(offsetStr, 10, 64)
_, _ = author, q _, _ = author, q
@ -370,10 +370,12 @@ func main() {
Table("books"). Table("books").
Joins("JOIN collection_books ON collection_books.book_id = books.id"). Joins("JOIN collection_books ON collection_books.book_id = books.id").
Joins("JOIN collections ON collections.id = collection_books.collection_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. query = query.
Where("collections.link = ?", collectionStr) Where("collections.link = ?", collection)
} else { } else {
fmt.Println("collection")
query = query. query = query.
Where("collections.id = ?", collection) Where("collections.id = ?", collection)
} }
@ -391,7 +393,7 @@ func main() {
query = db. query = db.
Find(&BooksIndex{}). Find(&BooksIndex{}).
Where("books_indices.title MATCH ?", q+"*"). 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") Order("rank")
} else if isAuthor && !isTitle { } else if isAuthor && !isTitle {
query = db. query = db.
@ -430,12 +432,11 @@ func main() {
}) })
}) })
sec.GET("/author/:id", func(ctx *gin.Context) { 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 { if err != nil {
ctx.AbortWithError(400, err) ctx.AbortWithError(400, err)
return return
} }
_ = authorID
var authorObj schemas.Author var authorObj schemas.Author
db.Find(&authorObj, "id = ?", authorID) db.Find(&authorObj, "id = ?", authorID)
authorName := personToString(authorObj.FirstName, authorObj.MiddleName, authorObj.LastName) authorName := personToString(authorObj.FirstName, authorObj.MiddleName, authorObj.LastName)
@ -458,7 +459,7 @@ func main() {
var authors []Person var authors []Person
for _, author := range book.Authors { for _, author := range book.Authors {
authors = append(authors, Person{ authors = append(authors, Person{
Id: int(author.ID), Id: author.ID,
Name: personToString(author.FirstName, author.MiddleName, author.LastName), Name: personToString(author.FirstName, author.MiddleName, author.LastName),
FirstName: author.FirstName, FirstName: author.FirstName,
MiddleName: author.MiddleName, MiddleName: author.MiddleName,
@ -473,7 +474,7 @@ func main() {
translators = &newSlice translators = &newSlice
} }
*translators = append(*translators, Person{ *translators = append(*translators, Person{
Id: int(translator.ID), Id: translator.ID,
Name: personToString(translator.FirstName, translator.MiddleName, translator.LastName), Name: personToString(translator.FirstName, translator.MiddleName, translator.LastName),
}) })
} }
@ -492,7 +493,7 @@ func main() {
genreName = *genre.Name genreName = *genre.Name
} }
*genres = append(*genres, Person{ *genres = append(*genres, Person{
Id: int(genre.ID), Id: genre.ID,
Name: genreName, Name: genreName,
}) })
} }
@ -760,7 +761,7 @@ func main() {
ctx.AbortWithStatus(403) ctx.AbortWithStatus(403)
return 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) ctx.SetCookie("token", token, 60*60*72, "/", "", false, false)
}) })
sec.GET("/ping", func(ctx *gin.Context) { sec.GET("/ping", func(ctx *gin.Context) {
@ -875,10 +876,11 @@ func main() {
ctx.Data(200, "image/webp", decodedImage) ctx.Data(200, "image/webp", decodedImage)
}) })
sec.GET("/user", func(ctx *gin.Context) { sec.GET("/user", func(ctx *gin.Context) {
userId := ctx.MustGet("id").(uint) userId := ctx.MustGet("id").(string)
if userId == 0 { userIdUUID := uuid.MustParse(userId)
if userIdUUID == uuid.Nil {
ctx.JSON(200, APIUser{ ctx.JSON(200, APIUser{
ID: 0, ID: uuid.Nil,
Username: "guest", Username: "guest",
Name: "Гость", Name: "Гость",
}) })
@ -889,31 +891,33 @@ func main() {
ctx.JSON(200, user) ctx.JSON(200, user)
}) })
sec.POST("/user/collection", func(ctx *gin.Context) { 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 { var requestBody struct {
Link string Link string
} }
ctx.BindJSON(&requestBody) ctx.BindJSON(&requestBody)
var collection schemas.Collection var collection schemas.Collection
db.Where("link = ?", requestBody.Link).Take(&collection) db.Where("link = ?", requestBody.Link).Take(&collection)
if collection.ID == 0 { if collection.ID == uuid.Nil {
ctx.AbortWithStatus(404) ctx.AbortWithStatus(404)
return return
} }
db. db.
Model(&schemas.Permission{ID: userId}). Model(&schemas.Permission{ID: userIdUUID}).
Association("ViewCollections"). Association("ViewCollections").
Append(&collection) Append(&collection)
ctx.Status(204) ctx.Status(204)
}) })
sec.DELETE("/user/collection/:id", func(ctx *gin.Context) { sec.DELETE("/user/collection/:id", func(ctx *gin.Context) {
// no 404 error, hahaha wtf // no 404 error, hahaha wtf
userId := ctx.MustGet("id").(uint) userId := ctx.MustGet("id").(string)
userIdUUID := uuid.MustParse(userId)
collId := ctx.Param("id") collId := ctx.Param("id")
var collection schemas.Collection var collection schemas.Collection
db.Find(&collection, collId) db.Find(&collection, "id = ?", collId)
db. db.
Model(&schemas.Permission{ID: userId}). Model(&schemas.Permission{ID: userIdUUID}).
Association("ViewCollections"). Association("ViewCollections").
Delete(&collection) Delete(&collection)
ctx.Status(204) ctx.Status(204)
@ -962,15 +966,16 @@ func main() {
// } // }
// }) // })
sec.GET("/collection", func(ctx *gin.Context) { sec.GET("/collection", func(ctx *gin.Context) {
userId := ctx.MustGet("id") userId := ctx.MustGet("id").(string)
userIdUUID := uuid.MustParse(userId)
collections := []struct { collections := []struct {
ID uint `json:"id"` ID uuid.UUID `json:"id"`
Name string `json:"name"` Name string `json:"name"`
}{} }{}
db. db.
Model(&schemas.Permission{ID: userId.(uint)}). Model(&schemas.Permission{ID: userIdUUID}).
Association("ViewCollections"). Association("ViewCollections").
Find(&collections) Find(&collections)
// db.Model(&schemas.Collection{}).Find(&collections, &schemas.Collection{UserID: userId.(uint)}) // 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) { sec.GET("/collection/:id", func(ctx *gin.Context) {
isToken := false isToken := false
if len(strings.Split(ctx.Param("id"), "-")) > 1 { if strings.Split(ctx.Param("id"), "-")[0] == "pub" {
isToken = true isToken = true
} }
collection := APICollection{} collection := APICollection{}
@ -992,7 +997,7 @@ func main() {
res = db. res = db.
Table("collections"). // what if delete this string?.. Table("collections"). // what if delete this string?..
Joins("JOIN view_collections ON view_collections.collection_id = collections.id"). 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")). Where("id = ?", ctx.Param("id")).
Preload("Creator"). Preload("Creator").
Find(&collection) Find(&collection)
@ -1004,6 +1009,8 @@ func main() {
ctx.JSON(200, collection) ctx.JSON(200, collection)
}) })
sec.PUT("/collection", func(ctx *gin.Context) { sec.PUT("/collection", func(ctx *gin.Context) {
userId := ctx.MustGet("id").(string)
userIdUUID := uuid.MustParse(userId)
var requestBody struct { var requestBody struct {
Name string Name string
} }
@ -1015,14 +1022,14 @@ func main() {
linkUUID := uuid.New() linkUUID := uuid.New()
newCollection := schemas.Collection{ newCollection := schemas.Collection{
Name: requestBody.Name, Name: requestBody.Name,
Creator: schemas.User{ID: ctx.MustGet("id").(uint)}, Creator: schemas.User{ID: userIdUUID},
Link: linkUUID.String(), Link: linkUUID.String(),
Created: time.Now(), Created: time.Now(),
Modified: time.Now(), Modified: time.Now(),
} }
db.Create(&newCollection) db.Create(&newCollection)
db. db.
Model(&schemas.Permission{ID: ctx.MustGet("id").(uint)}). Model(&schemas.Permission{ID: userIdUUID}).
Association("ViewCollections"). Association("ViewCollections").
Append(&newCollection) Append(&newCollection)
ctx.JSON(200, gin.H{"id": newCollection.ID}) ctx.JSON(200, gin.H{"id": newCollection.ID})
@ -1031,13 +1038,13 @@ func main() {
var requestBody struct { var requestBody struct {
BookID string `json:"book_id"` BookID string `json:"book_id"`
} }
collectionId, err := strconv.ParseUint(ctx.Param("id"), 10, 64) collectionId, err := uuid.Parse(ctx.Param("id"))
if err != nil { if err != nil {
ctx.AbortWithError(400, err) ctx.AbortWithError(400, err)
return return
} }
ctx.BindJSON(&requestBody) ctx.BindJSON(&requestBody)
bookID, err := strconv.ParseUint(requestBody.BookID, 10, 64) bookID, err := uuid.Parse(requestBody.BookID)
if err != nil { if err != nil {
ctx.AbortWithError(400, err) ctx.AbortWithError(400, err)
return return
@ -1055,11 +1062,11 @@ func main() {
return return
} }
collectionBookAssociation := db.Model(&schemas.Collection{ collectionBookAssociation := db.Model(&schemas.Collection{
ID: uint(collectionId), ID: collectionId,
}).Association("Books") }).Association("Books")
var associatedBook schemas.Book var associatedBook schemas.Book
collectionBookAssociation.Find(&associatedBook, bookID) collectionBookAssociation.Find(&associatedBook, bookID)
if associatedBook.ID != 0 { if associatedBook.ID != uuid.Nil {
// если книга уже есть в ассоциациях - удаляем // если книга уже есть в ассоциациях - удаляем
err := collectionBookAssociation.Delete(associatedBook) err := collectionBookAssociation.Delete(associatedBook)
log.Println(err) log.Println(err)
@ -1079,32 +1086,36 @@ func main() {
}) })
sec.DELETE("/collection/:id", func(ctx *gin.Context) { 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 { if err != nil {
ctx.AbortWithError(400, err) ctx.AbortWithError(400, err)
return return
} }
var collection schemas.Collection var collection schemas.Collection
db.Take(&collection, collectionId) db.Take(&collection, collectionId)
if collection.UserID == 0 { if collection.UserID == uuid.Nil {
ctx.AbortWithStatusJSON(404, gin.H{"err": "collection not found"}) ctx.AbortWithStatusJSON(404, gin.H{"err": "collection not found"})
return return
} }
if collection.UserID != ctx.MustGet("id").(uint) { if collection.UserID != userIdUUID {
ctx.AbortWithStatus(403) ctx.AbortWithStatus(403)
return return
} }
db.Model(&schemas.Collection{ db.Model(&schemas.Collection{
ID: uint(collectionId), ID: collectionId,
}).Association("Books").Clear() }).Association("Books").Clear()
db. db.
Model(&schemas.Permission{ID: ctx.MustGet("id").(uint)}). Model(&schemas.Permission{ID: userIdUUID}).
Association("ViewCollections"). Association("ViewCollections").
Delete(&schemas.Collection{ID: uint(collectionId)}) Delete(&schemas.Collection{ID: collectionId})
db.Delete(&schemas.Collection{}, collectionId) db.Delete(&schemas.Collection{}, collectionId)
ctx.Status(204) ctx.Status(204)
}) })
sec.POST("/book/upload", func(ctx *gin.Context) { sec.POST("/book/upload", func(ctx *gin.Context) {
userId := ctx.MustGet("id").(string)
userIdUUID := uuid.MustParse(userId)
form, err := ctx.MultipartForm() form, err := ctx.MultipartForm()
if err != nil { if err != nil {
ctx.String(400, "Форму ты сломал: %s", err.Error()) ctx.String(400, "Форму ты сломал: %s", err.Error())
@ -1119,7 +1130,7 @@ func main() {
fileReader, _ := file.Open() fileReader, _ := file.Open()
fileData, _ := io.ReadAll(fileReader) fileData, _ := io.ReadAll(fileReader)
contentType := file.Header.Get("Content-Type") contentType := file.Header.Get("Content-Type")
var bookID uint64 var bookID uuid.UUID
bookUUID := uuid.NewString() bookUUID := uuid.NewString()
switch contentType { switch contentType {
case "application/pdf": case "application/pdf":
@ -1205,26 +1216,26 @@ func main() {
} }
var collection schemas.Collection 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 { if res.RowsAffected == 0 {
linkUUID := uuid.New() linkUUID := uuid.New()
newCollection := schemas.Collection{ newCollection := schemas.Collection{
Name: "Загрузки", Name: "Загрузки",
Creator: schemas.User{ID: ctx.MustGet("id").(uint)}, Creator: schemas.User{ID: userIdUUID},
Link: linkUUID.String(), Link: linkUUID.String(),
Created: time.Now(), Created: time.Now(),
Modified: time.Now(), Modified: time.Now(),
} }
db.Create(&newCollection) db.Create(&newCollection)
db. db.
Model(&schemas.Permission{ID: ctx.MustGet("id").(uint)}). Model(&schemas.Permission{ID: userIdUUID}).
Association("ViewCollections"). Association("ViewCollections").
Append(&newCollection) 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{ collectionBookAssociation := db.Model(&schemas.Collection{
ID: uint(collection.ID), ID: collection.ID,
}).Association("Books") }).Association("Books")
// если книги еще нет в ассоциациях - создаем // если книги еще нет в ассоциациях - создаем
err = collectionBookAssociation.Append(&schemas.Book{ID: bookID}) err = collectionBookAssociation.Append(&schemas.Book{ID: bookID})
@ -1269,11 +1280,11 @@ func main() {
} }
type APIAuthor struct { type APIAuthor struct {
ID uint64 `json:"id"` ID uuid.UUID `json:"id"`
Key string `json:"key"` Key string `json:"key"`
FirstName string `json:"firstName"` FirstName string `json:"firstName"`
MiddleName string `json:"middleName"` MiddleName string `json:"middleName"`
LastName string `json:"lastName"` LastName string `json:"lastName"`
} }
func (APIAuthor) TableName() string { func (APIAuthor) TableName() string {
@ -1281,7 +1292,7 @@ func (APIAuthor) TableName() string {
} }
type APIBook struct { type APIBook struct {
ID uint `json:"id"` ID uuid.UUID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Authors []APIAuthor `gorm:"many2many:BookAuthor;joinForeignKey:book_id;joinReferences:author_id" json:"authors"` Authors []APIAuthor `gorm:"many2many:BookAuthor;joinForeignKey:book_id;joinReferences:author_id" json:"authors"`
Filetype string `json:"filetype"` Filetype string `json:"filetype"`
@ -1292,7 +1303,7 @@ func (APIBook) TableName() string {
} }
type APIUser struct { type APIUser struct {
ID uint64 `gorm:"primaryKey" json:"id"` ID uuid.UUID `gorm:"primaryKey" json:"id"`
Name string `json:"name"` Name string `json:"name"`
Username string `json:"username"` Username string `json:"username"`
Collections []ShortAPICollection `json:"collections" gorm:"ForeignKey:id;"` Collections []ShortAPICollection `json:"collections" gorm:"ForeignKey:id;"`
@ -1304,11 +1315,11 @@ func (APIUser) TableName() string {
} }
type APICollection struct { type APICollection struct {
ID uint `gorm:"primaryKey" json:"id"` ID uuid.UUID `gorm:"primaryKey" json:"id"`
Link string `gorm:"uniqueIndex" json:"link"` Link string `gorm:"uniqueIndex" json:"link"`
Name string `json:"name"` Name string `json:"name"`
UserID uint `json:"user_id"` UserID uuid.UUID `json:"user_id"`
Creator APIUser `gorm:"foreignKey:UserID" json:"creator"` Creator APIUser `gorm:"foreignKey:UserID" json:"creator"`
//Books []APIBook `gorm:"many2many:CollectionBook;joinReferences:book_id;joinForeignKey:collection_id" json:"books"` //Books []APIBook `gorm:"many2many:CollectionBook;joinReferences:book_id;joinForeignKey:collection_id" json:"books"`
Created time.Time `json:"created"` Created time.Time `json:"created"`
Modified time.Time `json:"modified"` Modified time.Time `json:"modified"`

View File

@ -5,6 +5,7 @@ import (
"io" "io"
"mi6e4ka/yabl-api/schemas" "mi6e4ka/yabl-api/schemas"
"github.com/google/uuid"
"golang.org/x/net/html/charset" "golang.org/x/net/html/charset"
"gorm.io/gorm" "gorm.io/gorm"
) )
@ -66,7 +67,7 @@ func nilCheck(nilString *string) string {
return *nilString return *nilString
} }
func FB2toDB(tx *gorm.DB, book FB2) uint64 { func FB2toDB(tx *gorm.DB, book FB2) uuid.UUID {
var genres *[]schemas.Genre var genres *[]schemas.Genre
//book.Genres = nil //book.Genres = nil
if book.Genres != nil { if book.Genres != nil {

View File

@ -1,23 +1,42 @@
package schemas package schemas
import "time" import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type Language struct { type Language struct {
ID uint `gorm:",unique;autoIncrement:true"` ID uuid.UUID
Code string `gorm:"primaryKey;index:,unique"` Code string `gorm:"primaryKey;index:,unique"`
ISO *string ISO *string
} }
func (b *Language) BeforeCreate(tx *gorm.DB) (err error) {
if b.ID == uuid.Nil {
b.ID = uuid.New()
}
return
}
type Genre struct { type Genre struct {
ID uint `gorm:",unique;autoIncrement:true"` ID uuid.UUID
RawTag string `gorm:"index:,unique"` RawTag string `gorm:"index:,unique"`
Tag *string Tag *string
Name *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 { type Author struct {
ID uint `gorm:"primaryKey;index:,unique;autoIncrement:true"` ID uuid.UUID `gorm:"primaryKey;index:,unique;autoIncrement:true"`
Key string `gorm:",unique"` Key string `gorm:",unique"`
FirstName string FirstName string
MiddleName *string MiddleName *string
LastName *string LastName *string
@ -26,9 +45,16 @@ type Author struct {
Books []Book `gorm:"many2many:BookAuthor"` 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 { type User struct {
ID uint `gorm:"primaryKey"` ID uuid.UUID `gorm:"primaryKey"`
Username string `gorm:"autoIncrement:false;index:,unique"` Username string `gorm:"autoIncrement:false;index:,unique"`
Avatar *string Avatar *string
Name *string Name *string
Admin bool Admin bool
@ -42,26 +68,54 @@ type User struct {
BookShelf []ReaderBook BookShelf []ReaderBook
} }
func (b *User) BeforeCreate(tx *gorm.DB) (err error) {
if b.ID == uuid.Nil {
b.ID = uuid.New()
}
return
}
type Translator struct { type Translator struct {
ID uint `gorm:"primaryKey;index:,unique;autoIncrement:true"` ID uuid.UUID `gorm:"primaryKey;index:,unique"`
Key string `gorm:",unique"` Key string `gorm:",unique"`
FirstName string FirstName string
MiddleName *string MiddleName *string
LastName *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 { type Sequence struct {
ID uint `gorm:",unique;autoIncrement:true"` ID uuid.UUID `gorm:",unique"`
Name string `gorm:"primaryKey;index:,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 { type Publisher struct {
ID uint `gorm:",unique;autoIncrement:true"` ID uuid.UUID `gorm:",unique;autoIncrement:true"`
Name string `gorm:"primaryKey;index:,unique"` 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 { type Book struct {
ID uint64 `gorm:"primaryKey"` ID uuid.UUID `gorm:"primaryKey"`
Title string Title string
Authors []Author `gorm:"many2many:BookAuthor;References:ID"` Authors []Author `gorm:"many2many:BookAuthor;References:ID"`
Lang *string Lang *string
@ -97,42 +151,72 @@ type Book struct {
ExternalCover *string ExternalCover *string
} }
func (b *Book) BeforeCreate(tx *gorm.DB) (err error) {
if b.ID == uuid.Nil {
b.ID = uuid.New()
}
return
}
// type Filetype struct { // type Filetype struct {
// ID uint `gorm:"primaryKey"` // ID uint `gorm:"primaryKey"`
// Filetype string `gorm:"uniqueIndex"` // Filetype string `gorm:"uniqueIndex"`
// Name string // Name string
// } // }
type ReaderBook struct { type ReaderBook struct {
ID uint `gorm:"primaryKey"` ID uuid.UUID `gorm:"primaryKey"`
UserID uint UserID uuid.UUID
BookID uint BookID uuid.UUID
Book Book `gorm:"foreignKey:BookID"` Book Book `gorm:"foreignKey:BookID"`
Progress float64 Progress float64
LastRead time.Time 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 { type Collection struct {
ID uint `gorm:"primaryKey"` ID uuid.UUID `gorm:"primaryKey"`
Link string `gorm:"uniqueIndex"` Link string `gorm:"uniqueIndex"`
Name string Name string
UserID uint UserID uuid.UUID
Creator User `gorm:"foreignKey:UserID"` Creator User `gorm:"foreignKey:UserID"`
Books []Book `gorm:"many2many:CollectionBook;"` Books []Book `gorm:"many2many:CollectionBook;"`
Created time.Time Created time.Time
Modified 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 { type Share struct {
ID uint `gorm:"primaryKey"` ID uuid.UUID `gorm:"primaryKey"`
Token string `gorm:"uniqueIndex"` Token string `gorm:"uniqueIndex"`
CreatedAt time.Time CreatedAt time.Time
ExpiredAt *time.Time ExpiredAt *time.Time
BookID *uint BookID *uuid.UUID
Book *Book Book *Book
UserID uint UserID uuid.UUID
User User User User
} }
func (b *Share) BeforeCreate(tx *gorm.DB) (err error) {
if b.ID == uuid.Nil {
b.ID = uuid.New()
}
return
}
type Permission struct { type Permission struct {
ID uint `gorm:"primaryKey"` ID uuid.UUID `gorm:"primaryKey"`
User User `gorm:"foreignKey:ID"` User User `gorm:"foreignKey:ID"`
EditBooks []Book `gorm:"many2many:EditableBook;"` EditBooks []Book `gorm:"many2many:EditableBook;"`
BooksWhitelist *[]Book `gorm:"many2many:WhitelistedBook;"` BooksWhitelist *[]Book `gorm:"many2many:WhitelistedBook;"`
@ -143,6 +227,14 @@ type Permission struct {
CanUpload bool CanUpload bool
CanCreateCollections 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 { type Settings struct {
LibPath string LibPath string
ReadOnly bool ReadOnly bool

View File

@ -3,6 +3,7 @@ module book-tools
go 1.24.4 go 1.24.4
require ( require (
github.com/google/uuid v1.6.0
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1

View File

@ -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 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=

View File

@ -13,6 +13,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/google/uuid"
"gorm.io/gorm" "gorm.io/gorm"
) )
@ -63,7 +64,7 @@ func (a *App) Run() error {
log.Printf("Failed add book to transaction: %v", tx.Error) log.Printf("Failed add book to transaction: %v", tx.Error)
return return
} }
if bookID > 0 { if bookID != uuid.Nil {
addedBooks++ addedBooks++
} }
} }

View File

@ -4,6 +4,7 @@ import (
"encoding/xml" "encoding/xml"
"io" "io"
"github.com/google/uuid"
"golang.org/x/net/html/charset" "golang.org/x/net/html/charset"
"gorm.io/gorm" "gorm.io/gorm"
) )
@ -65,7 +66,7 @@ func nilCheck(nilString *string) string {
return *nilString return *nilString
} }
func FB2toDB(tx *gorm.DB, book FB2) uint64 { func FB2toDB(tx *gorm.DB, book FB2) uuid.UUID {
var genres *[]Genre var genres *[]Genre
//book.Genres = nil //book.Genres = nil
if book.Genres != nil { if book.Genres != nil {

View File

@ -1,23 +1,42 @@
package reaper package reaper
import "time" import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type Language struct { type Language struct {
ID uint `gorm:",unique;autoIncrement:true"` ID uuid.UUID
Code string `gorm:"primaryKey;index:,unique"` Code string `gorm:"primaryKey;index:,unique"`
ISO *string ISO *string
} }
func (b *Language) BeforeCreate(tx *gorm.DB) (err error) {
if b.ID == uuid.Nil {
b.ID = uuid.New()
}
return
}
type Genre struct { type Genre struct {
ID uint `gorm:",unique;autoIncrement:true"` ID uuid.UUID
RawTag string `gorm:"index:,unique"` RawTag string `gorm:"index:,unique"`
Tag *string Tag *string
Name *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 { type Author struct {
ID uint `gorm:"primaryKey;index:,unique;autoIncrement:true"` ID uuid.UUID `gorm:"primaryKey;index:,unique;autoIncrement:true"`
Key string `gorm:",unique"` Key string `gorm:",unique"`
FirstName string FirstName string
MiddleName *string MiddleName *string
LastName *string LastName *string
@ -26,9 +45,16 @@ type Author struct {
Books []Book `gorm:"many2many:BookAuthor"` 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 { type User struct {
ID uint `gorm:"primaryKey"` ID uuid.UUID `gorm:"primaryKey"`
Username string `gorm:"autoIncrement:false;index:,unique"` Username string `gorm:"autoIncrement:false;index:,unique"`
Avatar *string Avatar *string
Name *string Name *string
Admin bool Admin bool
@ -42,26 +68,54 @@ type User struct {
BookShelf []ReaderBook BookShelf []ReaderBook
} }
func (b *User) BeforeCreate(tx *gorm.DB) (err error) {
if b.ID == uuid.Nil {
b.ID = uuid.New()
}
return
}
type Translator struct { type Translator struct {
ID uint `gorm:"primaryKey;index:,unique;autoIncrement:true"` ID uuid.UUID `gorm:"primaryKey;index:,unique"`
Key string `gorm:",unique"` Key string `gorm:",unique"`
FirstName string FirstName string
MiddleName *string MiddleName *string
LastName *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 { type Sequence struct {
ID uint `gorm:",unique;autoIncrement:true"` ID uuid.UUID `gorm:",unique"`
Name string `gorm:"primaryKey;index:,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 { type Publisher struct {
ID uint `gorm:",unique;autoIncrement:true"` ID uuid.UUID `gorm:",unique;autoIncrement:true"`
Name string `gorm:"primaryKey;index:,unique"` 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 { type Book struct {
ID uint64 `gorm:"primaryKey"` ID uuid.UUID `gorm:"primaryKey"`
Title string Title string
Authors []Author `gorm:"many2many:BookAuthor;References:ID"` Authors []Author `gorm:"many2many:BookAuthor;References:ID"`
Lang *string Lang *string
@ -97,26 +151,48 @@ type Book struct {
ExternalCover *string ExternalCover *string
} }
func (b *Book) BeforeCreate(tx *gorm.DB) (err error) {
if b.ID == uuid.Nil {
b.ID = uuid.New()
}
return
}
// type Filetype struct { // type Filetype struct {
// ID uint `gorm:"primaryKey"` // ID uint `gorm:"primaryKey"`
// Filetype string `gorm:"uniqueIndex"` // Filetype string `gorm:"uniqueIndex"`
// Name string // Name string
// } // }
type ReaderBook struct { type ReaderBook struct {
ID uint `gorm:"primaryKey"` ID uuid.UUID `gorm:"primaryKey"`
UserID uint UserID uuid.UUID
BookID uint BookID uuid.UUID
Book Book `gorm:"foreignKey:BookID"` Book Book `gorm:"foreignKey:BookID"`
Progress float64 Progress float64
LastRead time.Time 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 { type Collection struct {
ID uint `gorm:"primaryKey"` ID uuid.UUID `gorm:"primaryKey"`
Link string `gorm:"uniqueIndex"` Link string `gorm:"uniqueIndex"`
Name string Name string
UserID uint UserID uuid.UUID
Creator User `gorm:"foreignKey:UserID"` Creator User `gorm:"foreignKey:UserID"`
Books []Book `gorm:"many2many:CollectionBook;"` Books []Book `gorm:"many2many:CollectionBook;"`
Created time.Time Created time.Time
Modified time.Time Modified time.Time
} }
func (b *Collection) BeforeCreate(tx *gorm.DB) (err error) {
if b.ID == uuid.Nil {
b.ID = uuid.New()
}
return
}