Forráskód Böngészése

feat(syncing-server): distinguish between legacy and current items model usage (#712)

* feat(syncing-server): turn mysql items model into legacy

* fix: rename MySQL model to SQL model to include SQLite option

* feat(syncing-server): distinguish between legacy and current items model usage
Karol Sójko 1 éve
szülő
commit
bf8f91f83d
32 módosított fájl, 613 hozzáadás és 20 törlés
  1. 50 0
      packages/syncing-server/migrations/mysql-legacy/1606470249552-init_database.ts
  2. 15 0
      packages/syncing-server/migrations/mysql-legacy/1617615657558-add_extension_settings.ts
  3. 27 0
      packages/syncing-server/migrations/mysql-legacy/1629964808297-drop_unused_indexes.ts
  4. 11 0
      packages/syncing-server/migrations/mysql-legacy/1630318893601-refactor_calculating_integrity_hash.ts
  5. 12 0
      packages/syncing-server/migrations/mysql-legacy/1630417724617-restrict_content_type.ts
  6. 26 0
      packages/syncing-server/migrations/mysql-legacy/1631529502150-add_revision_for_duplicated_items.ts
  7. 13 0
      packages/syncing-server/migrations/mysql-legacy/1631530260504-drop_item_revisions_joining_table.ts
  8. 36 0
      packages/syncing-server/migrations/mysql-legacy/1632219307742-cleanup_orphan_items_and_revisions.ts
  9. 28 0
      packages/syncing-server/migrations/mysql-legacy/1632221263106-add_revisions_items_relation.ts
  10. 13 0
      packages/syncing-server/migrations/mysql-legacy/1637738491169-add_item_content_size.ts
  11. 11 0
      packages/syncing-server/migrations/mysql-legacy/1639134926025-remove_extension_settings.ts
  12. 11 0
      packages/syncing-server/migrations/mysql-legacy/1642073387521-remove_sf_extension_items.ts
  13. 11 0
      packages/syncing-server/migrations/mysql-legacy/1647501696205-remove_user_agent.ts
  14. 13 0
      packages/syncing-server/migrations/mysql-legacy/1654518291191-add_updated_with_session.ts
  15. 16 0
      packages/syncing-server/migrations/mysql-legacy/1689671563304-add-notifications.ts
  16. 25 0
      packages/syncing-server/migrations/mysql-legacy/1689671563305-add-shared-vault-and-key-system-associations.ts
  17. 29 0
      packages/syncing-server/migrations/mysql-legacy/1689677728282-add-shared-vaults-with-users-and-invites.ts
  18. 17 0
      packages/syncing-server/migrations/mysql-legacy/1689745128577-add-messages.ts
  19. 27 0
      packages/syncing-server/migrations/mysql-legacy/1689746180559-rename-key-message-identifier.ts
  20. 23 0
      packages/syncing-server/migrations/mysql-legacy/1690900526061-delete_privileges.ts
  21. 11 0
      packages/syncing-server/migrations/mysql-legacy/1690975361562-update_unknown_content.ts
  22. 22 0
      packages/syncing-server/migrations/mysql-legacy/1692176803410-remove_revisions_foreign_key.ts
  23. 21 0
      packages/syncing-server/migrations/mysql-legacy/1692264556858-remove_associations.ts
  24. 13 0
      packages/syncing-server/migrations/mysql-legacy/1692619430384-remove-shared-vault-limit.ts
  25. 19 0
      packages/syncing-server/migrations/mysql/1693219736168-add-shared-vault-information.ts
  26. 65 0
      packages/syncing-server/migrations/sqlite/1693220037441-add-shared-vault-information.ts
  27. 28 13
      packages/syncing-server/src/Bootstrap/Container.ts
  28. 10 2
      packages/syncing-server/src/Bootstrap/DataSource.ts
  29. 3 1
      packages/syncing-server/src/Bootstrap/Types.ts
  30. 1 0
      packages/syncing-server/src/Infra/TypeORM/MongoDBItem.ts
  31. 3 1
      packages/syncing-server/src/Infra/TypeORM/SQLItem.ts
  32. 3 3
      packages/syncing-server/src/Infra/TypeORM/TypeORMItemRepositoryResolver.ts

+ 50 - 0
packages/syncing-server/migrations/mysql-legacy/1606470249552-init_database.ts

@@ -0,0 +1,50 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class initDatabase1606470249552 implements MigrationInterface {
+  name = 'initDatabase1606470249552'
+
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await this.fixUpdatedAtTimestampsFromLegacyMigration(queryRunner)
+
+    await queryRunner.query(
+      'CREATE TABLE IF NOT EXISTS `items` (`uuid` varchar(36) NOT NULL, `duplicate_of` varchar(36) NULL, `items_key_id` varchar(255) NULL, `content` mediumtext NULL, `content_type` varchar(255) NULL, `enc_item_key` text NULL, `auth_hash` varchar(255) NULL, `user_uuid` varchar(36) NULL, `deleted` tinyint(1) NULL DEFAULT 0, `last_user_agent` text NULL, `created_at` datetime(6) NOT NULL, `updated_at` datetime(6) NOT NULL, `created_at_timestamp` BIGINT NOT NULL, `updated_at_timestamp` BIGINT NOT NULL, INDEX `index_items_on_content_type` (`content_type`), INDEX `index_items_on_user_uuid` (`user_uuid`), INDEX `index_items_on_deleted` (`deleted`), INDEX `updated_at_timestamp` (`updated_at_timestamp`), INDEX `index_items_on_updated_at` (`updated_at`), INDEX `user_uuid_and_updated_at_timestamp_and_created_at_timestamp` (`user_uuid`, `updated_at_timestamp`, `created_at_timestamp`), INDEX `index_items_on_user_uuid_and_updated_at_and_created_at` (`user_uuid`, `updated_at`, `created_at`), INDEX `index_items_on_user_uuid_and_content_type` (`user_uuid`, `content_type`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
+    )
+    await queryRunner.query(
+      'CREATE TABLE IF NOT EXISTS `revisions` (`uuid` varchar(36) NOT NULL, `item_uuid` varchar(36) NULL, `content` mediumtext NULL, `content_type` varchar(255) NULL, `items_key_id` varchar(255) NULL, `enc_item_key` text NULL, `auth_hash` varchar(255) NULL, `creation_date` date NULL, `created_at` datetime(6) NULL, `updated_at` datetime(6) NULL, INDEX `index_revisions_on_item_uuid` (`item_uuid`), INDEX `index_revisions_on_creation_date` (`creation_date`), INDEX `index_revisions_on_created_at` (`created_at`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
+    )
+    await queryRunner.query(
+      'CREATE TABLE IF NOT EXISTS `item_revisions` (`uuid` varchar(36) NOT NULL, `item_uuid` varchar(36) NOT NULL, `revision_uuid` varchar(36) NOT NULL, INDEX `index_item_revisions_on_item_uuid` (`item_uuid`), INDEX `index_item_revisions_on_revision_uuid` (`revision_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
+    )
+  }
+
+  public async down(_queryRunner: QueryRunner): Promise<void> {
+    return
+  }
+
+  private async fixUpdatedAtTimestampsFromLegacyMigration(queryRunner: QueryRunner): Promise<void> {
+    const itemsTableExistsQueryResult = await queryRunner.manager.query(
+      'SELECT COUNT(*) as count FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = "items"',
+    )
+    const itemsTableExists = itemsTableExistsQueryResult[0].count === 1
+    if (!itemsTableExists) {
+      return
+    }
+
+    const updatedAtTimestampColumnExistsQueryResult = await queryRunner.manager.query(
+      'SELECT COUNT(*) as count FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = "items" AND column_name = "updated_at_timestamp"',
+    )
+    const updatedAtTimestampColumnExists = updatedAtTimestampColumnExistsQueryResult[0].count === 1
+    if (updatedAtTimestampColumnExists) {
+      return
+    }
+
+    await queryRunner.query('ALTER TABLE `items` ADD COLUMN `updated_at_timestamp` BIGINT NOT NULL')
+    await queryRunner.query('ALTER TABLE `items` ADD COLUMN `created_at_timestamp` BIGINT NOT NULL')
+    await queryRunner.query(
+      'ALTER TABLE `items` ADD INDEX `user_uuid_and_updated_at_timestamp_and_created_at_timestamp` (`user_uuid`, `updated_at_timestamp`, `created_at_timestamp`)',
+    )
+    await queryRunner.query('ALTER TABLE `items` ADD INDEX `updated_at_timestamp` (`updated_at_timestamp`)')
+    await queryRunner.query('UPDATE `items` SET `created_at_timestamp` = UNIX_TIMESTAMP(`created_at`) * 1000000')
+    await queryRunner.query('UPDATE `items` SET `updated_at_timestamp` = UNIX_TIMESTAMP(`updated_at`) * 1000000')
+  }
+}

+ 15 - 0
packages/syncing-server/migrations/mysql-legacy/1617615657558-add_extension_settings.ts

@@ -0,0 +1,15 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class addExtensionSettings1617615657558 implements MigrationInterface {
+  name = 'addExtensionSettings1617615657558'
+
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(
+      'CREATE TABLE IF NOT EXISTS `extension_settings` (`uuid` varchar(36) NOT NULL, `extension_id` varchar(255) NULL, `mute_emails` tinyint(1) NULL DEFAULT 0, `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX `index_extension_settings_on_extension_id` (`extension_id`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
+    )
+  }
+
+  public async down(_queryRunner: QueryRunner): Promise<void> {
+    return
+  }
+}

+ 27 - 0
packages/syncing-server/migrations/mysql-legacy/1629964808297-drop_unused_indexes.ts

@@ -0,0 +1,27 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class dropUnusedIndexes1629964808297 implements MigrationInterface {
+  name = 'dropUnusedIndexes1629964808297'
+
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    const indexItemsOnUserAndTimestamp = await queryRunner.manager.query(
+      'SHOW INDEX FROM `items` where `key_name` = "index_items_on_user_uuid_and_updated_at_and_created_at"',
+    )
+    const indexItemsOnUserAndTimestampExists = indexItemsOnUserAndTimestamp && indexItemsOnUserAndTimestamp.length > 0
+    if (indexItemsOnUserAndTimestampExists) {
+      await queryRunner.query('ALTER TABLE `items` DROP INDEX index_items_on_user_uuid_and_updated_at_and_created_at')
+    }
+
+    const indexItemsOnUpdatedAt = await queryRunner.manager.query(
+      'SHOW INDEX FROM `items` where `key_name` = "index_items_on_updated_at"',
+    )
+    const indexItemsOnUpdatedAtExists = indexItemsOnUpdatedAt && indexItemsOnUpdatedAt.length > 0
+    if (indexItemsOnUpdatedAtExists) {
+      await queryRunner.query('ALTER TABLE `items` DROP INDEX index_items_on_updated_at')
+    }
+  }
+
+  public async down(): Promise<void> {
+    return
+  }
+}

+ 11 - 0
packages/syncing-server/migrations/mysql-legacy/1630318893601-refactor_calculating_integrity_hash.ts

@@ -0,0 +1,11 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class refactorCalculatingIntegrityHash1630318893601 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('ALTER TABLE `items` ADD INDEX `user_uuid_and_deleted` (`user_uuid`, `deleted`)')
+  }
+
+  public async down(): Promise<void> {
+    return
+  }
+}

+ 12 - 0
packages/syncing-server/migrations/mysql-legacy/1630417724617-restrict_content_type.ts

@@ -0,0 +1,12 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class restrictContentType1630417724617 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('UPDATE `items` SET content_type = "Unknown" WHERE `content_type` IS NULL')
+    await queryRunner.query('ALTER TABLE `items` CHANGE `content_type` `content_type` varchar(255) NOT NULL')
+  }
+
+  public async down(): Promise<void> {
+    return
+  }
+}

+ 26 - 0
packages/syncing-server/migrations/mysql-legacy/1631529502150-add_revision_for_duplicated_items.ts

@@ -0,0 +1,26 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+import { v4 } from 'uuid'
+
+export class addRevisionForDuplicatedItems1631529502150 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    const itemRevisions = await queryRunner.manager.query(
+      'SELECT r.uuid as originalRevisionUuid, ir.item_uuid as properItemUuid, ir.uuid as relationUuid FROM revisions r INNER JOIN item_revisions ir ON ir.revision_uuid = r.uuid AND ir.item_uuid <> r.item_uuid',
+    )
+
+    for (const itemRevision of itemRevisions) {
+      const revisionUuid = v4()
+
+      await queryRunner.manager.query(
+        `INSERT INTO revisions (uuid, item_uuid, content, content_type, items_key_id, enc_item_key, auth_hash, creation_date, created_at, updated_at) SELECT "${revisionUuid}", "${itemRevision['properItemUuid']}", content, content_type, items_key_id, enc_item_key, auth_hash, creation_date, created_at, updated_at FROM revisions WHERE uuid = "${itemRevision['originalRevisionUuid']}"`,
+      )
+      await queryRunner.manager.query(
+        `UPDATE item_revisions SET revision_uuid = "${revisionUuid}" WHERE uuid = "${itemRevision['relationUuid']}"`,
+      )
+    }
+  }
+
+  public async down(): Promise<void> {
+    return
+  }
+}

+ 13 - 0
packages/syncing-server/migrations/mysql-legacy/1631530260504-drop_item_revisions_joining_table.ts

@@ -0,0 +1,13 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class dropItemRevisionsJoiningTable1631530260504 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('DROP TABLE `item_revisions`')
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(
+      'CREATE TABLE `item_revisions` (`uuid` varchar(36) NOT NULL, `item_uuid` varchar(36) NOT NULL, `revision_uuid` varchar(36) NOT NULL, INDEX `index_item_revisions_on_item_uuid` (`item_uuid`), INDEX `index_item_revisions_on_revision_uuid` (`revision_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
+    )
+  }
+}

+ 36 - 0
packages/syncing-server/migrations/mysql-legacy/1632219307742-cleanup_orphan_items_and_revisions.ts

@@ -0,0 +1,36 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class cleanupOrphanItemsAndRevisions1632219307742 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    const usersTableExistsQueryResult = await queryRunner.manager.query(
+      'SELECT COUNT(*) as count FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = "users"',
+    )
+    const usersTableExists = usersTableExistsQueryResult[0].count === 1
+    if (usersTableExists) {
+      const orphanedItems = await queryRunner.manager.query(
+        'SELECT i.uuid as uuid FROM items i LEFT JOIN users u ON i.user_uuid = u.uuid WHERE u.uuid IS NULL',
+      )
+
+      for (const orphanedItem of orphanedItems) {
+        await queryRunner.manager.query(`DELETE FROM revisions WHERE item_uuid = "${orphanedItem['uuid']}"`)
+        await queryRunner.manager.query(`DELETE FROM items WHERE uuid = "${orphanedItem['uuid']}"`)
+      }
+    }
+
+    await queryRunner.manager.query('DELETE FROM items WHERE user_uuid IS NULL')
+
+    const orphanedRevisions = await queryRunner.manager.query(
+      'SELECT r.uuid as uuid FROM revisions r LEFT JOIN items i ON r.item_uuid = i.uuid WHERE i.uuid IS NULL',
+    )
+
+    for (const orphanedRevision of orphanedRevisions) {
+      await queryRunner.manager.query(`DELETE FROM revisions WHERE uuid = "${orphanedRevision['uuid']}"`)
+    }
+
+    await queryRunner.manager.query('DELETE FROM revisions WHERE item_uuid IS NULL')
+  }
+
+  public async down(): Promise<void> {
+    return
+  }
+}

+ 28 - 0
packages/syncing-server/migrations/mysql-legacy/1632221263106-add_revisions_items_relation.ts

@@ -0,0 +1,28 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class addRevisionsItemsRelation1632221263106 implements MigrationInterface {
+  name = 'addRevisionsItemsRelation1632221263106'
+
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    const indexRevisionsOnItemUuid = await queryRunner.manager.query(
+      'SHOW INDEX FROM `revisions` where `key_name` = "index_revisions_on_item_uuid"',
+    )
+    const indexRevisionsOnItemUuidExists = indexRevisionsOnItemUuid && indexRevisionsOnItemUuid.length > 0
+    if (indexRevisionsOnItemUuidExists) {
+      await queryRunner.query('DROP INDEX `index_revisions_on_item_uuid` ON `revisions`')
+    }
+
+    await queryRunner.query('ALTER TABLE `revisions` CHANGE `item_uuid` `item_uuid` varchar(36) NOT NULL')
+    await queryRunner.query('ALTER TABLE `items` CHANGE `user_uuid` `user_uuid` varchar(36) NOT NULL')
+    await queryRunner.query(
+      'ALTER TABLE `revisions` ADD CONSTRAINT `FK_ab3b92e54701fe3010022a31d90` FOREIGN KEY (`item_uuid`) REFERENCES `items`(`uuid`) ON DELETE CASCADE ON UPDATE NO ACTION',
+    )
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('ALTER TABLE `revisions` DROP FOREIGN KEY `FK_ab3b92e54701fe3010022a31d90`')
+    await queryRunner.query('ALTER TABLE `items` CHANGE `user_uuid` `user_uuid` varchar(36) NULL')
+    await queryRunner.query('ALTER TABLE `revisions` CHANGE `item_uuid` `item_uuid` varchar(36) NULL')
+    await queryRunner.query('CREATE INDEX `index_revisions_on_item_uuid` ON `revisions` (`item_uuid`)')
+  }
+}

+ 13 - 0
packages/syncing-server/migrations/mysql-legacy/1637738491169-add_item_content_size.ts

@@ -0,0 +1,13 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class addItemContentSize1637738491169 implements MigrationInterface {
+  name = 'addItemContentSize1637738491169'
+
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('ALTER TABLE `items` ADD `content_size` INT UNSIGNED NULL')
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('ALTER TABLE `items` DROP COLUMN `content_size`')
+  }
+}

+ 11 - 0
packages/syncing-server/migrations/mysql-legacy/1639134926025-remove_extension_settings.ts

@@ -0,0 +1,11 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class removeExtensionSettings1639134926025 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('DROP TABLE `extension_settings`')
+  }
+
+  public async down(): Promise<void> {
+    return
+  }
+}

+ 11 - 0
packages/syncing-server/migrations/mysql-legacy/1642073387521-remove_sf_extension_items.ts

@@ -0,0 +1,11 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class removeSfExtensionItems1642073387521 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.manager.query('DELETE FROM items WHERE content_type = "SF|Extension"')
+  }
+
+  public async down(): Promise<void> {
+    return
+  }
+}

+ 11 - 0
packages/syncing-server/migrations/mysql-legacy/1647501696205-remove_user_agent.ts

@@ -0,0 +1,11 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class removeUserAgent1647501696205 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('ALTER TABLE `items` DROP COLUMN `last_user_agent`')
+  }
+
+  public async down(): Promise<void> {
+    return
+  }
+}

+ 13 - 0
packages/syncing-server/migrations/mysql-legacy/1654518291191-add_updated_with_session.ts

@@ -0,0 +1,13 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class addUpdatedWithSession1654518291191 implements MigrationInterface {
+  name = 'addUpdatedWithSession1654518291191'
+
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('ALTER TABLE `items` ADD `updated_with_session` varchar(36) NULL')
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('ALTER TABLE `items` DROP COLUMN `updated_with_session`')
+  }
+}

+ 16 - 0
packages/syncing-server/migrations/mysql-legacy/1689671563304-add-notifications.ts

@@ -0,0 +1,16 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class AddNotifications1689671563304 implements MigrationInterface {
+  name = 'AddNotifications1689671563304'
+
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(
+      'CREATE TABLE IF NOT EXISTS `notifications` (`uuid` varchar(36) NOT NULL, `user_uuid` varchar(36) NOT NULL, `type` varchar(36) NOT NULL, `payload` text NOT NULL, `created_at_timestamp` bigint NOT NULL, `updated_at_timestamp` bigint NOT NULL, INDEX `index_notifications_on_user_uuid` (`user_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
+    )
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('DROP INDEX `index_notifications_on_user_uuid` ON `notifications`')
+    await queryRunner.query('DROP TABLE `notifications`')
+  }
+}

+ 25 - 0
packages/syncing-server/migrations/mysql-legacy/1689671563305-add-shared-vault-and-key-system-associations.ts

@@ -0,0 +1,25 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class AddSharedVaultAndKeySystemAssociations1689671563305 implements MigrationInterface {
+  name = 'AddSharedVaultAndKeySystemAssociations1689671563305'
+
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(
+      'CREATE TABLE `shared_vault_associations` (`uuid` varchar(36) NOT NULL, `shared_vault_uuid` varchar(36) NOT NULL, `item_uuid` varchar(36) NOT NULL, `last_edited_by` varchar(36) NOT NULL, `created_at_timestamp` bigint NOT NULL, `updated_at_timestamp` bigint NOT NULL, INDEX `shared_vault_uuid_on_shared_vault_associations` (`shared_vault_uuid`), INDEX `item_uuid_on_shared_vault_associations` (`item_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
+    )
+    await queryRunner.query(
+      'CREATE TABLE `key_system_associations` (`uuid` varchar(36) NOT NULL, `key_system_uuid` varchar(36) NOT NULL, `item_uuid` varchar(36) NOT NULL, `created_at_timestamp` bigint NOT NULL, `updated_at_timestamp` bigint NOT NULL, INDEX `key_system_uuid_on_key_system_associations` (`key_system_uuid`), INDEX `item_uuid_on_key_system_associations` (`item_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
+    )
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('DROP INDEX `item_uuid_on_key_system_associations` ON `key_system_associations`')
+    await queryRunner.query('DROP INDEX `key_system_uuid_on_key_system_associations` ON `key_system_associations`')
+    await queryRunner.query('DROP TABLE `key_system_associations`')
+    await queryRunner.query('DROP INDEX `item_uuid_on_shared_vault_associations` ON `shared_vault_associations`')
+    await queryRunner.query(
+      'DROP INDEX `shared_vault_uuid_on_shared_vault_associations` ON `shared_vault_associations`',
+    )
+    await queryRunner.query('DROP TABLE `shared_vault_associations`')
+  }
+}

+ 29 - 0
packages/syncing-server/migrations/mysql-legacy/1689677728282-add-shared-vaults-with-users-and-invites.ts

@@ -0,0 +1,29 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class AddSharedVaultsWithUsersAndInvites1689677728282 implements MigrationInterface {
+  name = 'AddSharedVaultsWithUsersAndInvites1689677728282'
+
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(
+      'CREATE TABLE `shared_vaults` (`uuid` varchar(36) NOT NULL, `user_uuid` varchar(36) NOT NULL, `file_upload_bytes_used` int NOT NULL, `file_upload_bytes_limit` int NOT NULL, `created_at_timestamp` bigint NOT NULL, `updated_at_timestamp` bigint NOT NULL, INDEX `user_uuid_on_shared_vaults` (`user_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
+    )
+    await queryRunner.query(
+      'CREATE TABLE `shared_vault_users` (`uuid` varchar(36) NOT NULL, `shared_vault_uuid` varchar(36) NOT NULL, `user_uuid` varchar(36) NOT NULL, `permission` varchar(24) NOT NULL, `created_at_timestamp` bigint NOT NULL, `updated_at_timestamp` bigint NOT NULL, INDEX `shared_vault_uuid_on_shared_vault_users` (`shared_vault_uuid`), INDEX `user_uuid_on_shared_vault_users` (`user_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
+    )
+    await queryRunner.query(
+      'CREATE TABLE `shared_vault_invites` (`uuid` varchar(36) NOT NULL, `shared_vault_uuid` varchar(36) NOT NULL, `user_uuid` varchar(36) NOT NULL, `sender_uuid` varchar(36) NOT NULL, `encrypted_message` text NOT NULL, `permission` varchar(24) NOT NULL, `created_at_timestamp` bigint NOT NULL, `updated_at_timestamp` bigint NOT NULL, INDEX `shared_vault_uuid_on_shared_vault_invites` (`shared_vault_uuid`), INDEX `user_uuid_on_shared_vault_invites` (`user_uuid`), INDEX `sender_uuid_on_shared_vault_invites` (`sender_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
+    )
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('DROP INDEX `sender_uuid_on_shared_vault_invites` ON `shared_vault_invites`')
+    await queryRunner.query('DROP INDEX `user_uuid_on_shared_vault_invites` ON `shared_vault_invites`')
+    await queryRunner.query('DROP INDEX `shared_vault_uuid_on_shared_vault_invites` ON `shared_vault_invites`')
+    await queryRunner.query('DROP TABLE `shared_vault_invites`')
+    await queryRunner.query('DROP INDEX `user_uuid_on_shared_vault_users` ON `shared_vault_users`')
+    await queryRunner.query('DROP INDEX `shared_vault_uuid_on_shared_vault_users` ON `shared_vault_users`')
+    await queryRunner.query('DROP TABLE `shared_vault_users`')
+    await queryRunner.query('DROP INDEX `user_uuid_on_shared_vaults` ON `shared_vaults`')
+    await queryRunner.query('DROP TABLE `shared_vaults`')
+  }
+}

+ 17 - 0
packages/syncing-server/migrations/mysql-legacy/1689745128577-add-messages.ts

@@ -0,0 +1,17 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class AddMessages1689745128577 implements MigrationInterface {
+  name = 'AddMessages1689745128577'
+
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(
+      'CREATE TABLE `messages` (`uuid` varchar(36) NOT NULL, `recipient_uuid` varchar(36) NOT NULL, `sender_uuid` varchar(36) NOT NULL, `encrypted_message` text NOT NULL, `replaceability_identifier` varchar(255) NULL, `created_at_timestamp` bigint NOT NULL, `updated_at_timestamp` bigint NOT NULL, INDEX `recipient_uuid_on_messages` (`recipient_uuid`), INDEX `sender_uuid_on_messages` (`sender_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
+    )
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('DROP INDEX `sender_uuid_on_messages` ON `messages`')
+    await queryRunner.query('DROP INDEX `recipient_uuid_on_messages` ON `messages`')
+    await queryRunner.query('DROP TABLE `messages`')
+  }
+}

+ 27 - 0
packages/syncing-server/migrations/mysql-legacy/1689746180559-rename-key-message-identifier.ts

@@ -0,0 +1,27 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class RenameKeyMessageIdentifier1689746180559 implements MigrationInterface {
+  name = 'RenameKeyMessageIdentifier1689746180559'
+
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('DROP INDEX `key_system_uuid_on_key_system_associations` ON `key_system_associations`')
+    await queryRunner.query(
+      'ALTER TABLE `key_system_associations` CHANGE `key_system_uuid` `key_system_identifier` varchar(36) NOT NULL',
+    )
+    await queryRunner.query(
+      'CREATE INDEX `key_system_identifier_on_key_system_associations` ON `key_system_associations` (`key_system_identifier`)',
+    )
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(
+      'DROP INDEX `key_system_identifier_on_key_system_associations` ON `key_system_associations`',
+    )
+    await queryRunner.query(
+      'ALTER TABLE `key_system_associations` CHANGE `key_system_identifier` `key_system_uuid` varchar(36) NOT NULL',
+    )
+    await queryRunner.query(
+      'CREATE INDEX `key_system_uuid_on_key_system_associations` ON `key_system_associations` (`key_system_uuid`)',
+    )
+  }
+}

+ 23 - 0
packages/syncing-server/migrations/mysql-legacy/1690900526061-delete_privileges.ts

@@ -0,0 +1,23 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class DeletePrivileges1690900526061 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    const itemsWithPrivilegesContentTypeQueryResult = await queryRunner.manager.query(
+      'SELECT COUNT(*) as count FROM items i WHERE i.content_type = "SN|Privileges"',
+    )
+    const itemsWithPrivilegesContentTypeCount = +itemsWithPrivilegesContentTypeQueryResult[0].count
+
+    const batchSize = 1_000
+    const batchCount = Math.ceil(itemsWithPrivilegesContentTypeCount / batchSize)
+
+    for (let batchIndex = 0; batchIndex < batchCount; batchIndex++) {
+      await queryRunner.startTransaction()
+      await queryRunner.manager.query(`DELETE FROM items WHERE content_type = "SN|Privileges" LIMIT ${batchSize}`)
+      await queryRunner.commitTransaction()
+    }
+  }
+
+  public async down(): Promise<void> {
+    return
+  }
+}

+ 11 - 0
packages/syncing-server/migrations/mysql-legacy/1690975361562-update_unknown_content.ts

@@ -0,0 +1,11 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class UpdateUnknownContent1690975361562 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.manager.query('UPDATE items SET content_type = "Note" WHERE content_type = "Unknown"')
+  }
+
+  public async down(): Promise<void> {
+    return
+  }
+}

+ 22 - 0
packages/syncing-server/migrations/mysql-legacy/1692176803410-remove_revisions_foreign_key.ts

@@ -0,0 +1,22 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class RemoveRevisionsForeignKey1692176803410 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    const revisionsTableExistsQueryResult = await queryRunner.manager.query(
+      'SELECT COUNT(*) as count FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = "revisions"',
+    )
+    const revisionsTableExists = revisionsTableExistsQueryResult[0].count === 1
+    if (revisionsTableExists) {
+      try {
+        await queryRunner.query('ALTER TABLE `revisions` DROP FOREIGN KEY `FK_ab3b92e54701fe3010022a31d90`')
+      } catch (error) {
+        // eslint-disable-next-line no-console
+        console.log('Error dropping foreign key: ', (error as Error).message)
+      }
+    }
+  }
+
+  public async down(): Promise<void> {
+    return
+  }
+}

+ 21 - 0
packages/syncing-server/migrations/mysql-legacy/1692264556858-remove_associations.ts

@@ -0,0 +1,21 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class RemoveAssociations1692264556858 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(
+      'DROP INDEX `key_system_identifier_on_key_system_associations` ON `key_system_associations`',
+    )
+    await queryRunner.query('DROP INDEX `item_uuid_on_key_system_associations` ON `key_system_associations`')
+    await queryRunner.query('DROP TABLE `key_system_associations`')
+
+    await queryRunner.query('DROP INDEX `item_uuid_on_shared_vault_associations` ON `shared_vault_associations`')
+    await queryRunner.query(
+      'DROP INDEX `shared_vault_uuid_on_shared_vault_associations` ON `shared_vault_associations`',
+    )
+    await queryRunner.query('DROP TABLE `shared_vault_associations`')
+  }
+
+  public async down(): Promise<void> {
+    return
+  }
+}

+ 13 - 0
packages/syncing-server/migrations/mysql-legacy/1692619430384-remove-shared-vault-limit.ts

@@ -0,0 +1,13 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class RemoveSharedVaultLimit1692619430384 implements MigrationInterface {
+  name = 'RemoveSharedVaultLimit1692619430384'
+
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('ALTER TABLE `shared_vaults` DROP COLUMN `file_upload_bytes_limit`')
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('ALTER TABLE `shared_vaults` ADD `file_upload_bytes_limit` int NOT NULL')
+  }
+}

+ 19 - 0
packages/syncing-server/migrations/mysql/1693219736168-add-shared-vault-information.ts

@@ -0,0 +1,19 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class AddSharedVaultInformation1693219736168 implements MigrationInterface {
+  name = 'AddSharedVaultInformation1693219736168'
+
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('ALTER TABLE `items` ADD `last_edited_by` varchar(36) NULL')
+    await queryRunner.query('ALTER TABLE `items` ADD `shared_vault_uuid` varchar(36) NULL')
+    await queryRunner.query('ALTER TABLE `items` ADD `key_system_identifier` varchar(36) NULL')
+    await queryRunner.query('CREATE INDEX `index_items_on_shared_vault_uuid` ON `items` (`shared_vault_uuid`)')
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('DROP INDEX `index_items_on_shared_vault_uuid` ON `items`')
+    await queryRunner.query('ALTER TABLE `items` DROP COLUMN `key_system_identifier`')
+    await queryRunner.query('ALTER TABLE `items` DROP COLUMN `shared_vault_uuid`')
+    await queryRunner.query('ALTER TABLE `items` DROP COLUMN `last_edited_by`')
+  }
+}

+ 65 - 0
packages/syncing-server/migrations/sqlite/1693220037441-add-shared-vault-information.ts

@@ -0,0 +1,65 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class AddSharedVaultInformation1693220037441 implements MigrationInterface {
+  name = 'AddSharedVaultInformation1693220037441'
+
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('DROP INDEX "index_items_on_user_uuid_and_content_type"')
+    await queryRunner.query('DROP INDEX "user_uuid_and_updated_at_timestamp_and_created_at_timestamp"')
+    await queryRunner.query('DROP INDEX "user_uuid_and_deleted"')
+    await queryRunner.query('DROP INDEX "updated_at_timestamp"')
+    await queryRunner.query('DROP INDEX "index_items_on_deleted"')
+    await queryRunner.query('DROP INDEX "index_items_on_user_uuid"')
+    await queryRunner.query('DROP INDEX "index_items_on_content_type"')
+    await queryRunner.query(
+      'CREATE TABLE "temporary_items" ("uuid" varchar PRIMARY KEY NOT NULL, "duplicate_of" varchar(36), "items_key_id" varchar(255), "content" text, "content_type" varchar(255), "content_size" integer, "enc_item_key" text, "auth_hash" varchar(255), "user_uuid" varchar(36) NOT NULL, "deleted" tinyint(1) DEFAULT (0), "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL, "updated_with_session" varchar(36), "last_edited_by" varchar(36), "shared_vault_uuid" varchar(36), "key_system_identifier" varchar(36))',
+    )
+    await queryRunner.query(
+      'INSERT INTO "temporary_items"("uuid", "duplicate_of", "items_key_id", "content", "content_type", "content_size", "enc_item_key", "auth_hash", "user_uuid", "deleted", "created_at", "updated_at", "created_at_timestamp", "updated_at_timestamp", "updated_with_session") SELECT "uuid", "duplicate_of", "items_key_id", "content", "content_type", "content_size", "enc_item_key", "auth_hash", "user_uuid", "deleted", "created_at", "updated_at", "created_at_timestamp", "updated_at_timestamp", "updated_with_session" FROM "items"',
+    )
+    await queryRunner.query('DROP TABLE "items"')
+    await queryRunner.query('ALTER TABLE "temporary_items" RENAME TO "items"')
+    await queryRunner.query(
+      'CREATE INDEX "index_items_on_user_uuid_and_content_type" ON "items" ("user_uuid", "content_type") ',
+    )
+    await queryRunner.query(
+      'CREATE INDEX "user_uuid_and_updated_at_timestamp_and_created_at_timestamp" ON "items" ("user_uuid", "updated_at_timestamp", "created_at_timestamp") ',
+    )
+    await queryRunner.query('CREATE INDEX "user_uuid_and_deleted" ON "items" ("user_uuid", "deleted") ')
+    await queryRunner.query('CREATE INDEX "updated_at_timestamp" ON "items" ("updated_at_timestamp") ')
+    await queryRunner.query('CREATE INDEX "index_items_on_deleted" ON "items" ("deleted") ')
+    await queryRunner.query('CREATE INDEX "index_items_on_user_uuid" ON "items" ("user_uuid") ')
+    await queryRunner.query('CREATE INDEX "index_items_on_content_type" ON "items" ("content_type") ')
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('DROP INDEX "index_items_on_shared_vault_uuid"')
+    await queryRunner.query('DROP INDEX "user_uuid_and_deleted"')
+    await queryRunner.query('DROP INDEX "user_uuid_on_shared_vaults"')
+    await queryRunner.query('DROP INDEX "index_items_on_content_type"')
+    await queryRunner.query('DROP INDEX "index_items_on_user_uuid"')
+    await queryRunner.query('DROP INDEX "index_items_on_deleted"')
+    await queryRunner.query('DROP INDEX "updated_at_timestamp"')
+    await queryRunner.query('DROP INDEX "user_uuid_and_updated_at_timestamp_and_created_at_timestamp"')
+    await queryRunner.query('DROP INDEX "index_items_on_user_uuid_and_content_type"')
+    await queryRunner.query('ALTER TABLE "items" RENAME TO "temporary_items"')
+    await queryRunner.query(
+      'CREATE TABLE "items" ("uuid" varchar PRIMARY KEY NOT NULL, "duplicate_of" varchar(36), "items_key_id" varchar(255), "content" text, "content_type" varchar(255), "content_size" integer, "enc_item_key" text, "auth_hash" varchar(255), "user_uuid" varchar(36) NOT NULL, "deleted" tinyint(1) DEFAULT (0), "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL, "updated_with_session" varchar(36), "last_edited_by" varchar(36), "shared_vault_uuid" varchar(36), "key_system_identifier" varchar(36))',
+    )
+    await queryRunner.query(
+      'INSERT INTO "items"("uuid", "duplicate_of", "items_key_id", "content", "content_type", "content_size", "enc_item_key", "auth_hash", "user_uuid", "deleted", "created_at", "updated_at", "created_at_timestamp", "updated_at_timestamp", "updated_with_session", "last_edited_by", "shared_vault_uuid", "key_system_identifier") SELECT "uuid", "duplicate_of", "items_key_id", "content", "content_type", "content_size", "enc_item_key", "auth_hash", "user_uuid", "deleted", "created_at", "updated_at", "created_at_timestamp", "updated_at_timestamp", "updated_with_session", "last_edited_by", "shared_vault_uuid", "key_system_identifier" FROM "temporary_items"',
+    )
+    await queryRunner.query('DROP TABLE "temporary_items"')
+    await queryRunner.query('CREATE INDEX "index_items_on_content_type" ON "items" ("content_type") ')
+    await queryRunner.query('CREATE INDEX "index_items_on_user_uuid" ON "items" ("user_uuid") ')
+    await queryRunner.query('CREATE INDEX "index_items_on_deleted" ON "items" ("deleted") ')
+    await queryRunner.query('CREATE INDEX "updated_at_timestamp" ON "items" ("updated_at_timestamp") ')
+    await queryRunner.query(
+      'CREATE INDEX "user_uuid_and_updated_at_timestamp_and_created_at_timestamp" ON "items" ("user_uuid", "updated_at_timestamp", "created_at_timestamp") ',
+    )
+    await queryRunner.query(
+      'CREATE INDEX "index_items_on_user_uuid_and_content_type" ON "items" ("user_uuid", "content_type") ',
+    )
+    await queryRunner.query('CREATE INDEX "user_uuid_and_deleted" ON "items" ("user_uuid", "deleted") ')
+  }
+}

+ 28 - 13
packages/syncing-server/src/Bootstrap/Container.ts

@@ -158,6 +158,9 @@ import { TransitionItemsFromPrimaryToSecondaryDatabaseForUser } from '../Domain/
 import { SharedVaultFileMovedEventHandler } from '../Domain/Handler/SharedVaultFileMovedEventHandler'
 import { TransitionStatusUpdatedEventHandler } from '../Domain/Handler/TransitionStatusUpdatedEventHandler'
 import { TriggerTransitionFromPrimaryToSecondaryDatabaseForUser } from '../Domain/UseCase/Transition/TriggerTransitionFromPrimaryToSecondaryDatabaseForUser/TriggerTransitionFromPrimaryToSecondaryDatabaseForUser'
+import { SQLItem } from '../Infra/TypeORM/SQLItem'
+import { SQLItemPersistenceMapper } from '../Mapping/Persistence/SQLItemPersistenceMapper'
+import { SQLItemRepository } from '../Infra/TypeORM/SQLItemRepository'
 
 export class ContainerConfigLoader {
   private readonly DEFAULT_CONTENT_SIZE_TRANSFER_LIMIT = 10_000_000
@@ -305,6 +308,9 @@ export class ContainerConfigLoader {
     container
       .bind<MapperInterface<Item, SQLLegacyItem>>(TYPES.Sync_SQLLegacyItemPersistenceMapper)
       .toConstantValue(new SQLLegacyItemPersistenceMapper())
+    container
+      .bind<MapperInterface<Item, SQLItem>>(TYPES.Sync_SQLItemPersistenceMapper)
+      .toConstantValue(new SQLItemPersistenceMapper())
     container
       .bind<MapperInterface<ItemHash, ItemHashHttpRepresentation>>(TYPES.Sync_ItemHashHttpMapper)
       .toConstantValue(new ItemHashHttpMapper())
@@ -362,6 +368,9 @@ export class ContainerConfigLoader {
     container
       .bind<Repository<SQLLegacyItem>>(TYPES.Sync_ORMLegacyItemRepository)
       .toDynamicValue(() => appDataSource.getRepository(SQLLegacyItem))
+    container
+      .bind<Repository<SQLItem>>(TYPES.Sync_ORMItemRepository)
+      .toConstantValue(appDataSource.getRepository(SQLItem))
     container
       .bind<Repository<TypeORMSharedVault>>(TYPES.Sync_ORMSharedVaultRepository)
       .toConstantValue(appDataSource.getRepository(TypeORMSharedVault))
@@ -401,19 +410,25 @@ export class ContainerConfigLoader {
 
     // Repositories
     container
-      .bind<ItemRepositoryInterface>(TYPES.Sync_SQLLegacyItemRepository)
+      .bind<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository)
       .toConstantValue(
-        new SQLLegacyItemRepository(
-          container.get<Repository<SQLLegacyItem>>(TYPES.Sync_ORMLegacyItemRepository),
-          container.get<MapperInterface<Item, SQLLegacyItem>>(TYPES.Sync_SQLLegacyItemPersistenceMapper),
-          container.get<Logger>(TYPES.Sync_Logger),
-        ),
+        isConfiguredForHomeServer
+          ? new SQLItemRepository(
+              container.get<Repository<SQLItem>>(TYPES.Sync_ORMItemRepository),
+              container.get<MapperInterface<Item, SQLItem>>(TYPES.Sync_SQLItemPersistenceMapper),
+              container.get<Logger>(TYPES.Sync_Logger),
+            )
+          : new SQLLegacyItemRepository(
+              container.get<Repository<SQLLegacyItem>>(TYPES.Sync_ORMLegacyItemRepository),
+              container.get<MapperInterface<Item, SQLLegacyItem>>(TYPES.Sync_SQLLegacyItemPersistenceMapper),
+              container.get<Logger>(TYPES.Sync_Logger),
+            ),
       )
     container
       .bind<ItemRepositoryResolverInterface>(TYPES.Sync_ItemRepositoryResolver)
       .toConstantValue(
         new TypeORMItemRepositoryResolver(
-          container.get<ItemRepositoryInterface>(TYPES.Sync_SQLLegacyItemRepository),
+          container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
           isSecondaryDatabaseEnabled ? container.get<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository) : null,
         ),
       )
@@ -777,7 +792,7 @@ export class ContainerConfigLoader {
       )
       .toConstantValue(
         new TransitionItemsFromPrimaryToSecondaryDatabaseForUser(
-          container.get<ItemRepositoryInterface>(TYPES.Sync_SQLLegacyItemRepository),
+          container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
           isSecondaryDatabaseEnabled ? container.get<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository) : null,
           container.get<TimerInterface>(TYPES.Sync_Timer),
           container.get<Logger>(TYPES.Sync_Logger),
@@ -843,7 +858,7 @@ export class ContainerConfigLoader {
       .bind<DuplicateItemSyncedEventHandler>(TYPES.Sync_DuplicateItemSyncedEventHandler)
       .toConstantValue(
         new DuplicateItemSyncedEventHandler(
-          container.get<ItemRepositoryInterface>(TYPES.Sync_SQLLegacyItemRepository),
+          container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
           isSecondaryDatabaseEnabled ? container.get<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository) : null,
           container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),
           container.get<DomainEventPublisherInterface>(TYPES.Sync_DomainEventPublisher),
@@ -854,7 +869,7 @@ export class ContainerConfigLoader {
       .bind<AccountDeletionRequestedEventHandler>(TYPES.Sync_AccountDeletionRequestedEventHandler)
       .toConstantValue(
         new AccountDeletionRequestedEventHandler(
-          container.get<ItemRepositoryInterface>(TYPES.Sync_SQLLegacyItemRepository),
+          container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
           isSecondaryDatabaseEnabled ? container.get<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository) : null,
           container.get<Logger>(TYPES.Sync_Logger),
         ),
@@ -863,7 +878,7 @@ export class ContainerConfigLoader {
       .bind<ItemRevisionCreationRequestedEventHandler>(TYPES.Sync_ItemRevisionCreationRequestedEventHandler)
       .toConstantValue(
         new ItemRevisionCreationRequestedEventHandler(
-          container.get<ItemRepositoryInterface>(TYPES.Sync_SQLLegacyItemRepository),
+          container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
           isSecondaryDatabaseEnabled ? container.get<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository) : null,
           container.get<ItemBackupServiceInterface>(TYPES.Sync_ItemBackupService),
           container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),
@@ -918,7 +933,7 @@ export class ContainerConfigLoader {
       .toConstantValue(
         new ExtensionsHttpService(
           container.get<AxiosInstance>(TYPES.Sync_HTTPClient),
-          container.get<ItemRepositoryInterface>(TYPES.Sync_SQLLegacyItemRepository),
+          container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
           isSecondaryDatabaseEnabled ? container.get<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository) : null,
           container.get<ContentDecoderInterface>(TYPES.Sync_ContentDecoder),
           container.get<DomainEventPublisherInterface>(TYPES.Sync_DomainEventPublisher),
@@ -964,7 +979,7 @@ export class ContainerConfigLoader {
         .bind<EmailBackupRequestedEventHandler>(TYPES.Sync_EmailBackupRequestedEventHandler)
         .toConstantValue(
           new EmailBackupRequestedEventHandler(
-            container.get<ItemRepositoryInterface>(TYPES.Sync_SQLLegacyItemRepository),
+            container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
             isSecondaryDatabaseEnabled
               ? container.get<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository)
               : null,

+ 10 - 2
packages/syncing-server/src/Bootstrap/DataSource.ts

@@ -9,6 +9,7 @@ import { TypeORMSharedVaultUser } from '../Infra/TypeORM/TypeORMSharedVaultUser'
 import { TypeORMSharedVaultInvite } from '../Infra/TypeORM/TypeORMSharedVaultInvite'
 import { TypeORMMessage } from '../Infra/TypeORM/TypeORMMessage'
 import { MongoDBItem } from '../Infra/TypeORM/MongoDBItem'
+import { SQLItem } from '../Infra/TypeORM/SQLItem'
 
 export class AppDataSource {
   private _dataSource: DataSource | undefined
@@ -67,22 +68,29 @@ export class AppDataSource {
     this.env.load()
 
     const isConfiguredForMySQL = this.env.get('DB_TYPE') === 'mysql'
+    const isConfiguredForHomeServer = this.env.get('MODE', true) === 'home-server'
 
     const maxQueryExecutionTime = this.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
       ? +this.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
       : 45_000
 
+    const migrationsSourceDirectoryName = isConfiguredForMySQL
+      ? isConfiguredForHomeServer
+        ? 'mysql'
+        : 'mysql-legacy'
+      : 'sqlite'
+
     const commonDataSourceOptions = {
       maxQueryExecutionTime,
       entities: [
-        SQLLegacyItem,
+        isConfiguredForHomeServer ? SQLItem : SQLLegacyItem,
         TypeORMNotification,
         TypeORMSharedVault,
         TypeORMSharedVaultUser,
         TypeORMSharedVaultInvite,
         TypeORMMessage,
       ],
-      migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
+      migrations: [`${__dirname}/../../migrations/${migrationsSourceDirectoryName}/*.js`],
       migrationsRun: true,
       logging: <LoggerOptions>this.env.get('DB_DEBUG_LEVEL', true) ?? 'info',
     }

+ 3 - 1
packages/syncing-server/src/Bootstrap/Types.ts

@@ -8,7 +8,7 @@ const TYPES = {
   Sync_Env: Symbol.for('Sync_Env'),
   // Repositories
   Sync_ItemRepositoryResolver: Symbol.for('Sync_ItemRepositoryResolver'),
-  Sync_SQLLegacyItemRepository: Symbol.for('Sync_SQLLegacyItemRepository'),
+  Sync_SQLItemRepository: Symbol.for('Sync_SQLItemRepository'),
   Sync_MongoDBItemRepository: Symbol.for('Sync_MongoDBItemRepository'),
   Sync_SharedVaultRepository: Symbol.for('Sync_SharedVaultRepository'),
   Sync_SharedVaultInviteRepository: Symbol.for('Sync_SharedVaultInviteRepository'),
@@ -16,6 +16,7 @@ const TYPES = {
   Sync_NotificationRepository: Symbol.for('Sync_NotificationRepository'),
   Sync_MessageRepository: Symbol.for('Sync_MessageRepository'),
   // ORM
+  Sync_ORMItemRepository: Symbol.for('Sync_ORMItemRepository'),
   Sync_ORMLegacyItemRepository: Symbol.for('Sync_ORMLegacyItemRepository'),
   Sync_ORMSharedVaultRepository: Symbol.for('Sync_ORMSharedVaultRepository'),
   Sync_ORMSharedVaultInviteRepository: Symbol.for('Sync_ORMSharedVaultInviteRepository'),
@@ -132,6 +133,7 @@ const TYPES = {
   Sync_MessageHttpMapper: Symbol.for('Sync_MessageHttpMapper'),
   Sync_NotificationHttpMapper: Symbol.for('Sync_NotificationHttpMapper'),
   Sync_SQLLegacyItemPersistenceMapper: Symbol.for('Sync_SQLLegacyItemPersistenceMapper'),
+  Sync_SQLItemPersistenceMapper: Symbol.for('Sync_SQLItemPersistenceMapper'),
   Sync_MongoDBItemPersistenceMapper: Symbol.for('Sync_MongoDBItemPersistenceMapper'),
   Sync_ItemHttpMapper: Symbol.for('Sync_ItemHttpMapper'),
   Sync_ItemHashHttpMapper: Symbol.for('Sync_ItemHashHttpMapper'),

+ 1 - 0
packages/syncing-server/src/Infra/TypeORM/MongoDBItem.ts

@@ -58,6 +58,7 @@ export class MongoDBItem {
   declare lastEditedBy: string | null
 
   @Column()
+  @Index('index_items_on_shared_vault_uuid')
   declare sharedVaultUuid: string | null
 
   @Column()

+ 3 - 1
packages/syncing-server/src/Infra/TypeORM/SQLItem.ts

@@ -1,4 +1,5 @@
-import { Column, Entity } from 'typeorm'
+import { Column, Entity, Index } from 'typeorm'
+
 import { SQLLegacyItem } from './SQLLegacyItem'
 
 @Entity({ name: 'items' })
@@ -17,6 +18,7 @@ export class SQLItem extends SQLLegacyItem {
     length: 36,
     nullable: true,
   })
+  @Index('index_items_on_shared_vault_uuid')
   declare sharedVaultUuid: string | null
 
   @Column({

+ 3 - 3
packages/syncing-server/src/Infra/TypeORM/TypeORMItemRepositoryResolver.ts

@@ -5,13 +5,13 @@ import { ItemRepositoryResolverInterface } from '../../Domain/Item/ItemRepositor
 
 export class TypeORMItemRepositoryResolver implements ItemRepositoryResolverInterface {
   constructor(
-    private SQLItemRepository: ItemRepositoryInterface,
+    private sqlItemRepository: ItemRepositoryInterface,
     private mongoDbItemRepository: ItemRepositoryInterface | null,
   ) {}
 
   resolve(roleNames: RoleNameCollection): ItemRepositoryInterface {
     if (!this.mongoDbItemRepository) {
-      return this.SQLItemRepository
+      return this.sqlItemRepository
     }
 
     const transitionRoleName = RoleName.create(RoleName.NAMES.TransitionUser).getValue()
@@ -20,6 +20,6 @@ export class TypeORMItemRepositoryResolver implements ItemRepositoryResolverInte
       return this.mongoDbItemRepository
     }
 
-    return this.SQLItemRepository
+    return this.sqlItemRepository
   }
 }