mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-06-06 10:42:44 +02:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 99110f587a |
@@ -68,7 +68,7 @@
|
|||||||
<p class="text-xs truncate" v-html="getDeviceInfoString(session.deviceInfo)" />
|
<p class="text-xs truncate" v-html="getDeviceInfoString(session.deviceInfo)" />
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center w-24 min-w-24 sm:w-32 sm:min-w-32">
|
<td class="text-center w-24 min-w-24 sm:w-32 sm:min-w-32">
|
||||||
<p class="text-xs font-mono">{{ $elapsedPretty(session.timeListening) }}</p>
|
<p class="text-xs font-mono">{{ $elapsedPrettyLocalized(session.timeListening) }}</p>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center hover:underline w-24 min-w-24" @click.stop="clickCurrentTime(session)">
|
<td class="text-center hover:underline w-24 min-w-24" @click.stop="clickCurrentTime(session)">
|
||||||
<p class="text-xs font-mono">{{ $secondsToTimestamp(session.currentTime) }}</p>
|
<p class="text-xs font-mono">{{ $secondsToTimestamp(session.currentTime) }}</p>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
<p class="text-xs truncate" v-html="getDeviceInfoString(session.deviceInfo)" />
|
<p class="text-xs truncate" v-html="getDeviceInfoString(session.deviceInfo)" />
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<p class="text-xs font-mono">{{ $elapsedPretty(session.timeListening) }}</p>
|
<p class="text-xs font-mono">{{ $elapsedPrettyLocalized(session.timeListening) }}</p>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center hover:underline" @click.stop="clickCurrentTime(session)">
|
<td class="text-center hover:underline" @click.stop="clickCurrentTime(session)">
|
||||||
<p class="text-xs font-mono">{{ $secondsToTimestamp(session.currentTime) }}</p>
|
<p class="text-xs font-mono">{{ $secondsToTimestamp(session.currentTime) }}</p>
|
||||||
|
|||||||
@@ -37,6 +37,48 @@ Vue.prototype.$elapsedPretty = (seconds, useFullNames = false, useMilliseconds =
|
|||||||
return `${hours} ${useFullNames ? `hour${hours === 1 ? '' : 's'}` : 'hr'} ${minutes} ${useFullNames ? `minute${minutes === 1 ? '' : 's'}` : 'min'}`
|
return `${hours} ${useFullNames ? `hour${hours === 1 ? '' : 's'}` : 'hr'} ${minutes} ${useFullNames ? `minute${minutes === 1 ? '' : 's'}` : 'min'}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vue.prototype.$elapsedPrettyLocalized = (seconds, useFullNames = false, useMilliseconds = false) => {
|
||||||
|
if (isNaN(seconds) || seconds === null) return ''
|
||||||
|
|
||||||
|
try {
|
||||||
|
const df = new Intl.DurationFormat(Vue.prototype.$languageCodes.current, {
|
||||||
|
style: useFullNames ? 'long' : 'short'
|
||||||
|
})
|
||||||
|
|
||||||
|
const duration = {}
|
||||||
|
|
||||||
|
if (seconds < 60) {
|
||||||
|
if (useMilliseconds && seconds < 1) {
|
||||||
|
duration.milliseconds = Math.floor(seconds * 1000)
|
||||||
|
} else {
|
||||||
|
duration.seconds = Math.floor(seconds)
|
||||||
|
}
|
||||||
|
} else if (seconds < 3600) {
|
||||||
|
// 1 hour
|
||||||
|
duration.minutes = Math.floor(seconds / 60)
|
||||||
|
} else if (seconds < 86400) {
|
||||||
|
// 1 day
|
||||||
|
duration.hours = Math.floor(seconds / 3600)
|
||||||
|
const minutes = Math.floor((seconds % 3600) / 60)
|
||||||
|
if (minutes > 0) {
|
||||||
|
duration.minutes = minutes
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
duration.days = Math.floor(seconds / 86400)
|
||||||
|
const hours = Math.floor((seconds % 86400) / 3600)
|
||||||
|
if (hours > 0) {
|
||||||
|
duration.hours = hours
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return df.format(duration)
|
||||||
|
} catch (error) {
|
||||||
|
// Handle not supported
|
||||||
|
console.warn('Intl.DurationFormat not supported, not localizing duration')
|
||||||
|
return Vue.prototype.$elapsedPretty(seconds, useFullNames, useMilliseconds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Vue.prototype.$secondsToTimestamp = (seconds, includeMs = false, alwaysIncludeHours = false) => {
|
Vue.prototype.$secondsToTimestamp = (seconds, includeMs = false, alwaysIncludeHours = false) => {
|
||||||
if (!seconds) {
|
if (!seconds) {
|
||||||
return alwaysIncludeHours ? '00:00:00' : '0:00'
|
return alwaysIncludeHours ? '00:00:00' : '0:00'
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ class OidcAuthStrategy {
|
|||||||
throw new Error(`Group claim ${Database.serverSettings.authOpenIDGroupClaim} not found or empty in userinfo`)
|
throw new Error(`Group claim ${Database.serverSettings.authOpenIDGroupClaim} not found or empty in userinfo`)
|
||||||
}
|
}
|
||||||
|
|
||||||
let user = await Database.userModel.findOrCreateUserFromOpenIdUserInfo(userinfo)
|
let user = await Database.userModel.findOrCreateUserFromOpenIdUserInfo(userinfo, this)
|
||||||
|
|
||||||
if (!user?.isActive) {
|
if (!user?.isActive) {
|
||||||
throw new Error('User not active or not found')
|
throw new Error('User not active or not found')
|
||||||
|
|||||||
@@ -81,18 +81,6 @@ class TokenManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a JWT token for a given user
|
|
||||||
* TODO: Old method with no expiration
|
|
||||||
* @deprecated
|
|
||||||
*
|
|
||||||
* @param {{ id:string, username:string }} user
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
static generateAccessToken(user) {
|
|
||||||
return jwt.sign({ userId: user.id, username: user.username }, TokenManager.TokenSecret)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to generate a jwt token for a given user
|
* Function to generate a jwt token for a given user
|
||||||
* TODO: Old method with no expiration
|
* TODO: Old method with no expiration
|
||||||
@@ -102,7 +90,7 @@ class TokenManager {
|
|||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
generateAccessToken(user) {
|
generateAccessToken(user) {
|
||||||
return TokenManager.generateAccessToken(user)
|
return jwt.sign({ userId: user.id, username: user.username }, TokenManager.TokenSecret)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
const uuidv4 = require('uuid').v4
|
const uuidv4 = require('uuid').v4
|
||||||
const sequelize = require('sequelize')
|
const sequelize = require('sequelize')
|
||||||
const { LRUCache } = require('lru-cache')
|
|
||||||
|
|
||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
const SocketAuthority = require('../SocketAuthority')
|
const SocketAuthority = require('../SocketAuthority')
|
||||||
const { isNullOrNaN } = require('../utils')
|
const { isNullOrNaN } = require('../utils')
|
||||||
const TokenManager = require('../auth/TokenManager')
|
const { LRUCache } = require('lru-cache')
|
||||||
|
|
||||||
class UserCache {
|
class UserCache {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -215,9 +213,10 @@ class User extends Model {
|
|||||||
* or creates a new user if configured to do so.
|
* or creates a new user if configured to do so.
|
||||||
*
|
*
|
||||||
* @param {Object} userinfo
|
* @param {Object} userinfo
|
||||||
|
* @param {import('../Auth')} auth
|
||||||
* @returns {Promise<User>}
|
* @returns {Promise<User>}
|
||||||
*/
|
*/
|
||||||
static async findOrCreateUserFromOpenIdUserInfo(userinfo) {
|
static async findOrCreateUserFromOpenIdUserInfo(userinfo, auth) {
|
||||||
let user = await this.getUserByOpenIDSub(userinfo.sub)
|
let user = await this.getUserByOpenIDSub(userinfo.sub)
|
||||||
|
|
||||||
// Matched by sub
|
// Matched by sub
|
||||||
@@ -291,7 +290,7 @@ class User extends Model {
|
|||||||
// If no existing user was matched, auto-register if configured
|
// If no existing user was matched, auto-register if configured
|
||||||
if (global.ServerSettings.authOpenIDAutoRegister) {
|
if (global.ServerSettings.authOpenIDAutoRegister) {
|
||||||
Logger.info(`[User] openid: Auto-registering user with sub "${userinfo.sub}"`, userinfo)
|
Logger.info(`[User] openid: Auto-registering user with sub "${userinfo.sub}"`, userinfo)
|
||||||
user = await this.createUserFromOpenIdUserInfo(userinfo)
|
user = await this.createUserFromOpenIdUserInfo(userinfo, auth)
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,15 +301,16 @@ class User extends Model {
|
|||||||
/**
|
/**
|
||||||
* Create user from openid userinfo
|
* Create user from openid userinfo
|
||||||
* @param {Object} userinfo
|
* @param {Object} userinfo
|
||||||
|
* @param {import('../Auth')} auth
|
||||||
* @returns {Promise<User>}
|
* @returns {Promise<User>}
|
||||||
*/
|
*/
|
||||||
static async createUserFromOpenIdUserInfo(userinfo) {
|
static async createUserFromOpenIdUserInfo(userinfo, auth) {
|
||||||
const userId = uuidv4()
|
const userId = uuidv4()
|
||||||
// TODO: Ensure username is unique?
|
// TODO: Ensure username is unique?
|
||||||
const username = userinfo.preferred_username || userinfo.name || userinfo.sub
|
const username = userinfo.preferred_username || userinfo.name || userinfo.sub
|
||||||
const email = userinfo.email && userinfo.email_verified ? userinfo.email : null
|
const email = userinfo.email && userinfo.email_verified ? userinfo.email : null
|
||||||
|
|
||||||
const token = TokenManager.generateAccessToken({ id: userId, username })
|
const token = auth.generateAccessToken({ id: userId, username })
|
||||||
|
|
||||||
const newUser = {
|
const newUser = {
|
||||||
id: userId,
|
id: userId,
|
||||||
|
|||||||
Reference in New Issue
Block a user