mirror of
https://github.com/standardnotes/server
synced 2026-04-19 08:02:23 -04:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 55f8f65c3f | |||
| 3953dbc6b4 | |||
| 0b205287d1 | |||
| 4f0bc57b1a | |||
| 7d43316597 | |||
| 65d31f011b | |||
| 80dd6efae3 |
@@ -66,7 +66,7 @@ const RAW_RUNTIME_STATE =
|
|||||||
"reference": "workspace:packages/security"\
|
"reference": "workspace:packages/security"\
|
||||||
},\
|
},\
|
||||||
{\
|
{\
|
||||||
"name": "@standardnotes/settings",\
|
"name": "@standardnotes/settings-server",\
|
||||||
"reference": "workspace:packages/settings"\
|
"reference": "workspace:packages/settings"\
|
||||||
},\
|
},\
|
||||||
{\
|
{\
|
||||||
@@ -107,7 +107,7 @@ const RAW_RUNTIME_STATE =
|
|||||||
["@standardnotes/scheduler-server", ["workspace:packages/scheduler"]],\
|
["@standardnotes/scheduler-server", ["workspace:packages/scheduler"]],\
|
||||||
["@standardnotes/security", ["workspace:packages/security"]],\
|
["@standardnotes/security", ["workspace:packages/security"]],\
|
||||||
["@standardnotes/server-monorepo", ["workspace:."]],\
|
["@standardnotes/server-monorepo", ["workspace:."]],\
|
||||||
["@standardnotes/settings", ["workspace:packages/settings"]],\
|
["@standardnotes/settings-server", ["workspace:packages/settings"]],\
|
||||||
["@standardnotes/sncrypto-node", ["workspace:packages/sncrypto-node"]],\
|
["@standardnotes/sncrypto-node", ["workspace:packages/sncrypto-node"]],\
|
||||||
["@standardnotes/syncing-server", ["workspace:packages/syncing-server"]],\
|
["@standardnotes/syncing-server", ["workspace:packages/syncing-server"]],\
|
||||||
["@standardnotes/time", ["workspace:packages/time"]],\
|
["@standardnotes/time", ["workspace:packages/time"]],\
|
||||||
@@ -2582,6 +2582,20 @@ const RAW_RUNTIME_STATE =
|
|||||||
["reflect-metadata", "npm:0.1.13"]\
|
["reflect-metadata", "npm:0.1.13"]\
|
||||||
],\
|
],\
|
||||||
"linkType": "HARD"\
|
"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", [\
|
["@standardnotes/api-gateway", [\
|
||||||
@@ -2656,7 +2670,6 @@ const RAW_RUNTIME_STATE =
|
|||||||
["@standardnotes/predicates", "workspace:packages/predicates"],\
|
["@standardnotes/predicates", "workspace:packages/predicates"],\
|
||||||
["@standardnotes/responses", "npm:1.11.1"],\
|
["@standardnotes/responses", "npm:1.11.1"],\
|
||||||
["@standardnotes/security", "workspace:packages/security"],\
|
["@standardnotes/security", "workspace:packages/security"],\
|
||||||
["@standardnotes/settings", "workspace:packages/settings"],\
|
|
||||||
["@standardnotes/sncrypto-common", "npm:1.13.0"],\
|
["@standardnotes/sncrypto-common", "npm:1.13.0"],\
|
||||||
["@standardnotes/sncrypto-node", "workspace:packages/sncrypto-node"],\
|
["@standardnotes/sncrypto-node", "workspace:packages/sncrypto-node"],\
|
||||||
["@standardnotes/time", "workspace:packages/time"],\
|
["@standardnotes/time", "workspace:packages/time"],\
|
||||||
@@ -2812,6 +2825,19 @@ const RAW_RUNTIME_STATE =
|
|||||||
["reflect-metadata", "npm:0.1.13"]\
|
["reflect-metadata", "npm:0.1.13"]\
|
||||||
],\
|
],\
|
||||||
"linkType": "HARD"\
|
"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", [\
|
["@standardnotes/event-store", [\
|
||||||
@@ -2867,6 +2893,17 @@ const RAW_RUNTIME_STATE =
|
|||||||
["reflect-metadata", "npm:0.1.13"]\
|
["reflect-metadata", "npm:0.1.13"]\
|
||||||
],\
|
],\
|
||||||
"linkType": "HARD"\
|
"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", [\
|
["@standardnotes/files-server", [\
|
||||||
@@ -2948,6 +2985,13 @@ const RAW_RUNTIME_STATE =
|
|||||||
["reflect-metadata", "npm:0.1.13"]\
|
["reflect-metadata", "npm:0.1.13"]\
|
||||||
],\
|
],\
|
||||||
"linkType": "HARD"\
|
"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", [\
|
["@standardnotes/payloads", [\
|
||||||
@@ -3001,6 +3045,17 @@ const RAW_RUNTIME_STATE =
|
|||||||
["reflect-metadata", "npm:0.1.13"]\
|
["reflect-metadata", "npm:0.1.13"]\
|
||||||
],\
|
],\
|
||||||
"linkType": "HARD"\
|
"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", [\
|
["@standardnotes/revisions-server", [\
|
||||||
@@ -3132,15 +3187,46 @@ const RAW_RUNTIME_STATE =
|
|||||||
"linkType": "SOFT"\
|
"linkType": "SOFT"\
|
||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
["@standardnotes/settings", [\
|
["@standardnotes/settings-server", [\
|
||||||
["workspace:packages/settings", {\
|
["workspace:packages/settings", {\
|
||||||
"packageLocation": "./packages/settings/",\
|
"packageLocation": "./packages/settings/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
["@standardnotes/settings", "workspace:packages/settings"],\
|
["@standardnotes/settings-server", "workspace:packages/settings"],\
|
||||||
["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:5.30.5"],\
|
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.0"],\
|
||||||
["eslint-plugin-prettier", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:4.2.1"],\
|
["@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"],\
|
["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"\
|
"linkType": "SOFT"\
|
||||||
}]\
|
}]\
|
||||||
@@ -3153,6 +3239,14 @@ const RAW_RUNTIME_STATE =
|
|||||||
["reflect-metadata", "npm:0.1.13"]\
|
["reflect-metadata", "npm:0.1.13"]\
|
||||||
],\
|
],\
|
||||||
"linkType": "HARD"\
|
"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", [\
|
["@standardnotes/sncrypto-node", [\
|
||||||
@@ -3189,7 +3283,6 @@ const RAW_RUNTIME_STATE =
|
|||||||
["@standardnotes/payloads", "npm:1.5.1"],\
|
["@standardnotes/payloads", "npm:1.5.1"],\
|
||||||
["@standardnotes/responses", "npm:1.11.1"],\
|
["@standardnotes/responses", "npm:1.11.1"],\
|
||||||
["@standardnotes/security", "workspace:packages/security"],\
|
["@standardnotes/security", "workspace:packages/security"],\
|
||||||
["@standardnotes/settings", "workspace:packages/settings"],\
|
|
||||||
["@standardnotes/time", "workspace:packages/time"],\
|
["@standardnotes/time", "workspace:packages/time"],\
|
||||||
["@types/cors", "npm:2.8.12"],\
|
["@types/cors", "npm:2.8.12"],\
|
||||||
["@types/dotenv", "npm:8.2.0"],\
|
["@types/dotenv", "npm:8.2.0"],\
|
||||||
@@ -3273,6 +3366,17 @@ const RAW_RUNTIME_STATE =
|
|||||||
["reflect-metadata", "npm:0.1.13"]\
|
["reflect-metadata", "npm:0.1.13"]\
|
||||||
],\
|
],\
|
||||||
"linkType": "HARD"\
|
"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", [\
|
["@standardnotes/websockets-server", [\
|
||||||
@@ -6371,6 +6475,13 @@ const RAW_RUNTIME_STATE =
|
|||||||
["dompurify", "npm:2.4.0"]\
|
["dompurify", "npm:2.4.0"]\
|
||||||
],\
|
],\
|
||||||
"linkType": "HARD"\
|
"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", [\
|
["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.
+21
-22
@@ -3,20 +3,19 @@ import 'reflect-metadata'
|
|||||||
import 'newrelic'
|
import 'newrelic'
|
||||||
|
|
||||||
import { Stream } from 'stream'
|
import { Stream } from 'stream'
|
||||||
|
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
import * as dayjs from 'dayjs'
|
import * as dayjs from 'dayjs'
|
||||||
import * as utc from 'dayjs/plugin/utc'
|
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 { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||||
import TYPES from '../src/Bootstrap/Types'
|
import TYPES from '../src/Bootstrap/Types'
|
||||||
import { Env } from '../src/Bootstrap/Env'
|
import { Env } from '../src/Bootstrap/Env'
|
||||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
|
||||||
import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface'
|
import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface'
|
||||||
import { SettingRepositoryInterface } from '../src/Domain/Setting/SettingRepositoryInterface'
|
import { SettingRepositoryInterface } from '../src/Domain/Setting/SettingRepositoryInterface'
|
||||||
import { MuteFailedBackupsEmailsOption, MuteFailedCloudBackupsEmailsOption, SettingName } from '@standardnotes/settings'
|
|
||||||
import { RoleServiceInterface } from '../src/Domain/Role/RoleServiceInterface'
|
import { RoleServiceInterface } from '../src/Domain/Role/RoleServiceInterface'
|
||||||
import { PermissionName } from '@standardnotes/features'
|
|
||||||
import { SettingServiceInterface } from '../src/Domain/Setting/SettingServiceInterface'
|
import { SettingServiceInterface } from '../src/Domain/Setting/SettingServiceInterface'
|
||||||
|
|
||||||
const inputArgs = process.argv.slice(2)
|
const inputArgs = process.argv.slice(2)
|
||||||
@@ -30,38 +29,38 @@ const requestBackups = async (
|
|||||||
domainEventFactory: DomainEventFactoryInterface,
|
domainEventFactory: DomainEventFactoryInterface,
|
||||||
domainEventPublisher: DomainEventPublisherInterface,
|
domainEventPublisher: DomainEventPublisherInterface,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
let settingName: SettingName,
|
let settingName: string,
|
||||||
permissionName: PermissionName,
|
permissionName: PermissionName,
|
||||||
muteEmailsSettingName: SettingName,
|
muteEmailsSettingName: string,
|
||||||
muteEmailsSettingValue: string,
|
muteEmailsSettingValue: string,
|
||||||
providerTokenSettingName: SettingName
|
providerTokenSettingName: string
|
||||||
switch (backupProvider) {
|
switch (backupProvider) {
|
||||||
case 'email':
|
case 'email':
|
||||||
settingName = SettingName.EmailBackupFrequency
|
settingName = SettingName.NAMES.EmailBackupFrequency
|
||||||
permissionName = PermissionName.DailyEmailBackup
|
permissionName = PermissionName.DailyEmailBackup
|
||||||
muteEmailsSettingName = SettingName.MuteFailedBackupsEmails
|
muteEmailsSettingName = SettingName.NAMES.MuteFailedBackupsEmails
|
||||||
muteEmailsSettingValue = MuteFailedBackupsEmailsOption.Muted
|
muteEmailsSettingValue = 'muted'
|
||||||
break
|
break
|
||||||
case 'dropbox':
|
case 'dropbox':
|
||||||
settingName = SettingName.DropboxBackupFrequency
|
settingName = SettingName.NAMES.DropboxBackupFrequency
|
||||||
permissionName = PermissionName.DailyDropboxBackup
|
permissionName = PermissionName.DailyDropboxBackup
|
||||||
muteEmailsSettingName = SettingName.MuteFailedCloudBackupsEmails
|
muteEmailsSettingName = SettingName.NAMES.MuteFailedCloudBackupsEmails
|
||||||
muteEmailsSettingValue = MuteFailedCloudBackupsEmailsOption.Muted
|
muteEmailsSettingValue = 'muted'
|
||||||
providerTokenSettingName = SettingName.DropboxBackupToken
|
providerTokenSettingName = SettingName.NAMES.DropboxBackupToken
|
||||||
break
|
break
|
||||||
case 'one_drive':
|
case 'one_drive':
|
||||||
settingName = SettingName.OneDriveBackupFrequency
|
settingName = SettingName.NAMES.OneDriveBackupFrequency
|
||||||
permissionName = PermissionName.DailyOneDriveBackup
|
permissionName = PermissionName.DailyOneDriveBackup
|
||||||
muteEmailsSettingName = SettingName.MuteFailedCloudBackupsEmails
|
muteEmailsSettingName = SettingName.NAMES.MuteFailedCloudBackupsEmails
|
||||||
muteEmailsSettingValue = MuteFailedCloudBackupsEmailsOption.Muted
|
muteEmailsSettingValue = 'muted'
|
||||||
providerTokenSettingName = SettingName.OneDriveBackupToken
|
providerTokenSettingName = SettingName.NAMES.OneDriveBackupToken
|
||||||
break
|
break
|
||||||
case 'google_drive':
|
case 'google_drive':
|
||||||
settingName = SettingName.GoogleDriveBackupFrequency
|
settingName = SettingName.NAMES.GoogleDriveBackupFrequency
|
||||||
permissionName = PermissionName.DailyGDriveBackup
|
permissionName = PermissionName.DailyGDriveBackup
|
||||||
muteEmailsSettingName = SettingName.MuteFailedCloudBackupsEmails
|
muteEmailsSettingName = SettingName.NAMES.MuteFailedCloudBackupsEmails
|
||||||
muteEmailsSettingValue = MuteFailedCloudBackupsEmailsOption.Muted
|
muteEmailsSettingValue = 'muted'
|
||||||
providerTokenSettingName = SettingName.GoogleDriveBackupToken
|
providerTokenSettingName = SettingName.NAMES.GoogleDriveBackupToken
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
throw new Error(`Not handled backup provider: ${backupProvider}`)
|
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 { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||||
import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface'
|
import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface'
|
||||||
import { SettingRepositoryInterface } from '../src/Domain/Setting/SettingRepositoryInterface'
|
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 { RoleServiceInterface } from '../src/Domain/Role/RoleServiceInterface'
|
||||||
import { PermissionName } from '@standardnotes/features'
|
import { PermissionName } from '@standardnotes/features'
|
||||||
import { UserRepositoryInterface } from '../src/Domain/User/UserRepositoryInterface'
|
import { UserRepositoryInterface } from '../src/Domain/User/UserRepositoryInterface'
|
||||||
@@ -28,8 +28,8 @@ const requestBackups = async (
|
|||||||
domainEventPublisher: DomainEventPublisherInterface,
|
domainEventPublisher: DomainEventPublisherInterface,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const permissionName = PermissionName.DailyEmailBackup
|
const permissionName = PermissionName.DailyEmailBackup
|
||||||
const muteEmailsSettingName = SettingName.MuteFailedBackupsEmails
|
const muteEmailsSettingName = SettingName.NAMES.MuteFailedBackupsEmails
|
||||||
const muteEmailsSettingValue = MuteFailedBackupsEmailsOption.Muted
|
const muteEmailsSettingValue = 'muted'
|
||||||
|
|
||||||
if (!backupEmail) {
|
if (!backupEmail) {
|
||||||
throw new Error('Could not trigger email backup for user - missing email parameter')
|
throw new Error('Could not trigger email backup for user - missing email parameter')
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Redis, { Cluster } from 'ioredis'
|
import Redis, { Cluster } from 'ioredis'
|
||||||
import { SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||||
|
|
||||||
import { Setting } from '../src/Domain/Setting/Setting'
|
import { Setting } from '../src/Domain/Setting/Setting'
|
||||||
@@ -34,7 +34,7 @@ export class moveMfaItemsToUserSettings1627638504691 implements MigrationInterfa
|
|||||||
|
|
||||||
const setting = new Setting()
|
const setting = new Setting()
|
||||||
setting.uuid = item['uuid']
|
setting.uuid = item['uuid']
|
||||||
setting.name = SettingName.MfaSecret
|
setting.name = SettingName.NAMES.MfaSecret
|
||||||
setting.value = item['content']
|
setting.value = item['content']
|
||||||
if (item['deleted']) {
|
if (item['deleted']) {
|
||||||
setting.value = null
|
setting.value = null
|
||||||
|
|||||||
@@ -41,7 +41,6 @@
|
|||||||
"@standardnotes/predicates": "workspace:*",
|
"@standardnotes/predicates": "workspace:*",
|
||||||
"@standardnotes/responses": "^1.6.39",
|
"@standardnotes/responses": "^1.6.39",
|
||||||
"@standardnotes/security": "workspace:*",
|
"@standardnotes/security": "workspace:*",
|
||||||
"@standardnotes/settings": "workspace:*",
|
|
||||||
"@standardnotes/sncrypto-common": "^1.9.0",
|
"@standardnotes/sncrypto-common": "^1.9.0",
|
||||||
"@standardnotes/sncrypto-node": "workspace:*",
|
"@standardnotes/sncrypto-node": "workspace:*",
|
||||||
"@standardnotes/time": "workspace:*",
|
"@standardnotes/time": "workspace:*",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
import { Request } from 'express'
|
import { Request } from 'express'
|
||||||
import { inject } from 'inversify'
|
import { inject } from 'inversify'
|
||||||
import {
|
import {
|
||||||
@@ -69,7 +69,7 @@ export class AdminController extends BaseHttpController {
|
|||||||
const result = await this.doDeleteSetting.execute({
|
const result = await this.doDeleteSetting.execute({
|
||||||
uuid,
|
uuid,
|
||||||
userUuid,
|
userUuid,
|
||||||
settingName: SettingName.MfaSecret,
|
settingName: SettingName.NAMES.MfaSecret,
|
||||||
timestamp: updatedAt,
|
timestamp: updatedAt,
|
||||||
softDelete: true,
|
softDelete: true,
|
||||||
})
|
})
|
||||||
@@ -115,7 +115,7 @@ export class AdminController extends BaseHttpController {
|
|||||||
|
|
||||||
const result = await this.doDeleteSetting.execute({
|
const result = await this.doDeleteSetting.execute({
|
||||||
userUuid,
|
userUuid,
|
||||||
settingName: SettingName.EmailBackupFrequency,
|
settingName: SettingName.NAMES.EmailBackupFrequency,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
|
||||||
import { Request, Response } from 'express'
|
import { Request, Response } from 'express'
|
||||||
import { inject } from 'inversify'
|
import { inject } from 'inversify'
|
||||||
import {
|
import {
|
||||||
@@ -21,7 +20,7 @@ export class SubscriptionSettingsController extends BaseHttpController {
|
|||||||
async getSubscriptionSetting(request: Request, response: Response): Promise<results.JsonResult> {
|
async getSubscriptionSetting(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
const result = await this.doGetSubscriptionSetting.execute({
|
const result = await this.doGetSubscriptionSetting.execute({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: response.locals.user.uuid,
|
||||||
subscriptionSettingName: request.params.subscriptionSettingName as SubscriptionSettingName,
|
subscriptionSettingName: request.params.subscriptionSettingName,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { CrossServiceTokenData, TokenEncoderInterface } from '@standardnotes/security'
|
import { CrossServiceTokenData, TokenEncoderInterface } from '@standardnotes/security'
|
||||||
import { ErrorTag, RoleName } from '@standardnotes/common'
|
import { ErrorTag, RoleName } from '@standardnotes/common'
|
||||||
import { SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
import { Request, Response } from 'express'
|
import { Request, Response } from 'express'
|
||||||
import { inject } from 'inversify'
|
import { inject } from 'inversify'
|
||||||
import {
|
import {
|
||||||
@@ -77,7 +77,7 @@ export class SubscriptionTokensController extends BaseHttpController {
|
|||||||
const user = authenticateTokenResponse.user as User
|
const user = authenticateTokenResponse.user as User
|
||||||
let extensionKey = undefined
|
let extensionKey = undefined
|
||||||
const extensionKeySetting = await this.settingService.findSettingWithDecryptedValue({
|
const extensionKeySetting = await this.settingService.findSettingWithDecryptedValue({
|
||||||
settingName: SettingName.ExtensionKey,
|
settingName: SettingName.NAMES.ExtensionKey,
|
||||||
userUuid: user.uuid,
|
userUuid: user.uuid,
|
||||||
})
|
})
|
||||||
if (extensionKeySetting !== null) {
|
if (extensionKeySetting !== null) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { DomainEventHandlerInterface, ExtensionKeyGrantedEvent } from '@standardnotes/domain-events'
|
import { DomainEventHandlerInterface, ExtensionKeyGrantedEvent } from '@standardnotes/domain-events'
|
||||||
import { SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
import { OfflineFeaturesTokenData } from '@standardnotes/security'
|
import { OfflineFeaturesTokenData } from '@standardnotes/security'
|
||||||
import { ContentDecoderInterface } from '@standardnotes/common'
|
import { ContentDecoderInterface } from '@standardnotes/common'
|
||||||
import { inject, injectable } from 'inversify'
|
import { inject, injectable } from 'inversify'
|
||||||
@@ -54,7 +54,7 @@ export class ExtensionKeyGrantedEventHandler implements DomainEventHandlerInterf
|
|||||||
await this.settingService.createOrReplace({
|
await this.settingService.createOrReplace({
|
||||||
user,
|
user,
|
||||||
props: {
|
props: {
|
||||||
name: SettingName.ExtensionKey,
|
name: SettingName.NAMES.ExtensionKey,
|
||||||
unencryptedValue: event.payload.extensionKey,
|
unencryptedValue: event.payload.extensionKey,
|
||||||
serverEncryptionVersion: EncryptionVersion.Default,
|
serverEncryptionVersion: EncryptionVersion.Default,
|
||||||
sensitive: true,
|
sensitive: true,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { DomainEventHandlerInterface, FileRemovedEvent } from '@standardnotes/domain-events'
|
import { DomainEventHandlerInterface, FileRemovedEvent } from '@standardnotes/domain-events'
|
||||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
import { inject, injectable } from 'inversify'
|
import { inject, injectable } from 'inversify'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ export class FileRemovedEventHandler implements DomainEventHandlerInterface {
|
|||||||
const bytesUsedSetting = await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({
|
const bytesUsedSetting = await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({
|
||||||
userUuid: user.uuid,
|
userUuid: user.uuid,
|
||||||
userSubscriptionUuid: subscription.uuid,
|
userSubscriptionUuid: subscription.uuid,
|
||||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesUsed,
|
subscriptionSettingName: SettingName.NAMES.FileUploadBytesUsed,
|
||||||
})
|
})
|
||||||
if (bytesUsedSetting === null) {
|
if (bytesUsedSetting === null) {
|
||||||
this.logger.warn(`Could not find bytes used setting for user with uuid: ${user.uuid}`)
|
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({
|
await this.subscriptionSettingService.createOrReplace({
|
||||||
userSubscription: subscription,
|
userSubscription: subscription,
|
||||||
props: {
|
props: {
|
||||||
name: SubscriptionSettingName.FileUploadBytesUsed,
|
name: SettingName.NAMES.FileUploadBytesUsed,
|
||||||
unencryptedValue: (+bytesUsed - byteSize).toString(),
|
unencryptedValue: (+bytesUsed - byteSize).toString(),
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { DomainEventHandlerInterface, FileUploadedEvent } from '@standardnotes/domain-events'
|
import { DomainEventHandlerInterface, FileUploadedEvent } from '@standardnotes/domain-events'
|
||||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
import { inject, injectable } from 'inversify'
|
import { inject, injectable } from 'inversify'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ export class FileUploadedEventHandler implements DomainEventHandlerInterface {
|
|||||||
const bytesUsedSetting = await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({
|
const bytesUsedSetting = await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({
|
||||||
userUuid: (await subscription.user).uuid,
|
userUuid: (await subscription.user).uuid,
|
||||||
userSubscriptionUuid: subscription.uuid,
|
userSubscriptionUuid: subscription.uuid,
|
||||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesUsed,
|
subscriptionSettingName: SettingName.NAMES.FileUploadBytesUsed,
|
||||||
})
|
})
|
||||||
if (bytesUsedSetting !== null) {
|
if (bytesUsedSetting !== null) {
|
||||||
bytesUsed = bytesUsedSetting.value as string
|
bytesUsed = bytesUsedSetting.value as string
|
||||||
@@ -56,7 +56,7 @@ export class FileUploadedEventHandler implements DomainEventHandlerInterface {
|
|||||||
await this.subscriptionSettingService.createOrReplace({
|
await this.subscriptionSettingService.createOrReplace({
|
||||||
userSubscription: subscription,
|
userSubscription: subscription,
|
||||||
props: {
|
props: {
|
||||||
name: SubscriptionSettingName.FileUploadBytesUsed,
|
name: SettingName.NAMES.FileUploadBytesUsed,
|
||||||
unencryptedValue: (+bytesUsed + byteSize).toString(),
|
unencryptedValue: (+bytesUsed + byteSize).toString(),
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { DomainEventHandlerInterface, ListedAccountCreatedEvent } from '@standardnotes/domain-events'
|
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 { inject, injectable } from 'inversify'
|
||||||
import { Logger } from 'winston'
|
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 }
|
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({
|
const listedAuthorSecretsSetting = await this.settingService.findSettingWithDecryptedValue({
|
||||||
settingName: SettingName.ListedAuthorSecrets,
|
settingName: SettingName.NAMES.ListedAuthorSecrets,
|
||||||
userUuid: user.uuid,
|
userUuid: user.uuid,
|
||||||
})
|
})
|
||||||
if (listedAuthorSecretsSetting !== null) {
|
if (listedAuthorSecretsSetting !== null) {
|
||||||
const existingSecrets: ListedAuthorSecretsData = JSON.parse(listedAuthorSecretsSetting.value as string)
|
const existingSecrets = JSON.parse(listedAuthorSecretsSetting.value as string)
|
||||||
existingSecrets.push(newSecret)
|
existingSecrets.push(newSecret)
|
||||||
authSecrets = existingSecrets
|
authSecrets = existingSecrets
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ export class ListedAccountCreatedEventHandler implements DomainEventHandlerInter
|
|||||||
await this.settingService.createOrReplace({
|
await this.settingService.createOrReplace({
|
||||||
user,
|
user,
|
||||||
props: {
|
props: {
|
||||||
name: SettingName.ListedAuthorSecrets,
|
name: SettingName.NAMES.ListedAuthorSecrets,
|
||||||
unencryptedValue: JSON.stringify(authSecrets),
|
unencryptedValue: JSON.stringify(authSecrets),
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { DomainEventHandlerInterface, ListedAccountDeletedEvent } from '@standardnotes/domain-events'
|
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 { inject, injectable } from 'inversify'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ export class ListedAccountDeletedEventHandler implements DomainEventHandlerInter
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listedAuthorSecretsSetting = await this.settingService.findSettingWithDecryptedValue({
|
const listedAuthorSecretsSetting = await this.settingService.findSettingWithDecryptedValue({
|
||||||
settingName: SettingName.ListedAuthorSecrets,
|
settingName: SettingName.NAMES.ListedAuthorSecrets,
|
||||||
userUuid: user.uuid,
|
userUuid: user.uuid,
|
||||||
})
|
})
|
||||||
if (listedAuthorSecretsSetting === null) {
|
if (listedAuthorSecretsSetting === null) {
|
||||||
@@ -33,9 +33,9 @@ export class ListedAccountDeletedEventHandler implements DomainEventHandlerInter
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingSecrets: ListedAuthorSecretsData = JSON.parse(listedAuthorSecretsSetting.value as string)
|
const existingSecrets = JSON.parse(listedAuthorSecretsSetting.value as string)
|
||||||
const filteredSecrets = existingSecrets.filter(
|
const filteredSecrets = existingSecrets.filter(
|
||||||
(secret) =>
|
(secret: Record<string, unknown>) =>
|
||||||
secret.authorId !== event.payload.userId ||
|
secret.authorId !== event.payload.userId ||
|
||||||
(secret.authorId === event.payload.userId && secret.hostUrl !== event.payload.hostUrl),
|
(secret.authorId === event.payload.userId && secret.hostUrl !== event.payload.hostUrl),
|
||||||
)
|
)
|
||||||
@@ -43,7 +43,7 @@ export class ListedAccountDeletedEventHandler implements DomainEventHandlerInter
|
|||||||
await this.settingService.createOrReplace({
|
await this.settingService.createOrReplace({
|
||||||
user,
|
user,
|
||||||
props: {
|
props: {
|
||||||
name: SettingName.ListedAuthorSecrets,
|
name: SettingName.NAMES.ListedAuthorSecrets,
|
||||||
unencryptedValue: JSON.stringify(filteredSecrets),
|
unencryptedValue: JSON.stringify(filteredSecrets),
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
|||||||
import { UserSubscription } from '../Subscription/UserSubscription'
|
import { UserSubscription } from '../Subscription/UserSubscription'
|
||||||
import { UserSubscriptionRepositoryInterface } from '../Subscription/UserSubscriptionRepositoryInterface'
|
import { UserSubscriptionRepositoryInterface } from '../Subscription/UserSubscriptionRepositoryInterface'
|
||||||
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
||||||
import { SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
||||||
import { UserSubscriptionType } from '../Subscription/UserSubscriptionType'
|
import { UserSubscriptionType } from '../Subscription/UserSubscriptionType'
|
||||||
import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface'
|
import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface'
|
||||||
@@ -48,7 +48,7 @@ export class SubscriptionReassignedEventHandler implements DomainEventHandlerInt
|
|||||||
await this.settingService.createOrReplace({
|
await this.settingService.createOrReplace({
|
||||||
user,
|
user,
|
||||||
props: {
|
props: {
|
||||||
name: SettingName.ExtensionKey,
|
name: SettingName.NAMES.ExtensionKey,
|
||||||
unencryptedValue: event.payload.extensionKey,
|
unencryptedValue: event.payload.extensionKey,
|
||||||
serverEncryptionVersion: EncryptionVersion.Default,
|
serverEncryptionVersion: EncryptionVersion.Default,
|
||||||
sensitive: true,
|
sensitive: true,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
|||||||
import { OfflineSettingServiceInterface } from '../Setting/OfflineSettingServiceInterface'
|
import { OfflineSettingServiceInterface } from '../Setting/OfflineSettingServiceInterface'
|
||||||
import { ContentDecoderInterface } from '@standardnotes/common'
|
import { ContentDecoderInterface } from '@standardnotes/common'
|
||||||
import { OfflineSettingName } from '../Setting/OfflineSettingName'
|
import { OfflineSettingName } from '../Setting/OfflineSettingName'
|
||||||
import { SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
||||||
import { UserSubscriptionType } from '../Subscription/UserSubscriptionType'
|
import { UserSubscriptionType } from '../Subscription/UserSubscriptionType'
|
||||||
import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface'
|
import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface'
|
||||||
@@ -95,7 +95,7 @@ export class SubscriptionSyncRequestedEventHandler implements DomainEventHandler
|
|||||||
await this.settingService.createOrReplace({
|
await this.settingService.createOrReplace({
|
||||||
user,
|
user,
|
||||||
props: {
|
props: {
|
||||||
name: SettingName.ExtensionKey,
|
name: SettingName.NAMES.ExtensionKey,
|
||||||
unencryptedValue: event.payload.extensionKey,
|
unencryptedValue: event.payload.extensionKey,
|
||||||
serverEncryptionVersion: EncryptionVersion.Default,
|
serverEncryptionVersion: EncryptionVersion.Default,
|
||||||
sensitive: true,
|
sensitive: true,
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import { EphemeralSession } from './EphemeralSession'
|
|||||||
import { RevokedSessionRepositoryInterface } from './RevokedSessionRepositoryInterface'
|
import { RevokedSessionRepositoryInterface } from './RevokedSessionRepositoryInterface'
|
||||||
import { RevokedSession } from './RevokedSession'
|
import { RevokedSession } from './RevokedSession'
|
||||||
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
||||||
import { LogSessionUserAgentOption } from '@standardnotes/settings'
|
|
||||||
import { Setting } from '../Setting/Setting'
|
import { Setting } from '../Setting/Setting'
|
||||||
import { CryptoNode } from '@standardnotes/sncrypto-node'
|
import { CryptoNode } from '@standardnotes/sncrypto-node'
|
||||||
|
|
||||||
@@ -171,7 +170,7 @@ describe('SessionService', () => {
|
|||||||
user.uuid = '123'
|
user.uuid = '123'
|
||||||
|
|
||||||
settingService.findSettingWithDecryptedValue = jest.fn().mockReturnValue({
|
settingService.findSettingWithDecryptedValue = jest.fn().mockReturnValue({
|
||||||
value: LogSessionUserAgentOption.Disabled,
|
value: 'disabled',
|
||||||
} as jest.Mocked<Setting>)
|
} as jest.Mocked<Setting>)
|
||||||
|
|
||||||
const sessionPayload = await createService().createNewSessionForUser({
|
const sessionPayload = await createService().createNewSessionForUser({
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { EphemeralSession } from './EphemeralSession'
|
|||||||
import { RevokedSession } from './RevokedSession'
|
import { RevokedSession } from './RevokedSession'
|
||||||
import { RevokedSessionRepositoryInterface } from './RevokedSessionRepositoryInterface'
|
import { RevokedSessionRepositoryInterface } from './RevokedSessionRepositoryInterface'
|
||||||
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
||||||
import { LogSessionUserAgentOption, SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
import { SessionBody } from '@standardnotes/responses'
|
import { SessionBody } from '@standardnotes/responses'
|
||||||
import { Uuid } from '@standardnotes/common'
|
import { Uuid } from '@standardnotes/common'
|
||||||
import { CryptoNode } from '@standardnotes/sncrypto-node'
|
import { CryptoNode } from '@standardnotes/sncrypto-node'
|
||||||
@@ -291,7 +291,7 @@ export class SessionService implements SessionServiceInterface {
|
|||||||
|
|
||||||
private async isLoggingUserAgentEnabledOnSessions(user: User): Promise<boolean> {
|
private async isLoggingUserAgentEnabledOnSessions(user: User): Promise<boolean> {
|
||||||
const loggingSetting = await this.settingService.findSettingWithDecryptedValue({
|
const loggingSetting = await this.settingService.findSettingWithDecryptedValue({
|
||||||
settingName: SettingName.LogSessionUserAgent,
|
settingName: SettingName.NAMES.LogSessionUserAgent,
|
||||||
userUuid: user.uuid,
|
userUuid: user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -299,6 +299,6 @@ export class SessionService implements SessionServiceInterface {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return loggingSetting.value === LogSessionUserAgentOption.Enabled
|
return loggingSetting.value === 'enabled'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { Uuid } from '@standardnotes/common'
|
import { Uuid } from '@standardnotes/common'
|
||||||
import { SettingName } from '@standardnotes/settings'
|
|
||||||
|
|
||||||
export type FindSettingDTO = {
|
export type FindSettingDTO = {
|
||||||
userUuid: string
|
userUuid: string
|
||||||
settingName: SettingName
|
settingName: string
|
||||||
settingUuid?: Uuid
|
settingUuid?: Uuid
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { Uuid } from '@standardnotes/common'
|
import { Uuid } from '@standardnotes/common'
|
||||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
|
||||||
|
|
||||||
export type FindSubscriptionSettingDTO = {
|
export type FindSubscriptionSettingDTO = {
|
||||||
userUuid: Uuid
|
userUuid: Uuid
|
||||||
userSubscriptionUuid: Uuid
|
userSubscriptionUuid: Uuid
|
||||||
subscriptionSettingName: SubscriptionSettingName
|
subscriptionSettingName: string
|
||||||
settingUuid?: Uuid
|
settingUuid?: Uuid
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,8 @@ import {
|
|||||||
MuteEmailsSettingChangedEvent,
|
MuteEmailsSettingChangedEvent,
|
||||||
UserDisabledSessionUserAgentLoggingEvent,
|
UserDisabledSessionUserAgentLoggingEvent,
|
||||||
} from '@standardnotes/domain-events'
|
} from '@standardnotes/domain-events'
|
||||||
import {
|
import { MuteMarketingEmailsOption } from '@standardnotes/settings'
|
||||||
EmailBackupFrequency,
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
LogSessionUserAgentOption,
|
|
||||||
MuteMarketingEmailsOption,
|
|
||||||
OneDriveBackupFrequency,
|
|
||||||
SettingName,
|
|
||||||
} from '@standardnotes/settings'
|
|
||||||
import 'reflect-metadata'
|
import 'reflect-metadata'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
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 () => {
|
it('should trigger session cleanup if user is disabling session user agent logging', async () => {
|
||||||
const setting = {
|
const setting = {
|
||||||
name: SettingName.LogSessionUserAgent,
|
name: SettingName.NAMES.LogSessionUserAgent,
|
||||||
value: LogSessionUserAgentOption.Disabled,
|
value: 'disabled',
|
||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
|
|
||||||
await createInterpreter().interpretSettingUpdated(setting, user, LogSessionUserAgentOption.Disabled)
|
await createInterpreter().interpretSettingUpdated(setting, user, 'disabled')
|
||||||
|
|
||||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||||
expect(domainEventFactory.createUserDisabledSessionUserAgentLoggingEvent).toHaveBeenCalledWith({
|
expect(domainEventFactory.createUserDisabledSessionUserAgentLoggingEvent).toHaveBeenCalledWith({
|
||||||
@@ -86,11 +81,11 @@ describe('SettingInterpreter', () => {
|
|||||||
|
|
||||||
it('should trigger backup if email backup setting is created - emails not muted', async () => {
|
it('should trigger backup if email backup setting is created - emails not muted', async () => {
|
||||||
const setting = {
|
const setting = {
|
||||||
name: SettingName.EmailBackupFrequency,
|
name: SettingName.NAMES.EmailBackupFrequency,
|
||||||
value: EmailBackupFrequency.Daily,
|
value: 'daily',
|
||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
|
|
||||||
await createInterpreter().interpretSettingUpdated(setting, user, EmailBackupFrequency.Daily)
|
await createInterpreter().interpretSettingUpdated(setting, user, 'daily')
|
||||||
|
|
||||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||||
expect(domainEventFactory.createEmailBackupRequestedEvent).toHaveBeenCalledWith('4-5-6', '', false)
|
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 () => {
|
it('should trigger backup if email backup setting is created - emails muted', async () => {
|
||||||
const setting = {
|
const setting = {
|
||||||
name: SettingName.EmailBackupFrequency,
|
name: SettingName.NAMES.EmailBackupFrequency,
|
||||||
value: EmailBackupFrequency.Daily,
|
value: 'daily',
|
||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue({
|
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue({
|
||||||
name: SettingName.MuteFailedBackupsEmails,
|
name: SettingName.NAMES.MuteFailedBackupsEmails,
|
||||||
uuid: '6-7-8',
|
uuid: '6-7-8',
|
||||||
value: 'muted',
|
value: 'muted',
|
||||||
} as jest.Mocked<Setting>)
|
} as jest.Mocked<Setting>)
|
||||||
|
|
||||||
await createInterpreter().interpretSettingUpdated(setting, user, EmailBackupFrequency.Daily)
|
await createInterpreter().interpretSettingUpdated(setting, user, 'daily')
|
||||||
|
|
||||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||||
expect(domainEventFactory.createEmailBackupRequestedEvent).toHaveBeenCalledWith('4-5-6', '6-7-8', true)
|
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 () => {
|
it('should not trigger backup if email backup setting is disabled', async () => {
|
||||||
const setting = {
|
const setting = {
|
||||||
name: SettingName.EmailBackupFrequency,
|
name: SettingName.NAMES.EmailBackupFrequency,
|
||||||
value: EmailBackupFrequency.Disabled,
|
value: 'disabled',
|
||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null)
|
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(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||||
expect(domainEventFactory.createEmailBackupRequestedEvent).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 () => {
|
it('should trigger cloud backup if dropbox backup setting is created', async () => {
|
||||||
const setting = {
|
const setting = {
|
||||||
name: SettingName.DropboxBackupToken,
|
name: SettingName.NAMES.DropboxBackupToken,
|
||||||
value: 'test-token',
|
value: 'test-token',
|
||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null)
|
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 () => {
|
it('should trigger cloud backup if dropbox backup setting is created - muted emails', async () => {
|
||||||
const setting = {
|
const setting = {
|
||||||
name: SettingName.DropboxBackupToken,
|
name: SettingName.NAMES.DropboxBackupToken,
|
||||||
value: 'test-token',
|
value: 'test-token',
|
||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue({
|
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue({
|
||||||
name: SettingName.MuteFailedCloudBackupsEmails,
|
name: SettingName.NAMES.MuteFailedCloudBackupsEmails,
|
||||||
uuid: '6-7-8',
|
uuid: '6-7-8',
|
||||||
value: 'muted',
|
value: 'muted',
|
||||||
} as jest.Mocked<Setting>)
|
} as jest.Mocked<Setting>)
|
||||||
@@ -170,7 +165,7 @@ describe('SettingInterpreter', () => {
|
|||||||
|
|
||||||
it('should trigger cloud backup if google drive backup setting is created', async () => {
|
it('should trigger cloud backup if google drive backup setting is created', async () => {
|
||||||
const setting = {
|
const setting = {
|
||||||
name: SettingName.GoogleDriveBackupToken,
|
name: SettingName.NAMES.GoogleDriveBackupToken,
|
||||||
value: 'test-token',
|
value: 'test-token',
|
||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null)
|
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 () => {
|
it('should trigger cloud backup if one drive backup setting is created', async () => {
|
||||||
const setting = {
|
const setting = {
|
||||||
name: SettingName.OneDriveBackupToken,
|
name: SettingName.NAMES.OneDriveBackupToken,
|
||||||
value: 'test-token',
|
value: 'test-token',
|
||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null)
|
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 () => {
|
it('should trigger cloud backup if backup frequency setting is updated and a backup token setting is present', async () => {
|
||||||
settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValueOnce({
|
settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValueOnce({
|
||||||
name: SettingName.OneDriveBackupToken,
|
name: SettingName.NAMES.OneDriveBackupToken,
|
||||||
serverEncryptionVersion: 1,
|
serverEncryptionVersion: 1,
|
||||||
value: 'encrypted-backup-token',
|
value: 'encrypted-backup-token',
|
||||||
sensitive: true,
|
sensitive: true,
|
||||||
} as jest.Mocked<Setting>)
|
} as jest.Mocked<Setting>)
|
||||||
const setting = {
|
const setting = {
|
||||||
name: SettingName.OneDriveBackupFrequency,
|
name: SettingName.NAMES.OneDriveBackupFrequency,
|
||||||
serverEncryptionVersion: 0,
|
serverEncryptionVersion: 0,
|
||||||
value: 'daily',
|
value: 'daily',
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
@@ -251,19 +246,19 @@ describe('SettingInterpreter', () => {
|
|||||||
|
|
||||||
it('should not trigger cloud backup if backup frequency setting is updated as disabled', async () => {
|
it('should not trigger cloud backup if backup frequency setting is updated as disabled', async () => {
|
||||||
settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValueOnce({
|
settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValueOnce({
|
||||||
name: SettingName.OneDriveBackupToken,
|
name: SettingName.NAMES.OneDriveBackupToken,
|
||||||
serverEncryptionVersion: 1,
|
serverEncryptionVersion: 1,
|
||||||
value: 'encrypted-backup-token',
|
value: 'encrypted-backup-token',
|
||||||
sensitive: true,
|
sensitive: true,
|
||||||
} as jest.Mocked<Setting>)
|
} as jest.Mocked<Setting>)
|
||||||
const setting = {
|
const setting = {
|
||||||
name: SettingName.OneDriveBackupFrequency,
|
name: SettingName.NAMES.OneDriveBackupFrequency,
|
||||||
serverEncryptionVersion: 0,
|
serverEncryptionVersion: 0,
|
||||||
value: OneDriveBackupFrequency.Disabled,
|
value: 'disabled',
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
|
|
||||||
await createInterpreter().interpretSettingUpdated(setting, user, OneDriveBackupFrequency.Disabled)
|
await createInterpreter().interpretSettingUpdated(setting, user, 'disabled')
|
||||||
|
|
||||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||||
expect(domainEventFactory.createCloudBackupRequestedEvent).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 () => {
|
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)
|
settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValueOnce(null)
|
||||||
const setting = {
|
const setting = {
|
||||||
name: SettingName.OneDriveBackupFrequency,
|
name: SettingName.NAMES.OneDriveBackupFrequency,
|
||||||
serverEncryptionVersion: 0,
|
serverEncryptionVersion: 0,
|
||||||
value: 'daily',
|
value: 'daily',
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
|
|||||||
@@ -1,15 +1,5 @@
|
|||||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||||
import { EmailLevel } from '@standardnotes/domain-core'
|
import { EmailLevel, SettingName } from '@standardnotes/domain-core'
|
||||||
import {
|
|
||||||
DropboxBackupFrequency,
|
|
||||||
EmailBackupFrequency,
|
|
||||||
GoogleDriveBackupFrequency,
|
|
||||||
LogSessionUserAgentOption,
|
|
||||||
MuteFailedBackupsEmailsOption,
|
|
||||||
MuteFailedCloudBackupsEmailsOption,
|
|
||||||
OneDriveBackupFrequency,
|
|
||||||
SettingName,
|
|
||||||
} from '@standardnotes/settings'
|
|
||||||
import { inject, injectable } from 'inversify'
|
import { inject, injectable } from 'inversify'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
import TYPES from '../../Bootstrap/Types'
|
import TYPES from '../../Bootstrap/Types'
|
||||||
@@ -23,22 +13,18 @@ import { SettingRepositoryInterface } from './SettingRepositoryInterface'
|
|||||||
@injectable()
|
@injectable()
|
||||||
export class SettingInterpreter implements SettingInterpreterInterface {
|
export class SettingInterpreter implements SettingInterpreterInterface {
|
||||||
private readonly cloudBackupTokenSettings = [
|
private readonly cloudBackupTokenSettings = [
|
||||||
SettingName.DropboxBackupToken,
|
SettingName.NAMES.DropboxBackupToken,
|
||||||
SettingName.GoogleDriveBackupToken,
|
SettingName.NAMES.GoogleDriveBackupToken,
|
||||||
SettingName.OneDriveBackupToken,
|
SettingName.NAMES.OneDriveBackupToken,
|
||||||
]
|
]
|
||||||
|
|
||||||
private readonly cloudBackupFrequencySettings = [
|
private readonly cloudBackupFrequencySettings = [
|
||||||
SettingName.DropboxBackupFrequency,
|
SettingName.NAMES.DropboxBackupFrequency,
|
||||||
SettingName.GoogleDriveBackupFrequency,
|
SettingName.NAMES.GoogleDriveBackupFrequency,
|
||||||
SettingName.OneDriveBackupFrequency,
|
SettingName.NAMES.OneDriveBackupFrequency,
|
||||||
]
|
]
|
||||||
|
|
||||||
private readonly cloudBackupFrequencyDisabledValues = [
|
private readonly cloudBackupFrequencyDisabledValues = ['disabled']
|
||||||
DropboxBackupFrequency.Disabled,
|
|
||||||
GoogleDriveBackupFrequency.Disabled,
|
|
||||||
OneDriveBackupFrequency.Disabled,
|
|
||||||
]
|
|
||||||
|
|
||||||
private readonly emailSettingToSubscriptionRejectionLevelMap: Map<SettingName, string> = new Map([
|
private readonly emailSettingToSubscriptionRejectionLevelMap: Map<SettingName, string> = new Map([
|
||||||
[SettingName.MuteFailedBackupsEmails, EmailLevel.LEVELS.FailedEmailBackup],
|
[SettingName.MuteFailedBackupsEmails, EmailLevel.LEVELS.FailedEmailBackup],
|
||||||
@@ -77,11 +63,11 @@ export class SettingInterpreter implements SettingInterpreterInterface {
|
|||||||
let userHasEmailsMuted = false
|
let userHasEmailsMuted = false
|
||||||
let muteEmailsSettingUuid = ''
|
let muteEmailsSettingUuid = ''
|
||||||
const muteFailedEmailsBackupSetting = await this.settingRepository.findOneByNameAndUserUuid(
|
const muteFailedEmailsBackupSetting = await this.settingRepository.findOneByNameAndUserUuid(
|
||||||
SettingName.MuteFailedBackupsEmails,
|
SettingName.NAMES.MuteFailedBackupsEmails,
|
||||||
userUuid,
|
userUuid,
|
||||||
)
|
)
|
||||||
if (muteFailedEmailsBackupSetting !== null) {
|
if (muteFailedEmailsBackupSetting !== null) {
|
||||||
userHasEmailsMuted = muteFailedEmailsBackupSetting.value === MuteFailedBackupsEmailsOption.Muted
|
userHasEmailsMuted = muteFailedEmailsBackupSetting.value === 'muted'
|
||||||
muteEmailsSettingUuid = muteFailedEmailsBackupSetting.uuid
|
muteEmailsSettingUuid = muteFailedEmailsBackupSetting.uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,21 +86,19 @@ export class SettingInterpreter implements SettingInterpreterInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private isEnablingEmailBackupSetting(setting: Setting): boolean {
|
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 {
|
private isEnablingCloudBackupSetting(setting: Setting): boolean {
|
||||||
return (
|
return (
|
||||||
(this.cloudBackupFrequencySettings.includes(setting.name as SettingName) ||
|
(this.cloudBackupFrequencySettings.includes(setting.name) ||
|
||||||
this.cloudBackupTokenSettings.includes(setting.name as SettingName)) &&
|
this.cloudBackupTokenSettings.includes(setting.name)) &&
|
||||||
!this.cloudBackupFrequencyDisabledValues.includes(
|
!this.cloudBackupFrequencyDisabledValues.includes(setting.value as string)
|
||||||
setting.value as DropboxBackupFrequency | OneDriveBackupFrequency | GoogleDriveBackupFrequency,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private isDisablingSessionUserAgentLogging(setting: Setting): boolean {
|
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(
|
private async triggerEmailSubscriptionChange(
|
||||||
@@ -144,29 +128,26 @@ export class SettingInterpreter implements SettingInterpreterInterface {
|
|||||||
let cloudProvider
|
let cloudProvider
|
||||||
let tokenSettingName
|
let tokenSettingName
|
||||||
switch (setting.name) {
|
switch (setting.name) {
|
||||||
case SettingName.DropboxBackupToken:
|
case SettingName.NAMES.DropboxBackupToken:
|
||||||
case SettingName.DropboxBackupFrequency:
|
case SettingName.NAMES.DropboxBackupFrequency:
|
||||||
cloudProvider = 'DROPBOX'
|
cloudProvider = 'DROPBOX'
|
||||||
tokenSettingName = SettingName.DropboxBackupToken
|
tokenSettingName = SettingName.NAMES.DropboxBackupToken
|
||||||
break
|
break
|
||||||
case SettingName.GoogleDriveBackupToken:
|
case SettingName.NAMES.GoogleDriveBackupToken:
|
||||||
case SettingName.GoogleDriveBackupFrequency:
|
case SettingName.NAMES.GoogleDriveBackupFrequency:
|
||||||
cloudProvider = 'GOOGLE_DRIVE'
|
cloudProvider = 'GOOGLE_DRIVE'
|
||||||
tokenSettingName = SettingName.GoogleDriveBackupToken
|
tokenSettingName = SettingName.NAMES.GoogleDriveBackupToken
|
||||||
break
|
break
|
||||||
case SettingName.OneDriveBackupToken:
|
case SettingName.NAMES.OneDriveBackupToken:
|
||||||
case SettingName.OneDriveBackupFrequency:
|
case SettingName.NAMES.OneDriveBackupFrequency:
|
||||||
cloudProvider = 'ONE_DRIVE'
|
cloudProvider = 'ONE_DRIVE'
|
||||||
tokenSettingName = SettingName.OneDriveBackupToken
|
tokenSettingName = SettingName.NAMES.OneDriveBackupToken
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
let backupToken = null
|
let backupToken = null
|
||||||
if (this.cloudBackupFrequencySettings.includes(setting.name as SettingName)) {
|
if (this.cloudBackupFrequencySettings.includes(setting.name)) {
|
||||||
const tokenSetting = await this.settingRepository.findLastByNameAndUserUuid(
|
const tokenSetting = await this.settingRepository.findLastByNameAndUserUuid(tokenSettingName as string, userUuid)
|
||||||
tokenSettingName as SettingName,
|
|
||||||
userUuid,
|
|
||||||
)
|
|
||||||
if (tokenSetting !== null) {
|
if (tokenSetting !== null) {
|
||||||
backupToken = await this.settingDecrypter.decryptSettingValue(tokenSetting, userUuid)
|
backupToken = await this.settingDecrypter.decryptSettingValue(tokenSetting, userUuid)
|
||||||
}
|
}
|
||||||
@@ -183,11 +164,11 @@ export class SettingInterpreter implements SettingInterpreterInterface {
|
|||||||
let userHasEmailsMuted = false
|
let userHasEmailsMuted = false
|
||||||
let muteEmailsSettingUuid = ''
|
let muteEmailsSettingUuid = ''
|
||||||
const muteFailedCloudBackupSetting = await this.settingRepository.findOneByNameAndUserUuid(
|
const muteFailedCloudBackupSetting = await this.settingRepository.findOneByNameAndUserUuid(
|
||||||
SettingName.MuteFailedCloudBackupsEmails,
|
SettingName.NAMES.MuteFailedCloudBackupsEmails,
|
||||||
userUuid,
|
userUuid,
|
||||||
)
|
)
|
||||||
if (muteFailedCloudBackupSetting !== null) {
|
if (muteFailedCloudBackupSetting !== null) {
|
||||||
userHasEmailsMuted = muteFailedCloudBackupSetting.value === MuteFailedCloudBackupsEmailsOption.Muted
|
userHasEmailsMuted = muteFailedCloudBackupSetting.value === 'muted'
|
||||||
muteEmailsSettingUuid = muteFailedCloudBackupSetting.uuid
|
muteEmailsSettingUuid = muteFailedCloudBackupSetting.uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
import { ReadStream } from 'fs'
|
import { ReadStream } from 'fs'
|
||||||
|
|
||||||
import { SettingName } from '@standardnotes/settings'
|
|
||||||
import { DeleteSettingDto } from '../UseCase/DeleteSetting/DeleteSettingDto'
|
import { DeleteSettingDto } from '../UseCase/DeleteSetting/DeleteSettingDto'
|
||||||
import { Setting } from './Setting'
|
import { Setting } from './Setting'
|
||||||
|
|
||||||
export interface SettingRepositoryInterface {
|
export interface SettingRepositoryInterface {
|
||||||
findOneByUuid(uuid: string): Promise<Setting | null>
|
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>
|
findOneByNameAndUserUuid(name: string, userUuid: string): Promise<Setting | null>
|
||||||
findLastByNameAndUserUuid(name: string, userUuid: string): Promise<Setting | null>
|
findLastByNameAndUserUuid(name: string, userUuid: string): Promise<Setting | null>
|
||||||
findAllByUserUuid(userUuid: string): Promise<Setting[]>
|
findAllByUserUuid(userUuid: string): Promise<Setting[]>
|
||||||
streamAllByNameAndValue(name: SettingName, value: string): Promise<ReadStream>
|
streamAllByNameAndValue(name: string, value: string): Promise<ReadStream>
|
||||||
deleteByUserUuid(dto: DeleteSettingDto): Promise<void>
|
deleteByUserUuid(dto: DeleteSettingDto): Promise<void>
|
||||||
save(setting: Setting): Promise<Setting>
|
save(setting: Setting): Promise<Setting>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'reflect-metadata'
|
import 'reflect-metadata'
|
||||||
|
|
||||||
import { LogSessionUserAgentOption, MuteSignInEmailsOption, SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
||||||
import { User } from '../User/User'
|
import { User } from '../User/User'
|
||||||
@@ -54,9 +54,9 @@ describe('SettingService', () => {
|
|||||||
settingsAssociationService.getDefaultSettingsAndValuesForNewUser = jest.fn().mockReturnValue(
|
settingsAssociationService.getDefaultSettingsAndValuesForNewUser = jest.fn().mockReturnValue(
|
||||||
new Map([
|
new Map([
|
||||||
[
|
[
|
||||||
SettingName.MuteSignInEmails,
|
SettingName.NAMES.MuteSignInEmails,
|
||||||
{
|
{
|
||||||
value: MuteSignInEmailsOption.NotMuted,
|
value: 'not_muted',
|
||||||
sensitive: 0,
|
sensitive: 0,
|
||||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||||
},
|
},
|
||||||
@@ -67,11 +67,11 @@ describe('SettingService', () => {
|
|||||||
settingsAssociationService.getDefaultSettingsAndValuesForNewVaultAccount = jest.fn().mockReturnValue(
|
settingsAssociationService.getDefaultSettingsAndValuesForNewVaultAccount = jest.fn().mockReturnValue(
|
||||||
new Map([
|
new Map([
|
||||||
[
|
[
|
||||||
SettingName.LogSessionUserAgent,
|
SettingName.NAMES.LogSessionUserAgent,
|
||||||
{
|
{
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||||
value: LogSessionUserAgentOption.Disabled,
|
value: 'disabled',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
]),
|
]),
|
||||||
@@ -173,9 +173,7 @@ describe('SettingService', () => {
|
|||||||
|
|
||||||
settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValue(setting)
|
settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValue(setting)
|
||||||
|
|
||||||
expect(
|
expect(await createService().findSettingWithDecryptedValue({ userUuid: '1-2-3', settingName: 'test' })).toEqual({
|
||||||
await createService().findSettingWithDecryptedValue({ userUuid: '1-2-3', settingName: 'test' as SettingName }),
|
|
||||||
).toEqual({
|
|
||||||
serverEncryptionVersion: 1,
|
serverEncryptionVersion: 1,
|
||||||
value: 'decrypted',
|
value: 'decrypted',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { SettingName } from '@standardnotes/settings'
|
|
||||||
import { inject, injectable } from 'inversify'
|
import { inject, injectable } from 'inversify'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
import TYPES from '../../Bootstrap/Types'
|
import TYPES from '../../Bootstrap/Types'
|
||||||
@@ -74,7 +73,7 @@ export class SettingService implements SettingServiceInterface {
|
|||||||
|
|
||||||
const existing = await this.findSettingWithDecryptedValue({
|
const existing = await this.findSettingWithDecryptedValue({
|
||||||
userUuid: user.uuid,
|
userUuid: user.uuid,
|
||||||
settingName: props.name as SettingName,
|
settingName: props.name,
|
||||||
settingUuid: props.uuid,
|
settingUuid: props.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'reflect-metadata'
|
import 'reflect-metadata'
|
||||||
|
|
||||||
import { SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
import { PermissionName } from '@standardnotes/features'
|
import { PermissionName } from '@standardnotes/features'
|
||||||
|
|
||||||
import { SettingsAssociationService } from './SettingsAssociationService'
|
import { SettingsAssociationService } from './SettingsAssociationService'
|
||||||
@@ -11,52 +11,54 @@ describe('SettingsAssociationService', () => {
|
|||||||
const createService = () => new SettingsAssociationService()
|
const createService = () => new SettingsAssociationService()
|
||||||
|
|
||||||
it('should tell if a setting is mutable by the client', () => {
|
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', () => {
|
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', () => {
|
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', () => {
|
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,
|
EncryptionVersion.Unencrypted,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return default sensitivity for a setting which sensitivity is not strictly defined', () => {
|
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', () => {
|
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', () => {
|
it('should return the default set of settings for a newly registered user', () => {
|
||||||
const settings = createService().getDefaultSettingsAndValuesForNewUser()
|
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'])
|
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', () => {
|
it('should return the default set of settings for a newly registered vault account', () => {
|
||||||
const settings = createService().getDefaultSettingsAndValuesForNewVaultAccount()
|
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(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', () => {
|
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,
|
PermissionName.DailyEmailBackup,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not return a permission name if not associated to a given setting', () => {
|
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 { PermissionName } from '@standardnotes/features'
|
||||||
import {
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
LogSessionUserAgentOption,
|
|
||||||
MuteMarketingEmailsOption,
|
|
||||||
MuteSignInEmailsOption,
|
|
||||||
SettingName,
|
|
||||||
} from '@standardnotes/settings'
|
|
||||||
import { injectable } from 'inversify'
|
import { injectable } from 'inversify'
|
||||||
|
|
||||||
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
||||||
@@ -15,79 +10,79 @@ import { SettingsAssociationServiceInterface } from './SettingsAssociationServic
|
|||||||
@injectable()
|
@injectable()
|
||||||
export class SettingsAssociationService implements SettingsAssociationServiceInterface {
|
export class SettingsAssociationService implements SettingsAssociationServiceInterface {
|
||||||
private readonly UNENCRYPTED_SETTINGS = [
|
private readonly UNENCRYPTED_SETTINGS = [
|
||||||
SettingName.EmailBackupFrequency,
|
SettingName.NAMES.EmailBackupFrequency,
|
||||||
SettingName.MuteFailedBackupsEmails,
|
SettingName.NAMES.MuteFailedBackupsEmails,
|
||||||
SettingName.MuteFailedCloudBackupsEmails,
|
SettingName.NAMES.MuteFailedCloudBackupsEmails,
|
||||||
SettingName.MuteSignInEmails,
|
SettingName.NAMES.MuteSignInEmails,
|
||||||
SettingName.MuteMarketingEmails,
|
SettingName.NAMES.MuteMarketingEmails,
|
||||||
SettingName.DropboxBackupFrequency,
|
SettingName.NAMES.DropboxBackupFrequency,
|
||||||
SettingName.GoogleDriveBackupFrequency,
|
SettingName.NAMES.GoogleDriveBackupFrequency,
|
||||||
SettingName.OneDriveBackupFrequency,
|
SettingName.NAMES.OneDriveBackupFrequency,
|
||||||
SettingName.LogSessionUserAgent,
|
SettingName.NAMES.LogSessionUserAgent,
|
||||||
]
|
]
|
||||||
|
|
||||||
private readonly UNSENSITIVE_SETTINGS = [
|
private readonly UNSENSITIVE_SETTINGS = [
|
||||||
SettingName.DropboxBackupFrequency,
|
SettingName.NAMES.DropboxBackupFrequency,
|
||||||
SettingName.GoogleDriveBackupFrequency,
|
SettingName.NAMES.GoogleDriveBackupFrequency,
|
||||||
SettingName.OneDriveBackupFrequency,
|
SettingName.NAMES.OneDriveBackupFrequency,
|
||||||
SettingName.EmailBackupFrequency,
|
SettingName.NAMES.EmailBackupFrequency,
|
||||||
SettingName.MuteFailedBackupsEmails,
|
SettingName.NAMES.MuteFailedBackupsEmails,
|
||||||
SettingName.MuteFailedCloudBackupsEmails,
|
SettingName.NAMES.MuteFailedCloudBackupsEmails,
|
||||||
SettingName.MuteSignInEmails,
|
SettingName.NAMES.MuteSignInEmails,
|
||||||
SettingName.MuteMarketingEmails,
|
SettingName.NAMES.MuteMarketingEmails,
|
||||||
SettingName.ListedAuthorSecrets,
|
SettingName.NAMES.ListedAuthorSecrets,
|
||||||
SettingName.LogSessionUserAgent,
|
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>([
|
private readonly permissionsAssociatedWithSettings = new Map<string, PermissionName>([
|
||||||
[SettingName.EmailBackupFrequency, PermissionName.DailyEmailBackup],
|
[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,
|
sensitive: false,
|
||||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||||
value: MuteSignInEmailsOption.NotMuted,
|
value: 'not_muted',
|
||||||
replaceable: false,
|
replaceable: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
SettingName.MuteMarketingEmails,
|
SettingName.NAMES.MuteMarketingEmails,
|
||||||
{
|
{
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||||
value: MuteMarketingEmailsOption.NotMuted,
|
value: 'not_muted',
|
||||||
replaceable: false,
|
replaceable: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
SettingName.LogSessionUserAgent,
|
SettingName.NAMES.LogSessionUserAgent,
|
||||||
{
|
{
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||||
value: LogSessionUserAgentOption.Enabled,
|
value: 'enabled',
|
||||||
replaceable: false,
|
replaceable: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
|
|
||||||
private readonly vaultAccountDefaultSettingsOverwrites = new Map<SettingName, SettingDescription>([
|
private readonly vaultAccountDefaultSettingsOverwrites = new Map<string, SettingDescription>([
|
||||||
[
|
[
|
||||||
SettingName.LogSessionUserAgent,
|
SettingName.NAMES.LogSessionUserAgent,
|
||||||
{
|
{
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||||
value: LogSessionUserAgentOption.Disabled,
|
value: 'disabled',
|
||||||
replaceable: false,
|
replaceable: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
|
|
||||||
isSettingMutableByClient(settingName: SettingName): boolean {
|
isSettingMutableByClient(settingName: string): boolean {
|
||||||
if (this.CLIENT_IMMUTABLE_SETTINGS.includes(settingName)) {
|
if (this.CLIENT_IMMUTABLE_SETTINGS.includes(settingName)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -95,7 +90,7 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
getSensitivityForSetting(settingName: SettingName): boolean {
|
getSensitivityForSetting(settingName: string): boolean {
|
||||||
if (this.UNSENSITIVE_SETTINGS.includes(settingName)) {
|
if (this.UNSENSITIVE_SETTINGS.includes(settingName)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -103,7 +98,7 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
getEncryptionVersionForSetting(settingName: SettingName): EncryptionVersion {
|
getEncryptionVersionForSetting(settingName: string): EncryptionVersion {
|
||||||
if (this.UNENCRYPTED_SETTINGS.includes(settingName)) {
|
if (this.UNENCRYPTED_SETTINGS.includes(settingName)) {
|
||||||
return EncryptionVersion.Unencrypted
|
return EncryptionVersion.Unencrypted
|
||||||
}
|
}
|
||||||
@@ -111,7 +106,7 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt
|
|||||||
return EncryptionVersion.Default
|
return EncryptionVersion.Default
|
||||||
}
|
}
|
||||||
|
|
||||||
getPermissionAssociatedWithSetting(settingName: SettingName): PermissionName | undefined {
|
getPermissionAssociatedWithSetting(settingName: string): PermissionName | undefined {
|
||||||
if (!this.permissionsAssociatedWithSettings.has(settingName)) {
|
if (!this.permissionsAssociatedWithSettings.has(settingName)) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
@@ -119,11 +114,11 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt
|
|||||||
return this.permissionsAssociatedWithSettings.get(settingName)
|
return this.permissionsAssociatedWithSettings.get(settingName)
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultSettingsAndValuesForNewUser(): Map<SettingName, SettingDescription> {
|
getDefaultSettingsAndValuesForNewUser(): Map<string, SettingDescription> {
|
||||||
return this.defaultSettings
|
return this.defaultSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultSettingsAndValuesForNewVaultAccount(): Map<SettingName, SettingDescription> {
|
getDefaultSettingsAndValuesForNewVaultAccount(): Map<string, SettingDescription> {
|
||||||
const defaultVaultSettings = new Map(this.defaultSettings)
|
const defaultVaultSettings = new Map(this.defaultSettings)
|
||||||
|
|
||||||
for (const vaultAccountDefaultSettingOverwriteKey of this.vaultAccountDefaultSettingsOverwrites.keys()) {
|
for (const vaultAccountDefaultSettingOverwriteKey of this.vaultAccountDefaultSettingsOverwrites.keys()) {
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import { PermissionName } from '@standardnotes/features'
|
import { PermissionName } from '@standardnotes/features'
|
||||||
import { SettingName, SubscriptionSettingName } from '@standardnotes/settings'
|
|
||||||
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
||||||
import { SettingDescription } from './SettingDescription'
|
import { SettingDescription } from './SettingDescription'
|
||||||
|
|
||||||
export interface SettingsAssociationServiceInterface {
|
export interface SettingsAssociationServiceInterface {
|
||||||
getDefaultSettingsAndValuesForNewUser(): Map<SettingName, SettingDescription>
|
getDefaultSettingsAndValuesForNewUser(): Map<string, SettingDescription>
|
||||||
getDefaultSettingsAndValuesForNewVaultAccount(): Map<SettingName, SettingDescription>
|
getDefaultSettingsAndValuesForNewVaultAccount(): Map<string, SettingDescription>
|
||||||
getPermissionAssociatedWithSetting(settingName: SettingName): PermissionName | undefined
|
getPermissionAssociatedWithSetting(settingName: string): PermissionName | undefined
|
||||||
getEncryptionVersionForSetting(settingName: SettingName): EncryptionVersion
|
getEncryptionVersionForSetting(settingName: string): EncryptionVersion
|
||||||
getSensitivityForSetting(settingName: SettingName): boolean
|
getSensitivityForSetting(settingName: string): boolean
|
||||||
isSettingMutableByClient(settingName: SettingName | SubscriptionSettingName): boolean
|
isSettingMutableByClient(settingName: string): boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'reflect-metadata'
|
import 'reflect-metadata'
|
||||||
|
|
||||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ describe('SubscriptionSettingService', () => {
|
|||||||
subscriptionSettingsAssociationService.getDefaultSettingsAndValuesForSubscriptionName = jest.fn().mockReturnValue(
|
subscriptionSettingsAssociationService.getDefaultSettingsAndValuesForSubscriptionName = jest.fn().mockReturnValue(
|
||||||
new Map([
|
new Map([
|
||||||
[
|
[
|
||||||
SubscriptionSettingName.FileUploadBytesUsed,
|
SettingName.NAMES.FileUploadBytesUsed,
|
||||||
{
|
{
|
||||||
value: '0',
|
value: '0',
|
||||||
sensitive: 0,
|
sensitive: 0,
|
||||||
@@ -102,7 +102,7 @@ describe('SubscriptionSettingService', () => {
|
|||||||
subscriptionSettingsAssociationService.getDefaultSettingsAndValuesForSubscriptionName = jest.fn().mockReturnValue(
|
subscriptionSettingsAssociationService.getDefaultSettingsAndValuesForSubscriptionName = jest.fn().mockReturnValue(
|
||||||
new Map([
|
new Map([
|
||||||
[
|
[
|
||||||
SubscriptionSettingName.FileUploadBytesUsed,
|
SettingName.NAMES.FileUploadBytesUsed,
|
||||||
{
|
{
|
||||||
value: '0',
|
value: '0',
|
||||||
sensitive: 0,
|
sensitive: 0,
|
||||||
@@ -127,7 +127,7 @@ describe('SubscriptionSettingService', () => {
|
|||||||
subscriptionSettingsAssociationService.getDefaultSettingsAndValuesForSubscriptionName = jest.fn().mockReturnValue(
|
subscriptionSettingsAssociationService.getDefaultSettingsAndValuesForSubscriptionName = jest.fn().mockReturnValue(
|
||||||
new Map([
|
new Map([
|
||||||
[
|
[
|
||||||
SubscriptionSettingName.FileUploadBytesUsed,
|
SettingName.NAMES.FileUploadBytesUsed,
|
||||||
{
|
{
|
||||||
value: '0',
|
value: '0',
|
||||||
sensitive: 0,
|
sensitive: 0,
|
||||||
@@ -152,7 +152,7 @@ describe('SubscriptionSettingService', () => {
|
|||||||
subscriptionSettingsAssociationService.getDefaultSettingsAndValuesForSubscriptionName = jest.fn().mockReturnValue(
|
subscriptionSettingsAssociationService.getDefaultSettingsAndValuesForSubscriptionName = jest.fn().mockReturnValue(
|
||||||
new Map([
|
new Map([
|
||||||
[
|
[
|
||||||
SubscriptionSettingName.FileUploadBytesUsed,
|
SettingName.NAMES.FileUploadBytesUsed,
|
||||||
{
|
{
|
||||||
value: '0',
|
value: '0',
|
||||||
sensitive: 0,
|
sensitive: 0,
|
||||||
@@ -266,7 +266,7 @@ describe('SubscriptionSettingService', () => {
|
|||||||
await createService().findSubscriptionSettingWithDecryptedValue({
|
await createService().findSubscriptionSettingWithDecryptedValue({
|
||||||
userSubscriptionUuid: '2-3-4',
|
userSubscriptionUuid: '2-3-4',
|
||||||
userUuid: '1-2-3',
|
userUuid: '1-2-3',
|
||||||
subscriptionSettingName: 'test' as SubscriptionSettingName,
|
subscriptionSettingName: 'test',
|
||||||
}),
|
}),
|
||||||
).toEqual({
|
).toEqual({
|
||||||
serverEncryptionVersion: 1,
|
serverEncryptionVersion: 1,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { SubscriptionName, Uuid } from '@standardnotes/common'
|
import { SubscriptionName, Uuid } from '@standardnotes/common'
|
||||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
|
||||||
import { inject, injectable } from 'inversify'
|
import { inject, injectable } from 'inversify'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
@@ -98,7 +97,7 @@ export class SubscriptionSettingService implements SubscriptionSettingServiceInt
|
|||||||
const existing = await this.findSubscriptionSettingWithDecryptedValue({
|
const existing = await this.findSubscriptionSettingWithDecryptedValue({
|
||||||
userUuid: (await userSubscription.user).uuid,
|
userUuid: (await userSubscription.user).uuid,
|
||||||
userSubscriptionUuid: userSubscription.uuid,
|
userSubscriptionUuid: userSubscription.uuid,
|
||||||
subscriptionSettingName: props.name as SubscriptionSettingName,
|
subscriptionSettingName: props.name,
|
||||||
settingUuid: props.uuid,
|
settingUuid: props.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -128,7 +127,7 @@ export class SubscriptionSettingService implements SubscriptionSettingServiceInt
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async findPreviousSubscriptionSetting(
|
private async findPreviousSubscriptionSetting(
|
||||||
settingName: SubscriptionSettingName,
|
settingName: string,
|
||||||
currentUserSubscriptionUuid: Uuid,
|
currentUserSubscriptionUuid: Uuid,
|
||||||
userUuid: Uuid,
|
userUuid: Uuid,
|
||||||
): Promise<SubscriptionSetting | null> {
|
): Promise<SubscriptionSetting | null> {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'reflect-metadata'
|
import 'reflect-metadata'
|
||||||
|
|
||||||
import { RoleName, SubscriptionName } from '@standardnotes/common'
|
import { RoleName, SubscriptionName } from '@standardnotes/common'
|
||||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
import { PermissionName } from '@standardnotes/features'
|
import { PermissionName } from '@standardnotes/features'
|
||||||
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
||||||
@@ -50,14 +50,11 @@ describe('SubscriptionSettingsAssociationService', () => {
|
|||||||
|
|
||||||
const flatSettings = [
|
const flatSettings = [
|
||||||
...(
|
...(
|
||||||
settings as Map<
|
settings as Map<string, { value: string; sensitive: boolean; serverEncryptionVersion: EncryptionVersion }>
|
||||||
SubscriptionSettingName,
|
|
||||||
{ value: string; sensitive: boolean; serverEncryptionVersion: EncryptionVersion }
|
|
||||||
>
|
|
||||||
).keys(),
|
).keys(),
|
||||||
]
|
]
|
||||||
expect(flatSettings).toEqual(['FILE_UPLOAD_BYTES_USED', 'FILE_UPLOAD_BYTES_LIMIT'])
|
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,
|
sensitive: false,
|
||||||
serverEncryptionVersion: 0,
|
serverEncryptionVersion: 0,
|
||||||
value: '107374182400',
|
value: '107374182400',
|
||||||
@@ -78,14 +75,11 @@ describe('SubscriptionSettingsAssociationService', () => {
|
|||||||
|
|
||||||
const flatSettings = [
|
const flatSettings = [
|
||||||
...(
|
...(
|
||||||
settings as Map<
|
settings as Map<string, { value: string; sensitive: boolean; serverEncryptionVersion: EncryptionVersion }>
|
||||||
SubscriptionSettingName,
|
|
||||||
{ value: string; sensitive: boolean; serverEncryptionVersion: EncryptionVersion }
|
|
||||||
>
|
|
||||||
).keys(),
|
).keys(),
|
||||||
]
|
]
|
||||||
expect(flatSettings).toEqual(['FILE_UPLOAD_BYTES_USED', 'FILE_UPLOAD_BYTES_LIMIT'])
|
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,
|
sensitive: false,
|
||||||
serverEncryptionVersion: 0,
|
serverEncryptionVersion: 0,
|
||||||
value: '104857600',
|
value: '104857600',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { RoleName, SubscriptionName } from '@standardnotes/common'
|
import { RoleName, SubscriptionName } from '@standardnotes/common'
|
||||||
import { PermissionName } from '@standardnotes/features'
|
import { PermissionName } from '@standardnotes/features'
|
||||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
import { inject, injectable } from 'inversify'
|
import { inject, injectable } from 'inversify'
|
||||||
|
|
||||||
import TYPES from '../../Bootstrap/Types'
|
import TYPES from '../../Bootstrap/Types'
|
||||||
@@ -19,15 +19,12 @@ export class SubscriptionSettingsAssociationService implements SubscriptionSetti
|
|||||||
@inject(TYPES.RoleRepository) private roleRepository: RoleRepositoryInterface,
|
@inject(TYPES.RoleRepository) private roleRepository: RoleRepositoryInterface,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private readonly settingsToSubscriptionNameMap = new Map<
|
private readonly settingsToSubscriptionNameMap = new Map<SubscriptionName, Map<string, SettingDescription>>([
|
||||||
SubscriptionName,
|
|
||||||
Map<SubscriptionSettingName, SettingDescription>
|
|
||||||
>([
|
|
||||||
[
|
[
|
||||||
SubscriptionName.PlusPlan,
|
SubscriptionName.PlusPlan,
|
||||||
new Map([
|
new Map([
|
||||||
[
|
[
|
||||||
SubscriptionSettingName.FileUploadBytesUsed,
|
SettingName.NAMES.FileUploadBytesUsed,
|
||||||
{ sensitive: false, serverEncryptionVersion: EncryptionVersion.Unencrypted, value: '0', replaceable: false },
|
{ sensitive: false, serverEncryptionVersion: EncryptionVersion.Unencrypted, value: '0', replaceable: false },
|
||||||
],
|
],
|
||||||
]),
|
]),
|
||||||
@@ -36,7 +33,7 @@ export class SubscriptionSettingsAssociationService implements SubscriptionSetti
|
|||||||
SubscriptionName.ProPlan,
|
SubscriptionName.ProPlan,
|
||||||
new Map([
|
new Map([
|
||||||
[
|
[
|
||||||
SubscriptionSettingName.FileUploadBytesUsed,
|
SettingName.NAMES.FileUploadBytesUsed,
|
||||||
{ sensitive: false, serverEncryptionVersion: EncryptionVersion.Unencrypted, value: '0', replaceable: false },
|
{ sensitive: false, serverEncryptionVersion: EncryptionVersion.Unencrypted, value: '0', replaceable: false },
|
||||||
],
|
],
|
||||||
]),
|
]),
|
||||||
@@ -45,14 +42,14 @@ export class SubscriptionSettingsAssociationService implements SubscriptionSetti
|
|||||||
|
|
||||||
async getDefaultSettingsAndValuesForSubscriptionName(
|
async getDefaultSettingsAndValuesForSubscriptionName(
|
||||||
subscriptionName: SubscriptionName,
|
subscriptionName: SubscriptionName,
|
||||||
): Promise<Map<SubscriptionSettingName, SettingDescription> | undefined> {
|
): Promise<Map<string, SettingDescription> | undefined> {
|
||||||
const defaultSettings = this.settingsToSubscriptionNameMap.get(subscriptionName)
|
const defaultSettings = this.settingsToSubscriptionNameMap.get(subscriptionName)
|
||||||
|
|
||||||
if (defaultSettings === undefined) {
|
if (defaultSettings === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultSettings.set(SubscriptionSettingName.FileUploadBytesLimit, {
|
defaultSettings.set(SettingName.NAMES.FileUploadBytesLimit, {
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||||
value: (await this.getFileUploadLimit(subscriptionName)).toString(),
|
value: (await this.getFileUploadLimit(subscriptionName)).toString(),
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { SubscriptionName } from '@standardnotes/common'
|
import { SubscriptionName } from '@standardnotes/common'
|
||||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
|
||||||
|
|
||||||
import { SettingDescription } from './SettingDescription'
|
import { SettingDescription } from './SettingDescription'
|
||||||
|
|
||||||
export interface SubscriptionSettingsAssociationServiceInterface {
|
export interface SubscriptionSettingsAssociationServiceInterface {
|
||||||
getDefaultSettingsAndValuesForSubscriptionName(
|
getDefaultSettingsAndValuesForSubscriptionName(
|
||||||
subscriptionName: SubscriptionName,
|
subscriptionName: SubscriptionName,
|
||||||
): Promise<Map<SubscriptionSettingName, SettingDescription> | undefined>
|
): Promise<Map<string, SettingDescription> | undefined>
|
||||||
getFileUploadLimit(subscriptionName: SubscriptionName): Promise<number>
|
getFileUploadLimit(subscriptionName: SubscriptionName): Promise<number>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { SubscriptionName } from '@standardnotes/common'
|
|||||||
import { TimerInterface } from '@standardnotes/time'
|
import { TimerInterface } from '@standardnotes/time'
|
||||||
import { TokenEncoderInterface, ValetTokenData } from '@standardnotes/security'
|
import { TokenEncoderInterface, ValetTokenData } from '@standardnotes/security'
|
||||||
import { CreateValetTokenPayload, CreateValetTokenResponseData } from '@standardnotes/responses'
|
import { CreateValetTokenPayload, CreateValetTokenResponseData } from '@standardnotes/responses'
|
||||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
import TYPES from '../../../Bootstrap/Types'
|
import TYPES from '../../../Bootstrap/Types'
|
||||||
import { UseCaseInterface } from '../UseCaseInterface'
|
import { UseCaseInterface } from '../UseCaseInterface'
|
||||||
@@ -56,7 +56,7 @@ export class CreateValetToken implements UseCaseInterface {
|
|||||||
const uploadBytesUsedSetting = await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({
|
const uploadBytesUsedSetting = await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({
|
||||||
userUuid: regularSubscriptionUserUuid,
|
userUuid: regularSubscriptionUserUuid,
|
||||||
userSubscriptionUuid: regularSubscription.uuid,
|
userSubscriptionUuid: regularSubscription.uuid,
|
||||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesUsed,
|
subscriptionSettingName: SettingName.NAMES.FileUploadBytesUsed,
|
||||||
})
|
})
|
||||||
if (uploadBytesUsedSetting !== null) {
|
if (uploadBytesUsedSetting !== null) {
|
||||||
uploadBytesUsed = +(uploadBytesUsedSetting.value as string)
|
uploadBytesUsed = +(uploadBytesUsedSetting.value as string)
|
||||||
@@ -70,7 +70,7 @@ export class CreateValetToken implements UseCaseInterface {
|
|||||||
await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({
|
await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({
|
||||||
userUuid: regularSubscriptionUserUuid,
|
userUuid: regularSubscriptionUserUuid,
|
||||||
userSubscriptionUuid: regularSubscription.uuid,
|
userSubscriptionUuid: regularSubscription.uuid,
|
||||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesLimit,
|
subscriptionSettingName: SettingName.NAMES.FileUploadBytesLimit,
|
||||||
})
|
})
|
||||||
if (overwriteWithUserUploadBytesLimitSetting !== null) {
|
if (overwriteWithUserUploadBytesLimitSetting !== null) {
|
||||||
uploadBytesLimit = +(overwriteWithUserUploadBytesLimitSetting.value as string)
|
uploadBytesLimit = +(overwriteWithUserUploadBytesLimitSetting.value as string)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
import 'reflect-metadata'
|
import 'reflect-metadata'
|
||||||
import { SettingProjector } from '../../../Projection/SettingProjector'
|
import { SettingProjector } from '../../../Projection/SettingProjector'
|
||||||
import { Setting } from '../../Setting/Setting'
|
import { Setting } from '../../Setting/Setting'
|
||||||
@@ -45,12 +45,12 @@ describe('GetSetting', () => {
|
|||||||
it('should not retrieve a sensitive setting for user', async () => {
|
it('should not retrieve a sensitive setting for user', async () => {
|
||||||
setting = {
|
setting = {
|
||||||
sensitive: true,
|
sensitive: true,
|
||||||
name: SettingName.MfaSecret,
|
name: SettingName.NAMES.MfaSecret,
|
||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
|
|
||||||
settingService.findSettingWithDecryptedValue = jest.fn().mockReturnValue(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,
|
success: true,
|
||||||
sensitive: true,
|
sensitive: true,
|
||||||
})
|
})
|
||||||
@@ -59,7 +59,7 @@ describe('GetSetting', () => {
|
|||||||
it('should retrieve a sensitive setting for user if explicitly told to', async () => {
|
it('should retrieve a sensitive setting for user if explicitly told to', async () => {
|
||||||
setting = {
|
setting = {
|
||||||
sensitive: true,
|
sensitive: true,
|
||||||
name: SettingName.MfaSecret,
|
name: SettingName.NAMES.MfaSecret,
|
||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
|
|
||||||
settingService.findSettingWithDecryptedValue = jest.fn().mockReturnValue(setting)
|
settingService.findSettingWithDecryptedValue = jest.fn().mockReturnValue(setting)
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { SettingName } from '@standardnotes/settings'
|
|
||||||
import { inject, injectable } from 'inversify'
|
import { inject, injectable } from 'inversify'
|
||||||
import { GetSettingDto } from './GetSettingDto'
|
import { GetSettingDto } from './GetSettingDto'
|
||||||
import { GetSettingResponse } from './GetSettingResponse'
|
import { GetSettingResponse } from './GetSettingResponse'
|
||||||
@@ -19,7 +18,7 @@ export class GetSetting implements UseCaseInterface {
|
|||||||
|
|
||||||
const setting = await this.settingService.findSettingWithDecryptedValue({
|
const setting = await this.settingService.findSettingWithDecryptedValue({
|
||||||
userUuid,
|
userUuid,
|
||||||
settingName: settingName as SettingName,
|
settingName: settingName,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (setting === null) {
|
if (setting === null) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'reflect-metadata'
|
import 'reflect-metadata'
|
||||||
|
|
||||||
import { SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
import { SettingProjector } from '../../../Projection/SettingProjector'
|
import { SettingProjector } from '../../../Projection/SettingProjector'
|
||||||
import { Setting } from '../../Setting/Setting'
|
import { Setting } from '../../Setting/Setting'
|
||||||
@@ -31,7 +31,7 @@ describe('GetSettings', () => {
|
|||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
|
|
||||||
mfaSetting = {
|
mfaSetting = {
|
||||||
name: SettingName.MfaSecret,
|
name: SettingName.NAMES.MfaSecret,
|
||||||
updatedAt: 122,
|
updatedAt: 122,
|
||||||
sensitive: true,
|
sensitive: true,
|
||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
|
|||||||
+8
-8
@@ -1,6 +1,6 @@
|
|||||||
import 'reflect-metadata'
|
import 'reflect-metadata'
|
||||||
|
|
||||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
import { SubscriptionSettingProjector } from '../../../Projection/SubscriptionSettingProjector'
|
import { SubscriptionSettingProjector } from '../../../Projection/SubscriptionSettingProjector'
|
||||||
import { SubscriptionSetting } from '../../Setting/SubscriptionSetting'
|
import { SubscriptionSetting } from '../../Setting/SubscriptionSetting'
|
||||||
@@ -54,7 +54,7 @@ describe('GetSubscriptionSetting', () => {
|
|||||||
expect(
|
expect(
|
||||||
await createUseCase().execute({
|
await createUseCase().execute({
|
||||||
userUuid: '1-2-3',
|
userUuid: '1-2-3',
|
||||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesUsed,
|
subscriptionSettingName: SettingName.NAMES.FileUploadBytesUsed,
|
||||||
}),
|
}),
|
||||||
).toEqual({
|
).toEqual({
|
||||||
success: true,
|
success: true,
|
||||||
@@ -70,7 +70,7 @@ describe('GetSubscriptionSetting', () => {
|
|||||||
expect(
|
expect(
|
||||||
await createUseCase().execute({
|
await createUseCase().execute({
|
||||||
userUuid: '1-2-3',
|
userUuid: '1-2-3',
|
||||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesLimit,
|
subscriptionSettingName: SettingName.NAMES.FileUploadBytesLimit,
|
||||||
}),
|
}),
|
||||||
).toEqual({
|
).toEqual({
|
||||||
success: false,
|
success: false,
|
||||||
@@ -86,7 +86,7 @@ describe('GetSubscriptionSetting', () => {
|
|||||||
expect(
|
expect(
|
||||||
await createUseCase().execute({
|
await createUseCase().execute({
|
||||||
userUuid: '1-2-3',
|
userUuid: '1-2-3',
|
||||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesLimit,
|
subscriptionSettingName: SettingName.NAMES.FileUploadBytesLimit,
|
||||||
}),
|
}),
|
||||||
).toEqual({
|
).toEqual({
|
||||||
success: false,
|
success: false,
|
||||||
@@ -99,7 +99,7 @@ describe('GetSubscriptionSetting', () => {
|
|||||||
it('should not retrieve a sensitive setting for user', async () => {
|
it('should not retrieve a sensitive setting for user', async () => {
|
||||||
subscriptionSetting = {
|
subscriptionSetting = {
|
||||||
sensitive: true,
|
sensitive: true,
|
||||||
name: SubscriptionSettingName.FileUploadBytesLimit,
|
name: SettingName.NAMES.FileUploadBytesLimit,
|
||||||
} as jest.Mocked<SubscriptionSetting>
|
} as jest.Mocked<SubscriptionSetting>
|
||||||
|
|
||||||
subscriptionSettingService.findSubscriptionSettingWithDecryptedValue = jest
|
subscriptionSettingService.findSubscriptionSettingWithDecryptedValue = jest
|
||||||
@@ -109,7 +109,7 @@ describe('GetSubscriptionSetting', () => {
|
|||||||
expect(
|
expect(
|
||||||
await createUseCase().execute({
|
await createUseCase().execute({
|
||||||
userUuid: '1-2-3',
|
userUuid: '1-2-3',
|
||||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesLimit,
|
subscriptionSettingName: SettingName.NAMES.FileUploadBytesLimit,
|
||||||
}),
|
}),
|
||||||
).toEqual({
|
).toEqual({
|
||||||
success: true,
|
success: true,
|
||||||
@@ -120,7 +120,7 @@ describe('GetSubscriptionSetting', () => {
|
|||||||
it('should retrieve a sensitive setting for user if explicitly told to', async () => {
|
it('should retrieve a sensitive setting for user if explicitly told to', async () => {
|
||||||
subscriptionSetting = {
|
subscriptionSetting = {
|
||||||
sensitive: true,
|
sensitive: true,
|
||||||
name: SubscriptionSettingName.FileUploadBytesLimit,
|
name: SettingName.NAMES.FileUploadBytesLimit,
|
||||||
} as jest.Mocked<SubscriptionSetting>
|
} as jest.Mocked<SubscriptionSetting>
|
||||||
|
|
||||||
subscriptionSettingService.findSubscriptionSettingWithDecryptedValue = jest
|
subscriptionSettingService.findSubscriptionSettingWithDecryptedValue = jest
|
||||||
@@ -130,7 +130,7 @@ describe('GetSubscriptionSetting', () => {
|
|||||||
expect(
|
expect(
|
||||||
await createUseCase().execute({
|
await createUseCase().execute({
|
||||||
userUuid: '1-2-3',
|
userUuid: '1-2-3',
|
||||||
subscriptionSettingName: SubscriptionSettingName.FileUploadBytesLimit,
|
subscriptionSettingName: SettingName.NAMES.FileUploadBytesLimit,
|
||||||
allowSensitiveRetrieval: true,
|
allowSensitiveRetrieval: true,
|
||||||
}),
|
}),
|
||||||
).toEqual({
|
).toEqual({
|
||||||
|
|||||||
+1
-2
@@ -1,8 +1,7 @@
|
|||||||
import { Uuid } from '@standardnotes/common'
|
import { Uuid } from '@standardnotes/common'
|
||||||
import { SubscriptionSettingName } from '@standardnotes/settings'
|
|
||||||
|
|
||||||
export type GetSubscriptionSettingDTO = {
|
export type GetSubscriptionSettingDTO = {
|
||||||
userUuid: Uuid
|
userUuid: Uuid
|
||||||
subscriptionSettingName: SubscriptionSettingName
|
subscriptionSettingName: string
|
||||||
allowSensitiveRetrieval?: boolean
|
allowSensitiveRetrieval?: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { SimpleSetting } from '../../Setting/SimpleSetting'
|
|||||||
import { User } from '../../User/User'
|
import { User } from '../../User/User'
|
||||||
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||||
import { UpdateSetting } from './UpdateSetting'
|
import { UpdateSetting } from './UpdateSetting'
|
||||||
import { SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
describe('UpdateSetting', () => {
|
describe('UpdateSetting', () => {
|
||||||
let settingService: SettingServiceInterface
|
let settingService: SettingServiceInterface
|
||||||
@@ -59,7 +59,7 @@ describe('UpdateSetting', () => {
|
|||||||
|
|
||||||
it('should create a setting', async () => {
|
it('should create a setting', async () => {
|
||||||
const props = {
|
const props = {
|
||||||
name: SettingName.ExtensionKey,
|
name: SettingName.NAMES.ExtensionKey,
|
||||||
unencryptedValue: 'test-setting-value',
|
unencryptedValue: 'test-setting-value',
|
||||||
serverEncryptionVersion: EncryptionVersion.Default,
|
serverEncryptionVersion: EncryptionVersion.Default,
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
@@ -88,7 +88,7 @@ describe('UpdateSetting', () => {
|
|||||||
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
name: SettingName.ExtensionKey,
|
name: SettingName.NAMES.ExtensionKey,
|
||||||
unencryptedValue: 'test-setting-value',
|
unencryptedValue: 'test-setting-value',
|
||||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
@@ -136,7 +136,7 @@ describe('UpdateSetting', () => {
|
|||||||
roleService.userHasPermission = jest.fn().mockReturnValue(false)
|
roleService.userHasPermission = jest.fn().mockReturnValue(false)
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
name: SettingName.ExtensionKey,
|
name: SettingName.NAMES.ExtensionKey,
|
||||||
unencryptedValue: 'test-setting-value',
|
unencryptedValue: 'test-setting-value',
|
||||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
@@ -159,7 +159,7 @@ describe('UpdateSetting', () => {
|
|||||||
settingsAssociationService.isSettingMutableByClient = jest.fn().mockReturnValue(false)
|
settingsAssociationService.isSettingMutableByClient = jest.fn().mockReturnValue(false)
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
name: SettingName.ExtensionKey,
|
name: SettingName.NAMES.ExtensionKey,
|
||||||
unencryptedValue: 'test-setting-value',
|
unencryptedValue: 'test-setting-value',
|
||||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { SettingProjector } from '../../../Projection/SettingProjector'
|
|||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
import { SettingServiceInterface } from '../../Setting/SettingServiceInterface'
|
import { SettingServiceInterface } from '../../Setting/SettingServiceInterface'
|
||||||
import { User } from '../../User/User'
|
import { User } from '../../User/User'
|
||||||
import { SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
import { RoleServiceInterface } from '../../Role/RoleServiceInterface'
|
import { RoleServiceInterface } from '../../Role/RoleServiceInterface'
|
||||||
import { SettingsAssociationServiceInterface } from '../../Setting/SettingsAssociationServiceInterface'
|
import { SettingsAssociationServiceInterface } from '../../Setting/SettingsAssociationServiceInterface'
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ export class UpdateSetting implements UseCaseInterface {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(dto: UpdateSettingDto): Promise<UpdateSettingResponse> {
|
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 {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
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 {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
@@ -61,10 +61,8 @@ export class UpdateSetting implements UseCaseInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
props.serverEncryptionVersion = this.settingsAssociationService.getEncryptionVersionForSetting(
|
props.serverEncryptionVersion = this.settingsAssociationService.getEncryptionVersionForSetting(props.name)
|
||||||
props.name as SettingName,
|
props.sensitive = this.settingsAssociationService.getSensitivityForSetting(props.name)
|
||||||
)
|
|
||||||
props.sensitive = this.settingsAssociationService.getSensitivityForSetting(props.name as SettingName)
|
|
||||||
|
|
||||||
const response = await this.settingService.createOrReplace({
|
const response = await this.settingService.createOrReplace({
|
||||||
user,
|
user,
|
||||||
@@ -91,8 +89,8 @@ export class UpdateSetting implements UseCaseInterface {
|
|||||||
throw new Error(`Unrecognized status: ${exhaustiveCheck}!`)
|
throw new Error(`Unrecognized status: ${exhaustiveCheck}!`)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async userHasPermissionToUpdateSetting(user: User, settingName: SettingName): Promise<boolean> {
|
private async userHasPermissionToUpdateSetting(user: User, settingName: string): Promise<boolean> {
|
||||||
const settingIsMutableByClient = await this.settingsAssociationService.isSettingMutableByClient(settingName)
|
const settingIsMutableByClient = this.settingsAssociationService.isSettingMutableByClient(settingName)
|
||||||
if (!settingIsMutableByClient) {
|
if (!settingIsMutableByClient) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
|||||||
import { VerifyMFA } from './VerifyMFA'
|
import { VerifyMFA } from './VerifyMFA'
|
||||||
import { Setting } from '../Setting/Setting'
|
import { Setting } from '../Setting/Setting'
|
||||||
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
||||||
import { SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
import { SelectorInterface } from '@standardnotes/security'
|
import { SelectorInterface } from '@standardnotes/security'
|
||||||
import { LockRepositoryInterface } from '../User/LockRepositoryInterface'
|
import { LockRepositoryInterface } from '../User/LockRepositoryInterface'
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ describe('VerifyMFA', () => {
|
|||||||
lockRepository.lockSuccessfullOTP = jest.fn()
|
lockRepository.lockSuccessfullOTP = jest.fn()
|
||||||
|
|
||||||
setting = {
|
setting = {
|
||||||
name: SettingName.MfaSecret,
|
name: SettingName.NAMES.MfaSecret,
|
||||||
value: 'shhhh',
|
value: 'shhhh',
|
||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ describe('VerifyMFA', () => {
|
|||||||
|
|
||||||
it('should pass MFA verification if user has MFA deleted', async () => {
|
it('should pass MFA verification if user has MFA deleted', async () => {
|
||||||
setting = {
|
setting = {
|
||||||
name: SettingName.MfaSecret,
|
name: SettingName.NAMES.MfaSecret,
|
||||||
value: null,
|
value: null,
|
||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
|
|
||||||
@@ -148,7 +148,7 @@ describe('VerifyMFA', () => {
|
|||||||
|
|
||||||
it('should not pass MFA verification if mfa is not correct', async () => {
|
it('should not pass MFA verification if mfa is not correct', async () => {
|
||||||
setting = {
|
setting = {
|
||||||
name: SettingName.MfaSecret,
|
name: SettingName.NAMES.MfaSecret,
|
||||||
value: 'shhhh2',
|
value: 'shhhh2',
|
||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as crypto from 'crypto'
|
import * as crypto from 'crypto'
|
||||||
import { ErrorTag } from '@standardnotes/common'
|
import { ErrorTag } from '@standardnotes/common'
|
||||||
import { SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { inject, injectable } from 'inversify'
|
import { inject, injectable } from 'inversify'
|
||||||
import { authenticator } from 'otplib'
|
import { authenticator } from 'otplib'
|
||||||
@@ -50,7 +50,7 @@ export class VerifyMFA implements UseCaseInterface {
|
|||||||
|
|
||||||
const mfaSecret = await this.settingService.findSettingWithDecryptedValue({
|
const mfaSecret = await this.settingService.findSettingWithDecryptedValue({
|
||||||
userUuid: user.uuid,
|
userUuid: user.uuid,
|
||||||
settingName: SettingName.MfaSecret,
|
settingName: SettingName.NAMES.MfaSecret,
|
||||||
})
|
})
|
||||||
if (mfaSecret === null || mfaSecret.value === null) {
|
if (mfaSecret === null || mfaSecret.value === null) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import { UserSubscription } from '../../Subscription/UserSubscription'
|
|||||||
import { UserSubscriptionRepositoryInterface } from '../../Subscription/UserSubscriptionRepositoryInterface'
|
import { UserSubscriptionRepositoryInterface } from '../../Subscription/UserSubscriptionRepositoryInterface'
|
||||||
|
|
||||||
import { VerifyPredicate } from './VerifyPredicate'
|
import { VerifyPredicate } from './VerifyPredicate'
|
||||||
import { EmailBackupFrequency } from '@standardnotes/settings'
|
|
||||||
|
|
||||||
describe('VerifyPredicate', () => {
|
describe('VerifyPredicate', () => {
|
||||||
let settingRepository: SettingRepositoryInterface
|
let settingRepository: SettingRepositoryInterface
|
||||||
@@ -30,7 +29,7 @@ describe('VerifyPredicate', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should tell that a user has enabled email backups', async () => {
|
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)
|
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(setting)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -44,7 +43,7 @@ describe('VerifyPredicate', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should tell that a user has disabled email backups', async () => {
|
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)
|
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(setting)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Uuid } from '@standardnotes/common'
|
import { Uuid } from '@standardnotes/common'
|
||||||
import { PredicateName, PredicateVerificationResult } from '@standardnotes/predicates'
|
import { PredicateName, PredicateVerificationResult } from '@standardnotes/predicates'
|
||||||
import { EmailBackupFrequency, SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/domain-core'
|
||||||
import { inject, injectable } from 'inversify'
|
import { inject, injectable } from 'inversify'
|
||||||
|
|
||||||
import TYPES from '../../../Bootstrap/Types'
|
import TYPES from '../../../Bootstrap/Types'
|
||||||
@@ -40,9 +40,12 @@ export class VerifyPredicate implements UseCaseInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async hasUserEnabledEmailBackups(userUuid: Uuid): Promise<boolean> {
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { SettingName } from '@standardnotes/settings'
|
|
||||||
import { ReadStream } from 'fs'
|
import { ReadStream } from 'fs'
|
||||||
import { inject, injectable } from 'inversify'
|
import { inject, injectable } from 'inversify'
|
||||||
import { Repository } from 'typeorm'
|
import { Repository } from 'typeorm'
|
||||||
@@ -18,7 +17,7 @@ export class MySQLSettingRepository implements SettingRepositoryInterface {
|
|||||||
return this.ormRepository.save(setting)
|
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
|
return this.ormRepository
|
||||||
.createQueryBuilder('setting')
|
.createQueryBuilder('setting')
|
||||||
.where('setting.uuid = :uuid AND setting.name IN (:...names)', {
|
.where('setting.uuid = :uuid AND setting.name IN (:...names)', {
|
||||||
@@ -28,7 +27,7 @@ export class MySQLSettingRepository implements SettingRepositoryInterface {
|
|||||||
.getOne()
|
.getOne()
|
||||||
}
|
}
|
||||||
|
|
||||||
async streamAllByNameAndValue(name: SettingName, value: string): Promise<ReadStream> {
|
async streamAllByNameAndValue(name: string, value: string): Promise<ReadStream> {
|
||||||
return this.ormRepository
|
return this.ormRepository
|
||||||
.createQueryBuilder('setting')
|
.createQueryBuilder('setting')
|
||||||
.where('setting.name = :name AND setting.value = :value', {
|
.where('setting.name = :name AND setting.value = :value', {
|
||||||
|
|||||||
@@ -35,6 +35,19 @@ describe('RoleNameCollection', () => {
|
|||||||
expect(valueOrError.getValue().equals(roles2)).toBeFalsy()
|
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', () => {
|
it('should tell if collections are equal', () => {
|
||||||
const roles1 = [
|
const roles1 = [
|
||||||
RoleName.create(RoleName.NAMES.ProUser).getValue(),
|
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/RoleNameCollectionProps'
|
||||||
export * from './Common/Username'
|
export * from './Common/Username'
|
||||||
export * from './Common/UsernameProps'
|
export * from './Common/UsernameProps'
|
||||||
|
export * from './Common/Timestamps'
|
||||||
|
export * from './Common/TimestampsProps'
|
||||||
export * from './Common/Uuid'
|
export * from './Common/Uuid'
|
||||||
export * from './Common/UuidProps'
|
export * from './Common/UuidProps'
|
||||||
|
|
||||||
@@ -23,8 +25,14 @@ export * from './Core/ValueObjectProps'
|
|||||||
export * from './Email/EmailLevel'
|
export * from './Email/EmailLevel'
|
||||||
export * from './Email/EmailLevelProps'
|
export * from './Email/EmailLevelProps'
|
||||||
|
|
||||||
|
export * from './Encryption/EncryptionVersion'
|
||||||
|
export * from './Encryption/EncryptionVersionProps'
|
||||||
|
|
||||||
export * from './Mapping/MapperInterface'
|
export * from './Mapping/MapperInterface'
|
||||||
|
|
||||||
|
export * from './Setting/SettingName'
|
||||||
|
export * from './Setting/SettingNameProps'
|
||||||
|
|
||||||
export * from './Subscription/SubscriptionPlanName'
|
export * from './Subscription/SubscriptionPlanName'
|
||||||
export * from './Subscription/SubscriptionPlanNameProps'
|
export * from './Subscription/SubscriptionPlanNameProps'
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import 'reflect-metadata'
|
||||||
|
|
||||||
|
import 'newrelic'
|
||||||
|
|
||||||
|
import * as Sentry from '@sentry/node'
|
||||||
|
|
||||||
|
import '../src/Infra/InversifyExpress/InversifyExpressHealthCheckController'
|
||||||
|
import '../src/Infra/InversifyExpress/InversifyExpressSettingsController'
|
||||||
|
|
||||||
|
import * as cors from 'cors'
|
||||||
|
import { urlencoded, json, Request, Response, NextFunction, RequestHandler, ErrorRequestHandler } from 'express'
|
||||||
|
import * as winston from 'winston'
|
||||||
|
|
||||||
|
import { InversifyExpressServer } from 'inversify-express-utils'
|
||||||
|
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||||
|
import TYPES from '../src/Bootstrap/Types'
|
||||||
|
import { Env } from '../src/Bootstrap/Env'
|
||||||
|
|
||||||
|
const container = new ContainerConfigLoader()
|
||||||
|
void container.load().then((container) => {
|
||||||
|
const env: Env = new Env()
|
||||||
|
env.load()
|
||||||
|
|
||||||
|
const server = new InversifyExpressServer(container)
|
||||||
|
|
||||||
|
server.setConfig((app) => {
|
||||||
|
app.use((_request: Request, response: Response, next: NextFunction) => {
|
||||||
|
response.setHeader('X-Settings-Version', container.get(TYPES.VERSION))
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
app.use(json())
|
||||||
|
app.use(urlencoded({ extended: true }))
|
||||||
|
app.use(cors())
|
||||||
|
|
||||||
|
if (env.get('SENTRY_DSN', true)) {
|
||||||
|
Sentry.init({
|
||||||
|
dsn: env.get('SENTRY_DSN'),
|
||||||
|
integrations: [new Sentry.Integrations.Http({ tracing: false, breadcrumbs: true })],
|
||||||
|
tracesSampleRate: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(Sentry.Handlers.requestHandler() as RequestHandler)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const logger: winston.Logger = container.get(TYPES.Logger)
|
||||||
|
|
||||||
|
server.setErrorConfig((app) => {
|
||||||
|
if (env.get('SENTRY_DSN', true)) {
|
||||||
|
app.use(Sentry.Handlers.errorHandler() as ErrorRequestHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.use((error: Record<string, unknown>, _request: Request, response: Response, _next: NextFunction) => {
|
||||||
|
logger.error(error.stack)
|
||||||
|
|
||||||
|
response.status(500).send({
|
||||||
|
error: {
|
||||||
|
message:
|
||||||
|
"Unfortunately, we couldn't handle your request. Please try again or contact our support if the error persists.",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const serverInstance = server.build()
|
||||||
|
|
||||||
|
serverInstance.listen(env.get('PORT'))
|
||||||
|
|
||||||
|
logger.info(`Server started on port ${process.env.PORT}`)
|
||||||
|
})
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import 'reflect-metadata'
|
||||||
|
|
||||||
|
import 'newrelic'
|
||||||
|
|
||||||
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
|
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||||
|
import TYPES from '../src/Bootstrap/Types'
|
||||||
|
import { Env } from '../src/Bootstrap/Env'
|
||||||
|
import { DomainEventSubscriberFactoryInterface } from '@standardnotes/domain-events'
|
||||||
|
|
||||||
|
const container = new ContainerConfigLoader()
|
||||||
|
void container.load().then((container) => {
|
||||||
|
const env: Env = new Env()
|
||||||
|
env.load()
|
||||||
|
|
||||||
|
const logger: Logger = container.get(TYPES.Logger)
|
||||||
|
|
||||||
|
logger.info('Starting worker...')
|
||||||
|
|
||||||
|
const subscriberFactory: DomainEventSubscriberFactoryInterface = container.get(TYPES.DomainEventSubscriberFactory)
|
||||||
|
subscriberFactory.create().start()
|
||||||
|
|
||||||
|
setInterval(() => logger.info('Alive and kicking!'), 20 * 60 * 1000)
|
||||||
|
})
|
||||||
@@ -7,4 +7,5 @@ module.exports = {
|
|||||||
transform: {
|
transform: {
|
||||||
...tsjPreset.transform,
|
...tsjPreset.transform,
|
||||||
},
|
},
|
||||||
|
coveragePathIgnorePatterns: ['/Bootstrap/', '/Controller/', '/Infra/', '/Mapping/'],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,64 @@
|
|||||||
{
|
{
|
||||||
"name": "@standardnotes/settings",
|
"name": "@standardnotes/settings-server",
|
||||||
"version": "1.18.4",
|
"version": "1.0.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0 <19.0.0"
|
"node": ">=18.0.0 <19.0.0"
|
||||||
},
|
},
|
||||||
"description": "Settings SDK for Standard Notes projects",
|
"private": true,
|
||||||
|
"description": "Settings Server",
|
||||||
"main": "dist/src/index.js",
|
"main": "dist/src/index.js",
|
||||||
"author": "Standard Notes",
|
"typings": "dist/src/index.d.ts",
|
||||||
"types": "dist/src/index.d.ts",
|
"repository": "git@github.com:standardnotes/server.git",
|
||||||
"files": [
|
"author": "Karol Sójko <karolsojko@standardnotes.com>",
|
||||||
"dist/src/**/*.js",
|
|
||||||
"dist/src/**/*.d.ts"
|
|
||||||
],
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
},
|
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "rm -fr dist",
|
"clean": "rm -fr dist",
|
||||||
"prestart": "yarn clean",
|
"setup:env": "cp .env.sample .env",
|
||||||
"start": "tsc -p tsconfig.json --watch",
|
|
||||||
"build": "tsc --build",
|
"build": "tsc --build",
|
||||||
"lint": "eslint . --ext .ts"
|
"lint": "eslint . --ext .ts",
|
||||||
},
|
"lint:fix": "eslint . --ext .ts --fix",
|
||||||
"devDependencies": {
|
"pretest": "yarn lint && yarn build",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.30.0",
|
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50% --passWithNoTests",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"start": "yarn node dist/bin/server.js",
|
||||||
"typescript": "^4.8.4"
|
"worker": "yarn node dist/bin/worker.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"reflect-metadata": "^0.1.13"
|
"@newrelic/winston-enricher": "^4.0.0",
|
||||||
|
"@sentry/node": "^7.19.0",
|
||||||
|
"@standardnotes/api": "^1.20.13",
|
||||||
|
"@standardnotes/domain-core": "workspace:^",
|
||||||
|
"@standardnotes/domain-events": "workspace:*",
|
||||||
|
"@standardnotes/domain-events-infra": "workspace:*",
|
||||||
|
"@standardnotes/security": "workspace:^",
|
||||||
|
"@standardnotes/time": "workspace:^",
|
||||||
|
"aws-sdk": "^2.1260.0",
|
||||||
|
"cors": "2.8.5",
|
||||||
|
"dotenv": "^16.0.1",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"helmet": "^6.0.0",
|
||||||
|
"inversify": "^6.0.1",
|
||||||
|
"inversify-express-utils": "^6.4.3",
|
||||||
|
"ioredis": "^5.2.4",
|
||||||
|
"mysql2": "^2.3.3",
|
||||||
|
"newrelic": "^9.6.0",
|
||||||
|
"reflect-metadata": "0.1.13",
|
||||||
|
"typeorm": "^0.3.10",
|
||||||
|
"winston": "^3.8.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/cors": "^2.8.9",
|
||||||
|
"@types/dotenv": "^8.2.0",
|
||||||
|
"@types/express": "^4.17.14",
|
||||||
|
"@types/inversify-express-utils": "^2.0.0",
|
||||||
|
"@types/ioredis": "^5.0.0",
|
||||||
|
"@types/jest": "^29.1.1",
|
||||||
|
"@types/newrelic": "^7.0.4",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.29.0",
|
||||||
|
"eslint": "^8.14.0",
|
||||||
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
|
"jest": "^29.1.2",
|
||||||
|
"npm-check-updates": "^16.0.1",
|
||||||
|
"ts-jest": "^29.0.3",
|
||||||
|
"typescript": "^4.8.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,163 @@
|
|||||||
|
import * as winston from 'winston'
|
||||||
|
import Redis from 'ioredis'
|
||||||
|
import * as AWS from 'aws-sdk'
|
||||||
|
import { Container } from 'inversify'
|
||||||
|
import {
|
||||||
|
DomainEventHandlerInterface,
|
||||||
|
DomainEventMessageHandlerInterface,
|
||||||
|
DomainEventSubscriberFactoryInterface,
|
||||||
|
} from '@standardnotes/domain-events'
|
||||||
|
import { MapperInterface } from '@standardnotes/domain-core'
|
||||||
|
import { TokenDecoderInterface, CrossServiceTokenData, TokenDecoder } from '@standardnotes/security'
|
||||||
|
import {
|
||||||
|
RedisDomainEventSubscriberFactory,
|
||||||
|
RedisEventMessageHandler,
|
||||||
|
SQSDomainEventSubscriberFactory,
|
||||||
|
SQSEventMessageHandler,
|
||||||
|
SQSNewRelicEventMessageHandler,
|
||||||
|
} from '@standardnotes/domain-events-infra'
|
||||||
|
|
||||||
|
import { Env } from './Env'
|
||||||
|
import TYPES from './Types'
|
||||||
|
import { AppDataSource } from './DataSource'
|
||||||
|
import { InversifyExpressApiGatewayAuthMiddleware } from '../Infra/InversifyExpress/InversifyExpressApiGatewayAuthMiddleware'
|
||||||
|
import { Repository } from 'typeorm'
|
||||||
|
import { TypeORMSetting } from '../Infra/TypeORM/TypeORMSetting'
|
||||||
|
import { SettingRepositoryInterface } from '../Domain/Setting/SettingRepositoryInterface'
|
||||||
|
import { MySQLSettingRepository } from '../Infra/MySQL/MySQLSettingRepository'
|
||||||
|
import { SettingsController } from '../Controller/SettingsController'
|
||||||
|
import { SettingPersistenceMapper } from '../Mapping/SettingPersistenceMapper'
|
||||||
|
import { Setting } from '../Domain/Setting/Setting'
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const newrelicFormatter = require('@newrelic/winston-enricher')
|
||||||
|
|
||||||
|
export class ContainerConfigLoader {
|
||||||
|
async load(): Promise<Container> {
|
||||||
|
const env: Env = new Env()
|
||||||
|
env.load()
|
||||||
|
|
||||||
|
const container = new Container()
|
||||||
|
|
||||||
|
await AppDataSource.initialize()
|
||||||
|
|
||||||
|
const redisUrl = env.get('REDIS_URL')
|
||||||
|
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
|
||||||
|
let redis
|
||||||
|
if (isRedisInClusterMode) {
|
||||||
|
redis = new Redis.Cluster(redisUrl.split(','))
|
||||||
|
} else {
|
||||||
|
redis = new Redis(redisUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
container.bind(TYPES.Redis).toConstantValue(redis)
|
||||||
|
|
||||||
|
const newrelicWinstonFormatter = newrelicFormatter(winston)
|
||||||
|
const winstonFormatters = [winston.format.splat(), winston.format.json()]
|
||||||
|
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
|
||||||
|
winstonFormatters.push(newrelicWinstonFormatter())
|
||||||
|
}
|
||||||
|
|
||||||
|
const logger = winston.createLogger({
|
||||||
|
level: env.get('LOG_LEVEL') || 'info',
|
||||||
|
format: winston.format.combine(...winstonFormatters),
|
||||||
|
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL') || 'info' })],
|
||||||
|
})
|
||||||
|
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(logger)
|
||||||
|
|
||||||
|
if (env.get('SQS_QUEUE_URL', true)) {
|
||||||
|
const sqsConfig: AWS.SQS.Types.ClientConfiguration = {
|
||||||
|
apiVersion: 'latest',
|
||||||
|
region: env.get('SQS_AWS_REGION', true),
|
||||||
|
}
|
||||||
|
if (env.get('SQS_ACCESS_KEY_ID', true) && env.get('SQS_SECRET_ACCESS_KEY', true)) {
|
||||||
|
sqsConfig.credentials = {
|
||||||
|
accessKeyId: env.get('SQS_ACCESS_KEY_ID', true),
|
||||||
|
secretAccessKey: env.get('SQS_SECRET_ACCESS_KEY', true),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
container.bind<AWS.SQS>(TYPES.SQS).toConstantValue(new AWS.SQS(sqsConfig))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map
|
||||||
|
container
|
||||||
|
.bind<MapperInterface<Setting, TypeORMSetting>>(TYPES.SettingPersistenceMapper)
|
||||||
|
.toConstantValue(new SettingPersistenceMapper())
|
||||||
|
|
||||||
|
// ORM
|
||||||
|
container
|
||||||
|
.bind<Repository<TypeORMSetting>>(TYPES.ORMSettingRepository)
|
||||||
|
.toConstantValue(AppDataSource.getRepository(TypeORMSetting))
|
||||||
|
|
||||||
|
// env vars
|
||||||
|
container.bind(TYPES.REDIS_URL).toConstantValue(env.get('REDIS_URL'))
|
||||||
|
container.bind(TYPES.SQS_QUEUE_URL).toConstantValue(env.get('SQS_QUEUE_URL', true))
|
||||||
|
container.bind(TYPES.REDIS_EVENTS_CHANNEL).toConstantValue(env.get('REDIS_EVENTS_CHANNEL'))
|
||||||
|
container.bind(TYPES.AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET'))
|
||||||
|
container.bind(TYPES.NEW_RELIC_ENABLED).toConstantValue(env.get('NEW_RELIC_ENABLED', true))
|
||||||
|
container.bind(TYPES.VERSION).toConstantValue(env.get('VERSION'))
|
||||||
|
|
||||||
|
// Repositories
|
||||||
|
container
|
||||||
|
.bind<SettingRepositoryInterface>(TYPES.SettingRepository)
|
||||||
|
.toConstantValue(
|
||||||
|
new MySQLSettingRepository(
|
||||||
|
container.get(TYPES.ORMSettingRepository),
|
||||||
|
container.get(TYPES.SettingPersistenceMapper),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// use cases
|
||||||
|
|
||||||
|
// Controller
|
||||||
|
container.bind<SettingsController>(TYPES.SettingsController).toConstantValue(new SettingsController())
|
||||||
|
|
||||||
|
// Handlers
|
||||||
|
|
||||||
|
// Services
|
||||||
|
container
|
||||||
|
.bind<TokenDecoderInterface<CrossServiceTokenData>>(TYPES.CrossServiceTokenDecoder)
|
||||||
|
.toConstantValue(new TokenDecoder<CrossServiceTokenData>(container.get(TYPES.AUTH_JWT_SECRET)))
|
||||||
|
|
||||||
|
// Middleware
|
||||||
|
container
|
||||||
|
.bind<InversifyExpressApiGatewayAuthMiddleware>(TYPES.ApiGatewayAuthMiddleware)
|
||||||
|
.to(InversifyExpressApiGatewayAuthMiddleware)
|
||||||
|
|
||||||
|
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([])
|
||||||
|
|
||||||
|
if (env.get('SQS_QUEUE_URL', true)) {
|
||||||
|
container
|
||||||
|
.bind<DomainEventMessageHandlerInterface>(TYPES.DomainEventMessageHandler)
|
||||||
|
.toConstantValue(
|
||||||
|
env.get('NEW_RELIC_ENABLED', true) === 'true'
|
||||||
|
? new SQSNewRelicEventMessageHandler(eventHandlers, container.get(TYPES.Logger))
|
||||||
|
: new SQSEventMessageHandler(eventHandlers, container.get(TYPES.Logger)),
|
||||||
|
)
|
||||||
|
container
|
||||||
|
.bind<DomainEventSubscriberFactoryInterface>(TYPES.DomainEventSubscriberFactory)
|
||||||
|
.toConstantValue(
|
||||||
|
new SQSDomainEventSubscriberFactory(
|
||||||
|
container.get(TYPES.SQS),
|
||||||
|
container.get(TYPES.SQS_QUEUE_URL),
|
||||||
|
container.get(TYPES.DomainEventMessageHandler),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
container
|
||||||
|
.bind<DomainEventMessageHandlerInterface>(TYPES.DomainEventMessageHandler)
|
||||||
|
.toConstantValue(new RedisEventMessageHandler(eventHandlers, container.get(TYPES.Logger)))
|
||||||
|
container
|
||||||
|
.bind<DomainEventSubscriberFactoryInterface>(TYPES.DomainEventSubscriberFactory)
|
||||||
|
.toConstantValue(
|
||||||
|
new RedisDomainEventSubscriberFactory(
|
||||||
|
container.get(TYPES.Redis),
|
||||||
|
container.get(TYPES.DomainEventMessageHandler),
|
||||||
|
container.get(TYPES.REDIS_EVENTS_CHANNEL),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return container
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { DataSource, LoggerOptions } from 'typeorm'
|
||||||
|
|
||||||
|
import { TypeORMSetting } from '../Infra/TypeORM/TypeORMSetting'
|
||||||
|
|
||||||
|
import { Env } from './Env'
|
||||||
|
|
||||||
|
const env: Env = new Env()
|
||||||
|
env.load()
|
||||||
|
|
||||||
|
const maxQueryExecutionTime = env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
|
||||||
|
? +env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
|
||||||
|
: 45_000
|
||||||
|
|
||||||
|
export const AppDataSource = new DataSource({
|
||||||
|
type: 'mysql',
|
||||||
|
charset: 'utf8mb4',
|
||||||
|
supportBigNumbers: true,
|
||||||
|
bigNumberStrings: false,
|
||||||
|
maxQueryExecutionTime,
|
||||||
|
replication: {
|
||||||
|
master: {
|
||||||
|
host: env.get('DB_HOST'),
|
||||||
|
port: parseInt(env.get('DB_PORT')),
|
||||||
|
username: env.get('DB_USERNAME'),
|
||||||
|
password: env.get('DB_PASSWORD'),
|
||||||
|
database: env.get('DB_DATABASE'),
|
||||||
|
},
|
||||||
|
slaves: [
|
||||||
|
{
|
||||||
|
host: env.get('DB_REPLICA_HOST'),
|
||||||
|
port: parseInt(env.get('DB_PORT')),
|
||||||
|
username: env.get('DB_USERNAME'),
|
||||||
|
password: env.get('DB_PASSWORD'),
|
||||||
|
database: env.get('DB_DATABASE'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
removeNodeErrorCount: 10,
|
||||||
|
restoreNodeTimeout: 5,
|
||||||
|
},
|
||||||
|
entities: [TypeORMSetting],
|
||||||
|
migrations: [env.get('DB_MIGRATIONS_PATH', true) ?? 'dist/migrations/*.js'],
|
||||||
|
migrationsRun: true,
|
||||||
|
logging: <LoggerOptions>env.get('DB_DEBUG_LEVEL'),
|
||||||
|
})
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { config, DotenvParseOutput } from 'dotenv'
|
||||||
|
import { injectable } from 'inversify'
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class Env {
|
||||||
|
private env?: DotenvParseOutput
|
||||||
|
|
||||||
|
public load(): void {
|
||||||
|
const output = config()
|
||||||
|
this.env = <DotenvParseOutput>output.parsed
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(key: string, optional = false): string {
|
||||||
|
if (!this.env) {
|
||||||
|
this.load()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.env[key] && !optional) {
|
||||||
|
throw new Error(`Environment variable ${key} not set`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <string>process.env[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
const TYPES = {
|
||||||
|
DBConnection: Symbol.for('DBConnection'),
|
||||||
|
Logger: Symbol.for('Logger'),
|
||||||
|
Redis: Symbol.for('Redis'),
|
||||||
|
SQS: Symbol.for('SQS'),
|
||||||
|
// Map
|
||||||
|
SettingPersistenceMapper: Symbol.for('SettingPersistenceMapper'),
|
||||||
|
// ORM
|
||||||
|
ORMSettingRepository: Symbol.for('ORMSettingRepository'),
|
||||||
|
// Repositories
|
||||||
|
SettingRepository: Symbol.for('SettingRepository'),
|
||||||
|
// env vars
|
||||||
|
REDIS_URL: Symbol.for('REDIS_URL'),
|
||||||
|
SQS_QUEUE_URL: Symbol.for('SQS_QUEUE_URL'),
|
||||||
|
SQS_AWS_REGION: Symbol.for('SQS_AWS_REGION'),
|
||||||
|
REDIS_EVENTS_CHANNEL: Symbol.for('REDIS_EVENTS_CHANNEL'),
|
||||||
|
AUTH_JWT_SECRET: Symbol.for('AUTH_JWT_SECRET'),
|
||||||
|
NEW_RELIC_ENABLED: Symbol.for('NEW_RELIC_ENABLED'),
|
||||||
|
VERSION: Symbol.for('VERSION'),
|
||||||
|
// use cases
|
||||||
|
// Controller
|
||||||
|
SettingsController: Symbol.for('SettingsController'),
|
||||||
|
// Handlers
|
||||||
|
// Services
|
||||||
|
CrossServiceTokenDecoder: Symbol.for('CrossServiceTokenDecoder'),
|
||||||
|
DomainEventSubscriberFactory: Symbol.for('DomainEventSubscriberFactory'),
|
||||||
|
DomainEventMessageHandler: Symbol.for('DomainEventMessageHandler'),
|
||||||
|
Timer: Symbol.for('Timer'),
|
||||||
|
// Middleware
|
||||||
|
ApiGatewayAuthMiddleware: Symbol.for('ApiGatewayAuthMiddleware'),
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TYPES
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export class SettingsController {
|
||||||
|
constructor() {}
|
||||||
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
export enum CloudProvider {
|
|
||||||
Dropbox = 'Dropbox',
|
|
||||||
Google = 'Google Drive',
|
|
||||||
OneDrive = 'OneDrive',
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
export enum DropboxBackupFrequency {
|
|
||||||
Disabled = 'disabled',
|
|
||||||
Daily = 'daily',
|
|
||||||
Weekly = 'weekly',
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
export enum EmailBackupFrequency {
|
|
||||||
Disabled = 'disabled',
|
|
||||||
Daily = 'daily',
|
|
||||||
Weekly = 'weekly',
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
export enum GoogleDriveBackupFrequency {
|
|
||||||
Disabled = 'disabled',
|
|
||||||
Daily = 'daily',
|
|
||||||
Weekly = 'weekly',
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export type ListedAuthorSecretsData = Array<{ authorId: number; secret: string; hostUrl: string }>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export enum LogSessionUserAgentOption {
|
|
||||||
Disabled = 'disabled',
|
|
||||||
Enabled = 'enabled',
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export enum MuteFailedBackupsEmailsOption {
|
|
||||||
Muted = 'muted',
|
|
||||||
NotMuted = 'not_muted',
|
|
||||||
}
|
|
||||||
-4
@@ -1,4 +0,0 @@
|
|||||||
export enum MuteFailedCloudBackupsEmailsOption {
|
|
||||||
Muted = 'muted',
|
|
||||||
NotMuted = 'not_muted',
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export enum MuteMarketingEmailsOption {
|
|
||||||
Muted = 'muted',
|
|
||||||
NotMuted = 'not_muted',
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export enum MuteSignInEmailsOption {
|
|
||||||
Muted = 'muted',
|
|
||||||
NotMuted = 'not_muted',
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
export enum OneDriveBackupFrequency {
|
|
||||||
Disabled = 'disabled',
|
|
||||||
Daily = 'daily',
|
|
||||||
Weekly = 'weekly',
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import { SettingName } from './SettingName'
|
|
||||||
|
|
||||||
export type SensitiveSettingName = SettingName.MfaSecret | SettingName.ExtensionKey
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
|
import { SettingProps } from './SettingProps'
|
||||||
|
|
||||||
|
export class Setting extends Entity<SettingProps> {
|
||||||
|
get id(): UniqueEntityId {
|
||||||
|
return this._id
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor(props: SettingProps, id?: UniqueEntityId) {
|
||||||
|
super(props, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
static create(props: SettingProps, id?: UniqueEntityId): Result<Setting> {
|
||||||
|
return Result.ok<Setting>(new Setting(props, id))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
export enum SettingName {
|
|
||||||
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',
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { EncryptionVersion, SettingName, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
|
export interface SettingProps {
|
||||||
|
userUuid: Uuid
|
||||||
|
name: SettingName
|
||||||
|
value: string | null
|
||||||
|
serverEncryptionVersion: EncryptionVersion
|
||||||
|
sensitive: boolean
|
||||||
|
timestamps: Timestamps
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { SettingName, Uuid } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
|
import { Setting } from './Setting'
|
||||||
|
|
||||||
|
export interface SettingRepositoryInterface {
|
||||||
|
findOneByNameAndValue(name: SettingName, value: string): Promise<Setting | null>
|
||||||
|
setValueOnMultipleSettings(settingNames: string[], userUuid: Uuid, value: string | null): Promise<void>
|
||||||
|
}
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export enum SubscriptionSettingName {
|
|
||||||
FileUploadBytesLimit = 'FILE_UPLOAD_BYTES_LIMIT',
|
|
||||||
FileUploadBytesUsed = 'FILE_UPLOAD_BYTES_USED',
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { inject, injectable } from 'inversify'
|
||||||
|
|
||||||
|
import TYPES from '../../../Bootstrap/Types'
|
||||||
|
import { SettingRepositoryInterface } from '../../Setting/SettingRepositoryInterface'
|
||||||
|
import { UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||||
|
import { Setting } from '../../Setting/Setting'
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class GetSettings implements UseCaseInterface<Setting[]> {
|
||||||
|
constructor(
|
||||||
|
@inject(TYPES.SettingRepository) private settingRepository: SettingRepositoryInterface,
|
||||||
|
@inject(TYPES.Crypter) private crypter: CrypterInterface,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async execute(dto: GetSettingsDto): Promise<GetSettingsResponse> {
|
||||||
|
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||||
|
if (userUuidOrError.isFailed()) {
|
||||||
|
|
||||||
|
}
|
||||||
|
let settings = await this.settingRepository.findAllByUserUuid(userUuid)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
export * from './CloudProvider/CloudProvider'
|
|
||||||
export * from './DropboxBackupFrequency/DropboxBackupFrequency'
|
|
||||||
export * from './EmailBackupFrequency/EmailBackupFrequency'
|
|
||||||
export * from './GoogleDriveBackupFrequency/GoogleDriveBackupFrequency'
|
|
||||||
export * from './ListedAuthorSecretsData/ListedAuthorSecretsData'
|
|
||||||
export * from './LogSessionUserAgent/LogSessionUserAgentOption'
|
|
||||||
export * from './MuteFailedBackupsEmails/MuteFailedBackupsEmailsOption'
|
|
||||||
export * from './MuteFailedCloudBackupsEmails/MuteFailedCloudBackupsEmailsOption'
|
|
||||||
export * from './MuteMarketingEmails/MuteMarketingEmailsOption'
|
|
||||||
export * from './MuteSignInEmails/MuteSignInEmailsOption'
|
|
||||||
export * from './OneDriveBackupFrequency/OneDriveBackupFrequency'
|
|
||||||
export * from './Setting/SensitiveSettingName'
|
|
||||||
export * from './Setting/SettingName'
|
|
||||||
export * from './Setting/SubscriptionSettingName'
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export interface MuteAllEmailsRequestParams {
|
||||||
|
unsubscribeToken: string
|
||||||
|
}
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
import { CrossServiceTokenData, TokenDecoderInterface } from '@standardnotes/security'
|
||||||
|
import { NextFunction, Request, Response } from 'express'
|
||||||
|
import { inject, injectable } from 'inversify'
|
||||||
|
import { BaseMiddleware } from 'inversify-express-utils'
|
||||||
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
|
import TYPES from '../../Bootstrap/Types'
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class InversifyExpressApiGatewayAuthMiddleware extends BaseMiddleware {
|
||||||
|
constructor(
|
||||||
|
@inject(TYPES.CrossServiceTokenDecoder) private tokenDecoder: TokenDecoderInterface<CrossServiceTokenData>,
|
||||||
|
@inject(TYPES.Logger) private logger: Logger,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
async handler(request: Request, response: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (!request.headers['x-auth-token']) {
|
||||||
|
this.logger.debug('ApiGatewayAuthMiddleware missing x-auth-token header.')
|
||||||
|
|
||||||
|
response.status(401).send({
|
||||||
|
error: {
|
||||||
|
tag: 'invalid-auth',
|
||||||
|
message: 'Invalid login credentials.',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const token: CrossServiceTokenData | undefined = this.tokenDecoder.decodeToken(
|
||||||
|
request.headers['x-auth-token'] as string,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (token === undefined) {
|
||||||
|
this.logger.debug('ApiGatewayAuthMiddleware authentication failure.')
|
||||||
|
|
||||||
|
response.status(401).send({
|
||||||
|
error: {
|
||||||
|
tag: 'invalid-auth',
|
||||||
|
message: 'Invalid login credentials.',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response.locals.user = token.user
|
||||||
|
response.locals.roles = token.roles
|
||||||
|
response.locals.session = token.session
|
||||||
|
response.locals.readOnlyAccess = token.session?.readonly_access ?? false
|
||||||
|
|
||||||
|
return next()
|
||||||
|
} catch (error) {
|
||||||
|
return next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { controller, httpGet } from 'inversify-express-utils'
|
||||||
|
|
||||||
|
@controller('/healthcheck')
|
||||||
|
export class InversifyExpressHealthCheckController {
|
||||||
|
@httpGet('/')
|
||||||
|
public async get(): Promise<string> {
|
||||||
|
return 'OK'
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { BaseHttpController, controller } from 'inversify-express-utils'
|
||||||
|
import { inject } from 'inversify'
|
||||||
|
|
||||||
|
import TYPES from '../../Bootstrap/Types'
|
||||||
|
import { SettingsController } from '../../Controller/SettingsController'
|
||||||
|
|
||||||
|
@controller('/users/:userUuid/settings', TYPES.ApiGatewayAuthMiddleware)
|
||||||
|
export class InversifyExpressSettingsController extends BaseHttpController {
|
||||||
|
constructor(@inject(TYPES.SettingsController) private settingsController: SettingsController) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { MapperInterface, SettingName, Uuid } from '@standardnotes/domain-core'
|
||||||
|
import { Repository } from 'typeorm'
|
||||||
|
import { Setting } from '../../Domain/Setting/Setting'
|
||||||
|
|
||||||
|
import { SettingRepositoryInterface } from '../../Domain/Setting/SettingRepositoryInterface'
|
||||||
|
import { TypeORMSetting } from '../TypeORM/TypeORMSetting'
|
||||||
|
|
||||||
|
export class MySQLSettingRepository implements SettingRepositoryInterface {
|
||||||
|
constructor(
|
||||||
|
private ormRepository: Repository<TypeORMSetting>,
|
||||||
|
private settingMapper: MapperInterface<Setting, TypeORMSetting>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async findOneByNameAndValue(name: SettingName, value: string): Promise<Setting | null> {
|
||||||
|
const typeormSetting = await this.ormRepository
|
||||||
|
.createQueryBuilder()
|
||||||
|
.where('name = :name', { name: name.value })
|
||||||
|
.andWhere('value = :value', { value })
|
||||||
|
.getOne()
|
||||||
|
|
||||||
|
if (typeormSetting === null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.settingMapper.toDomain(typeormSetting)
|
||||||
|
}
|
||||||
|
|
||||||
|
async setValueOnMultipleSettings(settingNames: string[], userUuid: Uuid, value: string | null): Promise<void> {
|
||||||
|
await this.ormRepository
|
||||||
|
.createQueryBuilder()
|
||||||
|
.update()
|
||||||
|
.set({
|
||||||
|
value,
|
||||||
|
})
|
||||||
|
.where('user_uuid = :userUuid', { userUuid: userUuid.value })
|
||||||
|
.andWhere('name IN (:...settingNames)', { settingNames })
|
||||||
|
.execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { EncryptionVersion } from '@standardnotes/domain-core'
|
||||||
|
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
|
||||||
|
|
||||||
|
@Entity({ name: 'settings' })
|
||||||
|
@Index('index_settings_on_name_and_user_uuid', ['name', 'userUuid'])
|
||||||
|
export class TypeORMSetting {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
declare uuid: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
length: 255,
|
||||||
|
})
|
||||||
|
declare name: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'text',
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
declare value: string | null
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'server_encryption_version',
|
||||||
|
type: 'tinyint',
|
||||||
|
default: EncryptionVersion.VERSIONS.Unencrypted,
|
||||||
|
})
|
||||||
|
declare serverEncryptionVersion: number
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'created_at',
|
||||||
|
type: 'bigint',
|
||||||
|
})
|
||||||
|
declare createdAt: number
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'updated_at',
|
||||||
|
type: 'bigint',
|
||||||
|
})
|
||||||
|
@Index('index_settings_on_updated_at')
|
||||||
|
declare updatedAt: number
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'user_uuid',
|
||||||
|
length: 36,
|
||||||
|
type: 'varchar',
|
||||||
|
})
|
||||||
|
declare userUuid: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'tinyint',
|
||||||
|
width: 1,
|
||||||
|
nullable: false,
|
||||||
|
default: 0,
|
||||||
|
})
|
||||||
|
declare sensitive: boolean
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import {
|
||||||
|
EncryptionVersion,
|
||||||
|
MapperInterface,
|
||||||
|
SettingName,
|
||||||
|
Timestamps,
|
||||||
|
UniqueEntityId,
|
||||||
|
Uuid,
|
||||||
|
} from '@standardnotes/domain-core'
|
||||||
|
|
||||||
|
import { Setting } from '../Domain/Setting/Setting'
|
||||||
|
import { TypeORMSetting } from '../Infra/TypeORM/TypeORMSetting'
|
||||||
|
|
||||||
|
export class SettingPersistenceMapper implements MapperInterface<Setting, TypeORMSetting> {
|
||||||
|
toDomain(projection: TypeORMSetting): Setting {
|
||||||
|
const settingNameOrError = SettingName.create(projection.name)
|
||||||
|
if (settingNameOrError.isFailed()) {
|
||||||
|
throw new Error(`Could not create setting projection: ${settingNameOrError.getError()}`)
|
||||||
|
}
|
||||||
|
const name = settingNameOrError.getValue()
|
||||||
|
|
||||||
|
const serverEncryptionVersionOrError = EncryptionVersion.create(projection.serverEncryptionVersion)
|
||||||
|
if (serverEncryptionVersionOrError.isFailed()) {
|
||||||
|
throw new Error(`Could not create setting projection: ${serverEncryptionVersionOrError.getError()}`)
|
||||||
|
}
|
||||||
|
const serverEncryptionVersion = serverEncryptionVersionOrError.getValue()
|
||||||
|
|
||||||
|
const userUuidOrError = Uuid.create(projection.userUuid)
|
||||||
|
if (userUuidOrError.isFailed()) {
|
||||||
|
throw new Error(`Could not create setting projection: ${userUuidOrError.getError()}`)
|
||||||
|
}
|
||||||
|
const userUuid = userUuidOrError.getValue()
|
||||||
|
|
||||||
|
const timestampsOrError = Timestamps.create(projection.createdAt, projection.updatedAt)
|
||||||
|
if (timestampsOrError.isFailed()) {
|
||||||
|
throw new Error(`Could not create setting projection: ${timestampsOrError.getError()}`)
|
||||||
|
}
|
||||||
|
const timestamps = timestampsOrError.getValue()
|
||||||
|
|
||||||
|
const settingOrError = Setting.create(
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
sensitive: projection.sensitive,
|
||||||
|
serverEncryptionVersion,
|
||||||
|
timestamps,
|
||||||
|
userUuid,
|
||||||
|
value: projection.value,
|
||||||
|
},
|
||||||
|
new UniqueEntityId(projection.uuid),
|
||||||
|
)
|
||||||
|
if (settingOrError.isFailed()) {
|
||||||
|
throw new Error(`Could not create setting projection: ${settingOrError.getError()}`)
|
||||||
|
}
|
||||||
|
const setting = settingOrError.getValue()
|
||||||
|
|
||||||
|
return setting
|
||||||
|
}
|
||||||
|
|
||||||
|
toProjection(domain: Setting): TypeORMSetting {
|
||||||
|
const typeOrmSetting = new TypeORMSetting()
|
||||||
|
|
||||||
|
typeOrmSetting.name = domain.props.name.value
|
||||||
|
typeOrmSetting.sensitive = domain.props.sensitive
|
||||||
|
typeOrmSetting.serverEncryptionVersion = domain.props.serverEncryptionVersion.value
|
||||||
|
typeOrmSetting.userUuid = domain.props.userUuid.value
|
||||||
|
typeOrmSetting.uuid = domain.id.toString()
|
||||||
|
typeOrmSetting.value = domain.props.value
|
||||||
|
typeOrmSetting.createdAt = domain.props.timestamps.createdAt
|
||||||
|
typeOrmSetting.updatedAt = domain.props.timestamps.updatedAt
|
||||||
|
|
||||||
|
return typeOrmSetting
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './Domain'
|
|
||||||
@@ -5,7 +5,9 @@
|
|||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src/**/*"
|
"src/**/*",
|
||||||
|
"bin/**/*",
|
||||||
|
"migrations/**/*",
|
||||||
],
|
],
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user