mirror of
https://github.com/standardnotes/server
synced 2026-05-10 00:57:19 -04:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 55f8f65c3f | |||
| 3953dbc6b4 | |||
| 0b205287d1 | |||
| 4f0bc57b1a | |||
| 7d43316597 | |||
| 65d31f011b | |||
| 80dd6efae3 | |||
| a96f2c9153 | |||
| 225e0aaf88 | |||
| f0c85910bc | |||
| 124c443528 | |||
| 37c7f8d39f | |||
| c419f1ce22 | |||
| 4949cdfe2f | |||
| cd101b96ea | |||
| 40d0e4631f | |||
| a55a995660 | |||
| 1d576d48ad | |||
| 4ff8030f87 | |||
| c15e2e2c8f | |||
| 41d31a8d75 | |||
| 10e2a26352 | |||
| 6e547f77d0 | |||
| 530a426601 | |||
| 642d6bab77 | |||
| 7980af3d82 | |||
| 2980c42e88 | |||
| b03994f9db | |||
| 41906ec2f9 | |||
| 4d1e7ff2a5 | |||
| 7f18fcfc13 | |||
| ff02ce0747 | |||
| a6056600eb | |||
| 24c94326d5 | |||
| 48c0cb5e62 | |||
| 9968efe1b2 | |||
| 6368342149 | |||
| b5f73db210 | |||
| 22d6a02d04 | |||
| 4e0bcfcccf | |||
| 104313c15d |
@@ -187,7 +187,7 @@ jobs:
|
||||
tags: standardnotes/${{ inputs.service_name }}:${{ github.sha }}
|
||||
|
||||
- name: Run E2E test suite
|
||||
uses: convictional/trigger-workflow-and-wait@v1.6.3
|
||||
uses: convictional/trigger-workflow-and-wait@master
|
||||
with:
|
||||
owner: standardnotes
|
||||
repo: e2e
|
||||
|
||||
@@ -66,7 +66,7 @@ const RAW_RUNTIME_STATE =
|
||||
"reference": "workspace:packages/security"\
|
||||
},\
|
||||
{\
|
||||
"name": "@standardnotes/settings",\
|
||||
"name": "@standardnotes/settings-server",\
|
||||
"reference": "workspace:packages/settings"\
|
||||
},\
|
||||
{\
|
||||
@@ -107,7 +107,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@standardnotes/scheduler-server", ["workspace:packages/scheduler"]],\
|
||||
["@standardnotes/security", ["workspace:packages/security"]],\
|
||||
["@standardnotes/server-monorepo", ["workspace:."]],\
|
||||
["@standardnotes/settings", ["workspace:packages/settings"]],\
|
||||
["@standardnotes/settings-server", ["workspace:packages/settings"]],\
|
||||
["@standardnotes/sncrypto-node", ["workspace:packages/sncrypto-node"]],\
|
||||
["@standardnotes/syncing-server", ["workspace:packages/syncing-server"]],\
|
||||
["@standardnotes/time", ["workspace:packages/time"]],\
|
||||
@@ -2582,6 +2582,20 @@ const RAW_RUNTIME_STATE =
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.20.13", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-api-npm-1.20.13-3efe52d749-67bdb982ec.zip/node_modules/@standardnotes/api/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/api", "npm:1.20.13"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/encryption", "npm:1.19.21"],\
|
||||
["@standardnotes/models", "npm:1.38.0"],\
|
||||
["@standardnotes/responses", "npm:1.12.9"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/utils", "npm:1.13.0"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/api-gateway", [\
|
||||
@@ -2656,7 +2670,6 @@ const RAW_RUNTIME_STATE =
|
||||
["@standardnotes/predicates", "workspace:packages/predicates"],\
|
||||
["@standardnotes/responses", "npm:1.11.1"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/settings", "workspace:packages/settings"],\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.0"],\
|
||||
["@standardnotes/sncrypto-node", "workspace:packages/sncrypto-node"],\
|
||||
["@standardnotes/time", "workspace:packages/time"],\
|
||||
@@ -2812,6 +2825,19 @@ const RAW_RUNTIME_STATE =
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.19.21", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-encryption-npm-1.19.21-dfa10f00e6-c8c2c27bfe.zip/node_modules/@standardnotes/encryption/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/encryption", "npm:1.19.21"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/models", "npm:1.38.0"],\
|
||||
["@standardnotes/responses", "npm:1.12.9"],\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.3"],\
|
||||
["@standardnotes/utils", "npm:1.13.0"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/event-store", [\
|
||||
@@ -2867,6 +2893,17 @@ const RAW_RUNTIME_STATE =
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.55.3", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-features-npm-1.55.3-c124505183-b39fe2d49b.zip/node_modules/@standardnotes/features/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/features", "npm:1.55.3"],\
|
||||
["@standardnotes/auth", "npm:3.19.4"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/files-server", [\
|
||||
@@ -2948,6 +2985,13 @@ const RAW_RUNTIME_STATE =
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.38.0", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-models-npm-1.38.0-108f602f56-2dc2ac957e.zip/node_modules/@standardnotes/models/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/models", "npm:1.38.0"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/payloads", [\
|
||||
@@ -3001,6 +3045,17 @@ const RAW_RUNTIME_STATE =
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.12.9", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-responses-npm-1.12.9-280dc75972-353fe1ca6d.zip/node_modules/@standardnotes/responses/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/responses", "npm:1.12.9"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/features", "npm:1.55.3"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/revisions-server", [\
|
||||
@@ -3132,15 +3187,46 @@ const RAW_RUNTIME_STATE =
|
||||
"linkType": "SOFT"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/settings", [\
|
||||
["@standardnotes/settings-server", [\
|
||||
["workspace:packages/settings", {\
|
||||
"packageLocation": "./packages/settings/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/settings", "workspace:packages/settings"],\
|
||||
["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:5.30.5"],\
|
||||
["eslint-plugin-prettier", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:4.2.1"],\
|
||||
["@standardnotes/settings-server", "workspace:packages/settings"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.0"],\
|
||||
["@sentry/node", "npm:7.19.0"],\
|
||||
["@standardnotes/api", "npm:1.20.13"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/time", "workspace:packages/time"],\
|
||||
["@types/cors", "npm:2.8.12"],\
|
||||
["@types/dotenv", "npm:8.2.0"],\
|
||||
["@types/express", "npm:4.17.14"],\
|
||||
["@types/inversify-express-utils", "npm:2.0.0"],\
|
||||
["@types/ioredis", "npm:5.0.0"],\
|
||||
["@types/jest", "npm:29.1.1"],\
|
||||
["@types/newrelic", "npm:7.0.4"],\
|
||||
["@typescript-eslint/eslint-plugin", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:5.40.1"],\
|
||||
["aws-sdk", "npm:2.1260.0"],\
|
||||
["cors", "npm:2.8.5"],\
|
||||
["dotenv", "npm:16.0.1"],\
|
||||
["eslint", "npm:8.25.0"],\
|
||||
["eslint-plugin-prettier", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.2.1"],\
|
||||
["express", "npm:4.18.2"],\
|
||||
["helmet", "npm:6.0.0"],\
|
||||
["inversify", "npm:6.0.1"],\
|
||||
["inversify-express-utils", "npm:6.4.3"],\
|
||||
["ioredis", "npm:5.2.4"],\
|
||||
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.1.2"],\
|
||||
["mysql2", "npm:2.3.3"],\
|
||||
["newrelic", "npm:9.6.0"],\
|
||||
["npm-check-updates", "npm:16.0.1"],\
|
||||
["reflect-metadata", "npm:0.1.13"],\
|
||||
["typescript", "patch:typescript@npm%3A4.8.4#optional!builtin<compat/typescript>::version=4.8.4&hash=701156"]\
|
||||
["ts-jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.0.3"],\
|
||||
["typeorm", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:0.3.10"],\
|
||||
["typescript", "patch:typescript@npm%3A4.8.4#optional!builtin<compat/typescript>::version=4.8.4&hash=701156"],\
|
||||
["winston", "npm:3.8.2"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}]\
|
||||
@@ -3153,6 +3239,14 @@ const RAW_RUNTIME_STATE =
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.13.3", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-sncrypto-common-npm-1.13.3-97ef3850ce-a73af90962.zip/node_modules/@standardnotes/sncrypto-common/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.3"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/sncrypto-node", [\
|
||||
@@ -3189,7 +3283,6 @@ const RAW_RUNTIME_STATE =
|
||||
["@standardnotes/payloads", "npm:1.5.1"],\
|
||||
["@standardnotes/responses", "npm:1.11.1"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/settings", "workspace:packages/settings"],\
|
||||
["@standardnotes/time", "workspace:packages/time"],\
|
||||
["@types/cors", "npm:2.8.12"],\
|
||||
["@types/dotenv", "npm:8.2.0"],\
|
||||
@@ -3273,6 +3366,17 @@ const RAW_RUNTIME_STATE =
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.13.0", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-utils-npm-1.13.0-28780a59f0-1578e8adb7.zip/node_modules/@standardnotes/utils/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/utils", "npm:1.13.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["dompurify", "npm:2.4.1"],\
|
||||
["lodash", "npm:4.17.21"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/websockets-server", [\
|
||||
@@ -6371,6 +6475,13 @@ const RAW_RUNTIME_STATE =
|
||||
["dompurify", "npm:2.4.0"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:2.4.1", {\
|
||||
"packageLocation": "./.yarn/cache/dompurify-npm-2.4.1-1c79f22057-ddc0633356.zip/node_modules/dompurify/",\
|
||||
"packageDependencies": [\
|
||||
["dompurify", "npm:2.4.1"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["dot-prop", [\
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,6 +3,32 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.12.25](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.24...@standardnotes/analytics@2.12.25) (2022-12-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.12.24](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.23...@standardnotes/analytics@2.12.24) (2022-12-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **analytics:** daily analytics report template ([41906ec](https://github.com/standardnotes/server/commit/41906ec2f9fd4d605b1c002826173e14fb534e00))
|
||||
|
||||
## [2.12.23](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.22...@standardnotes/analytics@2.12.23) (2022-12-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.12.22](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.21...@standardnotes/analytics@2.12.22) (2022-12-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **analytics:** report event publishing ([a605660](https://github.com/standardnotes/server/commit/a6056600eb96bf175189ad6d62870c9d736f331b))
|
||||
|
||||
## [2.12.21](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.20...@standardnotes/analytics@2.12.21) (2022-12-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **analytics:** add debug logs for report ([48c0cb5](https://github.com/standardnotes/server/commit/48c0cb5e62dc8af930de191deaa1eb3ff6c5a29f))
|
||||
|
||||
## [2.12.20](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.19...@standardnotes/analytics@2.12.20) (2022-12-09)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
@@ -219,26 +219,27 @@ const requestReport = async (
|
||||
}
|
||||
|
||||
for (const adminEmail of adminEmails) {
|
||||
const event = domainEventFactory.createEmailRequestedEvent({
|
||||
messageIdentifier: 'VERSION_ADOPTION_REPORT',
|
||||
subject: getSubject(),
|
||||
body: getBody(
|
||||
{
|
||||
activityStatistics: yesterdayActivityStatistics,
|
||||
activityStatisticsOverTime: analyticsOverTime,
|
||||
statisticsOverTime,
|
||||
statisticMeasures,
|
||||
churn: {
|
||||
periodKeys: monthlyPeriodKeys,
|
||||
values: churnRates,
|
||||
await domainEventPublisher.publish(
|
||||
domainEventFactory.createEmailRequestedEvent({
|
||||
messageIdentifier: 'VERSION_ADOPTION_REPORT',
|
||||
subject: getSubject(),
|
||||
body: getBody(
|
||||
{
|
||||
activityStatistics: yesterdayActivityStatistics,
|
||||
activityStatisticsOverTime: analyticsOverTime,
|
||||
statisticsOverTime,
|
||||
statisticMeasures,
|
||||
churn: {
|
||||
periodKeys: monthlyPeriodKeys,
|
||||
values: churnRates,
|
||||
},
|
||||
},
|
||||
},
|
||||
timer,
|
||||
),
|
||||
level: EmailLevel.LEVELS.System,
|
||||
userEmail: adminEmail,
|
||||
})
|
||||
await domainEventPublisher.publish(event)
|
||||
timer,
|
||||
),
|
||||
level: EmailLevel.LEVELS.System,
|
||||
userEmail: adminEmail,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,6 +261,9 @@ void container.load().then((container) => {
|
||||
const calculateMonthlyRecurringRevenue: CalculateMonthlyRecurringRevenue = container.get(
|
||||
TYPES.CalculateMonthlyRecurringRevenue,
|
||||
)
|
||||
const adminEmails = container.get(TYPES.ADMIN_EMAILS) as string[]
|
||||
|
||||
logger.info(`Sending report to following admins: ${adminEmails}`)
|
||||
|
||||
Promise.resolve(
|
||||
requestReport(
|
||||
@@ -270,7 +274,7 @@ void container.load().then((container) => {
|
||||
periodKeyGenerator,
|
||||
calculateMonthlyRecurringRevenue,
|
||||
timer,
|
||||
container.get(TYPES.ADMIN_EMAILS),
|
||||
adminEmails,
|
||||
),
|
||||
)
|
||||
.then(() => {
|
||||
|
||||
@@ -5,17 +5,17 @@ COMMAND=$1 && shift 1
|
||||
|
||||
case "$COMMAND" in
|
||||
'start-worker' )
|
||||
echo "Starting Worker..."
|
||||
echo "[Docker] Starting Worker..."
|
||||
yarn workspace @standardnotes/analytics worker
|
||||
;;
|
||||
|
||||
'report' )
|
||||
echo "Starting Usage Report Generation..."
|
||||
echo "[Docker] Starting Usage Report Generation..."
|
||||
yarn workspace @standardnotes/analytics report
|
||||
;;
|
||||
|
||||
* )
|
||||
echo "Unknown command"
|
||||
echo "[Docker] Unknown command"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "2.12.20",
|
||||
"version": "2.12.25",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -377,7 +377,7 @@ const getChartUrls = (
|
||||
}
|
||||
|
||||
export const html = (data: any, timer: TimerInterface) => {
|
||||
const chartUrls = getChartUrls(event)
|
||||
const chartUrls = getChartUrls(data)
|
||||
|
||||
const successfullPaymentsActivity = data.activityStatistics.find(
|
||||
(a: { name: AnalyticsActivity }) => a.name === AnalyticsActivity.PaymentSuccess && Period.Yesterday,
|
||||
|
||||
@@ -11,6 +11,7 @@ WEB_SOCKET_SERVER_URL=http://websockets:3000
|
||||
PAYMENTS_SERVER_URL=http://payments:3000
|
||||
FILES_SERVER_URL=http://files:3000
|
||||
REVISIONS_SERVER_URL=http://revisions:3000
|
||||
EMAIL_SERVER_URL=http://email:3000
|
||||
|
||||
HTTP_CALL_TIMEOUT=60000
|
||||
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.40.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.40.1...@standardnotes/api-gateway@1.40.2) (2022-12-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.40.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.40.0...@standardnotes/api-gateway@1.40.1) (2022-12-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
# [1.40.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.39.24...@standardnotes/api-gateway@1.40.0) (2022-12-12)
|
||||
|
||||
### Features
|
||||
|
||||
* **api-gateway:** add unsubscribe from emails endpoint ([22d6a02](https://github.com/standardnotes/api-gateway/commit/22d6a02d049ba3bde890c7def91e19f013ba3e22))
|
||||
|
||||
## [1.39.24](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.39.23...@standardnotes/api-gateway@1.39.24) (2022-12-09)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.39.24",
|
||||
"version": "1.40.2",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -55,6 +55,7 @@ export class ContainerConfigLoader {
|
||||
container.bind(TYPES.SYNCING_SERVER_JS_URL).toConstantValue(env.get('SYNCING_SERVER_JS_URL'))
|
||||
container.bind(TYPES.AUTH_SERVER_URL).toConstantValue(env.get('AUTH_SERVER_URL'))
|
||||
container.bind(TYPES.REVISIONS_SERVER_URL).toConstantValue(env.get('REVISIONS_SERVER_URL', true))
|
||||
container.bind(TYPES.EMAIL_SERVER_URL).toConstantValue(env.get('EMAIL_SERVER_URL', true))
|
||||
container.bind(TYPES.PAYMENTS_SERVER_URL).toConstantValue(env.get('PAYMENTS_SERVER_URL', true))
|
||||
container.bind(TYPES.FILES_SERVER_URL).toConstantValue(env.get('FILES_SERVER_URL', true))
|
||||
container.bind(TYPES.AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET'))
|
||||
|
||||
@@ -8,6 +8,7 @@ const TYPES = {
|
||||
PAYMENTS_SERVER_URL: Symbol.for('PAYMENTS_SERVER_URL'),
|
||||
FILES_SERVER_URL: Symbol.for('FILES_SERVER_URL'),
|
||||
REVISIONS_SERVER_URL: Symbol.for('REVISIONS_SERVER_URL'),
|
||||
EMAIL_SERVER_URL: Symbol.for('EMAIL_SERVER_URL'),
|
||||
WORKSPACE_SERVER_URL: Symbol.for('WORKSPACE_SERVER_URL'),
|
||||
WEB_SOCKET_SERVER_URL: Symbol.for('WEB_SOCKET_SERVER_URL'),
|
||||
AUTH_JWT_SECRET: Symbol.for('AUTH_JWT_SECRET'),
|
||||
|
||||
@@ -29,4 +29,14 @@ export class ActionsController extends BaseHttpController {
|
||||
async methods(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callAuthServer(request, response, 'auth/methods', request.body)
|
||||
}
|
||||
|
||||
@httpGet('/unsubscribe/:token')
|
||||
async emailUnsubscribe(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callEmailServer(
|
||||
request,
|
||||
response,
|
||||
`subscriptions/actions/unsubscribe/${request.params.token}`,
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ export class HttpService implements HttpServiceInterface {
|
||||
@inject(TYPES.WORKSPACE_SERVER_URL) private workspaceServerUrl: string,
|
||||
@inject(TYPES.WEB_SOCKET_SERVER_URL) private webSocketServerUrl: string,
|
||||
@inject(TYPES.REVISIONS_SERVER_URL) private revisionsServerUrl: string,
|
||||
@inject(TYPES.EMAIL_SERVER_URL) private emailServerUrl: string,
|
||||
@inject(TYPES.HTTP_CALL_TIMEOUT) private httpCallTimeout: number,
|
||||
@inject(TYPES.CrossServiceTokenCache) private crossServiceTokenCache: CrossServiceTokenCacheInterface,
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
@@ -65,6 +66,21 @@ export class HttpService implements HttpServiceInterface {
|
||||
await this.callServer(this.authServerUrl, request, response, endpoint, payload)
|
||||
}
|
||||
|
||||
async callEmailServer(
|
||||
request: Request,
|
||||
response: Response,
|
||||
endpoint: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
): Promise<void> {
|
||||
if (!this.emailServerUrl) {
|
||||
response.status(400).send({ message: 'Email Server not configured' })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
await this.callServer(this.emailServerUrl, request, response, endpoint, payload)
|
||||
}
|
||||
|
||||
async callWorkspaceServer(
|
||||
request: Request,
|
||||
response: Response,
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { Request, Response } from 'express'
|
||||
|
||||
export interface HttpServiceInterface {
|
||||
callEmailServer(
|
||||
request: Request,
|
||||
response: Response,
|
||||
endpoint: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
): Promise<void>
|
||||
callAuthServer(
|
||||
request: Request,
|
||||
response: Response,
|
||||
|
||||
@@ -3,6 +3,32 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.67.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.67.0...@standardnotes/auth-server@1.67.1) (2022-12-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* user signed in email template ([c15e2e2](https://github.com/standardnotes/server/commit/c15e2e2c8f3a6c177e227d25440501fa38dd3d0e))
|
||||
|
||||
# [1.67.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.66.9...@standardnotes/auth-server@1.67.0) (2022-12-12)
|
||||
|
||||
### Features
|
||||
|
||||
* **auth:** add email subscription unsubscribed event handler ([10e2a26](https://github.com/standardnotes/server/commit/10e2a263522dfa33c06940f29cb77f783f66b20c))
|
||||
|
||||
## [1.66.9](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.66.8...@standardnotes/auth-server@1.66.9) (2022-12-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.66.8](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.66.7...@standardnotes/auth-server@1.66.8) (2022-12-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.66.7](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.66.6...@standardnotes/auth-server@1.66.7) (2022-12-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** linter issue ([104313c](https://github.com/standardnotes/server/commit/104313c15df79f6308d23e21f65111e5bd3d9c72))
|
||||
|
||||
## [1.66.6](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.66.5...@standardnotes/auth-server@1.66.6) (2022-12-09)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
+21
-22
@@ -3,20 +3,19 @@ import 'reflect-metadata'
|
||||
import 'newrelic'
|
||||
|
||||
import { Stream } from 'stream'
|
||||
|
||||
import { Logger } from 'winston'
|
||||
import * as dayjs from 'dayjs'
|
||||
import * as utc from 'dayjs/plugin/utc'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { PermissionName } from '@standardnotes/features'
|
||||
|
||||
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||
import TYPES from '../src/Bootstrap/Types'
|
||||
import { Env } from '../src/Bootstrap/Env'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface'
|
||||
import { SettingRepositoryInterface } from '../src/Domain/Setting/SettingRepositoryInterface'
|
||||
import { MuteFailedBackupsEmailsOption, MuteFailedCloudBackupsEmailsOption, SettingName } from '@standardnotes/settings'
|
||||
import { RoleServiceInterface } from '../src/Domain/Role/RoleServiceInterface'
|
||||
import { PermissionName } from '@standardnotes/features'
|
||||
import { SettingServiceInterface } from '../src/Domain/Setting/SettingServiceInterface'
|
||||
|
||||
const inputArgs = process.argv.slice(2)
|
||||
@@ -30,38 +29,38 @@ const requestBackups = async (
|
||||
domainEventFactory: DomainEventFactoryInterface,
|
||||
domainEventPublisher: DomainEventPublisherInterface,
|
||||
): Promise<void> => {
|
||||
let settingName: SettingName,
|
||||
let settingName: string,
|
||||
permissionName: PermissionName,
|
||||
muteEmailsSettingName: SettingName,
|
||||
muteEmailsSettingName: string,
|
||||
muteEmailsSettingValue: string,
|
||||
providerTokenSettingName: SettingName
|
||||
providerTokenSettingName: string
|
||||
switch (backupProvider) {
|
||||
case 'email':
|
||||
settingName = SettingName.EmailBackupFrequency
|
||||
settingName = SettingName.NAMES.EmailBackupFrequency
|
||||
permissionName = PermissionName.DailyEmailBackup
|
||||
muteEmailsSettingName = SettingName.MuteFailedBackupsEmails
|
||||
muteEmailsSettingValue = MuteFailedBackupsEmailsOption.Muted
|
||||
muteEmailsSettingName = SettingName.NAMES.MuteFailedBackupsEmails
|
||||
muteEmailsSettingValue = 'muted'
|
||||
break
|
||||
case 'dropbox':
|
||||
settingName = SettingName.DropboxBackupFrequency
|
||||
settingName = SettingName.NAMES.DropboxBackupFrequency
|
||||
permissionName = PermissionName.DailyDropboxBackup
|
||||
muteEmailsSettingName = SettingName.MuteFailedCloudBackupsEmails
|
||||
muteEmailsSettingValue = MuteFailedCloudBackupsEmailsOption.Muted
|
||||
providerTokenSettingName = SettingName.DropboxBackupToken
|
||||
muteEmailsSettingName = SettingName.NAMES.MuteFailedCloudBackupsEmails
|
||||
muteEmailsSettingValue = 'muted'
|
||||
providerTokenSettingName = SettingName.NAMES.DropboxBackupToken
|
||||
break
|
||||
case 'one_drive':
|
||||
settingName = SettingName.OneDriveBackupFrequency
|
||||
settingName = SettingName.NAMES.OneDriveBackupFrequency
|
||||
permissionName = PermissionName.DailyOneDriveBackup
|
||||
muteEmailsSettingName = SettingName.MuteFailedCloudBackupsEmails
|
||||
muteEmailsSettingValue = MuteFailedCloudBackupsEmailsOption.Muted
|
||||
providerTokenSettingName = SettingName.OneDriveBackupToken
|
||||
muteEmailsSettingName = SettingName.NAMES.MuteFailedCloudBackupsEmails
|
||||
muteEmailsSettingValue = 'muted'
|
||||
providerTokenSettingName = SettingName.NAMES.OneDriveBackupToken
|
||||
break
|
||||
case 'google_drive':
|
||||
settingName = SettingName.GoogleDriveBackupFrequency
|
||||
settingName = SettingName.NAMES.GoogleDriveBackupFrequency
|
||||
permissionName = PermissionName.DailyGDriveBackup
|
||||
muteEmailsSettingName = SettingName.MuteFailedCloudBackupsEmails
|
||||
muteEmailsSettingValue = MuteFailedCloudBackupsEmailsOption.Muted
|
||||
providerTokenSettingName = SettingName.GoogleDriveBackupToken
|
||||
muteEmailsSettingName = SettingName.NAMES.MuteFailedCloudBackupsEmails
|
||||
muteEmailsSettingValue = 'muted'
|
||||
providerTokenSettingName = SettingName.NAMES.GoogleDriveBackupToken
|
||||
break
|
||||
default:
|
||||
throw new Error(`Not handled backup provider: ${backupProvider}`)
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Env } from '../src/Bootstrap/Env'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface'
|
||||
import { SettingRepositoryInterface } from '../src/Domain/Setting/SettingRepositoryInterface'
|
||||
import { MuteFailedBackupsEmailsOption, SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { RoleServiceInterface } from '../src/Domain/Role/RoleServiceInterface'
|
||||
import { PermissionName } from '@standardnotes/features'
|
||||
import { UserRepositoryInterface } from '../src/Domain/User/UserRepositoryInterface'
|
||||
@@ -28,8 +28,8 @@ const requestBackups = async (
|
||||
domainEventPublisher: DomainEventPublisherInterface,
|
||||
): Promise<void> => {
|
||||
const permissionName = PermissionName.DailyEmailBackup
|
||||
const muteEmailsSettingName = SettingName.MuteFailedBackupsEmails
|
||||
const muteEmailsSettingValue = MuteFailedBackupsEmailsOption.Muted
|
||||
const muteEmailsSettingName = SettingName.NAMES.MuteFailedBackupsEmails
|
||||
const muteEmailsSettingValue = 'muted'
|
||||
|
||||
if (!backupEmail) {
|
||||
throw new Error('Could not trigger email backup for user - missing email parameter')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Redis, { Cluster } from 'ioredis'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
import { Setting } from '../src/Domain/Setting/Setting'
|
||||
@@ -34,7 +34,7 @@ export class moveMfaItemsToUserSettings1627638504691 implements MigrationInterfa
|
||||
|
||||
const setting = new Setting()
|
||||
setting.uuid = item['uuid']
|
||||
setting.name = SettingName.MfaSecret
|
||||
setting.name = SettingName.NAMES.MfaSecret
|
||||
setting.value = item['content']
|
||||
if (item['deleted']) {
|
||||
setting.value = null
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/auth-server",
|
||||
"version": "1.66.6",
|
||||
"version": "1.67.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
@@ -41,7 +41,6 @@
|
||||
"@standardnotes/predicates": "workspace:*",
|
||||
"@standardnotes/responses": "^1.6.39",
|
||||
"@standardnotes/security": "workspace:*",
|
||||
"@standardnotes/settings": "workspace:*",
|
||||
"@standardnotes/sncrypto-common": "^1.9.0",
|
||||
"@standardnotes/sncrypto-node": "workspace:*",
|
||||
"@standardnotes/time": "workspace:*",
|
||||
|
||||
@@ -193,6 +193,7 @@ import { SubscriptionInvitesController } from '../Controller/SubscriptionInvites
|
||||
import { CreateCrossServiceToken } from '../Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken'
|
||||
import { ProcessUserRequest } from '../Domain/UseCase/ProcessUserRequest/ProcessUserRequest'
|
||||
import { UserRequestsController } from '../Controller/UserRequestsController'
|
||||
import { EmailSubscriptionUnsubscribedEventHandler } from '../Domain/Handler/EmailSubscriptionUnsubscribedEventHandler'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const newrelicFormatter = require('@newrelic/winston-enricher')
|
||||
@@ -560,6 +561,15 @@ export class ContainerConfigLoader {
|
||||
)
|
||||
}
|
||||
|
||||
container
|
||||
.bind<EmailSubscriptionUnsubscribedEventHandler>(TYPES.EmailSubscriptionUnsubscribedEventHandler)
|
||||
.toConstantValue(
|
||||
new EmailSubscriptionUnsubscribedEventHandler(
|
||||
container.get(TYPES.UserRepository),
|
||||
container.get(TYPES.SettingService),
|
||||
),
|
||||
)
|
||||
|
||||
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
|
||||
['USER_REGISTERED', container.get(TYPES.UserRegisteredEventHandler)],
|
||||
['ACCOUNT_DELETION_REQUESTED', container.get(TYPES.AccountDeletionRequestedEventHandler)],
|
||||
@@ -582,6 +592,7 @@ export class ContainerConfigLoader {
|
||||
],
|
||||
['SHARED_SUBSCRIPTION_INVITATION_CREATED', container.get(TYPES.SharedSubscriptionInvitationCreatedEventHandler)],
|
||||
['PREDICATE_VERIFICATION_REQUESTED', container.get(TYPES.PredicateVerificationRequestedEventHandler)],
|
||||
['EMAIL_SUBSCRIPTION_UNSUBSCRIBED', container.get(TYPES.EmailSubscriptionUnsubscribedEventHandler)],
|
||||
])
|
||||
|
||||
if (env.get('SQS_QUEUE_URL', true)) {
|
||||
|
||||
@@ -138,6 +138,7 @@ const TYPES = {
|
||||
UserDisabledSessionUserAgentLoggingEventHandler: Symbol.for('UserDisabledSessionUserAgentLoggingEventHandler'),
|
||||
SharedSubscriptionInvitationCreatedEventHandler: Symbol.for('SharedSubscriptionInvitationCreatedEventHandler'),
|
||||
PredicateVerificationRequestedEventHandler: Symbol.for('PredicateVerificationRequestedEventHandler'),
|
||||
EmailSubscriptionUnsubscribedEventHandler: Symbol.for('EmailSubscriptionUnsubscribedEventHandler'),
|
||||
// Services
|
||||
DeviceDetector: Symbol.for('DeviceDetector'),
|
||||
SessionService: Symbol.for('SessionService'),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { Request } from 'express'
|
||||
import { inject } from 'inversify'
|
||||
import {
|
||||
@@ -69,7 +69,7 @@ export class AdminController extends BaseHttpController {
|
||||
const result = await this.doDeleteSetting.execute({
|
||||
uuid,
|
||||
userUuid,
|
||||
settingName: SettingName.MfaSecret,
|
||||
settingName: SettingName.NAMES.MfaSecret,
|
||||
timestamp: updatedAt,
|
||||
softDelete: true,
|
||||
})
|
||||
@@ -115,7 +115,7 @@ export class AdminController extends BaseHttpController {
|
||||
|
||||
const result = await this.doDeleteSetting.execute({
|
||||
userUuid,
|
||||
settingName: SettingName.EmailBackupFrequency,
|
||||
settingName: SettingName.NAMES.EmailBackupFrequency,
|
||||
})
|
||||
|
||||
if (result.success) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
||||
import { Request, Response } from 'express'
|
||||
import { inject } from 'inversify'
|
||||
import {
|
||||
@@ -21,7 +20,7 @@ export class SubscriptionSettingsController extends BaseHttpController {
|
||||
async getSubscriptionSetting(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.doGetSubscriptionSetting.execute({
|
||||
userUuid: response.locals.user.uuid,
|
||||
subscriptionSettingName: request.params.subscriptionSettingName as SubscriptionSettingName,
|
||||
subscriptionSettingName: request.params.subscriptionSettingName,
|
||||
})
|
||||
|
||||
if (result.success) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CrossServiceTokenData, TokenEncoderInterface } from '@standardnotes/security'
|
||||
import { ErrorTag, RoleName } from '@standardnotes/common'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { Request, Response } from 'express'
|
||||
import { inject } from 'inversify'
|
||||
import {
|
||||
@@ -77,7 +77,7 @@ export class SubscriptionTokensController extends BaseHttpController {
|
||||
const user = authenticateTokenResponse.user as User
|
||||
let extensionKey = undefined
|
||||
const extensionKeySetting = await this.settingService.findSettingWithDecryptedValue({
|
||||
settingName: SettingName.ExtensionKey,
|
||||
settingName: SettingName.NAMES.ExtensionKey,
|
||||
userUuid: user.uuid,
|
||||
})
|
||||
if (extensionKeySetting !== null) {
|
||||
|
||||
@@ -20,6 +20,5 @@ export const html = (email: string, device: string, browser: string, timeAndDate
|
||||
<br />
|
||||
SN
|
||||
</p>
|
||||
<a href="https://app.standardnotes.com/?settings=account">Mute these emails</a>
|
||||
</div>
|
||||
`
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import { EmailLevel } from '@standardnotes/domain-core'
|
||||
import { EmailSubscriptionUnsubscribedEvent } from '@standardnotes/domain-events'
|
||||
|
||||
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
||||
import { User } from '../User/User'
|
||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||
import { EmailSubscriptionUnsubscribedEventHandler } from './EmailSubscriptionUnsubscribedEventHandler'
|
||||
|
||||
describe('EmailSubscriptionUnsubscribedEventHandler', () => {
|
||||
let userRepository: UserRepositoryInterface
|
||||
let settingsService: SettingServiceInterface
|
||||
let event: EmailSubscriptionUnsubscribedEvent
|
||||
|
||||
const createHandler = () => new EmailSubscriptionUnsubscribedEventHandler(userRepository, settingsService)
|
||||
|
||||
beforeEach(() => {
|
||||
userRepository = {} as jest.Mocked<UserRepositoryInterface>
|
||||
userRepository.findOneByEmail = jest.fn().mockReturnValue({} as jest.Mocked<User>)
|
||||
|
||||
settingsService = {} as jest.Mocked<SettingServiceInterface>
|
||||
settingsService.createOrReplace = jest.fn()
|
||||
|
||||
event = {
|
||||
payload: {
|
||||
userEmail: 'test@test.te',
|
||||
level: EmailLevel.LEVELS.Marketing,
|
||||
},
|
||||
} as jest.Mocked<EmailSubscriptionUnsubscribedEvent>
|
||||
})
|
||||
|
||||
it('should not do anything if user is not found', async () => {
|
||||
userRepository.findOneByEmail = jest.fn().mockReturnValue(null)
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(settingsService.createOrReplace).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should update user marketing email settings', async () => {
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(settingsService.createOrReplace).toHaveBeenCalledWith({
|
||||
user: {},
|
||||
props: {
|
||||
name: 'MUTE_MARKETING_EMAILS',
|
||||
unencryptedValue: 'muted',
|
||||
sensitive: false,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('should update user sign in email settings', async () => {
|
||||
event.payload.level = EmailLevel.LEVELS.SignIn
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(settingsService.createOrReplace).toHaveBeenCalledWith({
|
||||
user: {},
|
||||
props: {
|
||||
name: 'MUTE_SIGN_IN_EMAILS',
|
||||
unencryptedValue: 'muted',
|
||||
sensitive: false,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('should update user email backup email settings', async () => {
|
||||
event.payload.level = EmailLevel.LEVELS.FailedEmailBackup
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(settingsService.createOrReplace).toHaveBeenCalledWith({
|
||||
user: {},
|
||||
props: {
|
||||
name: 'MUTE_FAILED_BACKUPS_EMAILS',
|
||||
unencryptedValue: 'muted',
|
||||
sensitive: false,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('should update user email backup email settings', async () => {
|
||||
event.payload.level = EmailLevel.LEVELS.FailedCloudBackup
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(settingsService.createOrReplace).toHaveBeenCalledWith({
|
||||
user: {},
|
||||
props: {
|
||||
name: 'MUTE_FAILED_CLOUD_BACKUPS_EMAILS',
|
||||
unencryptedValue: 'muted',
|
||||
sensitive: false,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('should throw error for unrecognized level', async () => {
|
||||
event.payload.level = 'foobar'
|
||||
|
||||
let caughtError = null
|
||||
try {
|
||||
await createHandler().handle(event)
|
||||
} catch (error) {
|
||||
caughtError = error
|
||||
}
|
||||
|
||||
expect(caughtError).not.toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,41 @@
|
||||
import { EmailLevel } from '@standardnotes/domain-core'
|
||||
import { DomainEventHandlerInterface, EmailSubscriptionUnsubscribedEvent } from '@standardnotes/domain-events'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
|
||||
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||
|
||||
export class EmailSubscriptionUnsubscribedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(private userRepository: UserRepositoryInterface, private settingsService: SettingServiceInterface) {}
|
||||
|
||||
async handle(event: EmailSubscriptionUnsubscribedEvent): Promise<void> {
|
||||
const user = await this.userRepository.findOneByEmail(event.payload.userEmail)
|
||||
if (user === null) {
|
||||
return
|
||||
}
|
||||
|
||||
await this.settingsService.createOrReplace({
|
||||
user,
|
||||
props: {
|
||||
name: this.getSettingNameFromLevel(event.payload.level),
|
||||
unencryptedValue: 'muted',
|
||||
sensitive: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
private getSettingNameFromLevel(level: string): string {
|
||||
switch (level) {
|
||||
case EmailLevel.LEVELS.FailedCloudBackup:
|
||||
return SettingName.MuteFailedCloudBackupsEmails
|
||||
case EmailLevel.LEVELS.FailedEmailBackup:
|
||||
return SettingName.MuteFailedBackupsEmails
|
||||
case EmailLevel.LEVELS.Marketing:
|
||||
return SettingName.MuteMarketingEmails
|
||||
case EmailLevel.LEVELS.SignIn:
|
||||
return SettingName.MuteSignInEmails
|
||||
default:
|
||||
throw new Error(`Unknown level: ${level}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DomainEventHandlerInterface, ExtensionKeyGrantedEvent } from '@standardnotes/domain-events'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { OfflineFeaturesTokenData } from '@standardnotes/security'
|
||||
import { ContentDecoderInterface } from '@standardnotes/common'
|
||||
import { inject, injectable } from 'inversify'
|
||||
@@ -54,7 +54,7 @@ export class ExtensionKeyGrantedEventHandler implements DomainEventHandlerInterf
|
||||
await this.settingService.createOrReplace({
|
||||
user,
|
||||
props: {
|
||||
name: SettingName.ExtensionKey,
|
||||
name: SettingName.NAMES.ExtensionKey,
|
||||
unencryptedValue: event.payload.extensionKey,
|
||||
serverEncryptionVersion: EncryptionVersion.Default,
|
||||
sensitive: true,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DomainEventHandlerInterface, FileRemovedEvent } from '@standardnotes/domain-events'
|
||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
@@ -38,7 +38,7 @@ export class FileRemovedEventHandler implements DomainEventHandlerInterface {
|
||||
const bytesUsedSetting = await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({
|
||||
userUuid: user.uuid,
|
||||
userSubscriptionUuid: subscription.uuid,
|
||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesUsed,
|
||||
subscriptionSettingName: SettingName.NAMES.FileUploadBytesUsed,
|
||||
})
|
||||
if (bytesUsedSetting === null) {
|
||||
this.logger.warn(`Could not find bytes used setting for user with uuid: ${user.uuid}`)
|
||||
@@ -51,7 +51,7 @@ export class FileRemovedEventHandler implements DomainEventHandlerInterface {
|
||||
await this.subscriptionSettingService.createOrReplace({
|
||||
userSubscription: subscription,
|
||||
props: {
|
||||
name: SubscriptionSettingName.FileUploadBytesUsed,
|
||||
name: SettingName.NAMES.FileUploadBytesUsed,
|
||||
unencryptedValue: (+bytesUsed - byteSize).toString(),
|
||||
sensitive: false,
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DomainEventHandlerInterface, FileUploadedEvent } from '@standardnotes/domain-events'
|
||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
@@ -47,7 +47,7 @@ export class FileUploadedEventHandler implements DomainEventHandlerInterface {
|
||||
const bytesUsedSetting = await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({
|
||||
userUuid: (await subscription.user).uuid,
|
||||
userSubscriptionUuid: subscription.uuid,
|
||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesUsed,
|
||||
subscriptionSettingName: SettingName.NAMES.FileUploadBytesUsed,
|
||||
})
|
||||
if (bytesUsedSetting !== null) {
|
||||
bytesUsed = bytesUsedSetting.value as string
|
||||
@@ -56,7 +56,7 @@ export class FileUploadedEventHandler implements DomainEventHandlerInterface {
|
||||
await this.subscriptionSettingService.createOrReplace({
|
||||
userSubscription: subscription,
|
||||
props: {
|
||||
name: SubscriptionSettingName.FileUploadBytesUsed,
|
||||
name: SettingName.NAMES.FileUploadBytesUsed,
|
||||
unencryptedValue: (+bytesUsed + byteSize).toString(),
|
||||
sensitive: false,
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DomainEventHandlerInterface, ListedAccountCreatedEvent } from '@standardnotes/domain-events'
|
||||
import { ListedAuthorSecretsData, SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
@@ -25,14 +25,14 @@ export class ListedAccountCreatedEventHandler implements DomainEventHandlerInter
|
||||
|
||||
const newSecret = { authorId: event.payload.userId, secret: event.payload.secret, hostUrl: event.payload.hostUrl }
|
||||
|
||||
let authSecrets: ListedAuthorSecretsData = [newSecret]
|
||||
let authSecrets = [newSecret]
|
||||
|
||||
const listedAuthorSecretsSetting = await this.settingService.findSettingWithDecryptedValue({
|
||||
settingName: SettingName.ListedAuthorSecrets,
|
||||
settingName: SettingName.NAMES.ListedAuthorSecrets,
|
||||
userUuid: user.uuid,
|
||||
})
|
||||
if (listedAuthorSecretsSetting !== null) {
|
||||
const existingSecrets: ListedAuthorSecretsData = JSON.parse(listedAuthorSecretsSetting.value as string)
|
||||
const existingSecrets = JSON.parse(listedAuthorSecretsSetting.value as string)
|
||||
existingSecrets.push(newSecret)
|
||||
authSecrets = existingSecrets
|
||||
}
|
||||
@@ -40,7 +40,7 @@ export class ListedAccountCreatedEventHandler implements DomainEventHandlerInter
|
||||
await this.settingService.createOrReplace({
|
||||
user,
|
||||
props: {
|
||||
name: SettingName.ListedAuthorSecrets,
|
||||
name: SettingName.NAMES.ListedAuthorSecrets,
|
||||
unencryptedValue: JSON.stringify(authSecrets),
|
||||
sensitive: false,
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DomainEventHandlerInterface, ListedAccountDeletedEvent } from '@standardnotes/domain-events'
|
||||
import { ListedAuthorSecretsData, SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
@@ -24,7 +24,7 @@ export class ListedAccountDeletedEventHandler implements DomainEventHandlerInter
|
||||
}
|
||||
|
||||
const listedAuthorSecretsSetting = await this.settingService.findSettingWithDecryptedValue({
|
||||
settingName: SettingName.ListedAuthorSecrets,
|
||||
settingName: SettingName.NAMES.ListedAuthorSecrets,
|
||||
userUuid: user.uuid,
|
||||
})
|
||||
if (listedAuthorSecretsSetting === null) {
|
||||
@@ -33,9 +33,9 @@ export class ListedAccountDeletedEventHandler implements DomainEventHandlerInter
|
||||
return
|
||||
}
|
||||
|
||||
const existingSecrets: ListedAuthorSecretsData = JSON.parse(listedAuthorSecretsSetting.value as string)
|
||||
const existingSecrets = JSON.parse(listedAuthorSecretsSetting.value as string)
|
||||
const filteredSecrets = existingSecrets.filter(
|
||||
(secret) =>
|
||||
(secret: Record<string, unknown>) =>
|
||||
secret.authorId !== event.payload.userId ||
|
||||
(secret.authorId === event.payload.userId && secret.hostUrl !== event.payload.hostUrl),
|
||||
)
|
||||
@@ -43,7 +43,7 @@ export class ListedAccountDeletedEventHandler implements DomainEventHandlerInter
|
||||
await this.settingService.createOrReplace({
|
||||
user,
|
||||
props: {
|
||||
name: SettingName.ListedAuthorSecrets,
|
||||
name: SettingName.NAMES.ListedAuthorSecrets,
|
||||
unencryptedValue: JSON.stringify(filteredSecrets),
|
||||
sensitive: false,
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||
import { UserSubscription } from '../Subscription/UserSubscription'
|
||||
import { UserSubscriptionRepositoryInterface } from '../Subscription/UserSubscriptionRepositoryInterface'
|
||||
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
||||
import { UserSubscriptionType } from '../Subscription/UserSubscriptionType'
|
||||
import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface'
|
||||
@@ -48,7 +48,7 @@ export class SubscriptionReassignedEventHandler implements DomainEventHandlerInt
|
||||
await this.settingService.createOrReplace({
|
||||
user,
|
||||
props: {
|
||||
name: SettingName.ExtensionKey,
|
||||
name: SettingName.NAMES.ExtensionKey,
|
||||
unencryptedValue: event.payload.extensionKey,
|
||||
serverEncryptionVersion: EncryptionVersion.Default,
|
||||
sensitive: true,
|
||||
|
||||
@@ -15,7 +15,7 @@ import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
||||
import { OfflineSettingServiceInterface } from '../Setting/OfflineSettingServiceInterface'
|
||||
import { ContentDecoderInterface } from '@standardnotes/common'
|
||||
import { OfflineSettingName } from '../Setting/OfflineSettingName'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
||||
import { UserSubscriptionType } from '../Subscription/UserSubscriptionType'
|
||||
import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface'
|
||||
@@ -95,7 +95,7 @@ export class SubscriptionSyncRequestedEventHandler implements DomainEventHandler
|
||||
await this.settingService.createOrReplace({
|
||||
user,
|
||||
props: {
|
||||
name: SettingName.ExtensionKey,
|
||||
name: SettingName.NAMES.ExtensionKey,
|
||||
unencryptedValue: event.payload.extensionKey,
|
||||
serverEncryptionVersion: EncryptionVersion.Default,
|
||||
sensitive: true,
|
||||
|
||||
@@ -12,7 +12,6 @@ import { EphemeralSession } from './EphemeralSession'
|
||||
import { RevokedSessionRepositoryInterface } from './RevokedSessionRepositoryInterface'
|
||||
import { RevokedSession } from './RevokedSession'
|
||||
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
||||
import { LogSessionUserAgentOption } from '@standardnotes/settings'
|
||||
import { Setting } from '../Setting/Setting'
|
||||
import { CryptoNode } from '@standardnotes/sncrypto-node'
|
||||
|
||||
@@ -171,7 +170,7 @@ describe('SessionService', () => {
|
||||
user.uuid = '123'
|
||||
|
||||
settingService.findSettingWithDecryptedValue = jest.fn().mockReturnValue({
|
||||
value: LogSessionUserAgentOption.Disabled,
|
||||
value: 'disabled',
|
||||
} as jest.Mocked<Setting>)
|
||||
|
||||
const sessionPayload = await createService().createNewSessionForUser({
|
||||
|
||||
@@ -16,7 +16,7 @@ import { EphemeralSession } from './EphemeralSession'
|
||||
import { RevokedSession } from './RevokedSession'
|
||||
import { RevokedSessionRepositoryInterface } from './RevokedSessionRepositoryInterface'
|
||||
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
||||
import { LogSessionUserAgentOption, SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { SessionBody } from '@standardnotes/responses'
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
import { CryptoNode } from '@standardnotes/sncrypto-node'
|
||||
@@ -291,7 +291,7 @@ export class SessionService implements SessionServiceInterface {
|
||||
|
||||
private async isLoggingUserAgentEnabledOnSessions(user: User): Promise<boolean> {
|
||||
const loggingSetting = await this.settingService.findSettingWithDecryptedValue({
|
||||
settingName: SettingName.LogSessionUserAgent,
|
||||
settingName: SettingName.NAMES.LogSessionUserAgent,
|
||||
userUuid: user.uuid,
|
||||
})
|
||||
|
||||
@@ -299,6 +299,6 @@ export class SessionService implements SessionServiceInterface {
|
||||
return true
|
||||
}
|
||||
|
||||
return loggingSetting.value === LogSessionUserAgentOption.Enabled
|
||||
return loggingSetting.value === 'enabled'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
|
||||
export type FindSettingDTO = {
|
||||
userUuid: string
|
||||
settingName: SettingName
|
||||
settingName: string
|
||||
settingUuid?: Uuid
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
||||
|
||||
export type FindSubscriptionSettingDTO = {
|
||||
userUuid: Uuid
|
||||
userSubscriptionUuid: Uuid
|
||||
subscriptionSettingName: SubscriptionSettingName
|
||||
subscriptionSettingName: string
|
||||
settingUuid?: Uuid
|
||||
}
|
||||
|
||||
@@ -5,13 +5,8 @@ import {
|
||||
MuteEmailsSettingChangedEvent,
|
||||
UserDisabledSessionUserAgentLoggingEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import {
|
||||
EmailBackupFrequency,
|
||||
LogSessionUserAgentOption,
|
||||
MuteMarketingEmailsOption,
|
||||
OneDriveBackupFrequency,
|
||||
SettingName,
|
||||
} from '@standardnotes/settings'
|
||||
import { MuteMarketingEmailsOption } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import 'reflect-metadata'
|
||||
import { Logger } from 'winston'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
@@ -71,11 +66,11 @@ describe('SettingInterpreter', () => {
|
||||
|
||||
it('should trigger session cleanup if user is disabling session user agent logging', async () => {
|
||||
const setting = {
|
||||
name: SettingName.LogSessionUserAgent,
|
||||
value: LogSessionUserAgentOption.Disabled,
|
||||
name: SettingName.NAMES.LogSessionUserAgent,
|
||||
value: 'disabled',
|
||||
} as jest.Mocked<Setting>
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, LogSessionUserAgentOption.Disabled)
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, 'disabled')
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createUserDisabledSessionUserAgentLoggingEvent).toHaveBeenCalledWith({
|
||||
@@ -86,11 +81,11 @@ describe('SettingInterpreter', () => {
|
||||
|
||||
it('should trigger backup if email backup setting is created - emails not muted', async () => {
|
||||
const setting = {
|
||||
name: SettingName.EmailBackupFrequency,
|
||||
value: EmailBackupFrequency.Daily,
|
||||
name: SettingName.NAMES.EmailBackupFrequency,
|
||||
value: 'daily',
|
||||
} as jest.Mocked<Setting>
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, EmailBackupFrequency.Daily)
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, 'daily')
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createEmailBackupRequestedEvent).toHaveBeenCalledWith('4-5-6', '', false)
|
||||
@@ -98,16 +93,16 @@ describe('SettingInterpreter', () => {
|
||||
|
||||
it('should trigger backup if email backup setting is created - emails muted', async () => {
|
||||
const setting = {
|
||||
name: SettingName.EmailBackupFrequency,
|
||||
value: EmailBackupFrequency.Daily,
|
||||
name: SettingName.NAMES.EmailBackupFrequency,
|
||||
value: 'daily',
|
||||
} as jest.Mocked<Setting>
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue({
|
||||
name: SettingName.MuteFailedBackupsEmails,
|
||||
name: SettingName.NAMES.MuteFailedBackupsEmails,
|
||||
uuid: '6-7-8',
|
||||
value: 'muted',
|
||||
} as jest.Mocked<Setting>)
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, EmailBackupFrequency.Daily)
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, 'daily')
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createEmailBackupRequestedEvent).toHaveBeenCalledWith('4-5-6', '6-7-8', true)
|
||||
@@ -115,12 +110,12 @@ describe('SettingInterpreter', () => {
|
||||
|
||||
it('should not trigger backup if email backup setting is disabled', async () => {
|
||||
const setting = {
|
||||
name: SettingName.EmailBackupFrequency,
|
||||
value: EmailBackupFrequency.Disabled,
|
||||
name: SettingName.NAMES.EmailBackupFrequency,
|
||||
value: 'disabled',
|
||||
} as jest.Mocked<Setting>
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, EmailBackupFrequency.Disabled)
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, 'disabled')
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
expect(domainEventFactory.createEmailBackupRequestedEvent).not.toHaveBeenCalled()
|
||||
@@ -128,7 +123,7 @@ describe('SettingInterpreter', () => {
|
||||
|
||||
it('should trigger cloud backup if dropbox backup setting is created', async () => {
|
||||
const setting = {
|
||||
name: SettingName.DropboxBackupToken,
|
||||
name: SettingName.NAMES.DropboxBackupToken,
|
||||
value: 'test-token',
|
||||
} as jest.Mocked<Setting>
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null)
|
||||
@@ -147,11 +142,11 @@ describe('SettingInterpreter', () => {
|
||||
|
||||
it('should trigger cloud backup if dropbox backup setting is created - muted emails', async () => {
|
||||
const setting = {
|
||||
name: SettingName.DropboxBackupToken,
|
||||
name: SettingName.NAMES.DropboxBackupToken,
|
||||
value: 'test-token',
|
||||
} as jest.Mocked<Setting>
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue({
|
||||
name: SettingName.MuteFailedCloudBackupsEmails,
|
||||
name: SettingName.NAMES.MuteFailedCloudBackupsEmails,
|
||||
uuid: '6-7-8',
|
||||
value: 'muted',
|
||||
} as jest.Mocked<Setting>)
|
||||
@@ -170,7 +165,7 @@ describe('SettingInterpreter', () => {
|
||||
|
||||
it('should trigger cloud backup if google drive backup setting is created', async () => {
|
||||
const setting = {
|
||||
name: SettingName.GoogleDriveBackupToken,
|
||||
name: SettingName.NAMES.GoogleDriveBackupToken,
|
||||
value: 'test-token',
|
||||
} as jest.Mocked<Setting>
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null)
|
||||
@@ -189,7 +184,7 @@ describe('SettingInterpreter', () => {
|
||||
|
||||
it('should trigger cloud backup if one drive backup setting is created', async () => {
|
||||
const setting = {
|
||||
name: SettingName.OneDriveBackupToken,
|
||||
name: SettingName.NAMES.OneDriveBackupToken,
|
||||
value: 'test-token',
|
||||
} as jest.Mocked<Setting>
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null)
|
||||
@@ -225,13 +220,13 @@ describe('SettingInterpreter', () => {
|
||||
|
||||
it('should trigger cloud backup if backup frequency setting is updated and a backup token setting is present', async () => {
|
||||
settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValueOnce({
|
||||
name: SettingName.OneDriveBackupToken,
|
||||
name: SettingName.NAMES.OneDriveBackupToken,
|
||||
serverEncryptionVersion: 1,
|
||||
value: 'encrypted-backup-token',
|
||||
sensitive: true,
|
||||
} as jest.Mocked<Setting>)
|
||||
const setting = {
|
||||
name: SettingName.OneDriveBackupFrequency,
|
||||
name: SettingName.NAMES.OneDriveBackupFrequency,
|
||||
serverEncryptionVersion: 0,
|
||||
value: 'daily',
|
||||
sensitive: false,
|
||||
@@ -251,19 +246,19 @@ describe('SettingInterpreter', () => {
|
||||
|
||||
it('should not trigger cloud backup if backup frequency setting is updated as disabled', async () => {
|
||||
settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValueOnce({
|
||||
name: SettingName.OneDriveBackupToken,
|
||||
name: SettingName.NAMES.OneDriveBackupToken,
|
||||
serverEncryptionVersion: 1,
|
||||
value: 'encrypted-backup-token',
|
||||
sensitive: true,
|
||||
} as jest.Mocked<Setting>)
|
||||
const setting = {
|
||||
name: SettingName.OneDriveBackupFrequency,
|
||||
name: SettingName.NAMES.OneDriveBackupFrequency,
|
||||
serverEncryptionVersion: 0,
|
||||
value: OneDriveBackupFrequency.Disabled,
|
||||
value: 'disabled',
|
||||
sensitive: false,
|
||||
} as jest.Mocked<Setting>
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, OneDriveBackupFrequency.Disabled)
|
||||
await createInterpreter().interpretSettingUpdated(setting, user, 'disabled')
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
expect(domainEventFactory.createCloudBackupRequestedEvent).not.toHaveBeenCalled()
|
||||
@@ -272,7 +267,7 @@ describe('SettingInterpreter', () => {
|
||||
it('should not trigger cloud backup if backup frequency setting is updated and a backup token setting is not present', async () => {
|
||||
settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValueOnce(null)
|
||||
const setting = {
|
||||
name: SettingName.OneDriveBackupFrequency,
|
||||
name: SettingName.NAMES.OneDriveBackupFrequency,
|
||||
serverEncryptionVersion: 0,
|
||||
value: 'daily',
|
||||
sensitive: false,
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { EmailLevel } from '@standardnotes/domain-core'
|
||||
import {
|
||||
DropboxBackupFrequency,
|
||||
EmailBackupFrequency,
|
||||
GoogleDriveBackupFrequency,
|
||||
LogSessionUserAgentOption,
|
||||
MuteFailedBackupsEmailsOption,
|
||||
MuteFailedCloudBackupsEmailsOption,
|
||||
OneDriveBackupFrequency,
|
||||
SettingName,
|
||||
} from '@standardnotes/settings'
|
||||
import { EmailLevel, SettingName } from '@standardnotes/domain-core'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
@@ -23,22 +13,18 @@ import { SettingRepositoryInterface } from './SettingRepositoryInterface'
|
||||
@injectable()
|
||||
export class SettingInterpreter implements SettingInterpreterInterface {
|
||||
private readonly cloudBackupTokenSettings = [
|
||||
SettingName.DropboxBackupToken,
|
||||
SettingName.GoogleDriveBackupToken,
|
||||
SettingName.OneDriveBackupToken,
|
||||
SettingName.NAMES.DropboxBackupToken,
|
||||
SettingName.NAMES.GoogleDriveBackupToken,
|
||||
SettingName.NAMES.OneDriveBackupToken,
|
||||
]
|
||||
|
||||
private readonly cloudBackupFrequencySettings = [
|
||||
SettingName.DropboxBackupFrequency,
|
||||
SettingName.GoogleDriveBackupFrequency,
|
||||
SettingName.OneDriveBackupFrequency,
|
||||
SettingName.NAMES.DropboxBackupFrequency,
|
||||
SettingName.NAMES.GoogleDriveBackupFrequency,
|
||||
SettingName.NAMES.OneDriveBackupFrequency,
|
||||
]
|
||||
|
||||
private readonly cloudBackupFrequencyDisabledValues = [
|
||||
DropboxBackupFrequency.Disabled,
|
||||
GoogleDriveBackupFrequency.Disabled,
|
||||
OneDriveBackupFrequency.Disabled,
|
||||
]
|
||||
private readonly cloudBackupFrequencyDisabledValues = ['disabled']
|
||||
|
||||
private readonly emailSettingToSubscriptionRejectionLevelMap: Map<SettingName, string> = new Map([
|
||||
[SettingName.MuteFailedBackupsEmails, EmailLevel.LEVELS.FailedEmailBackup],
|
||||
@@ -77,11 +63,11 @@ export class SettingInterpreter implements SettingInterpreterInterface {
|
||||
let userHasEmailsMuted = false
|
||||
let muteEmailsSettingUuid = ''
|
||||
const muteFailedEmailsBackupSetting = await this.settingRepository.findOneByNameAndUserUuid(
|
||||
SettingName.MuteFailedBackupsEmails,
|
||||
SettingName.NAMES.MuteFailedBackupsEmails,
|
||||
userUuid,
|
||||
)
|
||||
if (muteFailedEmailsBackupSetting !== null) {
|
||||
userHasEmailsMuted = muteFailedEmailsBackupSetting.value === MuteFailedBackupsEmailsOption.Muted
|
||||
userHasEmailsMuted = muteFailedEmailsBackupSetting.value === 'muted'
|
||||
muteEmailsSettingUuid = muteFailedEmailsBackupSetting.uuid
|
||||
}
|
||||
|
||||
@@ -100,21 +86,19 @@ export class SettingInterpreter implements SettingInterpreterInterface {
|
||||
}
|
||||
|
||||
private isEnablingEmailBackupSetting(setting: Setting): boolean {
|
||||
return setting.name === SettingName.EmailBackupFrequency && setting.value !== EmailBackupFrequency.Disabled
|
||||
return setting.name === SettingName.NAMES.EmailBackupFrequency && setting.value !== 'disabled'
|
||||
}
|
||||
|
||||
private isEnablingCloudBackupSetting(setting: Setting): boolean {
|
||||
return (
|
||||
(this.cloudBackupFrequencySettings.includes(setting.name as SettingName) ||
|
||||
this.cloudBackupTokenSettings.includes(setting.name as SettingName)) &&
|
||||
!this.cloudBackupFrequencyDisabledValues.includes(
|
||||
setting.value as DropboxBackupFrequency | OneDriveBackupFrequency | GoogleDriveBackupFrequency,
|
||||
)
|
||||
(this.cloudBackupFrequencySettings.includes(setting.name) ||
|
||||
this.cloudBackupTokenSettings.includes(setting.name)) &&
|
||||
!this.cloudBackupFrequencyDisabledValues.includes(setting.value as string)
|
||||
)
|
||||
}
|
||||
|
||||
private isDisablingSessionUserAgentLogging(setting: Setting): boolean {
|
||||
return SettingName.LogSessionUserAgent === setting.name && LogSessionUserAgentOption.Disabled === setting.value
|
||||
return SettingName.NAMES.LogSessionUserAgent === setting.name && 'disabled' === setting.value
|
||||
}
|
||||
|
||||
private async triggerEmailSubscriptionChange(
|
||||
@@ -144,29 +128,26 @@ export class SettingInterpreter implements SettingInterpreterInterface {
|
||||
let cloudProvider
|
||||
let tokenSettingName
|
||||
switch (setting.name) {
|
||||
case SettingName.DropboxBackupToken:
|
||||
case SettingName.DropboxBackupFrequency:
|
||||
case SettingName.NAMES.DropboxBackupToken:
|
||||
case SettingName.NAMES.DropboxBackupFrequency:
|
||||
cloudProvider = 'DROPBOX'
|
||||
tokenSettingName = SettingName.DropboxBackupToken
|
||||
tokenSettingName = SettingName.NAMES.DropboxBackupToken
|
||||
break
|
||||
case SettingName.GoogleDriveBackupToken:
|
||||
case SettingName.GoogleDriveBackupFrequency:
|
||||
case SettingName.NAMES.GoogleDriveBackupToken:
|
||||
case SettingName.NAMES.GoogleDriveBackupFrequency:
|
||||
cloudProvider = 'GOOGLE_DRIVE'
|
||||
tokenSettingName = SettingName.GoogleDriveBackupToken
|
||||
tokenSettingName = SettingName.NAMES.GoogleDriveBackupToken
|
||||
break
|
||||
case SettingName.OneDriveBackupToken:
|
||||
case SettingName.OneDriveBackupFrequency:
|
||||
case SettingName.NAMES.OneDriveBackupToken:
|
||||
case SettingName.NAMES.OneDriveBackupFrequency:
|
||||
cloudProvider = 'ONE_DRIVE'
|
||||
tokenSettingName = SettingName.OneDriveBackupToken
|
||||
tokenSettingName = SettingName.NAMES.OneDriveBackupToken
|
||||
break
|
||||
}
|
||||
|
||||
let backupToken = null
|
||||
if (this.cloudBackupFrequencySettings.includes(setting.name as SettingName)) {
|
||||
const tokenSetting = await this.settingRepository.findLastByNameAndUserUuid(
|
||||
tokenSettingName as SettingName,
|
||||
userUuid,
|
||||
)
|
||||
if (this.cloudBackupFrequencySettings.includes(setting.name)) {
|
||||
const tokenSetting = await this.settingRepository.findLastByNameAndUserUuid(tokenSettingName as string, userUuid)
|
||||
if (tokenSetting !== null) {
|
||||
backupToken = await this.settingDecrypter.decryptSettingValue(tokenSetting, userUuid)
|
||||
}
|
||||
@@ -183,11 +164,11 @@ export class SettingInterpreter implements SettingInterpreterInterface {
|
||||
let userHasEmailsMuted = false
|
||||
let muteEmailsSettingUuid = ''
|
||||
const muteFailedCloudBackupSetting = await this.settingRepository.findOneByNameAndUserUuid(
|
||||
SettingName.MuteFailedCloudBackupsEmails,
|
||||
SettingName.NAMES.MuteFailedCloudBackupsEmails,
|
||||
userUuid,
|
||||
)
|
||||
if (muteFailedCloudBackupSetting !== null) {
|
||||
userHasEmailsMuted = muteFailedCloudBackupSetting.value === MuteFailedCloudBackupsEmailsOption.Muted
|
||||
userHasEmailsMuted = muteFailedCloudBackupSetting.value === 'muted'
|
||||
muteEmailsSettingUuid = muteFailedCloudBackupSetting.uuid
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { ReadStream } from 'fs'
|
||||
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { DeleteSettingDto } from '../UseCase/DeleteSetting/DeleteSettingDto'
|
||||
import { Setting } from './Setting'
|
||||
|
||||
export interface SettingRepositoryInterface {
|
||||
findOneByUuid(uuid: string): Promise<Setting | null>
|
||||
findOneByUuidAndNames(uuid: string, names: SettingName[]): Promise<Setting | null>
|
||||
findOneByUuidAndNames(uuid: string, names: string[]): Promise<Setting | null>
|
||||
findOneByNameAndUserUuid(name: string, userUuid: string): Promise<Setting | null>
|
||||
findLastByNameAndUserUuid(name: string, userUuid: string): Promise<Setting | null>
|
||||
findAllByUserUuid(userUuid: string): Promise<Setting[]>
|
||||
streamAllByNameAndValue(name: SettingName, value: string): Promise<ReadStream>
|
||||
streamAllByNameAndValue(name: string, value: string): Promise<ReadStream>
|
||||
deleteByUserUuid(dto: DeleteSettingDto): Promise<void>
|
||||
save(setting: Setting): Promise<Setting>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { LogSessionUserAgentOption, MuteSignInEmailsOption, SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { Logger } from 'winston'
|
||||
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
||||
import { User } from '../User/User'
|
||||
@@ -54,9 +54,9 @@ describe('SettingService', () => {
|
||||
settingsAssociationService.getDefaultSettingsAndValuesForNewUser = jest.fn().mockReturnValue(
|
||||
new Map([
|
||||
[
|
||||
SettingName.MuteSignInEmails,
|
||||
SettingName.NAMES.MuteSignInEmails,
|
||||
{
|
||||
value: MuteSignInEmailsOption.NotMuted,
|
||||
value: 'not_muted',
|
||||
sensitive: 0,
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
},
|
||||
@@ -67,11 +67,11 @@ describe('SettingService', () => {
|
||||
settingsAssociationService.getDefaultSettingsAndValuesForNewVaultAccount = jest.fn().mockReturnValue(
|
||||
new Map([
|
||||
[
|
||||
SettingName.LogSessionUserAgent,
|
||||
SettingName.NAMES.LogSessionUserAgent,
|
||||
{
|
||||
sensitive: false,
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
value: LogSessionUserAgentOption.Disabled,
|
||||
value: 'disabled',
|
||||
},
|
||||
],
|
||||
]),
|
||||
@@ -173,9 +173,7 @@ describe('SettingService', () => {
|
||||
|
||||
settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValue(setting)
|
||||
|
||||
expect(
|
||||
await createService().findSettingWithDecryptedValue({ userUuid: '1-2-3', settingName: 'test' as SettingName }),
|
||||
).toEqual({
|
||||
expect(await createService().findSettingWithDecryptedValue({ userUuid: '1-2-3', settingName: 'test' })).toEqual({
|
||||
serverEncryptionVersion: 1,
|
||||
value: 'decrypted',
|
||||
})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
@@ -74,7 +73,7 @@ export class SettingService implements SettingServiceInterface {
|
||||
|
||||
const existing = await this.findSettingWithDecryptedValue({
|
||||
userUuid: user.uuid,
|
||||
settingName: props.name as SettingName,
|
||||
settingName: props.name,
|
||||
settingUuid: props.uuid,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { PermissionName } from '@standardnotes/features'
|
||||
|
||||
import { SettingsAssociationService } from './SettingsAssociationService'
|
||||
@@ -11,52 +11,54 @@ describe('SettingsAssociationService', () => {
|
||||
const createService = () => new SettingsAssociationService()
|
||||
|
||||
it('should tell if a setting is mutable by the client', () => {
|
||||
expect(createService().isSettingMutableByClient(SettingName.DropboxBackupFrequency)).toBeTruthy()
|
||||
expect(createService().isSettingMutableByClient(SettingName.NAMES.DropboxBackupFrequency)).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should tell if a setting is immutable by the client', () => {
|
||||
expect(createService().isSettingMutableByClient(SettingName.ListedAuthorSecrets)).toBeFalsy()
|
||||
expect(createService().isSettingMutableByClient(SettingName.NAMES.ListedAuthorSecrets)).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should return default encryption version for a setting which enecryption version is not strictly defined', () => {
|
||||
expect(createService().getEncryptionVersionForSetting(SettingName.MfaSecret)).toEqual(EncryptionVersion.Default)
|
||||
expect(createService().getEncryptionVersionForSetting(SettingName.NAMES.MfaSecret)).toEqual(
|
||||
EncryptionVersion.Default,
|
||||
)
|
||||
})
|
||||
|
||||
it('should return a defined encryption version for a setting which enecryption version is strictly defined', () => {
|
||||
expect(createService().getEncryptionVersionForSetting(SettingName.EmailBackupFrequency)).toEqual(
|
||||
expect(createService().getEncryptionVersionForSetting(SettingName.NAMES.EmailBackupFrequency)).toEqual(
|
||||
EncryptionVersion.Unencrypted,
|
||||
)
|
||||
})
|
||||
|
||||
it('should return default sensitivity for a setting which sensitivity is not strictly defined', () => {
|
||||
expect(createService().getSensitivityForSetting(SettingName.DropboxBackupToken)).toBeTruthy()
|
||||
expect(createService().getSensitivityForSetting(SettingName.NAMES.DropboxBackupToken)).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should return a defined sensitivity for a setting which sensitivity is strictly defined', () => {
|
||||
expect(createService().getSensitivityForSetting(SettingName.DropboxBackupFrequency)).toBeFalsy()
|
||||
expect(createService().getSensitivityForSetting(SettingName.NAMES.DropboxBackupFrequency)).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should return the default set of settings for a newly registered user', () => {
|
||||
const settings = createService().getDefaultSettingsAndValuesForNewUser()
|
||||
const flatSettings = [...(settings as Map<SettingName, SettingDescription>).keys()]
|
||||
const flatSettings = [...(settings as Map<string, SettingDescription>).keys()]
|
||||
expect(flatSettings).toEqual(['MUTE_SIGN_IN_EMAILS', 'MUTE_MARKETING_EMAILS', 'LOG_SESSION_USER_AGENT'])
|
||||
})
|
||||
|
||||
it('should return the default set of settings for a newly registered vault account', () => {
|
||||
const settings = createService().getDefaultSettingsAndValuesForNewVaultAccount()
|
||||
const flatSettings = [...(settings as Map<SettingName, SettingDescription>).keys()]
|
||||
const flatSettings = [...(settings as Map<string, SettingDescription>).keys()]
|
||||
expect(flatSettings).toEqual(['MUTE_SIGN_IN_EMAILS', 'MUTE_MARKETING_EMAILS', 'LOG_SESSION_USER_AGENT'])
|
||||
|
||||
expect(settings.get(SettingName.LogSessionUserAgent)?.value).toEqual('disabled')
|
||||
expect(settings.get(SettingName.NAMES.LogSessionUserAgent)?.value).toEqual('disabled')
|
||||
})
|
||||
|
||||
it('should return a permission name associated to a given setting', () => {
|
||||
expect(createService().getPermissionAssociatedWithSetting(SettingName.EmailBackupFrequency)).toEqual(
|
||||
expect(createService().getPermissionAssociatedWithSetting(SettingName.NAMES.EmailBackupFrequency)).toEqual(
|
||||
PermissionName.DailyEmailBackup,
|
||||
)
|
||||
})
|
||||
|
||||
it('should not return a permission name if not associated to a given setting', () => {
|
||||
expect(createService().getPermissionAssociatedWithSetting(SettingName.ExtensionKey)).toBeUndefined()
|
||||
expect(createService().getPermissionAssociatedWithSetting(SettingName.NAMES.ExtensionKey)).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import { PermissionName } from '@standardnotes/features'
|
||||
import {
|
||||
LogSessionUserAgentOption,
|
||||
MuteMarketingEmailsOption,
|
||||
MuteSignInEmailsOption,
|
||||
SettingName,
|
||||
} from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { injectable } from 'inversify'
|
||||
|
||||
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
||||
@@ -15,79 +10,79 @@ import { SettingsAssociationServiceInterface } from './SettingsAssociationServic
|
||||
@injectable()
|
||||
export class SettingsAssociationService implements SettingsAssociationServiceInterface {
|
||||
private readonly UNENCRYPTED_SETTINGS = [
|
||||
SettingName.EmailBackupFrequency,
|
||||
SettingName.MuteFailedBackupsEmails,
|
||||
SettingName.MuteFailedCloudBackupsEmails,
|
||||
SettingName.MuteSignInEmails,
|
||||
SettingName.MuteMarketingEmails,
|
||||
SettingName.DropboxBackupFrequency,
|
||||
SettingName.GoogleDriveBackupFrequency,
|
||||
SettingName.OneDriveBackupFrequency,
|
||||
SettingName.LogSessionUserAgent,
|
||||
SettingName.NAMES.EmailBackupFrequency,
|
||||
SettingName.NAMES.MuteFailedBackupsEmails,
|
||||
SettingName.NAMES.MuteFailedCloudBackupsEmails,
|
||||
SettingName.NAMES.MuteSignInEmails,
|
||||
SettingName.NAMES.MuteMarketingEmails,
|
||||
SettingName.NAMES.DropboxBackupFrequency,
|
||||
SettingName.NAMES.GoogleDriveBackupFrequency,
|
||||
SettingName.NAMES.OneDriveBackupFrequency,
|
||||
SettingName.NAMES.LogSessionUserAgent,
|
||||
]
|
||||
|
||||
private readonly UNSENSITIVE_SETTINGS = [
|
||||
SettingName.DropboxBackupFrequency,
|
||||
SettingName.GoogleDriveBackupFrequency,
|
||||
SettingName.OneDriveBackupFrequency,
|
||||
SettingName.EmailBackupFrequency,
|
||||
SettingName.MuteFailedBackupsEmails,
|
||||
SettingName.MuteFailedCloudBackupsEmails,
|
||||
SettingName.MuteSignInEmails,
|
||||
SettingName.MuteMarketingEmails,
|
||||
SettingName.ListedAuthorSecrets,
|
||||
SettingName.LogSessionUserAgent,
|
||||
SettingName.NAMES.DropboxBackupFrequency,
|
||||
SettingName.NAMES.GoogleDriveBackupFrequency,
|
||||
SettingName.NAMES.OneDriveBackupFrequency,
|
||||
SettingName.NAMES.EmailBackupFrequency,
|
||||
SettingName.NAMES.MuteFailedBackupsEmails,
|
||||
SettingName.NAMES.MuteFailedCloudBackupsEmails,
|
||||
SettingName.NAMES.MuteSignInEmails,
|
||||
SettingName.NAMES.MuteMarketingEmails,
|
||||
SettingName.NAMES.ListedAuthorSecrets,
|
||||
SettingName.NAMES.LogSessionUserAgent,
|
||||
]
|
||||
|
||||
private readonly CLIENT_IMMUTABLE_SETTINGS = [SettingName.ListedAuthorSecrets]
|
||||
private readonly CLIENT_IMMUTABLE_SETTINGS = [SettingName.NAMES.ListedAuthorSecrets]
|
||||
|
||||
private readonly permissionsAssociatedWithSettings = new Map<SettingName, PermissionName>([
|
||||
[SettingName.EmailBackupFrequency, PermissionName.DailyEmailBackup],
|
||||
private readonly permissionsAssociatedWithSettings = new Map<string, PermissionName>([
|
||||
[SettingName.NAMES.EmailBackupFrequency, PermissionName.DailyEmailBackup],
|
||||
])
|
||||
|
||||
private readonly defaultSettings = new Map<SettingName, SettingDescription>([
|
||||
private readonly defaultSettings = new Map<string, SettingDescription>([
|
||||
[
|
||||
SettingName.MuteSignInEmails,
|
||||
SettingName.NAMES.MuteSignInEmails,
|
||||
{
|
||||
sensitive: false,
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
value: MuteSignInEmailsOption.NotMuted,
|
||||
value: 'not_muted',
|
||||
replaceable: false,
|
||||
},
|
||||
],
|
||||
[
|
||||
SettingName.MuteMarketingEmails,
|
||||
SettingName.NAMES.MuteMarketingEmails,
|
||||
{
|
||||
sensitive: false,
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
value: MuteMarketingEmailsOption.NotMuted,
|
||||
value: 'not_muted',
|
||||
replaceable: false,
|
||||
},
|
||||
],
|
||||
[
|
||||
SettingName.LogSessionUserAgent,
|
||||
SettingName.NAMES.LogSessionUserAgent,
|
||||
{
|
||||
sensitive: false,
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
value: LogSessionUserAgentOption.Enabled,
|
||||
value: 'enabled',
|
||||
replaceable: false,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
private readonly vaultAccountDefaultSettingsOverwrites = new Map<SettingName, SettingDescription>([
|
||||
private readonly vaultAccountDefaultSettingsOverwrites = new Map<string, SettingDescription>([
|
||||
[
|
||||
SettingName.LogSessionUserAgent,
|
||||
SettingName.NAMES.LogSessionUserAgent,
|
||||
{
|
||||
sensitive: false,
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
value: LogSessionUserAgentOption.Disabled,
|
||||
value: 'disabled',
|
||||
replaceable: false,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
isSettingMutableByClient(settingName: SettingName): boolean {
|
||||
isSettingMutableByClient(settingName: string): boolean {
|
||||
if (this.CLIENT_IMMUTABLE_SETTINGS.includes(settingName)) {
|
||||
return false
|
||||
}
|
||||
@@ -95,7 +90,7 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt
|
||||
return true
|
||||
}
|
||||
|
||||
getSensitivityForSetting(settingName: SettingName): boolean {
|
||||
getSensitivityForSetting(settingName: string): boolean {
|
||||
if (this.UNSENSITIVE_SETTINGS.includes(settingName)) {
|
||||
return false
|
||||
}
|
||||
@@ -103,7 +98,7 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt
|
||||
return true
|
||||
}
|
||||
|
||||
getEncryptionVersionForSetting(settingName: SettingName): EncryptionVersion {
|
||||
getEncryptionVersionForSetting(settingName: string): EncryptionVersion {
|
||||
if (this.UNENCRYPTED_SETTINGS.includes(settingName)) {
|
||||
return EncryptionVersion.Unencrypted
|
||||
}
|
||||
@@ -111,7 +106,7 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt
|
||||
return EncryptionVersion.Default
|
||||
}
|
||||
|
||||
getPermissionAssociatedWithSetting(settingName: SettingName): PermissionName | undefined {
|
||||
getPermissionAssociatedWithSetting(settingName: string): PermissionName | undefined {
|
||||
if (!this.permissionsAssociatedWithSettings.has(settingName)) {
|
||||
return undefined
|
||||
}
|
||||
@@ -119,11 +114,11 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt
|
||||
return this.permissionsAssociatedWithSettings.get(settingName)
|
||||
}
|
||||
|
||||
getDefaultSettingsAndValuesForNewUser(): Map<SettingName, SettingDescription> {
|
||||
getDefaultSettingsAndValuesForNewUser(): Map<string, SettingDescription> {
|
||||
return this.defaultSettings
|
||||
}
|
||||
|
||||
getDefaultSettingsAndValuesForNewVaultAccount(): Map<SettingName, SettingDescription> {
|
||||
getDefaultSettingsAndValuesForNewVaultAccount(): Map<string, SettingDescription> {
|
||||
const defaultVaultSettings = new Map(this.defaultSettings)
|
||||
|
||||
for (const vaultAccountDefaultSettingOverwriteKey of this.vaultAccountDefaultSettingsOverwrites.keys()) {
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { PermissionName } from '@standardnotes/features'
|
||||
import { SettingName, SubscriptionSettingName } from '@standardnotes/settings'
|
||||
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
||||
import { SettingDescription } from './SettingDescription'
|
||||
|
||||
export interface SettingsAssociationServiceInterface {
|
||||
getDefaultSettingsAndValuesForNewUser(): Map<SettingName, SettingDescription>
|
||||
getDefaultSettingsAndValuesForNewVaultAccount(): Map<SettingName, SettingDescription>
|
||||
getPermissionAssociatedWithSetting(settingName: SettingName): PermissionName | undefined
|
||||
getEncryptionVersionForSetting(settingName: SettingName): EncryptionVersion
|
||||
getSensitivityForSetting(settingName: SettingName): boolean
|
||||
isSettingMutableByClient(settingName: SettingName | SubscriptionSettingName): boolean
|
||||
getDefaultSettingsAndValuesForNewUser(): Map<string, SettingDescription>
|
||||
getDefaultSettingsAndValuesForNewVaultAccount(): Map<string, SettingDescription>
|
||||
getPermissionAssociatedWithSetting(settingName: string): PermissionName | undefined
|
||||
getEncryptionVersionForSetting(settingName: string): EncryptionVersion
|
||||
getSensitivityForSetting(settingName: string): boolean
|
||||
isSettingMutableByClient(settingName: string): boolean
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { Logger } from 'winston'
|
||||
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
||||
|
||||
@@ -68,7 +68,7 @@ describe('SubscriptionSettingService', () => {
|
||||
subscriptionSettingsAssociationService.getDefaultSettingsAndValuesForSubscriptionName = jest.fn().mockReturnValue(
|
||||
new Map([
|
||||
[
|
||||
SubscriptionSettingName.FileUploadBytesUsed,
|
||||
SettingName.NAMES.FileUploadBytesUsed,
|
||||
{
|
||||
value: '0',
|
||||
sensitive: 0,
|
||||
@@ -102,7 +102,7 @@ describe('SubscriptionSettingService', () => {
|
||||
subscriptionSettingsAssociationService.getDefaultSettingsAndValuesForSubscriptionName = jest.fn().mockReturnValue(
|
||||
new Map([
|
||||
[
|
||||
SubscriptionSettingName.FileUploadBytesUsed,
|
||||
SettingName.NAMES.FileUploadBytesUsed,
|
||||
{
|
||||
value: '0',
|
||||
sensitive: 0,
|
||||
@@ -127,7 +127,7 @@ describe('SubscriptionSettingService', () => {
|
||||
subscriptionSettingsAssociationService.getDefaultSettingsAndValuesForSubscriptionName = jest.fn().mockReturnValue(
|
||||
new Map([
|
||||
[
|
||||
SubscriptionSettingName.FileUploadBytesUsed,
|
||||
SettingName.NAMES.FileUploadBytesUsed,
|
||||
{
|
||||
value: '0',
|
||||
sensitive: 0,
|
||||
@@ -152,7 +152,7 @@ describe('SubscriptionSettingService', () => {
|
||||
subscriptionSettingsAssociationService.getDefaultSettingsAndValuesForSubscriptionName = jest.fn().mockReturnValue(
|
||||
new Map([
|
||||
[
|
||||
SubscriptionSettingName.FileUploadBytesUsed,
|
||||
SettingName.NAMES.FileUploadBytesUsed,
|
||||
{
|
||||
value: '0',
|
||||
sensitive: 0,
|
||||
@@ -266,7 +266,7 @@ describe('SubscriptionSettingService', () => {
|
||||
await createService().findSubscriptionSettingWithDecryptedValue({
|
||||
userSubscriptionUuid: '2-3-4',
|
||||
userUuid: '1-2-3',
|
||||
subscriptionSettingName: 'test' as SubscriptionSettingName,
|
||||
subscriptionSettingName: 'test',
|
||||
}),
|
||||
).toEqual({
|
||||
serverEncryptionVersion: 1,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { SubscriptionName, Uuid } from '@standardnotes/common'
|
||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
@@ -98,7 +97,7 @@ export class SubscriptionSettingService implements SubscriptionSettingServiceInt
|
||||
const existing = await this.findSubscriptionSettingWithDecryptedValue({
|
||||
userUuid: (await userSubscription.user).uuid,
|
||||
userSubscriptionUuid: userSubscription.uuid,
|
||||
subscriptionSettingName: props.name as SubscriptionSettingName,
|
||||
subscriptionSettingName: props.name,
|
||||
settingUuid: props.uuid,
|
||||
})
|
||||
|
||||
@@ -128,7 +127,7 @@ export class SubscriptionSettingService implements SubscriptionSettingServiceInt
|
||||
}
|
||||
|
||||
private async findPreviousSubscriptionSetting(
|
||||
settingName: SubscriptionSettingName,
|
||||
settingName: string,
|
||||
currentUserSubscriptionUuid: Uuid,
|
||||
userUuid: Uuid,
|
||||
): Promise<SubscriptionSetting | null> {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { RoleName, SubscriptionName } from '@standardnotes/common'
|
||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
|
||||
import { PermissionName } from '@standardnotes/features'
|
||||
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
||||
@@ -50,14 +50,11 @@ describe('SubscriptionSettingsAssociationService', () => {
|
||||
|
||||
const flatSettings = [
|
||||
...(
|
||||
settings as Map<
|
||||
SubscriptionSettingName,
|
||||
{ value: string; sensitive: boolean; serverEncryptionVersion: EncryptionVersion }
|
||||
>
|
||||
settings as Map<string, { value: string; sensitive: boolean; serverEncryptionVersion: EncryptionVersion }>
|
||||
).keys(),
|
||||
]
|
||||
expect(flatSettings).toEqual(['FILE_UPLOAD_BYTES_USED', 'FILE_UPLOAD_BYTES_LIMIT'])
|
||||
expect(settings?.get(SubscriptionSettingName.FileUploadBytesLimit)).toEqual({
|
||||
expect(settings?.get(SettingName.NAMES.FileUploadBytesLimit)).toEqual({
|
||||
sensitive: false,
|
||||
serverEncryptionVersion: 0,
|
||||
value: '107374182400',
|
||||
@@ -78,14 +75,11 @@ describe('SubscriptionSettingsAssociationService', () => {
|
||||
|
||||
const flatSettings = [
|
||||
...(
|
||||
settings as Map<
|
||||
SubscriptionSettingName,
|
||||
{ value: string; sensitive: boolean; serverEncryptionVersion: EncryptionVersion }
|
||||
>
|
||||
settings as Map<string, { value: string; sensitive: boolean; serverEncryptionVersion: EncryptionVersion }>
|
||||
).keys(),
|
||||
]
|
||||
expect(flatSettings).toEqual(['FILE_UPLOAD_BYTES_USED', 'FILE_UPLOAD_BYTES_LIMIT'])
|
||||
expect(settings?.get(SubscriptionSettingName.FileUploadBytesLimit)).toEqual({
|
||||
expect(settings?.get(SettingName.NAMES.FileUploadBytesLimit)).toEqual({
|
||||
sensitive: false,
|
||||
serverEncryptionVersion: 0,
|
||||
value: '104857600',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { RoleName, SubscriptionName } from '@standardnotes/common'
|
||||
import { PermissionName } from '@standardnotes/features'
|
||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { inject, injectable } from 'inversify'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
@@ -19,15 +19,12 @@ export class SubscriptionSettingsAssociationService implements SubscriptionSetti
|
||||
@inject(TYPES.RoleRepository) private roleRepository: RoleRepositoryInterface,
|
||||
) {}
|
||||
|
||||
private readonly settingsToSubscriptionNameMap = new Map<
|
||||
SubscriptionName,
|
||||
Map<SubscriptionSettingName, SettingDescription>
|
||||
>([
|
||||
private readonly settingsToSubscriptionNameMap = new Map<SubscriptionName, Map<string, SettingDescription>>([
|
||||
[
|
||||
SubscriptionName.PlusPlan,
|
||||
new Map([
|
||||
[
|
||||
SubscriptionSettingName.FileUploadBytesUsed,
|
||||
SettingName.NAMES.FileUploadBytesUsed,
|
||||
{ sensitive: false, serverEncryptionVersion: EncryptionVersion.Unencrypted, value: '0', replaceable: false },
|
||||
],
|
||||
]),
|
||||
@@ -36,7 +33,7 @@ export class SubscriptionSettingsAssociationService implements SubscriptionSetti
|
||||
SubscriptionName.ProPlan,
|
||||
new Map([
|
||||
[
|
||||
SubscriptionSettingName.FileUploadBytesUsed,
|
||||
SettingName.NAMES.FileUploadBytesUsed,
|
||||
{ sensitive: false, serverEncryptionVersion: EncryptionVersion.Unencrypted, value: '0', replaceable: false },
|
||||
],
|
||||
]),
|
||||
@@ -45,14 +42,14 @@ export class SubscriptionSettingsAssociationService implements SubscriptionSetti
|
||||
|
||||
async getDefaultSettingsAndValuesForSubscriptionName(
|
||||
subscriptionName: SubscriptionName,
|
||||
): Promise<Map<SubscriptionSettingName, SettingDescription> | undefined> {
|
||||
): Promise<Map<string, SettingDescription> | undefined> {
|
||||
const defaultSettings = this.settingsToSubscriptionNameMap.get(subscriptionName)
|
||||
|
||||
if (defaultSettings === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
defaultSettings.set(SubscriptionSettingName.FileUploadBytesLimit, {
|
||||
defaultSettings.set(SettingName.NAMES.FileUploadBytesLimit, {
|
||||
sensitive: false,
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
value: (await this.getFileUploadLimit(subscriptionName)).toString(),
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { SubscriptionName } from '@standardnotes/common'
|
||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
||||
|
||||
import { SettingDescription } from './SettingDescription'
|
||||
|
||||
export interface SubscriptionSettingsAssociationServiceInterface {
|
||||
getDefaultSettingsAndValuesForSubscriptionName(
|
||||
subscriptionName: SubscriptionName,
|
||||
): Promise<Map<SubscriptionSettingName, SettingDescription> | undefined>
|
||||
): Promise<Map<string, SettingDescription> | undefined>
|
||||
getFileUploadLimit(subscriptionName: SubscriptionName): Promise<number>
|
||||
}
|
||||
|
||||
+1
-3
@@ -47,9 +47,7 @@ describe('CreateOfflineSubscriptionToken', () => {
|
||||
domainEventPublisher.publish = jest.fn()
|
||||
|
||||
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
|
||||
domainEventFactory.createEmailRequestedEvent = jest
|
||||
.fn()
|
||||
.mockReturnValue({} as jest.Mocked<EmailRequestedEvent>)
|
||||
domainEventFactory.createEmailRequestedEvent = jest.fn().mockReturnValue({} as jest.Mocked<EmailRequestedEvent>)
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.convertStringDateToMicroseconds = jest.fn().mockReturnValue(1)
|
||||
|
||||
@@ -3,7 +3,7 @@ import { SubscriptionName } from '@standardnotes/common'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { TokenEncoderInterface, ValetTokenData } from '@standardnotes/security'
|
||||
import { CreateValetTokenPayload, CreateValetTokenResponseData } from '@standardnotes/responses'
|
||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
|
||||
import TYPES from '../../../Bootstrap/Types'
|
||||
import { UseCaseInterface } from '../UseCaseInterface'
|
||||
@@ -56,7 +56,7 @@ export class CreateValetToken implements UseCaseInterface {
|
||||
const uploadBytesUsedSetting = await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({
|
||||
userUuid: regularSubscriptionUserUuid,
|
||||
userSubscriptionUuid: regularSubscription.uuid,
|
||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesUsed,
|
||||
subscriptionSettingName: SettingName.NAMES.FileUploadBytesUsed,
|
||||
})
|
||||
if (uploadBytesUsedSetting !== null) {
|
||||
uploadBytesUsed = +(uploadBytesUsedSetting.value as string)
|
||||
@@ -70,7 +70,7 @@ export class CreateValetToken implements UseCaseInterface {
|
||||
await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({
|
||||
userUuid: regularSubscriptionUserUuid,
|
||||
userSubscriptionUuid: regularSubscription.uuid,
|
||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesLimit,
|
||||
subscriptionSettingName: SettingName.NAMES.FileUploadBytesLimit,
|
||||
})
|
||||
if (overwriteWithUserUploadBytesLimitSetting !== null) {
|
||||
uploadBytesLimit = +(overwriteWithUserUploadBytesLimitSetting.value as string)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import 'reflect-metadata'
|
||||
import { SettingProjector } from '../../../Projection/SettingProjector'
|
||||
import { Setting } from '../../Setting/Setting'
|
||||
@@ -45,12 +45,12 @@ describe('GetSetting', () => {
|
||||
it('should not retrieve a sensitive setting for user', async () => {
|
||||
setting = {
|
||||
sensitive: true,
|
||||
name: SettingName.MfaSecret,
|
||||
name: SettingName.NAMES.MfaSecret,
|
||||
} as jest.Mocked<Setting>
|
||||
|
||||
settingService.findSettingWithDecryptedValue = jest.fn().mockReturnValue(setting)
|
||||
|
||||
expect(await createUseCase().execute({ userUuid: '1-2-3', settingName: SettingName.MfaSecret })).toEqual({
|
||||
expect(await createUseCase().execute({ userUuid: '1-2-3', settingName: SettingName.NAMES.MfaSecret })).toEqual({
|
||||
success: true,
|
||||
sensitive: true,
|
||||
})
|
||||
@@ -59,7 +59,7 @@ describe('GetSetting', () => {
|
||||
it('should retrieve a sensitive setting for user if explicitly told to', async () => {
|
||||
setting = {
|
||||
sensitive: true,
|
||||
name: SettingName.MfaSecret,
|
||||
name: SettingName.NAMES.MfaSecret,
|
||||
} as jest.Mocked<Setting>
|
||||
|
||||
settingService.findSettingWithDecryptedValue = jest.fn().mockReturnValue(setting)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { GetSettingDto } from './GetSettingDto'
|
||||
import { GetSettingResponse } from './GetSettingResponse'
|
||||
@@ -19,7 +18,7 @@ export class GetSetting implements UseCaseInterface {
|
||||
|
||||
const setting = await this.settingService.findSettingWithDecryptedValue({
|
||||
userUuid,
|
||||
settingName: settingName as SettingName,
|
||||
settingName: settingName,
|
||||
})
|
||||
|
||||
if (setting === null) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
|
||||
import { SettingProjector } from '../../../Projection/SettingProjector'
|
||||
import { Setting } from '../../Setting/Setting'
|
||||
@@ -31,7 +31,7 @@ describe('GetSettings', () => {
|
||||
} as jest.Mocked<Setting>
|
||||
|
||||
mfaSetting = {
|
||||
name: SettingName.MfaSecret,
|
||||
name: SettingName.NAMES.MfaSecret,
|
||||
updatedAt: 122,
|
||||
sensitive: true,
|
||||
} as jest.Mocked<Setting>
|
||||
|
||||
+8
-8
@@ -1,6 +1,6 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
|
||||
import { SubscriptionSettingProjector } from '../../../Projection/SubscriptionSettingProjector'
|
||||
import { SubscriptionSetting } from '../../Setting/SubscriptionSetting'
|
||||
@@ -54,7 +54,7 @@ describe('GetSubscriptionSetting', () => {
|
||||
expect(
|
||||
await createUseCase().execute({
|
||||
userUuid: '1-2-3',
|
||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesUsed,
|
||||
subscriptionSettingName: SettingName.NAMES.FileUploadBytesUsed,
|
||||
}),
|
||||
).toEqual({
|
||||
success: true,
|
||||
@@ -70,7 +70,7 @@ describe('GetSubscriptionSetting', () => {
|
||||
expect(
|
||||
await createUseCase().execute({
|
||||
userUuid: '1-2-3',
|
||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesLimit,
|
||||
subscriptionSettingName: SettingName.NAMES.FileUploadBytesLimit,
|
||||
}),
|
||||
).toEqual({
|
||||
success: false,
|
||||
@@ -86,7 +86,7 @@ describe('GetSubscriptionSetting', () => {
|
||||
expect(
|
||||
await createUseCase().execute({
|
||||
userUuid: '1-2-3',
|
||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesLimit,
|
||||
subscriptionSettingName: SettingName.NAMES.FileUploadBytesLimit,
|
||||
}),
|
||||
).toEqual({
|
||||
success: false,
|
||||
@@ -99,7 +99,7 @@ describe('GetSubscriptionSetting', () => {
|
||||
it('should not retrieve a sensitive setting for user', async () => {
|
||||
subscriptionSetting = {
|
||||
sensitive: true,
|
||||
name: SubscriptionSettingName.FileUploadBytesLimit,
|
||||
name: SettingName.NAMES.FileUploadBytesLimit,
|
||||
} as jest.Mocked<SubscriptionSetting>
|
||||
|
||||
subscriptionSettingService.findSubscriptionSettingWithDecryptedValue = jest
|
||||
@@ -109,7 +109,7 @@ describe('GetSubscriptionSetting', () => {
|
||||
expect(
|
||||
await createUseCase().execute({
|
||||
userUuid: '1-2-3',
|
||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesLimit,
|
||||
subscriptionSettingName: SettingName.NAMES.FileUploadBytesLimit,
|
||||
}),
|
||||
).toEqual({
|
||||
success: true,
|
||||
@@ -120,7 +120,7 @@ describe('GetSubscriptionSetting', () => {
|
||||
it('should retrieve a sensitive setting for user if explicitly told to', async () => {
|
||||
subscriptionSetting = {
|
||||
sensitive: true,
|
||||
name: SubscriptionSettingName.FileUploadBytesLimit,
|
||||
name: SettingName.NAMES.FileUploadBytesLimit,
|
||||
} as jest.Mocked<SubscriptionSetting>
|
||||
|
||||
subscriptionSettingService.findSubscriptionSettingWithDecryptedValue = jest
|
||||
@@ -130,7 +130,7 @@ describe('GetSubscriptionSetting', () => {
|
||||
expect(
|
||||
await createUseCase().execute({
|
||||
userUuid: '1-2-3',
|
||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesLimit,
|
||||
subscriptionSettingName: SettingName.NAMES.FileUploadBytesLimit,
|
||||
allowSensitiveRetrieval: true,
|
||||
}),
|
||||
).toEqual({
|
||||
|
||||
+1
-2
@@ -1,8 +1,7 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
||||
|
||||
export type GetSubscriptionSettingDTO = {
|
||||
userUuid: Uuid
|
||||
subscriptionSettingName: SubscriptionSettingName
|
||||
subscriptionSettingName: string
|
||||
allowSensitiveRetrieval?: boolean
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { SimpleSetting } from '../../Setting/SimpleSetting'
|
||||
import { User } from '../../User/User'
|
||||
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||
import { UpdateSetting } from './UpdateSetting'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
|
||||
describe('UpdateSetting', () => {
|
||||
let settingService: SettingServiceInterface
|
||||
@@ -59,7 +59,7 @@ describe('UpdateSetting', () => {
|
||||
|
||||
it('should create a setting', async () => {
|
||||
const props = {
|
||||
name: SettingName.ExtensionKey,
|
||||
name: SettingName.NAMES.ExtensionKey,
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: EncryptionVersion.Default,
|
||||
sensitive: false,
|
||||
@@ -88,7 +88,7 @@ describe('UpdateSetting', () => {
|
||||
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
const props = {
|
||||
name: SettingName.ExtensionKey,
|
||||
name: SettingName.NAMES.ExtensionKey,
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
sensitive: false,
|
||||
@@ -136,7 +136,7 @@ describe('UpdateSetting', () => {
|
||||
roleService.userHasPermission = jest.fn().mockReturnValue(false)
|
||||
|
||||
const props = {
|
||||
name: SettingName.ExtensionKey,
|
||||
name: SettingName.NAMES.ExtensionKey,
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
sensitive: false,
|
||||
@@ -159,7 +159,7 @@ describe('UpdateSetting', () => {
|
||||
settingsAssociationService.isSettingMutableByClient = jest.fn().mockReturnValue(false)
|
||||
|
||||
const props = {
|
||||
name: SettingName.ExtensionKey,
|
||||
name: SettingName.NAMES.ExtensionKey,
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
sensitive: false,
|
||||
|
||||
@@ -9,7 +9,7 @@ import { SettingProjector } from '../../../Projection/SettingProjector'
|
||||
import { Logger } from 'winston'
|
||||
import { SettingServiceInterface } from '../../Setting/SettingServiceInterface'
|
||||
import { User } from '../../User/User'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { RoleServiceInterface } from '../../Role/RoleServiceInterface'
|
||||
import { SettingsAssociationServiceInterface } from '../../Setting/SettingsAssociationServiceInterface'
|
||||
|
||||
@@ -25,7 +25,7 @@ export class UpdateSetting implements UseCaseInterface {
|
||||
) {}
|
||||
|
||||
async execute(dto: UpdateSettingDto): Promise<UpdateSettingResponse> {
|
||||
if (!Object.values(SettingName).includes(dto.props.name as SettingName)) {
|
||||
if (!Object.values(SettingName.NAMES).includes(dto.props.name)) {
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
@@ -51,7 +51,7 @@ export class UpdateSetting implements UseCaseInterface {
|
||||
}
|
||||
}
|
||||
|
||||
if (!(await this.userHasPermissionToUpdateSetting(user, props.name as SettingName))) {
|
||||
if (!(await this.userHasPermissionToUpdateSetting(user, props.name))) {
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
@@ -61,10 +61,8 @@ export class UpdateSetting implements UseCaseInterface {
|
||||
}
|
||||
}
|
||||
|
||||
props.serverEncryptionVersion = this.settingsAssociationService.getEncryptionVersionForSetting(
|
||||
props.name as SettingName,
|
||||
)
|
||||
props.sensitive = this.settingsAssociationService.getSensitivityForSetting(props.name as SettingName)
|
||||
props.serverEncryptionVersion = this.settingsAssociationService.getEncryptionVersionForSetting(props.name)
|
||||
props.sensitive = this.settingsAssociationService.getSensitivityForSetting(props.name)
|
||||
|
||||
const response = await this.settingService.createOrReplace({
|
||||
user,
|
||||
@@ -91,8 +89,8 @@ export class UpdateSetting implements UseCaseInterface {
|
||||
throw new Error(`Unrecognized status: ${exhaustiveCheck}!`)
|
||||
}
|
||||
|
||||
private async userHasPermissionToUpdateSetting(user: User, settingName: SettingName): Promise<boolean> {
|
||||
const settingIsMutableByClient = await this.settingsAssociationService.isSettingMutableByClient(settingName)
|
||||
private async userHasPermissionToUpdateSetting(user: User, settingName: string): Promise<boolean> {
|
||||
const settingIsMutableByClient = this.settingsAssociationService.isSettingMutableByClient(settingName)
|
||||
if (!settingIsMutableByClient) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||
import { VerifyMFA } from './VerifyMFA'
|
||||
import { Setting } from '../Setting/Setting'
|
||||
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { SelectorInterface } from '@standardnotes/security'
|
||||
import { LockRepositoryInterface } from '../User/LockRepositoryInterface'
|
||||
|
||||
@@ -36,7 +36,7 @@ describe('VerifyMFA', () => {
|
||||
lockRepository.lockSuccessfullOTP = jest.fn()
|
||||
|
||||
setting = {
|
||||
name: SettingName.MfaSecret,
|
||||
name: SettingName.NAMES.MfaSecret,
|
||||
value: 'shhhh',
|
||||
} as jest.Mocked<Setting>
|
||||
|
||||
@@ -58,7 +58,7 @@ describe('VerifyMFA', () => {
|
||||
|
||||
it('should pass MFA verification if user has MFA deleted', async () => {
|
||||
setting = {
|
||||
name: SettingName.MfaSecret,
|
||||
name: SettingName.NAMES.MfaSecret,
|
||||
value: null,
|
||||
} as jest.Mocked<Setting>
|
||||
|
||||
@@ -148,7 +148,7 @@ describe('VerifyMFA', () => {
|
||||
|
||||
it('should not pass MFA verification if mfa is not correct', async () => {
|
||||
setting = {
|
||||
name: SettingName.MfaSecret,
|
||||
name: SettingName.NAMES.MfaSecret,
|
||||
value: 'shhhh2',
|
||||
} as jest.Mocked<Setting>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as crypto from 'crypto'
|
||||
import { ErrorTag } from '@standardnotes/common'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { authenticator } from 'otplib'
|
||||
@@ -50,7 +50,7 @@ export class VerifyMFA implements UseCaseInterface {
|
||||
|
||||
const mfaSecret = await this.settingService.findSettingWithDecryptedValue({
|
||||
userUuid: user.uuid,
|
||||
settingName: SettingName.MfaSecret,
|
||||
settingName: SettingName.NAMES.MfaSecret,
|
||||
})
|
||||
if (mfaSecret === null || mfaSecret.value === null) {
|
||||
return {
|
||||
|
||||
@@ -8,7 +8,6 @@ import { UserSubscription } from '../../Subscription/UserSubscription'
|
||||
import { UserSubscriptionRepositoryInterface } from '../../Subscription/UserSubscriptionRepositoryInterface'
|
||||
|
||||
import { VerifyPredicate } from './VerifyPredicate'
|
||||
import { EmailBackupFrequency } from '@standardnotes/settings'
|
||||
|
||||
describe('VerifyPredicate', () => {
|
||||
let settingRepository: SettingRepositoryInterface
|
||||
@@ -30,7 +29,7 @@ describe('VerifyPredicate', () => {
|
||||
})
|
||||
|
||||
it('should tell that a user has enabled email backups', async () => {
|
||||
setting = { value: EmailBackupFrequency.Weekly } as jest.Mocked<Setting>
|
||||
setting = { value: 'weekly' } as jest.Mocked<Setting>
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(setting)
|
||||
|
||||
expect(
|
||||
@@ -44,7 +43,7 @@ describe('VerifyPredicate', () => {
|
||||
})
|
||||
|
||||
it('should tell that a user has disabled email backups', async () => {
|
||||
setting = { value: EmailBackupFrequency.Disabled } as jest.Mocked<Setting>
|
||||
setting = { value: 'disabled' } as jest.Mocked<Setting>
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(setting)
|
||||
|
||||
expect(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
import { PredicateName, PredicateVerificationResult } from '@standardnotes/predicates'
|
||||
import { EmailBackupFrequency, SettingName } from '@standardnotes/settings'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
import { inject, injectable } from 'inversify'
|
||||
|
||||
import TYPES from '../../../Bootstrap/Types'
|
||||
@@ -40,9 +40,12 @@ export class VerifyPredicate implements UseCaseInterface {
|
||||
}
|
||||
|
||||
private async hasUserEnabledEmailBackups(userUuid: Uuid): Promise<boolean> {
|
||||
const setting = await this.settingRepository.findOneByNameAndUserUuid(SettingName.EmailBackupFrequency, userUuid)
|
||||
const setting = await this.settingRepository.findOneByNameAndUserUuid(
|
||||
SettingName.NAMES.EmailBackupFrequency,
|
||||
userUuid,
|
||||
)
|
||||
|
||||
if (setting === null || setting.value === EmailBackupFrequency.Disabled) {
|
||||
if (setting === null || setting.value === 'disabled') {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { ReadStream } from 'fs'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Repository } from 'typeorm'
|
||||
@@ -18,7 +17,7 @@ export class MySQLSettingRepository implements SettingRepositoryInterface {
|
||||
return this.ormRepository.save(setting)
|
||||
}
|
||||
|
||||
async findOneByUuidAndNames(uuid: string, names: SettingName[]): Promise<Setting | null> {
|
||||
async findOneByUuidAndNames(uuid: string, names: string[]): Promise<Setting | null> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder('setting')
|
||||
.where('setting.uuid = :uuid AND setting.name IN (:...names)', {
|
||||
@@ -28,7 +27,7 @@ export class MySQLSettingRepository implements SettingRepositoryInterface {
|
||||
.getOne()
|
||||
}
|
||||
|
||||
async streamAllByNameAndValue(name: SettingName, value: string): Promise<ReadStream> {
|
||||
async streamAllByNameAndValue(name: string, value: string): Promise<ReadStream> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder('setting')
|
||||
.where('setting.name = :name AND setting.value = :value', {
|
||||
|
||||
@@ -35,6 +35,19 @@ describe('RoleNameCollection', () => {
|
||||
expect(valueOrError.getValue().equals(roles2)).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should tell if collections are not equal', () => {
|
||||
const roles1 = [RoleName.create('PRO_USER').getValue(), RoleName.create('PLUS_USER').getValue()]
|
||||
|
||||
const roles2 = RoleNameCollection.create([
|
||||
RoleName.create('PRO_USER').getValue(),
|
||||
RoleName.create('PLUS_USER').getValue(),
|
||||
RoleName.create('CORE_USER').getValue(),
|
||||
]).getValue()
|
||||
|
||||
const valueOrError = RoleNameCollection.create(roles1)
|
||||
expect(valueOrError.getValue().equals(roles2)).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should tell if collections are equal', () => {
|
||||
const roles1 = [
|
||||
RoleName.create(RoleName.NAMES.ProUser).getValue(),
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Timestamps } from './Timestamps'
|
||||
|
||||
describe('Timestamps', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = Timestamps.create(1, 2)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().createdAt).toEqual(1)
|
||||
expect(valueOrError.getValue().updatedAt).toEqual(2)
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
let valueOrError = Timestamps.create(null as unknown as number, 'b' as unknown as number)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
|
||||
valueOrError = Timestamps.create(2, 'a' as unknown as number)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { TimestampsProps } from './TimestampsProps'
|
||||
|
||||
export class Timestamps extends ValueObject<TimestampsProps> {
|
||||
get createdAt(): number {
|
||||
return this.props.createdAt
|
||||
}
|
||||
|
||||
get updatedAt(): number {
|
||||
return this.props.updatedAt
|
||||
}
|
||||
|
||||
private constructor(props: TimestampsProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(createdAt: number, updatedAt: number): Result<Timestamps> {
|
||||
if (isNaN(createdAt) || createdAt === null || createdAt === undefined) {
|
||||
return Result.fail<Timestamps>(
|
||||
`Could not create Timestamps. Creation date should be a number, given: ${createdAt}`,
|
||||
)
|
||||
}
|
||||
if (isNaN(updatedAt) || updatedAt === null || updatedAt === undefined) {
|
||||
return Result.fail<Timestamps>(`Could not create Timestamps. Update date should be a number, given: ${createdAt}`)
|
||||
}
|
||||
|
||||
return Result.ok<Timestamps>(new Timestamps({ createdAt, updatedAt }))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface TimestampsProps {
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { EncryptionVersion } from './EncryptionVersion'
|
||||
|
||||
describe('EncryptionVersion', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = EncryptionVersion.create(1)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).toEqual(1)
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
let valueOrError = EncryptionVersion.create('asd' as unknown as number)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
|
||||
valueOrError = EncryptionVersion.create(null as unknown as number)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
|
||||
valueOrError = EncryptionVersion.create(undefined as unknown as number)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
|
||||
valueOrError = EncryptionVersion.create(754)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { EncryptionVersionProps } from './EncryptionVersionProps'
|
||||
|
||||
export class EncryptionVersion extends ValueObject<EncryptionVersionProps> {
|
||||
static readonly VERSIONS = {
|
||||
Unencrypted: 0,
|
||||
Default: 1,
|
||||
}
|
||||
|
||||
get value(): number {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
private constructor(props: EncryptionVersionProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(version: number): Result<EncryptionVersion> {
|
||||
if (
|
||||
isNaN(version) ||
|
||||
version === null ||
|
||||
version === undefined ||
|
||||
!Object.values(this.VERSIONS).includes(version)
|
||||
) {
|
||||
return Result.fail<EncryptionVersion>(
|
||||
`Could not create EncryptionVersion. Version should be a number, given: ${version}`,
|
||||
)
|
||||
}
|
||||
|
||||
return Result.ok<EncryptionVersion>(new EncryptionVersion({ value: version }))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface EncryptionVersionProps {
|
||||
value: number
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
|
||||
import { SettingNameProps } from './SettingNameProps'
|
||||
|
||||
export class SettingName extends ValueObject<SettingNameProps> {
|
||||
static readonly NAMES = {
|
||||
MfaSecret: 'MFA_SECRET',
|
||||
ExtensionKey: 'EXTENSION_KEY',
|
||||
EmailBackupFrequency: 'EMAIL_BACKUP_FREQUENCY',
|
||||
DropboxBackupFrequency: 'DROPBOX_BACKUP_FREQUENCY',
|
||||
DropboxBackupToken: 'DROPBOX_BACKUP_TOKEN',
|
||||
OneDriveBackupFrequency: 'ONE_DRIVE_BACKUP_FREQUENCY',
|
||||
OneDriveBackupToken: 'ONE_DRIVE_BACKUP_TOKEN',
|
||||
GoogleDriveBackupFrequency: 'GOOGLE_DRIVE_BACKUP_FREQUENCY',
|
||||
GoogleDriveBackupToken: 'GOOGLE_DRIVE_BACKUP_TOKEN',
|
||||
MuteFailedBackupsEmails: 'MUTE_FAILED_BACKUPS_EMAILS',
|
||||
MuteFailedCloudBackupsEmails: 'MUTE_FAILED_CLOUD_BACKUPS_EMAILS',
|
||||
MuteSignInEmails: 'MUTE_SIGN_IN_EMAILS',
|
||||
MuteMarketingEmails: 'MUTE_MARKETING_EMAILS',
|
||||
ListedAuthorSecrets: 'LISTED_AUTHOR_SECRETS',
|
||||
LogSessionUserAgent: 'LOG_SESSION_USER_AGENT',
|
||||
FileUploadBytesLimit: 'FILE_UPLOAD_BYTES_LIMIT',
|
||||
FileUploadBytesUsed: 'FILE_UPLOAD_BYTES_USED',
|
||||
EmailUnsubscribeToken: 'EMAIL_UNSUBSCRIBE_TOKEN',
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
isSensitive(): boolean {
|
||||
return [SettingName.NAMES.MfaSecret, SettingName.NAMES.ExtensionKey].includes(this.value)
|
||||
}
|
||||
|
||||
private constructor(props: SettingNameProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(name: string): Result<SettingName> {
|
||||
const isValidName = Object.values(this.NAMES).includes(name)
|
||||
if (!isValidName) {
|
||||
return Result.fail<SettingName>(`Invalid setting name: ${name}`)
|
||||
} else {
|
||||
return Result.ok<SettingName>(new SettingName({ value: name }))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface SettingNameProps {
|
||||
value: string
|
||||
}
|
||||
@@ -8,6 +8,8 @@ export * from './Common/RoleNameCollection'
|
||||
export * from './Common/RoleNameCollectionProps'
|
||||
export * from './Common/Username'
|
||||
export * from './Common/UsernameProps'
|
||||
export * from './Common/Timestamps'
|
||||
export * from './Common/TimestampsProps'
|
||||
export * from './Common/Uuid'
|
||||
export * from './Common/UuidProps'
|
||||
|
||||
@@ -23,8 +25,14 @@ export * from './Core/ValueObjectProps'
|
||||
export * from './Email/EmailLevel'
|
||||
export * from './Email/EmailLevelProps'
|
||||
|
||||
export * from './Encryption/EncryptionVersion'
|
||||
export * from './Encryption/EncryptionVersionProps'
|
||||
|
||||
export * from './Mapping/MapperInterface'
|
||||
|
||||
export * from './Setting/SettingName'
|
||||
export * from './Setting/SettingNameProps'
|
||||
|
||||
export * from './Subscription/SubscriptionPlanName'
|
||||
export * from './Subscription/SubscriptionPlanNameProps'
|
||||
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.9.56](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.9.55...@standardnotes/domain-events-infra@1.9.56) (2022-12-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.9.55](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.9.54...@standardnotes/domain-events-infra@1.9.55) (2022-12-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.9.54](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.9.53...@standardnotes/domain-events-infra@1.9.54) (2022-12-09)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events-infra",
|
||||
"version": "1.9.54",
|
||||
"version": "1.9.56",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.104.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.104.0...@standardnotes/domain-events@2.104.1) (2022-12-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **domain-events:** add additional domain event services ([2980c42](https://github.com/standardnotes/server/commit/2980c42e88b6be5f065c91c86bf85a706975f801))
|
||||
|
||||
# [2.104.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.103.2...@standardnotes/domain-events@2.104.0) (2022-12-12)
|
||||
|
||||
### Features
|
||||
|
||||
* **domain-events:** add event for email subscription unsubscribed ([7f18fcf](https://github.com/standardnotes/server/commit/7f18fcfc139911620f2ea72729357aefd0613315))
|
||||
|
||||
## [2.103.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.103.1...@standardnotes/domain-events@2.103.2) (2022-12-09)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events",
|
||||
"version": "2.103.2",
|
||||
"version": "2.104.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -10,4 +10,7 @@ export enum DomainEventService {
|
||||
Scheduler = 'scheduler',
|
||||
Workspace = 'workspace',
|
||||
Analytics = 'analytics',
|
||||
Revisions = 'revisions',
|
||||
Email = 'email',
|
||||
Settings = 'settings',
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
import { EmailSubscriptionUnsubscribedEventPayload } from './EmailSubscriptionUnsubscribedEventPayload'
|
||||
|
||||
export interface EmailSubscriptionUnsubscribedEvent extends DomainEventInterface {
|
||||
type: 'EMAIL_SUBSCRIPTION_UNSUBSCRIBED'
|
||||
payload: EmailSubscriptionUnsubscribedEventPayload
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface EmailSubscriptionUnsubscribedEventPayload {
|
||||
userEmail: string
|
||||
level: string
|
||||
}
|
||||
@@ -16,6 +16,8 @@ export * from './Event/EmailBackupRequestedEvent'
|
||||
export * from './Event/EmailBackupRequestedEventPayload'
|
||||
export * from './Event/EmailRequestedEvent'
|
||||
export * from './Event/EmailRequestedEventPayload'
|
||||
export * from './Event/EmailSubscriptionUnsubscribedEvent'
|
||||
export * from './Event/EmailSubscriptionUnsubscribedEventPayload'
|
||||
export * from './Event/ExitDiscountAppliedEvent'
|
||||
export * from './Event/ExitDiscountAppliedEventPayload'
|
||||
export * from './Event/ExitDiscountApplyRequestedEvent'
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.6.53](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.6.52...@standardnotes/event-store@1.6.53) (2022-12-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.6.52](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.6.51...@standardnotes/event-store@1.6.52) (2022-12-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.6.51](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.6.50...@standardnotes/event-store@1.6.51) (2022-12-09)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/event-store",
|
||||
"version": "1.6.51",
|
||||
"version": "1.6.53",
|
||||
"description": "Event Store Service",
|
||||
"private": true,
|
||||
"main": "dist/src/index.js",
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.8.52](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.8.51...@standardnotes/files-server@1.8.52) (2022-12-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.8.51](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.8.50...@standardnotes/files-server@1.8.51) (2022-12-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.8.50](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.8.49...@standardnotes/files-server@1.8.50) (2022-12-09)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files-server",
|
||||
"version": "1.8.50",
|
||||
"version": "1.8.52",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.9.26](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.9.25...@standardnotes/revisions-server@1.9.26) (2022-12-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **revisions:** responses to match previous response structure ([530a426](https://github.com/standardnotes/server/commit/530a42660157b63d034cd2228fc83a3fcce921e0))
|
||||
|
||||
## [1.9.25](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.9.24...@standardnotes/revisions-server@1.9.25) (2022-12-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.9.24](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.9.23...@standardnotes/revisions-server@1.9.24) (2022-12-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.9.23](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.9.22...@standardnotes/revisions-server@1.9.23) (2022-12-09)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/revisions-server",
|
||||
"version": "1.9.23",
|
||||
"version": "1.9.26",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -2,11 +2,13 @@ import { Logger } from 'winston'
|
||||
import { HttpResponse, HttpStatusCode } from '@standardnotes/api'
|
||||
|
||||
import { GetRevisionsMetada } from '../Domain/UseCase/GetRevisionsMetada/GetRevisionsMetada'
|
||||
import { GetRevisionsMetadataRequestParams } from '../Infra/Http/GetRevisionsMetadataRequestParams'
|
||||
import { GetRevisionRequestParams } from '../Infra/Http/GetRevisionRequestParams'
|
||||
import { GetRevisionsMetadataRequestParams } from '../Infra/Http/Request/GetRevisionsMetadataRequestParams'
|
||||
import { GetRevisionRequestParams } from '../Infra/Http/Request/GetRevisionRequestParams'
|
||||
import { DeleteRevisionRequestParams } from '../Infra/Http/Request/DeleteRevisionRequestParams'
|
||||
import { GetRevision } from '../Domain/UseCase/GetRevision/GetRevision'
|
||||
import { DeleteRevision } from '../Domain/UseCase/DeleteRevision/DeleteRevision'
|
||||
import { DeleteRevisionRequestParams } from '../Infra/Http/DeleteRevisionRequestParams'
|
||||
import { GetRevisionsMetadataResponse } from '../Infra/Http/Response/GetRevisionsMetadataResponse'
|
||||
import { GetRevisionResponse } from '../Infra/Http/Response/GetRevisionResponse'
|
||||
|
||||
export class RevisionsController {
|
||||
constructor(
|
||||
@@ -16,7 +18,7 @@ export class RevisionsController {
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async getRevisions(params: GetRevisionsMetadataRequestParams): Promise<HttpResponse> {
|
||||
async getRevisions(params: GetRevisionsMetadataRequestParams): Promise<GetRevisionsMetadataResponse> {
|
||||
const revisionMetadataOrError = await this.getRevisionsMetadata.execute({
|
||||
itemUuid: params.itemUuid,
|
||||
userUuid: params.userUuid,
|
||||
@@ -41,7 +43,7 @@ export class RevisionsController {
|
||||
}
|
||||
}
|
||||
|
||||
async getRevision(params: GetRevisionRequestParams): Promise<HttpResponse> {
|
||||
async getRevision(params: GetRevisionRequestParams): Promise<GetRevisionResponse> {
|
||||
const revisionOrError = await this.doGetRevision.execute({
|
||||
revisionUuid: params.revisionUuid,
|
||||
userUuid: params.userUuid,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user