mirror of
https://github.com/standardnotes/server
synced 2026-05-01 18:01:23 -04:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 04b52e6773 | |||
| 2a1859e4be | |||
| dd9a9c68cb | |||
| 9147ff5d49 | |||
| 503b84531b | |||
| fe8ca828fb | |||
| 03a4a3f2ab | |||
| 3a8607d146 | |||
| 93b6e65554 | |||
| 5984e4c3e7 | |||
| b4257c10ea | |||
| c164bde847 | |||
| 883df939dd | |||
| c7807d0f9e | |||
| fc90343aaa | |||
| fbcb45c3a2 |
@@ -3326,10 +3326,10 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["@hexagon/base64", [\
|
||||
["npm:1.1.26", {\
|
||||
"packageLocation": "./.yarn/cache/@hexagon-base64-npm-1.1.26-dbfda05df8-e42582ed12.zip/node_modules/@hexagon/base64/",\
|
||||
["npm:1.1.27", {\
|
||||
"packageLocation": "./.yarn/cache/@hexagon-base64-npm-1.1.27-df6f264962-899fffaf54.zip/node_modules/@hexagon/base64/",\
|
||||
"packageDependencies": [\
|
||||
["@hexagon/base64", "npm:1.1.26"]\
|
||||
["@hexagon/base64", "npm:1.1.27"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
@@ -5067,44 +5067,29 @@ const RAW_RUNTIME_STATE =
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@simplewebauthn/iso-webcrypto", [\
|
||||
["npm:7.2.0", {\
|
||||
"packageLocation": "./.yarn/cache/@simplewebauthn-iso-webcrypto-npm-7.2.0-db7b12b859-b57899d0ad.zip/node_modules/@simplewebauthn/iso-webcrypto/",\
|
||||
"packageDependencies": [\
|
||||
["@simplewebauthn/iso-webcrypto", "npm:7.2.0"],\
|
||||
["@simplewebauthn/typescript-types", "npm:7.0.0"],\
|
||||
["@types/node", "npm:18.16.16"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@simplewebauthn/server", [\
|
||||
["npm:7.2.0", {\
|
||||
"packageLocation": "./.yarn/cache/@simplewebauthn-server-npm-7.2.0-f1ed5fde8a-2e37c87edd.zip/node_modules/@simplewebauthn/server/",\
|
||||
["npm:8.1.1", {\
|
||||
"packageLocation": "./.yarn/cache/@simplewebauthn-server-npm-8.1.1-106d3bd108-a07c2a067b.zip/node_modules/@simplewebauthn/server/",\
|
||||
"packageDependencies": [\
|
||||
["@simplewebauthn/server", "npm:7.2.0"],\
|
||||
["@hexagon/base64", "npm:1.1.26"],\
|
||||
["@simplewebauthn/server", "npm:8.1.1"],\
|
||||
["@hexagon/base64", "npm:1.1.27"],\
|
||||
["@peculiar/asn1-android", "npm:2.3.6"],\
|
||||
["@peculiar/asn1-ecc", "npm:2.3.6"],\
|
||||
["@peculiar/asn1-rsa", "npm:2.3.6"],\
|
||||
["@peculiar/asn1-schema", "npm:2.3.6"],\
|
||||
["@peculiar/asn1-x509", "npm:2.3.6"],\
|
||||
["@simplewebauthn/iso-webcrypto", "npm:7.2.0"],\
|
||||
["@simplewebauthn/typescript-types", "npm:7.0.0"],\
|
||||
["@types/debug", "npm:4.1.8"],\
|
||||
["@types/node", "npm:18.16.16"],\
|
||||
["cbor-x", "npm:1.5.3"],\
|
||||
["cross-fetch", "npm:3.1.6"],\
|
||||
["debug", "virtual:ac3d8e680759ce54399273724d44e041d6c9b73454d191d411a8c44bb27e22f02aaf6ed9d3ad0ac1c298eac4833cff369c9c7b84c573016112c4f84be2cd8543#npm:4.3.4"]\
|
||||
["@simplewebauthn/typescript-types", "npm:8.0.0"],\
|
||||
["cbor-x", "npm:1.5.4"],\
|
||||
["cross-fetch", "npm:4.0.0"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@simplewebauthn/typescript-types", [\
|
||||
["npm:7.0.0", {\
|
||||
"packageLocation": "./.yarn/cache/@simplewebauthn-typescript-types-npm-7.0.0-cc6ca20415-124238ea18.zip/node_modules/@simplewebauthn/typescript-types/",\
|
||||
["npm:8.0.0", {\
|
||||
"packageLocation": "./.yarn/cache/@simplewebauthn-typescript-types-npm-8.0.0-f3b313c27b-21e0b13268.zip/node_modules/@simplewebauthn/typescript-types/",\
|
||||
"packageDependencies": [\
|
||||
["@simplewebauthn/typescript-types", "npm:7.0.0"]\
|
||||
["@simplewebauthn/typescript-types", "npm:8.0.0"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
@@ -5864,8 +5849,8 @@ const RAW_RUNTIME_STATE =
|
||||
["@cbor-extract/cbor-extract-linux-arm64", "npm:2.1.1"],\
|
||||
["@cbor-extract/cbor-extract-linux-x64", "npm:2.1.1"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.1"],\
|
||||
["@simplewebauthn/server", "npm:7.2.0"],\
|
||||
["@simplewebauthn/typescript-types", "npm:7.0.0"],\
|
||||
["@simplewebauthn/server", "npm:8.1.1"],\
|
||||
["@simplewebauthn/typescript-types", "npm:8.0.0"],\
|
||||
["@standardnotes/api", "npm:1.26.26"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
@@ -6720,16 +6705,6 @@ const RAW_RUNTIME_STATE =
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@types/debug", [\
|
||||
["npm:4.1.8", {\
|
||||
"packageLocation": "./.yarn/cache/@types-debug-npm-4.1.8-a04e2ca136-9c190e8129.zip/node_modules/@types/debug/",\
|
||||
"packageDependencies": [\
|
||||
["@types/debug", "npm:4.1.8"],\
|
||||
["@types/ms", "npm:0.7.31"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@types/dotenv", [\
|
||||
["npm:8.2.0", {\
|
||||
"packageLocation": "./.yarn/cache/@types-dotenv-npm-8.2.0-f4d0e3d65b-13f90a36f7.zip/node_modules/@types/dotenv/",\
|
||||
@@ -6956,15 +6931,6 @@ const RAW_RUNTIME_STATE =
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@types/ms", [\
|
||||
["npm:0.7.31", {\
|
||||
"packageLocation": "./.yarn/cache/@types-ms-npm-0.7.31-ea3b89342b-cccb52777b.zip/node_modules/@types/ms/",\
|
||||
"packageDependencies": [\
|
||||
["@types/ms", "npm:0.7.31"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@types/newrelic", [\
|
||||
["npm:9.14.0", {\
|
||||
"packageLocation": "./.yarn/cache/@types-newrelic-npm-9.14.0-4668da51a1-2ec951bd8f.zip/node_modules/@types/newrelic/",\
|
||||
@@ -6982,13 +6948,6 @@ const RAW_RUNTIME_STATE =
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:18.16.16", {\
|
||||
"packageLocation": "./.yarn/cache/@types-node-npm-18.16.16-8a41330dc3-946bd4d8e6.zip/node_modules/@types/node/",\
|
||||
"packageDependencies": [\
|
||||
["@types/node", "npm:18.16.16"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:20.2.5", {\
|
||||
"packageLocation": "./.yarn/cache/@types-node-npm-20.2.5-0014d2d9ce-55e4f8d08e.zip/node_modules/@types/node/",\
|
||||
"packageDependencies": [\
|
||||
@@ -8706,10 +8665,10 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["cbor-x", [\
|
||||
["npm:1.5.3", {\
|
||||
"packageLocation": "./.yarn/cache/cbor-x-npm-1.5.3-1d452dd267-d4df85b339.zip/node_modules/cbor-x/",\
|
||||
["npm:1.5.4", {\
|
||||
"packageLocation": "./.yarn/cache/cbor-x-npm-1.5.4-2d5a649a4b-742aea498a.zip/node_modules/cbor-x/",\
|
||||
"packageDependencies": [\
|
||||
["cbor-x", "npm:1.5.3"],\
|
||||
["cbor-x", "npm:1.5.4"],\
|
||||
["cbor-extract", "npm:2.1.1"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
@@ -9434,11 +9393,11 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["cross-fetch", [\
|
||||
["npm:3.1.6", {\
|
||||
"packageLocation": "./.yarn/cache/cross-fetch-npm-3.1.6-cdb982d446-a8989fca82.zip/node_modules/cross-fetch/",\
|
||||
["npm:4.0.0", {\
|
||||
"packageLocation": "./.yarn/cache/cross-fetch-npm-4.0.0-9c67668db4-30e86b703a.zip/node_modules/cross-fetch/",\
|
||||
"packageDependencies": [\
|
||||
["cross-fetch", "npm:3.1.6"],\
|
||||
["node-fetch", "virtual:0f92dfe7f9dc4fd492639d4a5b7805c2b27442bf599fd4f370b22a7966ba078f5d4525e2a8e8af29369f20e1833ed084bd52be59679efaa6c1c6c10cdbcd8baa#npm:2.6.11"]\
|
||||
["cross-fetch", "npm:4.0.0"],\
|
||||
["node-fetch", "virtual:9c67668db478e95ba4d6a763bc55027eeff0d22eaf59478017ea07386fc33a3c7b7b625af78aa86a33991a9a500a7aa216e28632de568f02adefd662ef53a42d#npm:2.7.0"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
@@ -14174,6 +14133,13 @@ const RAW_RUNTIME_STATE =
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["npm:2.7.0", {\
|
||||
"packageLocation": "./.yarn/cache/node-fetch-npm-2.7.0-587d57004e-a3ad788903.zip/node_modules/node-fetch/",\
|
||||
"packageDependencies": [\
|
||||
["node-fetch", "npm:2.7.0"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["npm:3.3.1", {\
|
||||
"packageLocation": "./.yarn/cache/node-fetch-npm-3.3.1-576511fc5a-1d0c635bdf.zip/node_modules/node-fetch/",\
|
||||
"packageDependencies": [\
|
||||
@@ -14197,6 +14163,20 @@ const RAW_RUNTIME_STATE =
|
||||
"encoding"\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["virtual:9c67668db478e95ba4d6a763bc55027eeff0d22eaf59478017ea07386fc33a3c7b7b625af78aa86a33991a9a500a7aa216e28632de568f02adefd662ef53a42d#npm:2.7.0", {\
|
||||
"packageLocation": "./.yarn/__virtual__/node-fetch-virtual-0ec1497d1c/0/cache/node-fetch-npm-2.7.0-587d57004e-a3ad788903.zip/node_modules/node-fetch/",\
|
||||
"packageDependencies": [\
|
||||
["node-fetch", "virtual:9c67668db478e95ba4d6a763bc55027eeff0d22eaf59478017ea07386fc33a3c7b7b625af78aa86a33991a9a500a7aa216e28632de568f02adefd662ef53a42d#npm:2.7.0"],\
|
||||
["@types/encoding", null],\
|
||||
["encoding", null],\
|
||||
["whatwg-url", "npm:5.0.0"]\
|
||||
],\
|
||||
"packagePeers": [\
|
||||
"@types/encoding",\
|
||||
"encoding"\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["node-gyp", [\
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.74.10](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.9...@standardnotes/api-gateway@1.74.10) (2023-09-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adjust transition timestamps to be universal ([c7807d0](https://github.com/standardnotes/api-gateway/commit/c7807d0f9e69ce572c4c03ff606375d706f24d9f))
|
||||
|
||||
## [1.74.9](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.8...@standardnotes/api-gateway@1.74.9) (2023-09-13)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.74.9",
|
||||
"version": "1.74.10",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -80,15 +80,6 @@ export class UsersController extends BaseHttpController {
|
||||
)
|
||||
}
|
||||
|
||||
@httpGet('/transition-status', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||
async getTransitionStatus(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
response,
|
||||
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'users/transition-status'),
|
||||
)
|
||||
}
|
||||
|
||||
@httpGet('/:userId/params', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||
async getKeyParams(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callAuthServer(
|
||||
|
||||
@@ -43,7 +43,6 @@ export class EndpointResolver implements EndpointResolverInterface {
|
||||
['[PATCH]:users/:userId', 'auth.users.update'],
|
||||
['[PUT]:users/:userUuid/attributes/credentials', 'auth.users.updateCredentials'],
|
||||
['[GET]:users/params', 'auth.users.getKeyParams'],
|
||||
['[GET]:users/transition-status', 'auth.users.transition-status'],
|
||||
['[DELETE]:users/:userUuid', 'auth.users.delete'],
|
||||
['[POST]:listed', 'auth.users.createListedAccount'],
|
||||
['[POST]:auth', 'auth.users.register'],
|
||||
@@ -59,13 +58,11 @@ export class EndpointResolver implements EndpointResolverInterface {
|
||||
// Syncing Server
|
||||
['[POST]:items/sync', 'sync.items.sync'],
|
||||
['[POST]:items/check-integrity', 'sync.items.check_integrity'],
|
||||
['[POST]:items/transition', 'sync.items.transition'],
|
||||
['[GET]:items/:uuid', 'sync.items.get_item'],
|
||||
// Revisions Controller V2
|
||||
['[GET]:items/:itemUuid/revisions', 'revisions.revisions.getRevisions'],
|
||||
['[GET]:items/:itemUuid/revisions/:id', 'revisions.revisions.getRevision'],
|
||||
['[DELETE]:items/:itemUuid/revisions/:id', 'revisions.revisions.deleteRevision'],
|
||||
['[POST]:revisions/transition', 'revisions.revisions.transition'],
|
||||
// Messages Controller
|
||||
['[GET]:messages/', 'sync.messages.get-received'],
|
||||
['[GET]:messages/outbound', 'sync.messages.get-sent'],
|
||||
|
||||
@@ -3,6 +3,37 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.141.14](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.141.13...@standardnotes/auth-server@1.141.14) (2023-09-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** remove extensive logs from updating transitions ([2a1859e](https://github.com/standardnotes/server/commit/2a1859e4beff4cc7c4348ebbff8357a8e061bf5e))
|
||||
* **auth:** upgrade simplewebauthn dependency ([#826](https://github.com/standardnotes/server/issues/826)) ([dd9a9c6](https://github.com/standardnotes/server/commit/dd9a9c68cb431a61700a8e5d8247eb1b505a9d62))
|
||||
|
||||
## [1.141.13](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.141.12...@standardnotes/auth-server@1.141.13) (2023-09-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** set ttl for started and not picked up transitions to 10h ([fe8ca82](https://github.com/standardnotes/server/commit/fe8ca828fb37306e0e5056627e67366885e86861))
|
||||
|
||||
## [1.141.12](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.141.11...@standardnotes/auth-server@1.141.12) (2023-09-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** remove re-triggering revisions transition ([5984e4c](https://github.com/standardnotes/server/commit/5984e4c3e7e550e5ed53805bde1e6dabcbe54da8))
|
||||
|
||||
## [1.141.11](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.141.10...@standardnotes/auth-server@1.141.11) (2023-09-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** passing transition timestamp ([c164bde](https://github.com/standardnotes/server/commit/c164bde847b5974e74fd439f0d439526ad439443))
|
||||
|
||||
## [1.141.10](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.141.9...@standardnotes/auth-server@1.141.10) (2023-09-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adjust transition timestamps to be universal ([c7807d0](https://github.com/standardnotes/server/commit/c7807d0f9e69ce572c4c03ff606375d706f24d9f))
|
||||
|
||||
## [1.141.9](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.141.8...@standardnotes/auth-server@1.141.9) (2023-09-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Env } from '../src/Bootstrap/Env'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface'
|
||||
import { UserRepositoryInterface } from '../src/Domain/User/UserRepositoryInterface'
|
||||
import { TransitionStatusRepositoryInterface } from '../src/Domain/Transition/TransitionStatusRepositoryInterface'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
const inputArgs = process.argv.slice(2)
|
||||
const startDateString = inputArgs[0]
|
||||
@@ -18,23 +18,28 @@ const endDateString = inputArgs[1]
|
||||
|
||||
const requestTransition = async (
|
||||
userRepository: UserRepositoryInterface,
|
||||
transitionStatusRepository: TransitionStatusRepositoryInterface,
|
||||
logger: Logger,
|
||||
domainEventFactory: DomainEventFactoryInterface,
|
||||
domainEventPublisher: DomainEventPublisherInterface,
|
||||
timer: TimerInterface,
|
||||
): Promise<void> => {
|
||||
const startDate = new Date(startDateString)
|
||||
const endDate = new Date(endDateString)
|
||||
|
||||
const users = await userRepository.findAllCreatedBetween(startDate, endDate)
|
||||
|
||||
logger.info(`Found ${users.length} users created between ${startDateString} and ${endDateString}`)
|
||||
const timestamp = timer.getTimestampInMicroseconds()
|
||||
|
||||
logger.info(
|
||||
`[TRANSITION ${timestamp}] Found ${users.length} users created between ${startDateString} and ${endDateString}`,
|
||||
)
|
||||
|
||||
let usersTriggered = 0
|
||||
for (const user of users) {
|
||||
const transitionRequestedEvent = domainEventFactory.createTransitionRequestedEvent({
|
||||
userUuid: user.uuid,
|
||||
type: 'items',
|
||||
timestamp,
|
||||
})
|
||||
|
||||
usersTriggered += 1
|
||||
@@ -43,22 +48,8 @@ const requestTransition = async (
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Triggered transition for ${usersTriggered} users created between ${startDateString} and ${endDateString}`,
|
||||
`[TRANSITION ${timestamp}] Triggered transition for ${usersTriggered} users created between ${startDateString} and ${endDateString}`,
|
||||
)
|
||||
|
||||
const revisionStatuses = await transitionStatusRepository.getStatuses('revisions')
|
||||
const failedStatuses = revisionStatuses.filter((status) => status.status === 'FAILED')
|
||||
|
||||
logger.info(`Found ${failedStatuses.length} failed revision transitions`)
|
||||
|
||||
for (const status of failedStatuses) {
|
||||
const transitionRequestedEvent = domainEventFactory.createTransitionRequestedEvent({
|
||||
userUuid: status.userUuid,
|
||||
type: 'revisions',
|
||||
})
|
||||
|
||||
await domainEventPublisher.publish(transitionRequestedEvent)
|
||||
}
|
||||
}
|
||||
|
||||
const container = new ContainerConfigLoader('worker')
|
||||
@@ -75,13 +66,9 @@ void container.load().then((container) => {
|
||||
const userRepository: UserRepositoryInterface = container.get(TYPES.Auth_UserRepository)
|
||||
const domainEventFactory: DomainEventFactoryInterface = container.get(TYPES.Auth_DomainEventFactory)
|
||||
const domainEventPublisher: DomainEventPublisherInterface = container.get(TYPES.Auth_DomainEventPublisher)
|
||||
const transitionStatusRepository: TransitionStatusRepositoryInterface = container.get(
|
||||
TYPES.Auth_TransitionStatusRepository,
|
||||
)
|
||||
const timer = container.get<TimerInterface>(TYPES.Auth_Timer)
|
||||
|
||||
Promise.resolve(
|
||||
requestTransition(userRepository, transitionStatusRepository, logger, domainEventFactory, domainEventPublisher),
|
||||
)
|
||||
Promise.resolve(requestTransition(userRepository, logger, domainEventFactory, domainEventPublisher, timer))
|
||||
.then(() => {
|
||||
logger.info(`Finished transition request for users created between ${startDateString} and ${endDateString}`)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/auth-server",
|
||||
"version": "1.141.9",
|
||||
"version": "1.141.14",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -40,8 +40,8 @@
|
||||
"@aws-sdk/client-sqs": "^3.332.0",
|
||||
"@cbor-extract/cbor-extract-linux-arm64": "^2.1.1",
|
||||
"@cbor-extract/cbor-extract-linux-x64": "^2.1.1",
|
||||
"@simplewebauthn/server": "^7.2.0",
|
||||
"@simplewebauthn/typescript-types": "^7.0.0",
|
||||
"@simplewebauthn/server": "^8.1.1",
|
||||
"@simplewebauthn/typescript-types": "^8.0.0",
|
||||
"@standardnotes/api": "^1.26.26",
|
||||
"@standardnotes/common": "workspace:*",
|
||||
"@standardnotes/domain-core": "workspace:^",
|
||||
|
||||
@@ -943,7 +943,6 @@ export class ContainerConfigLoader {
|
||||
new UpdateTransitionStatus(
|
||||
container.get<TransitionStatusRepositoryInterface>(TYPES.Auth_TransitionStatusRepository),
|
||||
container.get<RoleServiceInterface>(TYPES.Auth_RoleService),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
@@ -1276,7 +1275,6 @@ export class ContainerConfigLoader {
|
||||
container.get<ClearLoginAttempts>(TYPES.Auth_ClearLoginAttempts),
|
||||
container.get<IncreaseLoginAttempts>(TYPES.Auth_IncreaseLoginAttempts),
|
||||
container.get<ChangeCredentials>(TYPES.Auth_ChangeCredentials),
|
||||
container.get<GetTransitionStatus>(TYPES.Auth_GetTransitionStatus),
|
||||
container.get<ControllerContainerInterface>(TYPES.Auth_ControllerContainer),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -33,7 +33,11 @@ import { DomainEventFactoryInterface } from './DomainEventFactoryInterface'
|
||||
export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||
constructor(@inject(TYPES.Auth_Timer) private timer: TimerInterface) {}
|
||||
|
||||
createTransitionRequestedEvent(dto: { userUuid: string; type: 'items' | 'revisions' }): TransitionRequestedEvent {
|
||||
createTransitionRequestedEvent(dto: {
|
||||
userUuid: string
|
||||
type: 'items' | 'revisions'
|
||||
timestamp: number
|
||||
}): TransitionRequestedEvent {
|
||||
return {
|
||||
type: 'TRANSITION_REQUESTED',
|
||||
createdAt: this.timer.getUTCDate(),
|
||||
@@ -44,10 +48,7 @@ export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||
},
|
||||
origin: DomainEventService.Auth,
|
||||
},
|
||||
payload: {
|
||||
timestamp: this.timer.getTimestampInMicroseconds(),
|
||||
...dto,
|
||||
},
|
||||
payload: dto,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,5 +90,9 @@ export interface DomainEventFactoryInterface {
|
||||
}): StatisticPersistenceRequestedEvent
|
||||
createSessionCreatedEvent(dto: { userUuid: string }): SessionCreatedEvent
|
||||
createSessionRefreshedEvent(dto: { userUuid: string }): SessionRefreshedEvent
|
||||
createTransitionRequestedEvent(dto: { userUuid: string; type: 'items' | 'revisions' }): TransitionRequestedEvent
|
||||
createTransitionRequestedEvent(dto: {
|
||||
userUuid: string
|
||||
type: 'items' | 'revisions'
|
||||
timestamp: number
|
||||
}): TransitionRequestedEvent
|
||||
}
|
||||
|
||||
+2
-2
@@ -35,7 +35,7 @@ export class GenerateAuthenticatorAuthenticationOptions
|
||||
.update(`u2f-selector-${dto.username}${this.pseudoKeyParamsKey}`)
|
||||
.digest('base64url')
|
||||
|
||||
const options = generateAuthenticationOptions({
|
||||
const options = await generateAuthenticationOptions({
|
||||
allowCredentials: [
|
||||
{
|
||||
id: Buffer.from(credentialIdHash),
|
||||
@@ -56,7 +56,7 @@ export class GenerateAuthenticatorAuthenticationOptions
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const authenticators = await this.authenticatorRepository.findByUserUuid(userUuid)
|
||||
const options = generateAuthenticationOptions({
|
||||
const options = await generateAuthenticationOptions({
|
||||
allowCredentials: authenticators.map((authenticator) => ({
|
||||
id: authenticator.props.credentialId,
|
||||
type: 'public-key',
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ export class GenerateAuthenticatorRegistrationOptions
|
||||
}
|
||||
|
||||
const authenticators = await this.authenticatorRepository.findByUserUuid(userUuid)
|
||||
const options = generateRegistrationOptions({
|
||||
const options = await generateRegistrationOptions({
|
||||
rpID: this.relyingPartyId,
|
||||
rpName: this.relyingPartyName,
|
||||
userID: userUuid.value,
|
||||
|
||||
+1
-6
@@ -3,19 +3,14 @@ import { RoleName, Uuid } from '@standardnotes/domain-core'
|
||||
import { RoleServiceInterface } from '../../Role/RoleServiceInterface'
|
||||
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
|
||||
import { UpdateTransitionStatus } from './UpdateTransitionStatus'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
describe('UpdateTransitionStatus', () => {
|
||||
let transitionStatusRepository: TransitionStatusRepositoryInterface
|
||||
let roleService: RoleServiceInterface
|
||||
let logger: Logger
|
||||
|
||||
const createUseCase = () => new UpdateTransitionStatus(transitionStatusRepository, roleService, logger)
|
||||
const createUseCase = () => new UpdateTransitionStatus(transitionStatusRepository, roleService)
|
||||
|
||||
beforeEach(() => {
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.info = jest.fn()
|
||||
|
||||
transitionStatusRepository = {} as jest.Mocked<TransitionStatusRepositoryInterface>
|
||||
transitionStatusRepository.removeStatus = jest.fn()
|
||||
transitionStatusRepository.updateStatus = jest.fn()
|
||||
|
||||
@@ -2,13 +2,11 @@ import { Result, RoleName, UseCaseInterface, Uuid } from '@standardnotes/domain-
|
||||
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
|
||||
import { UpdateTransitionStatusDTO } from './UpdateTransitionStatusDTO'
|
||||
import { RoleServiceInterface } from '../../Role/RoleServiceInterface'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
export class UpdateTransitionStatus implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private transitionStatusRepository: TransitionStatusRepositoryInterface,
|
||||
private roleService: RoleServiceInterface,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async execute(dto: UpdateTransitionStatusDTO): Promise<Result<void>> {
|
||||
@@ -30,24 +28,6 @@ export class UpdateTransitionStatus implements UseCaseInterface<void> {
|
||||
await this.roleService.addRoleToUser(userUuid, RoleName.create(RoleName.NAMES.TransitionUser).getValue())
|
||||
}
|
||||
|
||||
const itemStatuses = await this.transitionStatusRepository.getStatuses('items')
|
||||
const itemsStartedStatusesCount = itemStatuses.filter((status) => status.status === 'STARTED').length
|
||||
const itemsInProgressStatusesCount = itemStatuses.filter((status) => status.status === 'IN_PROGRESS').length
|
||||
const itemsFailedStatusesCount = itemStatuses.filter((status) => status.status === 'FAILED').length
|
||||
|
||||
this.logger.info(
|
||||
`[TRANSITION ${dto.transitionTimestamp}] Items transition statuses: ${itemsStartedStatusesCount} started, ${itemsInProgressStatusesCount} in progress, ${itemsFailedStatusesCount} failed`,
|
||||
)
|
||||
|
||||
const revisionStatuses = await this.transitionStatusRepository.getStatuses('revisions')
|
||||
const revisionsStartedStatusesCount = revisionStatuses.filter((status) => status.status === 'STARTED').length
|
||||
const revisionsInProgressStatusesCount = revisionStatuses.filter((status) => status.status === 'IN_PROGRESS').length
|
||||
const revisionsFailedStatusesCount = revisionStatuses.filter((status) => status.status === 'FAILED').length
|
||||
|
||||
this.logger.info(
|
||||
`[TRANSITION ${dto.transitionTimestamp}] Revisions transition statuses: ${revisionsStartedStatusesCount} started, ${revisionsInProgressStatusesCount} in progress, ${revisionsFailedStatusesCount} failed`,
|
||||
)
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import { IncreaseLoginAttempts } from '../../Domain/UseCase/IncreaseLoginAttempt
|
||||
import { InviteToSharedSubscription } from '../../Domain/UseCase/InviteToSharedSubscription/InviteToSharedSubscription'
|
||||
import { UpdateUser } from '../../Domain/UseCase/UpdateUser'
|
||||
import { User } from '../../Domain/User/User'
|
||||
import { GetTransitionStatus } from '../../Domain/UseCase/GetTransitionStatus/GetTransitionStatus'
|
||||
|
||||
describe('AnnotatedUsersController', () => {
|
||||
let updateUser: UpdateUser
|
||||
@@ -25,7 +24,6 @@ describe('AnnotatedUsersController', () => {
|
||||
let increaseLoginAttempts: IncreaseLoginAttempts
|
||||
let changeCredentials: ChangeCredentials
|
||||
let inviteToSharedSubscription: InviteToSharedSubscription
|
||||
let getTransitionStatus: GetTransitionStatus
|
||||
|
||||
let request: express.Request
|
||||
let response: express.Response
|
||||
@@ -40,7 +38,6 @@ describe('AnnotatedUsersController', () => {
|
||||
clearLoginAttempts,
|
||||
increaseLoginAttempts,
|
||||
changeCredentials,
|
||||
getTransitionStatus,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -72,9 +69,6 @@ describe('AnnotatedUsersController', () => {
|
||||
inviteToSharedSubscription = {} as jest.Mocked<InviteToSharedSubscription>
|
||||
inviteToSharedSubscription.execute = jest.fn()
|
||||
|
||||
getTransitionStatus = {} as jest.Mocked<GetTransitionStatus>
|
||||
getTransitionStatus.execute = jest.fn()
|
||||
|
||||
request = {
|
||||
headers: {},
|
||||
body: {},
|
||||
|
||||
@@ -18,7 +18,6 @@ import { ClearLoginAttempts } from '../../Domain/UseCase/ClearLoginAttempts'
|
||||
import { IncreaseLoginAttempts } from '../../Domain/UseCase/IncreaseLoginAttempts'
|
||||
import { ChangeCredentials } from '../../Domain/UseCase/ChangeCredentials/ChangeCredentials'
|
||||
import { BaseUsersController } from './Base/BaseUsersController'
|
||||
import { GetTransitionStatus } from '../../Domain/UseCase/GetTransitionStatus/GetTransitionStatus'
|
||||
|
||||
@controller('/users')
|
||||
export class AnnotatedUsersController extends BaseUsersController {
|
||||
@@ -30,7 +29,6 @@ export class AnnotatedUsersController extends BaseUsersController {
|
||||
@inject(TYPES.Auth_ClearLoginAttempts) override clearLoginAttempts: ClearLoginAttempts,
|
||||
@inject(TYPES.Auth_IncreaseLoginAttempts) override increaseLoginAttempts: IncreaseLoginAttempts,
|
||||
@inject(TYPES.Auth_ChangeCredentials) override changeCredentialsUseCase: ChangeCredentials,
|
||||
@inject(TYPES.Auth_GetTransitionStatus) override getTransitionStatusUseCase: GetTransitionStatus,
|
||||
) {
|
||||
super(
|
||||
updateUser,
|
||||
@@ -40,7 +38,6 @@ export class AnnotatedUsersController extends BaseUsersController {
|
||||
clearLoginAttempts,
|
||||
increaseLoginAttempts,
|
||||
changeCredentialsUseCase,
|
||||
getTransitionStatusUseCase,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -54,11 +51,6 @@ export class AnnotatedUsersController extends BaseUsersController {
|
||||
return super.keyParams(request)
|
||||
}
|
||||
|
||||
@httpGet('/transition-status', TYPES.Auth_RequiredCrossServiceTokenMiddleware)
|
||||
override async transitionStatus(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.transitionStatus(request, response)
|
||||
}
|
||||
|
||||
@httpDelete('/:userUuid', TYPES.Auth_RequiredCrossServiceTokenMiddleware)
|
||||
override async deleteAccount(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.deleteAccount(request, response)
|
||||
|
||||
@@ -10,7 +10,6 @@ import { GetUserSubscription } from '../../../Domain/UseCase/GetUserSubscription
|
||||
import { IncreaseLoginAttempts } from '../../../Domain/UseCase/IncreaseLoginAttempts'
|
||||
import { UpdateUser } from '../../../Domain/UseCase/UpdateUser'
|
||||
import { ErrorTag } from '@standardnotes/responses'
|
||||
import { GetTransitionStatus } from '../../../Domain/UseCase/GetTransitionStatus/GetTransitionStatus'
|
||||
|
||||
export class BaseUsersController extends BaseHttpController {
|
||||
constructor(
|
||||
@@ -21,7 +20,6 @@ export class BaseUsersController extends BaseHttpController {
|
||||
protected clearLoginAttempts: ClearLoginAttempts,
|
||||
protected increaseLoginAttempts: IncreaseLoginAttempts,
|
||||
protected changeCredentialsUseCase: ChangeCredentials,
|
||||
protected getTransitionStatusUseCase: GetTransitionStatus,
|
||||
private controllerContainer?: ControllerContainerInterface,
|
||||
) {
|
||||
super()
|
||||
@@ -32,7 +30,6 @@ export class BaseUsersController extends BaseHttpController {
|
||||
this.controllerContainer.register('auth.users.getSubscription', this.getSubscription.bind(this))
|
||||
this.controllerContainer.register('auth.users.updateCredentials', this.changeCredentials.bind(this))
|
||||
this.controllerContainer.register('auth.users.delete', this.deleteAccount.bind(this))
|
||||
this.controllerContainer.register('auth.users.transition-status', this.transitionStatus.bind(this))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,30 +103,6 @@ export class BaseUsersController extends BaseHttpController {
|
||||
return this.json(result.keyParams)
|
||||
}
|
||||
|
||||
async transitionStatus(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.getTransitionStatusUseCase.execute({
|
||||
userUuid: response.locals.user.uuid,
|
||||
transitionType: 'items',
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message: result.getError(),
|
||||
},
|
||||
},
|
||||
400,
|
||||
)
|
||||
}
|
||||
|
||||
response.setHeader('x-invalidate-cache', response.locals.user.uuid)
|
||||
|
||||
return this.json({
|
||||
status: result.getValue(),
|
||||
})
|
||||
}
|
||||
|
||||
async deleteAccount(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
if (request.params.userUuid !== response.locals.user.uuid) {
|
||||
return this.json(
|
||||
|
||||
@@ -28,7 +28,9 @@ export class RedisTransitionStatusRepository implements TransitionStatusReposito
|
||||
status: 'STARTED' | 'IN_PROGRESS' | 'FAILED',
|
||||
): Promise<void> {
|
||||
if (status === 'IN_PROGRESS') {
|
||||
await this.redisClient.setex(`${this.PREFIX}:${transitionType}:${userUuid}`, 7200, status)
|
||||
await this.redisClient.setex(`${this.PREFIX}:${transitionType}:${userUuid}`, 7_200, status)
|
||||
} else if (status === 'STARTED') {
|
||||
await this.redisClient.setex(`${this.PREFIX}:${transitionType}:${userUuid}`, 36_000, status)
|
||||
} else {
|
||||
await this.redisClient.set(`${this.PREFIX}:${transitionType}:${userUuid}`, status)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,30 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.15.55](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.54...@standardnotes/home-server@1.15.55) (2023-09-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.54](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.53...@standardnotes/home-server@1.15.54) (2023-09-14)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.53](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.52...@standardnotes/home-server@1.15.53) (2023-09-13)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.52](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.51...@standardnotes/home-server@1.15.52) (2023-09-13)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.51](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.50...@standardnotes/home-server@1.15.51) (2023-09-13)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.50](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.49...@standardnotes/home-server@1.15.50) (2023-09-13)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.49](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.48...@standardnotes/home-server@1.15.49) (2023-09-13)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/home-server",
|
||||
"version": "1.15.49",
|
||||
"version": "1.15.55",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.33.21](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.33.20...@standardnotes/revisions-server@1.33.21) (2023-09-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* skip already updated items and revisions in integrity check ([#825](https://github.com/standardnotes/server/issues/825)) ([03a4a3f](https://github.com/standardnotes/server/commit/03a4a3f2abc0b4e09942ba39dbd227524068dfb6))
|
||||
|
||||
## [1.33.20](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.33.19...@standardnotes/revisions-server@1.33.20) (2023-09-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adjust transition timestamps to be universal ([c7807d0](https://github.com/standardnotes/server/commit/c7807d0f9e69ce572c4c03ff606375d706f24d9f))
|
||||
|
||||
## [1.33.19](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.33.18...@standardnotes/revisions-server@1.33.19) (2023-09-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* include handling updated items in revisions in secondary ([fbcb45c](https://github.com/standardnotes/server/commit/fbcb45c3a23fde09702fae7bfcb409bdbb610191))
|
||||
|
||||
## [1.33.18](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.33.17...@standardnotes/revisions-server@1.33.18) (2023-09-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/revisions-server",
|
||||
"version": "1.33.18",
|
||||
"version": "1.33.21",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -520,9 +520,6 @@ export class ContainerConfigLoader {
|
||||
container.get<DeleteRevision>(TYPES.Revisions_DeleteRevision),
|
||||
container.get<RevisionHttpMapper>(TYPES.Revisions_RevisionHttpMapper),
|
||||
container.get<RevisionMetadataHttpMapper>(TYPES.Revisions_RevisionMetadataHttpMapper),
|
||||
container.get<TriggerTransitionFromPrimaryToSecondaryDatabaseForUser>(
|
||||
TYPES.Revisions_TriggerTransitionFromPrimaryToSecondaryDatabaseForUser,
|
||||
),
|
||||
container.get<ControllerContainerInterface>(TYPES.Revisions_ControllerContainer),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -9,6 +9,7 @@ export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||
createTransitionStatusUpdatedEvent(dto: {
|
||||
userUuid: string
|
||||
transitionType: 'items' | 'revisions'
|
||||
transitionTimestamp: number
|
||||
status: 'STARTED' | 'FAILED' | 'FINISHED'
|
||||
}): TransitionStatusUpdatedEvent {
|
||||
return {
|
||||
@@ -21,10 +22,7 @@ export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||
},
|
||||
origin: DomainEventService.SyncingServer,
|
||||
},
|
||||
payload: {
|
||||
transitionTimestamp: this.timer.getTimestampInMicroseconds(),
|
||||
...dto,
|
||||
},
|
||||
payload: dto,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ export interface DomainEventFactoryInterface {
|
||||
createTransitionStatusUpdatedEvent(dto: {
|
||||
userUuid: string
|
||||
transitionType: 'items' | 'revisions'
|
||||
transitionTimestamp: number
|
||||
status: 'STARTED' | 'IN_PROGRESS' | 'FAILED' | 'FINISHED'
|
||||
}): TransitionStatusUpdatedEvent
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ export class TransitionRequestedEventHandler implements DomainEventHandlerInterf
|
||||
|
||||
const result = await this.triggerTransitionFromPrimaryToSecondaryDatabaseForUser.execute({
|
||||
userUuid: event.payload.userUuid,
|
||||
transitionTimestamp: event.payload.timestamp,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
|
||||
@@ -25,6 +25,7 @@ export class TransitionStatusUpdatedEventHandler implements DomainEventHandlerIn
|
||||
userUuid: event.payload.userUuid,
|
||||
status: 'STARTED',
|
||||
transitionType: 'revisions',
|
||||
transitionTimestamp: event.payload.transitionTimestamp,
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -42,6 +43,7 @@ export class TransitionStatusUpdatedEventHandler implements DomainEventHandlerIn
|
||||
userUuid: event.payload.userUuid,
|
||||
status: 'FAILED',
|
||||
transitionType: 'revisions',
|
||||
transitionTimestamp: event.payload.transitionTimestamp,
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -54,6 +56,7 @@ export class TransitionStatusUpdatedEventHandler implements DomainEventHandlerIn
|
||||
userUuid: event.payload.userUuid,
|
||||
status: 'FINISHED',
|
||||
transitionType: 'revisions',
|
||||
transitionTimestamp: event.payload.transitionTimestamp,
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -65,6 +68,7 @@ export class TransitionStatusUpdatedEventHandler implements DomainEventHandlerIn
|
||||
userUuid: event.payload.userUuid,
|
||||
status: 'IN_PROGRESS',
|
||||
transitionType: 'revisions',
|
||||
transitionTimestamp: event.payload.transitionTimestamp,
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -80,6 +84,7 @@ export class TransitionStatusUpdatedEventHandler implements DomainEventHandlerIn
|
||||
userUuid: event.payload.userUuid,
|
||||
status: 'FAILED',
|
||||
transitionType: 'revisions',
|
||||
transitionTimestamp: event.payload.transitionTimestamp,
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -91,6 +96,7 @@ export class TransitionStatusUpdatedEventHandler implements DomainEventHandlerIn
|
||||
userUuid: event.payload.userUuid,
|
||||
status: 'FINISHED',
|
||||
transitionType: 'revisions',
|
||||
transitionTimestamp: event.payload.transitionTimestamp,
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
+80
-16
@@ -30,9 +30,12 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
let newRevisionsInSecondaryCount = 0
|
||||
let updatedRevisionsInSecondary: Revision[] = []
|
||||
if (await this.hasAlreadyDataInSecondaryDatabase(userUuid)) {
|
||||
const newRevisions = await this.getNewRevisionsCreatedInSecondaryDatabase(userUuid)
|
||||
for (const existingRevision of newRevisions.alreadyExistingInPrimary) {
|
||||
const { alreadyExistingInPrimary, newRevisionsInSecondary, updatedInSecondary } =
|
||||
await this.getNewRevisionsCreatedInSecondaryDatabase(userUuid)
|
||||
|
||||
for (const existingRevision of alreadyExistingInPrimary) {
|
||||
this.logger.info(`Removing revision ${existingRevision.id.toString()} from secondary database`)
|
||||
await (this.secondRevisionsRepository as RevisionRepositoryInterface).removeOneByUuid(
|
||||
Uuid.create(existingRevision.id.toString()).getValue(),
|
||||
@@ -40,24 +43,34 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
|
||||
)
|
||||
}
|
||||
|
||||
if (newRevisions.newRevisionsInSecondary.length > 0) {
|
||||
if (newRevisionsInSecondary.length > 0) {
|
||||
this.logger.info(
|
||||
`Found ${newRevisions.newRevisionsInSecondary.length} new revisions in secondary database for user ${userUuid.value}`,
|
||||
`Found ${newRevisionsInSecondary.length} new revisions in secondary database for user ${userUuid.value}`,
|
||||
)
|
||||
}
|
||||
|
||||
newRevisionsInSecondaryCount = newRevisions.newRevisionsInSecondary.length
|
||||
newRevisionsInSecondaryCount = newRevisionsInSecondary.length
|
||||
|
||||
if (updatedInSecondary.length > 0) {
|
||||
this.logger.info(
|
||||
`Found ${updatedInSecondary.length} updated revisions in secondary database for user ${userUuid.value}`,
|
||||
)
|
||||
}
|
||||
|
||||
updatedRevisionsInSecondary = updatedInSecondary
|
||||
}
|
||||
|
||||
const updatedRevisionsInSecondaryCount = updatedRevisionsInSecondary.length
|
||||
|
||||
await this.allowForSecondaryDatabaseToCatchUp()
|
||||
|
||||
const migrationTimeStart = this.timer.getTimestampInMicroseconds()
|
||||
|
||||
this.logger.debug(`Transitioning revisions for user ${userUuid.value}`)
|
||||
|
||||
const migrationResult = await this.migrateRevisionsForUser(userUuid)
|
||||
const migrationResult = await this.migrateRevisionsForUser(userUuid, updatedRevisionsInSecondary)
|
||||
if (migrationResult.isFailed()) {
|
||||
if (newRevisionsInSecondaryCount === 0) {
|
||||
if (newRevisionsInSecondaryCount === 0 && updatedRevisionsInSecondaryCount === 0) {
|
||||
const cleanupResult = await this.deleteRevisionsForUser(userUuid, this.secondRevisionsRepository)
|
||||
if (cleanupResult.isFailed()) {
|
||||
this.logger.error(
|
||||
@@ -74,9 +87,10 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
|
||||
const integrityCheckResult = await this.checkIntegrityBetweenPrimaryAndSecondaryDatabase(
|
||||
userUuid,
|
||||
newRevisionsInSecondaryCount,
|
||||
updatedRevisionsInSecondary,
|
||||
)
|
||||
if (integrityCheckResult.isFailed()) {
|
||||
if (newRevisionsInSecondaryCount === 0) {
|
||||
if (newRevisionsInSecondaryCount === 0 && updatedRevisionsInSecondaryCount === 0) {
|
||||
const cleanupResult = await this.deleteRevisionsForUser(userUuid, this.secondRevisionsRepository)
|
||||
if (cleanupResult.isFailed()) {
|
||||
this.logger.error(
|
||||
@@ -107,7 +121,10 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
|
||||
return Result.ok()
|
||||
}
|
||||
|
||||
private async migrateRevisionsForUser(userUuid: Uuid): Promise<Result<void>> {
|
||||
private async migrateRevisionsForUser(
|
||||
userUuid: Uuid,
|
||||
updatedRevisionsInSecondary: Revision[],
|
||||
): Promise<Result<void>> {
|
||||
try {
|
||||
const totalRevisionsCountForUser = await this.primaryRevisionsRepository.countByUserUuid(userUuid)
|
||||
let totalRevisionsCountTransitionedToSecondary = 0
|
||||
@@ -123,6 +140,18 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
|
||||
|
||||
for (const revision of revisions) {
|
||||
try {
|
||||
if (
|
||||
updatedRevisionsInSecondary.find(
|
||||
(updatedRevision) => updatedRevision.id.toString() === revision.id.toString(),
|
||||
)
|
||||
) {
|
||||
this.logger.info(
|
||||
`Skipping saving revision ${revision.id.toString()} as it was updated in secondary database`,
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
this.logger.debug(
|
||||
`Transitioning revision #${
|
||||
totalRevisionsCountTransitionedToSecondary + 1
|
||||
@@ -188,6 +217,7 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
|
||||
private async getNewRevisionsCreatedInSecondaryDatabase(userUuid: Uuid): Promise<{
|
||||
alreadyExistingInPrimary: Revision[]
|
||||
newRevisionsInSecondary: Revision[]
|
||||
updatedInSecondary: Revision[]
|
||||
}> {
|
||||
const revisions = await (this.secondRevisionsRepository as RevisionRepositoryInterface).findByUserUuid({
|
||||
userUuid: userUuid,
|
||||
@@ -195,23 +225,35 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
|
||||
|
||||
const alreadyExistingInPrimary: Revision[] = []
|
||||
const newRevisionsInSecondary: Revision[] = []
|
||||
const updatedInSecondary: Revision[] = []
|
||||
|
||||
for (const revision of revisions) {
|
||||
const revisionExistsInPrimary = await this.checkIfRevisionExistsInPrimaryDatabase(revision)
|
||||
if (revisionExistsInPrimary) {
|
||||
const { revisionInPrimary, newerRevisionInSecondary } =
|
||||
await this.checkIfRevisionExistsInPrimaryDatabase(revision)
|
||||
if (revisionInPrimary !== null) {
|
||||
alreadyExistingInPrimary.push(revision)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
if (newerRevisionInSecondary !== null) {
|
||||
updatedInSecondary.push(newerRevisionInSecondary)
|
||||
continue
|
||||
}
|
||||
if (revisionInPrimary === null && newerRevisionInSecondary === null) {
|
||||
newRevisionsInSecondary.push(revision)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
alreadyExistingInPrimary: alreadyExistingInPrimary,
|
||||
newRevisionsInSecondary: newRevisionsInSecondary,
|
||||
updatedInSecondary: updatedInSecondary,
|
||||
}
|
||||
}
|
||||
|
||||
private async checkIfRevisionExistsInPrimaryDatabase(revision: Revision): Promise<boolean> {
|
||||
private async checkIfRevisionExistsInPrimaryDatabase(
|
||||
revision: Revision,
|
||||
): Promise<{ revisionInPrimary: Revision | null; newerRevisionInSecondary: Revision | null }> {
|
||||
const revisionInPrimary = await this.primaryRevisionsRepository.findOneByUuid(
|
||||
Uuid.create(revision.id.toString()).getValue(),
|
||||
revision.props.userUuid as Uuid,
|
||||
@@ -219,7 +261,10 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
|
||||
)
|
||||
|
||||
if (revisionInPrimary === null) {
|
||||
return false
|
||||
return {
|
||||
revisionInPrimary: null,
|
||||
newerRevisionInSecondary: null,
|
||||
}
|
||||
}
|
||||
|
||||
if (!revision.isIdenticalTo(revisionInPrimary)) {
|
||||
@@ -229,15 +274,23 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
|
||||
)}, revision in primary database: ${JSON.stringify(revisionInPrimary)}`,
|
||||
)
|
||||
|
||||
return false
|
||||
return {
|
||||
revisionInPrimary: null,
|
||||
newerRevisionInSecondary:
|
||||
revision.props.dates.updatedAt > revisionInPrimary.props.dates.updatedAt ? revision : null,
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
return {
|
||||
revisionInPrimary: revisionInPrimary,
|
||||
newerRevisionInSecondary: null,
|
||||
}
|
||||
}
|
||||
|
||||
private async checkIntegrityBetweenPrimaryAndSecondaryDatabase(
|
||||
userUuid: Uuid,
|
||||
newRevisionsInSecondaryCount: number,
|
||||
updatedRevisionsInSecondary: Revision[],
|
||||
): Promise<Result<boolean>> {
|
||||
try {
|
||||
const totalRevisionsCountForUserInPrimary = await this.primaryRevisionsRepository.countByUserUuid(userUuid)
|
||||
@@ -267,6 +320,17 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
|
||||
return Result.fail(`Revision ${revision.id.toString()} not found in secondary database`)
|
||||
}
|
||||
|
||||
if (
|
||||
updatedRevisionsInSecondary.find(
|
||||
(updatedRevision) => updatedRevision.id.toString() === revision.id.toString(),
|
||||
)
|
||||
) {
|
||||
this.logger.info(
|
||||
`Skipping integrity check for revision ${revision.id.toString()} as it was updated in secondary database`,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if (!revision.isIdenticalTo(revisionInSecondary)) {
|
||||
return Result.fail(
|
||||
`Revision ${revision.id.toString()} is not identical in primary and secondary database. Revision in primary database: ${JSON.stringify(
|
||||
|
||||
+1
@@ -23,6 +23,7 @@ describe('TriggerTransitionFromPrimaryToSecondaryDatabaseForUser', () => {
|
||||
|
||||
await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
transitionTimestamp: 123,
|
||||
})
|
||||
|
||||
expect(domainEventPubliser.publish).toHaveBeenCalled()
|
||||
|
||||
+1
@@ -15,6 +15,7 @@ export class TriggerTransitionFromPrimaryToSecondaryDatabaseForUser implements U
|
||||
userUuid: dto.userUuid,
|
||||
status: 'STARTED',
|
||||
transitionType: 'revisions',
|
||||
transitionTimestamp: dto.transitionTimestamp,
|
||||
})
|
||||
|
||||
await this.domainEventPubliser.publish(event)
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
export interface TriggerTransitionFromPrimaryToSecondaryDatabaseForUserDTO {
|
||||
userUuid: string
|
||||
transitionTimestamp: number
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Request, Response } from 'express'
|
||||
import { controller, httpDelete, httpGet, httpPost, results } from 'inversify-express-utils'
|
||||
import { controller, httpDelete, httpGet, results } from 'inversify-express-utils'
|
||||
import { inject } from 'inversify'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
@@ -12,7 +12,6 @@ import { Revision } from '../../Domain/Revision/Revision'
|
||||
import { RevisionMetadata } from '../../Domain/Revision/RevisionMetadata'
|
||||
import { RevisionHttpRepresentation } from '../../Mapping/Http/RevisionHttpRepresentation'
|
||||
import { RevisionMetadataHttpRepresentation } from '../../Mapping/Http/RevisionMetadataHttpRepresentation'
|
||||
import { TriggerTransitionFromPrimaryToSecondaryDatabaseForUser } from '../../Domain/UseCase/Transition/TriggerTransitionFromPrimaryToSecondaryDatabaseForUser/TriggerTransitionFromPrimaryToSecondaryDatabaseForUser'
|
||||
|
||||
@controller('', TYPES.Revisions_ApiGatewayAuthMiddleware)
|
||||
export class AnnotatedRevisionsController extends BaseRevisionsController {
|
||||
@@ -24,17 +23,8 @@ export class AnnotatedRevisionsController extends BaseRevisionsController {
|
||||
override revisionHttpMapper: MapperInterface<Revision, RevisionHttpRepresentation>,
|
||||
@inject(TYPES.Revisions_RevisionMetadataHttpMapper)
|
||||
override revisionMetadataHttpMapper: MapperInterface<RevisionMetadata, RevisionMetadataHttpRepresentation>,
|
||||
@inject(TYPES.Revisions_TriggerTransitionFromPrimaryToSecondaryDatabaseForUser)
|
||||
override triggerTransitionFromPrimaryToSecondaryDatabaseForUser: TriggerTransitionFromPrimaryToSecondaryDatabaseForUser,
|
||||
) {
|
||||
super(
|
||||
getRevisionsMetadata,
|
||||
doGetRevision,
|
||||
doDeleteRevision,
|
||||
revisionHttpMapper,
|
||||
revisionMetadataHttpMapper,
|
||||
triggerTransitionFromPrimaryToSecondaryDatabaseForUser,
|
||||
)
|
||||
super(getRevisionsMetadata, doGetRevision, doDeleteRevision, revisionHttpMapper, revisionMetadataHttpMapper)
|
||||
}
|
||||
|
||||
@httpGet('/items/:itemUuid/revisions')
|
||||
@@ -51,9 +41,4 @@ export class AnnotatedRevisionsController extends BaseRevisionsController {
|
||||
override async deleteRevision(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.deleteRevision(request, response)
|
||||
}
|
||||
|
||||
@httpPost('/revisions/transition')
|
||||
override async transition(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.transition(request, response)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import { GetRevision } from '../../../Domain/UseCase/GetRevision/GetRevision'
|
||||
import { GetRevisionsMetada } from '../../../Domain/UseCase/GetRevisionsMetada/GetRevisionsMetada'
|
||||
import { RevisionHttpRepresentation } from '../../../Mapping/Http/RevisionHttpRepresentation'
|
||||
import { RevisionMetadataHttpRepresentation } from '../../../Mapping/Http/RevisionMetadataHttpRepresentation'
|
||||
import { TriggerTransitionFromPrimaryToSecondaryDatabaseForUser } from '../../../Domain/UseCase/Transition/TriggerTransitionFromPrimaryToSecondaryDatabaseForUser/TriggerTransitionFromPrimaryToSecondaryDatabaseForUser'
|
||||
|
||||
export class BaseRevisionsController extends BaseHttpController {
|
||||
constructor(
|
||||
@@ -20,7 +19,6 @@ export class BaseRevisionsController extends BaseHttpController {
|
||||
protected doDeleteRevision: DeleteRevision,
|
||||
protected revisionHttpMapper: MapperInterface<Revision, RevisionHttpRepresentation>,
|
||||
protected revisionMetadataHttpMapper: MapperInterface<RevisionMetadata, RevisionMetadataHttpRepresentation>,
|
||||
protected triggerTransitionFromPrimaryToSecondaryDatabaseForUser: TriggerTransitionFromPrimaryToSecondaryDatabaseForUser,
|
||||
private controllerContainer?: ControllerContainerInterface,
|
||||
) {
|
||||
super()
|
||||
@@ -29,7 +27,6 @@ export class BaseRevisionsController extends BaseHttpController {
|
||||
this.controllerContainer.register('revisions.revisions.getRevisions', this.getRevisions.bind(this))
|
||||
this.controllerContainer.register('revisions.revisions.getRevision', this.getRevision.bind(this))
|
||||
this.controllerContainer.register('revisions.revisions.deleteRevision', this.deleteRevision.bind(this))
|
||||
this.controllerContainer.register('revisions.revisions.transition', this.transition.bind(this))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,23 +105,4 @@ export class BaseRevisionsController extends BaseHttpController {
|
||||
message: revisionOrError.getValue(),
|
||||
})
|
||||
}
|
||||
|
||||
async transition(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.triggerTransitionFromPrimaryToSecondaryDatabaseForUser.execute({
|
||||
userUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: { message: result.getError() },
|
||||
},
|
||||
400,
|
||||
)
|
||||
}
|
||||
|
||||
response.setHeader('x-invalidate-cache', response.locals.user.uuid)
|
||||
|
||||
return this.json({ success: true })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.95.19](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.95.18...@standardnotes/syncing-server@1.95.19) (2023-09-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** remove unused index in mongodb ([9147ff5](https://github.com/standardnotes/syncing-server-js/commit/9147ff5d49c507d943f4f8c6775f7c1fff878b0f))
|
||||
|
||||
## [1.95.18](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.95.17...@standardnotes/syncing-server@1.95.18) (2023-09-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* skip already updated items and revisions in integrity check ([#825](https://github.com/standardnotes/syncing-server-js/issues/825)) ([03a4a3f](https://github.com/standardnotes/syncing-server-js/commit/03a4a3f2abc0b4e09942ba39dbd227524068dfb6))
|
||||
* **syncing-server:** updating with missing creation date ([#824](https://github.com/standardnotes/syncing-server-js/issues/824)) ([3a8607d](https://github.com/standardnotes/syncing-server-js/commit/3a8607d1465cabedad68b84c753e407342e60d20))
|
||||
|
||||
## [1.95.17](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.95.16...@standardnotes/syncing-server@1.95.17) (2023-09-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adjust transition timestamps to be universal ([c7807d0](https://github.com/standardnotes/syncing-server-js/commit/c7807d0f9e69ce572c4c03ff606375d706f24d9f))
|
||||
|
||||
## [1.95.16](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.95.15...@standardnotes/syncing-server@1.95.16) (2023-09-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* include handling updated items in revisions in secondary ([fbcb45c](https://github.com/standardnotes/syncing-server-js/commit/fbcb45c3a23fde09702fae7bfcb409bdbb610191))
|
||||
|
||||
## [1.95.15](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.95.14...@standardnotes/syncing-server@1.95.15) (2023-09-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -6,16 +6,19 @@ import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||
import TYPES from '../src/Bootstrap/Types'
|
||||
import { Env } from '../src/Bootstrap/Env'
|
||||
import { TriggerTransitionFromPrimaryToSecondaryDatabaseForUser } from '../src/Domain/UseCase/Transition/TriggerTransitionFromPrimaryToSecondaryDatabaseForUser/TriggerTransitionFromPrimaryToSecondaryDatabaseForUser'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
const inputArgs = process.argv.slice(2)
|
||||
const userUuid = inputArgs[0]
|
||||
|
||||
const requestTransition = async (
|
||||
triggerTransitionFromPrimaryToSecondaryDatabaseForUser: TriggerTransitionFromPrimaryToSecondaryDatabaseForUser,
|
||||
timer: TimerInterface,
|
||||
logger: Logger,
|
||||
): Promise<void> => {
|
||||
const result = await triggerTransitionFromPrimaryToSecondaryDatabaseForUser.execute({
|
||||
userUuid,
|
||||
transitionTimestamp: timer.getTimestampInMicroseconds(),
|
||||
})
|
||||
if (result.isFailed()) {
|
||||
logger.error(`Could not trigger transition for user ${userUuid}: ${result.getError()}`)
|
||||
@@ -35,8 +38,9 @@ void container.load().then((container) => {
|
||||
|
||||
const triggerTransitionFromPrimaryToSecondaryDatabaseForUser: TriggerTransitionFromPrimaryToSecondaryDatabaseForUser =
|
||||
container.get(TYPES.Sync_TriggerTransitionFromPrimaryToSecondaryDatabaseForUser)
|
||||
const timer = container.get<TimerInterface>(TYPES.Sync_Timer)
|
||||
|
||||
Promise.resolve(requestTransition(triggerTransitionFromPrimaryToSecondaryDatabaseForUser, logger))
|
||||
Promise.resolve(requestTransition(triggerTransitionFromPrimaryToSecondaryDatabaseForUser, timer, logger))
|
||||
.then(() => {
|
||||
logger.info(`Transition triggered for user ${userUuid}`)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/syncing-server",
|
||||
"version": "1.95.15",
|
||||
"version": "1.95.19",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -1090,9 +1090,6 @@ export class ContainerConfigLoader {
|
||||
container.get<SyncItems>(TYPES.Sync_SyncItems),
|
||||
container.get<CheckIntegrity>(TYPES.Sync_CheckIntegrity),
|
||||
container.get<GetItem>(TYPES.Sync_GetItem),
|
||||
container.get<TriggerTransitionFromPrimaryToSecondaryDatabaseForUser>(
|
||||
TYPES.Sync_TriggerTransitionFromPrimaryToSecondaryDatabaseForUser,
|
||||
),
|
||||
container.get<MapperInterface<Item, ItemHttpRepresentation>>(TYPES.Sync_ItemHttpMapper),
|
||||
container.get<SyncResponseFactoryResolverInterface>(TYPES.Sync_SyncResponseFactoryResolver),
|
||||
container.get<ControllerContainerInterface>(TYPES.Sync_ControllerContainer),
|
||||
|
||||
@@ -172,6 +172,7 @@ export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||
createTransitionStatusUpdatedEvent(dto: {
|
||||
userUuid: string
|
||||
transitionType: 'items' | 'revisions'
|
||||
transitionTimestamp: number
|
||||
status: 'STARTED' | 'FAILED' | 'FINISHED'
|
||||
}): TransitionStatusUpdatedEvent {
|
||||
return {
|
||||
@@ -184,10 +185,7 @@ export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||
},
|
||||
origin: DomainEventService.SyncingServer,
|
||||
},
|
||||
payload: {
|
||||
transitionTimestamp: this.timer.getTimestampInMicroseconds(),
|
||||
...dto,
|
||||
},
|
||||
payload: dto,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ export interface DomainEventFactoryInterface {
|
||||
createTransitionStatusUpdatedEvent(dto: {
|
||||
userUuid: string
|
||||
transitionType: 'items' | 'revisions'
|
||||
transitionTimestamp: number
|
||||
status: 'STARTED' | 'IN_PROGRESS' | 'FAILED' | 'FINISHED'
|
||||
}): TransitionStatusUpdatedEvent
|
||||
createEmailRequestedEvent(dto: {
|
||||
|
||||
@@ -18,6 +18,7 @@ export class TransitionRequestedEventHandler implements DomainEventHandlerInterf
|
||||
|
||||
const result = await this.triggerTransitionFromPrimaryToSecondaryDatabaseForUser.execute({
|
||||
userUuid: event.payload.userUuid,
|
||||
transitionTimestamp: event.payload.timestamp,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
|
||||
@@ -25,6 +25,7 @@ export class TransitionStatusUpdatedEventHandler implements DomainEventHandlerIn
|
||||
userUuid: event.payload.userUuid,
|
||||
status: 'FINISHED',
|
||||
transitionType: 'items',
|
||||
transitionTimestamp: event.payload.transitionTimestamp,
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -36,6 +37,7 @@ export class TransitionStatusUpdatedEventHandler implements DomainEventHandlerIn
|
||||
userUuid: event.payload.userUuid,
|
||||
status: 'IN_PROGRESS',
|
||||
transitionType: 'items',
|
||||
transitionTimestamp: event.payload.transitionTimestamp,
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -51,6 +53,7 @@ export class TransitionStatusUpdatedEventHandler implements DomainEventHandlerIn
|
||||
userUuid: event.payload.userUuid,
|
||||
status: 'FAILED',
|
||||
transitionType: 'items',
|
||||
transitionTimestamp: event.payload.transitionTimestamp,
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -62,6 +65,7 @@ export class TransitionStatusUpdatedEventHandler implements DomainEventHandlerIn
|
||||
userUuid: event.payload.userUuid,
|
||||
status: 'FINISHED',
|
||||
transitionType: 'items',
|
||||
transitionTimestamp: event.payload.transitionTimestamp,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
+48
-2
@@ -298,7 +298,7 @@ describe('UpdateExistingItem', () => {
|
||||
expect(itemRepository.save).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return error if created at time is not give in any form', async () => {
|
||||
it('should fallback to updated at timestamp if created at time is not give in any form', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
@@ -308,13 +308,59 @@ describe('UpdateExistingItem', () => {
|
||||
...itemHash1.props,
|
||||
created_at: undefined,
|
||||
created_at_timestamp: undefined,
|
||||
updated_at_timestamp: 123,
|
||||
}).getValue(),
|
||||
sessionUuid: '00000000-0000-0000-0000-000000000000',
|
||||
performingUserUuid: '00000000-0000-0000-0000-000000000000',
|
||||
roleNames: [RoleName.NAMES.CoreUser],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
expect(itemRepository.save).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fallback to updated at date if created at time is not give in any form', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
existingItem: item1,
|
||||
onGoingRevisionsTransition: false,
|
||||
itemHash: ItemHash.create({
|
||||
...itemHash1.props,
|
||||
created_at: undefined,
|
||||
created_at_timestamp: undefined,
|
||||
updated_at_timestamp: undefined,
|
||||
updated_at: '2020-01-01T00:00:00.000Z',
|
||||
}).getValue(),
|
||||
sessionUuid: '00000000-0000-0000-0000-000000000000',
|
||||
performingUserUuid: '00000000-0000-0000-0000-000000000000',
|
||||
roleNames: [RoleName.NAMES.CoreUser],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
expect(itemRepository.save).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fallback to 0 if created at and update at time is not give in any form', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
existingItem: item1,
|
||||
onGoingRevisionsTransition: false,
|
||||
itemHash: ItemHash.create({
|
||||
...itemHash1.props,
|
||||
created_at: undefined,
|
||||
created_at_timestamp: undefined,
|
||||
updated_at_timestamp: undefined,
|
||||
updated_at: undefined,
|
||||
}).getValue(),
|
||||
sessionUuid: '00000000-0000-0000-0000-000000000000',
|
||||
performingUserUuid: '00000000-0000-0000-0000-000000000000',
|
||||
roleNames: [RoleName.NAMES.CoreUser],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
expect(itemRepository.save).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return error if dates could not be created from timestamps', async () => {
|
||||
|
||||
+25
-11
@@ -23,6 +23,7 @@ import { SharedVaultOperationOnItem } from '../../../SharedVault/SharedVaultOper
|
||||
import { AddNotificationForUser } from '../../Messaging/AddNotificationForUser/AddNotificationForUser'
|
||||
import { RemoveNotificationsForUser } from '../../Messaging/RemoveNotificationsForUser/RemoveNotificationsForUser'
|
||||
import { ItemRepositoryResolverInterface } from '../../../Item/ItemRepositoryResolverInterface'
|
||||
import { ItemHash } from '../../../Item/ItemHash'
|
||||
|
||||
export class UpdateExistingItem implements UseCaseInterface<Item> {
|
||||
constructor(
|
||||
@@ -115,17 +116,7 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
|
||||
)
|
||||
const updatedAtDate = this.timer.convertMicrosecondsToDate(updatedAtTimestamp)
|
||||
|
||||
let createdAtTimestamp: number
|
||||
let createdAtDate: Date
|
||||
if (dto.itemHash.props.created_at_timestamp) {
|
||||
createdAtTimestamp = dto.itemHash.props.created_at_timestamp
|
||||
createdAtDate = this.timer.convertMicrosecondsToDate(createdAtTimestamp)
|
||||
} else if (dto.itemHash.props.created_at) {
|
||||
createdAtTimestamp = this.timer.convertStringDateToMicroseconds(dto.itemHash.props.created_at)
|
||||
createdAtDate = this.timer.convertStringDateToDate(dto.itemHash.props.created_at)
|
||||
} else {
|
||||
return Result.fail('Created at timestamp is required.')
|
||||
}
|
||||
const { createdAtDate, createdAtTimestamp } = this.determineCreatedAt(dto.itemHash)
|
||||
|
||||
const datesOrError = Dates.create(createdAtDate, updatedAtDate)
|
||||
if (datesOrError.isFailed()) {
|
||||
@@ -221,6 +212,29 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
|
||||
return Result.ok(dto.existingItem)
|
||||
}
|
||||
|
||||
private determineCreatedAt(itemHash: ItemHash): { createdAtDate: Date; createdAtTimestamp: number } {
|
||||
let createdAtTimestamp: number
|
||||
let createdAtDate: Date
|
||||
if (itemHash.props.created_at_timestamp) {
|
||||
createdAtTimestamp = itemHash.props.created_at_timestamp
|
||||
createdAtDate = this.timer.convertMicrosecondsToDate(createdAtTimestamp)
|
||||
} else if (itemHash.props.created_at) {
|
||||
createdAtTimestamp = this.timer.convertStringDateToMicroseconds(itemHash.props.created_at)
|
||||
createdAtDate = this.timer.convertStringDateToDate(itemHash.props.created_at)
|
||||
} else if (itemHash.props.updated_at_timestamp) {
|
||||
createdAtTimestamp = itemHash.props.updated_at_timestamp
|
||||
createdAtDate = this.timer.convertMicrosecondsToDate(itemHash.props.updated_at_timestamp)
|
||||
} else if (itemHash.props.updated_at) {
|
||||
createdAtTimestamp = this.timer.convertStringDateToMicroseconds(itemHash.props.updated_at)
|
||||
createdAtDate = this.timer.convertStringDateToDate(itemHash.props.updated_at)
|
||||
} else {
|
||||
createdAtTimestamp = 0
|
||||
createdAtDate = new Date(0)
|
||||
}
|
||||
|
||||
return { createdAtDate, createdAtTimestamp }
|
||||
}
|
||||
|
||||
private async addNotificationsAndPublishEvents(
|
||||
userUuid: Uuid,
|
||||
sharedVaultOperation: SharedVaultOperationOnItem | null,
|
||||
|
||||
+59
-18
@@ -31,29 +31,41 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
let newItemsInSecondaryCount = 0
|
||||
let updatedItemsInSecondary: Item[] = []
|
||||
if (await this.hasAlreadyDataInSecondaryDatabase(userUuid)) {
|
||||
const newItems = await this.getNewItemsCreatedInSecondaryDatabase(userUuid)
|
||||
for (const existingItem of newItems.alreadyExistingInPrimary) {
|
||||
const { alreadyExistingInPrimary, newItemsInSecondary, updatedInSecondary } =
|
||||
await this.getNewItemsCreatedInSecondaryDatabase(userUuid)
|
||||
|
||||
for (const existingItem of alreadyExistingInPrimary) {
|
||||
this.logger.info(`Removing item ${existingItem.uuid.value} from secondary database`)
|
||||
await (this.secondaryItemRepository as ItemRepositoryInterface).remove(existingItem)
|
||||
}
|
||||
|
||||
if (newItems.newItemsInSecondary.length > 0) {
|
||||
if (newItemsInSecondary.length > 0) {
|
||||
this.logger.info(
|
||||
`Found ${newItems.newItemsInSecondary.length} new items in secondary database for user ${userUuid.value}`,
|
||||
`Found ${newItemsInSecondary.length} new items in secondary database for user ${userUuid.value}`,
|
||||
)
|
||||
}
|
||||
|
||||
newItemsInSecondaryCount = newItems.newItemsInSecondary.length
|
||||
newItemsInSecondaryCount = newItemsInSecondary.length
|
||||
|
||||
if (updatedInSecondary.length > 0) {
|
||||
this.logger.info(
|
||||
`Found ${updatedInSecondary.length} updated items in secondary database for user ${userUuid.value}`,
|
||||
)
|
||||
}
|
||||
|
||||
updatedItemsInSecondary = updatedInSecondary
|
||||
}
|
||||
const updatedItemsInSecondaryCount = updatedItemsInSecondary.length
|
||||
|
||||
await this.allowForSecondaryDatabaseToCatchUp()
|
||||
|
||||
const migrationTimeStart = this.timer.getTimestampInMicroseconds()
|
||||
|
||||
const migrationResult = await this.migrateItemsForUser(userUuid)
|
||||
const migrationResult = await this.migrateItemsForUser(userUuid, updatedItemsInSecondary)
|
||||
if (migrationResult.isFailed()) {
|
||||
if (newItemsInSecondaryCount === 0) {
|
||||
if (newItemsInSecondaryCount === 0 && updatedItemsInSecondaryCount === 0) {
|
||||
const cleanupResult = await this.deleteItemsForUser(userUuid, this.secondaryItemRepository)
|
||||
if (cleanupResult.isFailed()) {
|
||||
this.logger.error(
|
||||
@@ -70,9 +82,10 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
|
||||
const integrityCheckResult = await this.checkIntegrityBetweenPrimaryAndSecondaryDatabase(
|
||||
userUuid,
|
||||
newItemsInSecondaryCount,
|
||||
updatedItemsInSecondary,
|
||||
)
|
||||
if (integrityCheckResult.isFailed()) {
|
||||
if (newItemsInSecondaryCount === 0) {
|
||||
if (newItemsInSecondaryCount === 0 && updatedItemsInSecondaryCount === 0) {
|
||||
const cleanupResult = await this.deleteItemsForUser(userUuid, this.secondaryItemRepository)
|
||||
if (cleanupResult.isFailed()) {
|
||||
this.logger.error(
|
||||
@@ -124,34 +137,46 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
|
||||
private async getNewItemsCreatedInSecondaryDatabase(userUuid: Uuid): Promise<{
|
||||
alreadyExistingInPrimary: Item[]
|
||||
newItemsInSecondary: Item[]
|
||||
updatedInSecondary: Item[]
|
||||
}> {
|
||||
const items = await (this.secondaryItemRepository as ItemRepositoryInterface).findAll({
|
||||
userUuid: userUuid.value,
|
||||
})
|
||||
|
||||
const alreadyExistingInPrimary: Item[] = []
|
||||
const updatedInSecondary: Item[] = []
|
||||
const newItemsInSecondary: Item[] = []
|
||||
|
||||
for (const item of items) {
|
||||
const itemExistsInPrimary = await this.checkIfItemExistsInPrimaryDatabase(item)
|
||||
if (itemExistsInPrimary) {
|
||||
const { itemInPrimary, newerItemInSecondary } = await this.checkIfItemExistsInPrimaryDatabase(item)
|
||||
if (itemInPrimary !== null) {
|
||||
alreadyExistingInPrimary.push(item)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
if (newerItemInSecondary !== null) {
|
||||
updatedInSecondary.push(newerItemInSecondary)
|
||||
continue
|
||||
}
|
||||
if (itemInPrimary === null && newerItemInSecondary === null) {
|
||||
newItemsInSecondary.push(item)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
alreadyExistingInPrimary: alreadyExistingInPrimary,
|
||||
newItemsInSecondary: newItemsInSecondary,
|
||||
alreadyExistingInPrimary,
|
||||
newItemsInSecondary,
|
||||
updatedInSecondary,
|
||||
}
|
||||
}
|
||||
|
||||
private async checkIfItemExistsInPrimaryDatabase(item: Item): Promise<boolean> {
|
||||
private async checkIfItemExistsInPrimaryDatabase(
|
||||
item: Item,
|
||||
): Promise<{ itemInPrimary: Item | null; newerItemInSecondary: Item | null }> {
|
||||
const itemInPrimary = await this.primaryItemRepository.findByUuid(item.uuid)
|
||||
|
||||
if (itemInPrimary === null) {
|
||||
return false
|
||||
return { itemInPrimary: null, newerItemInSecondary: null }
|
||||
}
|
||||
|
||||
if (!item.isIdenticalTo(itemInPrimary)) {
|
||||
@@ -161,13 +186,16 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
|
||||
)}, revision in primary database: ${JSON.stringify(itemInPrimary)}`,
|
||||
)
|
||||
|
||||
return false
|
||||
return {
|
||||
itemInPrimary: null,
|
||||
newerItemInSecondary: item.props.timestamps.updatedAt > itemInPrimary.props.timestamps.updatedAt ? item : null,
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
return { itemInPrimary: itemInPrimary, newerItemInSecondary: null }
|
||||
}
|
||||
|
||||
private async migrateItemsForUser(userUuid: Uuid): Promise<Result<void>> {
|
||||
private async migrateItemsForUser(userUuid: Uuid, updatedItemsInSecondary: Item[]): Promise<Result<void>> {
|
||||
try {
|
||||
const totalItemsCountForUser = await this.primaryItemRepository.countAll({ userUuid: userUuid.value })
|
||||
const totalPages = Math.ceil(totalItemsCountForUser / this.pageSize)
|
||||
@@ -181,6 +209,11 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
|
||||
const items = await this.primaryItemRepository.findAll(query)
|
||||
|
||||
for (const item of items) {
|
||||
if (updatedItemsInSecondary.find((updatedItem) => updatedItem.uuid.equals(item.uuid))) {
|
||||
this.logger.info(`Skipping saving item ${item.uuid.value} as it was updated in secondary database`)
|
||||
|
||||
continue
|
||||
}
|
||||
await (this.secondaryItemRepository as ItemRepositoryInterface).save(item)
|
||||
}
|
||||
}
|
||||
@@ -204,6 +237,7 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
|
||||
private async checkIntegrityBetweenPrimaryAndSecondaryDatabase(
|
||||
userUuid: Uuid,
|
||||
newItemsInSecondaryCount: number,
|
||||
updatedItemsInSecondary: Item[],
|
||||
): Promise<Result<boolean>> {
|
||||
try {
|
||||
const totalItemsCountForUserInPrimary = await this.primaryItemRepository.countAll({ userUuid: userUuid.value })
|
||||
@@ -235,6 +269,13 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
|
||||
return Result.fail(`Item ${item.uuid.value} not found in secondary database`)
|
||||
}
|
||||
|
||||
if (updatedItemsInSecondary.find((updatedItem) => updatedItem.uuid.equals(item.uuid))) {
|
||||
this.logger.info(
|
||||
`Skipping integrity check for item ${item.uuid.value} as it was updated in secondary database`,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if (!item.isIdenticalTo(itemInSecondary)) {
|
||||
return Result.fail(
|
||||
`Item ${
|
||||
|
||||
+1
@@ -23,6 +23,7 @@ describe('TriggerTransitionFromPrimaryToSecondaryDatabaseForUser', () => {
|
||||
|
||||
await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
transitionTimestamp: 123,
|
||||
})
|
||||
|
||||
expect(domainEventPubliser.publish).toHaveBeenCalled()
|
||||
|
||||
+1
@@ -15,6 +15,7 @@ export class TriggerTransitionFromPrimaryToSecondaryDatabaseForUser implements U
|
||||
userUuid: dto.userUuid,
|
||||
status: 'STARTED',
|
||||
transitionType: 'items',
|
||||
transitionTimestamp: dto.transitionTimestamp,
|
||||
})
|
||||
|
||||
await this.domainEventPubliser.publish(event)
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
export interface TriggerTransitionFromPrimaryToSecondaryDatabaseForUserDTO {
|
||||
userUuid: string
|
||||
transitionTimestamp: number
|
||||
}
|
||||
|
||||
+1
-16
@@ -11,7 +11,6 @@ import { SyncItems } from '../../Domain/UseCase/Syncing/SyncItems/SyncItems'
|
||||
import { BaseItemsController } from './Base/BaseItemsController'
|
||||
import { MapperInterface } from '@standardnotes/domain-core'
|
||||
import { ItemHttpRepresentation } from '../../Mapping/Http/ItemHttpRepresentation'
|
||||
import { TriggerTransitionFromPrimaryToSecondaryDatabaseForUser } from '../../Domain/UseCase/Transition/TriggerTransitionFromPrimaryToSecondaryDatabaseForUser/TriggerTransitionFromPrimaryToSecondaryDatabaseForUser'
|
||||
|
||||
@controller('/items', TYPES.Sync_AuthMiddleware)
|
||||
export class AnnotatedItemsController extends BaseItemsController {
|
||||
@@ -19,20 +18,11 @@ export class AnnotatedItemsController extends BaseItemsController {
|
||||
@inject(TYPES.Sync_SyncItems) override syncItems: SyncItems,
|
||||
@inject(TYPES.Sync_CheckIntegrity) override checkIntegrity: CheckIntegrity,
|
||||
@inject(TYPES.Sync_GetItem) override getItem: GetItem,
|
||||
@inject(TYPES.Sync_TriggerTransitionFromPrimaryToSecondaryDatabaseForUser)
|
||||
override triggerTransitionFromPrimaryToSecondaryDatabaseForUser: TriggerTransitionFromPrimaryToSecondaryDatabaseForUser,
|
||||
@inject(TYPES.Sync_ItemHttpMapper) override itemHttpMapper: MapperInterface<Item, ItemHttpRepresentation>,
|
||||
@inject(TYPES.Sync_SyncResponseFactoryResolver)
|
||||
override syncResponseFactoryResolver: SyncResponseFactoryResolverInterface,
|
||||
) {
|
||||
super(
|
||||
syncItems,
|
||||
checkIntegrity,
|
||||
getItem,
|
||||
triggerTransitionFromPrimaryToSecondaryDatabaseForUser,
|
||||
itemHttpMapper,
|
||||
syncResponseFactoryResolver,
|
||||
)
|
||||
super(syncItems, checkIntegrity, getItem, itemHttpMapper, syncResponseFactoryResolver)
|
||||
}
|
||||
|
||||
@httpPost('/sync')
|
||||
@@ -45,11 +35,6 @@ export class AnnotatedItemsController extends BaseItemsController {
|
||||
return super.checkItemsIntegrity(request, response)
|
||||
}
|
||||
|
||||
@httpPost('/transition')
|
||||
override async transition(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.transition(request, response)
|
||||
}
|
||||
|
||||
@httpGet('/:uuid')
|
||||
override async getSingleItem(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.getSingleItem(request, response)
|
||||
|
||||
@@ -12,14 +12,12 @@ import { ApiVersion } from '../../../Domain/Api/ApiVersion'
|
||||
import { SyncItems } from '../../../Domain/UseCase/Syncing/SyncItems/SyncItems'
|
||||
import { ItemHttpRepresentation } from '../../../Mapping/Http/ItemHttpRepresentation'
|
||||
import { ItemHash } from '../../../Domain/Item/ItemHash'
|
||||
import { TriggerTransitionFromPrimaryToSecondaryDatabaseForUser } from '../../../Domain/UseCase/Transition/TriggerTransitionFromPrimaryToSecondaryDatabaseForUser/TriggerTransitionFromPrimaryToSecondaryDatabaseForUser'
|
||||
|
||||
export class BaseItemsController extends BaseHttpController {
|
||||
constructor(
|
||||
protected syncItems: SyncItems,
|
||||
protected checkIntegrity: CheckIntegrity,
|
||||
protected getItem: GetItem,
|
||||
protected triggerTransitionFromPrimaryToSecondaryDatabaseForUser: TriggerTransitionFromPrimaryToSecondaryDatabaseForUser,
|
||||
protected itemHttpMapper: MapperInterface<Item, ItemHttpRepresentation>,
|
||||
protected syncResponseFactoryResolver: SyncResponseFactoryResolverInterface,
|
||||
private controllerContainer?: ControllerContainerInterface,
|
||||
@@ -30,7 +28,6 @@ export class BaseItemsController extends BaseHttpController {
|
||||
this.controllerContainer.register('sync.items.sync', this.sync.bind(this))
|
||||
this.controllerContainer.register('sync.items.check_integrity', this.checkItemsIntegrity.bind(this))
|
||||
this.controllerContainer.register('sync.items.get_item', this.getSingleItem.bind(this))
|
||||
this.controllerContainer.register('sync.items.transition', this.transition.bind(this))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,25 +110,6 @@ export class BaseItemsController extends BaseHttpController {
|
||||
})
|
||||
}
|
||||
|
||||
async transition(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.triggerTransitionFromPrimaryToSecondaryDatabaseForUser.execute({
|
||||
userUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: { message: result.getError() },
|
||||
},
|
||||
400,
|
||||
)
|
||||
}
|
||||
|
||||
response.setHeader('x-invalidate-cache', response.locals.user.uuid)
|
||||
|
||||
return this.json({ success: true })
|
||||
}
|
||||
|
||||
async getSingleItem(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.getItem.execute({
|
||||
userUuid: response.locals.user.uuid,
|
||||
|
||||
@@ -18,7 +18,6 @@ export class MongoDBItem {
|
||||
declare content: string | null
|
||||
|
||||
@Column()
|
||||
@Index('index_items_on_content_type')
|
||||
declare contentType: string | null
|
||||
|
||||
@Column()
|
||||
|
||||
@@ -2659,10 +2659,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hexagon/base64@npm:^1.1.25":
|
||||
version: 1.1.26
|
||||
resolution: "@hexagon/base64@npm:1.1.26"
|
||||
checksum: e42582ed12465bffaf96307c9d5b7dfd36166ec4dc41a1838b9a560c90c9d136d006099a205e1684fb0dc18002cc5af51a49dd7b81a7c4b86798372d6ee26af3
|
||||
"@hexagon/base64@npm:^1.1.27":
|
||||
version: 1.1.27
|
||||
resolution: "@hexagon/base64@npm:1.1.27"
|
||||
checksum: 899fffaf54b291e1df997bf33dbf6e068fcfbd83155adc114e14bcb9c1e36c5f820dfaaee3d0c2409f7e84efa4352f51655eac8bec4c2432fca443bf179bce8d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -3793,7 +3793,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@peculiar/asn1-android@npm:^2.3.3":
|
||||
"@peculiar/asn1-android@npm:^2.3.6":
|
||||
version: 2.3.6
|
||||
resolution: "@peculiar/asn1-android@npm:2.3.6"
|
||||
dependencies:
|
||||
@@ -3804,7 +3804,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@peculiar/asn1-ecc@npm:^2.3.4":
|
||||
"@peculiar/asn1-ecc@npm:^2.3.6":
|
||||
version: 2.3.6
|
||||
resolution: "@peculiar/asn1-ecc@npm:2.3.6"
|
||||
dependencies:
|
||||
@@ -3816,7 +3816,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@peculiar/asn1-rsa@npm:^2.3.4":
|
||||
"@peculiar/asn1-rsa@npm:^2.3.6":
|
||||
version: 2.3.6
|
||||
resolution: "@peculiar/asn1-rsa@npm:2.3.6"
|
||||
dependencies:
|
||||
@@ -3828,7 +3828,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@peculiar/asn1-schema@npm:^2.3.3, @peculiar/asn1-schema@npm:^2.3.6":
|
||||
"@peculiar/asn1-schema@npm:^2.3.6":
|
||||
version: 2.3.6
|
||||
resolution: "@peculiar/asn1-schema@npm:2.3.6"
|
||||
dependencies:
|
||||
@@ -3839,7 +3839,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@peculiar/asn1-x509@npm:^2.3.4, @peculiar/asn1-x509@npm:^2.3.6":
|
||||
"@peculiar/asn1-x509@npm:^2.3.6":
|
||||
version: 2.3.6
|
||||
resolution: "@peculiar/asn1-x509@npm:2.3.6"
|
||||
dependencies:
|
||||
@@ -3987,41 +3987,27 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@simplewebauthn/iso-webcrypto@npm:^7.2.0":
|
||||
version: 7.2.0
|
||||
resolution: "@simplewebauthn/iso-webcrypto@npm:7.2.0"
|
||||
"@simplewebauthn/server@npm:^8.1.1":
|
||||
version: 8.1.1
|
||||
resolution: "@simplewebauthn/server@npm:8.1.1"
|
||||
dependencies:
|
||||
"@simplewebauthn/typescript-types": "npm:*"
|
||||
"@types/node": "npm:^18.11.9"
|
||||
checksum: b57899d0ada391507ce8f4601328ed62df5d09f75f6e91b018278631270a96f37ceab95f9e824c9555dd05820e5a99ac386ed067db7902d37bdb6d995fbd7eaf
|
||||
"@hexagon/base64": "npm:^1.1.27"
|
||||
"@peculiar/asn1-android": "npm:^2.3.6"
|
||||
"@peculiar/asn1-ecc": "npm:^2.3.6"
|
||||
"@peculiar/asn1-rsa": "npm:^2.3.6"
|
||||
"@peculiar/asn1-schema": "npm:^2.3.6"
|
||||
"@peculiar/asn1-x509": "npm:^2.3.6"
|
||||
"@simplewebauthn/typescript-types": "npm:^8.0.0"
|
||||
cbor-x: "npm:^1.5.2"
|
||||
cross-fetch: "npm:^4.0.0"
|
||||
checksum: a07c2a067b25b7f4afe215dcf81e0280aa5c382f5eb3cb1ef9327c6dc84c9618ed5774fc61bbc5717b8633dd5b8b4c41c7800cb32e83c6a4b1d2d1e1e50f5250
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@simplewebauthn/server@npm:^7.2.0":
|
||||
version: 7.2.0
|
||||
resolution: "@simplewebauthn/server@npm:7.2.0"
|
||||
dependencies:
|
||||
"@hexagon/base64": "npm:^1.1.25"
|
||||
"@peculiar/asn1-android": "npm:^2.3.3"
|
||||
"@peculiar/asn1-ecc": "npm:^2.3.4"
|
||||
"@peculiar/asn1-rsa": "npm:^2.3.4"
|
||||
"@peculiar/asn1-schema": "npm:^2.3.3"
|
||||
"@peculiar/asn1-x509": "npm:^2.3.4"
|
||||
"@simplewebauthn/iso-webcrypto": "npm:^7.2.0"
|
||||
"@simplewebauthn/typescript-types": "npm:*"
|
||||
"@types/debug": "npm:^4.1.7"
|
||||
"@types/node": "npm:^18.11.9"
|
||||
cbor-x: "npm:^1.4.1"
|
||||
cross-fetch: "npm:^3.1.5"
|
||||
debug: "npm:^4.3.2"
|
||||
checksum: 2e37c87edd05abace8ba8c5b1f4e2cb4adb9ec4dcf0b237d25f375f35538d25e31cc0ae196029006cf5124c983216a3cf69127732942863d2960bd72ed5783c4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@simplewebauthn/typescript-types@npm:*, @simplewebauthn/typescript-types@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "@simplewebauthn/typescript-types@npm:7.0.0"
|
||||
checksum: 124238ea1859c80761c4cdbf19107e2e8e96fdefa64affb55fb4fc67d1ac5e3354c3098c908729d2de439a633115d98da77ded7289286fe576559306fa933815
|
||||
"@simplewebauthn/typescript-types@npm:^8.0.0":
|
||||
version: 8.0.0
|
||||
resolution: "@simplewebauthn/typescript-types@npm:8.0.0"
|
||||
checksum: 21e0b13268f237d7cd6ecdc6cdceb884ddcc85e18a3554b65d10da4f502056aefa810db94f504beaad96b4ac0c7861a022954dcc6aa8721d4215571c2c3dcdf5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -4744,8 +4730,8 @@ __metadata:
|
||||
"@cbor-extract/cbor-extract-linux-arm64": "npm:^2.1.1"
|
||||
"@cbor-extract/cbor-extract-linux-x64": "npm:^2.1.1"
|
||||
"@newrelic/winston-enricher": "npm:^4.0.1"
|
||||
"@simplewebauthn/server": "npm:^7.2.0"
|
||||
"@simplewebauthn/typescript-types": "npm:^7.0.0"
|
||||
"@simplewebauthn/server": "npm:^8.1.1"
|
||||
"@simplewebauthn/typescript-types": "npm:^8.0.0"
|
||||
"@standardnotes/api": "npm:^1.26.26"
|
||||
"@standardnotes/common": "workspace:*"
|
||||
"@standardnotes/domain-core": "workspace:^"
|
||||
@@ -5589,15 +5575,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/debug@npm:^4.1.7":
|
||||
version: 4.1.8
|
||||
resolution: "@types/debug@npm:4.1.8"
|
||||
dependencies:
|
||||
"@types/ms": "npm:*"
|
||||
checksum: 9c190e812984e0f6e02dfdfb0c7a3081a55cf3fc712a4e059336bd9f8329db70211eb851ce409311520876549cff2c4785ce48dd4c9fef8e48549c87bec29ded
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/dotenv@npm:^8.2.0":
|
||||
version: 8.2.0
|
||||
resolution: "@types/dotenv@npm:8.2.0"
|
||||
@@ -5792,13 +5769,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/ms@npm:*":
|
||||
version: 0.7.31
|
||||
resolution: "@types/ms@npm:0.7.31"
|
||||
checksum: cccb52777bb683c65ac5bab61351cd3910c9ce3512b1d903a591fc9694bb83afad6e48bf0beee5b47b6a8b620a05f5d82f8febfd55de05e7d9eb93586cc196c8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/newrelic@npm:^9.14.0":
|
||||
version: 9.14.0
|
||||
resolution: "@types/newrelic@npm:9.14.0"
|
||||
@@ -5820,13 +5790,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:^18.11.9":
|
||||
version: 18.16.16
|
||||
resolution: "@types/node@npm:18.16.16"
|
||||
checksum: 946bd4d8e6fa54220e4193bc594de8a2e138e6afebb6efb7d862d98e30ced25a19476a6f47c81e690b9ac77f616f64217e0bcf4811916ccd9b5935e5bea0e4a0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:^20.5.7":
|
||||
version: 20.5.7
|
||||
resolution: "@types/node@npm:20.5.7"
|
||||
@@ -7208,15 +7171,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cbor-x@npm:^1.4.1":
|
||||
version: 1.5.3
|
||||
resolution: "cbor-x@npm:1.5.3"
|
||||
"cbor-x@npm:^1.5.2":
|
||||
version: 1.5.4
|
||||
resolution: "cbor-x@npm:1.5.4"
|
||||
dependencies:
|
||||
cbor-extract: "npm:^2.1.1"
|
||||
dependenciesMeta:
|
||||
cbor-extract:
|
||||
optional: true
|
||||
checksum: d4df85b33969826f4c96a4b4a8fbe03132fb0817fba876f16d41ad6d1a7d2668ec04c923f313220506029cc2b5ab212901ba24b4594d0115e0f527ef31506fbf
|
||||
checksum: 742aea498abfe004a7ff4db2a1c0e00d9e9c1d89db4ad9aa94a9b886cd2ce10a133f20e32788c83696eef368e18c2b5bc82e4b1480c5af91937816a5630989d6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -7848,12 +7811,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cross-fetch@npm:^3.1.5":
|
||||
version: 3.1.6
|
||||
resolution: "cross-fetch@npm:3.1.6"
|
||||
"cross-fetch@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "cross-fetch@npm:4.0.0"
|
||||
dependencies:
|
||||
node-fetch: "npm:^2.6.11"
|
||||
checksum: a8989fca821cae97520976d00f85ce7c3ab8af7e00cc06c94fd94c49ada6847f4cdeabca8e0ebd4aa6c7343f70bea7e0c64d5910b846aab218136a450585aa61
|
||||
node-fetch: "npm:^2.6.12"
|
||||
checksum: 30e86b703a455baca17b7f2088fdd88b71193b39e7cb61f3385511dc6064b7741c816329c0abff8a74d306969455c8797131d056518a981fd4d2424ecd4ab451
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -12007,7 +11970,21 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-fetch@npm:^2.6.11, node-fetch@npm:^2.6.7":
|
||||
"node-fetch@npm:^2.6.12":
|
||||
version: 2.7.0
|
||||
resolution: "node-fetch@npm:2.7.0"
|
||||
dependencies:
|
||||
whatwg-url: "npm:^5.0.0"
|
||||
peerDependencies:
|
||||
encoding: ^0.1.0
|
||||
peerDependenciesMeta:
|
||||
encoding:
|
||||
optional: true
|
||||
checksum: a3ad7889038bf6c49046272515d4f0e3167088b40fd37e1cc6eeea745f5a68cec798d55ac3210e2bc51891cb745e3dc30a734cc5f4b4df764f45886881b198b1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-fetch@npm:^2.6.7":
|
||||
version: 2.6.11
|
||||
resolution: "node-fetch@npm:2.6.11"
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user