feat: on the fly vault creation during sync

This commit is contained in:
Mo
2023-05-31 09:01:26 -05:00
parent 9953860a61
commit ed48350d3e
15 changed files with 53 additions and 52 deletions
@@ -25,10 +25,6 @@ export class CreateVault1684173017359 implements MigrationInterface {
length: '36',
isNullable: false,
}),
new TableColumn({
name: 'vault_key_timestamp',
type: 'bigint',
}),
new TableColumn({
name: 'file_upload_bytes_used',
type: 'bigint',
@@ -148,6 +148,7 @@ export class ServerContainerConfigLoader extends CommonContainerConfigLoader {
context.container.get(TYPES.ItemProjector),
context.container.get(TYPES.MAX_ITEMS_LIMIT),
context.container.get(TYPES.VaultUserRepository),
context.container.get(TYPES.VaultService),
context.container.get(TYPES.Logger),
)
})
@@ -16,7 +16,6 @@ import { ProjectorInterface } from '../Projection/ProjectorInterface'
import { Vault } from '../Domain/Vault/Model/Vault'
import { VaultProjection } from '../Projection/VaultProjection'
import { VaultUserProjection } from '../Projection/VaultUserProjection'
import { VaultUserServiceInterface } from '../Domain/VaultUser/Service/VaultUserServiceInterface'
import { CreateVaultFileValetToken } from '../Domain/UseCase/CreateVaultFileValetToken/CreateVaultFileValetToken'
import { RemovedVaultUserServiceInterface } from '../Domain/RemovedVaultUser/Service/RemovedVaultUserServiceInterface'
@@ -24,7 +23,6 @@ import { RemovedVaultUserServiceInterface } from '../Domain/RemovedVaultUser/Ser
export class VaultsController extends BaseHttpController {
constructor(
@inject(TYPES.VaultService) private vaultService: VaultServiceInterface,
@inject(TYPES.VaultUserService) private vaultUserService: VaultUserServiceInterface,
@inject(TYPES.RemovedVaultUserService) private removedVaultUserService: RemovedVaultUserServiceInterface,
@inject(TYPES.VaultProjector) private vaultProjector: ProjectorInterface<Vault, VaultProjection>,
@inject(TYPES.VaultUserProjector) private vaultUserProjector: ProjectorInterface<VaultUser, VaultUserProjection>,
@@ -74,7 +72,6 @@ export class VaultsController extends BaseHttpController {
const result = await this.vaultService.createVault({
userUuid: response.locals.user.uuid,
vaultUuid: request.body.vault_uuid,
vaultKeyTimestamp: request.body.vault_key_timestamp,
specifiedItemsKeyUuid: request.body.specified_items_key_uuid,
})
@@ -82,19 +79,9 @@ export class VaultsController extends BaseHttpController {
return this.errorResponse(400, 'Could not create vault')
}
const vaultUser = await this.vaultUserService.addVaultUser({
vaultUuid: result.uuid,
userUuid: response.locals.user.uuid,
permissions: 'admin',
})
if (!vaultUser) {
return this.errorResponse(400, 'Could not add user to vault')
}
return this.json({
vault: this.vaultProjector.projectFull(result),
vaultUser: this.vaultUserProjector.projectFull(vaultUser),
vault: this.vaultProjector.projectFull(result.vault),
vaultUser: this.vaultUserProjector.projectFull(result.vaultUser),
})
}
@@ -103,7 +90,6 @@ export class VaultsController extends BaseHttpController {
const result = await this.vaultService.updateVault({
vaultUuid: request.params.vaultUuid,
originatorUuid: response.locals.user.uuid,
vaultKeyTimestamp: request.body.vault_key_timestamp,
specifiedItemsKeyUuid: request.body.specified_items_key_uuid,
})
@@ -21,6 +21,7 @@ import { ProjectorInterface } from '../../Projection/ProjectorInterface'
import { ItemProjection } from '../../Projection/ItemProjection'
import { VaultUserRepositoryInterface } from '../VaultUser/Repository/VaultUserRepositoryInterface'
import { ConflictType } from '../../Tmp/ConflictType'
import { VaultServiceInterface } from '../Vault/Service/VaultServiceInterface'
export class ItemService implements ItemServiceInterface {
private readonly DEFAULT_ITEMS_LIMIT = 150
@@ -39,6 +40,7 @@ export class ItemService implements ItemServiceInterface {
private itemProjector: ProjectorInterface<Item, ItemProjection>,
private maxItemsSyncLimit: number,
private vaultUsersRepository: VaultUserRepositoryInterface,
private vaultService: VaultServiceInterface,
private logger: Logger,
) {}
@@ -97,6 +99,8 @@ export class ItemService implements ItemServiceInterface {
const savedItems: Array<Item> = []
const conflicts: Array<ItemConflict> = []
await this.dynamicallyCreateVaultsBasedOnIncomingItems(dto)
const lastUpdatedTimestamp = this.timer.getTimestampInMicroseconds()
for (const itemHash of dto.itemHashes) {
@@ -183,6 +187,30 @@ export class ItemService implements ItemServiceInterface {
return retrievedItems
}
private async dynamicallyCreateVaultsBasedOnIncomingItems(dto: SaveItemsDTO): Promise<void> {
const processedVaultUuids = new Set()
for (const itemHash of dto.itemHashes) {
if (!itemHash.vault_uuid || processedVaultUuids.has(itemHash.vault_uuid)) {
continue
}
processedVaultUuids.add(itemHash.vault_uuid)
const vaultItemsKeyUuid =
itemHash.content_type === ContentType.VaultItemsKey ? itemHash.uuid : itemHash.items_key_id
if (!vaultItemsKeyUuid) {
continue
}
await this.vaultService.createVault({
userUuid: dto.userUuid,
vaultUuid: itemHash.vault_uuid,
specifiedItemsKeyUuid: vaultItemsKeyUuid,
})
}
}
private calculateSyncToken(lastUpdatedTimestamp: number, savedItems: Array<Item>): string {
if (savedItems.length) {
const sortedItems = savedItems.sort((itemA: Item, itemB: Item) => {
@@ -64,7 +64,7 @@ export class VaultFilter implements ItemSaveRuleInterface {
) {
return {
type: 'move-to-other-vault',
sourceVaultUuid: existingItemVaultUuid,
vaultUuid: existingItemVaultUuid,
targetVaultUuid: targetItemVaultUuid,
existingItem: dto.existingItem,
...common,
@@ -195,7 +195,7 @@ export class VaultFilter implements ItemSaveRuleInterface {
}
private async handleMoveToOtherVaultOperation(operation: MoveToOtherVaultSaveOperation): Promise<ItemSaveRuleResult> {
const sourceVaultPermissions = await this.getVaultPermissions(operation.userUuid, operation.sourceVaultUuid)
const sourceVaultPermissions = await this.getVaultPermissions(operation.userUuid, operation.vaultUuid)
const targetVaultPermissions = await this.getVaultPermissions(operation.userUuid, operation.targetVaultUuid)
if (!sourceVaultPermissions || !targetVaultPermissions) {
@@ -20,7 +20,7 @@ export type RemoveFromVaultSaveOperation = SaveOperationCommon & {
export type MoveToOtherVaultSaveOperation = SaveOperationCommon & {
type: 'move-to-other-vault'
sourceVaultUuid: string
vaultUuid: string
targetVaultUuid: string
existingItem: Item
}
@@ -11,7 +11,6 @@ export class VaultFactory implements VaultFactoryInterface {
newVault.uuid = dto.vaultHash.uuid
newVault.userUuid = dto.userUuid
newVault.specifiedItemsKeyUuid = dto.vaultHash.specified_items_key_uuid
newVault.vaultKeyTimestamp = dto.vaultHash.vault_key_timestamp
newVault.fileUploadBytesUsed = dto.vaultHash.file_upload_bytes_used
newVault.fileUploadBytesLimit = dto.vaultHash.file_upload_bytes_limit
@@ -2,7 +2,6 @@ export type VaultHash = {
uuid: string
user_uuid: string
specified_items_key_uuid: string
vault_key_timestamp: number
file_upload_bytes_used: number
file_upload_bytes_limit: number
created_at_timestamp?: number
@@ -18,12 +18,6 @@ export class Vault {
})
declare specifiedItemsKeyUuid: string
@Column({
name: 'vault_key_timestamp',
type: 'bigint',
})
declare vaultKeyTimestamp: number
@Column({
name: 'file_upload_bytes_used',
type: 'bigint',
@@ -1,7 +1,7 @@
import { TimerInterface } from '@standardnotes/time'
import { Vault } from '../Model/Vault'
import { VaultsRepositoryInterface } from '../Repository/VaultRepositoryInterface'
import { CreateVaultDTO, VaultServiceInterface, UpdateVaultDTO } from './VaultServiceInterface'
import { CreateVaultDTO, VaultServiceInterface, UpdateVaultDTO, CreateVaultResult } from './VaultServiceInterface'
import { VaultFactoryInterface } from '../Factory/VaultFactoryInterface'
import { VaultUserServiceInterface } from '../../VaultUser/Service/VaultUserServiceInterface'
import { VaultInviteServiceInterface } from '../../VaultInvite/Service/VaultInviteServiceInterface'
@@ -15,7 +15,7 @@ export class VaultService implements VaultServiceInterface {
private timer: TimerInterface,
) {}
async createVault(dto: CreateVaultDTO): Promise<Vault | null> {
async createVault(dto: CreateVaultDTO): Promise<CreateVaultResult | null> {
const existingVault = await this.vaultRepository.findByUuid(dto.vaultUuid)
if (existingVault) {
return null
@@ -28,7 +28,6 @@ export class VaultService implements VaultServiceInterface {
uuid: dto.vaultUuid,
user_uuid: dto.userUuid,
specified_items_key_uuid: dto.specifiedItemsKeyUuid,
vault_key_timestamp: dto.vaultKeyTimestamp,
file_upload_bytes_limit: 1_000_000,
file_upload_bytes_used: 0,
created_at_timestamp: timestamp,
@@ -38,7 +37,13 @@ export class VaultService implements VaultServiceInterface {
const savedVault = await this.vaultRepository.create(vault)
return savedVault
const vaultUser = await this.vaultUserService.addVaultUser({
vaultUuid: savedVault.uuid,
userUuid: dto.userUuid,
permissions: 'admin',
})
return { vault, vaultUser }
}
async getVault(dto: { vaultUuid: string }): Promise<Vault | null> {
@@ -67,12 +72,7 @@ export class VaultService implements VaultServiceInterface {
return null
}
if (dto.vaultKeyTimestamp < vault.vaultKeyTimestamp) {
return null
}
vault.specifiedItemsKeyUuid = dto.specifiedItemsKeyUuid
vault.vaultKeyTimestamp = dto.vaultKeyTimestamp
vault.updatedAtTimestamp = this.timer.getTimestampInMicroseconds()
const savedVault = await this.vaultRepository.save(vault)
@@ -1,21 +1,25 @@
import { VaultUser } from './../../VaultUser/Model/VaultUser'
import { Vault } from '../Model/Vault'
export type CreateVaultDTO = {
userUuid: string
vaultUuid: string
vaultKeyTimestamp: number
specifiedItemsKeyUuid: string
}
export type CreateVaultResult = {
vault: Vault
vaultUser: VaultUser
}
export type UpdateVaultDTO = {
originatorUuid: string
vaultUuid: string
vaultKeyTimestamp: number
specifiedItemsKeyUuid: string
}
export interface VaultServiceInterface {
createVault(dto: CreateVaultDTO): Promise<Vault | null>
createVault(dto: CreateVaultDTO): Promise<CreateVaultResult | null>
updateVault(dto: UpdateVaultDTO): Promise<Vault | null>
@@ -22,10 +22,10 @@ export class VaultUserService implements VaultUserServiceInterface {
vaultUuid: string
userUuid: string
permissions: VaultUserPermission
}): Promise<VaultUser | null> {
}): Promise<VaultUser> {
const vault = await this.vaultRepository.findByUuid(dto.vaultUuid)
if (!vault) {
return null
throw new Error('Attempting to add a vault user to a non-existent vault')
}
await this.removedVaultUserService.deleteRemovedVaultUser({
@@ -3,11 +3,7 @@ import { VaultUserPermission } from '../Model/VaultUserPermission'
import { GetVaultUsersDTO } from './GetVaultUsersDTO'
export interface VaultUserServiceInterface {
addVaultUser(dto: {
vaultUuid: string
userUuid: string
permissions: VaultUserPermission
}): Promise<VaultUser | null>
addVaultUser(dto: { vaultUuid: string; userUuid: string; permissions: VaultUserPermission }): Promise<VaultUser>
getAllVaultUsersForUser(dto: GetVaultUsersDTO): Promise<VaultUser[]>
@@ -2,7 +2,6 @@ export type VaultProjection = {
uuid: string
user_uuid: string
specified_items_key_uuid: string
vault_key_timestamp: number
file_upload_bytes_used: number
file_upload_bytes_limit: number
created_at_timestamp: number
@@ -21,7 +21,6 @@ export class VaultProjector implements ProjectorInterface<Vault, VaultProjection
uuid: vault.uuid,
user_uuid: vault.userUuid,
specified_items_key_uuid: vault.specifiedItemsKeyUuid,
vault_key_timestamp: vault.vaultKeyTimestamp,
file_upload_bytes_used: vault.fileUploadBytesUsed,
file_upload_bytes_limit: vault.fileUploadBytesLimit,
created_at_timestamp: vault.createdAtTimestamp,