mirror of
https://github.com/standardnotes/server
synced 2026-01-21 17:04:28 -05:00
Compare commits
9 Commits
@standardn
...
@standardn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09e1a892ca | ||
|
|
7b1eec21e5 | ||
|
|
a58262d584 | ||
|
|
a8f03e157b | ||
|
|
a401962bcd | ||
|
|
9759814f63 | ||
|
|
c7cf53722c | ||
|
|
8cb33dc906 | ||
|
|
1d73e4f072 |
8
.pnp.cjs
generated
8
.pnp.cjs
generated
@@ -6156,6 +6156,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@types/cors", "npm:2.8.13"],\
|
||||
["@types/dotenv", "npm:8.2.0"],\
|
||||
["@types/express", "npm:4.17.17"],\
|
||||
["@types/ioredis", "npm:5.0.0"],\
|
||||
["@types/jest", "npm:29.5.2"],\
|
||||
["@types/newrelic", "npm:9.14.0"],\
|
||||
["@types/node", "npm:20.5.7"],\
|
||||
@@ -6168,6 +6169,7 @@ const RAW_RUNTIME_STATE =
|
||||
["express", "npm:4.18.2"],\
|
||||
["inversify", "npm:6.0.1"],\
|
||||
["inversify-express-utils", "npm:6.4.3"],\
|
||||
["ioredis", "npm:5.3.2"],\
|
||||
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.5.0"],\
|
||||
["mongodb", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:6.0.0"],\
|
||||
["mysql2", "npm:3.3.3"],\
|
||||
@@ -6340,6 +6342,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@types/cors", "npm:2.8.13"],\
|
||||
["@types/dotenv", "npm:8.2.0"],\
|
||||
["@types/express", "npm:4.17.17"],\
|
||||
["@types/ioredis", "npm:5.0.0"],\
|
||||
["@types/jest", "npm:29.5.2"],\
|
||||
["@types/jsonwebtoken", "npm:9.0.2"],\
|
||||
["@types/newrelic", "npm:9.14.0"],\
|
||||
@@ -6359,6 +6362,7 @@ const RAW_RUNTIME_STATE =
|
||||
["helmet", "npm:7.0.0"],\
|
||||
["inversify", "npm:6.0.1"],\
|
||||
["inversify-express-utils", "npm:6.4.3"],\
|
||||
["ioredis", "npm:5.3.2"],\
|
||||
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.5.0"],\
|
||||
["jsonwebtoken", "npm:9.0.0"],\
|
||||
["mongodb", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:6.0.0"],\
|
||||
@@ -16772,7 +16776,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@types/better-sqlite3", null],\
|
||||
["@types/google-cloud__spanner", null],\
|
||||
["@types/hdb-pool", null],\
|
||||
["@types/ioredis", null],\
|
||||
["@types/ioredis", "npm:5.0.0"],\
|
||||
["@types/mongodb", null],\
|
||||
["@types/mssql", null],\
|
||||
["@types/mysql2", null],\
|
||||
@@ -16796,7 +16800,7 @@ const RAW_RUNTIME_STATE =
|
||||
["dotenv", "npm:16.1.3"],\
|
||||
["glob", "npm:8.1.0"],\
|
||||
["hdb-pool", null],\
|
||||
["ioredis", null],\
|
||||
["ioredis", "npm:5.3.2"],\
|
||||
["mkdirp", "npm:2.1.6"],\
|
||||
["mongodb", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:6.0.0"],\
|
||||
["mssql", null],\
|
||||
|
||||
@@ -139,6 +139,11 @@ LINKING_RESULT=$(link_queue_and_topic $AUTH_TOPIC_ARN $FILES_QUEUE_ARN)
|
||||
echo "linking done:"
|
||||
echo "$LINKING_RESULT"
|
||||
|
||||
echo "linking topic $SYNCING_SERVER_TOPIC_ARN to queue $FILES_QUEUE_ARN"
|
||||
LINKING_RESULT=$(link_queue_and_topic $SYNCING_SERVER_TOPIC_ARN $FILES_QUEUE_ARN)
|
||||
echo "linking done:"
|
||||
echo "$LINKING_RESULT"
|
||||
|
||||
QUEUE_NAME="syncing-server-local-queue"
|
||||
|
||||
echo "creating queue $QUEUE_NAME"
|
||||
|
||||
@@ -3,8 +3,6 @@ module.exports = {
|
||||
testEnvironment: 'node',
|
||||
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.ts$',
|
||||
testTimeout: 20000,
|
||||
coverageReporters: ['text'],
|
||||
reporters: ['summary'],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 100,
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.26.22](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.21...@standardnotes/analytics@2.26.22) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.26.21](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.20...@standardnotes/analytics@2.26.21) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.26.20](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.19...@standardnotes/analytics@2.26.20) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "2.26.20",
|
||||
"version": "2.26.22",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.75.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.1...@standardnotes/api-gateway@1.75.2) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.75.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.0...@standardnotes/api-gateway@1.75.1) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
# [1.75.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.17...@standardnotes/api-gateway@1.75.0) (2023-09-21)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.75.0",
|
||||
"version": "1.75.2",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.146.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.146.1...@standardnotes/auth-server@1.146.2) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.146.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.146.0...@standardnotes/auth-server@1.146.1) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
# [1.146.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.145.0...@standardnotes/auth-server@1.146.0) (2023-09-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/auth-server",
|
||||
"version": "1.146.0",
|
||||
"version": "1.146.2",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.12.34](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.33...@standardnotes/domain-events-infra@1.12.34) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.12.33](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.32...@standardnotes/domain-events-infra@1.12.33) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.12.32](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.31...@standardnotes/domain-events-infra@1.12.32) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events-infra",
|
||||
"version": "1.12.32",
|
||||
"version": "1.12.34",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.130.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.129.1...@standardnotes/domain-events@2.130.0) (2023-09-25)
|
||||
|
||||
### Features
|
||||
|
||||
* remove shared vault files upon shared vault removal ([#852](https://github.com/standardnotes/server/issues/852)) ([7b1eec2](https://github.com/standardnotes/server/commit/7b1eec21e54330bebbeebb80cec3ba4284112aab))
|
||||
|
||||
## [2.129.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.129.0...@standardnotes/domain-events@2.129.1) (2023-09-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remember paging progress on transitioning ([1d73e4f](https://github.com/standardnotes/server/commit/1d73e4f0720d41029af4d4b2b7a10d101add6c82))
|
||||
|
||||
# [2.129.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.128.0...@standardnotes/domain-events@2.129.0) (2023-09-21)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events",
|
||||
"version": "2.129.0",
|
||||
"version": "2.130.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export interface SharedVaultRemovedEventPayload {
|
||||
sharedVaultUuid: string
|
||||
vaultOwnerUuid: string
|
||||
}
|
||||
|
||||
@@ -3,4 +3,5 @@ export interface TransitionStatusUpdatedEventPayload {
|
||||
transitionType: 'items' | 'revisions'
|
||||
transitionTimestamp: number
|
||||
status: string
|
||||
page?: number
|
||||
}
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.11.50](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.49...@standardnotes/event-store@1.11.50) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.49](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.48...@standardnotes/event-store@1.11.49) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.48](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.47...@standardnotes/event-store@1.11.48) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/event-store",
|
||||
"version": "1.11.48",
|
||||
"version": "1.11.50",
|
||||
"description": "Event Store Service",
|
||||
"private": true,
|
||||
"main": "dist/src/index.js",
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.23.0](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.28...@standardnotes/files-server@1.23.0) (2023-09-25)
|
||||
|
||||
### Features
|
||||
|
||||
* remove shared vault files upon shared vault removal ([#852](https://github.com/standardnotes/files/issues/852)) ([7b1eec2](https://github.com/standardnotes/files/commit/7b1eec21e54330bebbeebb80cec3ba4284112aab))
|
||||
|
||||
## [1.22.28](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.27...@standardnotes/files-server@1.22.28) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.22.27](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.26...@standardnotes/files-server@1.22.27) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files-server",
|
||||
"version": "1.22.27",
|
||||
"version": "1.23.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import {
|
||||
AccountDeletionRequestedEvent,
|
||||
AccountDeletionRequestedEventPayload,
|
||||
DomainEventPublisherInterface,
|
||||
FileRemovedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { MarkFilesToBeRemoved } from '../UseCase/MarkFilesToBeRemoved/MarkFilesToBeRemoved'
|
||||
|
||||
import { AccountDeletionRequestedEventHandler } from './AccountDeletionRequestedEventHandler'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { RemovedFileDescription } from '../File/RemovedFileDescription'
|
||||
|
||||
describe('AccountDeletionRequestedEventHandler', () => {
|
||||
let markFilesToBeRemoved: MarkFilesToBeRemoved
|
||||
let event: AccountDeletionRequestedEvent
|
||||
let domainEventPublisher: DomainEventPublisherInterface
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
|
||||
const createHandler = () =>
|
||||
new AccountDeletionRequestedEventHandler(markFilesToBeRemoved, domainEventPublisher, domainEventFactory)
|
||||
|
||||
beforeEach(() => {
|
||||
markFilesToBeRemoved = {} as jest.Mocked<MarkFilesToBeRemoved>
|
||||
markFilesToBeRemoved.execute = jest.fn().mockReturnValue({
|
||||
success: true,
|
||||
filesRemoved: [{} as jest.Mocked<RemovedFileDescription>],
|
||||
})
|
||||
|
||||
event = {} as jest.Mocked<AccountDeletionRequestedEvent>
|
||||
event.payload = {
|
||||
userUuid: '1-2-3',
|
||||
regularSubscriptionUuid: '1-2-3',
|
||||
} as jest.Mocked<AccountDeletionRequestedEventPayload>
|
||||
|
||||
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
|
||||
domainEventPublisher.publish = jest.fn()
|
||||
|
||||
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
|
||||
domainEventFactory.createFileRemovedEvent = jest.fn().mockReturnValue({} as jest.Mocked<FileRemovedEvent>)
|
||||
})
|
||||
|
||||
it('should mark files to be remove for user', async () => {
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(markFilesToBeRemoved.execute).toHaveBeenCalledWith({ ownerUuid: '1-2-3' })
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not mark files to be remove for user if user has no regular subscription', async () => {
|
||||
event.payload.regularSubscriptionUuid = undefined
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(markFilesToBeRemoved.execute).not.toHaveBeenCalled()
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not publish events if failed to mark files to be removed', async () => {
|
||||
markFilesToBeRemoved.execute = jest.fn().mockReturnValue({
|
||||
success: false,
|
||||
})
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(markFilesToBeRemoved.execute).toHaveBeenCalledWith({ ownerUuid: '1-2-3' })
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -22,15 +22,17 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
|
||||
return
|
||||
}
|
||||
|
||||
const response = await this.markFilesToBeRemoved.execute({
|
||||
const result = await this.markFilesToBeRemoved.execute({
|
||||
ownerUuid: event.payload.userUuid,
|
||||
})
|
||||
|
||||
if (!response.success) {
|
||||
if (result.isFailed()) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const fileRemoved of response.filesRemoved) {
|
||||
const filesRemoved = result.getValue()
|
||||
|
||||
for (const fileRemoved of filesRemoved) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createFileRemovedEvent({
|
||||
regularSubscriptionUuid: event.payload.regularSubscriptionUuid,
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import {
|
||||
SharedSubscriptionInvitationCanceledEvent,
|
||||
SharedSubscriptionInvitationCanceledEventPayload,
|
||||
DomainEventPublisherInterface,
|
||||
FileRemovedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { MarkFilesToBeRemoved } from '../UseCase/MarkFilesToBeRemoved/MarkFilesToBeRemoved'
|
||||
|
||||
import { SharedSubscriptionInvitationCanceledEventHandler } from './SharedSubscriptionInvitationCanceledEventHandler'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { RemovedFileDescription } from '../File/RemovedFileDescription'
|
||||
|
||||
describe('SharedSubscriptionInvitationCanceledEventHandler', () => {
|
||||
let markFilesToBeRemoved: MarkFilesToBeRemoved
|
||||
let event: SharedSubscriptionInvitationCanceledEvent
|
||||
let domainEventPublisher: DomainEventPublisherInterface
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
|
||||
const createHandler = () =>
|
||||
new SharedSubscriptionInvitationCanceledEventHandler(markFilesToBeRemoved, domainEventPublisher, domainEventFactory)
|
||||
|
||||
beforeEach(() => {
|
||||
markFilesToBeRemoved = {} as jest.Mocked<MarkFilesToBeRemoved>
|
||||
markFilesToBeRemoved.execute = jest.fn().mockReturnValue({
|
||||
success: true,
|
||||
filesRemoved: [{} as jest.Mocked<RemovedFileDescription>],
|
||||
})
|
||||
|
||||
event = {} as jest.Mocked<SharedSubscriptionInvitationCanceledEvent>
|
||||
event.payload = {
|
||||
inviteeIdentifier: '1-2-3',
|
||||
inviteeIdentifierType: 'uuid',
|
||||
} as jest.Mocked<SharedSubscriptionInvitationCanceledEventPayload>
|
||||
|
||||
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
|
||||
domainEventPublisher.publish = jest.fn()
|
||||
|
||||
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
|
||||
domainEventFactory.createFileRemovedEvent = jest.fn().mockReturnValue({} as jest.Mocked<FileRemovedEvent>)
|
||||
})
|
||||
|
||||
it('should mark files to be remove for user', async () => {
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(markFilesToBeRemoved.execute).toHaveBeenCalledWith({ ownerUuid: '1-2-3' })
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not mark files to be remove for user if identifier is not of uuid type', async () => {
|
||||
event.payload.inviteeIdentifierType = 'email'
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(markFilesToBeRemoved.execute).not.toHaveBeenCalled()
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not publish events if failed to mark files to be removed', async () => {
|
||||
markFilesToBeRemoved.execute = jest.fn().mockReturnValue({
|
||||
success: false,
|
||||
})
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(markFilesToBeRemoved.execute).toHaveBeenCalledWith({ ownerUuid: '1-2-3' })
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -22,15 +22,17 @@ export class SharedSubscriptionInvitationCanceledEventHandler implements DomainE
|
||||
return
|
||||
}
|
||||
|
||||
const response = await this.markFilesToBeRemoved.execute({
|
||||
const result = await this.markFilesToBeRemoved.execute({
|
||||
ownerUuid: event.payload.inviteeIdentifier,
|
||||
})
|
||||
|
||||
if (!response.success) {
|
||||
if (result.isFailed()) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const fileRemoved of response.filesRemoved) {
|
||||
const filesRemoved = result.getValue()
|
||||
|
||||
for (const fileRemoved of filesRemoved) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createFileRemovedEvent({
|
||||
regularSubscriptionUuid: event.payload.inviterSubscriptionUuid,
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import {
|
||||
DomainEventHandlerInterface,
|
||||
DomainEventPublisherInterface,
|
||||
SharedVaultRemovedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { MarkFilesToBeRemoved } from '../UseCase/MarkFilesToBeRemoved/MarkFilesToBeRemoved'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
|
||||
export class SharedVaultRemovedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
private markFilesToBeRemoved: MarkFilesToBeRemoved,
|
||||
private domainEventPublisher: DomainEventPublisherInterface,
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: SharedVaultRemovedEvent): Promise<void> {
|
||||
const result = await this.markFilesToBeRemoved.execute({
|
||||
ownerUuid: event.payload.sharedVaultUuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(
|
||||
`Could not mark files to be removed for shared vault: ${event.payload.sharedVaultUuid}: ${result.getError()}`,
|
||||
)
|
||||
}
|
||||
|
||||
const filesRemoved = result.getValue()
|
||||
|
||||
for (const fileRemoved of filesRemoved) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createSharedVaultFileRemovedEvent({
|
||||
fileByteSize: fileRemoved.fileByteSize,
|
||||
fileName: fileRemoved.fileName,
|
||||
filePath: fileRemoved.filePath,
|
||||
sharedVaultUuid: event.payload.sharedVaultUuid,
|
||||
vaultOwnerUuid: event.payload.vaultOwnerUuid,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,9 @@ describe('MarkFilesToBeRemoved', () => {
|
||||
})
|
||||
|
||||
it('should mark files for being removed', async () => {
|
||||
expect(await createUseCase().execute({ ownerUuid: '1-2-3' })).toEqual({ success: true })
|
||||
const result = await createUseCase().execute({ ownerUuid: '1-2-3' })
|
||||
|
||||
expect(result.isFailed()).toEqual(false)
|
||||
|
||||
expect(fileRemover.markFilesToBeRemoved).toHaveBeenCalledWith('1-2-3')
|
||||
})
|
||||
@@ -31,9 +33,7 @@ describe('MarkFilesToBeRemoved', () => {
|
||||
throw new Error('Oops')
|
||||
})
|
||||
|
||||
expect(await createUseCase().execute({ ownerUuid: '1-2-3' })).toEqual({
|
||||
success: false,
|
||||
message: 'Could not mark resources for removal',
|
||||
})
|
||||
const result = await createUseCase().execute({ ownerUuid: '1-2-3' })
|
||||
expect(result.isFailed()).toEqual(true)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,36 +1,30 @@
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
|
||||
|
||||
import TYPES from '../../../Bootstrap/Types'
|
||||
import { FileRemoverInterface } from '../../Services/FileRemoverInterface'
|
||||
import { UseCaseInterface } from '../UseCaseInterface'
|
||||
import { MarkFilesToBeRemovedDTO } from './MarkFilesToBeRemovedDTO'
|
||||
import { MarkFilesToBeRemovedResponse } from './MarkFilesToBeRemovedResponse'
|
||||
import { RemovedFileDescription } from '../../File/RemovedFileDescription'
|
||||
|
||||
@injectable()
|
||||
export class MarkFilesToBeRemoved implements UseCaseInterface {
|
||||
export class MarkFilesToBeRemoved implements UseCaseInterface<RemovedFileDescription[]> {
|
||||
constructor(
|
||||
@inject(TYPES.Files_FileRemover) private fileRemover: FileRemoverInterface,
|
||||
@inject(TYPES.Files_Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
async execute(dto: MarkFilesToBeRemovedDTO): Promise<MarkFilesToBeRemovedResponse> {
|
||||
async execute(dto: MarkFilesToBeRemovedDTO): Promise<Result<RemovedFileDescription[]>> {
|
||||
try {
|
||||
this.logger.debug(`Marking files for later removal for user: ${dto.ownerUuid}`)
|
||||
|
||||
const filesRemoved = await this.fileRemover.markFilesToBeRemoved(dto.ownerUuid)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
filesRemoved,
|
||||
}
|
||||
return Result.ok(filesRemoved)
|
||||
} catch (error) {
|
||||
this.logger.error(`Could not mark resources for removal: ${dto.ownerUuid} - ${(error as Error).message}`)
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: 'Could not mark resources for removal',
|
||||
}
|
||||
return Result.fail('Could not mark resources for removal')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.16.4](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.3...@standardnotes/home-server@1.16.4) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.16.3](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.2...@standardnotes/home-server@1.16.3) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.16.2](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.1...@standardnotes/home-server@1.16.2) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.16.1](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.0...@standardnotes/home-server@1.16.1) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
# [1.16.0](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.80...@standardnotes/home-server@1.16.0) (2023-09-22)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/home-server",
|
||||
"version": "1.16.0",
|
||||
"version": "1.16.4",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.37.1](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.37.0...@standardnotes/revisions-server@1.37.1) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
# [1.37.0](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.36.7...@standardnotes/revisions-server@1.37.0) (2023-09-25)
|
||||
|
||||
### Features
|
||||
|
||||
* add storing paging progress in redis ([9759814](https://github.com/standardnotes/server/commit/9759814f637b8ae25b325e35bc7f5159747980b6))
|
||||
|
||||
## [1.36.7](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.36.6...@standardnotes/revisions-server@1.36.7) (2023-09-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remember paging progress on transitioning ([1d73e4f](https://github.com/standardnotes/server/commit/1d73e4f0720d41029af4d4b2b7a10d101add6c82))
|
||||
|
||||
## [1.36.6](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.36.5...@standardnotes/revisions-server@1.36.6) (2023-09-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/revisions-server",
|
||||
"version": "1.36.6",
|
||||
"version": "1.37.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -41,6 +41,7 @@
|
||||
"express": "^4.18.2",
|
||||
"inversify": "^6.0.1",
|
||||
"inversify-express-utils": "^6.4.3",
|
||||
"ioredis": "^5.3.2",
|
||||
"mongodb": "^6.0.0",
|
||||
"mysql2": "^3.0.1",
|
||||
"reflect-metadata": "0.1.13",
|
||||
@@ -52,6 +53,7 @@
|
||||
"@types/cors": "^2.8.9",
|
||||
"@types/dotenv": "^8.2.0",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/ioredis": "^5.0.0",
|
||||
"@types/jest": "^29.5.1",
|
||||
"@types/node": "^20.5.7",
|
||||
"@typescript-eslint/eslint-plugin": "^6.5.0",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ControllerContainer, ControllerContainerInterface, MapperInterface } from '@standardnotes/domain-core'
|
||||
import Redis from 'ioredis'
|
||||
import { Container, interfaces } from 'inversify'
|
||||
import { MongoRepository, Repository } from 'typeorm'
|
||||
import * as winston from 'winston'
|
||||
@@ -68,6 +69,8 @@ import { RemoveRevisionsFromSharedVault } from '../Domain/UseCase/RemoveRevision
|
||||
import { ItemRemovedFromSharedVaultEventHandler } from '../Domain/Handler/ItemRemovedFromSharedVaultEventHandler'
|
||||
import { TransitionRequestedEventHandler } from '../Domain/Handler/TransitionRequestedEventHandler'
|
||||
import { SharedVaultRemovedEventHandler } from '../Domain/Handler/SharedVaultRemovedEventHandler'
|
||||
import { TransitionRepositoryInterface } from '../Domain/Transition/TransitionRepositoryInterface'
|
||||
import { RedisTransitionRepository } from '../Infra/Redis/RedisTransitionRepository'
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
constructor(private mode: 'server' | 'worker' = 'server') {}
|
||||
@@ -88,11 +91,28 @@ export class ContainerConfigLoader {
|
||||
const isConfiguredForSelfHosting = env.get('MODE', true) === 'self-hosted'
|
||||
const isConfiguredForHomeServerOrSelfHosting = isConfiguredForHomeServer || isConfiguredForSelfHosting
|
||||
const isSecondaryDatabaseEnabled = env.get('SECONDARY_DB_ENABLED', true) === 'true'
|
||||
const isConfiguredForInMemoryCache = env.get('CACHE_TYPE', true) === 'memory'
|
||||
|
||||
const container = new Container({
|
||||
defaultScope: 'Singleton',
|
||||
})
|
||||
|
||||
if (!isConfiguredForInMemoryCache) {
|
||||
const redisUrl = env.get('REDIS_URL')
|
||||
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
|
||||
let redis
|
||||
if (isRedisInClusterMode) {
|
||||
redis = new Redis.Cluster(redisUrl.split(','))
|
||||
} else {
|
||||
redis = new Redis(redisUrl)
|
||||
}
|
||||
|
||||
container.bind(TYPES.Revisions_Redis).toConstantValue(redis)
|
||||
container
|
||||
.bind<TransitionRepositoryInterface>(TYPES.Revisions_TransitionStatusRepository)
|
||||
.toConstantValue(new RedisTransitionRepository(container.get<Redis>(TYPES.Revisions_Redis)))
|
||||
}
|
||||
|
||||
let logger: winston.Logger
|
||||
if (configuration?.logger) {
|
||||
logger = configuration.logger as winston.Logger
|
||||
@@ -348,6 +368,9 @@ export class ContainerConfigLoader {
|
||||
isSecondaryDatabaseEnabled
|
||||
? container.get<RevisionRepositoryInterface>(TYPES.Revisions_MongoDBRevisionRepository)
|
||||
: null,
|
||||
isConfiguredForInMemoryCache
|
||||
? null
|
||||
: container.get<TransitionRepositoryInterface>(TYPES.Revisions_TransitionStatusRepository),
|
||||
container.get<TimerInterface>(TYPES.Revisions_Timer),
|
||||
container.get<winston.Logger>(TYPES.Revisions_Logger),
|
||||
env.get('MIGRATION_BATCH_SIZE', true) ? +env.get('MIGRATION_BATCH_SIZE', true) : 100,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const TYPES = {
|
||||
Revisions_DBConnection: Symbol.for('Revisions_DBConnection'),
|
||||
Revisions_Logger: Symbol.for('Revisions_Logger'),
|
||||
Revisions_Redis: Symbol.for('Revisions_Redis'),
|
||||
Revisions_SQS: Symbol.for('Revisions_SQS'),
|
||||
Revisions_SNS: Symbol.for('Revisions_SNS'),
|
||||
Revisions_S3: Symbol.for('Revisions_S3'),
|
||||
@@ -27,6 +28,7 @@ const TYPES = {
|
||||
Revisions_MongoDBRevisionRepository: Symbol.for('Revisions_MongoDBRevisionRepository'),
|
||||
Revisions_DumpRepository: Symbol.for('Revisions_DumpRepository'),
|
||||
Revisions_RevisionRepositoryResolver: Symbol.for('Revisions_RevisionRepositoryResolver'),
|
||||
Revisions_TransitionStatusRepository: Symbol.for('Revisions_TransitionStatusRepository'),
|
||||
// env vars
|
||||
Revisions_AUTH_JWT_SECRET: Symbol.for('Revisions_AUTH_JWT_SECRET'),
|
||||
Revisions_SQS_QUEUE_URL: Symbol.for('Revisions_SQS_QUEUE_URL'),
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface TransitionRepositoryInterface {
|
||||
getPagingProgress(userUuid: string): Promise<number>
|
||||
setPagingProgress(userUuid: string, progress: number): Promise<void>
|
||||
}
|
||||
@@ -5,11 +5,13 @@ import { Logger } from 'winston'
|
||||
|
||||
import { TransitionRevisionsFromPrimaryToSecondaryDatabaseForUserDTO } from './TransitionRevisionsFromPrimaryToSecondaryDatabaseForUserDTO'
|
||||
import { RevisionRepositoryInterface } from '../../../Revision/RevisionRepositoryInterface'
|
||||
import { TransitionRepositoryInterface } from '../../../Transition/TransitionRepositoryInterface'
|
||||
|
||||
export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private primaryRevisionsRepository: RevisionRepositoryInterface,
|
||||
private secondRevisionsRepository: RevisionRepositoryInterface | null,
|
||||
private transitionStatusRepository: TransitionRepositoryInterface | null,
|
||||
private timer: TimerInterface,
|
||||
private logger: Logger,
|
||||
private pageSize: number,
|
||||
@@ -22,6 +24,10 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
|
||||
return Result.fail('Secondary revision repository is not set')
|
||||
}
|
||||
|
||||
if (this.transitionStatusRepository === null) {
|
||||
return Result.fail('Transition status repository is not set')
|
||||
}
|
||||
|
||||
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return Result.fail(userUuidOrError.getError())
|
||||
@@ -71,10 +77,21 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
|
||||
|
||||
private async migrateRevisionsForUser(userUuid: Uuid): Promise<Result<string[]>> {
|
||||
try {
|
||||
const initialPage = await (this.transitionStatusRepository as TransitionRepositoryInterface).getPagingProgress(
|
||||
userUuid.value,
|
||||
)
|
||||
|
||||
this.logger.info(`[${userUuid.value}] Migrating from page ${initialPage}`)
|
||||
|
||||
const totalRevisionsCountForUser = await this.primaryRevisionsRepository.countByUserUuid(userUuid)
|
||||
const totalPages = Math.ceil(totalRevisionsCountForUser / this.pageSize)
|
||||
const revisionsToSkipInIntegrityCheck = []
|
||||
for (let currentPage = 1; currentPage <= totalPages; currentPage++) {
|
||||
for (let currentPage = initialPage; currentPage <= totalPages; currentPage++) {
|
||||
await (this.transitionStatusRepository as TransitionRepositoryInterface).setPagingProgress(
|
||||
userUuid.value,
|
||||
currentPage,
|
||||
)
|
||||
|
||||
const query = {
|
||||
userUuid: userUuid,
|
||||
offset: (currentPage - 1) * this.pageSize,
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import * as IORedis from 'ioredis'
|
||||
|
||||
import { TransitionRepositoryInterface } from '../../Domain/Transition/TransitionRepositoryInterface'
|
||||
|
||||
export class RedisTransitionRepository implements TransitionRepositoryInterface {
|
||||
private readonly PREFIX = 'transition-revisions-paging-progress'
|
||||
|
||||
constructor(private redisClient: IORedis.Redis) {}
|
||||
|
||||
async getPagingProgress(userUuid: string): Promise<number> {
|
||||
const progress = await this.redisClient.get(`${this.PREFIX}:${userUuid}`)
|
||||
|
||||
if (progress === null) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return parseInt(progress)
|
||||
}
|
||||
|
||||
async setPagingProgress(userUuid: string, progress: number): Promise<void> {
|
||||
await this.redisClient.setex(`${this.PREFIX}:${userUuid}`, 172_800, progress.toString())
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.20.54](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.53...@standardnotes/scheduler-server@1.20.54) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.20.53](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.52...@standardnotes/scheduler-server@1.20.53) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.20.52](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.51...@standardnotes/scheduler-server@1.20.52) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/scheduler-server",
|
||||
"version": "1.20.52",
|
||||
"version": "1.20.54",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,31 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.108.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.107.0...@standardnotes/syncing-server@1.108.0) (2023-09-25)
|
||||
|
||||
### Features
|
||||
|
||||
* remove shared vault files upon shared vault removal ([#852](https://github.com/standardnotes/syncing-server-js/issues/852)) ([7b1eec2](https://github.com/standardnotes/syncing-server-js/commit/7b1eec21e54330bebbeebb80cec3ba4284112aab))
|
||||
|
||||
# [1.107.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.106.0...@standardnotes/syncing-server@1.107.0) (2023-09-25)
|
||||
|
||||
### Features
|
||||
|
||||
* **syncing-server:** transfer shared vault items ([#851](https://github.com/standardnotes/syncing-server-js/issues/851)) ([a8f03e1](https://github.com/standardnotes/syncing-server-js/commit/a8f03e157be3d277e60d2756dd25c953775b1ba4))
|
||||
|
||||
# [1.106.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.105.1...@standardnotes/syncing-server@1.106.0) (2023-09-25)
|
||||
|
||||
### Features
|
||||
|
||||
* add storing paging progress in redis ([9759814](https://github.com/standardnotes/syncing-server-js/commit/9759814f637b8ae25b325e35bc7f5159747980b6))
|
||||
|
||||
## [1.105.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.105.0...@standardnotes/syncing-server@1.105.1) (2023-09-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add paging progress log ([8cb33dc](https://github.com/standardnotes/syncing-server-js/commit/8cb33dc906391ee8b1ebd333937045c328e4fc06))
|
||||
* remember paging progress on transitioning ([1d73e4f](https://github.com/standardnotes/syncing-server-js/commit/1d73e4f0720d41029af4d4b2b7a10d101add6c82))
|
||||
|
||||
# [1.105.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.104.0...@standardnotes/syncing-server@1.105.0) (2023-09-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/syncing-server",
|
||||
"version": "1.105.0",
|
||||
"version": "1.108.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -47,6 +47,7 @@
|
||||
"helmet": "^7.0.0",
|
||||
"inversify": "^6.0.1",
|
||||
"inversify-express-utils": "^6.4.3",
|
||||
"ioredis": "^5.3.2",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"mongodb": "^6.0.0",
|
||||
"mysql2": "^3.0.1",
|
||||
@@ -63,6 +64,7 @@
|
||||
"@types/cors": "^2.8.9",
|
||||
"@types/dotenv": "^8.2.0",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/ioredis": "^5.0.0",
|
||||
"@types/jest": "^29.5.1",
|
||||
"@types/jsonwebtoken": "^9.0.1",
|
||||
"@types/node": "^20.5.7",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as winston from 'winston'
|
||||
import Redis from 'ioredis'
|
||||
import { Container, interfaces } from 'inversify'
|
||||
|
||||
import { Env } from './Env'
|
||||
@@ -171,6 +172,9 @@ import { SharedVaultRemovedEventHandler } from '../Domain/Handler/SharedVaultRem
|
||||
import { DesignateSurvivor } from '../Domain/UseCase/SharedVaults/DesignateSurvivor/DesignateSurvivor'
|
||||
import { RemoveUserFromSharedVaults } from '../Domain/UseCase/SharedVaults/RemoveUserFromSharedVaults/RemoveUserFromSharedVaults'
|
||||
import { TransferSharedVault } from '../Domain/UseCase/SharedVaults/TransferSharedVault/TransferSharedVault'
|
||||
import { TransitionRepositoryInterface } from '../Domain/Transition/TransitionRepositoryInterface'
|
||||
import { RedisTransitionRepository } from '../Infra/Redis/RedisTransitionRepository'
|
||||
import { TransferSharedVaultItems } from '../Domain/UseCase/SharedVaults/TransferSharedVaultItems/TransferSharedVaultItems'
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
private readonly DEFAULT_CONTENT_SIZE_TRANSFER_LIMIT = 10_000_000
|
||||
@@ -228,6 +232,23 @@ export class ContainerConfigLoader {
|
||||
const isConfiguredForSelfHosting = env.get('MODE', true) === 'self-hosted'
|
||||
const isConfiguredForHomeServerOrSelfHosting = isConfiguredForHomeServer || isConfiguredForSelfHosting
|
||||
const isSecondaryDatabaseEnabled = env.get('SECONDARY_DB_ENABLED', true) === 'true'
|
||||
const isConfiguredForInMemoryCache = env.get('CACHE_TYPE', true) === 'memory'
|
||||
|
||||
if (!isConfiguredForInMemoryCache) {
|
||||
const redisUrl = env.get('REDIS_URL')
|
||||
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
|
||||
let redis
|
||||
if (isRedisInClusterMode) {
|
||||
redis = new Redis.Cluster(redisUrl.split(','))
|
||||
} else {
|
||||
redis = new Redis(redisUrl)
|
||||
}
|
||||
|
||||
container.bind(TYPES.Sync_Redis).toConstantValue(redis)
|
||||
container
|
||||
.bind<TransitionRepositoryInterface>(TYPES.Sync_TransitionStatusRepository)
|
||||
.toConstantValue(new RedisTransitionRepository(container.get<Redis>(TYPES.Sync_Redis)))
|
||||
}
|
||||
|
||||
container.bind<Env>(TYPES.Sync_Env).toConstantValue(env)
|
||||
|
||||
@@ -833,6 +854,9 @@ export class ContainerConfigLoader {
|
||||
new TransitionItemsFromPrimaryToSecondaryDatabaseForUser(
|
||||
container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
|
||||
isSecondaryDatabaseEnabled ? container.get<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository) : null,
|
||||
isConfiguredForInMemoryCache
|
||||
? null
|
||||
: container.get<TransitionRepositoryInterface>(TYPES.Sync_TransitionStatusRepository),
|
||||
container.get<TimerInterface>(TYPES.Sync_Timer),
|
||||
container.get<Logger>(TYPES.Sync_Logger),
|
||||
env.get('MIGRATION_BATCH_SIZE', true) ? +env.get('MIGRATION_BATCH_SIZE', true) : 100,
|
||||
@@ -867,12 +891,22 @@ export class ContainerConfigLoader {
|
||||
container.get<Logger>(TYPES.Sync_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<TransferSharedVaultItems>(TYPES.Sync_TransferSharedVaultItems)
|
||||
.toConstantValue(
|
||||
new TransferSharedVaultItems(
|
||||
isSecondaryDatabaseEnabled
|
||||
? container.get<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository)
|
||||
: container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<TransferSharedVault>(TYPES.Sync_TransferSharedVault)
|
||||
.toConstantValue(
|
||||
new TransferSharedVault(
|
||||
container.get<SharedVaultRepositoryInterface>(TYPES.Sync_SharedVaultRepository),
|
||||
container.get<SharedVaultUserRepositoryInterface>(TYPES.Sync_SharedVaultUserRepository),
|
||||
container.get<TransferSharedVaultItems>(TYPES.Sync_TransferSharedVaultItems),
|
||||
container.get<TimerInterface>(TYPES.Sync_Timer),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -15,6 +15,7 @@ const TYPES = {
|
||||
Sync_SharedVaultUserRepository: Symbol.for('Sync_SharedVaultUserRepository'),
|
||||
Sync_NotificationRepository: Symbol.for('Sync_NotificationRepository'),
|
||||
Sync_MessageRepository: Symbol.for('Sync_MessageRepository'),
|
||||
Sync_TransitionStatusRepository: Symbol.for('Sync_TransitionStatusRepository'),
|
||||
// ORM
|
||||
Sync_ORMItemRepository: Symbol.for('Sync_ORMItemRepository'),
|
||||
Sync_ORMLegacyItemRepository: Symbol.for('Sync_ORMLegacyItemRepository'),
|
||||
@@ -90,6 +91,7 @@ const TYPES = {
|
||||
Sync_DesignateSurvivor: Symbol.for('Sync_DesignateSurvivor'),
|
||||
Sync_RemoveUserFromSharedVaults: Symbol.for('Sync_RemoveUserFromSharedVaults'),
|
||||
Sync_TransferSharedVault: Symbol.for('Sync_TransferSharedVault'),
|
||||
Sync_TransferSharedVaultItems: Symbol.for('Sync_TransferSharedVaultItems'),
|
||||
// Handlers
|
||||
Sync_AccountDeletionRequestedEventHandler: Symbol.for('Sync_AccountDeletionRequestedEventHandler'),
|
||||
Sync_DuplicateItemSyncedEventHandler: Symbol.for('Sync_DuplicateItemSyncedEventHandler'),
|
||||
|
||||
@@ -42,7 +42,7 @@ export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||
}
|
||||
}
|
||||
|
||||
createSharedVaultRemovedEvent(dto: { sharedVaultUuid: string }): SharedVaultRemovedEvent {
|
||||
createSharedVaultRemovedEvent(dto: { sharedVaultUuid: string; vaultOwnerUuid: string }): SharedVaultRemovedEvent {
|
||||
return {
|
||||
type: 'SHARED_VAULT_REMOVED',
|
||||
createdAt: this.timer.getUTCDate(),
|
||||
|
||||
@@ -102,7 +102,7 @@ export interface DomainEventFactoryInterface {
|
||||
itemUuid: string
|
||||
userUuid: string
|
||||
}): ItemRemovedFromSharedVaultEvent
|
||||
createSharedVaultRemovedEvent(dto: { sharedVaultUuid: string }): SharedVaultRemovedEvent
|
||||
createSharedVaultRemovedEvent(dto: { sharedVaultUuid: string; vaultOwnerUuid: string }): SharedVaultRemovedEvent
|
||||
createUserDesignatedAsSurvivorInSharedVaultEvent(dto: {
|
||||
sharedVaultUuid: string
|
||||
userUuid: string
|
||||
|
||||
@@ -20,4 +20,5 @@ export interface ItemRepositoryInterface {
|
||||
markItemsAsDeleted(itemUuids: Array<string>, updatedAtTimestamp: number): Promise<void>
|
||||
updateContentSize(itemUuid: string, contentSize: number): Promise<void>
|
||||
unassignFromSharedVault(sharedVaultUuid: Uuid): Promise<void>
|
||||
updateSharedVaultOwner(dto: { sharedVaultUuid: Uuid; fromOwnerUuid: Uuid; toOwnerUuid: Uuid }): Promise<void>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface TransitionRepositoryInterface {
|
||||
getPagingProgress(userUuid: string): Promise<number>
|
||||
setPagingProgress(userUuid: string, progress: number): Promise<void>
|
||||
}
|
||||
@@ -101,6 +101,7 @@ export class DeleteSharedVault implements UseCaseInterface<void> {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createSharedVaultRemovedEvent({
|
||||
sharedVaultUuid: sharedVaultUuid.value,
|
||||
vaultOwnerUuid: sharedVault.props.userUuid.value,
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { SharedVaultUser, SharedVaultUserPermission, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
import { Result, SharedVaultUser, SharedVaultUserPermission, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultRepositoryInterface } from '../../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { TransferSharedVault } from './TransferSharedVault'
|
||||
import { SharedVault } from '../../../SharedVault/SharedVault'
|
||||
import { TransferSharedVaultItems } from '../TransferSharedVaultItems/TransferSharedVaultItems'
|
||||
|
||||
describe('TransferSharedVault', () => {
|
||||
let sharedVault: SharedVault
|
||||
@@ -12,8 +13,10 @@ describe('TransferSharedVault', () => {
|
||||
let sharedVaultRepository: SharedVaultRepositoryInterface
|
||||
let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
|
||||
let timer: TimerInterface
|
||||
let transferSharedVaultItems: TransferSharedVaultItems
|
||||
|
||||
const createUseCase = () => new TransferSharedVault(sharedVaultRepository, sharedVaultUserRepository, timer)
|
||||
const createUseCase = () =>
|
||||
new TransferSharedVault(sharedVaultRepository, sharedVaultUserRepository, transferSharedVaultItems, timer)
|
||||
|
||||
beforeEach(() => {
|
||||
sharedVault = SharedVault.create({
|
||||
@@ -40,6 +43,9 @@ describe('TransferSharedVault', () => {
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(123)
|
||||
|
||||
transferSharedVaultItems = {} as jest.Mocked<TransferSharedVaultItems>
|
||||
transferSharedVaultItems.execute = jest.fn().mockResolvedValue(Result.ok())
|
||||
})
|
||||
|
||||
it('should transfer shared vault to another user', async () => {
|
||||
@@ -145,4 +151,20 @@ describe('TransferSharedVault', () => {
|
||||
expect(sharedVaultRepository.save).not.toHaveBeenCalled()
|
||||
expect(sharedVaultUserRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fail if transfering items fails', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
transferSharedVaultItems.execute = jest.fn().mockResolvedValue(Result.fail('error'))
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUid: '00000000-0000-0000-0000-000000000000',
|
||||
fromUserUuid: '00000000-0000-0000-0000-000000000000',
|
||||
toUserUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(sharedVaultRepository.save).not.toHaveBeenCalled()
|
||||
expect(sharedVaultUserRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,11 +4,13 @@ import { TimerInterface } from '@standardnotes/time'
|
||||
import { SharedVaultRepositoryInterface } from '../../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { TransferSharedVaultDTO } from './TransferSharedVaultDTO'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { TransferSharedVaultItems } from '../TransferSharedVaultItems/TransferSharedVaultItems'
|
||||
|
||||
export class TransferSharedVault implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private sharedVaultRepository: SharedVaultRepositoryInterface,
|
||||
private sharedVaultUserRepository: SharedVaultUserRepositoryInterface,
|
||||
private transferSharedVaultItems: TransferSharedVaultItems,
|
||||
private timer: TimerInterface,
|
||||
) {}
|
||||
|
||||
@@ -48,6 +50,15 @@ export class TransferSharedVault implements UseCaseInterface<void> {
|
||||
return Result.fail('New owner is not a member of this shared vault')
|
||||
}
|
||||
|
||||
const transferingItemsResult = await this.transferSharedVaultItems.execute({
|
||||
fromUserUuid: fromUserUuid.value,
|
||||
toUserUuid: toUserUuid.value,
|
||||
sharedVaultUuid: sharedVaultUuid.value,
|
||||
})
|
||||
if (transferingItemsResult.isFailed()) {
|
||||
return Result.fail(`Could not transfer items: ${transferingItemsResult.getError()}`)
|
||||
}
|
||||
|
||||
newOwner.props.isDesignatedSurvivor = false
|
||||
newOwner.props.permission = SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Admin).getValue()
|
||||
newOwner.props.timestamps = Timestamps.create(
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { ItemRepositoryInterface } from '../../../Item/ItemRepositoryInterface'
|
||||
import { TransferSharedVaultItems } from './TransferSharedVaultItems'
|
||||
|
||||
describe('TransferSharedVaultItems', () => {
|
||||
let itemRepository: ItemRepositoryInterface
|
||||
|
||||
const createUseCase = () => new TransferSharedVaultItems(itemRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
itemRepository = {} as jest.Mocked<ItemRepositoryInterface>
|
||||
itemRepository.updateSharedVaultOwner = jest.fn()
|
||||
})
|
||||
|
||||
it('should update shared vault owner', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
fromUserUuid: '0d1d1c7c-5e3e-4b0b-8b4a-8c5b1f8c5b1f',
|
||||
toUserUuid: '0d1d1c7c-5e3e-4b0b-8b4a-8c5b1f8c5b1a',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
expect(itemRepository.updateSharedVaultOwner).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return error when from user uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
fromUserUuid: 'invalid',
|
||||
toUserUuid: '0d1d1c7c-5e3e-4b0b-8b4a-8c5b1f8c5b1a',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
|
||||
it('should return error when to user uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
fromUserUuid: '0d1d1c7c-5e3e-4b0b-8b4a-8c5b1f8c5b1f',
|
||||
toUserUuid: 'invalid',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
|
||||
it('should return error when shared vault uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
fromUserUuid: '0d1d1c7c-5e3e-4b0b-8b4a-8c5b1f8c5b1f',
|
||||
toUserUuid: '0d1d1c7c-5e3e-4b0b-8b4a-8c5b1f8c5b1a',
|
||||
sharedVaultUuid: 'invalid',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { ItemRepositoryInterface } from '../../../Item/ItemRepositoryInterface'
|
||||
import { TransferSharedVaultItemsDTO } from './TransferSharedVaultItemsDTO'
|
||||
|
||||
export class TransferSharedVaultItems implements UseCaseInterface<void> {
|
||||
constructor(private itemRepository: ItemRepositoryInterface) {}
|
||||
|
||||
async execute(dto: TransferSharedVaultItemsDTO): Promise<Result<void>> {
|
||||
const fromUserUuidOrError = Uuid.create(dto.fromUserUuid)
|
||||
if (fromUserUuidOrError.isFailed()) {
|
||||
return Result.fail(fromUserUuidOrError.getError())
|
||||
}
|
||||
const fromUserUuid = fromUserUuidOrError.getValue()
|
||||
|
||||
const toUserUuidOrError = Uuid.create(dto.toUserUuid)
|
||||
if (toUserUuidOrError.isFailed()) {
|
||||
return Result.fail(toUserUuidOrError.getError())
|
||||
}
|
||||
const toUserUuid = toUserUuidOrError.getValue()
|
||||
|
||||
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
return Result.fail(sharedVaultUuidOrError.getError())
|
||||
}
|
||||
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
|
||||
await this.itemRepository.updateSharedVaultOwner({
|
||||
sharedVaultUuid,
|
||||
fromOwnerUuid: fromUserUuid,
|
||||
toOwnerUuid: toUserUuid,
|
||||
})
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface TransferSharedVaultItemsDTO {
|
||||
sharedVaultUuid: string
|
||||
fromUserUuid: string
|
||||
toUserUuid: string
|
||||
}
|
||||
@@ -6,11 +6,13 @@ import { Logger } from 'winston'
|
||||
import { TransitionItemsFromPrimaryToSecondaryDatabaseForUserDTO } from './TransitionItemsFromPrimaryToSecondaryDatabaseForUserDTO'
|
||||
import { ItemRepositoryInterface } from '../../../Item/ItemRepositoryInterface'
|
||||
import { ItemQuery } from '../../../Item/ItemQuery'
|
||||
import { TransitionRepositoryInterface } from '../../../Transition/TransitionRepositoryInterface'
|
||||
|
||||
export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private primaryItemRepository: ItemRepositoryInterface,
|
||||
private secondaryItemRepository: ItemRepositoryInterface | null,
|
||||
private transitionStatusRepository: TransitionRepositoryInterface | null,
|
||||
private timer: TimerInterface,
|
||||
private logger: Logger,
|
||||
private pageSize: number,
|
||||
@@ -23,6 +25,10 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
|
||||
return Result.fail('Secondary item repository is not set')
|
||||
}
|
||||
|
||||
if (this.transitionStatusRepository === null) {
|
||||
return Result.fail('Transition status repository is not set')
|
||||
}
|
||||
|
||||
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return Result.fail(userUuidOrError.getError())
|
||||
@@ -77,10 +83,21 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
|
||||
|
||||
private async migrateItemsForUser(userUuid: Uuid): Promise<Result<string[]>> {
|
||||
try {
|
||||
const initialPage = await (this.transitionStatusRepository as TransitionRepositoryInterface).getPagingProgress(
|
||||
userUuid.value,
|
||||
)
|
||||
|
||||
this.logger.info(`[${userUuid.value}] Migrating from page ${initialPage}`)
|
||||
|
||||
const totalItemsCountForUser = await this.primaryItemRepository.countAll({ userUuid: userUuid.value })
|
||||
const totalPages = Math.ceil(totalItemsCountForUser / this.pageSize)
|
||||
const itemsToSkipInIntegrityCheck = []
|
||||
for (let currentPage = 1; currentPage <= totalPages; currentPage++) {
|
||||
for (let currentPage = initialPage; currentPage <= totalPages; currentPage++) {
|
||||
await (this.transitionStatusRepository as TransitionRepositoryInterface).setPagingProgress(
|
||||
userUuid.value,
|
||||
currentPage,
|
||||
)
|
||||
|
||||
const query: ItemQuery = {
|
||||
userUuid: userUuid.value,
|
||||
offset: (currentPage - 1) * this.pageSize,
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import * as IORedis from 'ioredis'
|
||||
|
||||
import { TransitionRepositoryInterface } from '../../Domain/Transition/TransitionRepositoryInterface'
|
||||
|
||||
export class RedisTransitionRepository implements TransitionRepositoryInterface {
|
||||
private readonly PREFIX = 'transition-items-paging-progress'
|
||||
|
||||
constructor(private redisClient: IORedis.Redis) {}
|
||||
|
||||
async getPagingProgress(userUuid: string): Promise<number> {
|
||||
const progress = await this.redisClient.get(`${this.PREFIX}:${userUuid}`)
|
||||
|
||||
if (progress === null) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return parseInt(progress)
|
||||
}
|
||||
|
||||
async setPagingProgress(userUuid: string, progress: number): Promise<void> {
|
||||
await this.redisClient.setex(`${this.PREFIX}:${userUuid}`, 172_800, progress.toString())
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,15 @@ export class MongoDBItemRepository implements ItemRepositoryInterface {
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async updateSharedVaultOwner(dto: { sharedVaultUuid: Uuid; fromOwnerUuid: Uuid; toOwnerUuid: Uuid }): Promise<void> {
|
||||
await this.mongoRepository.updateMany(
|
||||
{
|
||||
$and: [{ sharedVaultUuid: { $eq: dto.sharedVaultUuid.value } }, { userUuid: { $eq: dto.fromOwnerUuid.value } }],
|
||||
},
|
||||
{ $set: { userUuid: dto.toOwnerUuid.value } },
|
||||
)
|
||||
}
|
||||
|
||||
async unassignFromSharedVault(sharedVaultUuid: Uuid): Promise<void> {
|
||||
await this.mongoRepository.updateMany(
|
||||
{ sharedVaultUuid: { $eq: sharedVaultUuid.value } },
|
||||
|
||||
@@ -16,6 +16,24 @@ export class SQLItemRepository extends SQLLegacyItemRepository {
|
||||
super(ormRepository, mapper, logger)
|
||||
}
|
||||
|
||||
override 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,
|
||||
fromOwnerUuid: dto.fromOwnerUuid.value,
|
||||
})
|
||||
.execute()
|
||||
}
|
||||
|
||||
override async unassignFromSharedVault(sharedVaultUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder('item')
|
||||
|
||||
@@ -16,6 +16,10 @@ export class SQLLegacyItemRepository implements ItemRepositoryInterface {
|
||||
protected logger: Logger,
|
||||
) {}
|
||||
|
||||
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.')
|
||||
}
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.10.51](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.10.50...@standardnotes/websockets-server@1.10.51) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/websockets-server
|
||||
|
||||
## [1.10.50](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.10.49...@standardnotes/websockets-server@1.10.50) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/websockets-server
|
||||
|
||||
## [1.10.49](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.10.48...@standardnotes/websockets-server@1.10.49) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/websockets-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/websockets-server",
|
||||
"version": "1.10.49",
|
||||
"version": "1.10.51",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -5045,6 +5045,7 @@ __metadata:
|
||||
"@types/cors": "npm:^2.8.9"
|
||||
"@types/dotenv": "npm:^8.2.0"
|
||||
"@types/express": "npm:^4.17.14"
|
||||
"@types/ioredis": "npm:^5.0.0"
|
||||
"@types/jest": "npm:^29.5.1"
|
||||
"@types/newrelic": "npm:^9.14.0"
|
||||
"@types/node": "npm:^20.5.7"
|
||||
@@ -5057,6 +5058,7 @@ __metadata:
|
||||
express: "npm:^4.18.2"
|
||||
inversify: "npm:^6.0.1"
|
||||
inversify-express-utils: "npm:^6.4.3"
|
||||
ioredis: "npm:^5.3.2"
|
||||
jest: "npm:^29.5.0"
|
||||
mongodb: "npm:^6.0.0"
|
||||
mysql2: "npm:^3.0.1"
|
||||
@@ -5235,6 +5237,7 @@ __metadata:
|
||||
"@types/cors": "npm:^2.8.9"
|
||||
"@types/dotenv": "npm:^8.2.0"
|
||||
"@types/express": "npm:^4.17.14"
|
||||
"@types/ioredis": "npm:^5.0.0"
|
||||
"@types/jest": "npm:^29.5.1"
|
||||
"@types/jsonwebtoken": "npm:^9.0.1"
|
||||
"@types/newrelic": "npm:^9.14.0"
|
||||
@@ -5254,6 +5257,7 @@ __metadata:
|
||||
helmet: "npm:^7.0.0"
|
||||
inversify: "npm:^6.0.1"
|
||||
inversify-express-utils: "npm:^6.4.3"
|
||||
ioredis: "npm:^5.3.2"
|
||||
jest: "npm:^29.5.0"
|
||||
jsonwebtoken: "npm:^9.0.0"
|
||||
mongodb: "npm:^6.0.0"
|
||||
@@ -9531,7 +9535,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ioredis@npm:*, ioredis@npm:^5.2.4":
|
||||
"ioredis@npm:*, ioredis@npm:^5.2.4, ioredis@npm:^5.3.2":
|
||||
version: 5.3.2
|
||||
resolution: "ioredis@npm:5.3.2"
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user