2024-08-11 11:53:30 -05:00
const { Request , Response } = require ( 'express' )
2021-11-21 20:00:40 -06:00
const Logger = require ( '../Logger' )
2022-11-24 15:53:58 -06:00
const SocketAuthority = require ( '../SocketAuthority' )
2023-07-04 18:14:44 -05:00
const Database = require ( '../Database' )
2022-08-14 10:24:41 -05:00
const { sort } = require ( '../libs/fastSort' )
2024-08-11 12:16:45 -05:00
const { toNumber , isNullOrNaN } = require ( '../utils/index' )
2023-12-19 17:19:33 -06:00
const userStats = require ( '../utils/queries/userStats' )
2021-11-21 20:00:40 -06:00
2024-08-11 11:53:30 -05:00
/ * *
2024-08-11 17:01:25 -05:00
* @ typedef RequestUserObject
2024-08-11 16:07:29 -05:00
* @ property { import ( '../models/User' ) } user
2024-08-11 11:53:30 -05:00
*
2024-08-11 17:01:25 -05:00
* @ typedef { Request & RequestUserObject } RequestWithUser
2024-08-11 11:53:30 -05:00
* /
2021-11-21 20:00:40 -06:00
class MeController {
2024-05-23 16:32:34 -05:00
constructor ( ) { }
2021-11-21 20:00:40 -06:00
2024-08-11 11:53:30 -05:00
/ * *
* GET : / a p i / m e
*
* @ param { RequestWithUser } req
* @ param { Response } res
* /
2023-02-05 16:52:17 -06:00
getCurrentUser ( req , res ) {
2024-08-11 16:07:29 -05:00
res . json ( req . user . toOldJSONForBrowser ( ) )
2023-02-05 16:52:17 -06:00
}
2024-08-11 11:53:30 -05:00
/ * *
* GET : / a p i / m e / l i s t e n i n g - s e s s i o n s
*
2024-08-11 13:09:53 -05:00
* @ this import ( '../routers/ApiRouter' )
*
2024-08-11 11:53:30 -05:00
* @ param { RequestWithUser } req
* @ param { Response } res
* /
2021-11-21 20:00:40 -06:00
async getListeningSessions ( req , res ) {
2024-08-11 16:07:29 -05:00
const listeningSessions = await this . getUserListeningSessionsHelper ( req . user . id )
2022-06-04 10:52:37 -05:00
const itemsPerPage = toNumber ( req . query . itemsPerPage , 10 ) || 10
const page = toNumber ( req . query . page , 0 )
const start = page * itemsPerPage
const sessions = listeningSessions . slice ( start , start + itemsPerPage )
const payload = {
total : listeningSessions . length ,
numPages : Math . ceil ( listeningSessions . length / itemsPerPage ) ,
page ,
itemsPerPage ,
sessions
}
res . json ( payload )
2021-11-21 20:00:40 -06:00
}
2024-05-23 16:32:34 -05:00
/ * *
* GET : / a p i / m e / i t e m / l i s t e n i n g - s e s s i o n s / : l i b r a r y I t e m I d / : e p i s o d e I d
*
* @ this import ( '../routers/ApiRouter' )
*
2024-08-11 11:53:30 -05:00
* @ param { RequestWithUser } req
* @ param { Response } res
2024-05-23 16:32:34 -05:00
* /
2024-05-14 10:51:50 +02:00
async getItemListeningSessions ( req , res ) {
2026-02-15 22:01:36 -05:00
const libraryItem = await Database . libraryItemModel . getExpandedById ( req . params . libraryItemId )
2024-05-14 10:51:50 +02:00
const episode = await Database . podcastEpisodeModel . findByPk ( req . params . episodeId )
2024-05-05 13:14:30 +02:00
2025-01-04 15:20:41 -06:00
if ( ! libraryItem || ( libraryItem . isPodcast && ! episode ) ) {
2024-05-23 16:35:36 -05:00
Logger . error ( ` [MeController] Media item not found for library item id " ${ req . params . libraryItemId } " ` )
return res . sendStatus ( 404 )
2024-05-05 13:14:30 +02:00
}
2026-02-15 22:01:36 -05:00
// Check if user has access to this library item
if ( ! req . user . checkCanAccessLibraryItem ( libraryItem ) ) {
Logger . error ( ` [MeController] User " ${ req . user . username } " attempted to access listening sessions for library item " ${ req . params . libraryItemId } " without access ` )
return res . sendStatus ( 403 )
}
2024-05-23 16:35:36 -05:00
const mediaItemId = episode ? . id || libraryItem . mediaId
2024-08-11 16:07:29 -05:00
let listeningSessions = await this . getUserItemListeningSessionsHelper ( req . user . id , mediaItemId )
2024-05-05 13:14:30 +02:00
const itemsPerPage = toNumber ( req . query . itemsPerPage , 10 ) || 10
const page = toNumber ( req . query . page , 0 )
const start = page * itemsPerPage
const sessions = listeningSessions . slice ( start , start + itemsPerPage )
const payload = {
total : listeningSessions . length ,
numPages : Math . ceil ( listeningSessions . length / itemsPerPage ) ,
page ,
itemsPerPage ,
sessions
}
res . json ( payload )
}
2024-08-11 11:53:30 -05:00
/ * *
* GET : / a p i / m e / l i s t e n i n g - s t a t s
*
2024-08-11 13:09:53 -05:00
* @ this import ( '../routers/ApiRouter' )
*
2024-08-11 11:53:30 -05:00
* @ param { RequestWithUser } req
* @ param { Response } res
* /
2021-11-21 20:00:40 -06:00
async getListeningStats ( req , res ) {
2024-08-11 16:07:29 -05:00
const listeningStats = await this . getUserListeningStatsHelpers ( req . user . id )
2021-11-21 20:00:40 -06:00
res . json ( listeningStats )
}
2024-08-11 11:53:30 -05:00
/ * *
* GET : / a p i / m e / p r o g r e s s / : i d / : e p i s o d e I d ?
*
* @ param { RequestWithUser } req
* @ param { Response } res
* /
2022-06-03 18:59:42 -05:00
async getMediaProgress ( req , res ) {
2024-08-11 16:07:29 -05:00
const mediaProgress = req . user . getOldMediaProgress ( req . params . id , req . params . episodeId || null )
2022-06-03 18:59:42 -05:00
if ( ! mediaProgress ) {
return res . sendStatus ( 404 )
}
res . json ( mediaProgress )
}
2024-08-11 11:53:30 -05:00
/ * *
* DELETE : / a p i / m e / p r o g r e s s / : i d
*
* @ param { RequestWithUser } req
* @ param { Response } res
* /
2022-03-26 11:59:34 -05:00
async removeMediaProgress ( req , res ) {
2026-02-15 22:01:36 -05:00
// Verify the media progress belongs to the current user
const mediaProgress = req . user . mediaProgresses . find ( ( mp ) => mp . id === req . params . id )
if ( ! mediaProgress ) {
Logger . error ( ` [MeController] Media progress not found or does not belong to user " ${ req . user . username } " ` )
return res . sendStatus ( 404 )
}
2024-08-11 11:53:30 -05:00
await Database . mediaProgressModel . removeById ( req . params . id )
2024-08-11 16:07:29 -05:00
req . user . mediaProgresses = req . user . mediaProgresses . filter ( ( mp ) => mp . id !== req . params . id )
2022-06-25 11:01:01 -05:00
2024-08-11 16:07:29 -05:00
SocketAuthority . clientEmitter ( req . user . id , 'user_updated' , req . user . toOldJSONForBrowser ( ) )
2021-11-21 20:00:40 -06:00
res . sendStatus ( 200 )
}
2024-08-11 11:53:30 -05:00
/ * *
* PATCH : / a p i / m e / p r o g r e s s / : l i b r a r y I t e m I d / : e p i s o d e I d ?
* TODO : Update to use mediaItemId and mediaItemType
*
* @ param { RequestWithUser } req
* @ param { Response } res
* /
async createUpdateMediaProgress ( req , res ) {
const progressUpdatePayload = {
... req . body ,
libraryItemId : req . params . libraryItemId ,
episodeId : req . params . episodeId
2022-03-26 17:41:26 -05:00
}
2024-08-11 16:07:29 -05:00
const mediaProgressResponse = await req . user . createUpdateMediaProgressFromPayload ( progressUpdatePayload )
2024-08-11 11:53:30 -05:00
if ( mediaProgressResponse . error ) {
return res . status ( mediaProgressResponse . statusCode || 400 ) . send ( mediaProgressResponse . error )
2022-03-26 17:41:26 -05:00
}
2024-08-11 16:07:29 -05:00
SocketAuthority . clientEmitter ( req . user . id , 'user_updated' , req . user . toOldJSONForBrowser ( ) )
2022-03-26 17:41:26 -05:00
res . sendStatus ( 200 )
}
2024-08-11 11:53:30 -05:00
/ * *
* PATCH : / a p i / m e / p r o g r e s s / b a t c h / u p d a t e
* TODO : Update to use mediaItemId and mediaItemType
*
* @ param { RequestWithUser } req
* @ param { Response } res
* /
2022-03-26 11:59:34 -05:00
async batchUpdateMediaProgress ( req , res ) {
2023-07-04 18:14:44 -05:00
const itemProgressPayloads = req . body
if ( ! itemProgressPayloads ? . length ) {
2022-11-23 02:15:36 +01:00
return res . status ( 400 ) . send ( 'Missing request payload' )
2021-11-21 20:00:40 -06:00
}
2024-08-11 11:53:30 -05:00
let hasUpdated = false
2023-07-04 18:14:44 -05:00
for ( const itemProgress of itemProgressPayloads ) {
2024-08-11 16:07:29 -05:00
const mediaProgressResponse = await req . user . createUpdateMediaProgressFromPayload ( itemProgress )
2024-08-11 11:53:30 -05:00
if ( mediaProgressResponse . error ) {
Logger . error ( ` [MeController] batchUpdateMediaProgress: ${ mediaProgressResponse . error } ` )
continue
2022-03-17 13:33:22 -05:00
} else {
2024-08-11 11:53:30 -05:00
hasUpdated = true
2021-11-21 20:00:40 -06:00
}
2023-07-04 18:14:44 -05:00
}
2021-11-21 20:00:40 -06:00
2024-08-11 11:53:30 -05:00
if ( hasUpdated ) {
2024-08-11 16:07:29 -05:00
SocketAuthority . clientEmitter ( req . user . id , 'user_updated' , req . user . toOldJSONForBrowser ( ) )
2021-11-21 20:00:40 -06:00
}
res . sendStatus ( 200 )
}
2024-08-11 11:53:30 -05:00
/ * *
* POST : / a p i / m e / i t e m / : i d / b o o k m a r k
*
* @ param { RequestWithUser } req
* @ param { Response } res
* /
2022-03-17 20:28:04 -05:00
async createBookmark ( req , res ) {
2026-02-15 22:01:36 -05:00
const libraryItem = await Database . libraryItemModel . getExpandedById ( req . params . id )
if ( ! libraryItem ) {
return res . sendStatus ( 404 )
}
// Check if user has access to this library item
if ( ! req . user . checkCanAccessLibraryItem ( libraryItem ) ) {
Logger . error ( ` [MeController] User " ${ req . user . username } " attempted to create bookmark for library item " ${ req . params . id } " without access ` )
return res . sendStatus ( 403 )
}
2023-08-12 15:52:09 -05:00
2022-03-17 20:28:04 -05:00
const { time , title } = req . body
2024-08-11 12:16:45 -05:00
if ( isNullOrNaN ( time ) ) {
Logger . error ( ` [MeController] createBookmark invalid time ` , time )
return res . status ( 400 ) . send ( 'Invalid time' )
}
if ( ! title || typeof title !== 'string' ) {
Logger . error ( ` [MeController] createBookmark invalid title ` , title )
return res . status ( 400 ) . send ( 'Invalid title' )
}
2024-08-11 16:07:29 -05:00
const bookmark = await req . user . createBookmark ( req . params . id , time , title )
SocketAuthority . clientEmitter ( req . user . id , 'user_updated' , req . user . toOldJSONForBrowser ( ) )
2022-03-17 20:28:04 -05:00
res . json ( bookmark )
}
2024-08-11 12:16:45 -05:00
/ * *
* PATCH : / a p i / m e / i t e m / : i d / b o o k m a r k
*
* @ param { RequestWithUser } req
* @ param { Response } res
* /
2022-03-17 20:28:04 -05:00
async updateBookmark ( req , res ) {
2026-02-15 22:01:36 -05:00
const libraryItem = await Database . libraryItemModel . getExpandedById ( req . params . id )
if ( ! libraryItem ) {
return res . sendStatus ( 404 )
}
// Check if user has access to this library item
if ( ! req . user . checkCanAccessLibraryItem ( libraryItem ) ) {
Logger . error ( ` [MeController] User " ${ req . user . username } " attempted to update bookmark for library item " ${ req . params . id } " without access ` )
return res . sendStatus ( 403 )
}
2023-08-12 15:52:09 -05:00
2022-03-17 20:28:04 -05:00
const { time , title } = req . body
2024-08-11 12:16:45 -05:00
if ( isNullOrNaN ( time ) ) {
Logger . error ( ` [MeController] updateBookmark invalid time ` , time )
return res . status ( 400 ) . send ( 'Invalid time' )
}
if ( ! title || typeof title !== 'string' ) {
Logger . error ( ` [MeController] updateBookmark invalid title ` , title )
return res . status ( 400 ) . send ( 'Invalid title' )
2022-03-17 20:28:04 -05:00
}
2023-08-12 15:52:09 -05:00
2024-08-11 16:07:29 -05:00
const bookmark = await req . user . updateBookmark ( req . params . id , time , title )
2024-08-11 12:16:45 -05:00
if ( ! bookmark ) {
Logger . error ( ` [MeController] updateBookmark not found for library item id " ${ req . params . id } " and time " ${ time } " ` )
return res . sendStatus ( 404 )
}
2023-08-12 15:52:09 -05:00
2024-08-11 16:07:29 -05:00
SocketAuthority . clientEmitter ( req . user . id , 'user_updated' , req . user . toOldJSONForBrowser ( ) )
2022-03-17 20:28:04 -05:00
res . json ( bookmark )
}
2024-08-11 12:16:45 -05:00
/ * *
* DELETE : / a p i / m e / i t e m / : i d / b o o k m a r k / : t i m e
*
* @ param { RequestWithUser } req
* @ param { Response } res
* /
2022-03-17 20:28:04 -05:00
async removeBookmark ( req , res ) {
2026-02-15 22:01:36 -05:00
const libraryItem = await Database . libraryItemModel . getExpandedById ( req . params . id )
if ( ! libraryItem ) {
return res . sendStatus ( 404 )
}
// Check if user has access to this library item
if ( ! req . user . checkCanAccessLibraryItem ( libraryItem ) ) {
Logger . error ( ` [MeController] User " ${ req . user . username } " attempted to remove bookmark for library item " ${ req . params . id } " without access ` )
return res . sendStatus ( 403 )
}
2023-08-12 15:52:09 -05:00
const time = Number ( req . params . time )
2024-08-11 12:16:45 -05:00
if ( isNaN ( time ) ) {
return res . status ( 400 ) . send ( 'Invalid time' )
}
2022-03-17 20:28:04 -05:00
2024-08-11 16:07:29 -05:00
if ( ! req . user . findBookmark ( req . params . id , time ) ) {
2022-03-17 20:28:04 -05:00
Logger . error ( ` [MeController] removeBookmark not found ` )
return res . sendStatus ( 404 )
}
2023-08-12 15:52:09 -05:00
2024-08-11 16:07:29 -05:00
await req . user . removeBookmark ( req . params . id , time )
2024-08-11 12:16:45 -05:00
2024-08-11 16:07:29 -05:00
SocketAuthority . clientEmitter ( req . user . id , 'user_updated' , req . user . toOldJSONForBrowser ( ) )
2022-03-17 20:28:04 -05:00
res . sendStatus ( 200 )
}
2024-08-11 13:09:53 -05:00
/ * *
* PATCH : / a p i / m e / p a s s w o r d
* User change password . Requires current password .
* Guest users cannot change password .
*
* @ this import ( '../routers/ApiRouter' )
*
* @ param { RequestWithUser } req
* @ param { Response } res
* /
2025-07-07 15:04:40 -05:00
async updatePassword ( req , res ) {
2024-08-11 16:07:29 -05:00
if ( req . user . isGuest ) {
Logger . error ( ` [MeController] Guest user " ${ req . user . username } " attempted to change password ` )
2025-07-07 15:04:40 -05:00
return res . sendStatus ( 403 )
2022-04-29 18:38:13 -05:00
}
2025-07-07 15:04:40 -05:00
const { password , newPassword } = req . body
2025-07-22 15:17:00 -05:00
if ( ( typeof password !== 'string' && password !== null ) || ( typeof newPassword !== 'string' && newPassword !== null ) ) {
2025-07-07 15:04:40 -05:00
return res . status ( 400 ) . send ( 'Missing or invalid password or new password' )
}
const result = await this . auth . localAuthStrategy . changePassword ( req . user , password , newPassword )
if ( result . error ) {
return res . status ( 400 ) . send ( result . error )
}
res . sendStatus ( 200 )
2021-11-21 20:00:40 -06:00
}
2024-08-11 13:09:53 -05:00
/ * *
* GET : / a p i / m e / i t e m s - i n - p r o g r e s s
* Pull items in progress for all libraries
* Used in Android Auto in progress list since there is no easy library selection
* TODO : Update to use mediaItemId and mediaItemType . Use sort & limit in query
*
* @ param { RequestWithUser } req
* @ param { Response } res
* /
2023-09-04 16:33:55 -05:00
async getAllLibraryItemsInProgress ( req , res ) {
2022-08-14 10:24:41 -05:00
const limit = ! isNaN ( req . query . limit ) ? Number ( req . query . limit ) || 25 : 25
2024-08-11 16:07:29 -05:00
const mediaProgressesInProgress = req . user . mediaProgresses . filter ( ( mp ) => ! mp . isFinished && ( mp . currentTime > 0 || mp . ebookProgress > 0 ) )
2024-08-11 13:09:53 -05:00
const libraryItemsIds = [ ... new Set ( mediaProgressesInProgress . map ( ( mp ) => mp . extraData ? . libraryItemId ) . filter ( ( id ) => id ) ) ]
2025-01-04 15:20:41 -06:00
const libraryItems = await Database . libraryItemModel . findAllExpandedWhere ( { id : libraryItemsIds } )
2024-08-11 13:09:53 -05:00
2023-03-25 14:07:35 -05:00
let itemsInProgress = [ ]
2024-08-11 13:09:53 -05:00
for ( const mediaProgress of mediaProgressesInProgress ) {
const oldMediaProgress = mediaProgress . getOldMediaProgress ( )
const libraryItem = libraryItems . find ( ( li ) => li . id === oldMediaProgress . libraryItemId )
if ( libraryItem ) {
2025-01-04 15:20:41 -06:00
if ( oldMediaProgress . episodeId && libraryItem . isPodcast ) {
const episode = libraryItem . media . podcastEpisodes . find ( ( ep ) => ep . id === oldMediaProgress . episodeId )
2024-08-11 13:09:53 -05:00
if ( episode ) {
const libraryItemWithEpisode = {
2025-01-04 15:20:41 -06:00
... libraryItem . toOldJSONMinified ( ) ,
recentEpisode : episode . toOldJSON ( libraryItem . id ) ,
2024-08-11 13:09:53 -05:00
progressLastUpdate : oldMediaProgress . lastUpdate
}
itemsInProgress . push ( libraryItemWithEpisode )
2022-08-14 10:24:41 -05:00
}
2024-08-11 13:09:53 -05:00
} else if ( ! oldMediaProgress . episodeId ) {
itemsInProgress . push ( {
2025-01-04 15:20:41 -06:00
... libraryItem . toOldJSONMinified ( ) ,
2024-08-11 13:09:53 -05:00
progressLastUpdate : oldMediaProgress . lastUpdate
} )
2022-08-14 10:24:41 -05:00
}
}
}
2024-05-23 16:32:34 -05:00
itemsInProgress = sort ( itemsInProgress )
. desc ( ( li ) => li . progressLastUpdate )
. slice ( 0 , limit )
2022-08-14 10:24:41 -05:00
res . json ( {
libraryItems : itemsInProgress
} )
}
2022-09-28 17:12:27 -05:00
2024-08-11 13:09:53 -05:00
/ * *
* GET : / a p i / m e / s e r i e s / : i d / r e m o v e - f r o m - c o n t i n u e - l i s t e n i n g
*
* @ param { RequestWithUser } req
* @ param { Response } res
* /
2022-09-28 17:45:39 -05:00
async removeSeriesFromContinueListening ( req , res ) {
2024-08-11 13:09:53 -05:00
if ( ! ( await Database . seriesModel . checkExistsById ( req . params . id ) ) ) {
2022-09-28 17:45:39 -05:00
Logger . error ( ` [MeController] removeSeriesFromContinueListening: Series ${ req . params . id } not found ` )
2022-09-28 17:12:27 -05:00
return res . sendStatus ( 404 )
}
2024-08-11 16:07:29 -05:00
const hasUpdated = await req . user . addSeriesToHideFromContinueListening ( req . params . id )
2022-09-28 17:12:27 -05:00
if ( hasUpdated ) {
2024-08-11 16:07:29 -05:00
SocketAuthority . clientEmitter ( req . user . id , 'user_updated' , req . user . toOldJSONForBrowser ( ) )
2022-09-28 17:12:27 -05:00
}
2024-08-11 16:07:29 -05:00
res . json ( req . user . toOldJSONForBrowser ( ) )
2022-09-28 17:12:27 -05:00
}
2022-09-28 17:45:39 -05:00
2024-08-11 13:09:53 -05:00
/ * *
* GET : api / me / series / : id / readd - to - continue - listening
*
* @ param { RequestWithUser } req
* @ param { Response } res
* /
2022-11-15 17:20:57 -06:00
async readdSeriesFromContinueListening ( req , res ) {
2024-08-11 13:09:53 -05:00
if ( ! ( await Database . seriesModel . checkExistsById ( req . params . id ) ) ) {
2022-11-15 17:20:57 -06:00
Logger . error ( ` [MeController] readdSeriesFromContinueListening: Series ${ req . params . id } not found ` )
return res . sendStatus ( 404 )
}
2024-08-11 16:07:29 -05:00
const hasUpdated = await req . user . removeSeriesFromHideFromContinueListening ( req . params . id )
2022-11-15 17:20:57 -06:00
if ( hasUpdated ) {
2024-08-11 16:07:29 -05:00
SocketAuthority . clientEmitter ( req . user . id , 'user_updated' , req . user . toOldJSONForBrowser ( ) )
2022-11-15 17:20:57 -06:00
}
2024-08-11 16:07:29 -05:00
res . json ( req . user . toOldJSONForBrowser ( ) )
2022-11-15 17:20:57 -06:00
}
2024-08-11 13:09:53 -05:00
/ * *
* GET : api / me / progress / : id / remove - from - continue - listening
*
* @ param { RequestWithUser } req
* @ param { Response } res
* /
2022-09-28 17:45:39 -05:00
async removeItemFromContinueListening ( req , res ) {
2024-08-11 16:07:29 -05:00
const mediaProgress = req . user . mediaProgresses . find ( ( mp ) => mp . id === req . params . id )
2023-09-07 17:49:35 -05:00
if ( ! mediaProgress ) {
return res . sendStatus ( 404 )
}
2024-08-11 13:09:53 -05:00
// Already hidden
if ( mediaProgress . hideFromContinueListening ) {
2024-08-11 16:07:29 -05:00
return res . json ( req . user . toOldJSONForBrowser ( ) )
2022-09-28 17:45:39 -05:00
}
2024-08-11 13:09:53 -05:00
mediaProgress . hideFromContinueListening = true
await mediaProgress . save ( )
2024-08-11 16:07:29 -05:00
SocketAuthority . clientEmitter ( req . user . id , 'user_updated' , req . user . toOldJSONForBrowser ( ) )
2024-08-11 13:09:53 -05:00
2024-08-11 16:07:29 -05:00
res . json ( req . user . toOldJSONForBrowser ( ) )
2022-09-28 17:45:39 -05:00
}
2023-12-19 17:19:33 -06:00
2024-10-26 16:34:34 -04:00
/ * *
* POST : / a p i / m e / e r e a d e r - d e v i c e s
*
* @ param { RequestWithUser } req
* @ param { Response } res
* /
async updateUserEReaderDevices ( req , res ) {
if ( ! req . body . ereaderDevices || ! Array . isArray ( req . body . ereaderDevices ) ) {
return res . status ( 400 ) . send ( 'Invalid payload. ereaderDevices array required' )
}
const userEReaderDevices = req . body . ereaderDevices
for ( const device of userEReaderDevices ) {
if ( ! device . name || ! device . email ) {
return res . status ( 400 ) . send ( 'Invalid payload. ereaderDevices array items must have name and email' )
} else if ( device . availabilityOption !== 'specificUsers' || device . users ? . length !== 1 || device . users [ 0 ] !== req . user . id ) {
return res . status ( 400 ) . send ( 'Invalid payload. ereaderDevices array items must have availabilityOption "specificUsers" and only the current user' )
}
}
const otherDevices = Database . emailSettings . ereaderDevices . filter ( ( device ) => {
return ! Database . emailSettings . checkUserCanAccessDevice ( device , req . user ) || device . users ? . length !== 1
} )
const ereaderDevices = otherDevices . concat ( userEReaderDevices )
// Check for duplicate names
const nameSet = new Set ( )
const hasDupes = ereaderDevices . some ( ( device ) => {
if ( nameSet . has ( device . name ) ) {
return true // Duplicate found
}
nameSet . add ( device . name )
return false
} )
if ( hasDupes ) {
return res . status ( 400 ) . send ( 'Invalid payload. Duplicate "name" field found.' )
}
const updated = Database . emailSettings . update ( { ereaderDevices } )
if ( updated ) {
await Database . updateSetting ( Database . emailSettings )
SocketAuthority . clientEmitter ( req . user . id , 'ereader-devices-updated' , {
2025-07-24 17:29:08 -05:00
ereaderDevices : Database . emailSettings . getEReaderDevices ( req . user )
2024-10-26 16:34:34 -04:00
} )
}
res . json ( {
ereaderDevices : Database . emailSettings . getEReaderDevices ( req . user )
} )
}
2023-12-19 17:19:33 -06:00
/ * *
2024-02-16 16:05:02 -06:00
* GET : / a p i / m e / s t a t s / y e a r / : y e a r
2024-05-05 13:14:30 +02:00
*
* @ param { import ( 'express' ) . Request } req
* @ param { import ( 'express' ) . Response } res
2023-12-19 17:19:33 -06:00
* /
async getStatsForYear ( req , res ) {
const year = Number ( req . params . year )
if ( isNaN ( year ) || year < 2000 || year > 9999 ) {
Logger . error ( ` [MeController] Invalid year " ${ year } " ` )
return res . status ( 400 ) . send ( 'Invalid year' )
}
2024-08-11 16:07:29 -05:00
const data = await userStats . getStatsForYear ( req . user . id , year )
2023-12-19 17:19:33 -06:00
res . json ( data )
}
2021-11-21 20:00:40 -06:00
}
2024-05-05 13:14:30 +02:00
module . exports = new MeController ( )