diff --git a/docs/docs.go b/docs/docs.go index dfc2807..7a45e00 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -52,6 +52,148 @@ const docTemplate = `{ "description": "Title of the book to filter by", "name": "title", "in": "query" + }, + { + "type": "string", + "description": "First name, last name or username of the book author to filter by", + "name": "name", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter by highest ratings", + "name": "rating", + "in": "query" + }, + { + "type": "string", + "description": "Filter by Genre slug", + "name": "genre_slug", + "in": "query" + }, + { + "type": "string", + "description": "Filter by Tag slug", + "name": "tag_slug", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successfully retrieved list of books", + "schema": { + "$ref": "#/definitions/schemas.BooksResponseSchema" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/utils.ErrorResponse" + } + } + } + } + }, + "/admin/books/book-detail/{slug}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "This endpoint allows an admin to view details of a book", + "tags": [ + "Admin | Books" + ], + "summary": "View Book Details", + "parameters": [ + { + "type": "integer", + "default": 1, + "description": "Current Page (for reviews pagination)", + "name": "page", + "in": "query" + }, + { + "type": "string", + "description": "Book slug", + "name": "slug", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/schemas.BookDetailResponseSchema" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrorResponse" + } + } + } + } + }, + "/admin/books/by-username/{username}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieves a list of a particular author books with support for pagination and optional filtering based on book title.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Admin | Books" + ], + "summary": "List Author Books with Pagination", + "parameters": [ + { + "type": "string", + "description": "Username of the author", + "name": "username", + "in": "path", + "required": true + }, + { + "type": "integer", + "default": 1, + "description": "Current Page", + "name": "page", + "in": "query" + }, + { + "type": "string", + "description": "Title of the book to filter by", + "name": "title", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter by highest ratings", + "name": "rating", + "in": "query" + }, + { + "type": "string", + "description": "Filter by Genre slug", + "name": "genre_slug", + "in": "query" + }, + { + "type": "string", + "description": "Filter by Tag slug", + "name": "tag_slug", + "in": "query" } ], "responses": { diff --git a/docs/swagger.json b/docs/swagger.json index 974f6bd..dd05963 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -45,6 +45,148 @@ "description": "Title of the book to filter by", "name": "title", "in": "query" + }, + { + "type": "string", + "description": "First name, last name or username of the book author to filter by", + "name": "name", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter by highest ratings", + "name": "rating", + "in": "query" + }, + { + "type": "string", + "description": "Filter by Genre slug", + "name": "genre_slug", + "in": "query" + }, + { + "type": "string", + "description": "Filter by Tag slug", + "name": "tag_slug", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successfully retrieved list of books", + "schema": { + "$ref": "#/definitions/schemas.BooksResponseSchema" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/utils.ErrorResponse" + } + } + } + } + }, + "/admin/books/book-detail/{slug}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "This endpoint allows an admin to view details of a book", + "tags": [ + "Admin | Books" + ], + "summary": "View Book Details", + "parameters": [ + { + "type": "integer", + "default": 1, + "description": "Current Page (for reviews pagination)", + "name": "page", + "in": "query" + }, + { + "type": "string", + "description": "Book slug", + "name": "slug", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/schemas.BookDetailResponseSchema" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrorResponse" + } + } + } + } + }, + "/admin/books/by-username/{username}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieves a list of a particular author books with support for pagination and optional filtering based on book title.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Admin | Books" + ], + "summary": "List Author Books with Pagination", + "parameters": [ + { + "type": "string", + "description": "Username of the author", + "name": "username", + "in": "path", + "required": true + }, + { + "type": "integer", + "default": 1, + "description": "Current Page", + "name": "page", + "in": "query" + }, + { + "type": "string", + "description": "Title of the book to filter by", + "name": "title", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter by highest ratings", + "name": "rating", + "in": "query" + }, + { + "type": "string", + "description": "Filter by Genre slug", + "name": "genre_slug", + "in": "query" + }, + { + "type": "string", + "description": "Filter by Tag slug", + "name": "tag_slug", + "in": "query" } ], "responses": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index e1bd5ba..ad4bc68 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1367,6 +1367,23 @@ paths: in: query name: title type: string + - description: First name, last name or username of the book author to filter + by + in: query + name: name + type: string + - description: Filter by highest ratings + in: query + name: rating + type: boolean + - description: Filter by Genre slug + in: query + name: genre_slug + type: string + - description: Filter by Tag slug + in: query + name: tag_slug + type: string produces: - application/json responses: @@ -1383,6 +1400,83 @@ paths: summary: List Books with Pagination tags: - Admin | Books + /admin/books/book-detail/{slug}: + get: + description: This endpoint allows an admin to view details of a book + parameters: + - default: 1 + description: Current Page (for reviews pagination) + in: query + name: page + type: integer + - description: Book slug + in: path + name: slug + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/schemas.BookDetailResponseSchema' + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrorResponse' + security: + - BearerAuth: [] + summary: View Book Details + tags: + - Admin | Books + /admin/books/by-username/{username}: + get: + consumes: + - application/json + description: Retrieves a list of a particular author books with support for + pagination and optional filtering based on book title. + parameters: + - description: Username of the author + in: path + name: username + required: true + type: string + - default: 1 + description: Current Page + in: query + name: page + type: integer + - description: Title of the book to filter by + in: query + name: title + type: string + - description: Filter by highest ratings + in: query + name: rating + type: boolean + - description: Filter by Genre slug + in: query + name: genre_slug + type: string + - description: Filter by Tag slug + in: query + name: tag_slug + type: string + produces: + - application/json + responses: + "200": + description: Successfully retrieved list of books + schema: + $ref: '#/definitions/schemas.BooksResponseSchema' + "500": + description: Internal server error + schema: + $ref: '#/definitions/utils.ErrorResponse' + security: + - BearerAuth: [] + summary: List Author Books with Pagination + tags: + - Admin | Books /admin/books/contracts: get: consumes: diff --git a/initials/initial_data.go b/initials/initial_data.go index 9a50238..3eccf43 100644 --- a/initials/initial_data.go +++ b/initials/initial_data.go @@ -30,9 +30,9 @@ func createSuperUser(db *gorm.DB, cfg config.Config) models.User { func createWriter(db *gorm.DB, cfg config.Config) models.User { user := models.User{ FirstName: "Test", - LastName: "Writer", - Username: "test-writer", - AccountType: choices.ACCTYPE_WRITER, + LastName: "Author", + Username: "test-author", + AccountType: choices.ACCTYPE_AUTHOR, Email: cfg.FirstWriterEmail, Password: cfg.FirstWriterPassword, IsEmailVerified: true, diff --git a/managers/accounts.go b/managers/accounts.go index 97836e6..7744287 100644 --- a/managers/accounts.go +++ b/managers/accounts.go @@ -43,7 +43,7 @@ func (u UserManager) GetReaderByUsername(db *gorm.DB, username string) *models.U } func (u UserManager) GetWriterByUsername(db *gorm.DB, username string) *models.User { - user := models.User{Username: username, AccountType: choices.ACCTYPE_WRITER} + user := models.User{Username: username, AccountType: choices.ACCTYPE_AUTHOR} db.Scopes(scopes.VerifiedUserScope).Take(&user, user) if user.ID == uuid.Nil { return nil diff --git a/managers/books.go b/managers/books.go index 5150c00..c479b19 100644 --- a/managers/books.go +++ b/managers/books.go @@ -16,7 +16,7 @@ type BookManager struct { ModelList []models.Book } -func (b BookManager) GetLatest(db *gorm.DB, genreSlug string, tagSlug string, title string, usernameOpts ...string) ([]models.Book, *utils.ErrorResponse) { +func (b BookManager) GetLatest(db *gorm.DB, genreSlug string, tagSlug string, title string, byRating bool, username string, nameContains string) ([]models.Book, *utils.ErrorResponse) { books := b.ModelList query := db.Model(&b.Model) @@ -33,8 +33,7 @@ func (b BookManager) GetLatest(db *gorm.DB, genreSlug string, tagSlug string, ti tag := models.Tag{Slug: tagSlug} db.Take(&tag, tag) if tag.ID == uuid.Nil { - errData := utils.RequestErr(utils.ERR_NON_EXISTENT, "Invalid book tag") - return books, &errData + return books, nil } query = query.Where("books.id IN (?)", db.Table("book_tags").Select("book_id").Where("tag_id = ?", tag.ID)) } @@ -43,9 +42,8 @@ func (b BookManager) GetLatest(db *gorm.DB, genreSlug string, tagSlug string, ti query = query.Where("title ILIKE ?", "%"+title+"%") } - if len(usernameOpts) > 0 { - username := usernameOpts[0] - author := models.User{Username: username, AccountType: choices.ACCTYPE_WRITER} + if username != "" { + author := models.User{Username: username, AccountType: choices.ACCTYPE_AUTHOR} db.Take(&author, author) if author.ID == uuid.Nil { errData := utils.RequestErr(utils.ERR_NON_EXISTENT, "Invalid author username") @@ -53,7 +51,22 @@ func (b BookManager) GetLatest(db *gorm.DB, genreSlug string, tagSlug string, ti } query = query.Where(models.Book{AuthorID: author.ID}) } - query.Scopes(scopes.AuthorGenreTagBookScope).Order("created_at DESC").Find(&books) + + if nameContains != "" { + query = query.Joins("left join users on users.id = books.author_id"). + Where("users.username ILIKE ? OR users.first_name ILIKE ? OR users.last_name ILIKE ?", "%"+nameContains+"%", "%"+nameContains+"%", "%"+nameContains+"%") + } + + query = query.Select("books.*, COALESCE(AVG(reviews.rating), 0) AS avg_rating"). + Joins("left join reviews on reviews.book_id = books.id"). + Group("books.id") + + if byRating { + query = query.Order("COALESCE(AVG(reviews.rating), 0) DESC") + } else { + query = query.Order("books.created_at DESC") + } + query.Scopes(scopes.AuthorGenreTagBookPreloadScope).Find(&books) return books, nil } @@ -67,7 +80,7 @@ func (b BookManager) GetBySlug(db *gorm.DB, slug string) (*models.Book, *utils.E return &book, nil } -func (b BookManager) GetBookContracts(db *gorm.DB, name *string, contractStatus *choices.ContractStatusChoice) ([]models.Book) { +func (b BookManager) GetBookContracts(db *gorm.DB, name *string, contractStatus *choices.ContractStatusChoice) []models.Book { books := []models.Book{} q := db.Not("full_name = ?", "") if contractStatus != nil { @@ -92,7 +105,11 @@ func (b BookManager) GetContractedBookBySlug(db *gorm.DB, slug string) (*models. func (b BookManager) GetBySlugWithReviews(db *gorm.DB, slug string) (*models.Book, *utils.ErrorResponse) { book := models.Book{Slug: slug} - db.Scopes(scopes.AuthorGenreTagReviewsBookScope).Take(&book, book) + db.Scopes(scopes.AuthorGenreTagReviewsBookScope). + Select("books.*, AVG(reviews.rating) as avg_rating"). + Joins("LEFT JOIN reviews ON reviews.book_id = books.id"). + Group("books.id"). + Take(&book, book) if book.ID == uuid.Nil { errD := utils.RequestErr(utils.ERR_NON_EXISTENT, "No book with that slug") return nil, &errD @@ -155,7 +172,7 @@ func (b BookManager) SetContract(db *gorm.DB, book models.Book, idFrontImage str book.Synopsis = data.Synopsis book.Outline = data.Outline book.IntendedContract = data.IntendedContract - book.FullPurchaseMode = data.FullPurchaseMode + book.FullPurchaseMode = data.FullPurchaseMode if idFrontImage != "" { book.IDFrontImage = idFrontImage } @@ -216,11 +233,11 @@ func (t TagManager) GetAll(db *gorm.DB) []models.Tag { } func (t TagManager) GetBySlug(db *gorm.DB, slug string) *models.Tag { - - tag := models.Tag{Slug:slug} + + tag := models.Tag{Slug: slug} db.Take(&tag, tag) - if tag.ID == uuid.Nil{ + if tag.ID == uuid.Nil { return nil } @@ -239,11 +256,11 @@ func (g GenreManager) GetAll(db *gorm.DB) []models.Genre { } func (g GenreManager) GetBySlug(db *gorm.DB, slug string) *models.Genre { - - genre := models.Genre{Slug:slug} + + genre := models.Genre{Slug: slug} db.Take(&genre, genre) - if genre.ID == uuid.Nil{ + if genre.ID == uuid.Nil { return nil } @@ -266,7 +283,7 @@ func (b BoughtChapterManager) GetBoughtChapters(db *gorm.DB, buyer *models.User, chapters = append(chapters, boughtChapters[i].Chapter) } } - + if len(chapters) == 0 { // If the user hasn't bought any, he should see the first chapter for free chapters = book.Chapters[:1] diff --git a/models/book.go b/models/book.go index c092497..e7b2c62 100644 --- a/models/book.go +++ b/models/book.go @@ -53,6 +53,7 @@ type Book struct { Views string `gorm:"type:varchar(10000000)"` Reviews []Review `gorm:"<-:false"` Votes []Vote `gorm:"<-:false"` + AvgRating float64 // BOOK CONTRACT FullName string `gorm:"type: varchar(1000)"` diff --git a/models/choices/choices.go b/models/choices/choices.go index 2ed9954..3743a08 100644 --- a/models/choices/choices.go +++ b/models/choices/choices.go @@ -4,11 +4,11 @@ type AccType string const ( ACCTYPE_READER AccType = "READER" - ACCTYPE_WRITER AccType = "WRITER" + ACCTYPE_AUTHOR AccType = "AUTHOR" ) func (a AccType) IsValid() bool { switch a { - case ACCTYPE_READER, ACCTYPE_WRITER: + case ACCTYPE_READER, ACCTYPE_AUTHOR: return true } return false diff --git a/models/scopes/scopes.go b/models/scopes/scopes.go index 81ad883..c5cb2ad 100644 --- a/models/scopes/scopes.go +++ b/models/scopes/scopes.go @@ -21,8 +21,16 @@ func AuthorGenreTagBookScope(db *gorm.DB) *gorm.DB { return db.Joins("Author").Joins("Genre").Preload("Tags").Preload("Chapters").Preload("Votes") } +func AuthorGenreTagBookPreloadScope(db *gorm.DB) *gorm.DB { + return db.Preload("Author").Preload("Genre").Preload("Tags").Preload("Chapters").Preload("Votes") +} + +func TagsChaptersVotesBookScope(db *gorm.DB) *gorm.DB { + return db.Preload("Tags").Preload("Chapters").Preload("Votes") +} + func AuthorGenreTagReviewsBookScope(db *gorm.DB) *gorm.DB { - return db.Scopes(AuthorGenreTagBookScope).Preload("Reviews").Preload("Reviews.User").Preload("Reviews.Likes").Preload("Reviews.Replies") + return db.Scopes(AuthorGenreTagBookPreloadScope).Preload("Reviews").Preload("Reviews.User").Preload("Reviews.Likes").Preload("Reviews.Replies") } func BoughtChapterScope(db *gorm.DB) *gorm.DB { diff --git a/routes/admin_books.go b/routes/admin_books.go index 59fa78b..649b1c1 100644 --- a/routes/admin_books.go +++ b/routes/admin_books.go @@ -204,6 +204,10 @@ func (ep Endpoint) AdminDeleteBookTag(c *fiber.Ctx) error { // @Produce json // @Param page query int false "Current Page" default(1) // @Param title query string false "Title of the book to filter by" +// @Param name query string false "First name, last name or username of the book author to filter by" +// @Param rating query bool false "Filter by highest ratings" +// @Param genre_slug query string false "Filter by Genre slug" +// @Param tag_slug query string false "Filter by Tag slug" // @Success 200 {object} schemas.BooksResponseSchema "Successfully retrieved list of books" // @Failure 500 {object} utils.ErrorResponse "Internal server error" // @Router /admin/books [get] @@ -211,7 +215,12 @@ func (ep Endpoint) AdminDeleteBookTag(c *fiber.Ctx) error { func (ep Endpoint) AdminGetBooks(c *fiber.Ctx) error { db := ep.DB titleQuery := c.Query("title", "") - books, _ := bookManager.GetLatest(db, "", "", titleQuery) + ratingQuery := c.QueryBool("rating", false) + nameQuery := c.Query("name", "") + genreSlug := c.Query("genre_slug", "") + tagSlug := c.Query("tag_slug", "") + + books, _ := bookManager.GetLatest(db, genreSlug, tagSlug, titleQuery, ratingQuery, "", nameQuery) // Paginate and return books paginatedData, paginatedBooks, err := PaginateQueryset(books, c, 200) @@ -228,6 +237,83 @@ func (ep Endpoint) AdminGetBooks(c *fiber.Ctx) error { return c.Status(200).JSON(response) } +// @Summary List Author Books with Pagination +// @Description Retrieves a list of a particular author books with support for pagination and optional filtering based on book title. +// @Tags Admin | Books +// @Accept json +// @Produce json +// @Param username path string true "Username of the author" +// @Param page query int false "Current Page" default(1) +// @Param title query string false "Title of the book to filter by" +// @Param rating query bool false "Filter by highest ratings" +// @Param genre_slug query string false "Filter by Genre slug" +// @Param tag_slug query string false "Filter by Tag slug" +// @Success 200 {object} schemas.BooksResponseSchema "Successfully retrieved list of books" +// @Failure 500 {object} utils.ErrorResponse "Internal server error" +// @Router /admin/books/by-username/{username} [get] +// @Security BearerAuth +func (ep Endpoint) AdminGetAuthorBooks(c *fiber.Ctx) error { + db := ep.DB + titleQuery := c.Query("title", "") + ratingQuery := c.QueryBool("rating", false) + username := c.Params("username") + genreSlug := c.Query("genre_slug", "") + tagSlug := c.Query("tag_slug", "") + + author := models.User{Username: username, AccountType: choices.ACCTYPE_AUTHOR} + db.Take(&author, author) + + if author.ID == uuid.Nil { + return c.Status(404).JSON(utils.NotFoundErr("Author does not exist!")) + } + + books, _ := bookManager.GetLatest(db, genreSlug, tagSlug, titleQuery, ratingQuery, username, "") + + // Paginate and return books + paginatedData, paginatedBooks, err := PaginateQueryset(books, c, 200) + if err != nil { + return c.Status(400).JSON(err) + } + books = paginatedBooks.([]models.Book) + response := schemas.BooksResponseSchema{ + ResponseSchema: ResponseMessage("Books fetched successfully"), + Data: schemas.BooksResponseDataSchema{ + PaginatedResponseDataSchema: *paginatedData, + }.Init(books), + } + return c.Status(200).JSON(response) +} + +// @Summary View Book Details +// @Description This endpoint allows an admin to view details of a book +// @Tags Admin | Books +// @Param page query int false "Current Page (for reviews pagination)" default(1) +// @Param slug path string true "Book slug" +// @Success 200 {object} schemas.BookDetailResponseSchema +// @Failure 400 {object} utils.ErrorResponse +// @Router /admin/books/book-detail/{slug} [get] +// @Security BearerAuth +func (ep Endpoint) AdminGetBookDetails(c *fiber.Ctx) error { + db := ep.DB + book, err := bookManager.GetBySlugWithReviews(db, c.Params("slug")) + if err != nil { + return c.Status(404).JSON(err) + } + + // Paginate book reviews + paginatedData, paginatedReviews, err := PaginateQueryset(book.Reviews, c, 30) + if err != nil { + return c.Status(400).JSON(err) + } + + reviews := paginatedReviews.([]models.Review) + response := schemas.BookDetailResponseSchema{ + ResponseSchema: ResponseMessage("Book details fetched successfully"), + Data: schemas.BookDetailSchema{}.Init(*book, *paginatedData, reviews), + } + return c.Status(200).JSON(response) +} + // @Summary List Book Contracts with Pagination // @Description Retrieves a list of book contracts with support for pagination and optional filtering based on contract status. // @Tags Admin | Books diff --git a/routes/books.go b/routes/books.go index dc97283..46c5c4f 100644 --- a/routes/books.go +++ b/routes/books.go @@ -69,7 +69,7 @@ func (ep Endpoint) GetLatestBooks(c *fiber.Ctx) error { db := ep.DB genreSlug := c.Query("genre_slug") tagSlug := c.Query("tag_slug") - books, err := bookManager.GetLatest(db, genreSlug, tagSlug, "") + books, err := bookManager.GetLatest(db, genreSlug, tagSlug, "", false, "", "") if err != nil { return c.Status(404).JSON(err) } @@ -104,7 +104,7 @@ func (ep Endpoint) GetLatestAuthorBooks(c *fiber.Ctx) error { username := c.Params("username") genreSlug := c.Query("genre_slug") tagSlug := c.Query("tag_slug") - books, err := bookManager.GetLatest(db, genreSlug, tagSlug, "", username) + books, err := bookManager.GetLatest(db, genreSlug, tagSlug, "", false, username, "") if err != nil { return c.Status(404).JSON(err) } @@ -578,9 +578,11 @@ func (ep Endpoint) ReviewBook(c *fiber.Ctx) error { } // Check if current user has bought at least a chapter of the book - chapterBought := boughtChapterManager.CheckIfAtLeastAChapterWasBought(db, user, *book) - if !chapterBought { - return c.Status(400).JSON(utils.RequestErr(utils.ERR_NOT_ALLOWED, "Only the reader who has bought at least a chapter of the book can review it")) + if (user.SubscriptionExpired()) { + chapterBought := boughtChapterManager.CheckIfAtLeastAChapterWasBought(db, user, *book) + if !chapterBought { + return c.Status(400).JSON(utils.RequestErr(utils.ERR_NOT_ALLOWED, "User doesn't have active subscription and/or hasn't bought at least a chapter of the book")) + } } data := schemas.ReviewBookSchema{} if errCode, errData := ValidateRequest(c, &data); errData != nil { diff --git a/routes/middlewares.go b/routes/middlewares.go index a125098..3b4da1d 100644 --- a/routes/middlewares.go +++ b/routes/middlewares.go @@ -63,7 +63,7 @@ func (ep Endpoint) AuthorMiddleware(c *fiber.Ctx) error { if err != nil { return c.Status(401).JSON(utils.RequestErr(utils.ERR_INVALID_TOKEN, *err)) } - if user.AccountType != choices.ACCTYPE_WRITER { + if user.AccountType != choices.ACCTYPE_AUTHOR { return c.Status(401).JSON(utils.RequestErr(utils.ERR_AUTHORS_ONLY, "For Authors only!")) } c.Locals("user", user) diff --git a/routes/routers.go b/routes/routers.go index 34c566d..ff15f17 100644 --- a/routes/routers.go +++ b/routes/routers.go @@ -117,6 +117,8 @@ func SetupRoutes(app *fiber.App, db *gorm.DB, ws *internetcomputer.WalletService // Admin Books (2) adminRouter.Get("/books", endpoint.AdminMiddleware, endpoint.AdminGetBooks) + adminRouter.Get("/books/by-username/:username", endpoint.AdminMiddleware, endpoint.AdminGetAuthorBooks) + adminRouter.Get("/books/book-detail/:slug", endpoint.AdminMiddleware, endpoint.AdminGetBookDetails) adminRouter.Get("/books/contracts", endpoint.AdminMiddleware, endpoint.AdminGetBookContracts) adminRouter.Post("/books/genres", endpoint.AdminMiddleware, endpoint.AdminAddBookGenre) adminRouter.Post("/books/tags", endpoint.AdminMiddleware, endpoint.AdminAddBookTag) diff --git a/routes/utils.go b/routes/utils.go index 75b2dec..bef10f5 100644 --- a/routes/utils.go +++ b/routes/utils.go @@ -15,6 +15,8 @@ import ( "gorm.io/gorm" ) +var falsy = false + func ResponseMessage(message string) schemas.ResponseSchema { return schemas.ResponseSchema{Status: "success", Message: message} } @@ -149,7 +151,7 @@ func ViewBook(c *fiber.Ctx, db *gorm.DB, book models.Book) *models.Book { func IsAmongUserType(target string) bool { switch target { - case "ADMIN", string(choices.ACCTYPE_READER), string(choices.ACCTYPE_WRITER): + case "ADMIN", string(choices.ACCTYPE_READER), string(choices.ACCTYPE_AUTHOR): return true } return false diff --git a/schemas/book.go b/schemas/book.go index c3e95a9..8a9986d 100644 --- a/schemas/book.go +++ b/schemas/book.go @@ -79,6 +79,7 @@ type BookSchema struct { ChapterPrice int `json:"chapter_price"` Views int `json:"views"` Votes int `json:"votes"` + AvgRating float64 `json:"avg_rating"` CreatedAt time.Time `json:"created_at" example:"2024-06-05T02:32:34.462196+01:00"` UpdatedAt time.Time `json:"updated_at" example:"2024-06-05T02:32:34.462196+01:00"` } @@ -103,6 +104,7 @@ func (b BookSchema) Init(book models.Book) BookSchema { b.WordCount = book.WordCount() b.ChaptersCount = book.ChaptersCount() b.Votes = book.VotesCount() + b.AvgRating = book.AvgRating chapters := book.Chapters if len(chapters) > 0 {