-
+
{{ label }}{{ note }}
diff --git a/client/layouts/default.vue b/client/layouts/default.vue
index d1a521be..578a1deb 100644
--- a/client/layouts/default.vue
+++ b/client/layouts/default.vue
@@ -51,7 +51,7 @@ export default {
},
isShowingSideRail() {
if (!this.$route.name) return false
- return !this.$route.name.startsWith('config')
+ return !this.$route.name.startsWith('config') && this.$store.state.libraries.currentLibraryId
},
appContentMarginLeft() {
return this.isShowingSideRail ? 80 : 0
@@ -173,6 +173,7 @@ export default {
this.$store.commit('libraries/addUpdate', library)
},
async libraryRemoved(library) {
+ console.log('Library removed', library)
this.$store.commit('libraries/remove', library)
// When removed currently selected library then set next accessible library
@@ -191,7 +192,8 @@ export default {
this.$router.push(`/library/${nextLibrary.id}`)
}
} else {
- console.error('User has no accessible libraries')
+ console.error('User has no more accessible libraries')
+ this.$store.commit('libraries/setCurrentLibrary', null)
}
}
},
diff --git a/client/pages/config/libraries.vue b/client/pages/config/libraries.vue
index 9808a76b..0c4762ce 100644
--- a/client/pages/config/libraries.vue
+++ b/client/pages/config/libraries.vue
@@ -1,16 +1,26 @@
\ No newline at end of file
diff --git a/client/pages/config/library-stats.vue b/client/pages/config/library-stats.vue
index 3485dd81..82d0ef5a 100644
--- a/client/pages/config/library-stats.vue
+++ b/client/pages/config/library-stats.vue
@@ -67,6 +67,12 @@
\ No newline at end of file
diff --git a/client/store/libraries.js b/client/store/libraries.js
index acd8e96e..20f32cd1 100644
--- a/client/store/libraries.js
+++ b/client/store/libraries.js
@@ -2,7 +2,7 @@ export const state = () => ({
libraries: [],
lastLoad: 0,
listeners: [],
- currentLibraryId: 'main',
+ currentLibraryId: null,
folders: [],
issues: 0,
folderLastUpdate: 0,
diff --git a/index.js b/index.js
index d4fcc7a6..4400312e 100644
--- a/index.js
+++ b/index.js
@@ -1,4 +1,4 @@
-if(process.env.TOKEN_SECRET == null) process.env.TOKEN_SECRET = '09f26e402586e2faa8da4c98a35f1b20d6b033c6097befa8be3486a829587fe2f90a832bd3ff9d42710a4da095a2ce285b009f0c3730cd9b8e1af3eb84df6611'
+if (process.env.TOKEN_SECRET == null) process.env.TOKEN_SECRET = '09f26e402586e2faa8da4c98a35f1b20d6b033c6097befa8be3486a829587fe2f90a832bd3ff9d42710a4da095a2ce285b009f0c3730cd9b8e1af3eb84df6611'
const server = require('./server/Server')
global.appRoot = __dirname
@@ -9,20 +9,20 @@ if (isDev) {
process.env.PORT = devEnv.Port
process.env.CONFIG_PATH = devEnv.ConfigPath
process.env.METADATA_PATH = devEnv.MetadataPath
- process.env.AUDIOBOOK_PATH = devEnv.AudiobookPath
process.env.FFMPEG_PATH = devEnv.FFmpegPath
process.env.FFPROBE_PATH = devEnv.FFProbePath
+ process.env.SOURCE = 'local'
}
const PORT = process.env.PORT || 80
const HOST = process.env.HOST || '0.0.0.0'
const CONFIG_PATH = process.env.CONFIG_PATH || '/config'
-const AUDIOBOOK_PATH = process.env.AUDIOBOOK_PATH || '/audiobooks'
const METADATA_PATH = process.env.METADATA_PATH || '/metadata'
const UID = process.env.AUDIOBOOKSHELF_UID || 99
const GID = process.env.AUDIOBOOKSHELF_GID || 100
+const SOURCE = process.env.SOURCE || 'docker'
-console.log('Config', CONFIG_PATH, METADATA_PATH, AUDIOBOOK_PATH)
+console.log('Config', CONFIG_PATH, METADATA_PATH)
-const Server = new server(PORT, HOST, UID, GID, CONFIG_PATH, METADATA_PATH, AUDIOBOOK_PATH)
+const Server = new server(SOURCE, PORT, HOST, UID, GID, CONFIG_PATH, METADATA_PATH)
Server.start()
diff --git a/prod.js b/prod.js
index 3a4f56ac..20ea004d 100644
--- a/prod.js
+++ b/prod.js
@@ -1,16 +1,16 @@
const optionDefinitions = [
{ name: 'config', alias: 'c', type: String },
- { name: 'audiobooks', alias: 'a', type: String },
{ name: 'metadata', alias: 'm', type: String },
{ name: 'port', alias: 'p', type: String },
- { name: 'host', alias: 'h', type: String }
+ { name: 'host', alias: 'h', type: String },
+ { name: 'source', alias: 's', type: String }
]
const commandLineArgs = require('command-line-args')
const options = commandLineArgs(optionDefinitions)
const Path = require('path')
-if(process.env.TOKEN_SECRET == null) process.env.TOKEN_SECRET = '09f26e402586e2faa8da4c98a35f1b20d6b033c6097befa8be3486a829587fe2f90a832bd3ff9d42710a4da095a2ce285b009f0c3730cd9b8e1af3eb84df6611'
+if (process.env.TOKEN_SECRET == null) process.env.TOKEN_SECRET = '09f26e402586e2faa8da4c98a35f1b20d6b033c6097befa8be3486a829587fe2f90a832bd3ff9d42710a4da095a2ce285b009f0c3730cd9b8e1af3eb84df6611'
process.env.NODE_ENV = 'production'
const server = require('./server/Server')
@@ -18,18 +18,17 @@ const server = require('./server/Server')
global.appRoot = __dirname
var inputConfig = options.config ? Path.resolve(options.config) : null
-var inputAudiobook = options.audiobooks ? Path.resolve(options.audiobooks) : null
var inputMetadata = options.metadata ? Path.resolve(options.metadata) : null
const PORT = options.port || process.env.PORT || 3333
const HOST = options.host || process.env.HOST || "0.0.0.0"
const CONFIG_PATH = inputConfig || process.env.CONFIG_PATH || Path.resolve('config')
-const AUDIOBOOK_PATH = inputAudiobook || process.env.AUDIOBOOK_PATH || Path.resolve('audiobooks')
const METADATA_PATH = inputMetadata || process.env.METADATA_PATH || Path.resolve('metadata')
const UID = 99
const GID = 100
+const SOURCE = options.source || 'debian'
-console.log(process.env.NODE_ENV, 'Config', CONFIG_PATH, METADATA_PATH, AUDIOBOOK_PATH)
+console.log(process.env.NODE_ENV, 'Config', CONFIG_PATH, METADATA_PATH)
-const Server = new server(PORT, HOST, UID, GID, CONFIG_PATH, METADATA_PATH, AUDIOBOOK_PATH)
+const Server = new server(SOURCE, PORT, HOST, UID, GID, CONFIG_PATH, METADATA_PATH)
Server.start()
diff --git a/server/Auth.js b/server/Auth.js
index 852f5f2f..b30e060f 100644
--- a/server/Auth.js
+++ b/server/Auth.js
@@ -17,14 +17,6 @@ class Auth {
return this.db.users
}
- init() {
- var root = this.users.find(u => u.type === 'root')
- if (!root) {
- Logger.fatal('No Root User', this.users)
- throw new Error('No Root User')
- }
- }
-
cors(req, res, next) {
res.header('Access-Control-Allow-Origin', '*')
res.header("Access-Control-Allow-Methods", 'GET, POST, PATCH, PUT, DELETE, OPTIONS')
diff --git a/server/Db.js b/server/Db.js
index 2addf69b..524fd90f 100644
--- a/server/Db.js
+++ b/server/Db.js
@@ -46,6 +46,10 @@ class Db {
this.previousVersion = null
}
+ get hasRootUser() {
+ return this.users.some(u => u.id === 'root')
+ }
+
getEntityDb(entityName) {
if (entityName === 'user') return this.usersDb
else if (entityName === 'session') return this.sessionsDb
@@ -70,33 +74,6 @@ class Db {
return null
}
- getDefaultUser(token) {
- return new User({
- id: 'root',
- type: 'root',
- username: 'root',
- pash: '',
- stream: null,
- token,
- isActive: true,
- createdAt: Date.now()
- })
- }
-
- getDefaultLibrary() {
- var defaultLibrary = new Library()
- defaultLibrary.setData({
- id: 'main',
- name: 'Main',
- folder: { // Generates default folder
- id: 'audiobooks',
- fullPath: global.AudiobookPath,
- libraryId: 'main'
- }
- })
- return defaultLibrary
- }
-
reinit() {
this.libraryItemsDb = new njodb.Database(this.LibraryItemsPath)
this.usersDb = new njodb.Database(this.UsersPath)
@@ -123,23 +100,36 @@ class Db {
})
}
+ createRootUser(username, pash, token) {
+ const newRoot = new User({
+ id: 'root',
+ type: 'root',
+ username,
+ pash,
+ token,
+ isActive: true,
+ createdAt: Date.now()
+ })
+ return this.insertEntity('user', newRoot)
+ }
+
async init() {
await this.load()
// Insert Defaults
- var rootUser = this.users.find(u => u.type === 'root')
- if (!rootUser) {
- var token = await jwt.sign({ userId: 'root' }, process.env.TOKEN_SECRET)
- Logger.debug('Generated default token', token)
- Logger.info('[Db] Root user created')
- await this.insertEntity('user', this.getDefaultUser(token))
- } else {
- Logger.info(`[Db] Root user exists, pw: ${rootUser.hasPw}`)
- }
+ // var rootUser = this.users.find(u => u.type === 'root')
+ // if (!rootUser) {
+ // var token = await jwt.sign({ userId: 'root' }, process.env.TOKEN_SECRET)
+ // Logger.debug('Generated default token', token)
+ // Logger.info('[Db] Root user created')
+ // await this.insertEntity('user', this.getDefaultUser(token))
+ // } else {
+ // Logger.info(`[Db] Root user exists, pw: ${rootUser.hasPw}`)
+ // }
- if (!this.libraries.length) {
- await this.insertEntity('library', this.getDefaultLibrary())
- }
+ // if (!this.libraries.length) {
+ // await this.insertEntity('library', this.getDefaultLibrary())
+ // }
if (!this.serverSettings) {
this.serverSettings = new ServerSettings()
diff --git a/server/Server.js b/server/Server.js
index 7156b4e3..d76906f5 100644
--- a/server/Server.js
+++ b/server/Server.js
@@ -34,18 +34,18 @@ const AudioMetadataMangaer = require('./managers/AudioMetadataManager')
const RssFeedManager = require('./managers/RssFeedManager')
class Server {
- constructor(PORT, HOST, UID, GID, CONFIG_PATH, METADATA_PATH, AUDIOBOOK_PATH) {
+ constructor(SOURCE, PORT, HOST, UID, GID, CONFIG_PATH, METADATA_PATH) {
+ this.Source = SOURCE
this.Port = PORT
this.Host = HOST
global.Uid = isNaN(UID) ? 0 : Number(UID)
global.Gid = isNaN(GID) ? 0 : Number(GID)
global.ConfigPath = Path.normalize(CONFIG_PATH)
- global.AudiobookPath = Path.normalize(AUDIOBOOK_PATH)
global.MetadataPath = Path.normalize(METADATA_PATH)
+
// Fix backslash if not on Windows
if (process.platform !== 'win32') {
global.ConfigPath = global.ConfigPath.replace(/\\/g, '/')
- global.AudiobookPath = global.AudiobookPath.replace(/\\/g, '/')
global.MetadataPath = global.MetadataPath.replace(/\\/g, '/')
}
@@ -57,10 +57,6 @@ class Server {
fs.mkdirSync(global.MetadataPath)
filePerms.setDefaultDirSync(global.MetadataPath, false)
}
- if (!fs.pathExistsSync(global.AudiobookPath)) {
- fs.mkdirSync(global.AudiobookPath)
- filePerms.setDefaultDirSync(global.AudiobookPath, false)
- }
this.db = new Db()
this.watcher = new Watcher()
@@ -140,8 +136,6 @@ class Server {
await this.db.init()
}
- this.auth.init()
-
await this.checkUserMediaProgress() // Remove invalid user item progress
await this.purgeMetadata() // Remove metadata folders without library item
@@ -231,6 +225,25 @@ class Server {
app.post('/login', this.getLoginRateLimiter(), (req, res) => this.auth.login(req, res))
app.post('/logout', this.authMiddleware.bind(this), this.logout.bind(this))
+ app.post('/init', (req, res) => {
+ if (this.db.hasRootUser) {
+ Logger.error(`[Server] attempt to init server when server already has a root user`)
+ return res.sendStatus(500)
+ }
+ this.initializeServer(req, res)
+ })
+ app.get('/status', (req, res) => {
+ // status check for client to see if server has been initialized
+ // server has been initialized if a root user exists
+ const payload = {
+ isInit: this.db.hasRootUser
+ }
+ if (!payload.isInit) {
+ payload.ConfigPath = global.ConfigPath
+ payload.MetadataPath = global.MetadataPath
+ }
+ res.json(payload)
+ })
app.get('/ping', (req, res) => {
Logger.info('Recieved ping')
res.json({ success: true })
@@ -293,6 +306,17 @@ class Server {
})
}
+ async initializeServer(req, res) {
+ Logger.info(`[Server] Initializing new server`)
+ const newRoot = req.body.newRoot
+ let rootPash = newRoot.password ? await this.auth.hashPass(newRoot.password) : ''
+ if (!rootPash) Logger.warn(`[Server] Creating root user with no password`)
+ let rootToken = await this.auth.generateAccessToken({ userId: 'root' })
+ await this.db.createRootUser(newRoot.username, rootPash, rootToken)
+
+ res.sendStatus(200)
+ }
+
async filesChanged(fileUpdates) {
Logger.info('[Server]', fileUpdates.length, 'Files Changed')
await this.scanner.scanFilesChanged(fileUpdates)
@@ -433,7 +457,6 @@ class Server {
const initialPayload = {
// TODO: this is sent with user auth now, update mobile app to use that then remove this
serverSettings: this.db.serverSettings.toJSON(),
- audiobookPath: global.AudiobookPath,
metadataPath: global.MetadataPath,
configPath: global.ConfigPath,
user: client.user.toJSONForBrowser(),
diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js
index 8e2f0e3b..e81081cf 100644
--- a/server/controllers/LibraryController.js
+++ b/server/controllers/LibraryController.js
@@ -42,6 +42,7 @@ class LibraryController {
newLibraryPayload.displayOrder = this.db.libraries.length + 1
library.setData(newLibraryPayload)
await this.db.insertEntity('library', library)
+ // TODO: Only emit to users that have access
this.emitter('library_added', library.toJSON())
// Add library watcher
diff --git a/server/managers/DownloadManager.js b/server/managers/DownloadManager.js
index 05ae72fd..32fba3bf 100644
--- a/server/managers/DownloadManager.js
+++ b/server/managers/DownloadManager.js
@@ -242,13 +242,6 @@ class DownloadManager {
if (shouldIncludeCover) {
var _cover = audiobook.book.coverFullPath.replace(/\\/g, '/')
- // Supporting old local file prefix
- var bookCoverPath = audiobook.book.cover ? audiobook.book.cover.replace(/\\/g, '/') : null
- if (!_cover && bookCoverPath && bookCoverPath.startsWith('/local')) {
- _cover = Path.posix.join(global.AudiobookPath, _cover.replace('/local', ''))
- Logger.debug('Local cover url', _cover)
- }
-
ffmpegInputs.push({
input: _cover,
options: ['-f image2pipe']