diff --git a/client/components/app/ConfigSideNav.vue b/client/components/app/ConfigSideNav.vue
index 2321c4a3..09757ecf 100644
--- a/client/components/app/ConfigSideNav.vue
+++ b/client/components/app/ConfigSideNav.vue
@@ -38,12 +38,6 @@ export default {
userIsAdminOrUp() {
return this.$store.getters['user/getIsAdminOrUp']
},
- user() {
- return this.$store.state.user.user || {}
- },
- userId() {
- return this.user.id
- },
configRoutes() {
if (!this.userIsAdminOrUp) {
return [
@@ -70,6 +64,11 @@ export default {
title: 'Users',
path: '/config/users'
},
+ {
+ id: 'config-sessions',
+ title: 'Sessions',
+ path: '/config/sessions'
+ },
{
id: 'config-backups',
title: 'Backups',
@@ -93,11 +92,6 @@ export default {
title: 'Your Stats',
path: '/config/stats'
})
- configRoutes.push({
- id: 'config-users-id-sessions',
- title: 'Your Sessions',
- path: `/config/users/${this.userId}/sessions`
- })
}
return configRoutes
diff --git a/client/components/ui/InputDropdown.vue b/client/components/ui/InputDropdown.vue
index 64dba025..3efd2cf5 100644
--- a/client/components/ui/InputDropdown.vue
+++ b/client/components/ui/InputDropdown.vue
@@ -42,7 +42,8 @@ export default {
editable: {
type: Boolean,
default: true
- }
+ },
+ showAllWhenEmpty: Boolean
},
data() {
return {
@@ -72,6 +73,7 @@ export default {
itemsToShow() {
if (!this.editable) return this.items
if (!this.textInput || this.textInput === this.input) {
+ if (this.showAllWhenEmpty) return this.items
return []
}
return this.items.filter((i) => {
diff --git a/client/pages/config/sessions.vue b/client/pages/config/sessions.vue
new file mode 100644
index 00000000..c2dceae6
--- /dev/null
+++ b/client/pages/config/sessions.vue
@@ -0,0 +1,194 @@
+
+
+
+
+
+
Listening Sessions
+
+
+
+
+
+
+
+ Item |
+ User |
+ Play Method |
+ Device Info |
+ Listened |
+ Last Time |
+ Last Update |
+
+
+
+ {{ session.displayTitle }}
+ {{ session.displayAuthor }}
+ |
+
+ {{ filteredUserUsername }}
+ {{ session.user ? session.user.username : 'N/A' }}
+ |
+
+ {{ getPlayMethodName(session.playMethod) }}
+ |
+
+
+ |
+
+ {{ $elapsedPretty(session.timeListening) }}
+ |
+
+ {{ $secondsToTimestamp(session.currentTime) }}
+ |
+
+
+ {{ $dateDistanceFromNow(session.updatedAt) }}
+
+ |
+
+
+
+
+
Page {{ currentPage + 1 }} of {{ numPages }}
+
+
+
+
No sessions yet...
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/pages/config/users/_id/sessions.vue b/client/pages/config/users/_id/sessions.vue
index 1c56ce98..3098770c 100644
--- a/client/pages/config/users/_id/sessions.vue
+++ b/client/pages/config/users/_id/sessions.vue
@@ -17,7 +17,7 @@
-
Listening Sessions ({{ listeningSessions.length }})
+
Listening Sessions
diff --git a/server/Db.js b/server/Db.js
index 524fd90f..1a2937af 100644
--- a/server/Db.js
+++ b/server/Db.js
@@ -428,6 +428,15 @@ class Db {
})
}
+ getAllSessions() {
+ return this.sessionsDb.select(() => true).then((results) => {
+ return results.data || []
+ }).catch((error) => {
+ Logger.error('[Db] Failed to select sessions', error)
+ return []
+ })
+ }
+
selectUserSessions(userId) {
return this.sessionsDb.select((session) => session.userId === userId).then((results) => {
return results.data || []
diff --git a/server/controllers/SessionController.js b/server/controllers/SessionController.js
index 9b6d5a5c..2a7ff059 100644
--- a/server/controllers/SessionController.js
+++ b/server/controllers/SessionController.js
@@ -1,4 +1,5 @@
const Logger = require('../Logger')
+const { toNumber } = require('../utils/index')
class SessionController {
constructor() { }
@@ -7,6 +8,40 @@ class SessionController {
return res.json(req.session)
}
+ async getAllWithUserData(req, res) {
+ if (!req.user.isAdminOrUp) {
+ Logger.error(`[SessionController] getAllWithUserData: Non-admin user requested all session data ${req.user.id}/"${req.user.username}"`)
+ return res.sendStatus(404)
+ }
+
+ var listeningSessions = []
+ if (req.query.user) {
+ listeningSessions = await this.getUserListeningSessionsHelper(req.query.user)
+ } else {
+ listeningSessions = await this.getAllSessionsWithUserData()
+ }
+
+ 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
+ }
+
+ if (req.query.user) {
+ payload.userFilter = req.query.user
+ }
+
+ res.json(payload)
+ }
+
getSession(req, res) {
var libraryItem = this.db.getLibraryItem(req.session.libraryItemId)
var sessionForClient = req.session.toJSONForClient(libraryItem)
diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js
index aec6f3f0..e9b88c8c 100644
--- a/server/routers/ApiRouter.js
+++ b/server/routers/ApiRouter.js
@@ -174,6 +174,7 @@ class ApiRouter {
//
// Playback Session Routes
//
+ this.router.get('/sessions', SessionController.getAllWithUserData.bind(this))
this.router.get('/session/:id', SessionController.middleware.bind(this), SessionController.getSession.bind(this))
this.router.post('/session/:id/sync', SessionController.middleware.bind(this), SessionController.sync.bind(this))
this.router.post('/session/:id/close', SessionController.middleware.bind(this), SessionController.close.bind(this))
@@ -310,6 +311,19 @@ class ApiRouter {
return userSessions.sort((a, b) => b.updatedAt - a.updatedAt)
}
+ async getAllSessionsWithUserData() {
+ var sessions = await this.db.getAllSessions()
+ sessions.sort((a, b) => b.updatedAt - a.updatedAt)
+ return sessions.map(se => {
+ var user = this.db.users.find(u => u.id === se.userId)
+ var _se = {
+ ...se,
+ user: user ? { id: user.id, username: user.username } : null
+ }
+ return _se
+ })
+ }
+
async getUserListeningStatsHelpers(userId) {
const today = date.format(new Date(), 'YYYY-MM-DD')