Fixed error for Ratings page and stats page & added controlls to make Rating system and Ratings page optional (admin can turn it on or off for the server)

This commit is contained in:
fannta1990 2026-02-09 10:58:17 +01:00
parent 3a8075a077
commit e4e2770fbd
10 changed files with 128 additions and 49 deletions

View file

@ -997,46 +997,45 @@ class LibraryController {
stats.numAudioTracks = bookStats.numAudioFiles
// Get top 10 rated items
const topRatedReviews = await Database.reviewModel.findAll({
attributes: [
'libraryItemId',
[Sequelize.fn('AVG', Sequelize.col('rating')), 'avgRating'],
[Sequelize.fn('COUNT', Sequelize.col('id')), 'numReviews']
],
include: [
{
model: Database.libraryItemModel,
attributes: ['id'],
where: { libraryId: req.library.id },
include: [
{
model: Database.bookModel,
attributes: ['id'],
include: [
{
model: Database.bookMetadataModel,
attributes: ['title']
}
]
}
]
try {
const topRatedReviews = await Database.reviewModel.findAll({
attributes: [
'libraryItemId',
[Sequelize.fn('AVG', Sequelize.col('review.rating')), 'avgRating'],
[Sequelize.fn('COUNT', Sequelize.col('review.id')), 'numReviews']
],
include: [
{
model: Database.libraryItemModel,
attributes: ['id'],
where: { libraryId: req.library.id },
include: [
{
model: Database.bookModel,
attributes: ['id', 'title']
}
]
}
],
group: ['libraryItemId', 'libraryItem.id', 'libraryItem.book.id'],
order: [
[Sequelize.literal('avgRating'), 'DESC'],
[Sequelize.literal('numReviews'), 'DESC']
],
limit: 10
})
stats.topRatedItems = topRatedReviews.map((r) => {
return {
id: r.libraryItemId,
title: r.libraryItem?.book?.title || 'Unknown',
avgRating: parseFloat(r.getDataValue('avgRating')),
numReviews: parseInt(r.getDataValue('numReviews'))
}
],
group: ['libraryItemId', 'libraryItem.id', 'libraryItem.book.id', 'libraryItem.book.bookMetadata.id'],
order: [
[Sequelize.literal('avgRating'), 'DESC'],
[Sequelize.literal('numReviews'), 'DESC']
],
limit: 10
})
stats.topRatedItems = topRatedReviews.map((r) => {
return {
id: r.libraryItemId,
title: r.libraryItem?.book?.bookMetadata?.title || 'Unknown',
avgRating: parseFloat(r.getDataValue('avgRating')),
numReviews: parseInt(r.getDataValue('numReviews'))
}
})
})
} catch (error) {
Logger.error('[LibraryController] Failed to get top rated items for stats', error)
stats.topRatedItems = []
}
} else {
const genres = await libraryItemsPodcastFilters.getGenresWithCount(req.library.id)
const podcastStats = await libraryItemsPodcastFilters.getPodcastLibraryStats(req.library.id)

View file

@ -116,6 +116,10 @@ class ReviewController {
* @param {import('express').Response} res
*/
async findAllForUser(req, res) {
if (!Database.serverSettings.enableReviews) {
return res.status(403).send('Review feature is disabled')
}
try {
const reviews = await Database.reviewModel.findAll({
where: { userId: req.user.id },
@ -124,10 +128,23 @@ class ReviewController {
model: Database.libraryItemModel,
include: [
{
model: Database.bookModel
model: Database.bookModel,
include: [
{
model: Database.authorModel,
through: { attributes: [] }
},
{
model: Database.seriesModel,
through: { attributes: ['id', 'sequence'] }
}
]
},
{
model: Database.podcastModel
model: Database.podcastModel,
include: {
model: Database.podcastEpisodeModel
}
}
]
}
@ -138,7 +155,22 @@ class ReviewController {
res.json(reviews.map((r) => {
const json = r.toOldJSON()
if (r.libraryItem) {
json.libraryItem = r.libraryItem.toOldJSONMinified()
// Manually set media if missing (Sequelize hooks don't run on nested includes)
if (!r.libraryItem.media) {
if (r.libraryItem.mediaType === 'book' && r.libraryItem.book) {
r.libraryItem.media = r.libraryItem.book
} else if (r.libraryItem.mediaType === 'podcast' && r.libraryItem.podcast) {
r.libraryItem.media = r.libraryItem.podcast
}
}
if (r.libraryItem.media) {
try {
json.libraryItem = r.libraryItem.toOldJSONMinified()
} catch (err) {
Logger.error(`[ReviewController] Failed to minify library item ${r.libraryItem.id}`, err)
}
}
}
return json
}))
@ -156,6 +188,10 @@ class ReviewController {
* @param {import('express').NextFunction} next
*/
async middleware(req, res, next) {
if (!Database.serverSettings.enableReviews) {
return res.status(403).send('Review feature is disabled')
}
// Basic library item access check
req.libraryItem = await Database.libraryItemModel.getExpandedById(req.params.id)
if (!req.libraryItem?.media) return res.sendStatus(404)

View file

@ -55,6 +55,9 @@ class ServerSettings {
this.language = 'en-us'
this.allowedOrigins = []
this.enableReviews = true
this.showReviewsInSidebar = true
this.logLevel = Logger.logLevel
this.version = packageJson.version
@ -122,6 +125,9 @@ class ServerSettings {
this.timeFormat = settings.timeFormat || 'HH:mm'
this.language = settings.language || 'en-us'
this.allowedOrigins = settings.allowedOrigins || []
this.enableReviews = settings.enableReviews !== false
this.showReviewsInSidebar = settings.showReviewsInSidebar !== false
this.logLevel = settings.logLevel || Logger.logLevel
this.version = settings.version || null
this.buildNumber = settings.buildNumber || 0 // Added v2.4.5
@ -234,6 +240,8 @@ class ServerSettings {
timeFormat: this.timeFormat,
language: this.language,
allowedOrigins: this.allowedOrigins,
enableReviews: this.enableReviews,
showReviewsInSidebar: this.showReviewsInSidebar,
logLevel: this.logLevel,
version: this.version,
buildNumber: this.buildNumber,