mirror of
https://github.com/standardnotes/server
synced 2026-04-29 12:01:25 -04:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4715e019a2 | |||
| 794cd8734a | |||
| 14d42b26bb | |||
| 6bb44afd91 | |||
| c82345aeeb | |||
| 72ab08a0d0 |
@@ -275,6 +275,51 @@ const RAW_RUNTIME_STATE =
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/client-apigatewaymanagementapi", [\
|
||||
["npm:3.405.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-client-apigatewaymanagementapi-npm-3.405.0-e4e17d811f-d7103d0b37.zip/node_modules/@aws-sdk/client-apigatewaymanagementapi/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/client-apigatewaymanagementapi", "npm:3.405.0"],\
|
||||
["@aws-crypto/sha256-browser", "npm:3.0.0"],\
|
||||
["@aws-crypto/sha256-js", "npm:3.0.0"],\
|
||||
["@aws-sdk/client-sts", "npm:3.405.0"],\
|
||||
["@aws-sdk/credential-provider-node", "npm:3.405.0"],\
|
||||
["@aws-sdk/middleware-host-header", "npm:3.398.0"],\
|
||||
["@aws-sdk/middleware-logger", "npm:3.398.0"],\
|
||||
["@aws-sdk/middleware-recursion-detection", "npm:3.398.0"],\
|
||||
["@aws-sdk/middleware-signing", "npm:3.398.0"],\
|
||||
["@aws-sdk/middleware-user-agent", "npm:3.398.0"],\
|
||||
["@aws-sdk/types", "npm:3.398.0"],\
|
||||
["@aws-sdk/util-endpoints", "npm:3.398.0"],\
|
||||
["@aws-sdk/util-user-agent-browser", "npm:3.398.0"],\
|
||||
["@aws-sdk/util-user-agent-node", "virtual:0b227682399940b7e75175c6ae399b938ec07d3986be60e73f8026d2677e9fe96d05ac258f3f223cf1b6bdd7fd2a24f55f0efa40a8406ba949dc1b7af3a6a968#npm:3.405.0"],\
|
||||
["@smithy/config-resolver", "npm:2.0.5"],\
|
||||
["@smithy/fetch-http-handler", "npm:2.0.5"],\
|
||||
["@smithy/hash-node", "npm:2.0.5"],\
|
||||
["@smithy/invalid-dependency", "npm:2.0.5"],\
|
||||
["@smithy/middleware-content-length", "npm:2.0.5"],\
|
||||
["@smithy/middleware-endpoint", "npm:2.0.5"],\
|
||||
["@smithy/middleware-retry", "npm:2.0.5"],\
|
||||
["@smithy/middleware-serde", "npm:2.0.5"],\
|
||||
["@smithy/middleware-stack", "npm:2.0.0"],\
|
||||
["@smithy/node-config-provider", "npm:2.0.7"],\
|
||||
["@smithy/node-http-handler", "npm:2.0.5"],\
|
||||
["@smithy/protocol-http", "npm:2.0.5"],\
|
||||
["@smithy/smithy-client", "npm:2.0.5"],\
|
||||
["@smithy/types", "npm:2.2.2"],\
|
||||
["@smithy/url-parser", "npm:2.0.5"],\
|
||||
["@smithy/util-base64", "npm:2.0.0"],\
|
||||
["@smithy/util-body-length-browser", "npm:2.0.0"],\
|
||||
["@smithy/util-body-length-node", "npm:2.1.0"],\
|
||||
["@smithy/util-defaults-mode-browser", "npm:2.0.6"],\
|
||||
["@smithy/util-defaults-mode-node", "npm:2.0.7"],\
|
||||
["@smithy/util-retry", "npm:2.0.0"],\
|
||||
["@smithy/util-utf8", "npm:2.0.0"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/client-lambda", [\
|
||||
["npm:3.398.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-client-lambda-npm-3.398.0-fa4aacfc7b-85ef0fe18d.zip/node_modules/@aws-sdk/client-lambda/",\
|
||||
@@ -563,6 +608,46 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.405.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-client-sso-npm-3.405.0-0b22768239-323f99e024.zip/node_modules/@aws-sdk/client-sso/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/client-sso", "npm:3.405.0"],\
|
||||
["@aws-crypto/sha256-browser", "npm:3.0.0"],\
|
||||
["@aws-crypto/sha256-js", "npm:3.0.0"],\
|
||||
["@aws-sdk/middleware-host-header", "npm:3.398.0"],\
|
||||
["@aws-sdk/middleware-logger", "npm:3.398.0"],\
|
||||
["@aws-sdk/middleware-recursion-detection", "npm:3.398.0"],\
|
||||
["@aws-sdk/middleware-user-agent", "npm:3.398.0"],\
|
||||
["@aws-sdk/types", "npm:3.398.0"],\
|
||||
["@aws-sdk/util-endpoints", "npm:3.398.0"],\
|
||||
["@aws-sdk/util-user-agent-browser", "npm:3.398.0"],\
|
||||
["@aws-sdk/util-user-agent-node", "virtual:0b227682399940b7e75175c6ae399b938ec07d3986be60e73f8026d2677e9fe96d05ac258f3f223cf1b6bdd7fd2a24f55f0efa40a8406ba949dc1b7af3a6a968#npm:3.405.0"],\
|
||||
["@smithy/config-resolver", "npm:2.0.5"],\
|
||||
["@smithy/fetch-http-handler", "npm:2.0.5"],\
|
||||
["@smithy/hash-node", "npm:2.0.5"],\
|
||||
["@smithy/invalid-dependency", "npm:2.0.5"],\
|
||||
["@smithy/middleware-content-length", "npm:2.0.5"],\
|
||||
["@smithy/middleware-endpoint", "npm:2.0.5"],\
|
||||
["@smithy/middleware-retry", "npm:2.0.5"],\
|
||||
["@smithy/middleware-serde", "npm:2.0.5"],\
|
||||
["@smithy/middleware-stack", "npm:2.0.0"],\
|
||||
["@smithy/node-config-provider", "npm:2.0.7"],\
|
||||
["@smithy/node-http-handler", "npm:2.0.5"],\
|
||||
["@smithy/protocol-http", "npm:2.0.5"],\
|
||||
["@smithy/smithy-client", "npm:2.0.5"],\
|
||||
["@smithy/types", "npm:2.2.2"],\
|
||||
["@smithy/url-parser", "npm:2.0.5"],\
|
||||
["@smithy/util-base64", "npm:2.0.0"],\
|
||||
["@smithy/util-body-length-browser", "npm:2.0.0"],\
|
||||
["@smithy/util-body-length-node", "npm:2.1.0"],\
|
||||
["@smithy/util-defaults-mode-browser", "npm:2.0.6"],\
|
||||
["@smithy/util-defaults-mode-node", "npm:2.0.7"],\
|
||||
["@smithy/util-retry", "npm:2.0.0"],\
|
||||
["@smithy/util-utf8", "npm:2.0.0"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/client-sso-oidc", [\
|
||||
@@ -695,6 +780,50 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.405.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-client-sts-npm-3.405.0-b83c3faf19-01ea2a8695.zip/node_modules/@aws-sdk/client-sts/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/client-sts", "npm:3.405.0"],\
|
||||
["@aws-crypto/sha256-browser", "npm:3.0.0"],\
|
||||
["@aws-crypto/sha256-js", "npm:3.0.0"],\
|
||||
["@aws-sdk/credential-provider-node", "npm:3.405.0"],\
|
||||
["@aws-sdk/middleware-host-header", "npm:3.398.0"],\
|
||||
["@aws-sdk/middleware-logger", "npm:3.398.0"],\
|
||||
["@aws-sdk/middleware-recursion-detection", "npm:3.398.0"],\
|
||||
["@aws-sdk/middleware-sdk-sts", "npm:3.398.0"],\
|
||||
["@aws-sdk/middleware-signing", "npm:3.398.0"],\
|
||||
["@aws-sdk/middleware-user-agent", "npm:3.398.0"],\
|
||||
["@aws-sdk/types", "npm:3.398.0"],\
|
||||
["@aws-sdk/util-endpoints", "npm:3.398.0"],\
|
||||
["@aws-sdk/util-user-agent-browser", "npm:3.398.0"],\
|
||||
["@aws-sdk/util-user-agent-node", "virtual:0b227682399940b7e75175c6ae399b938ec07d3986be60e73f8026d2677e9fe96d05ac258f3f223cf1b6bdd7fd2a24f55f0efa40a8406ba949dc1b7af3a6a968#npm:3.405.0"],\
|
||||
["@smithy/config-resolver", "npm:2.0.5"],\
|
||||
["@smithy/fetch-http-handler", "npm:2.0.5"],\
|
||||
["@smithy/hash-node", "npm:2.0.5"],\
|
||||
["@smithy/invalid-dependency", "npm:2.0.5"],\
|
||||
["@smithy/middleware-content-length", "npm:2.0.5"],\
|
||||
["@smithy/middleware-endpoint", "npm:2.0.5"],\
|
||||
["@smithy/middleware-retry", "npm:2.0.5"],\
|
||||
["@smithy/middleware-serde", "npm:2.0.5"],\
|
||||
["@smithy/middleware-stack", "npm:2.0.0"],\
|
||||
["@smithy/node-config-provider", "npm:2.0.7"],\
|
||||
["@smithy/node-http-handler", "npm:2.0.5"],\
|
||||
["@smithy/protocol-http", "npm:2.0.5"],\
|
||||
["@smithy/smithy-client", "npm:2.0.5"],\
|
||||
["@smithy/types", "npm:2.2.2"],\
|
||||
["@smithy/url-parser", "npm:2.0.5"],\
|
||||
["@smithy/util-base64", "npm:2.0.0"],\
|
||||
["@smithy/util-body-length-browser", "npm:2.0.0"],\
|
||||
["@smithy/util-body-length-node", "npm:2.1.0"],\
|
||||
["@smithy/util-defaults-mode-browser", "npm:2.0.6"],\
|
||||
["@smithy/util-defaults-mode-node", "npm:2.0.7"],\
|
||||
["@smithy/util-retry", "npm:2.0.0"],\
|
||||
["@smithy/util-utf8", "npm:2.0.0"],\
|
||||
["fast-xml-parser", "npm:4.2.5"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/config-resolver", [\
|
||||
@@ -780,6 +909,23 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.405.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-ini-npm-3.405.0-759c2d9674-0d2694b969.zip/node_modules/@aws-sdk/credential-provider-ini/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/credential-provider-ini", "npm:3.405.0"],\
|
||||
["@aws-sdk/credential-provider-env", "npm:3.398.0"],\
|
||||
["@aws-sdk/credential-provider-process", "npm:3.405.0"],\
|
||||
["@aws-sdk/credential-provider-sso", "npm:3.405.0"],\
|
||||
["@aws-sdk/credential-provider-web-identity", "npm:3.398.0"],\
|
||||
["@aws-sdk/types", "npm:3.398.0"],\
|
||||
["@smithy/credential-provider-imds", "npm:2.0.5"],\
|
||||
["@smithy/property-provider", "npm:2.0.5"],\
|
||||
["@smithy/shared-ini-file-loader", "npm:2.0.6"],\
|
||||
["@smithy/types", "npm:2.2.2"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/credential-provider-node", [\
|
||||
@@ -817,6 +963,24 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.405.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-node-npm-3.405.0-33a4e3c01f-58cf90600d.zip/node_modules/@aws-sdk/credential-provider-node/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/credential-provider-node", "npm:3.405.0"],\
|
||||
["@aws-sdk/credential-provider-env", "npm:3.398.0"],\
|
||||
["@aws-sdk/credential-provider-ini", "npm:3.405.0"],\
|
||||
["@aws-sdk/credential-provider-process", "npm:3.405.0"],\
|
||||
["@aws-sdk/credential-provider-sso", "npm:3.405.0"],\
|
||||
["@aws-sdk/credential-provider-web-identity", "npm:3.398.0"],\
|
||||
["@aws-sdk/types", "npm:3.398.0"],\
|
||||
["@smithy/credential-provider-imds", "npm:2.0.5"],\
|
||||
["@smithy/property-provider", "npm:2.0.5"],\
|
||||
["@smithy/shared-ini-file-loader", "npm:2.0.6"],\
|
||||
["@smithy/types", "npm:2.2.2"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/credential-provider-process", [\
|
||||
@@ -842,6 +1006,18 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.405.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-process-npm-3.405.0-ed6dc867ed-bd23e267bd.zip/node_modules/@aws-sdk/credential-provider-process/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/credential-provider-process", "npm:3.405.0"],\
|
||||
["@aws-sdk/types", "npm:3.398.0"],\
|
||||
["@smithy/property-provider", "npm:2.0.5"],\
|
||||
["@smithy/shared-ini-file-loader", "npm:2.0.6"],\
|
||||
["@smithy/types", "npm:2.2.2"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/credential-provider-sso", [\
|
||||
@@ -871,6 +1047,20 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.405.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-sso-npm-3.405.0-24b76ee82f-754f796b2a.zip/node_modules/@aws-sdk/credential-provider-sso/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/credential-provider-sso", "npm:3.405.0"],\
|
||||
["@aws-sdk/client-sso", "npm:3.405.0"],\
|
||||
["@aws-sdk/token-providers", "npm:3.405.0"],\
|
||||
["@aws-sdk/types", "npm:3.398.0"],\
|
||||
["@smithy/property-provider", "npm:2.0.5"],\
|
||||
["@smithy/shared-ini-file-loader", "npm:2.0.6"],\
|
||||
["@smithy/types", "npm:2.2.2"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/credential-provider-web-identity", [\
|
||||
@@ -1538,6 +1728,48 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.405.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-token-providers-npm-3.405.0-29e68d4065-08e30dbc7b.zip/node_modules/@aws-sdk/token-providers/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/token-providers", "npm:3.405.0"],\
|
||||
["@aws-crypto/sha256-browser", "npm:3.0.0"],\
|
||||
["@aws-crypto/sha256-js", "npm:3.0.0"],\
|
||||
["@aws-sdk/middleware-host-header", "npm:3.398.0"],\
|
||||
["@aws-sdk/middleware-logger", "npm:3.398.0"],\
|
||||
["@aws-sdk/middleware-recursion-detection", "npm:3.398.0"],\
|
||||
["@aws-sdk/middleware-user-agent", "npm:3.398.0"],\
|
||||
["@aws-sdk/types", "npm:3.398.0"],\
|
||||
["@aws-sdk/util-endpoints", "npm:3.398.0"],\
|
||||
["@aws-sdk/util-user-agent-browser", "npm:3.398.0"],\
|
||||
["@aws-sdk/util-user-agent-node", "virtual:0b227682399940b7e75175c6ae399b938ec07d3986be60e73f8026d2677e9fe96d05ac258f3f223cf1b6bdd7fd2a24f55f0efa40a8406ba949dc1b7af3a6a968#npm:3.405.0"],\
|
||||
["@smithy/config-resolver", "npm:2.0.5"],\
|
||||
["@smithy/fetch-http-handler", "npm:2.0.5"],\
|
||||
["@smithy/hash-node", "npm:2.0.5"],\
|
||||
["@smithy/invalid-dependency", "npm:2.0.5"],\
|
||||
["@smithy/middleware-content-length", "npm:2.0.5"],\
|
||||
["@smithy/middleware-endpoint", "npm:2.0.5"],\
|
||||
["@smithy/middleware-retry", "npm:2.0.5"],\
|
||||
["@smithy/middleware-serde", "npm:2.0.5"],\
|
||||
["@smithy/middleware-stack", "npm:2.0.0"],\
|
||||
["@smithy/node-config-provider", "npm:2.0.7"],\
|
||||
["@smithy/node-http-handler", "npm:2.0.5"],\
|
||||
["@smithy/property-provider", "npm:2.0.5"],\
|
||||
["@smithy/protocol-http", "npm:2.0.5"],\
|
||||
["@smithy/shared-ini-file-loader", "npm:2.0.6"],\
|
||||
["@smithy/smithy-client", "npm:2.0.5"],\
|
||||
["@smithy/types", "npm:2.2.2"],\
|
||||
["@smithy/url-parser", "npm:2.0.5"],\
|
||||
["@smithy/util-base64", "npm:2.0.0"],\
|
||||
["@smithy/util-body-length-browser", "npm:2.0.0"],\
|
||||
["@smithy/util-body-length-node", "npm:2.1.0"],\
|
||||
["@smithy/util-defaults-mode-browser", "npm:2.0.6"],\
|
||||
["@smithy/util-defaults-mode-node", "npm:2.0.7"],\
|
||||
["@smithy/util-retry", "npm:2.0.0"],\
|
||||
["@smithy/util-utf8", "npm:2.0.0"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/types", [\
|
||||
@@ -1798,6 +2030,30 @@ const RAW_RUNTIME_STATE =
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["npm:3.405.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-util-user-agent-node-npm-3.405.0-160b854f92-6422874d9e.zip/node_modules/@aws-sdk/util-user-agent-node/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/util-user-agent-node", "npm:3.405.0"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["virtual:0b227682399940b7e75175c6ae399b938ec07d3986be60e73f8026d2677e9fe96d05ac258f3f223cf1b6bdd7fd2a24f55f0efa40a8406ba949dc1b7af3a6a968#npm:3.405.0", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@aws-sdk-util-user-agent-node-virtual-f09536a53a/0/cache/@aws-sdk-util-user-agent-node-npm-3.405.0-160b854f92-6422874d9e.zip/node_modules/@aws-sdk/util-user-agent-node/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/util-user-agent-node", "virtual:0b227682399940b7e75175c6ae399b938ec07d3986be60e73f8026d2677e9fe96d05ac258f3f223cf1b6bdd7fd2a24f55f0efa40a8406ba949dc1b7af3a6a968#npm:3.405.0"],\
|
||||
["@aws-sdk/types", "npm:3.398.0"],\
|
||||
["@smithy/node-config-provider", "npm:2.0.7"],\
|
||||
["@smithy/types", "npm:2.2.2"],\
|
||||
["@types/aws-crt", null],\
|
||||
["aws-crt", null],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"packagePeers": [\
|
||||
"@types/aws-crt",\
|
||||
"aws-crt"\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["virtual:bc215baaece010534480a5bbeef12e01b493e2e880132aa6bad8e946a74eb78bacf426e63b2a17a031e79d1b4089081937de686f0c09183e86cf908c3bd861cc#npm:3.398.0", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@aws-sdk-util-user-agent-node-virtual-cb6c764817/0/cache/@aws-sdk-util-user-agent-node-npm-3.398.0-e1c632b733-6d5dae585a.zip/node_modules/@aws-sdk/util-user-agent-node/",\
|
||||
"packageDependencies": [\
|
||||
@@ -4927,6 +5183,18 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:2.0.7", {\
|
||||
"packageLocation": "./.yarn/cache/@smithy-credential-provider-imds-npm-2.0.7-46bd1e8858-61c59aea7e.zip/node_modules/@smithy/credential-provider-imds/",\
|
||||
"packageDependencies": [\
|
||||
["@smithy/credential-provider-imds", "npm:2.0.7"],\
|
||||
["@smithy/node-config-provider", "npm:2.0.7"],\
|
||||
["@smithy/property-provider", "npm:2.0.6"],\
|
||||
["@smithy/types", "npm:2.2.2"],\
|
||||
["@smithy/url-parser", "npm:2.0.5"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@smithy/eventstream-codec", [\
|
||||
@@ -5111,6 +5379,17 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:2.0.7", {\
|
||||
"packageLocation": "./.yarn/cache/@smithy-node-config-provider-npm-2.0.7-806b68f393-d4b58ee69f.zip/node_modules/@smithy/node-config-provider/",\
|
||||
"packageDependencies": [\
|
||||
["@smithy/node-config-provider", "npm:2.0.7"],\
|
||||
["@smithy/property-provider", "npm:2.0.6"],\
|
||||
["@smithy/shared-ini-file-loader", "npm:2.0.6"],\
|
||||
["@smithy/types", "npm:2.2.2"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@smithy/node-http-handler", [\
|
||||
@@ -5136,6 +5415,15 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:2.0.6", {\
|
||||
"packageLocation": "./.yarn/cache/@smithy-property-provider-npm-2.0.6-4f294049d1-b9a4aff1f0.zip/node_modules/@smithy/property-provider/",\
|
||||
"packageDependencies": [\
|
||||
["@smithy/property-provider", "npm:2.0.6"],\
|
||||
["@smithy/types", "npm:2.2.2"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@smithy/protocol-http", [\
|
||||
@@ -5199,6 +5487,15 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:2.0.6", {\
|
||||
"packageLocation": "./.yarn/cache/@smithy-shared-ini-file-loader-npm-2.0.6-ebbee54019-4b538ef59a.zip/node_modules/@smithy/shared-ini-file-loader/",\
|
||||
"packageDependencies": [\
|
||||
["@smithy/shared-ini-file-loader", "npm:2.0.6"],\
|
||||
["@smithy/types", "npm:2.2.2"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@smithy/signature-v4", [\
|
||||
@@ -5324,6 +5621,17 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:2.0.6", {\
|
||||
"packageLocation": "./.yarn/cache/@smithy-util-defaults-mode-browser-npm-2.0.6-d40f165a01-286295e6e9.zip/node_modules/@smithy/util-defaults-mode-browser/",\
|
||||
"packageDependencies": [\
|
||||
["@smithy/util-defaults-mode-browser", "npm:2.0.6"],\
|
||||
["@smithy/property-provider", "npm:2.0.6"],\
|
||||
["@smithy/types", "npm:2.2.2"],\
|
||||
["bowser", "npm:2.11.0"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@smithy/util-defaults-mode-node", [\
|
||||
@@ -5339,6 +5647,19 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:2.0.7", {\
|
||||
"packageLocation": "./.yarn/cache/@smithy-util-defaults-mode-node-npm-2.0.7-8a9d03e11c-b1c74a3b41.zip/node_modules/@smithy/util-defaults-mode-node/",\
|
||||
"packageDependencies": [\
|
||||
["@smithy/util-defaults-mode-node", "npm:2.0.7"],\
|
||||
["@smithy/config-resolver", "npm:2.0.5"],\
|
||||
["@smithy/credential-provider-imds", "npm:2.0.7"],\
|
||||
["@smithy/node-config-provider", "npm:2.0.7"],\
|
||||
["@smithy/property-provider", "npm:2.0.6"],\
|
||||
["@smithy/types", "npm:2.2.2"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@smithy/util-hex-encoding", [\
|
||||
@@ -6161,6 +6482,7 @@ const RAW_RUNTIME_STATE =
|
||||
"packageLocation": "./packages/websockets/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/websockets-server", "workspace:packages/websockets"],\
|
||||
["@aws-sdk/client-apigatewaymanagementapi", "npm:3.405.0"],\
|
||||
["@aws-sdk/client-sqs", "npm:3.342.0"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.1"],\
|
||||
["@standardnotes/api", "npm:1.26.26"],\
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
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.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.26.4](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.3...@standardnotes/analytics@2.26.4) (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.26.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.2...@standardnotes/analytics@2.26.3) (2023-09-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "2.26.3",
|
||||
"version": "2.26.4",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -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.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.73.7...@standardnotes/api-gateway@1.74.0) (2023-09-06)
|
||||
|
||||
### Features
|
||||
|
||||
* should be able to access shared item revisions as third party user ([#807](https://github.com/standardnotes/api-gateway/issues/807)) ([794cd87](https://github.com/standardnotes/api-gateway/commit/794cd8734acf89fb29f09dfb169a3f08f252bb6a))
|
||||
|
||||
## [1.73.7](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.73.6...@standardnotes/api-gateway@1.73.7) (2023-09-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.73.7",
|
||||
"version": "1.74.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -72,6 +72,7 @@ export abstract class AuthMiddleware extends BaseMiddleware {
|
||||
response.locals.session = decodedToken.session
|
||||
response.locals.roles = decodedToken.roles
|
||||
response.locals.sharedVaultOwnerContext = decodedToken.shared_vault_owner_context
|
||||
response.locals.belongsToSharedVaults = decodedToken.belongs_to_shared_vaults ?? []
|
||||
} catch (error) {
|
||||
const errorMessage = (error as AxiosError).isAxiosError
|
||||
? JSON.stringify((error as AxiosError).response?.data)
|
||||
|
||||
@@ -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.138.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.137.6...@standardnotes/auth-server@1.138.0) (2023-09-06)
|
||||
|
||||
### Features
|
||||
|
||||
* should be able to access shared item revisions as third party user ([#807](https://github.com/standardnotes/server/issues/807)) ([794cd87](https://github.com/standardnotes/server/commit/794cd8734acf89fb29f09dfb169a3f08f252bb6a))
|
||||
|
||||
## [1.137.6](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.137.5...@standardnotes/auth-server@1.137.6) (2023-09-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddSharedVaultUsers1694000575425 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE `auth_shared_vault_users` (`uuid` varchar(36) NOT NULL, `shared_vault_uuid` varchar(36) NOT NULL, `user_uuid` varchar(36) NOT NULL, `permission` varchar(24) NOT NULL, `created_at_timestamp` bigint NOT NULL, `updated_at_timestamp` bigint NOT NULL, INDEX `shared_vault_uuid_on_auth_shared_vault_users` (`shared_vault_uuid`), INDEX `user_uuid_on_auth_shared_vault_users` (`user_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX `user_uuid_on_auth_shared_vault_users` ON `auth_shared_vault_users`')
|
||||
await queryRunner.query('DROP INDEX `shared_vault_uuid_on_auth_shared_vault_users` ON `auth_shared_vault_users`')
|
||||
await queryRunner.query('DROP TABLE `auth_shared_vault_users`')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddSharedVaultUsers1694000640645 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "auth_shared_vault_users" ("uuid" varchar PRIMARY KEY NOT NULL, "shared_vault_uuid" varchar(36) NOT NULL, "user_uuid" varchar(36) NOT NULL, "permission" varchar(24) NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL)',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'CREATE INDEX "shared_vault_uuid_on_auth_shared_vault_users" ON "auth_shared_vault_users" ("shared_vault_uuid") ',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'CREATE INDEX "user_uuid_on_auth_shared_vault_users" ON "auth_shared_vault_users" ("user_uuid") ',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "user_uuid_on_auth_shared_vault_users"')
|
||||
await queryRunner.query('DROP INDEX "shared_vault_uuid_on_auth_shared_vault_users"')
|
||||
await queryRunner.query('DROP TABLE "auth_shared_vault_users"')
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/auth-server",
|
||||
"version": "1.137.6",
|
||||
"version": "1.138.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -189,6 +189,7 @@ import {
|
||||
ControllerContainer,
|
||||
ControllerContainerInterface,
|
||||
MapperInterface,
|
||||
SharedVaultUser,
|
||||
} from '@standardnotes/domain-core'
|
||||
import { SessionTracePersistenceMapper } from '../Mapping/SessionTracePersistenceMapper'
|
||||
import { SessionTrace } from '../Domain/Session/SessionTrace'
|
||||
@@ -263,6 +264,14 @@ import { InMemoryTransitionStatusRepository } from '../Infra/InMemory/InMemoryTr
|
||||
import { TransitionStatusUpdatedEventHandler } from '../Domain/Handler/TransitionStatusUpdatedEventHandler'
|
||||
import { UpdateTransitionStatus } from '../Domain/UseCase/UpdateTransitionStatus/UpdateTransitionStatus'
|
||||
import { GetTransitionStatus } from '../Domain/UseCase/GetTransitionStatus/GetTransitionStatus'
|
||||
import { TypeORMSharedVaultUser } from '../Infra/TypeORM/TypeORMSharedVaultUser'
|
||||
import { SharedVaultUserPersistenceMapper } from '../Mapping/SharedVaultUserPersistenceMapper'
|
||||
import { SharedVaultUserRepositoryInterface } from '../Domain/SharedVault/SharedVaultUserRepositoryInterface'
|
||||
import { TypeORMSharedVaultUserRepository } from '../Infra/TypeORM/TypeORMSharedVaultUserRepository'
|
||||
import { AddSharedVaultUser } from '../Domain/UseCase/AddSharedVaultUser/AddSharedVaultUser'
|
||||
import { RemoveSharedVaultUser } from '../Domain/UseCase/RemoveSharedVaultUser/RemoveSharedVaultUser'
|
||||
import { UserAddedToSharedVaultEventHandler } from '../Domain/Handler/UserAddedToSharedVaultEventHandler'
|
||||
import { UserRemovedFromSharedVaultEventHandler } from '../Domain/Handler/UserRemovedFromSharedVaultEventHandler'
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
async load(configuration?: {
|
||||
@@ -372,6 +381,9 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind<MapperInterface<CacheEntry, TypeORMCacheEntry>>(TYPES.Auth_CacheEntryPersistenceMapper)
|
||||
.toConstantValue(new CacheEntryPersistenceMapper())
|
||||
container
|
||||
.bind<MapperInterface<SharedVaultUser, TypeORMSharedVaultUser>>(TYPES.Auth_SharedVaultUserPersistenceMapper)
|
||||
.toConstantValue(new SharedVaultUserPersistenceMapper())
|
||||
|
||||
// ORM
|
||||
container
|
||||
@@ -412,6 +424,9 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind<Repository<TypeORMCacheEntry>>(TYPES.Auth_ORMCacheEntryRepository)
|
||||
.toConstantValue(appDataSource.getRepository(TypeORMCacheEntry))
|
||||
container
|
||||
.bind<Repository<TypeORMSharedVaultUser>>(TYPES.Auth_ORMSharedVaultUserRepository)
|
||||
.toConstantValue(appDataSource.getRepository(TypeORMSharedVaultUser))
|
||||
|
||||
// Repositories
|
||||
container.bind<SessionRepositoryInterface>(TYPES.Auth_SessionRepository).to(TypeORMSessionRepository)
|
||||
@@ -468,6 +483,16 @@ export class ContainerConfigLoader {
|
||||
container.get(TYPES.Auth_CacheEntryPersistenceMapper),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<SharedVaultUserRepositoryInterface>(TYPES.Auth_SharedVaultUserRepository)
|
||||
.toConstantValue(
|
||||
new TypeORMSharedVaultUserRepository(
|
||||
container.get<Repository<TypeORMSharedVaultUser>>(TYPES.Auth_ORMSharedVaultUserRepository),
|
||||
container.get<MapperInterface<SharedVaultUser, TypeORMSharedVaultUser>>(
|
||||
TYPES.Auth_SharedVaultUserPersistenceMapper,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
// Middleware
|
||||
container.bind<SessionMiddleware>(TYPES.Auth_SessionMiddleware).to(SessionMiddleware)
|
||||
@@ -926,6 +951,18 @@ export class ContainerConfigLoader {
|
||||
container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<AddSharedVaultUser>(TYPES.Auth_AddSharedVaultUser)
|
||||
.toConstantValue(
|
||||
new AddSharedVaultUser(container.get<SharedVaultUserRepositoryInterface>(TYPES.Auth_SharedVaultUserRepository)),
|
||||
)
|
||||
container
|
||||
.bind<RemoveSharedVaultUser>(TYPES.Auth_RemoveSharedVaultUser)
|
||||
.toConstantValue(
|
||||
new RemoveSharedVaultUser(
|
||||
container.get<SharedVaultUserRepositoryInterface>(TYPES.Auth_SharedVaultUserRepository),
|
||||
),
|
||||
)
|
||||
|
||||
// Controller
|
||||
container
|
||||
@@ -1075,6 +1112,22 @@ export class ContainerConfigLoader {
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<UserAddedToSharedVaultEventHandler>(TYPES.Auth_UserAddedToSharedVaultEventHandler)
|
||||
.toConstantValue(
|
||||
new UserAddedToSharedVaultEventHandler(
|
||||
container.get<AddSharedVaultUser>(TYPES.Auth_AddSharedVaultUser),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<UserRemovedFromSharedVaultEventHandler>(TYPES.Auth_UserRemovedFromSharedVaultEventHandler)
|
||||
.toConstantValue(
|
||||
new UserRemovedFromSharedVaultEventHandler(
|
||||
container.get<RemoveSharedVaultUser>(TYPES.Auth_RemoveSharedVaultUser),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
|
||||
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
|
||||
['USER_REGISTERED', container.get(TYPES.Auth_UserRegisteredEventHandler)],
|
||||
@@ -1107,6 +1160,8 @@ export class ContainerConfigLoader {
|
||||
['EMAIL_SUBSCRIPTION_UNSUBSCRIBED', container.get(TYPES.Auth_EmailSubscriptionUnsubscribedEventHandler)],
|
||||
['PAYMENTS_ACCOUNT_DELETED', container.get(TYPES.Auth_PaymentsAccountDeletedEventHandler)],
|
||||
['TRANSITION_STATUS_UPDATED', container.get(TYPES.Auth_TransitionStatusUpdatedEventHandler)],
|
||||
['USER_ADDED_TO_SHARED_VAULT', container.get(TYPES.Auth_UserAddedToSharedVaultEventHandler)],
|
||||
['USER_REMOVED_FROM_SHARED_VAULT', container.get(TYPES.Auth_UserRemovedFromSharedVaultEventHandler)],
|
||||
])
|
||||
|
||||
if (isConfiguredForHomeServer) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import { TypeORMEmergencyAccessInvitation } from '../Infra/TypeORM/TypeORMEmerge
|
||||
import { TypeORMSessionTrace } from '../Infra/TypeORM/TypeORMSessionTrace'
|
||||
import { Env } from './Env'
|
||||
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
|
||||
import { TypeORMSharedVaultUser } from '../Infra/TypeORM/TypeORMSharedVaultUser'
|
||||
|
||||
export class AppDataSource {
|
||||
private _dataSource: DataSource | undefined
|
||||
@@ -64,6 +65,7 @@ export class AppDataSource {
|
||||
TypeORMAuthenticatorChallenge,
|
||||
TypeORMEmergencyAccessInvitation,
|
||||
TypeORMCacheEntry,
|
||||
TypeORMSharedVaultUser,
|
||||
],
|
||||
migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
|
||||
migrationsRun: true,
|
||||
|
||||
@@ -9,6 +9,7 @@ const TYPES = {
|
||||
Auth_AuthenticatorPersistenceMapper: Symbol.for('Auth_AuthenticatorPersistenceMapper'),
|
||||
Auth_AuthenticatorHttpMapper: Symbol.for('Auth_AuthenticatorHttpMapper'),
|
||||
Auth_CacheEntryPersistenceMapper: Symbol.for('Auth_CacheEntryPersistenceMapper'),
|
||||
Auth_SharedVaultUserPersistenceMapper: Symbol.for('Auth_SharedVaultUserPersistenceMapper'),
|
||||
// Controller
|
||||
Auth_ControllerContainer: Symbol.for('Auth_ControllerContainer'),
|
||||
Auth_AuthController: Symbol.for('Auth_AuthController'),
|
||||
@@ -36,6 +37,7 @@ const TYPES = {
|
||||
Auth_AuthenticatorChallengeRepository: Symbol.for('Auth_AuthenticatorChallengeRepository'),
|
||||
Auth_CacheEntryRepository: Symbol.for('Auth_CacheEntryRepository'),
|
||||
Auth_TransitionStatusRepository: Symbol.for('Auth_TransitionStatusRepository'),
|
||||
Auth_SharedVaultUserRepository: Symbol.for('Auth_SharedVaultUserRepository'),
|
||||
// ORM
|
||||
Auth_ORMOfflineSettingRepository: Symbol.for('Auth_ORMOfflineSettingRepository'),
|
||||
Auth_ORMOfflineUserSubscriptionRepository: Symbol.for('Auth_ORMOfflineUserSubscriptionRepository'),
|
||||
@@ -51,6 +53,7 @@ const TYPES = {
|
||||
Auth_ORMAuthenticatorRepository: Symbol.for('Auth_ORMAuthenticatorRepository'),
|
||||
Auth_ORMAuthenticatorChallengeRepository: Symbol.for('Auth_ORMAuthenticatorChallengeRepository'),
|
||||
Auth_ORMCacheEntryRepository: Symbol.for('Auth_ORMCacheEntryRepository'),
|
||||
Auth_ORMSharedVaultUserRepository: Symbol.for('Auth_ORMSharedVaultUserRepository'),
|
||||
// Middleware
|
||||
Auth_RequiredCrossServiceTokenMiddleware: Symbol.for('Auth_RequiredCrossServiceTokenMiddleware'),
|
||||
Auth_OptionalCrossServiceTokenMiddleware: Symbol.for('Auth_OptionalCrossServiceTokenMiddleware'),
|
||||
@@ -157,6 +160,8 @@ const TYPES = {
|
||||
Auth_UpdateStorageQuotaUsedForUser: Symbol.for('Auth_UpdateStorageQuotaUsedForUser'),
|
||||
Auth_UpdateTransitionStatus: Symbol.for('Auth_UpdateTransitionStatus'),
|
||||
Auth_GetTransitionStatus: Symbol.for('Auth_GetTransitionStatus'),
|
||||
Auth_AddSharedVaultUser: Symbol.for('Auth_AddSharedVaultUser'),
|
||||
Auth_RemoveSharedVaultUser: Symbol.for('Auth_RemoveSharedVaultUser'),
|
||||
// Handlers
|
||||
Auth_UserRegisteredEventHandler: Symbol.for('Auth_UserRegisteredEventHandler'),
|
||||
Auth_AccountDeletionRequestedEventHandler: Symbol.for('Auth_AccountDeletionRequestedEventHandler'),
|
||||
@@ -186,6 +191,8 @@ const TYPES = {
|
||||
Auth_EmailSubscriptionUnsubscribedEventHandler: Symbol.for('Auth_EmailSubscriptionUnsubscribedEventHandler'),
|
||||
Auth_PaymentsAccountDeletedEventHandler: Symbol.for('Auth_PaymentsAccountDeletedEventHandler'),
|
||||
Auth_TransitionStatusUpdatedEventHandler: Symbol.for('Auth_TransitionStatusUpdatedEventHandler'),
|
||||
Auth_UserAddedToSharedVaultEventHandler: Symbol.for('Auth_UserAddedToSharedVaultEventHandler'),
|
||||
Auth_UserRemovedFromSharedVaultEventHandler: Symbol.for('Auth_UserRemovedFromSharedVaultEventHandler'),
|
||||
// Services
|
||||
Auth_DeviceDetector: Symbol.for('Auth_DeviceDetector'),
|
||||
Auth_SessionService: Symbol.for('Auth_SessionService'),
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { DomainEventHandlerInterface, UserAddedToSharedVaultEvent } from '@standardnotes/domain-events'
|
||||
import { Logger } from 'winston'
|
||||
import { AddSharedVaultUser } from '../UseCase/AddSharedVaultUser/AddSharedVaultUser'
|
||||
|
||||
export class UserAddedToSharedVaultEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
private addSharedVaultUser: AddSharedVaultUser,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: UserAddedToSharedVaultEvent): Promise<void> {
|
||||
const result = await this.addSharedVaultUser.execute({
|
||||
userUuid: event.payload.userUuid,
|
||||
sharedVaultUuid: event.payload.sharedVaultUuid,
|
||||
permission: event.payload.permission,
|
||||
createdAt: event.payload.createdAt,
|
||||
updatedAt: event.payload.updatedAt,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(`Failed to add user to shared vault: ${result.getError()}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { DomainEventHandlerInterface, UserRemovedFromSharedVaultEvent } from '@standardnotes/domain-events'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { RemoveSharedVaultUser } from '../UseCase/RemoveSharedVaultUser/RemoveSharedVaultUser'
|
||||
|
||||
export class UserRemovedFromSharedVaultEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
private removeSharedVaultUser: RemoveSharedVaultUser,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: UserRemovedFromSharedVaultEvent): Promise<void> {
|
||||
const result = await this.removeSharedVaultUser.execute({
|
||||
userUuid: event.payload.userUuid,
|
||||
sharedVaultUuid: event.payload.sharedVaultUuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(`Failed to remove user from shared vault: ${result.getError()}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { SharedVaultUser, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
export interface SharedVaultUserRepositoryInterface {
|
||||
findByUserUuidAndSharedVaultUuid(dto: { userUuid: Uuid; sharedVaultUuid: Uuid }): Promise<SharedVaultUser | null>
|
||||
findByUserUuid(userUuid: Uuid): Promise<SharedVaultUser[]>
|
||||
save(sharedVaultUser: SharedVaultUser): Promise<void>
|
||||
remove(sharedVault: SharedVaultUser): Promise<void>
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import { Result, SharedVaultUser } from '@standardnotes/domain-core'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
|
||||
import { AddSharedVaultUser } from './AddSharedVaultUser'
|
||||
|
||||
describe('AddSharedVaultUser', () => {
|
||||
let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
|
||||
|
||||
const createUseCase = () => new AddSharedVaultUser(sharedVaultUserRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
sharedVaultUserRepository = {} as jest.Mocked<SharedVaultUserRepositoryInterface>
|
||||
sharedVaultUserRepository.save = jest.fn()
|
||||
})
|
||||
|
||||
it('should save shared vault user', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: 'read',
|
||||
createdAt: 1,
|
||||
updatedAt: 2,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
expect(sharedVaultUserRepository.save).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fail when user uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: 'invalid',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: 'read',
|
||||
createdAt: 1,
|
||||
updatedAt: 2,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(sharedVaultUserRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fail when shared vault uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: 'invalid',
|
||||
permission: 'read',
|
||||
createdAt: 1,
|
||||
updatedAt: 2,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(sharedVaultUserRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fail when permission is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: 'invalid',
|
||||
createdAt: 1,
|
||||
updatedAt: 2,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(sharedVaultUserRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fail when timestamps are invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: 'read',
|
||||
createdAt: 'invalid' as unknown as number,
|
||||
updatedAt: 1,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(sharedVaultUserRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fail when shared vault user is invalid', async () => {
|
||||
const mock = jest.spyOn(SharedVaultUser, 'create')
|
||||
mock.mockReturnValue(Result.fail('Oops'))
|
||||
|
||||
const result = await createUseCase().execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: 'read',
|
||||
createdAt: 2,
|
||||
updatedAt: 1,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
|
||||
mock.mockRestore()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,56 @@
|
||||
import {
|
||||
Result,
|
||||
SharedVaultUser,
|
||||
SharedVaultUserPermission,
|
||||
Timestamps,
|
||||
UseCaseInterface,
|
||||
Uuid,
|
||||
} from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
|
||||
import { AddSharedVaultUserDTO } from './AddSharedVaultUserDTO'
|
||||
|
||||
export class AddSharedVaultUser implements UseCaseInterface<void> {
|
||||
constructor(private sharedVaultUserRepository: SharedVaultUserRepositoryInterface) {}
|
||||
|
||||
async execute(dto: AddSharedVaultUserDTO): Promise<Result<void>> {
|
||||
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return Result.fail(userUuidOrError.getError())
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
return Result.fail(sharedVaultUuidOrError.getError())
|
||||
}
|
||||
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
|
||||
const permissionOrError = SharedVaultUserPermission.create(dto.permission)
|
||||
if (permissionOrError.isFailed()) {
|
||||
return Result.fail(permissionOrError.getError())
|
||||
}
|
||||
const permission = permissionOrError.getValue()
|
||||
|
||||
const timestampsOrError = Timestamps.create(dto.createdAt, dto.updatedAt)
|
||||
if (timestampsOrError.isFailed()) {
|
||||
return Result.fail(timestampsOrError.getError())
|
||||
}
|
||||
const timestamps = timestampsOrError.getValue()
|
||||
|
||||
const sharedVaultUserOrError = SharedVaultUser.create({
|
||||
userUuid,
|
||||
sharedVaultUuid,
|
||||
permission,
|
||||
timestamps,
|
||||
})
|
||||
if (sharedVaultUserOrError.isFailed()) {
|
||||
return Result.fail(sharedVaultUserOrError.getError())
|
||||
}
|
||||
const sharedVaultUser = sharedVaultUserOrError.getValue()
|
||||
|
||||
await this.sharedVaultUserRepository.save(sharedVaultUser)
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface AddSharedVaultUserDTO {
|
||||
sharedVaultUuid: string
|
||||
userUuid: string
|
||||
permission: string
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
}
|
||||
+44
-1
@@ -9,8 +9,9 @@ import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||
|
||||
import { CreateCrossServiceToken } from './CreateCrossServiceToken'
|
||||
import { GetSetting } from '../GetSetting/GetSetting'
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
import { Result, SharedVaultUser, SharedVaultUserPermission, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
|
||||
|
||||
describe('CreateCrossServiceToken', () => {
|
||||
let userProjector: ProjectorInterface<User>
|
||||
@@ -20,6 +21,7 @@ describe('CreateCrossServiceToken', () => {
|
||||
let userRepository: UserRepositoryInterface
|
||||
let getSettingUseCase: GetSetting
|
||||
let transitionStatusRepository: TransitionStatusRepositoryInterface
|
||||
let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
|
||||
const jwtTTL = 60
|
||||
|
||||
let session: Session
|
||||
@@ -36,6 +38,7 @@ describe('CreateCrossServiceToken', () => {
|
||||
jwtTTL,
|
||||
getSettingUseCase,
|
||||
transitionStatusRepository,
|
||||
sharedVaultUserRepository,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -70,6 +73,16 @@ describe('CreateCrossServiceToken', () => {
|
||||
|
||||
transitionStatusRepository = {} as jest.Mocked<TransitionStatusRepositoryInterface>
|
||||
transitionStatusRepository.getStatus = jest.fn().mockReturnValue('TO-DO')
|
||||
|
||||
sharedVaultUserRepository = {} as jest.Mocked<SharedVaultUserRepositoryInterface>
|
||||
sharedVaultUserRepository.findByUserUuid = jest.fn().mockReturnValue([
|
||||
SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create('read').getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123456789, 123456789).getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
}).getValue(),
|
||||
])
|
||||
})
|
||||
|
||||
it('should create a cross service token for user', async () => {
|
||||
@@ -86,6 +99,12 @@ describe('CreateCrossServiceToken', () => {
|
||||
uuid: '1-3-4',
|
||||
},
|
||||
],
|
||||
belongs_to_shared_vaults: [
|
||||
{
|
||||
shared_vault_uuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: 'read',
|
||||
},
|
||||
],
|
||||
session: {
|
||||
test: 'test',
|
||||
},
|
||||
@@ -115,6 +134,12 @@ describe('CreateCrossServiceToken', () => {
|
||||
uuid: '1-3-4',
|
||||
},
|
||||
],
|
||||
belongs_to_shared_vaults: [
|
||||
{
|
||||
shared_vault_uuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: 'read',
|
||||
},
|
||||
],
|
||||
session: {
|
||||
test: 'test',
|
||||
},
|
||||
@@ -141,6 +166,12 @@ describe('CreateCrossServiceToken', () => {
|
||||
uuid: '1-3-4',
|
||||
},
|
||||
],
|
||||
belongs_to_shared_vaults: [
|
||||
{
|
||||
shared_vault_uuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: 'read',
|
||||
},
|
||||
],
|
||||
user: {
|
||||
email: 'test@test.te',
|
||||
uuid: '00000000-0000-0000-0000-000000000000',
|
||||
@@ -164,6 +195,12 @@ describe('CreateCrossServiceToken', () => {
|
||||
uuid: '1-3-4',
|
||||
},
|
||||
],
|
||||
belongs_to_shared_vaults: [
|
||||
{
|
||||
shared_vault_uuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: 'read',
|
||||
},
|
||||
],
|
||||
user: {
|
||||
email: 'test@test.te',
|
||||
uuid: '00000000-0000-0000-0000-000000000000',
|
||||
@@ -208,6 +245,12 @@ describe('CreateCrossServiceToken', () => {
|
||||
uuid: '1-3-4',
|
||||
},
|
||||
],
|
||||
belongs_to_shared_vaults: [
|
||||
{
|
||||
shared_vault_uuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: 'read',
|
||||
},
|
||||
],
|
||||
session: {
|
||||
test: 'test',
|
||||
},
|
||||
|
||||
@@ -13,6 +13,7 @@ import { CreateCrossServiceTokenDTO } from './CreateCrossServiceTokenDTO'
|
||||
import { GetSetting } from '../GetSetting/GetSetting'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
|
||||
|
||||
@injectable()
|
||||
export class CreateCrossServiceToken implements UseCaseInterface<string> {
|
||||
@@ -27,6 +28,7 @@ export class CreateCrossServiceToken implements UseCaseInterface<string> {
|
||||
private getSettingUseCase: GetSetting,
|
||||
@inject(TYPES.Auth_TransitionStatusRepository)
|
||||
private transitionStatusRepository: TransitionStatusRepositoryInterface,
|
||||
@inject(TYPES.Auth_SharedVaultUserRepository) private sharedVaultUserRepository: SharedVaultUserRepositoryInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: CreateCrossServiceTokenDTO): Promise<Result<string>> {
|
||||
@@ -49,11 +51,19 @@ export class CreateCrossServiceToken implements UseCaseInterface<string> {
|
||||
|
||||
const roles = await user.roles
|
||||
|
||||
const sharedVaultAssociations = await this.sharedVaultUserRepository.findByUserUuid(
|
||||
Uuid.create(user.uuid).getValue(),
|
||||
)
|
||||
|
||||
const authTokenData: CrossServiceTokenData = {
|
||||
user: this.projectUser(user),
|
||||
roles: this.projectRoles(roles),
|
||||
shared_vault_owner_context: undefined,
|
||||
ongoing_transition: transitionStatus === 'STARTED',
|
||||
belongs_to_shared_vaults: sharedVaultAssociations.map((association) => ({
|
||||
shared_vault_uuid: association.props.sharedVaultUuid.value,
|
||||
permission: association.props.permission.value,
|
||||
})),
|
||||
}
|
||||
|
||||
if (dto.sharedVaultOwnerContext !== undefined) {
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import { SharedVaultUser } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
|
||||
import { RemoveSharedVaultUser } from './RemoveSharedVaultUser'
|
||||
|
||||
describe('RemoveSharedVaultUser', () => {
|
||||
let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
|
||||
|
||||
const createUseCase = () => new RemoveSharedVaultUser(sharedVaultUserRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
sharedVaultUserRepository = {} as jest.Mocked<SharedVaultUserRepositoryInterface>
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest
|
||||
.fn()
|
||||
.mockReturnValue({} as jest.Mocked<SharedVaultUser>)
|
||||
sharedVaultUserRepository.remove = jest.fn()
|
||||
})
|
||||
|
||||
it('should remove shared vault user', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
expect(sharedVaultUserRepository.remove).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fail when user uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: 'invalid',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(sharedVaultUserRepository.remove).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fail when shared vault uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: 'invalid',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(sharedVaultUserRepository.remove).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fail when shared vault user is not found', async () => {
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(sharedVaultUserRepository.remove).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
|
||||
import { RemoveSharedVaultUserDTO } from './RemoveSharedVaultUserDTO'
|
||||
|
||||
export class RemoveSharedVaultUser implements UseCaseInterface<void> {
|
||||
constructor(private sharedVaultUserRepository: SharedVaultUserRepositoryInterface) {}
|
||||
|
||||
async execute(dto: RemoveSharedVaultUserDTO): Promise<Result<void>> {
|
||||
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return Result.fail(userUuidOrError.getError())
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
return Result.fail(sharedVaultUuidOrError.getError())
|
||||
}
|
||||
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
|
||||
const sharedVaultUser = await this.sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid({
|
||||
userUuid,
|
||||
sharedVaultUuid,
|
||||
})
|
||||
if (!sharedVaultUser) {
|
||||
return Result.fail('Shared vault user not found')
|
||||
}
|
||||
|
||||
await this.sharedVaultUserRepository.remove(sharedVaultUser)
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface RemoveSharedVaultUserDTO {
|
||||
sharedVaultUuid: string
|
||||
userUuid: string
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
|
||||
|
||||
@Entity({ name: 'auth_shared_vault_users' })
|
||||
export class TypeORMSharedVaultUser {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
declare uuid: string
|
||||
|
||||
@Column({
|
||||
name: 'shared_vault_uuid',
|
||||
length: 36,
|
||||
})
|
||||
@Index('shared_vault_uuid_on_auth_shared_vault_users')
|
||||
declare sharedVaultUuid: string
|
||||
|
||||
@Column({
|
||||
name: 'user_uuid',
|
||||
length: 36,
|
||||
})
|
||||
@Index('user_uuid_on_auth_shared_vault_users')
|
||||
declare userUuid: string
|
||||
|
||||
@Column({
|
||||
name: 'permission',
|
||||
type: 'varchar',
|
||||
length: 24,
|
||||
})
|
||||
declare permission: string
|
||||
|
||||
@Column({
|
||||
name: 'created_at_timestamp',
|
||||
type: 'bigint',
|
||||
})
|
||||
declare createdAtTimestamp: number
|
||||
|
||||
@Column({
|
||||
name: 'updated_at_timestamp',
|
||||
type: 'bigint',
|
||||
})
|
||||
declare updatedAtTimestamp: number
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { Repository } from 'typeorm'
|
||||
import { MapperInterface, SharedVaultUser, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultUserRepositoryInterface } from '../../Domain/SharedVault/SharedVaultUserRepositoryInterface'
|
||||
import { TypeORMSharedVaultUser } from './TypeORMSharedVaultUser'
|
||||
|
||||
export class TypeORMSharedVaultUserRepository implements SharedVaultUserRepositoryInterface {
|
||||
constructor(
|
||||
private ormRepository: Repository<TypeORMSharedVaultUser>,
|
||||
private mapper: MapperInterface<SharedVaultUser, TypeORMSharedVaultUser>,
|
||||
) {}
|
||||
|
||||
async findByUserUuid(userUuid: Uuid): Promise<SharedVaultUser[]> {
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('shared_vault_user')
|
||||
.where('shared_vault_user.user_uuid = :userUuid', {
|
||||
userUuid: userUuid.value,
|
||||
})
|
||||
.getMany()
|
||||
|
||||
return persistence.map((p) => this.mapper.toDomain(p))
|
||||
}
|
||||
|
||||
async findByUserUuidAndSharedVaultUuid(dto: {
|
||||
userUuid: Uuid
|
||||
sharedVaultUuid: Uuid
|
||||
}): Promise<SharedVaultUser | null> {
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('shared_vault_user')
|
||||
.where('shared_vault_user.user_uuid = :userUuid', {
|
||||
userUuid: dto.userUuid.value,
|
||||
})
|
||||
.andWhere('shared_vault_user.shared_vault_uuid = :sharedVaultUuid', {
|
||||
sharedVaultUuid: dto.sharedVaultUuid.value,
|
||||
})
|
||||
.getOne()
|
||||
|
||||
if (persistence === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.mapper.toDomain(persistence)
|
||||
}
|
||||
|
||||
async save(sharedVaultUser: SharedVaultUser): Promise<void> {
|
||||
const persistence = this.mapper.toProjection(sharedVaultUser)
|
||||
|
||||
await this.ormRepository.save(persistence)
|
||||
}
|
||||
|
||||
async remove(sharedVaultUser: SharedVaultUser): Promise<void> {
|
||||
await this.ormRepository.remove(this.mapper.toProjection(sharedVaultUser))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import {
|
||||
Timestamps,
|
||||
MapperInterface,
|
||||
UniqueEntityId,
|
||||
Uuid,
|
||||
SharedVaultUserPermission,
|
||||
SharedVaultUser,
|
||||
} from '@standardnotes/domain-core'
|
||||
|
||||
import { TypeORMSharedVaultUser } from '../Infra/TypeORM/TypeORMSharedVaultUser'
|
||||
|
||||
export class SharedVaultUserPersistenceMapper implements MapperInterface<SharedVaultUser, TypeORMSharedVaultUser> {
|
||||
toDomain(projection: TypeORMSharedVaultUser): SharedVaultUser {
|
||||
const userUuidOrError = Uuid.create(projection.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
throw new Error(`Failed to create shared vault user from projection: ${userUuidOrError.getError()}`)
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const sharedVaultUuidOrError = Uuid.create(projection.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
throw new Error(`Failed to create shared vault user from projection: ${sharedVaultUuidOrError.getError()}`)
|
||||
}
|
||||
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
|
||||
const timestampsOrError = Timestamps.create(projection.createdAtTimestamp, projection.updatedAtTimestamp)
|
||||
if (timestampsOrError.isFailed()) {
|
||||
throw new Error(`Failed to create shared vault user from projection: ${timestampsOrError.getError()}`)
|
||||
}
|
||||
const timestamps = timestampsOrError.getValue()
|
||||
|
||||
const permissionOrError = SharedVaultUserPermission.create(projection.permission)
|
||||
if (permissionOrError.isFailed()) {
|
||||
throw new Error(`Failed to create shared vault user from projection: ${permissionOrError.getError()}`)
|
||||
}
|
||||
const permission = permissionOrError.getValue()
|
||||
|
||||
const sharedVaultUserOrError = SharedVaultUser.create(
|
||||
{
|
||||
userUuid,
|
||||
sharedVaultUuid,
|
||||
permission,
|
||||
timestamps,
|
||||
},
|
||||
new UniqueEntityId(projection.uuid),
|
||||
)
|
||||
if (sharedVaultUserOrError.isFailed()) {
|
||||
throw new Error(`Failed to create shared vault user from projection: ${sharedVaultUserOrError.getError()}`)
|
||||
}
|
||||
const sharedVaultUser = sharedVaultUserOrError.getValue()
|
||||
|
||||
return sharedVaultUser
|
||||
}
|
||||
|
||||
toProjection(domain: SharedVaultUser): TypeORMSharedVaultUser {
|
||||
const typeorm = new TypeORMSharedVaultUser()
|
||||
|
||||
typeorm.uuid = domain.id.toString()
|
||||
typeorm.sharedVaultUuid = domain.props.sharedVaultUuid.value
|
||||
typeorm.userUuid = domain.props.userUuid.value
|
||||
typeorm.permission = domain.props.permission.value
|
||||
typeorm.createdAtTimestamp = domain.props.timestamps.createdAt
|
||||
typeorm.updatedAtTimestamp = domain.props.timestamps.updatedAt
|
||||
|
||||
return typeorm
|
||||
}
|
||||
}
|
||||
@@ -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.27.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.26.3...@standardnotes/domain-core@1.27.0) (2023-09-06)
|
||||
|
||||
### Features
|
||||
|
||||
* should be able to access shared item revisions as third party user ([#807](https://github.com/standardnotes/server/issues/807)) ([794cd87](https://github.com/standardnotes/server/commit/794cd8734acf89fb29f09dfb169a3f08f252bb6a))
|
||||
|
||||
## [1.26.3](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.26.2...@standardnotes/domain-core@1.26.3) (2023-09-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-core
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-core",
|
||||
"version": "1.26.3",
|
||||
"version": "1.27.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Timestamps } from './Timestamps'
|
||||
|
||||
describe('Timestamps', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = Timestamps.create(123, 234)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().createdAt).toEqual(123)
|
||||
expect(valueOrError.getValue().updatedAt).toEqual(234)
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
const valueOrError = Timestamps.create('' as unknown as number, 123)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
const valueOrError = Timestamps.create(123, '' as unknown as number)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -16,12 +16,12 @@ export class Timestamps extends ValueObject<TimestampsProps> {
|
||||
}
|
||||
|
||||
static create(createdAt: number, updatedAt: number): Result<Timestamps> {
|
||||
if (isNaN(createdAt)) {
|
||||
if (isNaN(createdAt) || typeof createdAt !== 'number') {
|
||||
return Result.fail<Timestamps>(
|
||||
`Could not create Timestamps. Creation date should be a number, given: ${createdAt}`,
|
||||
)
|
||||
}
|
||||
if (isNaN(updatedAt)) {
|
||||
if (isNaN(updatedAt) || typeof updatedAt !== 'number') {
|
||||
return Result.fail<Timestamps>(`Could not create Timestamps. Update date should be a number, given: ${updatedAt}`)
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
import { SharedVaultUserPermission, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultUserPermission } from './SharedVaultUserPermission'
|
||||
import { SharedVaultUser } from './SharedVaultUser'
|
||||
import { Uuid } from '../Common/Uuid'
|
||||
import { Timestamps } from '../Common/Timestamps'
|
||||
|
||||
describe('SharedVaultUser', () => {
|
||||
it('should create an entity', () => {
|
||||
+3
-2
@@ -1,5 +1,6 @@
|
||||
import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core'
|
||||
|
||||
import { Entity } from '../Core/Entity'
|
||||
import { Result } from '../Core/Result'
|
||||
import { UniqueEntityId } from '../Core/UniqueEntityId'
|
||||
import { SharedVaultUserProps } from './SharedVaultUserProps'
|
||||
|
||||
export class SharedVaultUser extends Entity<SharedVaultUserProps> {
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Timestamps } from '../Common/Timestamps'
|
||||
import { Uuid } from '../Common/Uuid'
|
||||
import { SharedVaultUserPermission } from './SharedVaultUserPermission'
|
||||
|
||||
export interface SharedVaultUserProps {
|
||||
sharedVaultUuid: Uuid
|
||||
userUuid: Uuid
|
||||
permission: SharedVaultUserPermission
|
||||
timestamps: Timestamps
|
||||
}
|
||||
@@ -59,8 +59,10 @@ export * from './Service/ServiceIdentifier'
|
||||
export * from './Service/ServiceIdentifierProps'
|
||||
export * from './Service/ServiceInterface'
|
||||
|
||||
export * from './SharedVault/SharedVaultUser'
|
||||
export * from './SharedVault/SharedVaultUserPermission'
|
||||
export * from './SharedVault/SharedVaultUserPermissionProps'
|
||||
export * from './SharedVault/SharedVaultUserProps'
|
||||
|
||||
export * from './Subscription/SubscriptionPlanName'
|
||||
export * from './Subscription/SubscriptionPlanNameProps'
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.12.22](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.21...@standardnotes/domain-events-infra@1.12.22) (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.12.21](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.20...@standardnotes/domain-events-infra@1.12.21) (2023-09-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events-infra",
|
||||
"version": "1.12.21",
|
||||
"version": "1.12.22",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -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.
|
||||
|
||||
# [2.123.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.122.2...@standardnotes/domain-events@2.123.0) (2023-09-06)
|
||||
|
||||
### Features
|
||||
|
||||
* should be able to access shared item revisions as third party user ([#807](https://github.com/standardnotes/server/issues/807)) ([794cd87](https://github.com/standardnotes/server/commit/794cd8734acf89fb29f09dfb169a3f08f252bb6a))
|
||||
|
||||
## [2.122.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.122.1...@standardnotes/domain-events@2.122.2) (2023-09-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events",
|
||||
"version": "2.122.2",
|
||||
"version": "2.123.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
import { UserAddedToSharedVaultEventPayload } from './UserAddedToSharedVaultEventPayload'
|
||||
|
||||
export interface UserAddedToSharedVaultEvent extends DomainEventInterface {
|
||||
type: 'USER_ADDED_TO_SHARED_VAULT'
|
||||
payload: UserAddedToSharedVaultEventPayload
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface UserAddedToSharedVaultEventPayload {
|
||||
sharedVaultUuid: string
|
||||
userUuid: string
|
||||
permission: string
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
import { UserRemovedFromSharedVaultEventPayload } from './UserRemovedFromSharedVaultEventPayload'
|
||||
|
||||
export interface UserRemovedFromSharedVaultEvent extends DomainEventInterface {
|
||||
type: 'USER_REMOVED_FROM_SHARED_VAULT'
|
||||
payload: UserRemovedFromSharedVaultEventPayload
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface UserRemovedFromSharedVaultEventPayload {
|
||||
sharedVaultUuid: string
|
||||
userUuid: string
|
||||
}
|
||||
@@ -96,6 +96,8 @@ export * from './Event/SubscriptionSyncRequestedEvent'
|
||||
export * from './Event/SubscriptionSyncRequestedEventPayload'
|
||||
export * from './Event/TransitionStatusUpdatedEvent'
|
||||
export * from './Event/TransitionStatusUpdatedEventPayload'
|
||||
export * from './Event/UserAddedToSharedVaultEvent'
|
||||
export * from './Event/UserAddedToSharedVaultEventPayload'
|
||||
export * from './Event/UserDisabledSessionUserAgentLoggingEvent'
|
||||
export * from './Event/UserDisabledSessionUserAgentLoggingEventPayload'
|
||||
export * from './Event/UserEmailChangedEvent'
|
||||
@@ -104,6 +106,8 @@ export * from './Event/UserInvitedToSharedVaultEvent'
|
||||
export * from './Event/UserInvitedToSharedVaultEventPayload'
|
||||
export * from './Event/UserRegisteredEvent'
|
||||
export * from './Event/UserRegisteredEventPayload'
|
||||
export * from './Event/UserRemovedFromSharedVaultEvent'
|
||||
export * from './Event/UserRemovedFromSharedVaultEventPayload'
|
||||
export * from './Event/UserRolesChangedEvent'
|
||||
export * from './Event/UserRolesChangedEventPayload'
|
||||
export * from './Event/WebSocketMessageRequestedEvent'
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.11.32](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.31...@standardnotes/event-store@1.11.32) (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.31](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.30...@standardnotes/event-store@1.11.31) (2023-09-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/event-store",
|
||||
"version": "1.11.31",
|
||||
"version": "1.11.32",
|
||||
"description": "Event Store Service",
|
||||
"private": true,
|
||||
"main": "dist/src/index.js",
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.22.11](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.10...@standardnotes/files-server@1.22.11) (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.22.10](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.9...@standardnotes/files-server@1.22.10) (2023-09-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files-server",
|
||||
"version": "1.22.10",
|
||||
"version": "1.22.11",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.15.25](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.24...@standardnotes/home-server@1.15.25) (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.24](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.23...@standardnotes/home-server@1.15.24) (2023-09-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/home-server",
|
||||
"version": "1.15.24",
|
||||
"version": "1.15.25",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -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.31.0](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.30.14...@standardnotes/revisions-server@1.31.0) (2023-09-06)
|
||||
|
||||
### Features
|
||||
|
||||
* should be able to access shared item revisions as third party user ([#807](https://github.com/standardnotes/server/issues/807)) ([794cd87](https://github.com/standardnotes/server/commit/794cd8734acf89fb29f09dfb169a3f08f252bb6a))
|
||||
|
||||
## [1.30.14](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.30.13...@standardnotes/revisions-server@1.30.14) (2023-09-04)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class init1669113322388 implements MigrationInterface {
|
||||
name = 'init1669113322388'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await this.syncSchemaBetweenLegacyRevisions(queryRunner)
|
||||
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE IF NOT EXISTS `revisions` (`uuid` varchar(36) NOT NULL, `item_uuid` varchar(36) NOT NULL, `user_uuid` varchar(36) NOT NULL, `content` mediumtext NULL, `content_type` varchar(255) NULL, `items_key_id` varchar(255) NULL, `enc_item_key` text NULL, `auth_hash` varchar(255) NULL, `creation_date` date NULL, `created_at` datetime(6) NULL, `updated_at` datetime(6) NULL, INDEX `item_uuid` (`item_uuid`), INDEX `user_uuid` (`user_uuid`), INDEX `creation_date` (`creation_date`), INDEX `created_at` (`created_at`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX `created_at` ON `revisions`')
|
||||
await queryRunner.query('DROP INDEX `creation_date` ON `revisions`')
|
||||
await queryRunner.query('DROP INDEX `user_uuid` ON `revisions`')
|
||||
await queryRunner.query('DROP INDEX `item_uuid` ON `revisions`')
|
||||
await queryRunner.query('DROP TABLE `revisions`')
|
||||
}
|
||||
|
||||
private async syncSchemaBetweenLegacyRevisions(queryRunner: QueryRunner): Promise<void> {
|
||||
const revisionsTableExistsQueryResult = await queryRunner.manager.query(
|
||||
'SELECT COUNT(*) as count FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = "revisions"',
|
||||
)
|
||||
const revisionsTableExists = revisionsTableExistsQueryResult[0].count === 1
|
||||
if (!revisionsTableExists) {
|
||||
return
|
||||
}
|
||||
|
||||
const revisionsTableHasUserUuidColumnQueryResult = await queryRunner.manager.query(
|
||||
'SELECT COUNT(*) as count FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = "revisions" AND column_name = "user_uuid"',
|
||||
)
|
||||
const revisionsTableHasUserUuidColumn = revisionsTableHasUserUuidColumnQueryResult[0].count === 1
|
||||
if (revisionsTableHasUserUuidColumn) {
|
||||
return
|
||||
}
|
||||
|
||||
await queryRunner.query('ALTER TABLE `revisions` ADD COLUMN `user_uuid` varchar(36) NULL')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class removeDateIndexes1669636497932 implements MigrationInterface {
|
||||
name = 'removeDateIndexes1669636497932'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const indexRevisionsOnCreatedAt = await queryRunner.manager.query(
|
||||
'SHOW INDEX FROM `revisions` where `key_name` = "created_at"',
|
||||
)
|
||||
const indexRevisionsOnCreatedAtExist = indexRevisionsOnCreatedAt && indexRevisionsOnCreatedAt.length > 0
|
||||
if (indexRevisionsOnCreatedAtExist) {
|
||||
await queryRunner.query('DROP INDEX `created_at` ON `revisions`')
|
||||
}
|
||||
|
||||
const indexRevisionsOnCreationDate = await queryRunner.manager.query(
|
||||
'SHOW INDEX FROM `revisions` where `key_name` = "creation_date"',
|
||||
)
|
||||
const indexRevisionsOnCreationDateAtExist = indexRevisionsOnCreationDate && indexRevisionsOnCreationDate.length > 0
|
||||
if (indexRevisionsOnCreationDateAtExist) {
|
||||
await queryRunner.query('DROP INDEX `creation_date` ON `revisions`')
|
||||
}
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('CREATE INDEX `creation_date` ON `revisions` (`creation_date`)')
|
||||
await queryRunner.query('CREATE INDEX `created_at` ON `revisions` (`created_at`)')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class makeUserUuidNullable1669735585016 implements MigrationInterface {
|
||||
name = 'makeUserUuidNullable1669735585016'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `revisions` CHANGE `user_uuid` `user_uuid` varchar(36) NULL')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `revisions` CHANGE `user_uuid` `user_uuid` varchar(36) NOT NULL')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddSharedVaultInformation1693915383950 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `revisions` ADD `edited_by` varchar(36) NULL')
|
||||
await queryRunner.query('ALTER TABLE `revisions` ADD `shared_vault_uuid` varchar(36) NULL')
|
||||
await queryRunner.query('ALTER TABLE `revisions` ADD `key_system_identifier` varchar(36) NULL')
|
||||
await queryRunner.query('CREATE INDEX `index_revisions_on_shared_vault_uuid` ON `revisions` (`shared_vault_uuid`)')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX `index_revisions_on_shared_vault_uuid` ON `revisions`')
|
||||
await queryRunner.query('ALTER TABLE `revisions` DROP COLUMN `key_system_identifier`')
|
||||
await queryRunner.query('ALTER TABLE `revisions` DROP COLUMN `shared_vault_uuid`')
|
||||
await queryRunner.query('ALTER TABLE `revisions` DROP COLUMN `last_edited_by`')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddSharedVaultInformation1693915775491 implements MigrationInterface {
|
||||
name = 'AddSharedVaultInformation1693915775491'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "user_uuid"')
|
||||
await queryRunner.query('DROP INDEX "item_uuid"')
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "temporary_revisions" ("uuid" varchar PRIMARY KEY NOT NULL, "item_uuid" varchar(36) NOT NULL, "user_uuid" varchar(36), "content" text, "content_type" varchar(255), "items_key_id" varchar(255), "enc_item_key" text, "auth_hash" varchar(255), "creation_date" date, "created_at" datetime(6), "updated_at" datetime(6), "edited_by" varchar(36), "shared_vault_uuid" varchar(36), "key_system_identifier" varchar(36))',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'INSERT INTO "temporary_revisions"("uuid", "item_uuid", "user_uuid", "content", "content_type", "items_key_id", "enc_item_key", "auth_hash", "creation_date", "created_at", "updated_at") SELECT "uuid", "item_uuid", "user_uuid", "content", "content_type", "items_key_id", "enc_item_key", "auth_hash", "creation_date", "created_at", "updated_at" FROM "revisions"',
|
||||
)
|
||||
await queryRunner.query('DROP TABLE "revisions"')
|
||||
await queryRunner.query('ALTER TABLE "temporary_revisions" RENAME TO "revisions"')
|
||||
await queryRunner.query('CREATE INDEX "user_uuid" ON "revisions" ("user_uuid") ')
|
||||
await queryRunner.query('CREATE INDEX "item_uuid" ON "revisions" ("item_uuid") ')
|
||||
await queryRunner.query('CREATE INDEX "index_revisions_on_shared_vault_uuid" ON "revisions" ("shared_vault_uuid") ')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "index_revisions_on_shared_vault_uuid"')
|
||||
await queryRunner.query('DROP INDEX "item_uuid"')
|
||||
await queryRunner.query('DROP INDEX "user_uuid"')
|
||||
await queryRunner.query('ALTER TABLE "revisions" RENAME TO "temporary_revisions"')
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "revisions" ("uuid" varchar PRIMARY KEY NOT NULL, "item_uuid" varchar(36) NOT NULL, "user_uuid" varchar(36), "content" text, "content_type" varchar(255), "items_key_id" varchar(255), "enc_item_key" text, "auth_hash" varchar(255), "creation_date" date, "created_at" datetime(6), "updated_at" datetime(6))',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'INSERT INTO "revisions"("uuid", "item_uuid", "user_uuid", "content", "content_type", "items_key_id", "enc_item_key", "auth_hash", "creation_date", "created_at", "updated_at") SELECT "uuid", "item_uuid", "user_uuid", "content", "content_type", "items_key_id", "enc_item_key", "auth_hash", "creation_date", "created_at", "updated_at" FROM "temporary_revisions"',
|
||||
)
|
||||
await queryRunner.query('DROP TABLE "temporary_revisions"')
|
||||
await queryRunner.query('CREATE INDEX "item_uuid" ON "revisions" ("item_uuid") ')
|
||||
await queryRunner.query('CREATE INDEX "user_uuid" ON "revisions" ("user_uuid") ')
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/revisions-server",
|
||||
"version": "1.30.14",
|
||||
"version": "1.31.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -7,8 +7,8 @@ import { SNSClient, SNSClientConfig } from '@aws-sdk/client-sns'
|
||||
import { Revision } from '../Domain/Revision/Revision'
|
||||
import { RevisionMetadata } from '../Domain/Revision/RevisionMetadata'
|
||||
import { RevisionRepositoryInterface } from '../Domain/Revision/RevisionRepositoryInterface'
|
||||
import { SQLRevisionRepository } from '../Infra/TypeORM/SQL/SQLRevisionRepository'
|
||||
import { SQLRevision } from '../Infra/TypeORM/SQL/SQLRevision'
|
||||
import { SQLLegacyRevisionRepository } from '../Infra/TypeORM/SQL/SQLLegacyRevisionRepository'
|
||||
import { SQLLegacyRevision } from '../Infra/TypeORM/SQL/SQLLegacyRevision'
|
||||
import { AppDataSource } from './DataSource'
|
||||
import { Env } from './Env'
|
||||
import TYPES from './Types'
|
||||
@@ -48,8 +48,8 @@ import { BaseRevisionsController } from '../Infra/InversifyExpress/Base/BaseRevi
|
||||
import { Transform } from 'stream'
|
||||
import { MongoDBRevision } from '../Infra/TypeORM/MongoDB/MongoDBRevision'
|
||||
import { MongoDBRevisionRepository } from '../Infra/TypeORM/MongoDB/MongoDBRevisionRepository'
|
||||
import { SQLRevisionMetadataPersistenceMapper } from '../Mapping/Persistence/SQL/SQLRevisionMetadataPersistenceMapper'
|
||||
import { SQLRevisionPersistenceMapper } from '../Mapping/Persistence/SQL/SQLRevisionPersistenceMapper'
|
||||
import { SQLLegacyRevisionMetadataPersistenceMapper } from '../Mapping/Persistence/SQL/SQLLegacyRevisionMetadataPersistenceMapper'
|
||||
import { SQLLegacyRevisionPersistenceMapper } from '../Mapping/Persistence/SQL/SQLLegacyRevisionPersistenceMapper'
|
||||
import { MongoDBRevisionMetadataPersistenceMapper } from '../Mapping/Persistence/MongoDB/MongoDBRevisionMetadataPersistenceMapper'
|
||||
import { MongoDBRevisionPersistenceMapper } from '../Mapping/Persistence/MongoDB/MongoDBRevisionPersistenceMapper'
|
||||
import { RevisionHttpMapper } from '../Mapping/Http/RevisionHttpMapper'
|
||||
@@ -62,6 +62,10 @@ import { DomainEventFactoryInterface } from '../Domain/Event/DomainEventFactoryI
|
||||
import { DomainEventFactory } from '../Domain/Event/DomainEventFactory'
|
||||
import { TransitionStatusUpdatedEventHandler } from '../Domain/Handler/TransitionStatusUpdatedEventHandler'
|
||||
import { TriggerTransitionFromPrimaryToSecondaryDatabaseForUser } from '../Domain/UseCase/Transition/TriggerTransitionFromPrimaryToSecondaryDatabaseForUser/TriggerTransitionFromPrimaryToSecondaryDatabaseForUser'
|
||||
import { SQLRevision } from '../Infra/TypeORM/SQL/SQLRevision'
|
||||
import { SQLRevisionRepository } from '../Infra/TypeORM/SQL/SQLRevisionRepository'
|
||||
import { SQLRevisionMetadataPersistenceMapper } from '../Mapping/Persistence/SQL/SQLRevisionMetadataPersistenceMapper'
|
||||
import { SQLRevisionPersistenceMapper } from '../Mapping/Persistence/SQL/SQLRevisionPersistenceMapper'
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
async load(configuration?: {
|
||||
@@ -77,6 +81,8 @@ export class ContainerConfigLoader {
|
||||
env.load()
|
||||
|
||||
const isConfiguredForHomeServer = env.get('MODE', true) === 'home-server'
|
||||
const isConfiguredForSelfHosting = env.get('MODE', true) === 'self-hosted'
|
||||
const isConfiguredForHomeServerOrSelfHosting = isConfiguredForHomeServer || isConfiguredForSelfHosting
|
||||
const isSecondaryDatabaseEnabled = env.get('SECONDARY_DB_ENABLED', true) === 'true'
|
||||
|
||||
const container = new Container({
|
||||
@@ -197,9 +203,17 @@ export class ContainerConfigLoader {
|
||||
.toConstantValue(new DomainEventFactory(container.get(TYPES.Revisions_Timer)))
|
||||
|
||||
// Map
|
||||
container
|
||||
.bind<MapperInterface<RevisionMetadata, SQLLegacyRevision>>(
|
||||
TYPES.Revisions_SQLLegacyRevisionMetadataPersistenceMapper,
|
||||
)
|
||||
.toConstantValue(new SQLLegacyRevisionMetadataPersistenceMapper())
|
||||
container
|
||||
.bind<MapperInterface<RevisionMetadata, SQLRevision>>(TYPES.Revisions_SQLRevisionMetadataPersistenceMapper)
|
||||
.toConstantValue(new SQLRevisionMetadataPersistenceMapper())
|
||||
container
|
||||
.bind<MapperInterface<Revision, SQLLegacyRevision>>(TYPES.Revisions_SQLLegacyRevisionPersistenceMapper)
|
||||
.toConstantValue(new SQLLegacyRevisionPersistenceMapper())
|
||||
container
|
||||
.bind<MapperInterface<Revision, SQLRevision>>(TYPES.Revisions_SQLRevisionPersistenceMapper)
|
||||
.toConstantValue(new SQLRevisionPersistenceMapper())
|
||||
@@ -213,22 +227,36 @@ export class ContainerConfigLoader {
|
||||
.toConstantValue(new MongoDBRevisionPersistenceMapper())
|
||||
|
||||
// ORM
|
||||
container
|
||||
.bind<Repository<SQLLegacyRevision>>(TYPES.Revisions_ORMLegacyRevisionRepository)
|
||||
.toDynamicValue(() => appDataSource.getRepository(SQLLegacyRevision))
|
||||
container
|
||||
.bind<Repository<SQLRevision>>(TYPES.Revisions_ORMRevisionRepository)
|
||||
.toDynamicValue(() => appDataSource.getRepository(SQLRevision))
|
||||
.toConstantValue(appDataSource.getRepository(SQLRevision))
|
||||
|
||||
// Repositories
|
||||
container
|
||||
.bind<RevisionRepositoryInterface>(TYPES.Revisions_SQLRevisionRepository)
|
||||
.toConstantValue(
|
||||
new SQLRevisionRepository(
|
||||
container.get<Repository<SQLRevision>>(TYPES.Revisions_ORMRevisionRepository),
|
||||
container.get<MapperInterface<RevisionMetadata, SQLRevision>>(
|
||||
TYPES.Revisions_SQLRevisionMetadataPersistenceMapper,
|
||||
),
|
||||
container.get<MapperInterface<Revision, SQLRevision>>(TYPES.Revisions_SQLRevisionPersistenceMapper),
|
||||
container.get<winston.Logger>(TYPES.Revisions_Logger),
|
||||
),
|
||||
isConfiguredForHomeServerOrSelfHosting
|
||||
? new SQLRevisionRepository(
|
||||
container.get<Repository<SQLRevision>>(TYPES.Revisions_ORMRevisionRepository),
|
||||
container.get<MapperInterface<RevisionMetadata, SQLRevision>>(
|
||||
TYPES.Revisions_SQLRevisionMetadataPersistenceMapper,
|
||||
),
|
||||
container.get<MapperInterface<Revision, SQLRevision>>(TYPES.Revisions_SQLRevisionPersistenceMapper),
|
||||
container.get<winston.Logger>(TYPES.Revisions_Logger),
|
||||
)
|
||||
: new SQLLegacyRevisionRepository(
|
||||
container.get<Repository<SQLLegacyRevision>>(TYPES.Revisions_ORMLegacyRevisionRepository),
|
||||
container.get<MapperInterface<RevisionMetadata, SQLLegacyRevision>>(
|
||||
TYPES.Revisions_SQLLegacyRevisionMetadataPersistenceMapper,
|
||||
),
|
||||
container.get<MapperInterface<Revision, SQLLegacyRevision>>(
|
||||
TYPES.Revisions_SQLLegacyRevisionPersistenceMapper,
|
||||
),
|
||||
container.get<winston.Logger>(TYPES.Revisions_Logger),
|
||||
),
|
||||
)
|
||||
|
||||
if (isSecondaryDatabaseEnabled) {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { DataSource, EntityTarget, LoggerOptions, MongoRepository, ObjectLiteral, Repository } from 'typeorm'
|
||||
import { MysqlConnectionOptions } from 'typeorm/driver/mysql/MysqlConnectionOptions'
|
||||
|
||||
import { SQLRevision } from '../Infra/TypeORM/SQL/SQLRevision'
|
||||
import { SQLLegacyRevision } from '../Infra/TypeORM/SQL/SQLLegacyRevision'
|
||||
|
||||
import { Env } from './Env'
|
||||
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
|
||||
import { MongoDBRevision } from '../Infra/TypeORM/MongoDB/MongoDBRevision'
|
||||
import { SQLRevision } from '../Infra/TypeORM/SQL/SQLRevision'
|
||||
|
||||
export class AppDataSource {
|
||||
private _dataSource: DataSource | undefined
|
||||
@@ -65,14 +66,23 @@ export class AppDataSource {
|
||||
|
||||
const isConfiguredForMySQL = this.env.get('DB_TYPE') === 'mysql'
|
||||
|
||||
const isConfiguredForHomeServerOrSelfHosting =
|
||||
this.env.get('MODE', true) === 'home-server' || this.env.get('MODE', true) === 'self-hosted'
|
||||
|
||||
const maxQueryExecutionTime = this.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
|
||||
? +this.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
|
||||
: 45_000
|
||||
|
||||
const migrationsSourceDirectoryName = isConfiguredForMySQL
|
||||
? isConfiguredForHomeServerOrSelfHosting
|
||||
? 'mysql'
|
||||
: 'mysql-legacy'
|
||||
: 'sqlite'
|
||||
|
||||
const commonDataSourceOptions = {
|
||||
maxQueryExecutionTime,
|
||||
entities: [SQLRevision],
|
||||
migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
|
||||
entities: [isConfiguredForHomeServerOrSelfHosting ? SQLRevision : SQLLegacyRevision],
|
||||
migrations: [`${__dirname}/../../migrations/${migrationsSourceDirectoryName}/*.js`],
|
||||
migrationsRun: true,
|
||||
logging: <LoggerOptions>this.env.get('DB_DEBUG_LEVEL', true) ?? 'info',
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { AppDataSource } from './DataSource'
|
||||
import { Env } from './Env'
|
||||
|
||||
const env: Env = new Env()
|
||||
env.load()
|
||||
|
||||
export const MigrationsDataSource = new AppDataSource(env).dataSource
|
||||
@@ -6,7 +6,11 @@ const TYPES = {
|
||||
Revisions_S3: Symbol.for('Revisions_S3'),
|
||||
Revisions_Env: Symbol.for('Revisions_Env'),
|
||||
// Map
|
||||
Revisions_SQLLegacyRevisionMetadataPersistenceMapper: Symbol.for(
|
||||
'Revisions_SQLLegacyRevisionMetadataPersistenceMapper',
|
||||
),
|
||||
Revisions_SQLRevisionMetadataPersistenceMapper: Symbol.for('Revisions_SQLRevisionMetadataPersistenceMapper'),
|
||||
Revisions_SQLLegacyRevisionPersistenceMapper: Symbol.for('Revisions_SQLLegacyRevisionPersistenceMapper'),
|
||||
Revisions_SQLRevisionPersistenceMapper: Symbol.for('Revisions_SQLRevisionPersistenceMapper'),
|
||||
Revisions_MongoDBRevisionMetadataPersistenceMapper: Symbol.for('Revisions_MongoDBRevisionMetadataPersistenceMapper'),
|
||||
Revisions_MongoDBRevisionPersistenceMapper: Symbol.for('Revisions_MongoDBRevisionPersistenceMapper'),
|
||||
@@ -15,6 +19,7 @@ const TYPES = {
|
||||
Revisions_RevisionMetadataHttpMapper: Symbol.for('Revisions_RevisionMetadataHttpMapper'),
|
||||
// ORM
|
||||
Revisions_ORMRevisionRepository: Symbol.for('Revisions_ORMRevisionRepository'),
|
||||
Revisions_ORMLegacyRevisionRepository: Symbol.for('Revisions_ORMLegacyRevisionRepository'),
|
||||
// Mongo
|
||||
Revisions_ORMMongoRevisionRepository: Symbol.for('Revisions_ORMMongoRevisionRepository'),
|
||||
// Repositories
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface KeySystemAssociationProps {
|
||||
keySystemIdentifier: string
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { KeySystemAssociation } from './KeySystemAssociation'
|
||||
|
||||
describe('KeySystemAssociation', () => {
|
||||
it('should create a value object', () => {
|
||||
const entityOrError = KeySystemAssociation.create('00000000-0000-0000-0000-000000000000')
|
||||
|
||||
expect(entityOrError.isFailed()).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should fail to create a value object with an empty key system identifier', () => {
|
||||
const entityOrError = KeySystemAssociation.create('')
|
||||
|
||||
expect(entityOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Result, Validator, ValueObject } from '@standardnotes/domain-core'
|
||||
|
||||
import { KeySystemAssociationProps } from './KeySystemAssocationProps'
|
||||
|
||||
export class KeySystemAssociation extends ValueObject<KeySystemAssociationProps> {
|
||||
private constructor(props: KeySystemAssociationProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(keySystemIdentifier: string): Result<KeySystemAssociation> {
|
||||
const validationResult = Validator.isNotEmptyString(keySystemIdentifier)
|
||||
if (validationResult.isFailed()) {
|
||||
return Result.fail<KeySystemAssociation>(validationResult.getError())
|
||||
}
|
||||
|
||||
return Result.ok<KeySystemAssociation>(new KeySystemAssociation({ keySystemIdentifier }))
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
import { ContentType, Dates, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultAssociation } from '../SharedVault/SharedVaultAssociation'
|
||||
import { KeySystemAssociation } from '../KeySystem/KeySystemAssociation'
|
||||
|
||||
export interface RevisionProps {
|
||||
itemUuid: Uuid
|
||||
userUuid: Uuid | null
|
||||
@@ -10,4 +13,6 @@ export interface RevisionProps {
|
||||
authHash: string | null
|
||||
creationDate: Date
|
||||
dates: Dates
|
||||
sharedVaultAssociation?: SharedVaultAssociation
|
||||
keySystemAssociation?: KeySystemAssociation
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export interface RevisionRepositoryInterface {
|
||||
removeOneByUuid(revisionUuid: Uuid, userUuid: Uuid): Promise<void>
|
||||
findOneByUuid(revisionUuid: Uuid, userUuid: Uuid): Promise<Revision | null>
|
||||
findByItemUuid(itemUuid: Uuid): Promise<Array<Revision>>
|
||||
findMetadataByItemId(itemUuid: Uuid, userUuid: Uuid): Promise<Array<RevisionMetadata>>
|
||||
findMetadataByItemId(itemUuid: Uuid, userUuid: Uuid, sharedVaultUuids: Uuid[]): Promise<Array<RevisionMetadata>>
|
||||
updateUserUuid(itemUuid: Uuid, userUuid: Uuid): Promise<void>
|
||||
findByUserUuid(dto: { userUuid: Uuid; offset?: number; limit?: number }): Promise<Array<Revision>>
|
||||
insert(revision: Revision): Promise<boolean>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultAssociation } from './SharedVaultAssociation'
|
||||
|
||||
describe('SharedVaultAssociation', () => {
|
||||
it('should create a value object', () => {
|
||||
const entityOrError = SharedVaultAssociation.create({
|
||||
editedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
})
|
||||
|
||||
expect(entityOrError.isFailed()).toBeFalsy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Result, ValueObject } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultAssociationProps } from './SharedVaultAssociationProps'
|
||||
|
||||
export class SharedVaultAssociation extends ValueObject<SharedVaultAssociationProps> {
|
||||
private constructor(props: SharedVaultAssociationProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(props: SharedVaultAssociationProps): Result<SharedVaultAssociation> {
|
||||
return Result.ok<SharedVaultAssociation>(new SharedVaultAssociation(props))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
export interface SharedVaultAssociationProps {
|
||||
editedBy: Uuid
|
||||
sharedVaultUuid: Uuid
|
||||
}
|
||||
@@ -22,17 +22,30 @@ describe('GetRevisionsMetada', () => {
|
||||
itemUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
sharedVaultUuids: ['84c0f8e8-544a-4c7e-9adf-26209303bc1d'],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
expect(result.getValue().length).toEqual(1)
|
||||
})
|
||||
|
||||
it('should not return revisions metadata for an invalid shared vault uuid', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
itemUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
sharedVaultUuids: ['1-2-3'],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should not return revisions metadata for a an invalid item uuid', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
itemUuid: '1-2-3',
|
||||
userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
sharedVaultUuids: [],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
@@ -43,6 +56,7 @@ describe('GetRevisionsMetada', () => {
|
||||
userUuid: '1-2-3',
|
||||
itemUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
sharedVaultUuids: [],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
@@ -53,6 +67,7 @@ describe('GetRevisionsMetada', () => {
|
||||
itemUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['INVALID_ROLE_NAME'],
|
||||
sharedVaultUuids: [],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
|
||||
@@ -19,6 +19,15 @@ export class GetRevisionsMetada implements UseCaseInterface<RevisionMetadata[]>
|
||||
return Result.fail<RevisionMetadata[]>(`Could not get revisions: ${userUuidOrError.getError()}`)
|
||||
}
|
||||
|
||||
const sharedVaultUuids = []
|
||||
for (const sharedVaultUuid of dto.sharedVaultUuids) {
|
||||
const sharedVaultUuidOrError = Uuid.create(sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
return Result.fail<RevisionMetadata[]>(`Could not get revisions: ${sharedVaultUuidOrError.getError()}`)
|
||||
}
|
||||
sharedVaultUuids.push(sharedVaultUuidOrError.getValue())
|
||||
}
|
||||
|
||||
const roleNamesOrError = RoleNameCollection.create(dto.roleNames)
|
||||
if (roleNamesOrError.isFailed()) {
|
||||
return Result.fail(roleNamesOrError.getError())
|
||||
@@ -30,6 +39,7 @@ export class GetRevisionsMetada implements UseCaseInterface<RevisionMetadata[]>
|
||||
const revisionsMetdata = await revisionRepository.findMetadataByItemId(
|
||||
itemUuidOrError.getValue(),
|
||||
userUuidOrError.getValue(),
|
||||
sharedVaultUuids,
|
||||
)
|
||||
|
||||
return Result.ok<RevisionMetadata[]>(revisionsMetdata)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export interface GetRevisionsMetadaDTO {
|
||||
itemUuid: string
|
||||
sharedVaultUuids: string[]
|
||||
userUuid: string
|
||||
roleNames: string[]
|
||||
}
|
||||
|
||||
@@ -38,6 +38,9 @@ export class BaseRevisionsController extends BaseHttpController {
|
||||
itemUuid: request.params.itemUuid,
|
||||
userUuid: response.locals.user.uuid,
|
||||
roleNames: response.locals.roles.map((role: Role) => role.name),
|
||||
sharedVaultUuids: response.locals.belongsToSharedVaults.map(
|
||||
(association: { shared_vault_uuid: string; permission: string }) => association.shared_vault_uuid,
|
||||
),
|
||||
})
|
||||
|
||||
if (revisionMetadataOrError.isFailed()) {
|
||||
|
||||
@@ -47,6 +47,7 @@ export class ApiGatewayAuthMiddleware extends BaseMiddleware {
|
||||
response.locals.roles = token.roles
|
||||
response.locals.session = token.session
|
||||
response.locals.readOnlyAccess = token.session?.readonly_access ?? false
|
||||
response.locals.belongsToSharedVaults = token.belongs_to_shared_vaults ?? []
|
||||
|
||||
return next()
|
||||
} catch (error) {
|
||||
|
||||
@@ -37,4 +37,14 @@ export class MongoDBRevision {
|
||||
|
||||
@Column()
|
||||
declare updatedAt: Date
|
||||
|
||||
@Column()
|
||||
declare editedBy: string | null
|
||||
|
||||
@Column()
|
||||
@Index('index_revisions_on_shared_vault_uuid')
|
||||
declare sharedVaultUuid: string | null
|
||||
|
||||
@Column()
|
||||
declare keySystemIdentifier: string | null
|
||||
}
|
||||
|
||||
@@ -86,16 +86,36 @@ export class MongoDBRevisionRepository implements RevisionRepositoryInterface {
|
||||
return revisions
|
||||
}
|
||||
|
||||
async findMetadataByItemId(itemUuid: Uuid, userUuid: Uuid): Promise<RevisionMetadata[]> {
|
||||
const persistence = await this.mongoRepository.find({
|
||||
select: ['_id', 'contentType', 'createdAt', 'updatedAt'],
|
||||
where: {
|
||||
$and: [{ itemUuid: { $eq: itemUuid.value } }, { userUuid: { $eq: userUuid.value } }],
|
||||
},
|
||||
order: {
|
||||
createdAt: 'DESC',
|
||||
},
|
||||
})
|
||||
async findMetadataByItemId(
|
||||
itemUuid: Uuid,
|
||||
userUuid: Uuid,
|
||||
sharedVaultUuids: Uuid[],
|
||||
): Promise<Array<RevisionMetadata>> {
|
||||
let persistence = []
|
||||
if (sharedVaultUuids.length > 0) {
|
||||
persistence = await this.mongoRepository.find({
|
||||
select: ['_id', 'contentType', 'createdAt', 'updatedAt'],
|
||||
where: {
|
||||
$and: [
|
||||
{ itemUuid: { $eq: itemUuid.value } },
|
||||
{ sharedVaultUuid: { $in: sharedVaultUuids.map((uuid) => uuid.value) } },
|
||||
],
|
||||
},
|
||||
order: {
|
||||
createdAt: 'DESC',
|
||||
},
|
||||
})
|
||||
} else {
|
||||
persistence = await this.mongoRepository.find({
|
||||
select: ['_id', 'contentType', 'createdAt', 'updatedAt'],
|
||||
where: {
|
||||
$and: [{ itemUuid: { $eq: itemUuid.value } }, { userUuid: { $eq: userUuid.value } }],
|
||||
},
|
||||
order: {
|
||||
createdAt: 'DESC',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const revisions: RevisionMetadata[] = []
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
|
||||
|
||||
@Entity({ name: 'revisions' })
|
||||
export class SQLLegacyRevision {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
declare uuid: string
|
||||
|
||||
@Column({
|
||||
name: 'item_uuid',
|
||||
length: 36,
|
||||
})
|
||||
@Index('item_uuid')
|
||||
declare itemUuid: string
|
||||
|
||||
@Column({
|
||||
name: 'user_uuid',
|
||||
length: 36,
|
||||
type: 'varchar',
|
||||
nullable: true,
|
||||
})
|
||||
@Index('user_uuid')
|
||||
declare userUuid: string | null
|
||||
|
||||
@Column({
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
})
|
||||
declare content: string | null
|
||||
|
||||
@Column({
|
||||
name: 'content_type',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare contentType: string | null
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
name: 'items_key_id',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare itemsKeyId: string | null
|
||||
|
||||
@Column({
|
||||
name: 'enc_item_key',
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
})
|
||||
declare encItemKey: string | null
|
||||
|
||||
@Column({
|
||||
name: 'auth_hash',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare authHash: string | null
|
||||
|
||||
@Column({
|
||||
name: 'creation_date',
|
||||
type: 'date',
|
||||
nullable: true,
|
||||
})
|
||||
declare creationDate: Date
|
||||
|
||||
@Column({
|
||||
name: 'created_at',
|
||||
type: 'datetime',
|
||||
precision: 6,
|
||||
nullable: true,
|
||||
})
|
||||
declare createdAt: Date
|
||||
|
||||
@Column({
|
||||
name: 'updated_at',
|
||||
type: 'datetime',
|
||||
precision: 6,
|
||||
nullable: true,
|
||||
})
|
||||
declare updatedAt: Date
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
import { MapperInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { Repository } from 'typeorm'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { Revision } from '../../../Domain/Revision/Revision'
|
||||
import { RevisionMetadata } from '../../../Domain/Revision/RevisionMetadata'
|
||||
import { RevisionRepositoryInterface } from '../../../Domain/Revision/RevisionRepositoryInterface'
|
||||
import { SQLLegacyRevision } from './SQLLegacyRevision'
|
||||
|
||||
export class SQLLegacyRevisionRepository implements RevisionRepositoryInterface {
|
||||
constructor(
|
||||
protected ormRepository: Repository<SQLLegacyRevision>,
|
||||
protected revisionMetadataMapper: MapperInterface<RevisionMetadata, SQLLegacyRevision>,
|
||||
protected revisionMapper: MapperInterface<Revision, SQLLegacyRevision>,
|
||||
protected logger: Logger,
|
||||
) {}
|
||||
|
||||
async countByUserUuid(userUuid: Uuid): Promise<number> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.where('user_uuid = :userUuid', { userUuid: userUuid.value })
|
||||
.getCount()
|
||||
}
|
||||
|
||||
async findByUserUuid(dto: { userUuid: Uuid; offset?: number; limit?: number }): Promise<Revision[]> {
|
||||
const queryBuilder = this.ormRepository
|
||||
.createQueryBuilder('revision')
|
||||
.where('revision.user_uuid = :userUuid', { userUuid: dto.userUuid.value })
|
||||
.orderBy('revision.uuid', 'ASC')
|
||||
|
||||
if (dto.offset !== undefined) {
|
||||
queryBuilder.skip(dto.offset)
|
||||
}
|
||||
|
||||
if (dto.limit !== undefined) {
|
||||
queryBuilder.take(dto.limit)
|
||||
}
|
||||
|
||||
const sqlRevisions = await queryBuilder.getMany()
|
||||
|
||||
const revisions = []
|
||||
for (const sqlRevision of sqlRevisions) {
|
||||
revisions.push(this.revisionMapper.toDomain(sqlRevision))
|
||||
}
|
||||
|
||||
return revisions
|
||||
}
|
||||
|
||||
async updateUserUuid(itemUuid: Uuid, userUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.update()
|
||||
.set({
|
||||
userUuid: userUuid.value,
|
||||
})
|
||||
.where('item_uuid = :itemUuid', { itemUuid: itemUuid.value })
|
||||
.execute()
|
||||
}
|
||||
|
||||
async findByItemUuid(itemUuid: Uuid): Promise<Revision[]> {
|
||||
const SQLLegacyRevisions = await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.where('item_uuid = :itemUuid', { itemUuid: itemUuid.value })
|
||||
.getMany()
|
||||
|
||||
const revisions = []
|
||||
for (const revision of SQLLegacyRevisions) {
|
||||
revisions.push(this.revisionMapper.toDomain(revision))
|
||||
}
|
||||
|
||||
return revisions
|
||||
}
|
||||
|
||||
async removeByUserUuid(userUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.delete()
|
||||
.from('revisions')
|
||||
.where('user_uuid = :userUuid', { userUuid: userUuid.value })
|
||||
.execute()
|
||||
}
|
||||
|
||||
async removeOneByUuid(revisionUuid: Uuid, userUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.delete()
|
||||
.from('revisions')
|
||||
.where('uuid = :revisionUuid AND user_uuid = :userUuid', {
|
||||
userUuid: userUuid.value,
|
||||
revisionUuid: revisionUuid.value,
|
||||
})
|
||||
.execute()
|
||||
}
|
||||
|
||||
async findOneByUuid(revisionUuid: Uuid, userUuid: Uuid): Promise<Revision | null> {
|
||||
const SQLLegacyRevision = await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.where('uuid = :revisionUuid', { revisionUuid: revisionUuid.value })
|
||||
.andWhere('user_uuid = :userUuid', { userUuid: userUuid.value })
|
||||
.getOne()
|
||||
|
||||
if (SQLLegacyRevision === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.revisionMapper.toDomain(SQLLegacyRevision)
|
||||
}
|
||||
|
||||
async insert(revision: Revision): Promise<boolean> {
|
||||
const SQLLegacyRevision = this.revisionMapper.toProjection(revision)
|
||||
|
||||
await this.ormRepository.insert(SQLLegacyRevision)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
async findMetadataByItemId(
|
||||
itemUuid: Uuid,
|
||||
userUuid: Uuid,
|
||||
_sharedVaultUuids: Uuid[],
|
||||
): Promise<Array<RevisionMetadata>> {
|
||||
const queryBuilder = this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.select('uuid', 'uuid')
|
||||
.addSelect('content_type', 'contentType')
|
||||
.addSelect('created_at', 'createdAt')
|
||||
.addSelect('updated_at', 'updatedAt')
|
||||
.where('item_uuid = :itemUuid', { itemUuid: itemUuid.value })
|
||||
.andWhere('user_uuid = :userUuid', { userUuid: userUuid.value })
|
||||
.orderBy('created_at', 'DESC')
|
||||
|
||||
const simplifiedRevisions = await queryBuilder.getRawMany()
|
||||
|
||||
this.logger.debug(
|
||||
`Found ${simplifiedRevisions.length} revisions entries for item ${itemUuid.value}`,
|
||||
simplifiedRevisions,
|
||||
)
|
||||
|
||||
const metadata = []
|
||||
for (const simplifiedRevision of simplifiedRevisions) {
|
||||
metadata.push(this.revisionMetadataMapper.toDomain(simplifiedRevision))
|
||||
}
|
||||
|
||||
return metadata
|
||||
}
|
||||
}
|
||||
@@ -1,83 +1,31 @@
|
||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
|
||||
import { Column, Entity, Index } from 'typeorm'
|
||||
|
||||
import { SQLLegacyRevision } from './SQLLegacyRevision'
|
||||
|
||||
@Entity({ name: 'revisions' })
|
||||
export class SQLRevision {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
declare uuid: string
|
||||
|
||||
export class SQLRevision extends SQLLegacyRevision {
|
||||
@Column({
|
||||
name: 'item_uuid',
|
||||
type: 'varchar',
|
||||
name: 'edited_by',
|
||||
length: 36,
|
||||
nullable: true,
|
||||
})
|
||||
@Index('item_uuid')
|
||||
declare itemUuid: string
|
||||
declare editedBy: string | null
|
||||
|
||||
@Column({
|
||||
name: 'user_uuid',
|
||||
type: 'varchar',
|
||||
name: 'shared_vault_uuid',
|
||||
length: 36,
|
||||
type: 'varchar',
|
||||
nullable: true,
|
||||
})
|
||||
@Index('user_uuid')
|
||||
declare userUuid: string | null
|
||||
|
||||
@Column({
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
})
|
||||
declare content: string | null
|
||||
|
||||
@Column({
|
||||
name: 'content_type',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare contentType: string | null
|
||||
@Index('index_revisions_on_shared_vault_uuid')
|
||||
declare sharedVaultUuid: string | null
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
name: 'items_key_id',
|
||||
length: 255,
|
||||
name: 'key_system_identifier',
|
||||
length: 36,
|
||||
nullable: true,
|
||||
})
|
||||
declare itemsKeyId: string | null
|
||||
|
||||
@Column({
|
||||
name: 'enc_item_key',
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
})
|
||||
declare encItemKey: string | null
|
||||
|
||||
@Column({
|
||||
name: 'auth_hash',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare authHash: string | null
|
||||
|
||||
@Column({
|
||||
name: 'creation_date',
|
||||
type: 'date',
|
||||
nullable: true,
|
||||
})
|
||||
declare creationDate: Date
|
||||
|
||||
@Column({
|
||||
name: 'created_at',
|
||||
type: 'datetime',
|
||||
precision: 6,
|
||||
nullable: true,
|
||||
})
|
||||
declare createdAt: Date
|
||||
|
||||
@Column({
|
||||
name: 'updated_at',
|
||||
type: 'datetime',
|
||||
precision: 6,
|
||||
nullable: true,
|
||||
})
|
||||
declare updatedAt: Date
|
||||
declare keySystemIdentifier: string | null
|
||||
}
|
||||
|
||||
@@ -4,127 +4,40 @@ import { Logger } from 'winston'
|
||||
|
||||
import { Revision } from '../../../Domain/Revision/Revision'
|
||||
import { RevisionMetadata } from '../../../Domain/Revision/RevisionMetadata'
|
||||
import { RevisionRepositoryInterface } from '../../../Domain/Revision/RevisionRepositoryInterface'
|
||||
import { SQLLegacyRevisionRepository } from './SQLLegacyRevisionRepository'
|
||||
import { SQLRevision } from './SQLRevision'
|
||||
|
||||
export class SQLRevisionRepository implements RevisionRepositoryInterface {
|
||||
export class SQLRevisionRepository extends SQLLegacyRevisionRepository {
|
||||
constructor(
|
||||
private ormRepository: Repository<SQLRevision>,
|
||||
private revisionMetadataMapper: MapperInterface<RevisionMetadata, SQLRevision>,
|
||||
private revisionMapper: MapperInterface<Revision, SQLRevision>,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async countByUserUuid(userUuid: Uuid): Promise<number> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.where('user_uuid = :userUuid', { userUuid: userUuid.value })
|
||||
.getCount()
|
||||
protected override ormRepository: Repository<SQLRevision>,
|
||||
protected override revisionMetadataMapper: MapperInterface<RevisionMetadata, SQLRevision>,
|
||||
protected override revisionMapper: MapperInterface<Revision, SQLRevision>,
|
||||
protected override logger: Logger,
|
||||
) {
|
||||
super(ormRepository, revisionMetadataMapper, revisionMapper, logger)
|
||||
}
|
||||
|
||||
async findByUserUuid(dto: { userUuid: Uuid; offset?: number; limit?: number }): Promise<Revision[]> {
|
||||
const queryBuilder = this.ormRepository
|
||||
.createQueryBuilder('revision')
|
||||
.where('revision.user_uuid = :userUuid', { userUuid: dto.userUuid.value })
|
||||
.orderBy('revision.uuid', 'ASC')
|
||||
|
||||
if (dto.offset !== undefined) {
|
||||
queryBuilder.skip(dto.offset)
|
||||
}
|
||||
|
||||
if (dto.limit !== undefined) {
|
||||
queryBuilder.take(dto.limit)
|
||||
}
|
||||
|
||||
const sqlRevisions = await queryBuilder.getMany()
|
||||
|
||||
const revisions = []
|
||||
for (const sqlRevision of sqlRevisions) {
|
||||
revisions.push(this.revisionMapper.toDomain(sqlRevision))
|
||||
}
|
||||
|
||||
return revisions
|
||||
}
|
||||
|
||||
async updateUserUuid(itemUuid: Uuid, userUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.update()
|
||||
.set({
|
||||
userUuid: userUuid.value,
|
||||
})
|
||||
.where('item_uuid = :itemUuid', { itemUuid: itemUuid.value })
|
||||
.execute()
|
||||
}
|
||||
|
||||
async findByItemUuid(itemUuid: Uuid): Promise<Revision[]> {
|
||||
const SQLRevisions = await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.where('item_uuid = :itemUuid', { itemUuid: itemUuid.value })
|
||||
.getMany()
|
||||
|
||||
const revisions = []
|
||||
for (const revision of SQLRevisions) {
|
||||
revisions.push(this.revisionMapper.toDomain(revision))
|
||||
}
|
||||
|
||||
return revisions
|
||||
}
|
||||
|
||||
async removeByUserUuid(userUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.delete()
|
||||
.from('revisions')
|
||||
.where('user_uuid = :userUuid', { userUuid: userUuid.value })
|
||||
.execute()
|
||||
}
|
||||
|
||||
async removeOneByUuid(revisionUuid: Uuid, userUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.delete()
|
||||
.from('revisions')
|
||||
.where('uuid = :revisionUuid AND user_uuid = :userUuid', {
|
||||
userUuid: userUuid.value,
|
||||
revisionUuid: revisionUuid.value,
|
||||
})
|
||||
.execute()
|
||||
}
|
||||
|
||||
async findOneByUuid(revisionUuid: Uuid, userUuid: Uuid): Promise<Revision | null> {
|
||||
const SQLRevision = await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.where('uuid = :revisionUuid', { revisionUuid: revisionUuid.value })
|
||||
.andWhere('user_uuid = :userUuid', { userUuid: userUuid.value })
|
||||
.getOne()
|
||||
|
||||
if (SQLRevision === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.revisionMapper.toDomain(SQLRevision)
|
||||
}
|
||||
|
||||
async insert(revision: Revision): Promise<boolean> {
|
||||
const SQLRevision = this.revisionMapper.toProjection(revision)
|
||||
|
||||
await this.ormRepository.insert(SQLRevision)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
async findMetadataByItemId(itemUuid: Uuid, userUuid: Uuid): Promise<Array<RevisionMetadata>> {
|
||||
override async findMetadataByItemId(
|
||||
itemUuid: Uuid,
|
||||
userUuid: Uuid,
|
||||
sharedVaultUuids: Uuid[],
|
||||
): Promise<Array<RevisionMetadata>> {
|
||||
const queryBuilder = this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.select('uuid', 'uuid')
|
||||
.addSelect('content_type', 'contentType')
|
||||
.addSelect('created_at', 'createdAt')
|
||||
.addSelect('updated_at', 'updatedAt')
|
||||
.where('item_uuid = :itemUuid', { itemUuid: itemUuid.value })
|
||||
.andWhere('user_uuid = :userUuid', { userUuid: userUuid.value })
|
||||
.where('item_uuid = :itemUuid AND user_uuid = :userUuid', { itemUuid: itemUuid.value, userUuid: userUuid.value })
|
||||
.orderBy('created_at', 'DESC')
|
||||
|
||||
if (sharedVaultUuids.length > 0) {
|
||||
queryBuilder.orWhere('item_uuid = :itemUuid AND shared_vault_uuid IN (:...sharedVaultUuids)', {
|
||||
itemUuid: itemUuid.value,
|
||||
sharedVaultUuids: sharedVaultUuids.map((uuid) => uuid.value),
|
||||
})
|
||||
}
|
||||
|
||||
const simplifiedRevisions = await queryBuilder.getRawMany()
|
||||
|
||||
this.logger.debug(
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { MapperInterface, Dates, Uuid, ContentType } from '@standardnotes/domain-core'
|
||||
|
||||
import { Revision } from '../../Domain/Revision/Revision'
|
||||
import { SharedVaultAssociation } from '../../Domain/SharedVault/SharedVaultAssociation'
|
||||
import { KeySystemAssociation } from '../../Domain/KeySystem/KeySystemAssociation'
|
||||
|
||||
export class RevisionItemStringMapper implements MapperInterface<Revision, string> {
|
||||
toDomain(projection: string): Revision {
|
||||
@@ -24,6 +26,39 @@ export class RevisionItemStringMapper implements MapperInterface<Revision, strin
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
let sharedVaultAssociation: SharedVaultAssociation | undefined = undefined
|
||||
if (item.shared_vault_uuid && item.last_edited_by) {
|
||||
const sharedVaultUuidOrError = Uuid.create(item.shared_vault_uuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
throw new Error(`Failed to create revision from projection: ${sharedVaultUuidOrError.getError()}`)
|
||||
}
|
||||
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
|
||||
const lastEditedByOrError = Uuid.create(item.last_edited_by)
|
||||
if (lastEditedByOrError.isFailed()) {
|
||||
throw new Error(`Failed to create revision from projection: ${lastEditedByOrError.getError()}`)
|
||||
}
|
||||
const lastEditedBy = lastEditedByOrError.getValue()
|
||||
|
||||
const sharedVaultAssociationOrError = SharedVaultAssociation.create({
|
||||
sharedVaultUuid,
|
||||
editedBy: lastEditedBy,
|
||||
})
|
||||
if (sharedVaultAssociationOrError.isFailed()) {
|
||||
throw new Error(`Failed to create revision from projection: ${sharedVaultAssociationOrError.getError()}`)
|
||||
}
|
||||
sharedVaultAssociation = sharedVaultAssociationOrError.getValue()
|
||||
}
|
||||
|
||||
let keySystemAssociation: KeySystemAssociation | undefined = undefined
|
||||
if (item.key_system_identifier) {
|
||||
const keySystemAssociationOrError = KeySystemAssociation.create(item.key_system_identifier)
|
||||
if (keySystemAssociationOrError.isFailed()) {
|
||||
throw new Error(`Failed to create revision from projection: ${keySystemAssociationOrError.getError()}`)
|
||||
}
|
||||
keySystemAssociation = keySystemAssociationOrError.getValue()
|
||||
}
|
||||
|
||||
const revisionOrError = Revision.create({
|
||||
itemUuid,
|
||||
userUuid,
|
||||
@@ -34,6 +69,8 @@ export class RevisionItemStringMapper implements MapperInterface<Revision, strin
|
||||
encItemKey: item.enc_item_key,
|
||||
creationDate: new Date(),
|
||||
dates: Dates.create(new Date(), new Date()).getValue(),
|
||||
sharedVaultAssociation,
|
||||
keySystemAssociation,
|
||||
})
|
||||
|
||||
if (revisionOrError.isFailed()) {
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import { MapperInterface, Dates, UniqueEntityId, ContentType } from '@standardnotes/domain-core'
|
||||
|
||||
import { RevisionMetadata } from '../../../Domain/Revision/RevisionMetadata'
|
||||
import { SQLLegacyRevision } from '../../../Infra/TypeORM/SQL/SQLLegacyRevision'
|
||||
|
||||
export class SQLLegacyRevisionMetadataPersistenceMapper
|
||||
implements MapperInterface<RevisionMetadata, SQLLegacyRevision>
|
||||
{
|
||||
toDomain(projection: SQLLegacyRevision): RevisionMetadata {
|
||||
const contentTypeOrError = ContentType.create(projection.contentType)
|
||||
if (contentTypeOrError.isFailed()) {
|
||||
throw new Error(`Could not create content type: ${contentTypeOrError.getError()}`)
|
||||
}
|
||||
const contentType = contentTypeOrError.getValue()
|
||||
|
||||
const createdAt = projection.createdAt instanceof Date ? projection.createdAt : new Date(projection.createdAt)
|
||||
const updatedAt = projection.updatedAt instanceof Date ? projection.updatedAt : new Date(projection.updatedAt)
|
||||
|
||||
const datesOrError = Dates.create(createdAt, updatedAt)
|
||||
if (datesOrError.isFailed()) {
|
||||
throw new Error(`Could not create dates: ${datesOrError.getError()}`)
|
||||
}
|
||||
const dates = datesOrError.getValue()
|
||||
|
||||
const revisionMetadataOrError = RevisionMetadata.create(
|
||||
{
|
||||
contentType,
|
||||
dates,
|
||||
},
|
||||
new UniqueEntityId(projection.uuid),
|
||||
)
|
||||
|
||||
if (revisionMetadataOrError.isFailed()) {
|
||||
throw new Error(`Could not create revision metdata: ${revisionMetadataOrError.getError()}`)
|
||||
}
|
||||
|
||||
return revisionMetadataOrError.getValue()
|
||||
}
|
||||
|
||||
toProjection(_domain: RevisionMetadata): SQLLegacyRevision {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { MapperInterface, Dates, UniqueEntityId, Uuid, ContentType } from '@standardnotes/domain-core'
|
||||
|
||||
import { Revision } from '../../../Domain/Revision/Revision'
|
||||
import { SQLLegacyRevision } from '../../../Infra/TypeORM/SQL/SQLLegacyRevision'
|
||||
|
||||
export class SQLLegacyRevisionPersistenceMapper implements MapperInterface<Revision, SQLLegacyRevision> {
|
||||
toDomain(projection: SQLLegacyRevision): Revision {
|
||||
const contentTypeOrError = ContentType.create(projection.contentType)
|
||||
if (contentTypeOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${contentTypeOrError.getError()}`)
|
||||
}
|
||||
const contentType = contentTypeOrError.getValue()
|
||||
|
||||
const datesOrError = Dates.create(projection.createdAt, projection.updatedAt)
|
||||
if (datesOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${datesOrError.getError()}`)
|
||||
}
|
||||
const dates = datesOrError.getValue()
|
||||
|
||||
const itemUuidOrError = Uuid.create(projection.itemUuid)
|
||||
if (itemUuidOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${itemUuidOrError.getError()}`)
|
||||
}
|
||||
const itemUuid = itemUuidOrError.getValue()
|
||||
|
||||
let userUuid = null
|
||||
if (projection.userUuid !== null) {
|
||||
const userUuidOrError = Uuid.create(projection.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${userUuidOrError.getError()}`)
|
||||
}
|
||||
userUuid = userUuidOrError.getValue()
|
||||
}
|
||||
|
||||
const revisionOrError = Revision.create(
|
||||
{
|
||||
authHash: projection.authHash,
|
||||
content: projection.content,
|
||||
contentType,
|
||||
creationDate: projection.creationDate,
|
||||
encItemKey: projection.encItemKey,
|
||||
itemsKeyId: projection.itemsKeyId,
|
||||
itemUuid,
|
||||
userUuid,
|
||||
dates,
|
||||
},
|
||||
new UniqueEntityId(projection.uuid),
|
||||
)
|
||||
if (revisionOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${revisionOrError.getError()}`)
|
||||
}
|
||||
|
||||
return revisionOrError.getValue()
|
||||
}
|
||||
|
||||
toProjection(domain: Revision): SQLLegacyRevision {
|
||||
const sqlRevision = new SQLLegacyRevision()
|
||||
|
||||
sqlRevision.authHash = domain.props.authHash
|
||||
sqlRevision.content = domain.props.content
|
||||
sqlRevision.contentType = domain.props.contentType.value
|
||||
sqlRevision.createdAt = domain.props.dates.createdAt
|
||||
sqlRevision.updatedAt = domain.props.dates.updatedAt
|
||||
sqlRevision.creationDate = domain.props.creationDate
|
||||
sqlRevision.encItemKey = domain.props.encItemKey
|
||||
sqlRevision.itemUuid = domain.props.itemUuid.value
|
||||
sqlRevision.itemsKeyId = domain.props.itemsKeyId
|
||||
sqlRevision.userUuid = domain.props.userUuid ? domain.props.userUuid.value : null
|
||||
sqlRevision.uuid = domain.id.toString()
|
||||
|
||||
return sqlRevision
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ import { MapperInterface, Dates, UniqueEntityId, Uuid, ContentType } from '@stan
|
||||
|
||||
import { Revision } from '../../../Domain/Revision/Revision'
|
||||
import { SQLRevision } from '../../../Infra/TypeORM/SQL/SQLRevision'
|
||||
import { SharedVaultAssociation } from '../../../Domain/SharedVault/SharedVaultAssociation'
|
||||
import { KeySystemAssociation } from '../../../Domain/KeySystem/KeySystemAssociation'
|
||||
|
||||
export class SQLRevisionPersistenceMapper implements MapperInterface<Revision, SQLRevision> {
|
||||
toDomain(projection: SQLRevision): Revision {
|
||||
@@ -32,6 +34,39 @@ export class SQLRevisionPersistenceMapper implements MapperInterface<Revision, S
|
||||
userUuid = userUuidOrError.getValue()
|
||||
}
|
||||
|
||||
let sharedVaultAssociation: SharedVaultAssociation | undefined = undefined
|
||||
if (projection.sharedVaultUuid && projection.editedBy) {
|
||||
const sharedVaultUuidOrError = Uuid.create(projection.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
throw new Error(`Failed to create revision from projection: ${sharedVaultUuidOrError.getError()}`)
|
||||
}
|
||||
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
|
||||
const lastEditedByOrError = Uuid.create(projection.editedBy)
|
||||
if (lastEditedByOrError.isFailed()) {
|
||||
throw new Error(`Failed to create revision from projection: ${lastEditedByOrError.getError()}`)
|
||||
}
|
||||
const lastEditedBy = lastEditedByOrError.getValue()
|
||||
|
||||
const sharedVaultAssociationOrError = SharedVaultAssociation.create({
|
||||
sharedVaultUuid,
|
||||
editedBy: lastEditedBy,
|
||||
})
|
||||
if (sharedVaultAssociationOrError.isFailed()) {
|
||||
throw new Error(`Failed to create revision from projection: ${sharedVaultAssociationOrError.getError()}`)
|
||||
}
|
||||
sharedVaultAssociation = sharedVaultAssociationOrError.getValue()
|
||||
}
|
||||
|
||||
let keySystemAssociation: KeySystemAssociation | undefined = undefined
|
||||
if (projection.keySystemIdentifier) {
|
||||
const keySystemAssociationOrError = KeySystemAssociation.create(projection.keySystemIdentifier)
|
||||
if (keySystemAssociationOrError.isFailed()) {
|
||||
throw new Error(`Failed to create revision from projection: ${keySystemAssociationOrError.getError()}`)
|
||||
}
|
||||
keySystemAssociation = keySystemAssociationOrError.getValue()
|
||||
}
|
||||
|
||||
const revisionOrError = Revision.create(
|
||||
{
|
||||
authHash: projection.authHash,
|
||||
@@ -43,6 +78,8 @@ export class SQLRevisionPersistenceMapper implements MapperInterface<Revision, S
|
||||
itemUuid,
|
||||
userUuid,
|
||||
dates,
|
||||
sharedVaultAssociation,
|
||||
keySystemAssociation,
|
||||
},
|
||||
new UniqueEntityId(projection.uuid),
|
||||
)
|
||||
@@ -67,6 +104,15 @@ export class SQLRevisionPersistenceMapper implements MapperInterface<Revision, S
|
||||
sqlRevision.itemsKeyId = domain.props.itemsKeyId
|
||||
sqlRevision.userUuid = domain.props.userUuid ? domain.props.userUuid.value : null
|
||||
sqlRevision.uuid = domain.id.toString()
|
||||
sqlRevision.sharedVaultUuid = domain.props.sharedVaultAssociation
|
||||
? domain.props.sharedVaultAssociation.props.sharedVaultUuid.value
|
||||
: null
|
||||
sqlRevision.editedBy = domain.props.sharedVaultAssociation
|
||||
? domain.props.sharedVaultAssociation.props.editedBy.value
|
||||
: null
|
||||
sqlRevision.keySystemIdentifier = domain.props.keySystemAssociation
|
||||
? domain.props.keySystemAssociation.props.keySystemIdentifier
|
||||
: null
|
||||
|
||||
return sqlRevision
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.20.36](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.35...@standardnotes/scheduler-server@1.20.36) (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.20.35](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.34...@standardnotes/scheduler-server@1.20.35) (2023-09-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user