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