mirror of
https://github.com/standardnotes/server
synced 2026-01-21 17:04:28 -05:00
Compare commits
40 Commits
@standardn
...
@standardn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d25e14147c | ||
|
|
1d4bce7309 | ||
|
|
8c0c56930e | ||
|
|
2e9255344e | ||
|
|
346a7cabe1 | ||
|
|
4a430b2701 | ||
|
|
bb53e88a4e | ||
|
|
cfd04a5b39 | ||
|
|
e0f2d5e202 | ||
|
|
3035cbc5de | ||
|
|
7c271be310 | ||
|
|
ba373ebc6b | ||
|
|
2450d88a29 | ||
|
|
376a59c182 | ||
|
|
d584ca57f1 | ||
|
|
c50662849b | ||
|
|
82da690139 | ||
|
|
b11a9b0eac | ||
|
|
d7653474c3 | ||
|
|
ce2fd86ca3 | ||
|
|
c9c496c63f | ||
|
|
b1c9f8ca6e | ||
|
|
e84bd73a39 | ||
|
|
c69d1b02fb | ||
|
|
b590d33b88 | ||
|
|
c1e7a3eb7e | ||
|
|
fd743a9d5e | ||
|
|
4c40fd5186 | ||
|
|
2cb470b99e | ||
|
|
b6539f8795 | ||
|
|
6d7de4a8da | ||
|
|
41999f36f0 | ||
|
|
fa2a8da17b | ||
|
|
214684eae7 | ||
|
|
b3a92af04d | ||
|
|
210a314c81 | ||
|
|
46cba52bcb | ||
|
|
bf14ec05f9 | ||
|
|
6f88a96c3e | ||
|
|
e12c9c47a7 |
21
.github/workflows/pr.yml
vendored
Normal file
21
.github/workflows/pr.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Pull Request
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
- name: ESLint
|
||||
run: yarn lint
|
||||
- name: Build
|
||||
run: yarn build
|
||||
- name: Test
|
||||
run: yarn test
|
||||
46
.github/workflows/snjs.upgrade.event.yml
vendored
Normal file
46
.github/workflows/snjs.upgrade.event.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Update SNJS Packages
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
repository_dispatch:
|
||||
types: [snjs-updated-event]
|
||||
|
||||
jobs:
|
||||
SNJSUpdateEvent:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: main
|
||||
token: ${{ secrets.CI_PAT_TOKEN }}
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
- name: Setup git config
|
||||
run: |
|
||||
git config --global user.name "standardci"
|
||||
git config --global user.email "ci@standardnotes.com"
|
||||
- name: Import GPG key
|
||||
uses: crazy-max/ghaction-import-gpg@v4
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
passphrase: ${{ secrets.PASSPHRASE }}
|
||||
git_user_signingkey: true
|
||||
git_commit_gpgsign: true
|
||||
|
||||
- run: yarn install
|
||||
- run: |
|
||||
yarn upgrade:snjs
|
||||
yarn install --no-immutable
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
token: ${{ secrets.CI_PAT_TOKEN }}
|
||||
title: "${{ 'chore(deps): upgrade snjs' }}"
|
||||
body: Updates all packages prefixed with "@standardnotes/"
|
||||
commit-message: "${{ 'chore(deps): upgrade snjs' }}"
|
||||
delete-branch: true
|
||||
committer: standardci <ci@standardnotes.com>
|
||||
author: standardci <ci@standardnotes.com>
|
||||
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@standardnotes-common-npm-1.23.1-ed73dbb679-f498f4c469.zip
vendored
Normal file
BIN
.yarn/cache/@standardnotes-common-npm-1.23.1-ed73dbb679-f498f4c469.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@standardnotes-config-npm-2.4.3-f16699e480-b57b49242a.zip
vendored
Normal file
BIN
.yarn/cache/@standardnotes-config-npm-2.4.3-f16699e480-b57b49242a.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@standardnotes-features-npm-1.46.0-32e559e3b6-0653b9b425.zip
vendored
Normal file
BIN
.yarn/cache/@standardnotes-features-npm-1.46.0-32e559e3b6-0653b9b425.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@standardnotes-settings-npm-1.15.0-bfec86ee49-4397d453a1.zip
vendored
Normal file
BIN
.yarn/cache/@standardnotes-settings-npm-1.15.0-bfec86ee49-4397d453a1.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@standardnotes-utils-npm-1.6.12-8fa8d7d09b-e177b1fa51.zip
vendored
Normal file
BIN
.yarn/cache/@standardnotes-utils-npm-1.6.12-8fa8d7d09b-e177b1fa51.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@typescript-eslint-eslint-plugin-npm-5.30.0-c72e6ffad8-f2fe96082c.zip
vendored
Normal file
BIN
.yarn/cache/@typescript-eslint-eslint-plugin-npm-5.30.0-c72e6ffad8-f2fe96082c.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@typescript-eslint-parser-npm-5.30.0-02dd191940-7067ba4ede.zip
vendored
Normal file
BIN
.yarn/cache/@typescript-eslint-parser-npm-5.30.0-02dd191940-7067ba4ede.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@typescript-eslint-scope-manager-npm-5.30.0-53c13117cb-51246d0f6c.zip
vendored
Normal file
BIN
.yarn/cache/@typescript-eslint-scope-manager-npm-5.30.0-53c13117cb-51246d0f6c.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/@typescript-eslint-type-utils-npm-5.30.0-de31c989eb-6185117638.zip
vendored
Normal file
BIN
.yarn/cache/@typescript-eslint-type-utils-npm-5.30.0-de31c989eb-6185117638.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@typescript-eslint-types-npm-5.30.0-1829c252da-f83a506880.zip
vendored
Normal file
BIN
.yarn/cache/@typescript-eslint-types-npm-5.30.0-1829c252da-f83a506880.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@typescript-eslint-typescript-estree-npm-5.30.0-7d89cc3db4-cf8caea435.zip
vendored
Normal file
BIN
.yarn/cache/@typescript-eslint-typescript-estree-npm-5.30.0-7d89cc3db4-cf8caea435.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/@typescript-eslint-utils-npm-5.30.0-6128c1c85a-176eda4629.zip
vendored
Normal file
BIN
.yarn/cache/@typescript-eslint-utils-npm-5.30.0-6128c1c85a-176eda4629.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@typescript-eslint-visitor-keys-npm-5.30.0-1edc3a593e-bf2219cbb9.zip
vendored
Normal file
BIN
.yarn/cache/@typescript-eslint-visitor-keys-npm-5.30.0-1edc3a593e-bf2219cbb9.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/cint-npm-8.2.1-958b3dddeb-5f32feed16.zip
vendored
Normal file
BIN
.yarn/cache/cint-npm-8.2.1-958b3dddeb-5f32feed16.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/cli-table-npm-0.3.11-f912789cff-59fb61f992.zip
vendored
Normal file
BIN
.yarn/cache/cli-table-npm-0.3.11-f912789cff-59fb61f992.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/colors-npm-1.0.3-6c5d583ab3-234e8d3ab7.zip
vendored
Normal file
BIN
.yarn/cache/colors-npm-1.0.3-6c5d583ab3-234e8d3ab7.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/commander-npm-9.3.0-1393a6e1f6-d421ce66fe.zip
vendored
Normal file
BIN
.yarn/cache/commander-npm-9.3.0-1393a6e1f6-d421ce66fe.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/fast-memoize-npm-2.5.2-f42a7c6940-79fa759719.zip
vendored
Normal file
BIN
.yarn/cache/fast-memoize-npm-2.5.2-f42a7c6940-79fa759719.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/fp-and-or-npm-0.1.3-033d5c60bb-d556ad1fb0.zip
vendored
Normal file
BIN
.yarn/cache/fp-and-or-npm-0.1.3-033d5c60bb-d556ad1fb0.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/get-stdin-npm-8.0.0-920f876bc2-40128b6cd2.zip
vendored
Normal file
BIN
.yarn/cache/get-stdin-npm-8.0.0-920f876bc2-40128b6cd2.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/ini-npm-3.0.0-c2af955701-e92b6b0835.zip
vendored
Normal file
BIN
.yarn/cache/ini-npm-3.0.0-c2af955701-e92b6b0835.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/jju-npm-1.4.0-670678eaa3-3790481bd2.zip
vendored
Normal file
BIN
.yarn/cache/jju-npm-1.4.0-670678eaa3-3790481bd2.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/json-parse-helpfulerror-npm-1.0.3-003666633e-376d85c372.zip
vendored
Normal file
BIN
.yarn/cache/json-parse-helpfulerror-npm-1.0.3-003666633e-376d85c372.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/jsonlines-npm-0.1.1-0b9cdf648d-5408cbdbd3.zip
vendored
Normal file
BIN
.yarn/cache/jsonlines-npm-0.1.1-0b9cdf648d-5408cbdbd3.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/kleur-npm-4.1.5-46b6135f41-1dc476e327.zip
vendored
Normal file
BIN
.yarn/cache/kleur-npm-4.1.5-46b6135f41-1dc476e327.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/npm-check-updates-npm-14.1.1-c02bfe3d02-a3ec52312c.zip
vendored
Normal file
BIN
.yarn/cache/npm-check-updates-npm-14.1.1-c02bfe3d02-a3ec52312c.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/parse-github-url-npm-1.0.2-290c32ecbc-a19b8bc6f8.zip
vendored
Normal file
BIN
.yarn/cache/parse-github-url-npm-1.0.2-290c32ecbc-a19b8bc6f8.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/progress-npm-2.0.3-d1f87e2ac6-f67403fe7b.zip
vendored
Normal file
BIN
.yarn/cache/progress-npm-2.0.3-d1f87e2ac6-f67403fe7b.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/prompts-ncu-npm-2.5.0-5d11c7d5b9-e050961c30.zip
vendored
Normal file
BIN
.yarn/cache/prompts-ncu-npm-2.5.0-5d11c7d5b9-e050961c30.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/rc-config-loader-npm-4.1.0-207fabc6dd-1d07aaf611.zip
vendored
Normal file
BIN
.yarn/cache/rc-config-loader-npm-4.1.0-207fabc6dd-1d07aaf611.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/remote-git-tags-npm-3.0.0-d27b051c92-04d87e4c98.zip
vendored
Normal file
BIN
.yarn/cache/remote-git-tags-npm-3.0.0-d27b051c92-04d87e4c98.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/require-from-string-npm-2.0.2-8557e0db12-a03ef68954.zip
vendored
Normal file
BIN
.yarn/cache/require-from-string-npm-2.0.2-8557e0db12-a03ef68954.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/semver-utils-npm-1.1.4-61f884e528-93fd955a30.zip
vendored
Normal file
BIN
.yarn/cache/semver-utils-npm-1.1.4-61f884e528-93fd955a30.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/source-map-support-npm-0.5.21-09ca99e250-43e98d700d.zip
vendored
Normal file
BIN
.yarn/cache/source-map-support-npm-0.5.21-09ca99e250-43e98d700d.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/spawn-please-npm-1.0.0-5731ea7630-b8e1e1dc14.zip
vendored
Normal file
BIN
.yarn/cache/spawn-please-npm-1.0.0-5731ea7630-b8e1e1dc14.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/yaml-npm-2.1.1-e717f9b915-f48bb20991.zip
vendored
Normal file
BIN
.yarn/cache/yaml-npm-2.1.1-e717f9b915-f48bb20991.zip
vendored
Normal file
Binary file not shown.
@@ -39,7 +39,8 @@
|
||||
"start:files-worker": "yarn workspace @standardnotes/files-server worker",
|
||||
"start:api-gateway": "yarn workspace @standardnotes/api-gateway start",
|
||||
"release:prod": "lerna version --conventional-graduate --conventional-commits --yes -m \"chore(release): publish new version\"",
|
||||
"postversion": "./scripts/push-tags-one-by-one.sh"
|
||||
"postversion": "./scripts/push-tags-one-by-one.sh",
|
||||
"upgrade:snjs": "yarn workspaces foreach --verbose run upgrade:snjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.0.2",
|
||||
@@ -53,6 +54,8 @@
|
||||
"@typescript-eslint/parser": "^5.29.0",
|
||||
"eslint": "^8.17.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"ini": "^3.0.0",
|
||||
"npm-check-updates": "^14.1.1",
|
||||
"prettier": "^2.7.1",
|
||||
"ts-node": "^10.8.1",
|
||||
"typescript": "^4.7.4"
|
||||
|
||||
@@ -3,6 +3,40 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.3.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.3.1...@standardnotes/api-gateway@1.3.2) (2022-06-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* checking for html content-type ([4a430b2](https://github.com/standardnotes/api-gateway/commit/4a430b2701733358d14da64a5eaa4e03620033e0))
|
||||
|
||||
## [1.3.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.3.0...@standardnotes/api-gateway@1.3.1) (2022-06-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add origin meta property to daily analytics event ([376a59c](https://github.com/standardnotes/api-gateway/commit/376a59c1827411164a536157fc591a15e0a5b0b2))
|
||||
|
||||
# [1.3.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.2.2...@standardnotes/api-gateway@1.3.0) (2022-06-28)
|
||||
|
||||
### Features
|
||||
|
||||
* remove api metadata decorating html responses ([3035cbc](https://github.com/standardnotes/api-gateway/commit/3035cbc5ded1408bc4b8646563c4992ba5f27c75))
|
||||
|
||||
## [1.2.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.2.1...@standardnotes/api-gateway@1.2.2) (2022-06-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.2.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.2.0...@standardnotes/api-gateway@1.2.1) (2022-06-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* issue with NaN error code responses ([2cb470b](https://github.com/standardnotes/api-gateway/commit/2cb470b99edc2fac8d5c38e4eb16201e55fe8753))
|
||||
|
||||
# [1.2.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.1.6...@standardnotes/api-gateway@1.2.0) (2022-06-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add endpoint to mute marketing emails ([fa2a8da](https://github.com/standardnotes/api-gateway/commit/fa2a8da17bc6588021172adbbc4ecae5bd35f33a))
|
||||
|
||||
## [1.1.6](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.1.5...@standardnotes/api-gateway@1.1.6) (2022-06-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
@@ -7,7 +7,11 @@ import { Logger } from 'winston'
|
||||
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||
import TYPES from '../src/Bootstrap/Types'
|
||||
import { Env } from '../src/Bootstrap/Env'
|
||||
import { DomainEventPublisherInterface, DailyAnalyticsReportGeneratedEvent } from '@standardnotes/domain-events'
|
||||
import {
|
||||
DomainEventPublisherInterface,
|
||||
DailyAnalyticsReportGeneratedEvent,
|
||||
DomainEventService,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { AnalyticsActivity, AnalyticsStoreInterface, Period, StatisticsStoreInterface } from '@standardnotes/analytics'
|
||||
|
||||
const requestReport = async (
|
||||
@@ -23,6 +27,7 @@ const requestReport = async (
|
||||
userIdentifier: '',
|
||||
userIdentifierType: 'uuid',
|
||||
},
|
||||
origin: DomainEventService.ApiGateway,
|
||||
},
|
||||
payload: {
|
||||
applicationStatistics: await statisticsStore.getYesterdayApplicationUsage(),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.1.6",
|
||||
"version": "1.3.2",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -17,16 +17,17 @@
|
||||
"lint": "eslint . --ext .ts",
|
||||
"setup:env": "cp .env.sample .env",
|
||||
"start": "yarn node dist/bin/server.js",
|
||||
"report": "yarn node dist/bin/report.js"
|
||||
"report": "yarn node dist/bin/report.js",
|
||||
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@newrelic/winston-enricher": "^2.1.0",
|
||||
"@sentry/node": "^7.3.0",
|
||||
"@standardnotes/analytics": "^1.4.0",
|
||||
"@standardnotes/auth": "3.19.2",
|
||||
"@standardnotes/domain-events": "2.29.0",
|
||||
"@standardnotes/domain-events-infra": "1.4.127",
|
||||
"@standardnotes/time": "^1.7.0",
|
||||
"@standardnotes/analytics": "^1.6.0",
|
||||
"@standardnotes/auth": "3.19.4",
|
||||
"@standardnotes/domain-events": "2.32.5",
|
||||
"@standardnotes/domain-events-infra": "1.5.5",
|
||||
"@standardnotes/time": "^1.7.1",
|
||||
"aws-sdk": "^2.1160.0",
|
||||
"axios": "0.24.0",
|
||||
"cors": "2.8.5",
|
||||
@@ -55,6 +56,7 @@
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "^28.1.1",
|
||||
"nodemon": "^2.0.16",
|
||||
"npm-check-updates": "^14.1.1",
|
||||
"ts-jest": "^28.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,10 @@ export class AuthMiddleware extends BaseMiddleware {
|
||||
response.setHeader('content-type', (error as AxiosError).response?.headers['content-type'] as string)
|
||||
}
|
||||
|
||||
const errorCode = (error as AxiosError).isAxiosError ? +((error as AxiosError).code as string) : 500
|
||||
const errorCode =
|
||||
(error as AxiosError).isAxiosError && !isNaN(+((error as AxiosError).code as string))
|
||||
? +((error as AxiosError).code as string)
|
||||
: 500
|
||||
|
||||
response.status(errorCode).send(errorMessage)
|
||||
|
||||
|
||||
@@ -88,7 +88,10 @@ export class SubscriptionTokenAuthMiddleware extends BaseMiddleware {
|
||||
response.setHeader('content-type', (error as AxiosError).response?.headers['content-type'] as string)
|
||||
}
|
||||
|
||||
const errorCode = (error as AxiosError).isAxiosError ? +((error as AxiosError).code as string) : 500
|
||||
const errorCode =
|
||||
(error as AxiosError).isAxiosError && !isNaN(+((error as AxiosError).code as string))
|
||||
? +((error as AxiosError).code as string)
|
||||
: 500
|
||||
|
||||
response.status(errorCode).send(errorMessage)
|
||||
|
||||
|
||||
@@ -49,4 +49,14 @@ export class ActionsController extends BaseHttpController {
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
|
||||
@httpGet('/marketing-emails/mute/:settingUuid')
|
||||
async muteMarketingEmails(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
response,
|
||||
`internal/settings/marketing-emails/${request.params.settingUuid}/mute`,
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,10 @@ export class HttpService implements HttpServiceInterface {
|
||||
response.setHeader('content-type', (error as AxiosError).response?.headers['content-type'] as string)
|
||||
}
|
||||
|
||||
const errorCode = (error as AxiosError).isAxiosError ? +((error as AxiosError).code as string) : 500
|
||||
const errorCode =
|
||||
(error as AxiosError).isAxiosError && !isNaN(+((error as AxiosError).code as string))
|
||||
? +((error as AxiosError).code as string)
|
||||
: 500
|
||||
|
||||
response.status(errorCode).send(errorMessage)
|
||||
}
|
||||
@@ -159,6 +162,12 @@ export class HttpService implements HttpServiceInterface {
|
||||
|
||||
this.applyResponseHeaders(serviceResponse, response)
|
||||
|
||||
if (this.responseShouldNotBeDecorated(serviceResponse)) {
|
||||
response.status(serviceResponse.status).send(serviceResponse.data)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
response.status(serviceResponse.status).send({
|
||||
meta: {
|
||||
auth: {
|
||||
@@ -210,6 +219,13 @@ export class HttpService implements HttpServiceInterface {
|
||||
return payload
|
||||
}
|
||||
|
||||
private responseShouldNotBeDecorated(serviceResponse: AxiosResponse): boolean {
|
||||
return (
|
||||
serviceResponse.headers['content-type'] !== undefined &&
|
||||
serviceResponse.headers['content-type'].toLowerCase().includes('text/html')
|
||||
)
|
||||
}
|
||||
|
||||
private applyResponseHeaders(serviceResponse: AxiosResponse, response: Response): void {
|
||||
const returnedHeadersFromUnderlyingService = [
|
||||
'access-control-allow-methods',
|
||||
|
||||
@@ -3,6 +3,60 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.3.6](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.3.5...@standardnotes/auth-server@1.3.6) (2022-06-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* mute marketing value encryption version setting ([1d4bce7](https://github.com/standardnotes/auth/commit/1d4bce73098e77076b8d78b575582452e8a5f94d))
|
||||
|
||||
## [1.3.5](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.3.4...@standardnotes/auth-server@1.3.5) (2022-06-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add logs for processing email campaigns ([2e92553](https://github.com/standardnotes/auth/commit/2e9255344eeb68375a78020e5888c056c850c5e5))
|
||||
|
||||
## [1.3.4](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.3.3...@standardnotes/auth-server@1.3.4) (2022-06-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.3.3](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.3.2...@standardnotes/auth-server@1.3.3) (2022-06-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* change response type to html for muting marketing emails ([ba373eb](https://github.com/standardnotes/auth/commit/ba373ebc6b3038f7de2fab40f0429a655dbe2499))
|
||||
|
||||
## [1.3.2](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.3.1...@standardnotes/auth-server@1.3.2) (2022-06-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.3.1](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.3.0...@standardnotes/auth-server@1.3.1) (2022-06-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
# [1.3.0](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.2.1...@standardnotes/auth-server@1.3.0) (2022-06-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add endpoint for muting marketing emails ([6d7de4a](https://github.com/standardnotes/auth/commit/6d7de4a8daaf7569b8f2e031854e8baaff64a469))
|
||||
|
||||
## [1.2.1](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.2.0...@standardnotes/auth-server@1.2.1) (2022-06-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add setting uuid to email requests ([214684e](https://github.com/standardnotes/auth/commit/214684eae733675b7cefa522202e53d9556e5279))
|
||||
|
||||
# [1.2.0](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.1.7...@standardnotes/auth-server@1.2.0) (2022-06-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add docker commands to start sending emails ([210a314](https://github.com/standardnotes/auth/commit/210a314c8199e47e5b6a113bd60401c16bb95291))
|
||||
|
||||
## [1.1.7](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.1.6...@standardnotes/auth-server@1.1.7) (2022-06-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add sending out email campaigns ([bf14ec0](https://github.com/standardnotes/auth/commit/bf14ec05f92d893e7c74a4f29ef7e426bee9d768))
|
||||
|
||||
## [1.1.6](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.1.5...@standardnotes/auth-server@1.1.6) (2022-06-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
151
packages/auth/bin/email.ts
Normal file
151
packages/auth/bin/email.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import 'newrelic'
|
||||
|
||||
import { Stream } from 'stream'
|
||||
|
||||
import { Logger } from 'winston'
|
||||
import * as dayjs from 'dayjs'
|
||||
import * as utc from 'dayjs/plugin/utc'
|
||||
|
||||
import { UserRepositoryInterface } from '../src/Domain/User/UserRepositoryInterface'
|
||||
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||
import TYPES from '../src/Bootstrap/Types'
|
||||
import { Env } from '../src/Bootstrap/Env'
|
||||
import { SettingServiceInterface } from '../src/Domain/Setting/SettingServiceInterface'
|
||||
import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface'
|
||||
import { UserSubscriptionRepositoryInterface } from '../src/Domain/Subscription/UserSubscriptionRepositoryInterface'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { MuteMarketingEmailsOption, SettingName } from '@standardnotes/settings'
|
||||
import { EmailMessageIdentifier } from '@standardnotes/common'
|
||||
import { User } from '../src/Domain/User/User'
|
||||
import { EncryptionVersion } from '../src/Domain/Encryption/EncryptionVersion'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
const inputArgs = process.argv.slice(2)
|
||||
const emailMessageIdentifier = inputArgs[0]
|
||||
|
||||
const sendEmailCampaign = async (
|
||||
userRepository: UserRepositoryInterface,
|
||||
settingService: SettingServiceInterface,
|
||||
userSubscriptionRepository: UserSubscriptionRepositoryInterface,
|
||||
timer: TimerInterface,
|
||||
domainEventFactory: DomainEventFactoryInterface,
|
||||
domainEventPublisher: DomainEventPublisherInterface,
|
||||
logger: Logger,
|
||||
): Promise<void> => {
|
||||
const stream = await userRepository.streamAll()
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
stream
|
||||
.pipe(
|
||||
new Stream.Transform({
|
||||
objectMode: true,
|
||||
transform: async (rawUserData, _encoding, callback) => {
|
||||
logger.info(`Processing ${emailMessageIdentifier} email campaign for user ${rawUserData.user_uuid}`)
|
||||
|
||||
let emailsMutedSetting = await settingService.findSettingWithDecryptedValue({
|
||||
userUuid: rawUserData.user_uuid,
|
||||
settingName: SettingName.MuteMarketingEmails,
|
||||
})
|
||||
|
||||
if (emailsMutedSetting === null) {
|
||||
const user = (await userRepository.findOneByUuid(rawUserData.user_uuid)) as User
|
||||
|
||||
const { setting } = await settingService.createOrReplace({
|
||||
user,
|
||||
props: {
|
||||
name: SettingName.MuteMarketingEmails,
|
||||
unencryptedValue: MuteMarketingEmailsOption.NotMuted,
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
sensitive: false,
|
||||
},
|
||||
})
|
||||
emailsMutedSetting = setting
|
||||
}
|
||||
|
||||
if (emailsMutedSetting.value === MuteMarketingEmailsOption.Muted) {
|
||||
callback()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let activeSubscription = false
|
||||
let subscriptionPlanName = null
|
||||
|
||||
const userSubscription = await userSubscriptionRepository.findOneByUserUuid(rawUserData.user_uuid)
|
||||
if (userSubscription !== null) {
|
||||
activeSubscription =
|
||||
!userSubscription.cancelled && userSubscription.endsAt > timer.getTimestampInMicroseconds()
|
||||
subscriptionPlanName = userSubscription.planName
|
||||
}
|
||||
|
||||
await domainEventPublisher.publish(
|
||||
domainEventFactory.createEmailMessageRequestedEvent({
|
||||
userEmail: rawUserData.user_email,
|
||||
messageIdentifier: emailMessageIdentifier as EmailMessageIdentifier,
|
||||
context: {
|
||||
activeSubscription,
|
||||
subscriptionPlanName,
|
||||
muteEmailsSettingUuid: emailsMutedSetting.uuid,
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
callback()
|
||||
},
|
||||
}),
|
||||
)
|
||||
.on('finish', resolve)
|
||||
.on('error', reject)
|
||||
})
|
||||
}
|
||||
|
||||
const container = new ContainerConfigLoader()
|
||||
void container.load().then((container) => {
|
||||
dayjs.extend(utc)
|
||||
|
||||
const env: Env = new Env()
|
||||
env.load()
|
||||
|
||||
const logger: Logger = container.get(TYPES.Logger)
|
||||
|
||||
logger.info(`Starting email campaign for email ${emailMessageIdentifier} ...`)
|
||||
|
||||
if (!emailMessageIdentifier) {
|
||||
logger.error('No email message identifier passed as argument. Skipped sending.')
|
||||
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const userRepository: UserRepositoryInterface = container.get(TYPES.UserRepository)
|
||||
const settingService: SettingServiceInterface = container.get(TYPES.SettingService)
|
||||
const userSubscriptionRepository: UserSubscriptionRepositoryInterface = container.get(
|
||||
TYPES.UserSubscriptionRepository,
|
||||
)
|
||||
const timer: TimerInterface = container.get(TYPES.Timer)
|
||||
const domainEventFactory: DomainEventFactoryInterface = container.get(TYPES.DomainEventFactory)
|
||||
const domainEventPublisher: DomainEventPublisherInterface = container.get(TYPES.DomainEventPublisher)
|
||||
|
||||
Promise.resolve(
|
||||
sendEmailCampaign(
|
||||
userRepository,
|
||||
settingService,
|
||||
userSubscriptionRepository,
|
||||
timer,
|
||||
domainEventFactory,
|
||||
domainEventPublisher,
|
||||
logger,
|
||||
),
|
||||
)
|
||||
.then(() => {
|
||||
logger.info(`${emailMessageIdentifier} email campaign complete.`)
|
||||
|
||||
process.exit(0)
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error(`Could not finish ${emailMessageIdentifier} email campaign: ${error.message}`)
|
||||
|
||||
process.exit(1)
|
||||
})
|
||||
})
|
||||
@@ -44,6 +44,12 @@ case "$COMMAND" in
|
||||
yarn workspace @standardnotes/auth-server daily-backup:one_drive
|
||||
;;
|
||||
|
||||
'email-campaign' )
|
||||
echo "Starting Email Campaign Sending..."
|
||||
MESSAGE_IDENTIFIER=$1 && shift 1
|
||||
yarn workspace @standardnotes/auth-server email-campaign $MESSAGE_IDENTIFIER
|
||||
;;
|
||||
|
||||
* )
|
||||
echo "Unknown command"
|
||||
;;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/auth-server",
|
||||
"version": "1.1.6",
|
||||
"version": "1.3.6",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -25,24 +25,26 @@
|
||||
"daily-backup:google_drive": "yarn node dist/bin/backup.js google_drive daily",
|
||||
"daily-backup:one_drive": "yarn node dist/bin/backup.js one_drive daily",
|
||||
"weekly-backup:email": "yarn node dist/bin/backup.js email weekly",
|
||||
"typeorm": "typeorm-ts-node-commonjs"
|
||||
"email-campaign": "yarn node dist/bin/email.js",
|
||||
"typeorm": "typeorm-ts-node-commonjs",
|
||||
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@newrelic/winston-enricher": "^2.1.0",
|
||||
"@sentry/node": "^7.3.0",
|
||||
"@standardnotes/analytics": "^1.6.0",
|
||||
"@standardnotes/api": "^1.1.13",
|
||||
"@standardnotes/auth": "^3.19.2",
|
||||
"@standardnotes/common": "^1.23.0",
|
||||
"@standardnotes/domain-events": "^2.31.1",
|
||||
"@standardnotes/domain-events-infra": "^1.4.135",
|
||||
"@standardnotes/features": "^1.45.2",
|
||||
"@standardnotes/responses": "^1.6.15",
|
||||
"@standardnotes/scheduler": "^1.1.1",
|
||||
"@standardnotes/settings": "^1.14.2",
|
||||
"@standardnotes/sncrypto-common": "^1.8.1",
|
||||
"@standardnotes/sncrypto-node": "^1.8.1",
|
||||
"@standardnotes/time": "^1.6.8",
|
||||
"@standardnotes/api": "^1.1.18",
|
||||
"@standardnotes/auth": "^3.19.4",
|
||||
"@standardnotes/common": "^1.23.1",
|
||||
"@standardnotes/domain-events": "^2.32.5",
|
||||
"@standardnotes/domain-events-infra": "^1.5.5",
|
||||
"@standardnotes/features": "^1.46.0",
|
||||
"@standardnotes/responses": "^1.6.38",
|
||||
"@standardnotes/scheduler": "^1.1.2",
|
||||
"@standardnotes/settings": "^1.15.0",
|
||||
"@standardnotes/sncrypto-common": "^1.9.0",
|
||||
"@standardnotes/sncrypto-node": "^1.8.3",
|
||||
"@standardnotes/time": "^1.7.1",
|
||||
"aws-sdk": "^2.1159.0",
|
||||
"axios": "0.24.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
@@ -80,6 +82,7 @@
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "^28.1.1",
|
||||
"nodemon": "^2.0.16",
|
||||
"npm-check-updates": "^14.1.1",
|
||||
"ts-jest": "^28.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,6 +190,7 @@ import { GetUserAnalyticsId } from '../Domain/UseCase/GetUserAnalyticsId/GetUser
|
||||
import { AuthController } from '../Controller/AuthController'
|
||||
import { VerifyPredicate } from '../Domain/UseCase/VerifyPredicate/VerifyPredicate'
|
||||
import { PredicateVerificationRequestedEventHandler } from '../Domain/Handler/PredicateVerificationRequestedEventHandler'
|
||||
import { MuteMarketingEmails } from '../Domain/UseCase/MuteMarketingEmails/MuteMarketingEmails'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const newrelicWinstonEnricher = require('@newrelic/winston-enricher')
|
||||
@@ -409,6 +410,7 @@ export class ContainerConfigLoader {
|
||||
.to(CreateOfflineSubscriptionToken)
|
||||
container.bind<MuteFailedBackupsEmails>(TYPES.MuteFailedBackupsEmails).to(MuteFailedBackupsEmails)
|
||||
container.bind<MuteSignInEmails>(TYPES.MuteSignInEmails).to(MuteSignInEmails)
|
||||
container.bind<MuteMarketingEmails>(TYPES.MuteMarketingEmails).to(MuteMarketingEmails)
|
||||
container.bind<CreateValetToken>(TYPES.CreateValetToken).to(CreateValetToken)
|
||||
container.bind<CreateListedAccount>(TYPES.CreateListedAccount).to(CreateListedAccount)
|
||||
container.bind<InviteToSharedSubscription>(TYPES.InviteToSharedSubscription).to(InviteToSharedSubscription)
|
||||
|
||||
@@ -113,6 +113,7 @@ const TYPES = {
|
||||
AuthenticateOfflineSubscriptionToken: Symbol.for('AuthenticateOfflineSubscriptionToken'),
|
||||
MuteFailedBackupsEmails: Symbol.for('MuteFailedBackupsEmails'),
|
||||
MuteSignInEmails: Symbol.for('MuteSignInEmails'),
|
||||
MuteMarketingEmails: Symbol.for('MuteMarketingEmails'),
|
||||
CreateValetToken: Symbol.for('CreateValetToken'),
|
||||
CreateListedAccount: Symbol.for('CreateListedAccount'),
|
||||
InviteToSharedSubscription: Symbol.for('InviteToSharedSubscription'),
|
||||
|
||||
@@ -9,18 +9,21 @@ import { GetUserFeatures } from '../Domain/UseCase/GetUserFeatures/GetUserFeatur
|
||||
import { GetSetting } from '../Domain/UseCase/GetSetting/GetSetting'
|
||||
import { MuteFailedBackupsEmails } from '../Domain/UseCase/MuteFailedBackupsEmails/MuteFailedBackupsEmails'
|
||||
import { MuteSignInEmails } from '../Domain/UseCase/MuteSignInEmails/MuteSignInEmails'
|
||||
import { MuteMarketingEmails } from '../Domain/UseCase/MuteMarketingEmails/MuteMarketingEmails'
|
||||
|
||||
describe('InternalController', () => {
|
||||
let getUserFeatures: GetUserFeatures
|
||||
let getSetting: GetSetting
|
||||
let muteFailedBackupsEmails: MuteFailedBackupsEmails
|
||||
let muteSignInEmails: MuteSignInEmails
|
||||
let muteMarketingEmails: MuteMarketingEmails
|
||||
|
||||
let request: express.Request
|
||||
let response: express.Response
|
||||
let user: User
|
||||
|
||||
const createController = () =>
|
||||
new InternalController(getUserFeatures, getSetting, muteFailedBackupsEmails, muteSignInEmails)
|
||||
new InternalController(getUserFeatures, getSetting, muteFailedBackupsEmails, muteSignInEmails, muteMarketingEmails)
|
||||
|
||||
beforeEach(() => {
|
||||
user = {} as jest.Mocked<User>
|
||||
@@ -38,11 +41,19 @@ describe('InternalController', () => {
|
||||
muteSignInEmails = {} as jest.Mocked<MuteSignInEmails>
|
||||
muteSignInEmails.execute = jest.fn()
|
||||
|
||||
muteMarketingEmails = {} as jest.Mocked<MuteMarketingEmails>
|
||||
muteMarketingEmails.execute = jest.fn()
|
||||
|
||||
request = {
|
||||
headers: {},
|
||||
body: {},
|
||||
params: {},
|
||||
} as jest.Mocked<express.Request>
|
||||
|
||||
response = {} as jest.Mocked<express.Response>
|
||||
response.setHeader = jest.fn()
|
||||
response.status = jest.fn().mockReturnThis()
|
||||
response.send = jest.fn()
|
||||
})
|
||||
|
||||
it('should get user features', async () => {
|
||||
@@ -161,4 +172,31 @@ describe('InternalController', () => {
|
||||
|
||||
expect(result.statusCode).toEqual(404)
|
||||
})
|
||||
|
||||
it('should mute marketing emails user setting', async () => {
|
||||
request.params.settingUuid = '1-2-3'
|
||||
|
||||
muteMarketingEmails.execute = jest.fn().mockReturnValue({ success: true, message: 'foobar' })
|
||||
|
||||
await createController().muteMarketingEmails(request, response)
|
||||
|
||||
expect(muteMarketingEmails.execute).toHaveBeenCalledWith({ settingUuid: '1-2-3' })
|
||||
|
||||
expect(response.setHeader).toHaveBeenCalledWith('content-type', 'text/html')
|
||||
expect(response.send).toHaveBeenCalledWith('foobar')
|
||||
})
|
||||
|
||||
it('should not mute marketing emails user setting if it does not exist', async () => {
|
||||
request.params.settingUuid = '1-2-3'
|
||||
|
||||
muteMarketingEmails.execute = jest.fn().mockReturnValue({ success: false, message: 'foobar' })
|
||||
|
||||
await createController().muteMarketingEmails(request, response)
|
||||
|
||||
expect(muteMarketingEmails.execute).toHaveBeenCalledWith({ settingUuid: '1-2-3' })
|
||||
|
||||
expect(response.setHeader).toHaveBeenCalledWith('content-type', 'text/html')
|
||||
expect(response.status).toHaveBeenCalledWith(404)
|
||||
expect(response.send).toHaveBeenCalledWith('foobar')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Request } from 'express'
|
||||
import { Request, Response } from 'express'
|
||||
import { inject } from 'inversify'
|
||||
import {
|
||||
BaseHttpController,
|
||||
@@ -11,6 +11,7 @@ import TYPES from '../Bootstrap/Types'
|
||||
import { GetSetting } from '../Domain/UseCase/GetSetting/GetSetting'
|
||||
import { GetUserFeatures } from '../Domain/UseCase/GetUserFeatures/GetUserFeatures'
|
||||
import { MuteFailedBackupsEmails } from '../Domain/UseCase/MuteFailedBackupsEmails/MuteFailedBackupsEmails'
|
||||
import { MuteMarketingEmails } from '../Domain/UseCase/MuteMarketingEmails/MuteMarketingEmails'
|
||||
import { MuteSignInEmails } from '../Domain/UseCase/MuteSignInEmails/MuteSignInEmails'
|
||||
|
||||
@controller('/internal')
|
||||
@@ -20,6 +21,7 @@ export class InternalController extends BaseHttpController {
|
||||
@inject(TYPES.GetSetting) private doGetSetting: GetSetting,
|
||||
@inject(TYPES.MuteFailedBackupsEmails) private doMuteFailedBackupsEmails: MuteFailedBackupsEmails,
|
||||
@inject(TYPES.MuteSignInEmails) private doMuteSignInEmails: MuteSignInEmails,
|
||||
@inject(TYPES.MuteMarketingEmails) private doMuteMarketingEmails: MuteMarketingEmails,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
@@ -80,4 +82,22 @@ export class InternalController extends BaseHttpController {
|
||||
|
||||
return this.json({ message: result.message }, 404)
|
||||
}
|
||||
|
||||
@httpGet('/settings/marketing-emails/:settingUuid/mute')
|
||||
async muteMarketingEmails(request: Request, response: Response): Promise<void> {
|
||||
const { settingUuid } = request.params
|
||||
const result = await this.doMuteMarketingEmails.execute({
|
||||
settingUuid,
|
||||
})
|
||||
|
||||
response.setHeader('content-type', 'text/html')
|
||||
|
||||
if (result.success) {
|
||||
response.send(result.message)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
response.status(404).send(result.message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { RoleName } from '@standardnotes/common'
|
||||
import { EmailMessageIdentifier, RoleName } from '@standardnotes/common'
|
||||
import { PredicateName, PredicateAuthority, PredicateVerificationResult } from '@standardnotes/scheduler'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
@@ -18,6 +18,35 @@ describe('DomainEventFactory', () => {
|
||||
timer.getUTCDate = jest.fn().mockReturnValue(new Date(1))
|
||||
})
|
||||
|
||||
it('should create a EMAIL_MESSAGE_REQUESTED event', () => {
|
||||
expect(
|
||||
createFactory().createEmailMessageRequestedEvent({
|
||||
userEmail: 'test@test.te',
|
||||
messageIdentifier: EmailMessageIdentifier.ENCOURAGE_EMAIL_BACKUPS,
|
||||
context: {
|
||||
foo: 'bar',
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
createdAt: expect.any(Date),
|
||||
meta: {
|
||||
correlation: {
|
||||
userIdentifier: 'test@test.te',
|
||||
userIdentifierType: 'email',
|
||||
},
|
||||
origin: 'auth',
|
||||
},
|
||||
payload: {
|
||||
messageIdentifier: 'ENCOURAGE_EMAIL_BACKUPS',
|
||||
userEmail: 'test@test.te',
|
||||
context: {
|
||||
foo: 'bar',
|
||||
},
|
||||
},
|
||||
type: 'EMAIL_MESSAGE_REQUESTED',
|
||||
})
|
||||
})
|
||||
|
||||
it('should create a PREDICATE_VERIFIED event', () => {
|
||||
expect(
|
||||
createFactory().createPredicateVerifiedEvent({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RoleName, Uuid } from '@standardnotes/common'
|
||||
import { EmailMessageIdentifier, RoleName, Uuid } from '@standardnotes/common'
|
||||
import {
|
||||
AccountDeletionRequestedEvent,
|
||||
UserEmailChangedEvent,
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
SharedSubscriptionInvitationCanceledEvent,
|
||||
PredicateVerifiedEvent,
|
||||
DomainEventService,
|
||||
EmailMessageRequestedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { Predicate, PredicateVerificationResult } from '@standardnotes/scheduler'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
@@ -26,6 +27,25 @@ import { DomainEventFactoryInterface } from './DomainEventFactoryInterface'
|
||||
export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||
constructor(@inject(TYPES.Timer) private timer: TimerInterface) {}
|
||||
|
||||
createEmailMessageRequestedEvent(dto: {
|
||||
userEmail: string
|
||||
messageIdentifier: EmailMessageIdentifier
|
||||
context: Record<string, unknown>
|
||||
}): EmailMessageRequestedEvent {
|
||||
return {
|
||||
type: 'EMAIL_MESSAGE_REQUESTED',
|
||||
createdAt: this.timer.getUTCDate(),
|
||||
meta: {
|
||||
correlation: {
|
||||
userIdentifier: dto.userEmail,
|
||||
userIdentifierType: 'email',
|
||||
},
|
||||
origin: DomainEventService.Auth,
|
||||
},
|
||||
payload: dto,
|
||||
}
|
||||
}
|
||||
|
||||
createPredicateVerifiedEvent(dto: {
|
||||
userUuid: Uuid
|
||||
predicate: Predicate
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Uuid, RoleName } from '@standardnotes/common'
|
||||
import { Uuid, RoleName, EmailMessageIdentifier } from '@standardnotes/common'
|
||||
import { Predicate, PredicateVerificationResult } from '@standardnotes/scheduler'
|
||||
import {
|
||||
AccountDeletionRequestedEvent,
|
||||
@@ -14,10 +14,16 @@ import {
|
||||
SharedSubscriptionInvitationCreatedEvent,
|
||||
SharedSubscriptionInvitationCanceledEvent,
|
||||
PredicateVerifiedEvent,
|
||||
EmailMessageRequestedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { InviteeIdentifierType } from '../SharedSubscription/InviteeIdentifierType'
|
||||
|
||||
export interface DomainEventFactoryInterface {
|
||||
createEmailMessageRequestedEvent(dto: {
|
||||
userEmail: string
|
||||
messageIdentifier: EmailMessageIdentifier
|
||||
context: Record<string, unknown>
|
||||
}): EmailMessageRequestedEvent
|
||||
createUserSignedInEvent(dto: {
|
||||
userUuid: string
|
||||
userEmail: string
|
||||
|
||||
@@ -39,13 +39,13 @@ describe('SettingsAssociationService', () => {
|
||||
it('should return the default set of settings for a newly registered user', () => {
|
||||
const settings = createService().getDefaultSettingsAndValuesForNewUser()
|
||||
const flatSettings = [...(settings as Map<SettingName, SettingDescription>).keys()]
|
||||
expect(flatSettings).toEqual(['MUTE_SIGN_IN_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', () => {
|
||||
const settings = createService().getDefaultSettingsAndValuesForNewVaultAccount()
|
||||
const flatSettings = [...(settings as Map<SettingName, SettingDescription>).keys()]
|
||||
expect(flatSettings).toEqual(['MUTE_SIGN_IN_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')
|
||||
})
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { PermissionName } from '@standardnotes/features'
|
||||
import { LogSessionUserAgentOption, MuteSignInEmailsOption, SettingName } from '@standardnotes/settings'
|
||||
import {
|
||||
LogSessionUserAgentOption,
|
||||
MuteMarketingEmailsOption,
|
||||
MuteSignInEmailsOption,
|
||||
SettingName,
|
||||
} from '@standardnotes/settings'
|
||||
import { injectable } from 'inversify'
|
||||
|
||||
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
||||
@@ -14,6 +19,7 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt
|
||||
SettingName.MuteFailedBackupsEmails,
|
||||
SettingName.MuteFailedCloudBackupsEmails,
|
||||
SettingName.MuteSignInEmails,
|
||||
SettingName.MuteMarketingEmails,
|
||||
SettingName.DropboxBackupFrequency,
|
||||
SettingName.GoogleDriveBackupFrequency,
|
||||
SettingName.OneDriveBackupFrequency,
|
||||
@@ -28,6 +34,7 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt
|
||||
SettingName.MuteFailedBackupsEmails,
|
||||
SettingName.MuteFailedCloudBackupsEmails,
|
||||
SettingName.MuteSignInEmails,
|
||||
SettingName.MuteMarketingEmails,
|
||||
SettingName.ListedAuthorSecrets,
|
||||
SettingName.LogSessionUserAgent,
|
||||
]
|
||||
@@ -47,6 +54,14 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt
|
||||
value: MuteSignInEmailsOption.NotMuted,
|
||||
},
|
||||
],
|
||||
[
|
||||
SettingName.MuteMarketingEmails,
|
||||
{
|
||||
sensitive: false,
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
value: MuteMarketingEmailsOption.NotMuted,
|
||||
},
|
||||
],
|
||||
[
|
||||
SettingName.LogSessionUserAgent,
|
||||
{
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import 'reflect-metadata'
|
||||
import { Setting } from '../../Setting/Setting'
|
||||
import { SettingRepositoryInterface } from '../../Setting/SettingRepositoryInterface'
|
||||
|
||||
import { MuteMarketingEmails } from './MuteMarketingEmails'
|
||||
|
||||
describe('MuteMarketingEmails', () => {
|
||||
let settingRepository: SettingRepositoryInterface
|
||||
|
||||
const createUseCase = () => new MuteMarketingEmails(settingRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
const setting = {} as jest.Mocked<Setting>
|
||||
|
||||
settingRepository = {} as jest.Mocked<SettingRepositoryInterface>
|
||||
settingRepository.findOneByUuidAndNames = jest.fn().mockReturnValue(setting)
|
||||
settingRepository.save = jest.fn()
|
||||
})
|
||||
|
||||
it('should not succeed if extension setting is not found', async () => {
|
||||
settingRepository.findOneByUuidAndNames = jest.fn().mockReturnValue(null)
|
||||
|
||||
expect(await createUseCase().execute({ settingUuid: '1-2-3' })).toEqual({
|
||||
success: false,
|
||||
message: 'Could not find setting setting.',
|
||||
})
|
||||
})
|
||||
|
||||
it('should update mute email setting on extension setting', async () => {
|
||||
expect(await createUseCase().execute({ settingUuid: '1-2-3' })).toEqual({
|
||||
success: true,
|
||||
message: 'These emails have been muted.',
|
||||
})
|
||||
|
||||
expect(settingRepository.save).toHaveBeenCalledWith({
|
||||
value: 'muted',
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,34 @@
|
||||
import { MuteMarketingEmailsOption, SettingName } from '@standardnotes/settings'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import TYPES from '../../../Bootstrap/Types'
|
||||
import { SettingRepositoryInterface } from '../../Setting/SettingRepositoryInterface'
|
||||
import { UseCaseInterface } from '../UseCaseInterface'
|
||||
import { MuteMarketingEmailsDTO } from './MuteMarketingEmailsDTO'
|
||||
import { MuteMarketingEmailsResponse } from './MuteMarketingEmailsResponse'
|
||||
|
||||
@injectable()
|
||||
export class MuteMarketingEmails implements UseCaseInterface {
|
||||
constructor(@inject(TYPES.SettingRepository) private settingRepository: SettingRepositoryInterface) {}
|
||||
|
||||
async execute(dto: MuteMarketingEmailsDTO): Promise<MuteMarketingEmailsResponse> {
|
||||
const setting = await this.settingRepository.findOneByUuidAndNames(dto.settingUuid, [
|
||||
SettingName.MuteMarketingEmails,
|
||||
])
|
||||
|
||||
if (setting === null) {
|
||||
return {
|
||||
success: false,
|
||||
message: 'Could not find setting setting.',
|
||||
}
|
||||
}
|
||||
|
||||
setting.value = MuteMarketingEmailsOption.Muted
|
||||
|
||||
await this.settingRepository.save(setting)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'These emails have been muted.',
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type MuteMarketingEmailsDTO = {
|
||||
settingUuid: string
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export type MuteMarketingEmailsResponse = {
|
||||
success: boolean
|
||||
message: string
|
||||
}
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.8](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.1.7...@standardnotes/files-server@1.1.8) (2022-06-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.1.7](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.1.6...@standardnotes/files-server@1.1.7) (2022-06-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.1.6](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.1.5...@standardnotes/files-server@1.1.6) (2022-06-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files-server",
|
||||
"version": "1.1.6",
|
||||
"version": "1.1.8",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -21,17 +21,18 @@
|
||||
"pretest": "yarn lint && yarn build",
|
||||
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
|
||||
"start": "yarn node dist/bin/server.js",
|
||||
"worker": "yarn node dist/bin/worker.js"
|
||||
"worker": "yarn node dist/bin/worker.js",
|
||||
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sentry/node": "^7.3.0",
|
||||
"@standardnotes/auth": "^3.18.9",
|
||||
"@standardnotes/common": "^1.19.4",
|
||||
"@standardnotes/domain-events": "^2.27.6",
|
||||
"@standardnotes/domain-events-infra": "^1.4.93",
|
||||
"@standardnotes/sncrypto-common": "^1.3.0",
|
||||
"@standardnotes/sncrypto-node": "^1.3.0",
|
||||
"@standardnotes/time": "^1.4.5",
|
||||
"@standardnotes/auth": "^3.19.4",
|
||||
"@standardnotes/common": "^1.23.1",
|
||||
"@standardnotes/domain-events": "^2.32.5",
|
||||
"@standardnotes/domain-events-infra": "^1.5.5",
|
||||
"@standardnotes/sncrypto-common": "^1.9.0",
|
||||
"@standardnotes/sncrypto-node": "^1.8.3",
|
||||
"@standardnotes/time": "^1.7.1",
|
||||
"aws-sdk": "^2.1158.0",
|
||||
"connect-busboy": "^1.0.0",
|
||||
"cors": "^2.8.5",
|
||||
@@ -52,7 +53,7 @@
|
||||
"winston": "^3.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@standardnotes/config": "2.0.1",
|
||||
"@standardnotes/config": "2.4.3",
|
||||
"@types/connect-busboy": "^1.0.0",
|
||||
"@types/cors": "^2.8.9",
|
||||
"@types/express": "^4.17.11",
|
||||
@@ -67,6 +68,7 @@
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "^28.1.1",
|
||||
"nodemon": "^2.0.16",
|
||||
"npm-check-updates": "^14.1.1",
|
||||
"ts-jest": "^28.0.1",
|
||||
"uuid": "^8.3.2"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.2.3](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.2.2...@standardnotes/scheduler-server@1.2.3) (2022-06-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.2.2](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.2.1...@standardnotes/scheduler-server@1.2.2) (2022-06-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.2.1](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.2.0...@standardnotes/scheduler-server@1.2.1) (2022-06-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* change subscription encouragement scheduled from 14 to 30 days ([e12c9c4](https://github.com/standardnotes/server/commit/e12c9c47a73efa5e5af15144e653195455f90591))
|
||||
|
||||
# [1.2.0](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.1.5...@standardnotes/scheduler-server@1.2.0) (2022-06-27)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/scheduler-server",
|
||||
"version": "1.2.0",
|
||||
"version": "1.2.3",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -19,16 +19,17 @@
|
||||
"worker": "yarn node dist/bin/worker.js",
|
||||
"verify:jobs": "yarn node dist/bin/verify.js",
|
||||
"setup:env": "cp .env.sample .env",
|
||||
"typeorm": "typeorm-ts-node-commonjs"
|
||||
"typeorm": "typeorm-ts-node-commonjs",
|
||||
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@newrelic/winston-enricher": "^2.1.0",
|
||||
"@sentry/node": "^7.3.0",
|
||||
"@standardnotes/common": "^1.23.0",
|
||||
"@standardnotes/domain-events": "^2.32.3",
|
||||
"@standardnotes/domain-events-infra": "^1.5.0",
|
||||
"@standardnotes/scheduler": "^1.1.0",
|
||||
"@standardnotes/time": "^1.7.0",
|
||||
"@standardnotes/common": "^1.23.1",
|
||||
"@standardnotes/domain-events": "^2.32.5",
|
||||
"@standardnotes/domain-events-infra": "^1.5.5",
|
||||
"@standardnotes/scheduler": "^1.1.2",
|
||||
"@standardnotes/time": "^1.7.1",
|
||||
"aws-sdk": "^2.1158.0",
|
||||
"dayjs": "^1.11.3",
|
||||
"dotenv": "8.2.0",
|
||||
@@ -48,6 +49,7 @@
|
||||
"@typescript-eslint/eslint-plugin": "^5.29.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "^28.1.1",
|
||||
"npm-check-updates": "^14.1.1",
|
||||
"ts-jest": "^28.0.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ export class UserRegisteredEventHandler implements DomainEventHandlerInterface {
|
||||
private async scheduleEncourageSubscriptionPurchasing(event: UserRegisteredEvent): Promise<void> {
|
||||
const job = new Job()
|
||||
job.name = JobName.ENCOURAGE_SUBSCRIPTION_PURCHASING
|
||||
job.scheduledAt = this.timer.convertDateToMicroseconds(this.timer.getUTCDateNDaysAhead(14))
|
||||
job.scheduledAt = this.timer.convertDateToMicroseconds(this.timer.getUTCDateNDaysAhead(30))
|
||||
job.createdAt = this.timer.getTimestampInMicroseconds()
|
||||
job.status = JobStatus.Pending
|
||||
job.userIdentifier = event.payload.email
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.8](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.7...@standardnotes/syncing-server@1.1.8) (2022-06-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
## [1.1.7](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.6...@standardnotes/syncing-server@1.1.7) (2022-06-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
## [1.1.6](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.5...@standardnotes/syncing-server@1.1.6) (2022-06-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/syncing-server",
|
||||
"version": "1.1.6",
|
||||
"version": "1.1.8",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -19,19 +19,20 @@
|
||||
"pretest": "yarn lint && yarn build",
|
||||
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
|
||||
"start": "yarn node dist/bin/server.js",
|
||||
"worker": "yarn node dist/bin/worker.js"
|
||||
"worker": "yarn node dist/bin/worker.js",
|
||||
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@newrelic/winston-enricher": "^2.1.0",
|
||||
"@sentry/node": "^7.3.0",
|
||||
"@standardnotes/analytics": "^1.6.0",
|
||||
"@standardnotes/auth": "^3.19.2",
|
||||
"@standardnotes/common": "^1.22.0",
|
||||
"@standardnotes/domain-events": "^2.32.2",
|
||||
"@standardnotes/domain-events-infra": "^1.5.2",
|
||||
"@standardnotes/auth": "^3.19.4",
|
||||
"@standardnotes/common": "^1.23.1",
|
||||
"@standardnotes/domain-events": "^2.32.5",
|
||||
"@standardnotes/domain-events-infra": "^1.5.5",
|
||||
"@standardnotes/payloads": "^1.5.1",
|
||||
"@standardnotes/responses": "^1.6.15",
|
||||
"@standardnotes/settings": "1.14.3",
|
||||
"@standardnotes/responses": "^1.6.38",
|
||||
"@standardnotes/settings": "1.15.0",
|
||||
"@standardnotes/time": "^1.7.1",
|
||||
"aws-sdk": "^2.1159.0",
|
||||
"axios": "0.24.0",
|
||||
@@ -69,6 +70,7 @@
|
||||
"eslint": "^8.14.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "^28.1.1",
|
||||
"npm-check-updates": "^14.1.1",
|
||||
"ts-jest": "^28.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user