mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-04-07 01:15:44 +02:00
Migrate backups manager
This commit is contained in:
parent
0a179e4eed
commit
254ba1f089
@ -95,8 +95,9 @@ export default {
|
|||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
this.isBackingUp = false
|
this.isBackingUp = false
|
||||||
console.error('Failed', error)
|
console.error('Failed to apply backup', error)
|
||||||
this.$toast.error(this.$strings.ToastBackupRestoreFailed)
|
const errorMsg = error.response.data || this.$strings.ToastBackupRestoreFailed
|
||||||
|
this.$toast.error(errorMsg)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
deleteBackupClick(backup) {
|
deleteBackupClick(backup) {
|
||||||
|
@ -11,7 +11,7 @@ class Database {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.sequelize = null
|
this.sequelize = null
|
||||||
this.dbPath = null
|
this.dbPath = null
|
||||||
this.isNew = false // New database.sqlite created
|
this.isNew = false // New absdatabase.sqlite created
|
||||||
|
|
||||||
// Temporarily using format of old DB
|
// Temporarily using format of old DB
|
||||||
// below data should be loaded from the DB as needed
|
// below data should be loaded from the DB as needed
|
||||||
@ -40,14 +40,14 @@ class Database {
|
|||||||
|
|
||||||
async checkHasDb() {
|
async checkHasDb() {
|
||||||
if (!await fs.pathExists(this.dbPath)) {
|
if (!await fs.pathExists(this.dbPath)) {
|
||||||
Logger.info(`[Database] database.sqlite not found at ${this.dbPath}`)
|
Logger.info(`[Database] absdatabase.sqlite not found at ${this.dbPath}`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
async init(force = false) {
|
async init(force = false) {
|
||||||
this.dbPath = Path.join(global.ConfigPath, 'database.sqlite')
|
this.dbPath = Path.join(global.ConfigPath, 'absdatabase.sqlite')
|
||||||
|
|
||||||
// First check if this is a new database
|
// First check if this is a new database
|
||||||
this.isNew = !(await this.checkHasDb()) || force
|
this.isNew = !(await this.checkHasDb()) || force
|
||||||
@ -59,7 +59,7 @@ class Database {
|
|||||||
await this.buildModels(force)
|
await this.buildModels(force)
|
||||||
Logger.info(`[Database] Db initialized`, Object.keys(this.sequelize.models))
|
Logger.info(`[Database] Db initialized`, Object.keys(this.sequelize.models))
|
||||||
|
|
||||||
await this.loadData(force)
|
await this.loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect() {
|
async connect() {
|
||||||
@ -83,6 +83,17 @@ class Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async disconnect() {
|
||||||
|
Logger.info(`[Database] Disconnecting sqlite db`)
|
||||||
|
await this.sequelize.close()
|
||||||
|
this.sequelize = null
|
||||||
|
}
|
||||||
|
|
||||||
|
async reconnect() {
|
||||||
|
Logger.info(`[Database] Reconnecting sqlite db`)
|
||||||
|
await this.init()
|
||||||
|
}
|
||||||
|
|
||||||
buildModels(force = false) {
|
buildModels(force = false) {
|
||||||
require('./models/User')(this.sequelize)
|
require('./models/User')(this.sequelize)
|
||||||
require('./models/Library')(this.sequelize)
|
require('./models/Library')(this.sequelize)
|
||||||
@ -109,8 +120,8 @@ class Database {
|
|||||||
return this.sequelize.sync({ force, alter: false })
|
return this.sequelize.sync({ force, alter: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadData(force = false) {
|
async loadData() {
|
||||||
if (this.isNew && await dbMigration.checkShouldMigrate(force)) {
|
if (this.isNew && await dbMigration.checkShouldMigrate()) {
|
||||||
Logger.info(`[Database] New database was created and old database was detected - migrating old to new`)
|
Logger.info(`[Database] New database was created and old database was detected - migrating old to new`)
|
||||||
await dbMigration.migrate(this.models)
|
await dbMigration.migrate(this.models)
|
||||||
}
|
}
|
||||||
@ -143,6 +154,7 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createRootUser(username, pash, token) {
|
async createRootUser(username, pash, token) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
const newUser = await this.models.user.createRootUser(username, pash, token)
|
const newUser = await this.models.user.createRootUser(username, pash, token)
|
||||||
if (newUser) {
|
if (newUser) {
|
||||||
this.users.push(newUser)
|
this.users.push(newUser)
|
||||||
@ -152,60 +164,73 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateServerSettings() {
|
updateServerSettings() {
|
||||||
|
if (!this.sequelize) return false
|
||||||
global.ServerSettings = this.serverSettings.toJSON()
|
global.ServerSettings = this.serverSettings.toJSON()
|
||||||
return this.updateSetting(this.serverSettings)
|
return this.updateSetting(this.serverSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSetting(settings) {
|
updateSetting(settings) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.setting.updateSettingObj(settings.toJSON())
|
return this.models.setting.updateSettingObj(settings.toJSON())
|
||||||
}
|
}
|
||||||
|
|
||||||
async createUser(oldUser) {
|
async createUser(oldUser) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
await this.models.user.createFromOld(oldUser)
|
await this.models.user.createFromOld(oldUser)
|
||||||
this.users.push(oldUser)
|
this.users.push(oldUser)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUser(oldUser) {
|
updateUser(oldUser) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.user.updateFromOld(oldUser)
|
return this.models.user.updateFromOld(oldUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateBulkUsers(oldUsers) {
|
updateBulkUsers(oldUsers) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return Promise.all(oldUsers.map(u => this.updateUser(u)))
|
return Promise.all(oldUsers.map(u => this.updateUser(u)))
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeUser(userId) {
|
async removeUser(userId) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
await this.models.user.removeById(userId)
|
await this.models.user.removeById(userId)
|
||||||
this.users = this.users.filter(u => u.id !== userId)
|
this.users = this.users.filter(u => u.id !== userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
upsertMediaProgress(oldMediaProgress) {
|
upsertMediaProgress(oldMediaProgress) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.mediaProgress.upsertFromOld(oldMediaProgress)
|
return this.models.mediaProgress.upsertFromOld(oldMediaProgress)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeMediaProgress(mediaProgressId) {
|
removeMediaProgress(mediaProgressId) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.mediaProgress.removeById(mediaProgressId)
|
return this.models.mediaProgress.removeById(mediaProgressId)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateBulkBooks(oldBooks) {
|
updateBulkBooks(oldBooks) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return Promise.all(oldBooks.map(oldBook => this.models.book.saveFromOld(oldBook)))
|
return Promise.all(oldBooks.map(oldBook => this.models.book.saveFromOld(oldBook)))
|
||||||
}
|
}
|
||||||
|
|
||||||
async createLibrary(oldLibrary) {
|
async createLibrary(oldLibrary) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
await this.models.library.createFromOld(oldLibrary)
|
await this.models.library.createFromOld(oldLibrary)
|
||||||
this.libraries.push(oldLibrary)
|
this.libraries.push(oldLibrary)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLibrary(oldLibrary) {
|
updateLibrary(oldLibrary) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.library.updateFromOld(oldLibrary)
|
return this.models.library.updateFromOld(oldLibrary)
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeLibrary(libraryId) {
|
async removeLibrary(libraryId) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
await this.models.library.removeById(libraryId)
|
await this.models.library.removeById(libraryId)
|
||||||
this.libraries = this.libraries.filter(lib => lib.id !== libraryId)
|
this.libraries = this.libraries.filter(lib => lib.id !== libraryId)
|
||||||
}
|
}
|
||||||
|
|
||||||
async createCollection(oldCollection) {
|
async createCollection(oldCollection) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
const newCollection = await this.models.collection.createFromOld(oldCollection)
|
const newCollection = await this.models.collection.createFromOld(oldCollection)
|
||||||
// Create CollectionBooks
|
// Create CollectionBooks
|
||||||
if (newCollection) {
|
if (newCollection) {
|
||||||
@ -227,6 +252,7 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateCollection(oldCollection) {
|
updateCollection(oldCollection) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
const collectionBooks = []
|
const collectionBooks = []
|
||||||
let order = 1
|
let order = 1
|
||||||
oldCollection.books.forEach((libraryItemId) => {
|
oldCollection.books.forEach((libraryItemId) => {
|
||||||
@ -242,23 +268,28 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async removeCollection(collectionId) {
|
async removeCollection(collectionId) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
await this.models.collection.removeById(collectionId)
|
await this.models.collection.removeById(collectionId)
|
||||||
this.collections = this.collections.filter(c => c.id !== collectionId)
|
this.collections = this.collections.filter(c => c.id !== collectionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
createCollectionBook(collectionBook) {
|
createCollectionBook(collectionBook) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.collectionBook.create(collectionBook)
|
return this.models.collectionBook.create(collectionBook)
|
||||||
}
|
}
|
||||||
|
|
||||||
createBulkCollectionBooks(collectionBooks) {
|
createBulkCollectionBooks(collectionBooks) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.collectionBook.bulkCreate(collectionBooks)
|
return this.models.collectionBook.bulkCreate(collectionBooks)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeCollectionBook(collectionId, bookId) {
|
removeCollectionBook(collectionId, bookId) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.collectionBook.removeByIds(collectionId, bookId)
|
return this.models.collectionBook.removeByIds(collectionId, bookId)
|
||||||
}
|
}
|
||||||
|
|
||||||
async createPlaylist(oldPlaylist) {
|
async createPlaylist(oldPlaylist) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
const newPlaylist = await this.models.playlist.createFromOld(oldPlaylist)
|
const newPlaylist = await this.models.playlist.createFromOld(oldPlaylist)
|
||||||
if (newPlaylist) {
|
if (newPlaylist) {
|
||||||
const playlistMediaItems = []
|
const playlistMediaItems = []
|
||||||
@ -288,6 +319,7 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updatePlaylist(oldPlaylist) {
|
updatePlaylist(oldPlaylist) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
const playlistMediaItems = []
|
const playlistMediaItems = []
|
||||||
let order = 1
|
let order = 1
|
||||||
oldPlaylist.items.forEach((item) => {
|
oldPlaylist.items.forEach((item) => {
|
||||||
@ -304,36 +336,44 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async removePlaylist(playlistId) {
|
async removePlaylist(playlistId) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
await this.models.playlist.removeById(playlistId)
|
await this.models.playlist.removeById(playlistId)
|
||||||
this.playlists = this.playlists.filter(p => p.id !== playlistId)
|
this.playlists = this.playlists.filter(p => p.id !== playlistId)
|
||||||
}
|
}
|
||||||
|
|
||||||
createPlaylistMediaItem(playlistMediaItem) {
|
createPlaylistMediaItem(playlistMediaItem) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.playlistMediaItem.create(playlistMediaItem)
|
return this.models.playlistMediaItem.create(playlistMediaItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
createBulkPlaylistMediaItems(playlistMediaItems) {
|
createBulkPlaylistMediaItems(playlistMediaItems) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.playlistMediaItem.bulkCreate(playlistMediaItems)
|
return this.models.playlistMediaItem.bulkCreate(playlistMediaItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
removePlaylistMediaItem(playlistId, mediaItemId) {
|
removePlaylistMediaItem(playlistId, mediaItemId) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.playlistMediaItem.removeByIds(playlistId, mediaItemId)
|
return this.models.playlistMediaItem.removeByIds(playlistId, mediaItemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
getLibraryItem(libraryItemId) {
|
getLibraryItem(libraryItemId) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.libraryItems.find(li => li.id === libraryItemId)
|
return this.libraryItems.find(li => li.id === libraryItemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
async createLibraryItem(oldLibraryItem) {
|
async createLibraryItem(oldLibraryItem) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
await this.models.libraryItem.fullCreateFromOld(oldLibraryItem)
|
await this.models.libraryItem.fullCreateFromOld(oldLibraryItem)
|
||||||
this.libraryItems.push(oldLibraryItem)
|
this.libraryItems.push(oldLibraryItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLibraryItem(oldLibraryItem) {
|
updateLibraryItem(oldLibraryItem) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.libraryItem.fullUpdateFromOld(oldLibraryItem)
|
return this.models.libraryItem.fullUpdateFromOld(oldLibraryItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateBulkLibraryItems(oldLibraryItems) {
|
async updateBulkLibraryItems(oldLibraryItems) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
let updatesMade = 0
|
let updatesMade = 0
|
||||||
for (const oldLibraryItem of oldLibraryItems) {
|
for (const oldLibraryItem of oldLibraryItems) {
|
||||||
const hasUpdates = await this.models.libraryItem.fullUpdateFromOld(oldLibraryItem)
|
const hasUpdates = await this.models.libraryItem.fullUpdateFromOld(oldLibraryItem)
|
||||||
@ -343,6 +383,7 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createBulkLibraryItems(oldLibraryItems) {
|
async createBulkLibraryItems(oldLibraryItems) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
for (const oldLibraryItem of oldLibraryItems) {
|
for (const oldLibraryItem of oldLibraryItems) {
|
||||||
await this.models.libraryItem.fullCreateFromOld(oldLibraryItem)
|
await this.models.libraryItem.fullCreateFromOld(oldLibraryItem)
|
||||||
this.libraryItems.push(oldLibraryItem)
|
this.libraryItems.push(oldLibraryItem)
|
||||||
@ -350,68 +391,82 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async removeLibraryItem(libraryItemId) {
|
async removeLibraryItem(libraryItemId) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
await this.models.libraryItem.removeById(libraryItemId)
|
await this.models.libraryItem.removeById(libraryItemId)
|
||||||
this.libraryItems = this.libraryItems.filter(li => li.id !== libraryItemId)
|
this.libraryItems = this.libraryItems.filter(li => li.id !== libraryItemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
async createFeed(oldFeed) {
|
async createFeed(oldFeed) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
await this.models.feed.fullCreateFromOld(oldFeed)
|
await this.models.feed.fullCreateFromOld(oldFeed)
|
||||||
this.feeds.push(oldFeed)
|
this.feeds.push(oldFeed)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFeed(oldFeed) {
|
updateFeed(oldFeed) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.feed.fullUpdateFromOld(oldFeed)
|
return this.models.feed.fullUpdateFromOld(oldFeed)
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeFeed(feedId) {
|
async removeFeed(feedId) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
await this.models.feed.removeById(feedId)
|
await this.models.feed.removeById(feedId)
|
||||||
this.feeds = this.feeds.filter(f => f.id !== feedId)
|
this.feeds = this.feeds.filter(f => f.id !== feedId)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSeries(oldSeries) {
|
updateSeries(oldSeries) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.series.updateFromOld(oldSeries)
|
return this.models.series.updateFromOld(oldSeries)
|
||||||
}
|
}
|
||||||
|
|
||||||
async createSeries(oldSeries) {
|
async createSeries(oldSeries) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
await this.models.series.createFromOld(oldSeries)
|
await this.models.series.createFromOld(oldSeries)
|
||||||
this.series.push(oldSeries)
|
this.series.push(oldSeries)
|
||||||
}
|
}
|
||||||
|
|
||||||
async createBulkSeries(oldSeriesObjs) {
|
async createBulkSeries(oldSeriesObjs) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
await this.models.series.createBulkFromOld(oldSeriesObjs)
|
await this.models.series.createBulkFromOld(oldSeriesObjs)
|
||||||
this.series.push(...oldSeriesObjs)
|
this.series.push(...oldSeriesObjs)
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeSeries(seriesId) {
|
async removeSeries(seriesId) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
await this.models.series.removeById(seriesId)
|
await this.models.series.removeById(seriesId)
|
||||||
this.series = this.series.filter(se => se.id !== seriesId)
|
this.series = this.series.filter(se => se.id !== seriesId)
|
||||||
}
|
}
|
||||||
|
|
||||||
async createAuthor(oldAuthor) {
|
async createAuthor(oldAuthor) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
await this.models.createFromOld(oldAuthor)
|
await this.models.createFromOld(oldAuthor)
|
||||||
this.authors.push(oldAuthor)
|
this.authors.push(oldAuthor)
|
||||||
}
|
}
|
||||||
|
|
||||||
async createBulkAuthors(oldAuthors) {
|
async createBulkAuthors(oldAuthors) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
await this.models.author.createBulkFromOld(oldAuthors)
|
await this.models.author.createBulkFromOld(oldAuthors)
|
||||||
this.authors.push(...oldAuthors)
|
this.authors.push(...oldAuthors)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAuthor(oldAuthor) {
|
updateAuthor(oldAuthor) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.author.updateFromOld(oldAuthor)
|
return this.models.author.updateFromOld(oldAuthor)
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeAuthor(authorId) {
|
async removeAuthor(authorId) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
await this.models.author.removeById(authorId)
|
await this.models.author.removeById(authorId)
|
||||||
this.authors = this.authors.filter(au => au.id !== authorId)
|
this.authors = this.authors.filter(au => au.id !== authorId)
|
||||||
}
|
}
|
||||||
|
|
||||||
async createBulkBookAuthors(bookAuthors) {
|
async createBulkBookAuthors(bookAuthors) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
await this.models.bookAuthor.bulkCreate(bookAuthors)
|
await this.models.bookAuthor.bulkCreate(bookAuthors)
|
||||||
this.authors.push(...bookAuthors)
|
this.authors.push(...bookAuthors)
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeBulkBookAuthors(authorId = null, bookId = null) {
|
async removeBulkBookAuthors(authorId = null, bookId = null) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
if (!authorId && !bookId) return
|
if (!authorId && !bookId) return
|
||||||
await this.models.bookAuthor.removeByIds(authorId, bookId)
|
await this.models.bookAuthor.removeByIds(authorId, bookId)
|
||||||
this.authors = this.authors.filter(au => {
|
this.authors = this.authors.filter(au => {
|
||||||
@ -422,34 +477,42 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getPlaybackSessions(where = null) {
|
getPlaybackSessions(where = null) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.playbackSession.getOldPlaybackSessions(where)
|
return this.models.playbackSession.getOldPlaybackSessions(where)
|
||||||
}
|
}
|
||||||
|
|
||||||
getPlaybackSession(sessionId) {
|
getPlaybackSession(sessionId) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.playbackSession.getById(sessionId)
|
return this.models.playbackSession.getById(sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
createPlaybackSession(oldSession) {
|
createPlaybackSession(oldSession) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.playbackSession.createFromOld(oldSession)
|
return this.models.playbackSession.createFromOld(oldSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePlaybackSession(oldSession) {
|
updatePlaybackSession(oldSession) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.playbackSession.updateFromOld(oldSession)
|
return this.models.playbackSession.updateFromOld(oldSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
removePlaybackSession(sessionId) {
|
removePlaybackSession(sessionId) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.playbackSession.removeById(sessionId)
|
return this.models.playbackSession.removeById(sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
getDeviceByDeviceId(deviceId) {
|
getDeviceByDeviceId(deviceId) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.device.getOldDeviceByDeviceId(deviceId)
|
return this.models.device.getOldDeviceByDeviceId(deviceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDevice(oldDevice) {
|
updateDevice(oldDevice) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.device.updateFromOld(oldDevice)
|
return this.models.device.updateFromOld(oldDevice)
|
||||||
}
|
}
|
||||||
|
|
||||||
createDevice(oldDevice) {
|
createDevice(oldDevice) {
|
||||||
|
if (!this.sequelize) return false
|
||||||
return this.models.device.createFromOld(oldDevice)
|
return this.models.device.createFromOld(oldDevice)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ const CoverManager = require('./managers/CoverManager')
|
|||||||
const AbMergeManager = require('./managers/AbMergeManager')
|
const AbMergeManager = require('./managers/AbMergeManager')
|
||||||
const CacheManager = require('./managers/CacheManager')
|
const CacheManager = require('./managers/CacheManager')
|
||||||
const LogManager = require('./managers/LogManager')
|
const LogManager = require('./managers/LogManager')
|
||||||
// const BackupManager = require('./managers/BackupManager') // TODO
|
const BackupManager = require('./managers/BackupManager')
|
||||||
const PlaybackSessionManager = require('./managers/PlaybackSessionManager')
|
const PlaybackSessionManager = require('./managers/PlaybackSessionManager')
|
||||||
const PodcastManager = require('./managers/PodcastManager')
|
const PodcastManager = require('./managers/PodcastManager')
|
||||||
const AudioMetadataMangaer = require('./managers/AudioMetadataManager')
|
const AudioMetadataMangaer = require('./managers/AudioMetadataManager')
|
||||||
@ -59,7 +59,6 @@ class Server {
|
|||||||
filePerms.setDefaultDirSync(global.MetadataPath, false)
|
filePerms.setDefaultDirSync(global.MetadataPath, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// this.db = new Db()
|
|
||||||
this.watcher = new Watcher()
|
this.watcher = new Watcher()
|
||||||
this.auth = new Auth()
|
this.auth = new Auth()
|
||||||
|
|
||||||
@ -67,7 +66,7 @@ class Server {
|
|||||||
this.taskManager = new TaskManager()
|
this.taskManager = new TaskManager()
|
||||||
this.notificationManager = new NotificationManager()
|
this.notificationManager = new NotificationManager()
|
||||||
this.emailManager = new EmailManager()
|
this.emailManager = new EmailManager()
|
||||||
// this.backupManager = new BackupManager(this.db)
|
this.backupManager = new BackupManager()
|
||||||
this.logManager = new LogManager()
|
this.logManager = new LogManager()
|
||||||
this.cacheManager = new CacheManager()
|
this.cacheManager = new CacheManager()
|
||||||
this.abMergeManager = new AbMergeManager(this.taskManager)
|
this.abMergeManager = new AbMergeManager(this.taskManager)
|
||||||
@ -109,7 +108,7 @@ class Server {
|
|||||||
await this.purgeMetadata() // Remove metadata folders without library item
|
await this.purgeMetadata() // Remove metadata folders without library item
|
||||||
await this.cacheManager.ensureCachePaths()
|
await this.cacheManager.ensureCachePaths()
|
||||||
|
|
||||||
// await this.backupManager.init() // TODO: Implement backups
|
await this.backupManager.init()
|
||||||
await this.logManager.init()
|
await this.logManager.init()
|
||||||
await this.apiRouter.checkRemoveEmptySeries(Database.series) // Remove empty series
|
await this.apiRouter.checkRemoveEmptySeries(Database.series) // Remove empty series
|
||||||
await this.rssFeedManager.init()
|
await this.rssFeedManager.init()
|
||||||
|
@ -43,9 +43,8 @@ class BackupController {
|
|||||||
res.sendFile(req.backup.fullPath)
|
res.sendFile(req.backup.fullPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
async apply(req, res) {
|
apply(req, res) {
|
||||||
await this.backupManager.requestApplyBackup(req.backup)
|
this.backupManager.requestApplyBackup(req.backup, res)
|
||||||
res.sendStatus(200)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
middleware(req, res, next) {
|
middleware(req, res, next) {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
const sqlite3 = require('sqlite3')
|
||||||
const Path = require('path')
|
const Path = require('path')
|
||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
const SocketAuthority = require('../SocketAuthority')
|
const SocketAuthority = require('../SocketAuthority')
|
||||||
|
const Database = require('../Database')
|
||||||
|
|
||||||
const cron = require('../libs/nodeCron')
|
const cron = require('../libs/nodeCron')
|
||||||
const fs = require('../libs/fsExtra')
|
const fs = require('../libs/fsExtra')
|
||||||
@ -14,27 +16,32 @@ const filePerms = require('../utils/filePerms')
|
|||||||
const Backup = require('../objects/Backup')
|
const Backup = require('../objects/Backup')
|
||||||
|
|
||||||
class BackupManager {
|
class BackupManager {
|
||||||
constructor(db) {
|
constructor() {
|
||||||
this.BackupPath = Path.join(global.MetadataPath, 'backups')
|
this.BackupPath = Path.join(global.MetadataPath, 'backups')
|
||||||
this.ItemsMetadataPath = Path.join(global.MetadataPath, 'items')
|
this.ItemsMetadataPath = Path.join(global.MetadataPath, 'items')
|
||||||
this.AuthorsMetadataPath = Path.join(global.MetadataPath, 'authors')
|
this.AuthorsMetadataPath = Path.join(global.MetadataPath, 'authors')
|
||||||
|
|
||||||
this.db = db
|
|
||||||
|
|
||||||
this.scheduleTask = null
|
this.scheduleTask = null
|
||||||
|
|
||||||
this.backups = []
|
this.backups = []
|
||||||
}
|
}
|
||||||
|
|
||||||
get serverSettings() {
|
get backupSchedule() {
|
||||||
return this.db.serverSettings || {}
|
return global.ServerSettings.backupSchedule
|
||||||
|
}
|
||||||
|
|
||||||
|
get backupsToKeep() {
|
||||||
|
return global.ServerSettings.backupsToKeep || 2
|
||||||
|
}
|
||||||
|
|
||||||
|
get maxBackupSize() {
|
||||||
|
return global.ServerSettings.maxBackupSize || 1
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
const backupsDirExists = await fs.pathExists(this.BackupPath)
|
const backupsDirExists = await fs.pathExists(this.BackupPath)
|
||||||
if (!backupsDirExists) {
|
if (!backupsDirExists) {
|
||||||
await fs.ensureDir(this.BackupPath)
|
await fs.ensureDir(this.BackupPath)
|
||||||
await filePerms.setDefault(this.BackupPath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.loadBackups()
|
await this.loadBackups()
|
||||||
@ -42,42 +49,42 @@ class BackupManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scheduleCron() {
|
scheduleCron() {
|
||||||
if (!this.serverSettings.backupSchedule) {
|
if (!this.backupSchedule) {
|
||||||
Logger.info(`[BackupManager] Auto Backups are disabled`)
|
Logger.info(`[BackupManager] Auto Backups are disabled`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
var cronSchedule = this.serverSettings.backupSchedule
|
var cronSchedule = this.backupSchedule
|
||||||
this.scheduleTask = cron.schedule(cronSchedule, this.runBackup.bind(this))
|
this.scheduleTask = cron.schedule(cronSchedule, this.runBackup.bind(this))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.error(`[BackupManager] Failed to schedule backup cron ${this.serverSettings.backupSchedule}`, error)
|
Logger.error(`[BackupManager] Failed to schedule backup cron ${this.backupSchedule}`, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCronSchedule() {
|
updateCronSchedule() {
|
||||||
if (this.scheduleTask && !this.serverSettings.backupSchedule) {
|
if (this.scheduleTask && !this.backupSchedule) {
|
||||||
Logger.info(`[BackupManager] Disabling backup schedule`)
|
Logger.info(`[BackupManager] Disabling backup schedule`)
|
||||||
if (this.scheduleTask.stop) this.scheduleTask.stop()
|
if (this.scheduleTask.stop) this.scheduleTask.stop()
|
||||||
this.scheduleTask = null
|
this.scheduleTask = null
|
||||||
} else if (!this.scheduleTask && this.serverSettings.backupSchedule) {
|
} else if (!this.scheduleTask && this.backupSchedule) {
|
||||||
Logger.info(`[BackupManager] Starting backup schedule ${this.serverSettings.backupSchedule}`)
|
Logger.info(`[BackupManager] Starting backup schedule ${this.backupSchedule}`)
|
||||||
this.scheduleCron()
|
this.scheduleCron()
|
||||||
} else if (this.serverSettings.backupSchedule) {
|
} else if (this.backupSchedule) {
|
||||||
Logger.info(`[BackupManager] Restarting backup schedule ${this.serverSettings.backupSchedule}`)
|
Logger.info(`[BackupManager] Restarting backup schedule ${this.backupSchedule}`)
|
||||||
if (this.scheduleTask.stop) this.scheduleTask.stop()
|
if (this.scheduleTask.stop) this.scheduleTask.stop()
|
||||||
this.scheduleCron()
|
this.scheduleCron()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async uploadBackup(req, res) {
|
async uploadBackup(req, res) {
|
||||||
var backupFile = req.files.file
|
const backupFile = req.files.file
|
||||||
if (Path.extname(backupFile.name) !== '.audiobookshelf') {
|
if (Path.extname(backupFile.name) !== '.audiobookshelf') {
|
||||||
Logger.error(`[BackupManager] Invalid backup file uploaded "${backupFile.name}"`)
|
Logger.error(`[BackupManager] Invalid backup file uploaded "${backupFile.name}"`)
|
||||||
return res.status(500).send('Invalid backup file')
|
return res.status(500).send('Invalid backup file')
|
||||||
}
|
}
|
||||||
|
|
||||||
var tempPath = Path.join(this.BackupPath, backupFile.name)
|
const tempPath = Path.join(this.BackupPath, backupFile.name)
|
||||||
var success = await backupFile.mv(tempPath).then(() => true).catch((error) => {
|
const success = await backupFile.mv(tempPath).then(() => true).catch((error) => {
|
||||||
Logger.error('[BackupManager] Failed to move backup file', path, error)
|
Logger.error('[BackupManager] Failed to move backup file', path, error)
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
@ -86,10 +93,17 @@ class BackupManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const zip = new StreamZip.async({ file: tempPath })
|
const zip = new StreamZip.async({ file: tempPath })
|
||||||
const data = await zip.entryData('details')
|
|
||||||
var details = data.toString('utf8').split('\n')
|
|
||||||
|
|
||||||
var backup = new Backup({ details, fullPath: tempPath })
|
const entries = await zip.entries()
|
||||||
|
if (!Object.keys(entries).includes('absdatabase.sqlite')) {
|
||||||
|
Logger.error(`[BackupManager] Invalid backup with no absdatabase.sqlite file - might be a backup created on an old Audiobookshelf server.`)
|
||||||
|
return res.status(500).send('Invalid backup with no absdatabase.sqlite file - might be a backup created on an old Audiobookshelf server.')
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await zip.entryData('details')
|
||||||
|
const details = data.toString('utf8').split('\n')
|
||||||
|
|
||||||
|
const backup = new Backup({ details, fullPath: tempPath })
|
||||||
|
|
||||||
if (!backup.serverVersion) {
|
if (!backup.serverVersion) {
|
||||||
Logger.error(`[BackupManager] Invalid backup with no server version - might be a backup created before version 2.0.0`)
|
Logger.error(`[BackupManager] Invalid backup with no server version - might be a backup created before version 2.0.0`)
|
||||||
@ -98,7 +112,7 @@ class BackupManager {
|
|||||||
|
|
||||||
backup.fileSize = await getFileSize(backup.fullPath)
|
backup.fileSize = await getFileSize(backup.fullPath)
|
||||||
|
|
||||||
var existingBackupIndex = this.backups.findIndex(b => b.id === backup.id)
|
const existingBackupIndex = this.backups.findIndex(b => b.id === backup.id)
|
||||||
if (existingBackupIndex >= 0) {
|
if (existingBackupIndex >= 0) {
|
||||||
Logger.warn(`[BackupManager] Backup already exists with id ${backup.id} - overwriting`)
|
Logger.warn(`[BackupManager] Backup already exists with id ${backup.id} - overwriting`)
|
||||||
this.backups.splice(existingBackupIndex, 1, backup)
|
this.backups.splice(existingBackupIndex, 1, backup)
|
||||||
@ -122,14 +136,23 @@ class BackupManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async requestApplyBackup(backup) {
|
async requestApplyBackup(backup, res) {
|
||||||
const zip = new StreamZip.async({ file: backup.fullPath })
|
const zip = new StreamZip.async({ file: backup.fullPath })
|
||||||
await zip.extract('config/', global.ConfigPath)
|
|
||||||
if (backup.backupMetadataCovers) {
|
const entries = await zip.entries()
|
||||||
|
if (!Object.keys(entries).includes('absdatabase.sqlite')) {
|
||||||
|
Logger.error(`[BackupManager] Cannot apply old backup ${backup.fullPath}`)
|
||||||
|
return res.status(500).send('Invalid backup file. Does not include absdatabase.sqlite. This might be from an older Audiobookshelf server.')
|
||||||
|
}
|
||||||
|
|
||||||
|
await Database.disconnect()
|
||||||
|
|
||||||
|
await zip.extract('absdatabase.sqlite', global.ConfigPath)
|
||||||
await zip.extract('metadata-items/', this.ItemsMetadataPath)
|
await zip.extract('metadata-items/', this.ItemsMetadataPath)
|
||||||
await zip.extract('metadata-authors/', this.AuthorsMetadataPath)
|
await zip.extract('metadata-authors/', this.AuthorsMetadataPath)
|
||||||
}
|
|
||||||
await this.db.reinit()
|
await Database.reconnect()
|
||||||
|
|
||||||
SocketAuthority.emitter('backup_applied')
|
SocketAuthority.emitter('backup_applied')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,26 +205,37 @@ class BackupManager {
|
|||||||
async runBackup() {
|
async runBackup() {
|
||||||
// Check if Metadata Path is inside Config Path (otherwise there will be an infinite loop as the archiver tries to zip itself)
|
// Check if Metadata Path is inside Config Path (otherwise there will be an infinite loop as the archiver tries to zip itself)
|
||||||
Logger.info(`[BackupManager] Running Backup`)
|
Logger.info(`[BackupManager] Running Backup`)
|
||||||
var newBackup = new Backup()
|
const newBackup = new Backup()
|
||||||
|
newBackup.setData(this.BackupPath)
|
||||||
|
|
||||||
const newBackData = {
|
await fs.ensureDir(this.AuthorsMetadataPath)
|
||||||
backupMetadataCovers: this.serverSettings.backupMetadataCovers,
|
|
||||||
backupDirPath: this.BackupPath
|
// Create backup sqlite file
|
||||||
|
const sqliteBackupPath = await this.backupSqliteDb(newBackup).catch((error) => {
|
||||||
|
Logger.error(`[BackupManager] Failed to backup sqlite db`, error)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!sqliteBackupPath) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
newBackup.setData(newBackData)
|
|
||||||
|
|
||||||
var metadataAuthorsPath = this.AuthorsMetadataPath
|
// Zip sqlite file, /metadata/items, and /metadata/authors folders
|
||||||
if (!await fs.pathExists(metadataAuthorsPath)) metadataAuthorsPath = null
|
const zipResult = await this.zipBackup(sqliteBackupPath, newBackup).catch((error) => {
|
||||||
|
|
||||||
var zipResult = await this.zipBackup(metadataAuthorsPath, newBackup).then(() => true).catch((error) => {
|
|
||||||
Logger.error(`[BackupManager] Backup Failed ${error}`)
|
Logger.error(`[BackupManager] Backup Failed ${error}`)
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
if (zipResult) {
|
|
||||||
|
// Remove sqlite backup
|
||||||
|
await fs.remove(sqliteBackupPath)
|
||||||
|
|
||||||
|
if (!zipResult) return false
|
||||||
|
|
||||||
Logger.info(`[BackupManager] Backup successful ${newBackup.id}`)
|
Logger.info(`[BackupManager] Backup successful ${newBackup.id}`)
|
||||||
await filePerms.setDefault(newBackup.fullPath)
|
|
||||||
newBackup.fileSize = await getFileSize(newBackup.fullPath)
|
newBackup.fileSize = await getFileSize(newBackup.fullPath)
|
||||||
var existingIndex = this.backups.findIndex(b => b.id === newBackup.id)
|
|
||||||
|
const existingIndex = this.backups.findIndex(b => b.id === newBackup.id)
|
||||||
if (existingIndex >= 0) {
|
if (existingIndex >= 0) {
|
||||||
this.backups.splice(existingIndex, 1, newBackup)
|
this.backups.splice(existingIndex, 1, newBackup)
|
||||||
} else {
|
} else {
|
||||||
@ -209,17 +243,14 @@ class BackupManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check remove oldest backup
|
// Check remove oldest backup
|
||||||
if (this.backups.length > this.serverSettings.backupsToKeep) {
|
if (this.backups.length > this.backupsToKeep) {
|
||||||
this.backups.sort((a, b) => a.createdAt - b.createdAt)
|
this.backups.sort((a, b) => a.createdAt - b.createdAt)
|
||||||
|
|
||||||
var oldBackup = this.backups.shift()
|
const oldBackup = this.backups.shift()
|
||||||
Logger.debug(`[BackupManager] Removing old backup ${oldBackup.id}`)
|
Logger.debug(`[BackupManager] Removing old backup ${oldBackup.id}`)
|
||||||
this.removeBackup(oldBackup)
|
this.removeBackup(oldBackup)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeBackup(backup) {
|
async removeBackup(backup) {
|
||||||
@ -233,7 +264,35 @@ class BackupManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
zipBackup(metadataAuthorsPath, backup) {
|
/**
|
||||||
|
* @see https://github.com/TryGhost/node-sqlite3/pull/1116
|
||||||
|
* @param {Backup} backup
|
||||||
|
* @promise
|
||||||
|
*/
|
||||||
|
backupSqliteDb(backup) {
|
||||||
|
const db = new sqlite3.Database(Database.dbPath)
|
||||||
|
const dbFilePath = Path.join(global.ConfigPath, `absdatabase.${backup.id}.sqlite`)
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
const backup = db.backup(dbFilePath)
|
||||||
|
backup.step(-1)
|
||||||
|
backup.finish()
|
||||||
|
|
||||||
|
// Max time ~2 mins
|
||||||
|
for (let i = 0; i < 240; i++) {
|
||||||
|
if (backup.completed) {
|
||||||
|
return resolve(dbFilePath)
|
||||||
|
} else if (backup.failed) {
|
||||||
|
return reject(backup.message || 'Unknown failure reason')
|
||||||
|
}
|
||||||
|
await new Promise((r) => setTimeout(r, 500))
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.error(`[BackupManager] Backup sqlite timed out`)
|
||||||
|
reject('Backup timed out')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
zipBackup(sqliteBackupPath, backup) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// create a file to stream archive data to
|
// create a file to stream archive data to
|
||||||
const output = fs.createWriteStream(backup.fullPath)
|
const output = fs.createWriteStream(backup.fullPath)
|
||||||
@ -245,7 +304,7 @@ class BackupManager {
|
|||||||
// 'close' event is fired only when a file descriptor is involved
|
// 'close' event is fired only when a file descriptor is involved
|
||||||
output.on('close', () => {
|
output.on('close', () => {
|
||||||
Logger.info('[BackupManager]', archive.pointer() + ' total bytes')
|
Logger.info('[BackupManager]', archive.pointer() + ' total bytes')
|
||||||
resolve()
|
resolve(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
// This event is fired when the data source is drained no matter what was the data source.
|
// This event is fired when the data source is drained no matter what was the data source.
|
||||||
@ -281,7 +340,7 @@ class BackupManager {
|
|||||||
reject(err)
|
reject(err)
|
||||||
})
|
})
|
||||||
archive.on('progress', ({ fs: fsobj }) => {
|
archive.on('progress', ({ fs: fsobj }) => {
|
||||||
const maxBackupSizeInBytes = this.serverSettings.maxBackupSize * 1000 * 1000 * 1000
|
const maxBackupSizeInBytes = this.maxBackupSize * 1000 * 1000 * 1000
|
||||||
if (fsobj.processedBytes > maxBackupSizeInBytes) {
|
if (fsobj.processedBytes > maxBackupSizeInBytes) {
|
||||||
Logger.error(`[BackupManager] Archiver is too large - aborting to prevent endless loop, Bytes Processed: ${fsobj.processedBytes}`)
|
Logger.error(`[BackupManager] Archiver is too large - aborting to prevent endless loop, Bytes Processed: ${fsobj.processedBytes}`)
|
||||||
archive.abort()
|
archive.abort()
|
||||||
@ -295,26 +354,9 @@ class BackupManager {
|
|||||||
// pipe archive data to the file
|
// pipe archive data to the file
|
||||||
archive.pipe(output)
|
archive.pipe(output)
|
||||||
|
|
||||||
archive.directory(Path.join(this.db.LibraryItemsPath, 'data'), 'config/libraryItems/data')
|
archive.file(sqliteBackupPath, { name: 'absdatabase.sqlite' })
|
||||||
archive.directory(Path.join(this.db.UsersPath, 'data'), 'config/users/data')
|
|
||||||
archive.directory(Path.join(this.db.SessionsPath, 'data'), 'config/sessions/data')
|
|
||||||
archive.directory(Path.join(this.db.LibrariesPath, 'data'), 'config/libraries/data')
|
|
||||||
archive.directory(Path.join(this.db.SettingsPath, 'data'), 'config/settings/data')
|
|
||||||
archive.directory(Path.join(this.db.CollectionsPath, 'data'), 'config/collections/data')
|
|
||||||
archive.directory(Path.join(this.db.AuthorsPath, 'data'), 'config/authors/data')
|
|
||||||
archive.directory(Path.join(this.db.SeriesPath, 'data'), 'config/series/data')
|
|
||||||
archive.directory(Path.join(this.db.PlaylistsPath, 'data'), 'config/playlists/data')
|
|
||||||
archive.directory(Path.join(this.db.FeedsPath, 'data'), 'config/feeds/data')
|
|
||||||
|
|
||||||
if (this.serverSettings.backupMetadataCovers) {
|
|
||||||
Logger.debug(`[BackupManager] Backing up Metadata Items "${this.ItemsMetadataPath}"`)
|
|
||||||
archive.directory(this.ItemsMetadataPath, 'metadata-items')
|
archive.directory(this.ItemsMetadataPath, 'metadata-items')
|
||||||
|
archive.directory(this.AuthorsMetadataPath, 'metadata-authors')
|
||||||
if (metadataAuthorsPath) {
|
|
||||||
Logger.debug(`[BackupManager] Backing up Metadata Authors "${metadataAuthorsPath}"`)
|
|
||||||
archive.directory(metadataAuthorsPath, 'metadata-authors')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
archive.append(backup.detailsString, { name: 'details' })
|
archive.append(backup.detailsString, { name: 'details' })
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ class Backup {
|
|||||||
constructor(data = null) {
|
constructor(data = null) {
|
||||||
this.id = null
|
this.id = null
|
||||||
this.datePretty = null
|
this.datePretty = null
|
||||||
this.backupMetadataCovers = null
|
|
||||||
|
|
||||||
this.backupDirPath = null
|
this.backupDirPath = null
|
||||||
this.filename = null
|
this.filename = null
|
||||||
@ -23,9 +22,9 @@ class Backup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get detailsString() {
|
get detailsString() {
|
||||||
var details = []
|
const details = []
|
||||||
details.push(this.id)
|
details.push(this.id)
|
||||||
details.push(this.backupMetadataCovers ? '1' : '0')
|
details.push('1') // Unused old boolean spot
|
||||||
details.push(this.createdAt)
|
details.push(this.createdAt)
|
||||||
details.push(this.serverVersion)
|
details.push(this.serverVersion)
|
||||||
return details.join('\n')
|
return details.join('\n')
|
||||||
@ -33,7 +32,6 @@ class Backup {
|
|||||||
|
|
||||||
construct(data) {
|
construct(data) {
|
||||||
this.id = data.details[0]
|
this.id = data.details[0]
|
||||||
this.backupMetadataCovers = data.details[1] === '1'
|
|
||||||
this.createdAt = Number(data.details[2])
|
this.createdAt = Number(data.details[2])
|
||||||
this.serverVersion = data.details[3] || null
|
this.serverVersion = data.details[3] || null
|
||||||
|
|
||||||
@ -48,7 +46,6 @@ class Backup {
|
|||||||
toJSON() {
|
toJSON() {
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
backupMetadataCovers: this.backupMetadataCovers,
|
|
||||||
backupDirPath: this.backupDirPath,
|
backupDirPath: this.backupDirPath,
|
||||||
datePretty: this.datePretty,
|
datePretty: this.datePretty,
|
||||||
fullPath: this.fullPath,
|
fullPath: this.fullPath,
|
||||||
@ -60,13 +57,11 @@ class Backup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setData(data) {
|
setData(backupDirPath) {
|
||||||
this.id = date.format(new Date(), 'YYYY-MM-DD[T]HHmm')
|
this.id = date.format(new Date(), 'YYYY-MM-DD[T]HHmm')
|
||||||
this.datePretty = date.format(new Date(), 'ddd, MMM D YYYY HH:mm')
|
this.datePretty = date.format(new Date(), 'ddd, MMM D YYYY HH:mm')
|
||||||
|
|
||||||
this.backupMetadataCovers = data.backupMetadataCovers
|
this.backupDirPath = backupDirPath
|
||||||
|
|
||||||
this.backupDirPath = data.backupDirPath
|
|
||||||
|
|
||||||
this.filename = this.id + '.audiobookshelf'
|
this.filename = this.id + '.audiobookshelf'
|
||||||
this.path = Path.join('backups', this.filename)
|
this.path = Path.join('backups', this.filename)
|
||||||
|
@ -29,7 +29,6 @@ class ServerSettings {
|
|||||||
this.backupSchedule = false // If false then auto-backups are disabled
|
this.backupSchedule = false // If false then auto-backups are disabled
|
||||||
this.backupsToKeep = 2
|
this.backupsToKeep = 2
|
||||||
this.maxBackupSize = 1
|
this.maxBackupSize = 1
|
||||||
this.backupMetadataCovers = true
|
|
||||||
|
|
||||||
// Logger
|
// Logger
|
||||||
this.loggerDailyLogsToKeep = 7
|
this.loggerDailyLogsToKeep = 7
|
||||||
@ -82,7 +81,6 @@ class ServerSettings {
|
|||||||
this.backupSchedule = settings.backupSchedule || false
|
this.backupSchedule = settings.backupSchedule || false
|
||||||
this.backupsToKeep = settings.backupsToKeep || 2
|
this.backupsToKeep = settings.backupsToKeep || 2
|
||||||
this.maxBackupSize = settings.maxBackupSize || 1
|
this.maxBackupSize = settings.maxBackupSize || 1
|
||||||
this.backupMetadataCovers = settings.backupMetadataCovers !== false
|
|
||||||
|
|
||||||
this.loggerDailyLogsToKeep = settings.loggerDailyLogsToKeep || 7
|
this.loggerDailyLogsToKeep = settings.loggerDailyLogsToKeep || 7
|
||||||
this.loggerScannerLogsToKeep = settings.loggerScannerLogsToKeep || 2
|
this.loggerScannerLogsToKeep = settings.loggerScannerLogsToKeep || 2
|
||||||
@ -145,7 +143,6 @@ class ServerSettings {
|
|||||||
backupSchedule: this.backupSchedule,
|
backupSchedule: this.backupSchedule,
|
||||||
backupsToKeep: this.backupsToKeep,
|
backupsToKeep: this.backupsToKeep,
|
||||||
maxBackupSize: this.maxBackupSize,
|
maxBackupSize: this.maxBackupSize,
|
||||||
backupMetadataCovers: this.backupMetadataCovers,
|
|
||||||
loggerDailyLogsToKeep: this.loggerDailyLogsToKeep,
|
loggerDailyLogsToKeep: this.loggerDailyLogsToKeep,
|
||||||
loggerScannerLogsToKeep: this.loggerScannerLogsToKeep,
|
loggerScannerLogsToKeep: this.loggerScannerLogsToKeep,
|
||||||
homeBookshelfView: this.homeBookshelfView,
|
homeBookshelfView: this.homeBookshelfView,
|
||||||
|
@ -798,8 +798,7 @@ module.exports.migrate = async (DatabaseModels) => {
|
|||||||
/**
|
/**
|
||||||
* @returns {boolean} true if old database exists
|
* @returns {boolean} true if old database exists
|
||||||
*/
|
*/
|
||||||
module.exports.checkShouldMigrate = async (force = false) => {
|
module.exports.checkShouldMigrate = async () => {
|
||||||
if (await oldDbFiles.checkHasOldDb()) return true
|
if (await oldDbFiles.checkHasOldDb()) return true
|
||||||
if (!force) return false
|
|
||||||
return oldDbFiles.checkHasOldDbZip()
|
return oldDbFiles.checkHasOldDbZip()
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user