From 254ba1f0895a1ba739f4ab294311e3a55ea6254c Mon Sep 17 00:00:00 2001 From: advplyr Date: Sat, 8 Jul 2023 14:40:49 -0500 Subject: [PATCH] Migrate backups manager --- client/components/tables/BackupsTable.vue | 5 +- server/Database.js | 75 +++++++- server/Server.js | 7 +- server/controllers/BackupController.js | 5 +- server/managers/BackupManager.js | 198 +++++++++++++--------- server/objects/Backup.js | 13 +- server/objects/settings/ServerSettings.js | 3 - server/utils/migrations/dbMigration.js | 3 +- 8 files changed, 202 insertions(+), 107 deletions(-) diff --git a/client/components/tables/BackupsTable.vue b/client/components/tables/BackupsTable.vue index 78ecd8e2..f4ab1e7d 100644 --- a/client/components/tables/BackupsTable.vue +++ b/client/components/tables/BackupsTable.vue @@ -95,8 +95,9 @@ export default { }) .catch((error) => { this.isBackingUp = false - console.error('Failed', error) - this.$toast.error(this.$strings.ToastBackupRestoreFailed) + console.error('Failed to apply backup', error) + const errorMsg = error.response.data || this.$strings.ToastBackupRestoreFailed + this.$toast.error(errorMsg) }) }, deleteBackupClick(backup) { diff --git a/server/Database.js b/server/Database.js index d7f91559..e5e06fee 100644 --- a/server/Database.js +++ b/server/Database.js @@ -11,7 +11,7 @@ class Database { constructor() { this.sequelize = null this.dbPath = null - this.isNew = false // New database.sqlite created + this.isNew = false // New absdatabase.sqlite created // Temporarily using format of old DB // below data should be loaded from the DB as needed @@ -40,14 +40,14 @@ class Database { async checkHasDb() { 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 true } 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 this.isNew = !(await this.checkHasDb()) || force @@ -59,7 +59,7 @@ class Database { await this.buildModels(force) Logger.info(`[Database] Db initialized`, Object.keys(this.sequelize.models)) - await this.loadData(force) + await this.loadData() } 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) { require('./models/User')(this.sequelize) require('./models/Library')(this.sequelize) @@ -109,8 +120,8 @@ class Database { return this.sequelize.sync({ force, alter: false }) } - async loadData(force = false) { - if (this.isNew && await dbMigration.checkShouldMigrate(force)) { + async loadData() { + if (this.isNew && await dbMigration.checkShouldMigrate()) { Logger.info(`[Database] New database was created and old database was detected - migrating old to new`) await dbMigration.migrate(this.models) } @@ -143,6 +154,7 @@ class Database { } async createRootUser(username, pash, token) { + if (!this.sequelize) return false const newUser = await this.models.user.createRootUser(username, pash, token) if (newUser) { this.users.push(newUser) @@ -152,60 +164,73 @@ class Database { } updateServerSettings() { + if (!this.sequelize) return false global.ServerSettings = this.serverSettings.toJSON() return this.updateSetting(this.serverSettings) } updateSetting(settings) { + if (!this.sequelize) return false return this.models.setting.updateSettingObj(settings.toJSON()) } async createUser(oldUser) { + if (!this.sequelize) return false await this.models.user.createFromOld(oldUser) this.users.push(oldUser) return true } updateUser(oldUser) { + if (!this.sequelize) return false return this.models.user.updateFromOld(oldUser) } updateBulkUsers(oldUsers) { + if (!this.sequelize) return false return Promise.all(oldUsers.map(u => this.updateUser(u))) } async removeUser(userId) { + if (!this.sequelize) return false await this.models.user.removeById(userId) this.users = this.users.filter(u => u.id !== userId) } upsertMediaProgress(oldMediaProgress) { + if (!this.sequelize) return false return this.models.mediaProgress.upsertFromOld(oldMediaProgress) } removeMediaProgress(mediaProgressId) { + if (!this.sequelize) return false return this.models.mediaProgress.removeById(mediaProgressId) } updateBulkBooks(oldBooks) { + if (!this.sequelize) return false return Promise.all(oldBooks.map(oldBook => this.models.book.saveFromOld(oldBook))) } async createLibrary(oldLibrary) { + if (!this.sequelize) return false await this.models.library.createFromOld(oldLibrary) this.libraries.push(oldLibrary) } updateLibrary(oldLibrary) { + if (!this.sequelize) return false return this.models.library.updateFromOld(oldLibrary) } async removeLibrary(libraryId) { + if (!this.sequelize) return false await this.models.library.removeById(libraryId) this.libraries = this.libraries.filter(lib => lib.id !== libraryId) } async createCollection(oldCollection) { + if (!this.sequelize) return false const newCollection = await this.models.collection.createFromOld(oldCollection) // Create CollectionBooks if (newCollection) { @@ -227,6 +252,7 @@ class Database { } updateCollection(oldCollection) { + if (!this.sequelize) return false const collectionBooks = [] let order = 1 oldCollection.books.forEach((libraryItemId) => { @@ -242,23 +268,28 @@ class Database { } async removeCollection(collectionId) { + if (!this.sequelize) return false await this.models.collection.removeById(collectionId) this.collections = this.collections.filter(c => c.id !== collectionId) } createCollectionBook(collectionBook) { + if (!this.sequelize) return false return this.models.collectionBook.create(collectionBook) } createBulkCollectionBooks(collectionBooks) { + if (!this.sequelize) return false return this.models.collectionBook.bulkCreate(collectionBooks) } removeCollectionBook(collectionId, bookId) { + if (!this.sequelize) return false return this.models.collectionBook.removeByIds(collectionId, bookId) } async createPlaylist(oldPlaylist) { + if (!this.sequelize) return false const newPlaylist = await this.models.playlist.createFromOld(oldPlaylist) if (newPlaylist) { const playlistMediaItems = [] @@ -288,6 +319,7 @@ class Database { } updatePlaylist(oldPlaylist) { + if (!this.sequelize) return false const playlistMediaItems = [] let order = 1 oldPlaylist.items.forEach((item) => { @@ -304,36 +336,44 @@ class Database { } async removePlaylist(playlistId) { + if (!this.sequelize) return false await this.models.playlist.removeById(playlistId) this.playlists = this.playlists.filter(p => p.id !== playlistId) } createPlaylistMediaItem(playlistMediaItem) { + if (!this.sequelize) return false return this.models.playlistMediaItem.create(playlistMediaItem) } createBulkPlaylistMediaItems(playlistMediaItems) { + if (!this.sequelize) return false return this.models.playlistMediaItem.bulkCreate(playlistMediaItems) } removePlaylistMediaItem(playlistId, mediaItemId) { + if (!this.sequelize) return false return this.models.playlistMediaItem.removeByIds(playlistId, mediaItemId) } getLibraryItem(libraryItemId) { + if (!this.sequelize) return false return this.libraryItems.find(li => li.id === libraryItemId) } async createLibraryItem(oldLibraryItem) { + if (!this.sequelize) return false await this.models.libraryItem.fullCreateFromOld(oldLibraryItem) this.libraryItems.push(oldLibraryItem) } updateLibraryItem(oldLibraryItem) { + if (!this.sequelize) return false return this.models.libraryItem.fullUpdateFromOld(oldLibraryItem) } async updateBulkLibraryItems(oldLibraryItems) { + if (!this.sequelize) return false let updatesMade = 0 for (const oldLibraryItem of oldLibraryItems) { const hasUpdates = await this.models.libraryItem.fullUpdateFromOld(oldLibraryItem) @@ -343,6 +383,7 @@ class Database { } async createBulkLibraryItems(oldLibraryItems) { + if (!this.sequelize) return false for (const oldLibraryItem of oldLibraryItems) { await this.models.libraryItem.fullCreateFromOld(oldLibraryItem) this.libraryItems.push(oldLibraryItem) @@ -350,68 +391,82 @@ class Database { } async removeLibraryItem(libraryItemId) { + if (!this.sequelize) return false await this.models.libraryItem.removeById(libraryItemId) this.libraryItems = this.libraryItems.filter(li => li.id !== libraryItemId) } async createFeed(oldFeed) { + if (!this.sequelize) return false await this.models.feed.fullCreateFromOld(oldFeed) this.feeds.push(oldFeed) } updateFeed(oldFeed) { + if (!this.sequelize) return false return this.models.feed.fullUpdateFromOld(oldFeed) } async removeFeed(feedId) { + if (!this.sequelize) return false await this.models.feed.removeById(feedId) this.feeds = this.feeds.filter(f => f.id !== feedId) } updateSeries(oldSeries) { + if (!this.sequelize) return false return this.models.series.updateFromOld(oldSeries) } async createSeries(oldSeries) { + if (!this.sequelize) return false await this.models.series.createFromOld(oldSeries) this.series.push(oldSeries) } async createBulkSeries(oldSeriesObjs) { + if (!this.sequelize) return false await this.models.series.createBulkFromOld(oldSeriesObjs) this.series.push(...oldSeriesObjs) } async removeSeries(seriesId) { + if (!this.sequelize) return false await this.models.series.removeById(seriesId) this.series = this.series.filter(se => se.id !== seriesId) } async createAuthor(oldAuthor) { + if (!this.sequelize) return false await this.models.createFromOld(oldAuthor) this.authors.push(oldAuthor) } async createBulkAuthors(oldAuthors) { + if (!this.sequelize) return false await this.models.author.createBulkFromOld(oldAuthors) this.authors.push(...oldAuthors) } updateAuthor(oldAuthor) { + if (!this.sequelize) return false return this.models.author.updateFromOld(oldAuthor) } async removeAuthor(authorId) { + if (!this.sequelize) return false await this.models.author.removeById(authorId) this.authors = this.authors.filter(au => au.id !== authorId) } async createBulkBookAuthors(bookAuthors) { + if (!this.sequelize) return false await this.models.bookAuthor.bulkCreate(bookAuthors) this.authors.push(...bookAuthors) } async removeBulkBookAuthors(authorId = null, bookId = null) { + if (!this.sequelize) return false if (!authorId && !bookId) return await this.models.bookAuthor.removeByIds(authorId, bookId) this.authors = this.authors.filter(au => { @@ -422,34 +477,42 @@ class Database { } getPlaybackSessions(where = null) { + if (!this.sequelize) return false return this.models.playbackSession.getOldPlaybackSessions(where) } getPlaybackSession(sessionId) { + if (!this.sequelize) return false return this.models.playbackSession.getById(sessionId) } createPlaybackSession(oldSession) { + if (!this.sequelize) return false return this.models.playbackSession.createFromOld(oldSession) } updatePlaybackSession(oldSession) { + if (!this.sequelize) return false return this.models.playbackSession.updateFromOld(oldSession) } removePlaybackSession(sessionId) { + if (!this.sequelize) return false return this.models.playbackSession.removeById(sessionId) } getDeviceByDeviceId(deviceId) { + if (!this.sequelize) return false return this.models.device.getOldDeviceByDeviceId(deviceId) } updateDevice(oldDevice) { + if (!this.sequelize) return false return this.models.device.updateFromOld(oldDevice) } createDevice(oldDevice) { + if (!this.sequelize) return false return this.models.device.createFromOld(oldDevice) } } diff --git a/server/Server.js b/server/Server.js index b591e626..3afd03d9 100644 --- a/server/Server.js +++ b/server/Server.js @@ -29,7 +29,7 @@ const CoverManager = require('./managers/CoverManager') const AbMergeManager = require('./managers/AbMergeManager') const CacheManager = require('./managers/CacheManager') const LogManager = require('./managers/LogManager') -// const BackupManager = require('./managers/BackupManager') // TODO +const BackupManager = require('./managers/BackupManager') const PlaybackSessionManager = require('./managers/PlaybackSessionManager') const PodcastManager = require('./managers/PodcastManager') const AudioMetadataMangaer = require('./managers/AudioMetadataManager') @@ -59,7 +59,6 @@ class Server { filePerms.setDefaultDirSync(global.MetadataPath, false) } - // this.db = new Db() this.watcher = new Watcher() this.auth = new Auth() @@ -67,7 +66,7 @@ class Server { this.taskManager = new TaskManager() this.notificationManager = new NotificationManager() this.emailManager = new EmailManager() - // this.backupManager = new BackupManager(this.db) + this.backupManager = new BackupManager() this.logManager = new LogManager() this.cacheManager = new CacheManager() this.abMergeManager = new AbMergeManager(this.taskManager) @@ -109,7 +108,7 @@ class Server { await this.purgeMetadata() // Remove metadata folders without library item await this.cacheManager.ensureCachePaths() - // await this.backupManager.init() // TODO: Implement backups + await this.backupManager.init() await this.logManager.init() await this.apiRouter.checkRemoveEmptySeries(Database.series) // Remove empty series await this.rssFeedManager.init() diff --git a/server/controllers/BackupController.js b/server/controllers/BackupController.js index 811af850..207b3c74 100644 --- a/server/controllers/BackupController.js +++ b/server/controllers/BackupController.js @@ -43,9 +43,8 @@ class BackupController { res.sendFile(req.backup.fullPath) } - async apply(req, res) { - await this.backupManager.requestApplyBackup(req.backup) - res.sendStatus(200) + apply(req, res) { + this.backupManager.requestApplyBackup(req.backup, res) } middleware(req, res, next) { diff --git a/server/managers/BackupManager.js b/server/managers/BackupManager.js index 906a3063..7a61406f 100644 --- a/server/managers/BackupManager.js +++ b/server/managers/BackupManager.js @@ -1,6 +1,8 @@ +const sqlite3 = require('sqlite3') const Path = require('path') const Logger = require('../Logger') const SocketAuthority = require('../SocketAuthority') +const Database = require('../Database') const cron = require('../libs/nodeCron') const fs = require('../libs/fsExtra') @@ -14,27 +16,32 @@ const filePerms = require('../utils/filePerms') const Backup = require('../objects/Backup') class BackupManager { - constructor(db) { + constructor() { this.BackupPath = Path.join(global.MetadataPath, 'backups') this.ItemsMetadataPath = Path.join(global.MetadataPath, 'items') this.AuthorsMetadataPath = Path.join(global.MetadataPath, 'authors') - this.db = db - this.scheduleTask = null this.backups = [] } - get serverSettings() { - return this.db.serverSettings || {} + get backupSchedule() { + return global.ServerSettings.backupSchedule + } + + get backupsToKeep() { + return global.ServerSettings.backupsToKeep || 2 + } + + get maxBackupSize() { + return global.ServerSettings.maxBackupSize || 1 } async init() { const backupsDirExists = await fs.pathExists(this.BackupPath) if (!backupsDirExists) { await fs.ensureDir(this.BackupPath) - await filePerms.setDefault(this.BackupPath) } await this.loadBackups() @@ -42,42 +49,42 @@ class BackupManager { } scheduleCron() { - if (!this.serverSettings.backupSchedule) { + if (!this.backupSchedule) { Logger.info(`[BackupManager] Auto Backups are disabled`) return } try { - var cronSchedule = this.serverSettings.backupSchedule + var cronSchedule = this.backupSchedule this.scheduleTask = cron.schedule(cronSchedule, this.runBackup.bind(this)) } 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() { - if (this.scheduleTask && !this.serverSettings.backupSchedule) { + if (this.scheduleTask && !this.backupSchedule) { Logger.info(`[BackupManager] Disabling backup schedule`) if (this.scheduleTask.stop) this.scheduleTask.stop() this.scheduleTask = null - } else if (!this.scheduleTask && this.serverSettings.backupSchedule) { - Logger.info(`[BackupManager] Starting backup schedule ${this.serverSettings.backupSchedule}`) + } else if (!this.scheduleTask && this.backupSchedule) { + Logger.info(`[BackupManager] Starting backup schedule ${this.backupSchedule}`) this.scheduleCron() - } else if (this.serverSettings.backupSchedule) { - Logger.info(`[BackupManager] Restarting backup schedule ${this.serverSettings.backupSchedule}`) + } else if (this.backupSchedule) { + Logger.info(`[BackupManager] Restarting backup schedule ${this.backupSchedule}`) if (this.scheduleTask.stop) this.scheduleTask.stop() this.scheduleCron() } } async uploadBackup(req, res) { - var backupFile = req.files.file + const backupFile = req.files.file if (Path.extname(backupFile.name) !== '.audiobookshelf') { Logger.error(`[BackupManager] Invalid backup file uploaded "${backupFile.name}"`) return res.status(500).send('Invalid backup file') } - var tempPath = Path.join(this.BackupPath, backupFile.name) - var success = await backupFile.mv(tempPath).then(() => true).catch((error) => { + const tempPath = Path.join(this.BackupPath, backupFile.name) + const success = await backupFile.mv(tempPath).then(() => true).catch((error) => { Logger.error('[BackupManager] Failed to move backup file', path, error) return false }) @@ -86,10 +93,17 @@ class BackupManager { } 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) { 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) - var existingBackupIndex = this.backups.findIndex(b => b.id === backup.id) + const existingBackupIndex = this.backups.findIndex(b => b.id === backup.id) if (existingBackupIndex >= 0) { Logger.warn(`[BackupManager] Backup already exists with id ${backup.id} - overwriting`) 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 }) - await zip.extract('config/', global.ConfigPath) - if (backup.backupMetadataCovers) { - await zip.extract('metadata-items/', this.ItemsMetadataPath) - await zip.extract('metadata-authors/', this.AuthorsMetadataPath) + + 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 this.db.reinit() + + await Database.disconnect() + + await zip.extract('absdatabase.sqlite', global.ConfigPath) + await zip.extract('metadata-items/', this.ItemsMetadataPath) + await zip.extract('metadata-authors/', this.AuthorsMetadataPath) + + await Database.reconnect() + SocketAuthority.emitter('backup_applied') } @@ -182,44 +205,52 @@ class BackupManager { async runBackup() { // 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`) - var newBackup = new Backup() + const newBackup = new Backup() + newBackup.setData(this.BackupPath) - const newBackData = { - backupMetadataCovers: this.serverSettings.backupMetadataCovers, - backupDirPath: this.BackupPath + await fs.ensureDir(this.AuthorsMetadataPath) + + // 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 - if (!await fs.pathExists(metadataAuthorsPath)) metadataAuthorsPath = null - - var zipResult = await this.zipBackup(metadataAuthorsPath, newBackup).then(() => true).catch((error) => { + // Zip sqlite file, /metadata/items, and /metadata/authors folders + const zipResult = await this.zipBackup(sqliteBackupPath, newBackup).catch((error) => { Logger.error(`[BackupManager] Backup Failed ${error}`) return false }) - if (zipResult) { - Logger.info(`[BackupManager] Backup successful ${newBackup.id}`) - await filePerms.setDefault(newBackup.fullPath) - newBackup.fileSize = await getFileSize(newBackup.fullPath) - var existingIndex = this.backups.findIndex(b => b.id === newBackup.id) - if (existingIndex >= 0) { - this.backups.splice(existingIndex, 1, newBackup) - } else { - this.backups.push(newBackup) - } - // Check remove oldest backup - if (this.backups.length > this.serverSettings.backupsToKeep) { - this.backups.sort((a, b) => a.createdAt - b.createdAt) + // Remove sqlite backup + await fs.remove(sqliteBackupPath) - var oldBackup = this.backups.shift() - Logger.debug(`[BackupManager] Removing old backup ${oldBackup.id}`) - this.removeBackup(oldBackup) - } - return true + if (!zipResult) return false + + Logger.info(`[BackupManager] Backup successful ${newBackup.id}`) + + newBackup.fileSize = await getFileSize(newBackup.fullPath) + + const existingIndex = this.backups.findIndex(b => b.id === newBackup.id) + if (existingIndex >= 0) { + this.backups.splice(existingIndex, 1, newBackup) } else { - return false + this.backups.push(newBackup) } + + // Check remove oldest backup + if (this.backups.length > this.backupsToKeep) { + this.backups.sort((a, b) => a.createdAt - b.createdAt) + + const oldBackup = this.backups.shift() + Logger.debug(`[BackupManager] Removing old backup ${oldBackup.id}`) + this.removeBackup(oldBackup) + } + return true } 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) => { // create a file to stream archive data to const output = fs.createWriteStream(backup.fullPath) @@ -245,7 +304,7 @@ class BackupManager { // 'close' event is fired only when a file descriptor is involved output.on('close', () => { 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. @@ -281,7 +340,7 @@ class BackupManager { reject(err) }) archive.on('progress', ({ fs: fsobj }) => { - const maxBackupSizeInBytes = this.serverSettings.maxBackupSize * 1000 * 1000 * 1000 + const maxBackupSizeInBytes = this.maxBackupSize * 1000 * 1000 * 1000 if (fsobj.processedBytes > maxBackupSizeInBytes) { Logger.error(`[BackupManager] Archiver is too large - aborting to prevent endless loop, Bytes Processed: ${fsobj.processedBytes}`) archive.abort() @@ -295,26 +354,9 @@ class BackupManager { // pipe archive data to the file archive.pipe(output) - archive.directory(Path.join(this.db.LibraryItemsPath, 'data'), 'config/libraryItems/data') - 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') - - if (metadataAuthorsPath) { - Logger.debug(`[BackupManager] Backing up Metadata Authors "${metadataAuthorsPath}"`) - archive.directory(metadataAuthorsPath, 'metadata-authors') - } - } + archive.file(sqliteBackupPath, { name: 'absdatabase.sqlite' }) + archive.directory(this.ItemsMetadataPath, 'metadata-items') + archive.directory(this.AuthorsMetadataPath, 'metadata-authors') archive.append(backup.detailsString, { name: 'details' }) diff --git a/server/objects/Backup.js b/server/objects/Backup.js index 1d9d4aa2..da0df4eb 100644 --- a/server/objects/Backup.js +++ b/server/objects/Backup.js @@ -6,7 +6,6 @@ class Backup { constructor(data = null) { this.id = null this.datePretty = null - this.backupMetadataCovers = null this.backupDirPath = null this.filename = null @@ -23,9 +22,9 @@ class Backup { } get detailsString() { - var details = [] + const details = [] details.push(this.id) - details.push(this.backupMetadataCovers ? '1' : '0') + details.push('1') // Unused old boolean spot details.push(this.createdAt) details.push(this.serverVersion) return details.join('\n') @@ -33,7 +32,6 @@ class Backup { construct(data) { this.id = data.details[0] - this.backupMetadataCovers = data.details[1] === '1' this.createdAt = Number(data.details[2]) this.serverVersion = data.details[3] || null @@ -48,7 +46,6 @@ class Backup { toJSON() { return { id: this.id, - backupMetadataCovers: this.backupMetadataCovers, backupDirPath: this.backupDirPath, datePretty: this.datePretty, 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.datePretty = date.format(new Date(), 'ddd, MMM D YYYY HH:mm') - this.backupMetadataCovers = data.backupMetadataCovers - - this.backupDirPath = data.backupDirPath + this.backupDirPath = backupDirPath this.filename = this.id + '.audiobookshelf' this.path = Path.join('backups', this.filename) diff --git a/server/objects/settings/ServerSettings.js b/server/objects/settings/ServerSettings.js index a303a790..e0416e66 100644 --- a/server/objects/settings/ServerSettings.js +++ b/server/objects/settings/ServerSettings.js @@ -29,7 +29,6 @@ class ServerSettings { this.backupSchedule = false // If false then auto-backups are disabled this.backupsToKeep = 2 this.maxBackupSize = 1 - this.backupMetadataCovers = true // Logger this.loggerDailyLogsToKeep = 7 @@ -82,7 +81,6 @@ class ServerSettings { this.backupSchedule = settings.backupSchedule || false this.backupsToKeep = settings.backupsToKeep || 2 this.maxBackupSize = settings.maxBackupSize || 1 - this.backupMetadataCovers = settings.backupMetadataCovers !== false this.loggerDailyLogsToKeep = settings.loggerDailyLogsToKeep || 7 this.loggerScannerLogsToKeep = settings.loggerScannerLogsToKeep || 2 @@ -145,7 +143,6 @@ class ServerSettings { backupSchedule: this.backupSchedule, backupsToKeep: this.backupsToKeep, maxBackupSize: this.maxBackupSize, - backupMetadataCovers: this.backupMetadataCovers, loggerDailyLogsToKeep: this.loggerDailyLogsToKeep, loggerScannerLogsToKeep: this.loggerScannerLogsToKeep, homeBookshelfView: this.homeBookshelfView, diff --git a/server/utils/migrations/dbMigration.js b/server/utils/migrations/dbMigration.js index 7a018c73..676f2344 100644 --- a/server/utils/migrations/dbMigration.js +++ b/server/utils/migrations/dbMigration.js @@ -798,8 +798,7 @@ module.exports.migrate = async (DatabaseModels) => { /** * @returns {boolean} true if old database exists */ -module.exports.checkShouldMigrate = async (force = false) => { +module.exports.checkShouldMigrate = async () => { if (await oldDbFiles.checkHasOldDb()) return true - if (!force) return false return oldDbFiles.checkHasOldDbZip() } \ No newline at end of file