mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2025-12-24 04:09:49 +00:00
Introduce placeholder migrations for Backbone models so they never implicitly run migrations whenever they are `fetch`ed. We prefer to run our migrations explicitly upon app startup and then let Backbone models be (slightly) dumb(er) models, without inadvertently triggering migrations.
166 lines
4.9 KiB
JavaScript
166 lines
4.9 KiB
JavaScript
const { last } = require('lodash');
|
|
|
|
const { runMigrations } = require('./run_migrations');
|
|
|
|
|
|
// IMPORTANT: The migrations below are run on a database that may be very large
|
|
// due to attachments being directly stored inside the database. Please avoid
|
|
// any expensive operations, e.g. modifying all messages / attachments, etc., as
|
|
// it may cause out-of-memory errors for users with long histories:
|
|
// https://github.com/signalapp/Signal-Desktop/issues/2163
|
|
exports.migrations = [
|
|
{
|
|
version: '12.0',
|
|
migrate(transaction, next) {
|
|
console.log('Migration 12');
|
|
console.log('creating object stores');
|
|
const messages = transaction.db.createObjectStore('messages');
|
|
messages.createIndex('conversation', ['conversationId', 'received_at'], {
|
|
unique: false,
|
|
});
|
|
messages.createIndex('receipt', 'sent_at', { unique: false });
|
|
messages.createIndex('unread', ['conversationId', 'unread'], { unique: false });
|
|
messages.createIndex('expires_at', 'expires_at', { unique: false });
|
|
|
|
const conversations = transaction.db.createObjectStore('conversations');
|
|
conversations.createIndex('inbox', 'active_at', { unique: false });
|
|
conversations.createIndex('group', 'members', {
|
|
unique: false,
|
|
multiEntry: true,
|
|
});
|
|
conversations.createIndex('type', 'type', {
|
|
unique: false,
|
|
});
|
|
conversations.createIndex('search', 'tokens', {
|
|
unique: false,
|
|
multiEntry: true,
|
|
});
|
|
|
|
transaction.db.createObjectStore('groups');
|
|
|
|
transaction.db.createObjectStore('sessions');
|
|
transaction.db.createObjectStore('identityKeys');
|
|
transaction.db.createObjectStore('preKeys');
|
|
transaction.db.createObjectStore('signedPreKeys');
|
|
transaction.db.createObjectStore('items');
|
|
|
|
console.log('creating debug log');
|
|
transaction.db.createObjectStore('debug');
|
|
|
|
next();
|
|
},
|
|
},
|
|
{
|
|
version: '13.0',
|
|
migrate(transaction, next) {
|
|
console.log('Migration 13');
|
|
console.log('Adding fields to identity keys');
|
|
const identityKeys = transaction.objectStore('identityKeys');
|
|
const request = identityKeys.openCursor();
|
|
const promises = [];
|
|
request.onsuccess = (event) => {
|
|
const cursor = event.target.result;
|
|
if (cursor) {
|
|
const attributes = cursor.value;
|
|
attributes.timestamp = 0;
|
|
attributes.firstUse = false;
|
|
attributes.nonblockingApproval = false;
|
|
attributes.verified = 0;
|
|
promises.push(new Promise(((resolve, reject) => {
|
|
const putRequest = identityKeys.put(attributes, attributes.id);
|
|
putRequest.onsuccess = resolve;
|
|
putRequest.onerror = (e) => {
|
|
console.log(e);
|
|
reject(e);
|
|
};
|
|
})));
|
|
cursor.continue();
|
|
} else {
|
|
// no more results
|
|
// eslint-disable-next-line more/no-then
|
|
Promise.all(promises).then(() => {
|
|
next();
|
|
});
|
|
}
|
|
};
|
|
request.onerror = (event) => {
|
|
console.log(event);
|
|
};
|
|
},
|
|
},
|
|
{
|
|
version: '14.0',
|
|
migrate(transaction, next) {
|
|
console.log('Migration 14');
|
|
console.log('Adding unprocessed message store');
|
|
const unprocessed = transaction.db.createObjectStore('unprocessed');
|
|
unprocessed.createIndex('received', 'timestamp', { unique: false });
|
|
next();
|
|
},
|
|
},
|
|
{
|
|
version: '15.0',
|
|
migrate(transaction, next) {
|
|
console.log('Migration 15');
|
|
console.log('Adding messages index for de-duplication');
|
|
const messages = transaction.objectStore('messages');
|
|
messages.createIndex('unique', ['source', 'sourceDevice', 'sent_at'], {
|
|
unique: true,
|
|
});
|
|
next();
|
|
},
|
|
},
|
|
{
|
|
version: '16.0',
|
|
migrate(transaction, next) {
|
|
console.log('Migration 16');
|
|
console.log('Dropping log table, since we now log to disk');
|
|
transaction.db.deleteObjectStore('debug');
|
|
next();
|
|
},
|
|
},
|
|
{
|
|
version: 17,
|
|
async migrate(transaction, next) {
|
|
console.log('Migration 17');
|
|
|
|
const start = Date.now();
|
|
|
|
const messagesStore = transaction.objectStore('messages');
|
|
console.log('Create index from attachment schema version to attachment');
|
|
messagesStore.createIndex('schemaVersion', 'schemaVersion', { unique: false });
|
|
|
|
const duration = Date.now() - start;
|
|
|
|
console.log(
|
|
'Complete migration to database version 17.',
|
|
`Duration: ${duration}ms`
|
|
);
|
|
next();
|
|
},
|
|
},
|
|
];
|
|
|
|
const database = {
|
|
id: 'signal',
|
|
nolog: true,
|
|
migrations: exports.migrations,
|
|
};
|
|
|
|
exports.run = ({ Backbone } = {}) =>
|
|
runMigrations({ Backbone, database });
|
|
|
|
exports.getDatabase = () => ({
|
|
name: database.id,
|
|
version: last(exports.migrations).version,
|
|
});
|
|
|
|
exports.getLatestVersion = () => {
|
|
const lastMigration = last(migrations);
|
|
if (!lastMigration) {
|
|
return null;
|
|
}
|
|
|
|
return lastMigration.version;
|
|
};
|