mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-07-07 09:51:37 +00:00
131 lines
5.8 KiB
JavaScript
131 lines
5.8 KiB
JavaScript
/**
|
|
* @typedef MigrationContext
|
|
* @property {import('sequelize').QueryInterface} queryInterface
|
|
* @property {import('../Logger')} logger
|
|
*
|
|
* @typedef MigrationOptions
|
|
* @property {MigrationContext} context
|
|
*/
|
|
|
|
const migrationVersion = '2.33.2'
|
|
const migrationName = `${migrationVersion}-setup-audible`
|
|
const loggerPrefix = `[${migrationVersion} migration]`
|
|
|
|
/**
|
|
* Creates audibleAccounts and audibleBooks tables if they don't exist,
|
|
* adds missing columns to existing tables, and ensures all indexes exist.
|
|
*
|
|
* @param {MigrationOptions} options
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async function up({ context: { queryInterface, logger } }) {
|
|
logger.info(`${loggerPrefix} UPGRADE BEGIN: ${migrationName}`)
|
|
|
|
const DataTypes = queryInterface.sequelize.Sequelize.DataTypes
|
|
|
|
// Safe index creation — silently ignores "already exists" errors
|
|
const addIndexSafe = async (table, fields, options = {}) => {
|
|
try {
|
|
await queryInterface.addIndex(table, fields, options)
|
|
} catch (err) {
|
|
logger.info(`${loggerPrefix} index on ${table}(${fields.join(',')}) already exists, skipping`)
|
|
}
|
|
}
|
|
|
|
// audibleAccounts table
|
|
const accountsDesc = await queryInterface.describeTable('audibleAccounts').catch(() => null)
|
|
if (!accountsDesc) {
|
|
logger.info(`${loggerPrefix} creating table "audibleAccounts"`)
|
|
await queryInterface.createTable('audibleAccounts', {
|
|
id: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, primaryKey: true },
|
|
email: { type: DataTypes.STRING, allowNull: false },
|
|
region: { type: DataTypes.STRING, allowNull: false, defaultValue: 'us' },
|
|
encryptedToken: { type: DataTypes.TEXT, allowNull: false },
|
|
encryptedCookies: { type: DataTypes.TEXT, allowNull: true },
|
|
libraryId: { type: DataTypes.UUID, allowNull: true },
|
|
shelfPosition: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 },
|
|
lastSync: { type: DataTypes.DATE },
|
|
isActive: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true },
|
|
createdAt: { type: DataTypes.DATE, allowNull: false },
|
|
updatedAt: { type: DataTypes.DATE, allowNull: false },
|
|
userId: { type: DataTypes.UUID, references: { model: 'users', key: 'id' }, onDelete: 'CASCADE' }
|
|
})
|
|
logger.info(`${loggerPrefix} created table "audibleAccounts"`)
|
|
} else {
|
|
if (!accountsDesc.encryptedCookies) {
|
|
logger.info(`${loggerPrefix} adding column "encryptedCookies" to "audibleAccounts"`)
|
|
await queryInterface.addColumn('audibleAccounts', 'encryptedCookies', { type: DataTypes.TEXT, allowNull: true })
|
|
}
|
|
if (!accountsDesc.libraryId) {
|
|
logger.info(`${loggerPrefix} adding column "libraryId" to "audibleAccounts"`)
|
|
await queryInterface.addColumn('audibleAccounts', 'libraryId', { type: DataTypes.TEXT, allowNull: true })
|
|
}
|
|
if (!accountsDesc.shelfPosition) {
|
|
logger.info(`${loggerPrefix} adding column "shelfPosition" to "audibleAccounts"`)
|
|
await queryInterface.addColumn('audibleAccounts', 'shelfPosition', { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 })
|
|
}
|
|
}
|
|
|
|
// Indexes on audibleAccounts (applied to both new and existing tables)
|
|
await addIndexSafe('audibleAccounts', ['userId'])
|
|
await addIndexSafe('audibleAccounts', ['email', 'userId'], { unique: true, name: 'audibleAccounts_email_userId_unique' })
|
|
|
|
// audibleBooks table
|
|
const booksDesc = await queryInterface.describeTable('audibleBooks').catch(() => null)
|
|
if (!booksDesc) {
|
|
logger.info(`${loggerPrefix} creating table "audibleBooks"`)
|
|
await queryInterface.createTable('audibleBooks', {
|
|
id: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, primaryKey: true },
|
|
asin: { type: DataTypes.STRING, allowNull: false },
|
|
title: { type: DataTypes.STRING, allowNull: false },
|
|
subtitle: { type: DataTypes.STRING },
|
|
authors: { type: DataTypes.JSON, defaultValue: [] },
|
|
narrators: { type: DataTypes.JSON, defaultValue: [] },
|
|
seriesName: { type: DataTypes.STRING },
|
|
seriesPosition: { type: DataTypes.STRING },
|
|
releaseDate: { type: DataTypes.STRING },
|
|
coverUrl: { type: DataTypes.TEXT },
|
|
publisherName: { type: DataTypes.STRING },
|
|
summary: { type: DataTypes.TEXT },
|
|
runtimeLengthMin: { type: DataTypes.INTEGER },
|
|
status: { type: DataTypes.STRING, allowNull: false, defaultValue: 'available' },
|
|
lastChecked: { type: DataTypes.DATE },
|
|
createdAt: { type: DataTypes.DATE, allowNull: false },
|
|
updatedAt: { type: DataTypes.DATE, allowNull: false },
|
|
audibleAccountId: { type: DataTypes.UUID, references: { model: 'audibleAccounts', key: 'id' }, onDelete: 'CASCADE' }
|
|
})
|
|
logger.info(`${loggerPrefix} created table "audibleBooks"`)
|
|
} else {
|
|
logger.info(`${loggerPrefix} table "audibleBooks" already exists`)
|
|
}
|
|
|
|
// Indexes on audibleBooks (applied to both new and existing tables)
|
|
await addIndexSafe('audibleBooks', ['audibleAccountId'])
|
|
await addIndexSafe('audibleBooks', ['asin', 'audibleAccountId'], { unique: true, name: 'audibleBooks_asin_accountId_unique' })
|
|
|
|
logger.info(`${loggerPrefix} UPGRADE END: ${migrationName}`)
|
|
}
|
|
|
|
/**
|
|
* @param {MigrationOptions} options
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async function down({ context: { queryInterface, logger } }) {
|
|
logger.info(`${loggerPrefix} DOWNGRADE BEGIN: ${migrationName}`)
|
|
|
|
const booksDesc = await queryInterface.describeTable('audibleBooks').catch(() => null)
|
|
if (booksDesc) {
|
|
await queryInterface.dropTable('audibleBooks')
|
|
logger.info(`${loggerPrefix} dropped table "audibleBooks"`)
|
|
}
|
|
|
|
const accountsDesc = await queryInterface.describeTable('audibleAccounts').catch(() => null)
|
|
if (accountsDesc) {
|
|
await queryInterface.dropTable('audibleAccounts')
|
|
logger.info(`${loggerPrefix} dropped table "audibleAccounts"`)
|
|
}
|
|
|
|
logger.info(`${loggerPrefix} DOWNGRADE END: ${migrationName}`)
|
|
}
|
|
|
|
module.exports = { up, down }
|