Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9daf384514 | |||
| 0c326e82a8 | |||
| f6de4891b1 | |||
| 2666b76b44 |
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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"`
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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=
|
||||||
|
|||||||
@ -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++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
|
}
|
||||||
|
|||||||
@ -72,7 +72,7 @@ const BookPage = () => {
|
|||||||
}
|
}
|
||||||
loadImgFromCache();
|
loadImgFromCache();
|
||||||
let localBook = await getBook(id);
|
let localBook = await getBook(id);
|
||||||
if (window.onLine && !localBook) {
|
if (window.onLine /*&& !localBook*/) {
|
||||||
axios
|
axios
|
||||||
.get("/book/" + id)
|
.get("/book/" + id)
|
||||||
.then((res) => setBookInfo(res.data))
|
.then((res) => setBookInfo(res.data))
|
||||||
|
|||||||
@ -6,12 +6,13 @@
|
|||||||
|
|
||||||
.main_down_menu {
|
.main_down_menu {
|
||||||
display: none;
|
display: none;
|
||||||
position: fixed;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 100%;
|
left: 0;
|
||||||
|
width: calc(100%);
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
background-color: var(--md-sys-color-background);
|
background-color: var(--md-sys-color-background);
|
||||||
padding: 10px 0;
|
padding: 5px 0 10px 0;
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
}
|
}
|
||||||
.main_down_menu_item {
|
.main_down_menu_item {
|
||||||
@ -38,6 +39,7 @@
|
|||||||
.main_page_container {
|
.main_page_container {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 50px;
|
gap: 50px;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 470px) { /* кнопки в столбик */
|
@media screen and (max-width: 470px) { /* кнопки в столбик */
|
||||||
|
|||||||
@ -23,7 +23,7 @@ const AccountPage = () => {
|
|||||||
icon: "shelves",
|
icon: "shelves",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Загрузить книгу",
|
name: "Загрузить",
|
||||||
path: "/upload",
|
path: "/upload",
|
||||||
icon: "upload",
|
icon: "upload",
|
||||||
},
|
},
|
||||||
|
|||||||
@ -57,11 +57,10 @@ function UploadBook() {
|
|||||||
setUploadStatuses((prev) => ({ ...prev, [i]: 1 }));
|
setUploadStatuses((prev) => ({ ...prev, [i]: 1 }));
|
||||||
console.log(uploadStatuses);
|
console.log(uploadStatuses);
|
||||||
} else {
|
} else {
|
||||||
toast("Ошибка на сервере, go дебажить.");
|
toast.error("Ошибка при загрузке книги");
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Ошибка:", err);
|
toast.error("Ошибка при загрузке книги");
|
||||||
toast("Шатался интернет или сервер умер.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,11 +71,9 @@ function UploadBook() {
|
|||||||
return <span>Недоступно оффлайн</span>;
|
return <span>Недоступно оффлайн</span>;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center h-screen">
|
<div className="flex flex-col items-center justify-center h-full w-full">
|
||||||
<h1 className="text-2xl font-bold mb-4">Загрузка литературы</h1>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`flex items-center justify-around w-full max-w-lg h-50 mb-4 rounded-xl flex-col gap-1 px-4 border-2 border-dashed transition-colors duration-200 cursor-pointer border-(--md-sys-color-outline) ${
|
className={`flex items-center justify-around w-[calc(100%-32px)] max-w-lg h-50 mb-4 px-4 rounded-xl flex-col gap-1 transition-colors duration-200 ${uploading ? 'cursor-wait' : 'cursor-pointer'} ${
|
||||||
isDragging
|
isDragging
|
||||||
? "bg-(--md-sys-color-surface-variant)"
|
? "bg-(--md-sys-color-surface-variant)"
|
||||||
: "bg-(--md-sys-color-inverse-on-surface)"
|
: "bg-(--md-sys-color-inverse-on-surface)"
|
||||||
@ -87,8 +84,8 @@ function UploadBook() {
|
|||||||
onDragLeave={handleDragLeave}
|
onDragLeave={handleDragLeave}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
<span className="font-bold">Перетащи файл сюда или кликни</span>
|
<span className="font-bold text-center">Перетащите файл или нажмите для выбора</span>
|
||||||
<IconButton className="w-24 h-24">
|
<IconButton className="w-24 h-24" disabled={uploading}>
|
||||||
<Icon className="text-6xl w-24 h-24">upload_file</Icon>
|
<Icon className="text-6xl w-24 h-24">upload_file</Icon>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<span className="text-sm">Поддерживается .fb2 и .pdf</span>
|
<span className="text-sm">Поддерживается .fb2 и .pdf</span>
|
||||||
@ -99,13 +96,14 @@ function UploadBook() {
|
|||||||
accept=".fb2,.pdf"
|
accept=".fb2,.pdf"
|
||||||
ref={fileSelectorRef}
|
ref={fileSelectorRef}
|
||||||
className="hidden"
|
className="hidden"
|
||||||
|
disabled={uploading}
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full max-w-lg bg-(--md-sys-color-surface-variant) flex p-[18px] rounded-2xl flex-col max-h-60 overflow-scroll gap-3 mb-4">
|
<div className={`w-[calc(100%-32px)] max-w-lg bg-(--md-sys-color-inverse-on-surface) flex p-4 rounded-2xl flex-col max-h-60 overflow-scroll gap-3 mb-4 ${uploadedFiles.length === 0 ? 'hidden' : ''}`}>
|
||||||
{uploadedFiles.length === 0 ? <span>Нечего загружать</span> : <></>}
|
{uploadedFiles.length === 0 ? <span>Нечего загружать</span> : <></>}
|
||||||
{uploadedFiles.map((file, i) => (
|
{uploadedFiles.map((file, i) => (
|
||||||
<div className="flex justify-between" key={i}>
|
<div className="flex justify-between items-center" key={i}>
|
||||||
<span className="truncate w-[calc(100%-42px)] inline-block">
|
<span className="truncate w-[calc(100%-42px)] inline-block">
|
||||||
{file.name}
|
{file.name}
|
||||||
</span>
|
</span>
|
||||||
@ -130,7 +128,7 @@ function UploadBook() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FilledButton onClick={handleUpload} disabled={uploading}>
|
<FilledButton onClick={handleUpload} disabled={uploading || uploadedFiles.length===0}>
|
||||||
Загрузить
|
Загрузить
|
||||||
</FilledButton>
|
</FilledButton>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user