fix: merge mysql and mysql-legacy together (#883)
This commit is contained in:
parent
d570146378
commit
e19f7a7b7f
51 changed files with 512 additions and 1584 deletions
|
@ -1,41 +0,0 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class init1669113322388 implements MigrationInterface {
|
||||
name = 'init1669113322388'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await this.syncSchemaBetweenLegacyRevisions(queryRunner)
|
||||
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE IF NOT EXISTS `revisions` (`uuid` varchar(36) NOT NULL, `item_uuid` varchar(36) NOT NULL, `user_uuid` varchar(36) NOT 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 `item_uuid` (`item_uuid`), INDEX `user_uuid` (`user_uuid`), INDEX `creation_date` (`creation_date`), INDEX `created_at` (`created_at`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX `created_at` ON `revisions`')
|
||||
await queryRunner.query('DROP INDEX `creation_date` ON `revisions`')
|
||||
await queryRunner.query('DROP INDEX `user_uuid` ON `revisions`')
|
||||
await queryRunner.query('DROP INDEX `item_uuid` ON `revisions`')
|
||||
await queryRunner.query('DROP TABLE `revisions`')
|
||||
}
|
||||
|
||||
private async syncSchemaBetweenLegacyRevisions(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) {
|
||||
return
|
||||
}
|
||||
|
||||
const revisionsTableHasUserUuidColumnQueryResult = await queryRunner.manager.query(
|
||||
'SELECT COUNT(*) as count FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = "revisions" AND column_name = "user_uuid"',
|
||||
)
|
||||
const revisionsTableHasUserUuidColumn = revisionsTableHasUserUuidColumnQueryResult[0].count === 1
|
||||
if (revisionsTableHasUserUuidColumn) {
|
||||
return
|
||||
}
|
||||
|
||||
await queryRunner.query('ALTER TABLE `revisions` ADD COLUMN `user_uuid` varchar(36) NULL')
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class removeDateIndexes1669636497932 implements MigrationInterface {
|
||||
name = 'removeDateIndexes1669636497932'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const indexRevisionsOnCreatedAt = await queryRunner.manager.query(
|
||||
'SHOW INDEX FROM `revisions` where `key_name` = "created_at"',
|
||||
)
|
||||
const indexRevisionsOnCreatedAtExist = indexRevisionsOnCreatedAt && indexRevisionsOnCreatedAt.length > 0
|
||||
if (indexRevisionsOnCreatedAtExist) {
|
||||
await queryRunner.query('DROP INDEX `created_at` ON `revisions`')
|
||||
}
|
||||
|
||||
const indexRevisionsOnCreationDate = await queryRunner.manager.query(
|
||||
'SHOW INDEX FROM `revisions` where `key_name` = "creation_date"',
|
||||
)
|
||||
const indexRevisionsOnCreationDateAtExist = indexRevisionsOnCreationDate && indexRevisionsOnCreationDate.length > 0
|
||||
if (indexRevisionsOnCreationDateAtExist) {
|
||||
await queryRunner.query('DROP INDEX `creation_date` ON `revisions`')
|
||||
}
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('CREATE INDEX `creation_date` ON `revisions` (`creation_date`)')
|
||||
await queryRunner.query('CREATE INDEX `created_at` ON `revisions` (`created_at`)')
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class makeUserUuidNullable1669735585016 implements MigrationInterface {
|
||||
name = 'makeUserUuidNullable1669735585016'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `revisions` CHANGE `user_uuid` `user_uuid` varchar(36) NULL')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `revisions` CHANGE `user_uuid` `user_uuid` varchar(36) NOT NULL')
|
||||
}
|
||||
}
|
|
@ -34,7 +34,7 @@ export class removeDateIndexes1669636497932 implements MigrationInterface {
|
|||
)
|
||||
const revisionsTableExists = revisionsTableExistsQueryResult[0].count === 1
|
||||
if (revisionsTableExists) {
|
||||
await queryRunner.query('RENAME TABLE `revisions` TO `revisions_revisions`')
|
||||
await queryRunner.query('ALTER TABLE `revisions` RENAME TO `revisions_revisions`, ALGORITHM=INSTANT')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ export class makeUserUuidNullable1669735585016 implements MigrationInterface {
|
|||
)
|
||||
const revisionsTableExists = revisionsTableExistsQueryResult[0].count === 1
|
||||
if (revisionsTableExists) {
|
||||
await queryRunner.query('RENAME TABLE `revisions` TO `revisions_revisions`')
|
||||
await queryRunner.query('ALTER TABLE `revisions` RENAME TO `revisions_revisions`, ALGORITHM=INSTANT')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,13 @@ export class AddSharedVaultInformation1693915383950 implements MigrationInterfac
|
|||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await this.renameRevisionsTable(queryRunner)
|
||||
|
||||
await queryRunner.query('ALTER TABLE `revisions_revisions` ADD `edited_by` varchar(36) NULL')
|
||||
await queryRunner.query('ALTER TABLE `revisions_revisions` ADD `shared_vault_uuid` varchar(36) NULL')
|
||||
await queryRunner.query('ALTER TABLE `revisions_revisions` ADD `key_system_identifier` varchar(36) NULL')
|
||||
await queryRunner.query('ALTER TABLE `revisions_revisions` ADD `edited_by` varchar(36) NULL, ALGORITHM = INSTANT')
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE `revisions_revisions` ADD `shared_vault_uuid` varchar(36) NULL, ALGORITHM = INSTANT',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE `revisions_revisions` ADD `key_system_identifier` varchar(36) NULL, ALGORITHM = INSTANT',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'CREATE INDEX `index_revisions_on_shared_vault_uuid` ON `revisions_revisions` (`shared_vault_uuid`)',
|
||||
)
|
||||
|
@ -25,7 +29,7 @@ export class AddSharedVaultInformation1693915383950 implements MigrationInterfac
|
|||
)
|
||||
const revisionsTableExists = revisionsTableExistsQueryResult[0].count === 1
|
||||
if (revisionsTableExists) {
|
||||
await queryRunner.query('RENAME TABLE `revisions` TO `revisions_revisions`')
|
||||
await queryRunner.query('ALTER TABLE `revisions` RENAME TO `revisions_revisions`, ALGORITHM=INSTANT')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ export class RenameRevisionsTable1694425333972 implements MigrationInterface {
|
|||
)
|
||||
const revisionsTableExists = revisionsTableExistsQueryResult[0].count === 1
|
||||
if (revisionsTableExists) {
|
||||
await queryRunner.query('RENAME TABLE `revisions` TO `revisions_revisions`')
|
||||
await queryRunner.query('ALTER TABLE `revisions` RENAME TO `revisions_revisions`, ALGORITHM=INSTANT')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,8 +11,6 @@ import * as winston from 'winston'
|
|||
import { Revision } from '../Domain/Revision/Revision'
|
||||
import { RevisionMetadata } from '../Domain/Revision/RevisionMetadata'
|
||||
import { RevisionRepositoryInterface } from '../Domain/Revision/RevisionRepositoryInterface'
|
||||
import { SQLLegacyRevisionRepository } from '../Infra/TypeORM/SQL/SQLLegacyRevisionRepository'
|
||||
import { SQLLegacyRevision } from '../Infra/TypeORM/SQL/SQLLegacyRevision'
|
||||
import { AppDataSource } from './DataSource'
|
||||
import { Env } from './Env'
|
||||
import TYPES from './Types'
|
||||
|
@ -48,8 +46,6 @@ import { S3DumpRepository } from '../Infra/S3/S3ItemDumpRepository'
|
|||
import { RevisionItemStringMapper } from '../Mapping/Backup/RevisionItemStringMapper'
|
||||
import { BaseRevisionsController } from '../Infra/InversifyExpress/Base/BaseRevisionsController'
|
||||
import { Transform } from 'stream'
|
||||
import { SQLLegacyRevisionMetadataPersistenceMapper } from '../Mapping/Persistence/SQL/SQLLegacyRevisionMetadataPersistenceMapper'
|
||||
import { SQLLegacyRevisionPersistenceMapper } from '../Mapping/Persistence/SQL/SQLLegacyRevisionPersistenceMapper'
|
||||
import { RevisionHttpMapper } from '../Mapping/Http/RevisionHttpMapper'
|
||||
import { RevisionMetadataHttpRepresentation } from '../Mapping/Http/RevisionMetadataHttpRepresentation'
|
||||
import { RevisionHttpRepresentation } from '../Mapping/Http/RevisionHttpRepresentation'
|
||||
|
@ -160,25 +156,14 @@ export class ContainerConfigLoader {
|
|||
}
|
||||
|
||||
// Map
|
||||
container
|
||||
.bind<MapperInterface<RevisionMetadata, SQLLegacyRevision>>(
|
||||
TYPES.Revisions_SQLLegacyRevisionMetadataPersistenceMapper,
|
||||
)
|
||||
.toConstantValue(new SQLLegacyRevisionMetadataPersistenceMapper())
|
||||
container
|
||||
.bind<MapperInterface<RevisionMetadata, SQLRevision>>(TYPES.Revisions_SQLRevisionMetadataPersistenceMapper)
|
||||
.toConstantValue(new SQLRevisionMetadataPersistenceMapper())
|
||||
container
|
||||
.bind<MapperInterface<Revision, SQLLegacyRevision>>(TYPES.Revisions_SQLLegacyRevisionPersistenceMapper)
|
||||
.toConstantValue(new SQLLegacyRevisionPersistenceMapper(container.get<TimerInterface>(TYPES.Revisions_Timer)))
|
||||
container
|
||||
.bind<MapperInterface<Revision, SQLRevision>>(TYPES.Revisions_SQLRevisionPersistenceMapper)
|
||||
.toConstantValue(new SQLRevisionPersistenceMapper(container.get<TimerInterface>(TYPES.Revisions_Timer)))
|
||||
|
||||
// ORM
|
||||
container
|
||||
.bind<Repository<SQLLegacyRevision>>(TYPES.Revisions_ORMLegacyRevisionRepository)
|
||||
.toDynamicValue(() => appDataSource.getRepository(SQLLegacyRevision))
|
||||
container
|
||||
.bind<Repository<SQLRevision>>(TYPES.Revisions_ORMRevisionRepository)
|
||||
.toConstantValue(appDataSource.getRepository(SQLRevision))
|
||||
|
@ -187,25 +172,14 @@ export class ContainerConfigLoader {
|
|||
container
|
||||
.bind<RevisionRepositoryInterface>(TYPES.Revisions_SQLRevisionRepository)
|
||||
.toConstantValue(
|
||||
isConfiguredForHomeServerOrSelfHosting
|
||||
? new SQLRevisionRepository(
|
||||
container.get<Repository<SQLRevision>>(TYPES.Revisions_ORMRevisionRepository),
|
||||
container.get<MapperInterface<RevisionMetadata, SQLRevision>>(
|
||||
TYPES.Revisions_SQLRevisionMetadataPersistenceMapper,
|
||||
),
|
||||
container.get<MapperInterface<Revision, SQLRevision>>(TYPES.Revisions_SQLRevisionPersistenceMapper),
|
||||
container.get<winston.Logger>(TYPES.Revisions_Logger),
|
||||
)
|
||||
: new SQLLegacyRevisionRepository(
|
||||
container.get<Repository<SQLLegacyRevision>>(TYPES.Revisions_ORMLegacyRevisionRepository),
|
||||
container.get<MapperInterface<RevisionMetadata, SQLLegacyRevision>>(
|
||||
TYPES.Revisions_SQLLegacyRevisionMetadataPersistenceMapper,
|
||||
),
|
||||
container.get<MapperInterface<Revision, SQLLegacyRevision>>(
|
||||
TYPES.Revisions_SQLLegacyRevisionPersistenceMapper,
|
||||
),
|
||||
container.get<winston.Logger>(TYPES.Revisions_Logger),
|
||||
),
|
||||
new SQLRevisionRepository(
|
||||
container.get<Repository<SQLRevision>>(TYPES.Revisions_ORMRevisionRepository),
|
||||
container.get<MapperInterface<RevisionMetadata, SQLRevision>>(
|
||||
TYPES.Revisions_SQLRevisionMetadataPersistenceMapper,
|
||||
),
|
||||
container.get<MapperInterface<Revision, SQLRevision>>(TYPES.Revisions_SQLRevisionPersistenceMapper),
|
||||
container.get<winston.Logger>(TYPES.Revisions_Logger),
|
||||
),
|
||||
)
|
||||
|
||||
container
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { DataSource, EntityTarget, LoggerOptions, ObjectLiteral, Repository } from 'typeorm'
|
||||
import { MysqlConnectionOptions } from 'typeorm/driver/mysql/MysqlConnectionOptions'
|
||||
|
||||
import { SQLLegacyRevision } from '../Infra/TypeORM/SQL/SQLLegacyRevision'
|
||||
|
||||
import { Env } from './Env'
|
||||
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
|
||||
import { SQLRevision } from '../Infra/TypeORM/SQL/SQLRevision'
|
||||
|
@ -34,23 +32,15 @@ export class AppDataSource {
|
|||
|
||||
const isConfiguredForMySQL = this.configuration.env.get('DB_TYPE') === 'mysql'
|
||||
|
||||
const isConfiguredForHomeServerOrSelfHosting =
|
||||
this.configuration.env.get('MODE', true) === 'home-server' ||
|
||||
this.configuration.env.get('MODE', true) === 'self-hosted'
|
||||
|
||||
const maxQueryExecutionTime = this.configuration.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
|
||||
? +this.configuration.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
|
||||
: 45_000
|
||||
|
||||
const migrationsSourceDirectoryName = isConfiguredForMySQL
|
||||
? isConfiguredForHomeServerOrSelfHosting
|
||||
? 'mysql'
|
||||
: 'mysql-legacy'
|
||||
: 'sqlite'
|
||||
const migrationsSourceDirectoryName = isConfiguredForMySQL ? 'mysql' : 'sqlite'
|
||||
|
||||
const commonDataSourceOptions = {
|
||||
maxQueryExecutionTime,
|
||||
entities: [isConfiguredForHomeServerOrSelfHosting ? SQLRevision : SQLLegacyRevision],
|
||||
entities: [SQLRevision],
|
||||
migrations: [`${__dirname}/../../migrations/${migrationsSourceDirectoryName}/*.js`],
|
||||
migrationsRun: this.configuration.runMigrations,
|
||||
logging: <LoggerOptions>this.configuration.env.get('DB_DEBUG_LEVEL', true) ?? 'info',
|
||||
|
|
|
@ -7,18 +7,13 @@ const TYPES = {
|
|||
Revisions_S3: Symbol.for('Revisions_S3'),
|
||||
Revisions_Env: Symbol.for('Revisions_Env'),
|
||||
// Map
|
||||
Revisions_SQLLegacyRevisionMetadataPersistenceMapper: Symbol.for(
|
||||
'Revisions_SQLLegacyRevisionMetadataPersistenceMapper',
|
||||
),
|
||||
Revisions_SQLRevisionMetadataPersistenceMapper: Symbol.for('Revisions_SQLRevisionMetadataPersistenceMapper'),
|
||||
Revisions_SQLLegacyRevisionPersistenceMapper: Symbol.for('Revisions_SQLLegacyRevisionPersistenceMapper'),
|
||||
Revisions_SQLRevisionPersistenceMapper: Symbol.for('Revisions_SQLRevisionPersistenceMapper'),
|
||||
Revisions_RevisionItemStringMapper: Symbol.for('Revisions_RevisionItemStringMapper'),
|
||||
Revisions_RevisionHttpMapper: Symbol.for('Revisions_RevisionHttpMapper'),
|
||||
Revisions_RevisionMetadataHttpMapper: Symbol.for('Revisions_RevisionMetadataHttpMapper'),
|
||||
// ORM
|
||||
Revisions_ORMRevisionRepository: Symbol.for('Revisions_ORMRevisionRepository'),
|
||||
Revisions_ORMLegacyRevisionRepository: Symbol.for('Revisions_ORMLegacyRevisionRepository'),
|
||||
// Repositories
|
||||
Revisions_SQLRevisionRepository: Symbol.for('Revisions_SQLRevisionRepository'),
|
||||
Revisions_DumpRepository: Symbol.for('Revisions_DumpRepository'),
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
|
||||
|
||||
@Entity({ name: 'revisions' })
|
||||
export class SQLLegacyRevision {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
declare uuid: string
|
||||
|
||||
@Column({
|
||||
name: 'item_uuid',
|
||||
length: 36,
|
||||
})
|
||||
@Index('item_uuid')
|
||||
declare itemUuid: string
|
||||
|
||||
@Column({
|
||||
name: 'user_uuid',
|
||||
length: 36,
|
||||
type: 'varchar',
|
||||
nullable: true,
|
||||
})
|
||||
@Index('user_uuid')
|
||||
declare userUuid: string | null
|
||||
|
||||
@Column({
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
})
|
||||
declare content: string | null
|
||||
|
||||
@Column({
|
||||
name: 'content_type',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare contentType: string | null
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
name: 'items_key_id',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare itemsKeyId: string | null
|
||||
|
||||
@Column({
|
||||
name: 'enc_item_key',
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
})
|
||||
declare encItemKey: string | null
|
||||
|
||||
@Column({
|
||||
name: 'auth_hash',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare authHash: string | null
|
||||
|
||||
@Column({
|
||||
name: 'creation_date',
|
||||
type: 'date',
|
||||
nullable: true,
|
||||
})
|
||||
declare creationDate: Date
|
||||
|
||||
@Column({
|
||||
name: 'created_at',
|
||||
type: 'datetime',
|
||||
precision: 6,
|
||||
nullable: true,
|
||||
})
|
||||
declare createdAt: Date
|
||||
|
||||
@Column({
|
||||
name: 'updated_at',
|
||||
type: 'datetime',
|
||||
precision: 6,
|
||||
nullable: true,
|
||||
})
|
||||
declare updatedAt: Date
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
import { MapperInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { Repository } from 'typeorm'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { Revision } from '../../../Domain/Revision/Revision'
|
||||
import { RevisionMetadata } from '../../../Domain/Revision/RevisionMetadata'
|
||||
import { RevisionRepositoryInterface } from '../../../Domain/Revision/RevisionRepositoryInterface'
|
||||
import { SQLLegacyRevision } from './SQLLegacyRevision'
|
||||
|
||||
export class SQLLegacyRevisionRepository implements RevisionRepositoryInterface {
|
||||
constructor(
|
||||
protected ormRepository: Repository<SQLLegacyRevision>,
|
||||
protected revisionMetadataMapper: MapperInterface<RevisionMetadata, SQLLegacyRevision>,
|
||||
protected revisionMapper: MapperInterface<Revision, SQLLegacyRevision>,
|
||||
protected logger: Logger,
|
||||
) {}
|
||||
|
||||
async clearSharedVaultAndKeySystemAssociations(_dto: { itemUuid?: Uuid; sharedVaultUuid: Uuid }): Promise<void> {
|
||||
this.logger.error('Method clearSharedVaultAndKeySystemAssociations not implemented.')
|
||||
}
|
||||
|
||||
async countByUserUuid(userUuid: Uuid): Promise<number> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.where('user_uuid = :userUuid', { userUuid: userUuid.value })
|
||||
.getCount()
|
||||
}
|
||||
|
||||
async findByUserUuid(dto: { userUuid: Uuid; offset?: number; limit?: number }): Promise<Revision[]> {
|
||||
const queryBuilder = this.ormRepository
|
||||
.createQueryBuilder('revision')
|
||||
.where('revision.user_uuid = :userUuid', { userUuid: dto.userUuid.value })
|
||||
.orderBy('revision.created_at', 'ASC')
|
||||
|
||||
if (dto.offset !== undefined) {
|
||||
queryBuilder.skip(dto.offset)
|
||||
}
|
||||
|
||||
if (dto.limit !== undefined) {
|
||||
queryBuilder.take(dto.limit)
|
||||
}
|
||||
|
||||
const sqlRevisions = await queryBuilder.getMany()
|
||||
|
||||
const revisions = []
|
||||
for (const sqlRevision of sqlRevisions) {
|
||||
revisions.push(this.revisionMapper.toDomain(sqlRevision))
|
||||
}
|
||||
|
||||
return revisions
|
||||
}
|
||||
|
||||
async updateUserUuid(itemUuid: Uuid, userUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.update()
|
||||
.set({
|
||||
userUuid: userUuid.value,
|
||||
})
|
||||
.where('item_uuid = :itemUuid', { itemUuid: itemUuid.value })
|
||||
.execute()
|
||||
}
|
||||
|
||||
async findByItemUuid(itemUuid: Uuid): Promise<Revision[]> {
|
||||
const SQLLegacyRevisions = await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.where('item_uuid = :itemUuid', { itemUuid: itemUuid.value })
|
||||
.getMany()
|
||||
|
||||
const revisions = []
|
||||
for (const revision of SQLLegacyRevisions) {
|
||||
revisions.push(this.revisionMapper.toDomain(revision))
|
||||
}
|
||||
|
||||
return revisions
|
||||
}
|
||||
|
||||
async removeByUserUuid(userUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.delete()
|
||||
.from('revisions')
|
||||
.where('user_uuid = :userUuid', { userUuid: userUuid.value })
|
||||
.execute()
|
||||
}
|
||||
|
||||
async removeOneByUuid(revisionUuid: Uuid, userUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.delete()
|
||||
.from('revisions')
|
||||
.where('uuid = :revisionUuid AND user_uuid = :userUuid', {
|
||||
userUuid: userUuid.value,
|
||||
revisionUuid: revisionUuid.value,
|
||||
})
|
||||
.execute()
|
||||
}
|
||||
|
||||
async findOneByUuid(revisionUuid: Uuid, userUuid: Uuid, _sharedVaultUuids: Uuid[]): Promise<Revision | null> {
|
||||
const SQLLegacyRevision = await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.where('uuid = :revisionUuid', { revisionUuid: revisionUuid.value })
|
||||
.andWhere('user_uuid = :userUuid', { userUuid: userUuid.value })
|
||||
.getOne()
|
||||
|
||||
if (SQLLegacyRevision === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.revisionMapper.toDomain(SQLLegacyRevision)
|
||||
}
|
||||
|
||||
async insert(revision: Revision): Promise<boolean> {
|
||||
const projection = this.revisionMapper.toProjection(revision)
|
||||
|
||||
await this.ormRepository.insert(projection)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
async update(revision: Revision): Promise<boolean> {
|
||||
const projection = this.revisionMapper.toProjection(revision)
|
||||
|
||||
const { uuid, ...rest } = projection
|
||||
|
||||
await this.ormRepository.update({ uuid: uuid }, rest)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
async findMetadataByItemId(
|
||||
itemUuid: Uuid,
|
||||
userUuid: Uuid,
|
||||
_sharedVaultUuids: Uuid[],
|
||||
): Promise<Array<RevisionMetadata>> {
|
||||
const queryBuilder = this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.select('uuid', 'uuid')
|
||||
.addSelect('content_type', 'contentType')
|
||||
.addSelect('created_at', 'createdAt')
|
||||
.addSelect('updated_at', 'updatedAt')
|
||||
.addSelect('item_uuid', 'itemUuid')
|
||||
.where('item_uuid = :itemUuid', { itemUuid: itemUuid.value })
|
||||
.andWhere('user_uuid = :userUuid', { userUuid: userUuid.value })
|
||||
.orderBy('created_at', 'DESC')
|
||||
|
||||
const simplifiedRevisions = await queryBuilder.getRawMany()
|
||||
|
||||
this.logger.debug(
|
||||
`Found ${simplifiedRevisions.length} revisions entries for item ${itemUuid.value}`,
|
||||
simplifiedRevisions,
|
||||
)
|
||||
|
||||
const metadata = []
|
||||
for (const simplifiedRevision of simplifiedRevisions) {
|
||||
metadata.push(this.revisionMetadataMapper.toDomain(simplifiedRevision))
|
||||
}
|
||||
|
||||
return metadata
|
||||
}
|
||||
}
|
|
@ -1,9 +1,86 @@
|
|||
import { Column, Entity, Index } from 'typeorm'
|
||||
|
||||
import { SQLLegacyRevision } from './SQLLegacyRevision'
|
||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
|
||||
|
||||
@Entity({ name: 'revisions_revisions' })
|
||||
export class SQLRevision extends SQLLegacyRevision {
|
||||
export class SQLRevision {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
declare uuid: string
|
||||
|
||||
@Column({
|
||||
name: 'item_uuid',
|
||||
length: 36,
|
||||
})
|
||||
@Index('item_uuid')
|
||||
declare itemUuid: string
|
||||
|
||||
@Column({
|
||||
name: 'user_uuid',
|
||||
length: 36,
|
||||
type: 'varchar',
|
||||
nullable: true,
|
||||
})
|
||||
@Index('user_uuid')
|
||||
declare userUuid: string | null
|
||||
|
||||
@Column({
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
})
|
||||
declare content: string | null
|
||||
|
||||
@Column({
|
||||
name: 'content_type',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare contentType: string | null
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
name: 'items_key_id',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare itemsKeyId: string | null
|
||||
|
||||
@Column({
|
||||
name: 'enc_item_key',
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
})
|
||||
declare encItemKey: string | null
|
||||
|
||||
@Column({
|
||||
name: 'auth_hash',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare authHash: string | null
|
||||
|
||||
@Column({
|
||||
name: 'creation_date',
|
||||
type: 'date',
|
||||
nullable: true,
|
||||
})
|
||||
declare creationDate: Date
|
||||
|
||||
@Column({
|
||||
name: 'created_at',
|
||||
type: 'datetime',
|
||||
precision: 6,
|
||||
nullable: true,
|
||||
})
|
||||
declare createdAt: Date
|
||||
|
||||
@Column({
|
||||
name: 'updated_at',
|
||||
type: 'datetime',
|
||||
precision: 6,
|
||||
nullable: true,
|
||||
})
|
||||
declare updatedAt: Date
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
name: 'edited_by',
|
||||
|
|
|
@ -4,20 +4,18 @@ import { Logger } from 'winston'
|
|||
|
||||
import { Revision } from '../../../Domain/Revision/Revision'
|
||||
import { RevisionMetadata } from '../../../Domain/Revision/RevisionMetadata'
|
||||
import { SQLLegacyRevisionRepository } from './SQLLegacyRevisionRepository'
|
||||
import { RevisionRepositoryInterface } from '../../../Domain/Revision/RevisionRepositoryInterface'
|
||||
import { SQLRevision } from './SQLRevision'
|
||||
|
||||
export class SQLRevisionRepository extends SQLLegacyRevisionRepository {
|
||||
export class SQLRevisionRepository implements RevisionRepositoryInterface {
|
||||
constructor(
|
||||
protected override ormRepository: Repository<SQLRevision>,
|
||||
protected override revisionMetadataMapper: MapperInterface<RevisionMetadata, SQLRevision>,
|
||||
protected override revisionMapper: MapperInterface<Revision, SQLRevision>,
|
||||
protected override logger: Logger,
|
||||
) {
|
||||
super(ormRepository, revisionMetadataMapper, revisionMapper, logger)
|
||||
}
|
||||
protected ormRepository: Repository<SQLRevision>,
|
||||
protected revisionMetadataMapper: MapperInterface<RevisionMetadata, SQLRevision>,
|
||||
protected revisionMapper: MapperInterface<Revision, SQLRevision>,
|
||||
protected logger: Logger,
|
||||
) {}
|
||||
|
||||
override async removeByUserUuid(userUuid: Uuid): Promise<void> {
|
||||
async removeByUserUuid(userUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.delete()
|
||||
|
@ -26,7 +24,7 @@ export class SQLRevisionRepository extends SQLLegacyRevisionRepository {
|
|||
.execute()
|
||||
}
|
||||
|
||||
override async removeOneByUuid(revisionUuid: Uuid, userUuid: Uuid): Promise<void> {
|
||||
async removeOneByUuid(revisionUuid: Uuid, userUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.delete()
|
||||
|
@ -38,7 +36,7 @@ export class SQLRevisionRepository extends SQLLegacyRevisionRepository {
|
|||
.execute()
|
||||
}
|
||||
|
||||
override async findOneByUuid(revisionUuid: Uuid, userUuid: Uuid, sharedVaultUuids: Uuid[]): Promise<Revision | null> {
|
||||
async findOneByUuid(revisionUuid: Uuid, userUuid: Uuid, sharedVaultUuids: Uuid[]): Promise<Revision | null> {
|
||||
const queryBuilder = this.ormRepository.createQueryBuilder()
|
||||
|
||||
if (sharedVaultUuids.length > 0) {
|
||||
|
@ -66,10 +64,7 @@ export class SQLRevisionRepository extends SQLLegacyRevisionRepository {
|
|||
return this.revisionMapper.toDomain(sqlRevision)
|
||||
}
|
||||
|
||||
override async clearSharedVaultAndKeySystemAssociations(dto: {
|
||||
itemUuid?: Uuid
|
||||
sharedVaultUuid: Uuid
|
||||
}): Promise<void> {
|
||||
async clearSharedVaultAndKeySystemAssociations(dto: { itemUuid?: Uuid; sharedVaultUuid: Uuid }): Promise<void> {
|
||||
const queryBuilder = this.ormRepository.createQueryBuilder().update().set({
|
||||
sharedVaultUuid: null,
|
||||
keySystemIdentifier: null,
|
||||
|
@ -89,7 +84,7 @@ export class SQLRevisionRepository extends SQLLegacyRevisionRepository {
|
|||
await queryBuilder.execute()
|
||||
}
|
||||
|
||||
override async findMetadataByItemId(
|
||||
async findMetadataByItemId(
|
||||
itemUuid: Uuid,
|
||||
userUuid: Uuid,
|
||||
sharedVaultUuids: Uuid[],
|
||||
|
@ -134,4 +129,78 @@ export class SQLRevisionRepository extends SQLLegacyRevisionRepository {
|
|||
|
||||
return metadata
|
||||
}
|
||||
|
||||
async countByUserUuid(userUuid: Uuid): Promise<number> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.where('user_uuid = :userUuid', { userUuid: userUuid.value })
|
||||
.getCount()
|
||||
}
|
||||
|
||||
async findByUserUuid(dto: { userUuid: Uuid; offset?: number; limit?: number }): Promise<Revision[]> {
|
||||
const queryBuilder = this.ormRepository
|
||||
.createQueryBuilder('revision')
|
||||
.where('revision.user_uuid = :userUuid', { userUuid: dto.userUuid.value })
|
||||
.orderBy('revision.created_at', 'ASC')
|
||||
|
||||
if (dto.offset !== undefined) {
|
||||
queryBuilder.skip(dto.offset)
|
||||
}
|
||||
|
||||
if (dto.limit !== undefined) {
|
||||
queryBuilder.take(dto.limit)
|
||||
}
|
||||
|
||||
const sqlRevisions = await queryBuilder.getMany()
|
||||
|
||||
const revisions = []
|
||||
for (const sqlRevision of sqlRevisions) {
|
||||
revisions.push(this.revisionMapper.toDomain(sqlRevision))
|
||||
}
|
||||
|
||||
return revisions
|
||||
}
|
||||
|
||||
async updateUserUuid(itemUuid: Uuid, userUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.update()
|
||||
.set({
|
||||
userUuid: userUuid.value,
|
||||
})
|
||||
.where('item_uuid = :itemUuid', { itemUuid: itemUuid.value })
|
||||
.execute()
|
||||
}
|
||||
|
||||
async findByItemUuid(itemUuid: Uuid): Promise<Revision[]> {
|
||||
const SQLRevisions = await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.where('item_uuid = :itemUuid', { itemUuid: itemUuid.value })
|
||||
.getMany()
|
||||
|
||||
const revisions = []
|
||||
for (const revision of SQLRevisions) {
|
||||
revisions.push(this.revisionMapper.toDomain(revision))
|
||||
}
|
||||
|
||||
return revisions
|
||||
}
|
||||
|
||||
async insert(revision: Revision): Promise<boolean> {
|
||||
const projection = this.revisionMapper.toProjection(revision)
|
||||
|
||||
await this.ormRepository.insert(projection)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
async update(revision: Revision): Promise<boolean> {
|
||||
const projection = this.revisionMapper.toProjection(revision)
|
||||
|
||||
const { uuid, ...rest } = projection
|
||||
|
||||
await this.ormRepository.update({ uuid: uuid }, rest)
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
import { MapperInterface, Dates, UniqueEntityId, ContentType, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { RevisionMetadata } from '../../../Domain/Revision/RevisionMetadata'
|
||||
import { SQLLegacyRevision } from '../../../Infra/TypeORM/SQL/SQLLegacyRevision'
|
||||
|
||||
export class SQLLegacyRevisionMetadataPersistenceMapper
|
||||
implements MapperInterface<RevisionMetadata, SQLLegacyRevision>
|
||||
{
|
||||
toDomain(projection: SQLLegacyRevision): RevisionMetadata {
|
||||
const contentTypeOrError = ContentType.create(projection.contentType)
|
||||
if (contentTypeOrError.isFailed()) {
|
||||
throw new Error(`Could not create content type: ${contentTypeOrError.getError()}`)
|
||||
}
|
||||
const contentType = contentTypeOrError.getValue()
|
||||
|
||||
const createdAt = projection.createdAt instanceof Date ? projection.createdAt : new Date(projection.createdAt)
|
||||
const updatedAt = projection.updatedAt instanceof Date ? projection.updatedAt : new Date(projection.updatedAt)
|
||||
|
||||
const datesOrError = Dates.create(createdAt, updatedAt)
|
||||
if (datesOrError.isFailed()) {
|
||||
throw new Error(`Could not create dates: ${datesOrError.getError()}`)
|
||||
}
|
||||
const dates = datesOrError.getValue()
|
||||
|
||||
const itemUuidOrError = Uuid.create(projection.itemUuid)
|
||||
if (itemUuidOrError.isFailed()) {
|
||||
throw new Error(`Could not create item uuid: ${itemUuidOrError.getError()}`)
|
||||
}
|
||||
const itemUuid = itemUuidOrError.getValue()
|
||||
|
||||
const revisionMetadataOrError = RevisionMetadata.create(
|
||||
{
|
||||
contentType,
|
||||
dates,
|
||||
sharedVaultUuid: null,
|
||||
itemUuid,
|
||||
},
|
||||
new UniqueEntityId(projection.uuid),
|
||||
)
|
||||
|
||||
if (revisionMetadataOrError.isFailed()) {
|
||||
throw new Error(`Could not create revision metdata: ${revisionMetadataOrError.getError()}`)
|
||||
}
|
||||
|
||||
return revisionMetadataOrError.getValue()
|
||||
}
|
||||
|
||||
toProjection(_domain: RevisionMetadata): SQLLegacyRevision {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
import { MapperInterface, Dates, UniqueEntityId, Uuid, ContentType } from '@standardnotes/domain-core'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
import { Revision } from '../../../Domain/Revision/Revision'
|
||||
import { SQLLegacyRevision } from '../../../Infra/TypeORM/SQL/SQLLegacyRevision'
|
||||
|
||||
export class SQLLegacyRevisionPersistenceMapper implements MapperInterface<Revision, SQLLegacyRevision> {
|
||||
constructor(private timer: TimerInterface) {}
|
||||
|
||||
toDomain(projection: SQLLegacyRevision): Revision {
|
||||
const contentTypeOrError = ContentType.create(projection.contentType)
|
||||
if (contentTypeOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${contentTypeOrError.getError()}`)
|
||||
}
|
||||
const contentType = contentTypeOrError.getValue()
|
||||
|
||||
const datesOrError = Dates.create(projection.createdAt, projection.updatedAt)
|
||||
if (datesOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${datesOrError.getError()}`)
|
||||
}
|
||||
const dates = datesOrError.getValue()
|
||||
|
||||
const itemUuidOrError = Uuid.create(projection.itemUuid)
|
||||
if (itemUuidOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${itemUuidOrError.getError()}`)
|
||||
}
|
||||
const itemUuid = itemUuidOrError.getValue()
|
||||
|
||||
let userUuid = null
|
||||
if (projection.userUuid !== null) {
|
||||
const userUuidOrError = Uuid.create(projection.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${userUuidOrError.getError()}`)
|
||||
}
|
||||
userUuid = userUuidOrError.getValue()
|
||||
}
|
||||
|
||||
const revisionOrError = Revision.create(
|
||||
{
|
||||
authHash: projection.authHash,
|
||||
content: projection.content,
|
||||
contentType,
|
||||
creationDate: new Date(this.timer.convertDateToFormattedString(projection.creationDate, 'YYYY-MM-DD')),
|
||||
encItemKey: projection.encItemKey,
|
||||
itemsKeyId: projection.itemsKeyId,
|
||||
itemUuid,
|
||||
userUuid,
|
||||
dates,
|
||||
},
|
||||
new UniqueEntityId(projection.uuid),
|
||||
)
|
||||
if (revisionOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${revisionOrError.getError()}`)
|
||||
}
|
||||
|
||||
return revisionOrError.getValue()
|
||||
}
|
||||
|
||||
toProjection(domain: Revision): SQLLegacyRevision {
|
||||
const sqlRevision = new SQLLegacyRevision()
|
||||
|
||||
sqlRevision.authHash = domain.props.authHash
|
||||
sqlRevision.content = domain.props.content
|
||||
sqlRevision.contentType = domain.props.contentType.value
|
||||
sqlRevision.createdAt = domain.props.dates.createdAt
|
||||
sqlRevision.updatedAt = domain.props.dates.updatedAt
|
||||
sqlRevision.creationDate = new Date(
|
||||
this.timer.convertDateToFormattedString(domain.props.creationDate, 'YYYY-MM-DD'),
|
||||
)
|
||||
sqlRevision.encItemKey = domain.props.encItemKey
|
||||
sqlRevision.itemUuid = domain.props.itemUuid.value
|
||||
sqlRevision.itemsKeyId = domain.props.itemsKeyId
|
||||
sqlRevision.userUuid = domain.props.userUuid ? domain.props.userUuid.value : null
|
||||
sqlRevision.uuid = domain.id.toString()
|
||||
|
||||
return sqlRevision
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
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')
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
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',
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
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`)')
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
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`')
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
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`')
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
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`')
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
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`')
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
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`')
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
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`')
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
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`)',
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
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')
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddDesignatedSurvivor1695284084365 implements MigrationInterface {
|
||||
name = 'AddDesignatedSurvivor1695284084365'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `shared_vault_users` ADD `is_designated_survivor` tinyint NOT NULL DEFAULT 0')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `shared_vault_users` DROP COLUMN `is_designated_survivor`')
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class RemoveNotifications1695643525793 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DELETE FROM `notifications`')
|
||||
}
|
||||
|
||||
public async down(): Promise<void> {
|
||||
return
|
||||
}
|
||||
}
|
|
@ -4,16 +4,16 @@ export class AddSharedVaultInformation1693219736168 implements MigrationInterfac
|
|||
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('ALTER TABLE `items` ADD `last_edited_by` varchar(36) NULL, ALGORITHM = INSTANT')
|
||||
await queryRunner.query('ALTER TABLE `items` ADD `shared_vault_uuid` varchar(36) NULL, ALGORITHM = INSTANT')
|
||||
await queryRunner.query('ALTER TABLE `items` ADD `key_system_identifier` varchar(36) NULL, ALGORITHM = INSTANT')
|
||||
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`')
|
||||
await queryRunner.query('ALTER TABLE `items` DROP COLUMN `key_system_identifier`, ALGORITHM = INSTANT')
|
||||
await queryRunner.query('ALTER TABLE `items` DROP COLUMN `shared_vault_uuid`, ALGORITHM = INSTANT')
|
||||
await queryRunner.query('ALTER TABLE `items` DROP COLUMN `last_edited_by`, ALGORITHM = INSTANT')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import TYPES from './Types'
|
|||
import { AppDataSource } from './DataSource'
|
||||
import { SNSClient, SNSClientConfig } from '@aws-sdk/client-sns'
|
||||
import { ItemRepositoryInterface } from '../Domain/Item/ItemRepositoryInterface'
|
||||
import { SQLLegacyItemRepository } from '../Infra/TypeORM/SQLLegacyItemRepository'
|
||||
import { Repository } from 'typeorm'
|
||||
import { Item } from '../Domain/Item/Item'
|
||||
import {
|
||||
|
@ -64,8 +63,6 @@ import {
|
|||
} from '@standardnotes/domain-core'
|
||||
import { BaseItemsController } from '../Infra/InversifyExpressUtils/Base/BaseItemsController'
|
||||
import { Transform } from 'stream'
|
||||
import { SQLLegacyItem } from '../Infra/TypeORM/SQLLegacyItem'
|
||||
import { SQLLegacyItemPersistenceMapper } from '../Mapping/Persistence/SQLLegacyItemPersistenceMapper'
|
||||
import { ItemHttpRepresentation } from '../Mapping/Http/ItemHttpRepresentation'
|
||||
import { ItemHttpMapper } from '../Mapping/Http/ItemHttpMapper'
|
||||
import { SavedItemHttpRepresentation } from '../Mapping/Http/SavedItemHttpRepresentation'
|
||||
|
@ -303,9 +300,6 @@ export class ContainerConfigLoader {
|
|||
)
|
||||
|
||||
// Mapping
|
||||
container
|
||||
.bind<MapperInterface<Item, SQLLegacyItem>>(TYPES.Sync_SQLLegacyItemPersistenceMapper)
|
||||
.toConstantValue(new SQLLegacyItemPersistenceMapper())
|
||||
container
|
||||
.bind<MapperInterface<Item, SQLItem>>(TYPES.Sync_SQLItemPersistenceMapper)
|
||||
.toConstantValue(new SQLItemPersistenceMapper())
|
||||
|
@ -363,9 +357,6 @@ export class ContainerConfigLoader {
|
|||
.toConstantValue(new NotificationHttpMapper())
|
||||
|
||||
// ORM
|
||||
container
|
||||
.bind<Repository<SQLLegacyItem>>(TYPES.Sync_ORMLegacyItemRepository)
|
||||
.toDynamicValue(() => appDataSource.getRepository(SQLLegacyItem))
|
||||
container
|
||||
.bind<Repository<SQLItem>>(TYPES.Sync_ORMItemRepository)
|
||||
.toConstantValue(appDataSource.getRepository(SQLItem))
|
||||
|
@ -389,17 +380,11 @@ export class ContainerConfigLoader {
|
|||
container
|
||||
.bind<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository)
|
||||
.toConstantValue(
|
||||
isConfiguredForHomeServerOrSelfHosting
|
||||
? 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),
|
||||
),
|
||||
new SQLItemRepository(
|
||||
container.get<Repository<SQLItem>>(TYPES.Sync_ORMItemRepository),
|
||||
container.get<MapperInterface<Item, SQLItem>>(TYPES.Sync_SQLItemPersistenceMapper),
|
||||
container.get<Logger>(TYPES.Sync_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<SharedVaultRepositoryInterface>(TYPES.Sync_SharedVaultRepository)
|
||||
|
|
|
@ -2,7 +2,6 @@ import { DataSource, EntityTarget, LoggerOptions, ObjectLiteral, Repository } fr
|
|||
import { MysqlConnectionOptions } from 'typeorm/driver/mysql/MysqlConnectionOptions'
|
||||
import { Env } from './Env'
|
||||
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
|
||||
import { SQLLegacyItem } from '../Infra/TypeORM/SQLLegacyItem'
|
||||
import { TypeORMNotification } from '../Infra/TypeORM/TypeORMNotification'
|
||||
import { TypeORMSharedVault } from '../Infra/TypeORM/TypeORMSharedVault'
|
||||
import { TypeORMSharedVaultUser } from '../Infra/TypeORM/TypeORMSharedVaultUser'
|
||||
|
@ -36,24 +35,17 @@ export class AppDataSource {
|
|||
this.configuration.env.load()
|
||||
|
||||
const isConfiguredForMySQL = this.configuration.env.get('DB_TYPE') === 'mysql'
|
||||
const isConfiguredForHomeServerOrSelfHosting =
|
||||
this.configuration.env.get('MODE', true) === 'home-server' ||
|
||||
this.configuration.env.get('MODE', true) === 'self-hosted'
|
||||
|
||||
const maxQueryExecutionTime = this.configuration.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
|
||||
? +this.configuration.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
|
||||
: 45_000
|
||||
|
||||
const migrationsSourceDirectoryName = isConfiguredForMySQL
|
||||
? isConfiguredForHomeServerOrSelfHosting
|
||||
? 'mysql'
|
||||
: 'mysql-legacy'
|
||||
: 'sqlite'
|
||||
const migrationsSourceDirectoryName = isConfiguredForMySQL ? 'mysql' : 'sqlite'
|
||||
|
||||
const commonDataSourceOptions = {
|
||||
maxQueryExecutionTime,
|
||||
entities: [
|
||||
isConfiguredForHomeServerOrSelfHosting ? SQLItem : SQLLegacyItem,
|
||||
SQLItem,
|
||||
TypeORMNotification,
|
||||
TypeORMSharedVault,
|
||||
TypeORMSharedVaultUser,
|
||||
|
|
|
@ -15,7 +15,6 @@ const TYPES = {
|
|||
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'),
|
||||
Sync_ORMSharedVaultUserRepository: Symbol.for('Sync_ORMSharedVaultUserRepository'),
|
||||
|
@ -128,7 +127,6 @@ const TYPES = {
|
|||
Sync_MessagePersistenceMapper: Symbol.for('Sync_MessagePersistenceMapper'),
|
||||
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_ItemHttpMapper: Symbol.for('Sync_ItemHttpMapper'),
|
||||
Sync_ItemHashHttpMapper: Symbol.for('Sync_ItemHashHttpMapper'),
|
||||
|
|
|
@ -1,9 +1,121 @@
|
|||
import { Column, Entity, Index } from 'typeorm'
|
||||
|
||||
import { SQLLegacyItem } from './SQLLegacyItem'
|
||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
|
||||
|
||||
@Entity({ name: 'items' })
|
||||
export class SQLItem extends SQLLegacyItem {
|
||||
@Index('index_items_on_user_uuid_and_content_type', ['userUuid', 'contentType'])
|
||||
@Index('user_uuid_and_updated_at_timestamp_and_created_at_timestamp', [
|
||||
'userUuid',
|
||||
'updatedAtTimestamp',
|
||||
'createdAtTimestamp',
|
||||
])
|
||||
@Index('user_uuid_and_deleted', ['userUuid', 'deleted'])
|
||||
export class SQLItem {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
declare uuid: string
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
name: 'duplicate_of',
|
||||
length: 36,
|
||||
nullable: true,
|
||||
})
|
||||
declare duplicateOf: string | null
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
name: 'items_key_id',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare itemsKeyId: string | null
|
||||
|
||||
@Column({
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
})
|
||||
declare content: string | null
|
||||
|
||||
@Column({
|
||||
name: 'content_type',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
@Index('index_items_on_content_type')
|
||||
declare contentType: string | null
|
||||
|
||||
@Column({
|
||||
name: 'content_size',
|
||||
type: 'int',
|
||||
nullable: true,
|
||||
})
|
||||
declare contentSize: number | null
|
||||
|
||||
@Column({
|
||||
name: 'enc_item_key',
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
})
|
||||
declare encItemKey: string | null
|
||||
|
||||
@Column({
|
||||
name: 'auth_hash',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare authHash: string | null
|
||||
|
||||
@Column({
|
||||
name: 'user_uuid',
|
||||
length: 36,
|
||||
})
|
||||
@Index('index_items_on_user_uuid')
|
||||
declare userUuid: string
|
||||
|
||||
@Column({
|
||||
type: 'tinyint',
|
||||
precision: 1,
|
||||
nullable: true,
|
||||
default: 0,
|
||||
})
|
||||
@Index('index_items_on_deleted')
|
||||
declare deleted: boolean
|
||||
|
||||
@Column({
|
||||
name: 'created_at',
|
||||
type: 'datetime',
|
||||
precision: 6,
|
||||
})
|
||||
declare createdAt: Date
|
||||
|
||||
@Column({
|
||||
name: 'updated_at',
|
||||
type: 'datetime',
|
||||
precision: 6,
|
||||
})
|
||||
declare updatedAt: Date
|
||||
|
||||
@Column({
|
||||
name: 'created_at_timestamp',
|
||||
type: 'bigint',
|
||||
})
|
||||
declare createdAtTimestamp: number
|
||||
|
||||
@Column({
|
||||
name: 'updated_at_timestamp',
|
||||
type: 'bigint',
|
||||
})
|
||||
@Index('updated_at_timestamp')
|
||||
declare updatedAtTimestamp: number
|
||||
|
||||
@Column({
|
||||
name: 'updated_with_session',
|
||||
type: 'varchar',
|
||||
length: 36,
|
||||
nullable: true,
|
||||
})
|
||||
declare updatedWithSession: string | null
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
name: 'last_edited_by',
|
||||
|
|
|
@ -3,20 +3,20 @@ import { MapperInterface, Uuid } from '@standardnotes/domain-core'
|
|||
import { Logger } from 'winston'
|
||||
|
||||
import { Item } from '../../Domain/Item/Item'
|
||||
import { SQLItem } from './SQLItem'
|
||||
import { SQLLegacyItemRepository } from './SQLLegacyItemRepository'
|
||||
import { ItemQuery } from '../../Domain/Item/ItemQuery'
|
||||
import { ItemRepositoryInterface } from '../../Domain/Item/ItemRepositoryInterface'
|
||||
import { ExtendedIntegrityPayload } from '../../Domain/Item/ExtendedIntegrityPayload'
|
||||
import { ItemContentSizeDescriptor } from '../../Domain/Item/ItemContentSizeDescriptor'
|
||||
import { SQLItem } from './SQLItem'
|
||||
|
||||
export class SQLItemRepository extends SQLLegacyItemRepository {
|
||||
export class SQLItemRepository implements ItemRepositoryInterface {
|
||||
constructor(
|
||||
protected override ormRepository: Repository<SQLItem>,
|
||||
protected override mapper: MapperInterface<Item, SQLItem>,
|
||||
protected override logger: Logger,
|
||||
) {
|
||||
super(ormRepository, mapper, logger)
|
||||
}
|
||||
protected ormRepository: Repository<SQLItem>,
|
||||
protected mapper: MapperInterface<Item, SQLItem>,
|
||||
protected logger: Logger,
|
||||
) {}
|
||||
|
||||
override async deleteByUserUuidInSharedVaults(userUuid: Uuid, sharedVaultUuids: Uuid[]): Promise<void> {
|
||||
async deleteByUserUuidInSharedVaults(userUuid: Uuid, sharedVaultUuids: Uuid[]): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder('item')
|
||||
.delete()
|
||||
|
@ -28,7 +28,7 @@ export class SQLItemRepository extends SQLLegacyItemRepository {
|
|||
.execute()
|
||||
}
|
||||
|
||||
override async deleteByUserUuidAndNotInSharedVault(userUuid: Uuid): Promise<void> {
|
||||
async deleteByUserUuidAndNotInSharedVault(userUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder('item')
|
||||
.delete()
|
||||
|
@ -38,25 +38,21 @@ export class SQLItemRepository extends SQLLegacyItemRepository {
|
|||
.execute()
|
||||
}
|
||||
|
||||
override async updateSharedVaultOwner(dto: {
|
||||
sharedVaultUuid: Uuid
|
||||
fromOwnerUuid: Uuid
|
||||
toOwnerUuid: Uuid
|
||||
}): Promise<void> {
|
||||
async updateSharedVaultOwner(dto: { sharedVaultUuid: Uuid; fromOwnerUuid: Uuid; toOwnerUuid: Uuid }): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder('item')
|
||||
.update()
|
||||
.set({
|
||||
userUuid: dto.toOwnerUuid.value,
|
||||
})
|
||||
.where('shared_vault_uuid = :sharedVaultUuid AND user_uuid = :fromOwnerUuid', {
|
||||
sharedVaultUuid: dto.sharedVaultUuid.value,
|
||||
.where('user_uuid = :fromOwnerUuid AND shared_vault_uuid = :sharedVaultUuid', {
|
||||
fromOwnerUuid: dto.fromOwnerUuid.value,
|
||||
sharedVaultUuid: dto.sharedVaultUuid.value,
|
||||
})
|
||||
.execute()
|
||||
}
|
||||
|
||||
override async unassignFromSharedVault(sharedVaultUuid: Uuid): Promise<void> {
|
||||
async unassignFromSharedVault(sharedVaultUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder('item')
|
||||
.update()
|
||||
|
@ -69,7 +65,186 @@ export class SQLItemRepository extends SQLLegacyItemRepository {
|
|||
.execute()
|
||||
}
|
||||
|
||||
protected override createFindAllQueryBuilder(query: ItemQuery): SelectQueryBuilder<SQLItem> {
|
||||
async removeByUuid(uuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder('item')
|
||||
.delete()
|
||||
.from('items')
|
||||
.where('uuid = :uuid', { uuid: uuid.value })
|
||||
.execute()
|
||||
}
|
||||
|
||||
async insert(item: Item): Promise<void> {
|
||||
const projection = this.mapper.toProjection(item)
|
||||
|
||||
await this.ormRepository.insert(projection)
|
||||
}
|
||||
|
||||
async update(item: Item): Promise<void> {
|
||||
const projection = this.mapper.toProjection(item)
|
||||
|
||||
const { uuid, ...updateValues } = projection
|
||||
|
||||
await this.ormRepository.update({ uuid: uuid }, updateValues)
|
||||
}
|
||||
|
||||
async remove(item: Item): Promise<void> {
|
||||
await this.ormRepository.remove(this.mapper.toProjection(item))
|
||||
}
|
||||
|
||||
async updateContentSize(itemUuid: string, contentSize: number): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder('item')
|
||||
.update()
|
||||
.set({
|
||||
contentSize,
|
||||
})
|
||||
.where('uuid = :itemUuid', {
|
||||
itemUuid,
|
||||
})
|
||||
.execute()
|
||||
}
|
||||
|
||||
async findContentSizeForComputingTransferLimit(query: ItemQuery): Promise<ItemContentSizeDescriptor[]> {
|
||||
const queryBuilder = this.createFindAllQueryBuilder(query)
|
||||
queryBuilder.select('item.uuid', 'uuid')
|
||||
queryBuilder.addSelect('item.content_size', 'contentSize')
|
||||
|
||||
const items = await queryBuilder.getRawMany()
|
||||
|
||||
const itemContentSizeDescriptors: ItemContentSizeDescriptor[] = []
|
||||
for (const item of items) {
|
||||
const ItemContentSizeDescriptorOrError = ItemContentSizeDescriptor.create(item.uuid, item.contentSize)
|
||||
if (ItemContentSizeDescriptorOrError.isFailed()) {
|
||||
this.logger.error(
|
||||
`Failed to create ItemContentSizeDescriptor for item ${
|
||||
item.uuid
|
||||
}: ${ItemContentSizeDescriptorOrError.getError()}`,
|
||||
)
|
||||
continue
|
||||
}
|
||||
itemContentSizeDescriptors.push(ItemContentSizeDescriptorOrError.getValue())
|
||||
}
|
||||
|
||||
return itemContentSizeDescriptors
|
||||
}
|
||||
|
||||
async findByUuid(uuid: Uuid): Promise<Item | null> {
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('item')
|
||||
.where('item.uuid = :uuid', {
|
||||
uuid: uuid.value,
|
||||
})
|
||||
.getOne()
|
||||
|
||||
if (persistence === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
const item = this.mapper.toDomain(persistence)
|
||||
|
||||
return item
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to map item ${uuid.value} for user ${persistence.userUuid} by uuid: ${(error as Error).message}`,
|
||||
)
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async findDatesForComputingIntegrityHash(userUuid: string): Promise<Array<{ updated_at_timestamp: number }>> {
|
||||
const queryBuilder = this.ormRepository.createQueryBuilder('item')
|
||||
queryBuilder.select('item.updated_at_timestamp')
|
||||
queryBuilder.where('item.user_uuid = :userUuid', { userUuid: userUuid })
|
||||
queryBuilder.andWhere('item.deleted = :deleted', { deleted: false })
|
||||
|
||||
const items = await queryBuilder.getRawMany()
|
||||
|
||||
return items.sort((itemA, itemB) => itemB.updated_at_timestamp - itemA.updated_at_timestamp)
|
||||
}
|
||||
|
||||
async findItemsForComputingIntegrityPayloads(userUuid: string): Promise<ExtendedIntegrityPayload[]> {
|
||||
const queryBuilder = this.ormRepository.createQueryBuilder('item')
|
||||
queryBuilder.select('item.uuid', 'uuid')
|
||||
queryBuilder.addSelect('item.updated_at_timestamp', 'updated_at_timestamp')
|
||||
queryBuilder.addSelect('item.content_type', 'content_type')
|
||||
queryBuilder.where('item.user_uuid = :userUuid', { userUuid: userUuid })
|
||||
queryBuilder.andWhere('item.deleted = :deleted', { deleted: false })
|
||||
|
||||
const items = await queryBuilder.getRawMany()
|
||||
|
||||
return items.sort((itemA, itemB) => itemB.updated_at_timestamp - itemA.updated_at_timestamp)
|
||||
}
|
||||
|
||||
async findByUuidAndUserUuid(uuid: string, userUuid: string): Promise<Item | null> {
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('item')
|
||||
.where('item.uuid = :uuid AND item.user_uuid = :userUuid', {
|
||||
uuid,
|
||||
userUuid,
|
||||
})
|
||||
.getOne()
|
||||
|
||||
if (persistence === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
const item = this.mapper.toDomain(persistence)
|
||||
|
||||
return item
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to map item ${uuid} for user ${persistence.userUuid} by uuid and userUuid: ${(error as Error).message}`,
|
||||
)
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async findAll(query: ItemQuery): Promise<Item[]> {
|
||||
const persistence = await this.createFindAllQueryBuilder(query).getMany()
|
||||
|
||||
const domainItems: Item[] = []
|
||||
for (const persistencItem of persistence) {
|
||||
try {
|
||||
domainItems.push(this.mapper.toDomain(persistencItem))
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to map item ${persistencItem.uuid} for user ${persistencItem.userUuid} to domain: ${
|
||||
(error as Error).message
|
||||
}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return domainItems
|
||||
}
|
||||
|
||||
async countAll(query: ItemQuery): Promise<number> {
|
||||
return this.createFindAllQueryBuilder(query).getCount()
|
||||
}
|
||||
|
||||
async markItemsAsDeleted(itemUuids: Array<string>, updatedAtTimestamp: number): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder('item')
|
||||
.update()
|
||||
.set({
|
||||
deleted: true,
|
||||
content: null,
|
||||
encItemKey: null,
|
||||
authHash: null,
|
||||
updatedAtTimestamp,
|
||||
})
|
||||
.where('uuid IN (:...uuids)', {
|
||||
uuids: itemUuids,
|
||||
})
|
||||
.execute()
|
||||
}
|
||||
|
||||
protected createFindAllQueryBuilder(query: ItemQuery): SelectQueryBuilder<SQLItem> {
|
||||
const queryBuilder = this.ormRepository.createQueryBuilder('item')
|
||||
|
||||
if (query.sortBy !== undefined && query.sortOrder !== undefined) {
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
|
||||
|
||||
@Entity({ name: 'items' })
|
||||
@Index('index_items_on_user_uuid_and_content_type', ['userUuid', 'contentType'])
|
||||
@Index('user_uuid_and_updated_at_timestamp_and_created_at_timestamp', [
|
||||
'userUuid',
|
||||
'updatedAtTimestamp',
|
||||
'createdAtTimestamp',
|
||||
])
|
||||
@Index('user_uuid_and_deleted', ['userUuid', 'deleted'])
|
||||
export class SQLLegacyItem {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
declare uuid: string
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
name: 'duplicate_of',
|
||||
length: 36,
|
||||
nullable: true,
|
||||
})
|
||||
declare duplicateOf: string | null
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
name: 'items_key_id',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare itemsKeyId: string | null
|
||||
|
||||
@Column({
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
})
|
||||
declare content: string | null
|
||||
|
||||
@Column({
|
||||
name: 'content_type',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
@Index('index_items_on_content_type')
|
||||
declare contentType: string | null
|
||||
|
||||
@Column({
|
||||
name: 'content_size',
|
||||
type: 'int',
|
||||
nullable: true,
|
||||
})
|
||||
declare contentSize: number | null
|
||||
|
||||
@Column({
|
||||
name: 'enc_item_key',
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
})
|
||||
declare encItemKey: string | null
|
||||
|
||||
@Column({
|
||||
name: 'auth_hash',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare authHash: string | null
|
||||
|
||||
@Column({
|
||||
name: 'user_uuid',
|
||||
length: 36,
|
||||
})
|
||||
@Index('index_items_on_user_uuid')
|
||||
declare userUuid: string
|
||||
|
||||
@Column({
|
||||
type: 'tinyint',
|
||||
precision: 1,
|
||||
nullable: true,
|
||||
default: 0,
|
||||
})
|
||||
@Index('index_items_on_deleted')
|
||||
declare deleted: boolean
|
||||
|
||||
@Column({
|
||||
name: 'created_at',
|
||||
type: 'datetime',
|
||||
precision: 6,
|
||||
})
|
||||
declare createdAt: Date
|
||||
|
||||
@Column({
|
||||
name: 'updated_at',
|
||||
type: 'datetime',
|
||||
precision: 6,
|
||||
})
|
||||
declare updatedAt: Date
|
||||
|
||||
@Column({
|
||||
name: 'created_at_timestamp',
|
||||
type: 'bigint',
|
||||
})
|
||||
declare createdAtTimestamp: number
|
||||
|
||||
@Column({
|
||||
name: 'updated_at_timestamp',
|
||||
type: 'bigint',
|
||||
})
|
||||
@Index('updated_at_timestamp')
|
||||
declare updatedAtTimestamp: number
|
||||
|
||||
@Column({
|
||||
name: 'updated_with_session',
|
||||
type: 'varchar',
|
||||
length: 36,
|
||||
nullable: true,
|
||||
})
|
||||
declare updatedWithSession: string | null
|
||||
}
|
|
@ -1,263 +0,0 @@
|
|||
import { Repository, SelectQueryBuilder } from 'typeorm'
|
||||
import { MapperInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { Item } from '../../Domain/Item/Item'
|
||||
import { ItemQuery } from '../../Domain/Item/ItemQuery'
|
||||
import { ItemRepositoryInterface } from '../../Domain/Item/ItemRepositoryInterface'
|
||||
import { ExtendedIntegrityPayload } from '../../Domain/Item/ExtendedIntegrityPayload'
|
||||
import { SQLLegacyItem } from './SQLLegacyItem'
|
||||
import { ItemContentSizeDescriptor } from '../../Domain/Item/ItemContentSizeDescriptor'
|
||||
|
||||
export class SQLLegacyItemRepository implements ItemRepositoryInterface {
|
||||
constructor(
|
||||
protected ormRepository: Repository<SQLLegacyItem>,
|
||||
protected mapper: MapperInterface<Item, SQLLegacyItem>,
|
||||
protected logger: Logger,
|
||||
) {}
|
||||
|
||||
async deleteByUserUuidInSharedVaults(_userUuid: Uuid, _sharedVaultUuids: Uuid[]): Promise<void> {
|
||||
this.logger.error('Method deleteByUserUuidInSharedVaults not supported.')
|
||||
}
|
||||
|
||||
async updateSharedVaultOwner(_dto: { sharedVaultUuid: Uuid; fromOwnerUuid: Uuid; toOwnerUuid: Uuid }): Promise<void> {
|
||||
this.logger.error('Method updateSharedVaultOwner not supported.')
|
||||
}
|
||||
|
||||
async unassignFromSharedVault(_sharedVaultUuid: Uuid): Promise<void> {
|
||||
this.logger.error('Method unassignFromSharedVault not supported.')
|
||||
}
|
||||
|
||||
async removeByUuid(uuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder('item')
|
||||
.delete()
|
||||
.from('items')
|
||||
.where('uuid = :uuid', { uuid: uuid.value })
|
||||
.execute()
|
||||
}
|
||||
|
||||
async insert(item: Item): Promise<void> {
|
||||
const projection = this.mapper.toProjection(item)
|
||||
|
||||
await this.ormRepository.insert(projection)
|
||||
}
|
||||
|
||||
async update(item: Item): Promise<void> {
|
||||
const projection = this.mapper.toProjection(item)
|
||||
|
||||
const { uuid, ...updateValues } = projection
|
||||
|
||||
await this.ormRepository.update({ uuid: uuid }, updateValues)
|
||||
}
|
||||
|
||||
async remove(item: Item): Promise<void> {
|
||||
await this.ormRepository.remove(this.mapper.toProjection(item))
|
||||
}
|
||||
|
||||
async updateContentSize(itemUuid: string, contentSize: number): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder('item')
|
||||
.update()
|
||||
.set({
|
||||
contentSize,
|
||||
})
|
||||
.where('uuid = :itemUuid', {
|
||||
itemUuid,
|
||||
})
|
||||
.execute()
|
||||
}
|
||||
|
||||
async findContentSizeForComputingTransferLimit(query: ItemQuery): Promise<ItemContentSizeDescriptor[]> {
|
||||
const queryBuilder = this.createFindAllQueryBuilder(query)
|
||||
queryBuilder.select('item.uuid', 'uuid')
|
||||
queryBuilder.addSelect('item.content_size', 'contentSize')
|
||||
|
||||
const items = await queryBuilder.getRawMany()
|
||||
|
||||
const itemContentSizeDescriptors: ItemContentSizeDescriptor[] = []
|
||||
for (const item of items) {
|
||||
const ItemContentSizeDescriptorOrError = ItemContentSizeDescriptor.create(item.uuid, item.contentSize)
|
||||
if (ItemContentSizeDescriptorOrError.isFailed()) {
|
||||
this.logger.error(
|
||||
`Failed to create ItemContentSizeDescriptor for item ${
|
||||
item.uuid
|
||||
}: ${ItemContentSizeDescriptorOrError.getError()}`,
|
||||
)
|
||||
continue
|
||||
}
|
||||
itemContentSizeDescriptors.push(ItemContentSizeDescriptorOrError.getValue())
|
||||
}
|
||||
|
||||
return itemContentSizeDescriptors
|
||||
}
|
||||
|
||||
async deleteByUserUuidAndNotInSharedVault(userUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder('item')
|
||||
.delete()
|
||||
.from('items')
|
||||
.where('user_uuid = :userUuid', { userUuid: userUuid.value })
|
||||
.execute()
|
||||
}
|
||||
|
||||
async findByUuid(uuid: Uuid): Promise<Item | null> {
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('item')
|
||||
.where('item.uuid = :uuid', {
|
||||
uuid: uuid.value,
|
||||
})
|
||||
.getOne()
|
||||
|
||||
if (persistence === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
const item = this.mapper.toDomain(persistence)
|
||||
|
||||
return item
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to map item ${uuid.value} for user ${persistence.userUuid} by uuid: ${(error as Error).message}`,
|
||||
)
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async findDatesForComputingIntegrityHash(userUuid: string): Promise<Array<{ updated_at_timestamp: number }>> {
|
||||
const queryBuilder = this.ormRepository.createQueryBuilder('item')
|
||||
queryBuilder.select('item.updated_at_timestamp')
|
||||
queryBuilder.where('item.user_uuid = :userUuid', { userUuid: userUuid })
|
||||
queryBuilder.andWhere('item.deleted = :deleted', { deleted: false })
|
||||
|
||||
const items = await queryBuilder.getRawMany()
|
||||
|
||||
return items.sort((itemA, itemB) => itemB.updated_at_timestamp - itemA.updated_at_timestamp)
|
||||
}
|
||||
|
||||
async findItemsForComputingIntegrityPayloads(userUuid: string): Promise<ExtendedIntegrityPayload[]> {
|
||||
const queryBuilder = this.ormRepository.createQueryBuilder('item')
|
||||
queryBuilder.select('item.uuid', 'uuid')
|
||||
queryBuilder.addSelect('item.updated_at_timestamp', 'updated_at_timestamp')
|
||||
queryBuilder.addSelect('item.content_type', 'content_type')
|
||||
queryBuilder.where('item.user_uuid = :userUuid', { userUuid: userUuid })
|
||||
queryBuilder.andWhere('item.deleted = :deleted', { deleted: false })
|
||||
|
||||
const items = await queryBuilder.getRawMany()
|
||||
|
||||
return items.sort((itemA, itemB) => itemB.updated_at_timestamp - itemA.updated_at_timestamp)
|
||||
}
|
||||
|
||||
async findByUuidAndUserUuid(uuid: string, userUuid: string): Promise<Item | null> {
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('item')
|
||||
.where('item.uuid = :uuid AND item.user_uuid = :userUuid', {
|
||||
uuid,
|
||||
userUuid,
|
||||
})
|
||||
.getOne()
|
||||
|
||||
if (persistence === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
const item = this.mapper.toDomain(persistence)
|
||||
|
||||
return item
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to map item ${uuid} for user ${persistence.userUuid} by uuid and userUuid: ${(error as Error).message}`,
|
||||
)
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async findAll(query: ItemQuery): Promise<Item[]> {
|
||||
const persistence = await this.createFindAllQueryBuilder(query).getMany()
|
||||
|
||||
const domainItems: Item[] = []
|
||||
for (const persistencItem of persistence) {
|
||||
try {
|
||||
domainItems.push(this.mapper.toDomain(persistencItem))
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to map item ${persistencItem.uuid} for user ${persistencItem.userUuid} to domain: ${
|
||||
(error as Error).message
|
||||
}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return domainItems
|
||||
}
|
||||
|
||||
async countAll(query: ItemQuery): Promise<number> {
|
||||
return this.createFindAllQueryBuilder(query).getCount()
|
||||
}
|
||||
|
||||
async markItemsAsDeleted(itemUuids: Array<string>, updatedAtTimestamp: number): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder('item')
|
||||
.update()
|
||||
.set({
|
||||
deleted: true,
|
||||
content: null,
|
||||
encItemKey: null,
|
||||
authHash: null,
|
||||
updatedAtTimestamp,
|
||||
})
|
||||
.where('uuid IN (:...uuids)', {
|
||||
uuids: itemUuids,
|
||||
})
|
||||
.execute()
|
||||
}
|
||||
|
||||
protected createFindAllQueryBuilder(query: ItemQuery): SelectQueryBuilder<SQLLegacyItem> {
|
||||
const queryBuilder = this.ormRepository.createQueryBuilder('item')
|
||||
|
||||
if (query.sortBy !== undefined && query.sortOrder !== undefined) {
|
||||
queryBuilder.orderBy(`item.${query.sortBy}`, query.sortOrder)
|
||||
}
|
||||
|
||||
if (query.userUuid !== undefined) {
|
||||
queryBuilder.where('item.user_uuid = :userUuid', { userUuid: query.userUuid })
|
||||
}
|
||||
|
||||
if (query.uuids && query.uuids.length > 0) {
|
||||
queryBuilder.andWhere('item.uuid IN (:...uuids)', { uuids: query.uuids })
|
||||
}
|
||||
if (query.deleted !== undefined) {
|
||||
queryBuilder.andWhere('item.deleted = :deleted', { deleted: query.deleted })
|
||||
}
|
||||
if (query.contentType) {
|
||||
if (Array.isArray(query.contentType)) {
|
||||
queryBuilder.andWhere('item.content_type IN (:...contentTypes)', { contentTypes: query.contentType })
|
||||
} else {
|
||||
queryBuilder.andWhere('item.content_type = :contentType', { contentType: query.contentType })
|
||||
}
|
||||
}
|
||||
if (query.lastSyncTime && query.syncTimeComparison) {
|
||||
queryBuilder.andWhere(`item.updated_at_timestamp ${query.syncTimeComparison} :lastSyncTime`, {
|
||||
lastSyncTime: query.lastSyncTime,
|
||||
})
|
||||
}
|
||||
if (query.createdBetween !== undefined) {
|
||||
queryBuilder.andWhere('item.created_at >= :createdAfter AND item.created_at <= :createdBefore', {
|
||||
createdAfter: query.createdBetween[0].toISOString(),
|
||||
createdBefore: query.createdBetween[1].toISOString(),
|
||||
})
|
||||
}
|
||||
if (query.offset !== undefined) {
|
||||
queryBuilder.skip(query.offset)
|
||||
}
|
||||
if (query.limit !== undefined) {
|
||||
queryBuilder.take(query.limit)
|
||||
}
|
||||
|
||||
return queryBuilder
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
import { Timestamps, MapperInterface, UniqueEntityId, Uuid, ContentType, Dates } from '@standardnotes/domain-core'
|
||||
|
||||
import { Item } from '../../Domain/Item/Item'
|
||||
|
||||
import { SQLLegacyItem } from '../../Infra/TypeORM/SQLLegacyItem'
|
||||
|
||||
export class SQLLegacyItemPersistenceMapper implements MapperInterface<Item, SQLLegacyItem> {
|
||||
toDomain(projection: SQLLegacyItem): Item {
|
||||
const uuidOrError = Uuid.create(projection.uuid)
|
||||
if (uuidOrError.isFailed()) {
|
||||
throw new Error(`Failed to create item from projection: ${uuidOrError.getError()}`)
|
||||
}
|
||||
const uuid = uuidOrError.getValue()
|
||||
|
||||
let duplicateOf = null
|
||||
if (projection.duplicateOf) {
|
||||
const duplicateOfOrError = Uuid.create(projection.duplicateOf)
|
||||
if (duplicateOfOrError.isFailed()) {
|
||||
throw new Error(`Failed to create item from projection: ${duplicateOfOrError.getError()}`)
|
||||
}
|
||||
duplicateOf = duplicateOfOrError.getValue()
|
||||
}
|
||||
|
||||
const contentTypeOrError = ContentType.create(projection.contentType)
|
||||
if (contentTypeOrError.isFailed()) {
|
||||
throw new Error(`Failed to create item from projection: ${contentTypeOrError.getError()}`)
|
||||
}
|
||||
const contentType = contentTypeOrError.getValue()
|
||||
|
||||
const userUuidOrError = Uuid.create(projection.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
throw new Error(`Failed to create item from projection: ${userUuidOrError.getError()}`)
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const datesOrError = Dates.create(projection.createdAt, projection.updatedAt)
|
||||
if (datesOrError.isFailed()) {
|
||||
throw new Error(`Failed to create item from projection: ${datesOrError.getError()}`)
|
||||
}
|
||||
const dates = datesOrError.getValue()
|
||||
|
||||
const timestampsOrError = Timestamps.create(projection.createdAtTimestamp, projection.updatedAtTimestamp)
|
||||
if (timestampsOrError.isFailed()) {
|
||||
throw new Error(`Failed to create item from projection: ${timestampsOrError.getError()}`)
|
||||
}
|
||||
const timestamps = timestampsOrError.getValue()
|
||||
|
||||
let updatedWithSession = null
|
||||
if (projection.updatedWithSession) {
|
||||
const updatedWithSessionOrError = Uuid.create(projection.updatedWithSession)
|
||||
if (updatedWithSessionOrError.isFailed()) {
|
||||
throw new Error(`Failed to create item from projection: ${updatedWithSessionOrError.getError()}`)
|
||||
}
|
||||
updatedWithSession = updatedWithSessionOrError.getValue()
|
||||
}
|
||||
|
||||
const itemOrError = Item.create(
|
||||
{
|
||||
duplicateOf,
|
||||
itemsKeyId: projection.itemsKeyId,
|
||||
content: projection.content,
|
||||
contentType,
|
||||
contentSize: projection.contentSize ?? undefined,
|
||||
encItemKey: projection.encItemKey,
|
||||
authHash: projection.authHash,
|
||||
userUuid,
|
||||
deleted: !!projection.deleted,
|
||||
dates,
|
||||
timestamps,
|
||||
updatedWithSession,
|
||||
},
|
||||
new UniqueEntityId(uuid.value),
|
||||
)
|
||||
if (itemOrError.isFailed()) {
|
||||
throw new Error(`Failed to create item from projection: ${itemOrError.getError()}`)
|
||||
}
|
||||
|
||||
return itemOrError.getValue()
|
||||
}
|
||||
|
||||
toProjection(domain: Item): SQLLegacyItem {
|
||||
const typeorm = new SQLLegacyItem()
|
||||
|
||||
typeorm.uuid = domain.id.toString()
|
||||
typeorm.duplicateOf = domain.props.duplicateOf ? domain.props.duplicateOf.value : null
|
||||
typeorm.itemsKeyId = domain.props.itemsKeyId
|
||||
typeorm.content = domain.props.content
|
||||
typeorm.contentType = domain.props.contentType.value
|
||||
typeorm.contentSize = domain.props.contentSize ?? null
|
||||
typeorm.encItemKey = domain.props.encItemKey
|
||||
typeorm.authHash = domain.props.authHash
|
||||
typeorm.userUuid = domain.props.userUuid.value
|
||||
typeorm.deleted = !!domain.props.deleted
|
||||
typeorm.createdAt = domain.props.dates.createdAt
|
||||
typeorm.updatedAt = domain.props.dates.updatedAt
|
||||
typeorm.createdAtTimestamp = domain.props.timestamps.createdAt
|
||||
typeorm.updatedAtTimestamp = domain.props.timestamps.updatedAt
|
||||
typeorm.updatedWithSession = domain.props.updatedWithSession ? domain.props.updatedWithSession.value : null
|
||||
|
||||
return typeorm
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue