mirror of
https://github.com/standardnotes/server
synced 2026-01-22 02:04:30 -05:00
Compare commits
35 Commits
@standardn
...
@standardn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c271be310 | ||
|
|
ba373ebc6b | ||
|
|
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 | ||
|
|
83085052f8 | ||
|
|
eda618d845 | ||
|
|
8d4280f4ca | ||
|
|
b57816bba4 | ||
|
|
b6db194a22 | ||
|
|
8f708164cd | ||
|
|
ff09ae0a47 | ||
|
|
d21d752029 |
6
.env.sample
Normal file
6
.env.sample
Normal file
@@ -0,0 +1,6 @@
|
||||
DB_PORT=3306
|
||||
DB_USERNAME=std_notes_user
|
||||
DB_PASSWORD=changeme123
|
||||
DB_DATABASE=standard_notes_db
|
||||
|
||||
REDIS_PORT=6379
|
||||
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
|
||||
- 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>
|
||||
22
.github/workflows/syncing-server.release.yml
vendored
22
.github/workflows/syncing-server.release.yml
vendored
@@ -185,25 +185,7 @@ jobs:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: DEV - Create New Relic deployment marker for Web
|
||||
uses: newrelic/deployment-marker-action@v1
|
||||
with:
|
||||
accountId: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
|
||||
apiKey: ${{ secrets.NEW_RELIC_API_KEY }}
|
||||
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_SYNCING_SERVER_WEB_DEV }}
|
||||
revision: "${{ github.sha }}"
|
||||
description: "Automated Deployment via Github Actions"
|
||||
user: "${{ github.actor }}"
|
||||
- name: DEV - Create New Relic deployment marker for Worker
|
||||
uses: newrelic/deployment-marker-action@v1
|
||||
with:
|
||||
accountId: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
|
||||
apiKey: ${{ secrets.NEW_RELIC_API_KEY }}
|
||||
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_SYNCING_SERVER_WORKER_DEV }}
|
||||
revision: "${{ github.sha }}"
|
||||
description: "Automated Deployment via Github Actions"
|
||||
user: "${{ github.actor }}"
|
||||
- name: PROD - Create New Relic deployment marker for Web
|
||||
- name: Create New Relic deployment marker for Web
|
||||
uses: newrelic/deployment-marker-action@v1
|
||||
with:
|
||||
accountId: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
|
||||
@@ -212,7 +194,7 @@ jobs:
|
||||
revision: "${{ github.sha }}"
|
||||
description: "Automated Deployment via Github Actions"
|
||||
user: "${{ github.actor }}"
|
||||
- name: PROD - Create New Relic deployment marker for Worker
|
||||
- name: Create New Relic deployment marker for Worker
|
||||
uses: newrelic/deployment-marker-action@v1
|
||||
with:
|
||||
accountId: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -18,3 +18,6 @@ newrelic_agent.log
|
||||
|
||||
packages/files/uploads/*
|
||||
!packages/files/uploads/.gitkeep
|
||||
|
||||
data/*
|
||||
!data/.gitkeep
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@sentry-core-npm-7.3.0-8e7d2a21e6-1768568404.zip
vendored
Normal file
BIN
.yarn/cache/@sentry-core-npm-7.3.0-8e7d2a21e6-1768568404.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@sentry-hub-npm-7.3.0-b5d4219eb3-a052a7c940.zip
vendored
Normal file
BIN
.yarn/cache/@sentry-hub-npm-7.3.0-b5d4219eb3-a052a7c940.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@sentry-node-npm-7.3.0-ae73eb5bb5-a92c2d2d1b.zip
vendored
Normal file
BIN
.yarn/cache/@sentry-node-npm-7.3.0-ae73eb5bb5-a92c2d2d1b.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@sentry-types-npm-7.3.0-4a455bc29c-3ddbc3c7eb.zip
vendored
Normal file
BIN
.yarn/cache/@sentry-types-npm-7.3.0-4a455bc29c-3ddbc3c7eb.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@sentry-utils-npm-7.3.0-a4b6fc94f0-2696b1bfad.zip
vendored
Normal file
BIN
.yarn/cache/@sentry-utils-npm-7.3.0-a4b6fc94f0-2696b1bfad.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/@standardnotes-auth-npm-3.19.4-29b35c1352-84ac9e669e.zip
vendored
Normal file
BIN
.yarn/cache/@standardnotes-auth-npm-3.19.4-29b35c1352-84ac9e669e.zip
vendored
Normal file
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.
BIN
.yarn/cache/@standardnotes-domain-events-npm-2.32.3-d2cd9a7c7f-ea6151d137.zip
vendored
Normal file
BIN
.yarn/cache/@standardnotes-domain-events-npm-2.32.3-d2cd9a7c7f-ea6151d137.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/@standardnotes-domain-events-npm-2.32.4-0b0cc85196-9f8a1637aa.zip
vendored
Normal file
BIN
.yarn/cache/@standardnotes-domain-events-npm-2.32.4-0b0cc85196-9f8a1637aa.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/@standardnotes-features-npm-1.45.6-6f88b4fd2a-572780d6e2.zip
vendored
Normal file
BIN
.yarn/cache/@standardnotes-features-npm-1.45.6-6f88b4fd2a-572780d6e2.zip
vendored
Normal file
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.
BIN
.yarn/cache/@standardnotes-scheduler-npm-1.1.2-78f89314fb-68642a0874.zip
vendored
Normal file
BIN
.yarn/cache/@standardnotes-scheduler-npm-1.1.2-78f89314fb-68642a0874.zip
vendored
Normal file
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/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.
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.
Binary file not shown.
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.
0
data/.gitkeep
Normal file
0
data/.gitkeep
Normal file
24
docker-compose.yml
Normal file
24
docker-compose.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
db:
|
||||
image: mysql:5.6
|
||||
environment:
|
||||
MYSQL_DATABASE: '${DB_DATABASE}'
|
||||
MYSQL_USER: '${DB_USERNAME}'
|
||||
MYSQL_PASSWORD: '${DB_PASSWORD}'
|
||||
MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
|
||||
expose:
|
||||
- ${DB_PORT}:3306
|
||||
restart: unless-stopped
|
||||
command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8 --collation-server=utf8_general_ci
|
||||
volumes:
|
||||
- ./data/mysql:/var/lib/mysql
|
||||
- ./data/import:/docker-entrypoint-initdb.d
|
||||
|
||||
cache:
|
||||
image: redis:6.0-alpine
|
||||
volumes:
|
||||
- ./data/redis/:/data
|
||||
expose:
|
||||
- ${REDIS_PORT}:6379
|
||||
restart: unless-stopped
|
||||
14
package.json
14
package.json
@@ -23,7 +23,7 @@
|
||||
"test:syncing-server": "yarn workspace @standardnotes/syncing-server test",
|
||||
"test:files": "yarn workspace @standardnotes/files-server test",
|
||||
"clean": "yarn workspaces foreach -p --verbose run clean",
|
||||
"setup:env": "yarn workspaces foreach -p --verbose run setup:env",
|
||||
"setup:env": "cp .env.sample .env && yarn workspaces foreach -p --verbose run setup:env",
|
||||
"build": "yarn workspaces foreach -pt -j 10 --verbose run build",
|
||||
"build:auth": "yarn workspace @standardnotes/auth-server build",
|
||||
"build:scheduler": "yarn workspace @standardnotes/scheduler-server build",
|
||||
@@ -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",
|
||||
@@ -48,13 +49,20 @@
|
||||
"@lerna-lite/list": "^1.5.1",
|
||||
"@lerna-lite/run": "^1.5.1",
|
||||
"@types/jest": "^28.1.3",
|
||||
"@types/newrelic": "^7.0.3",
|
||||
"@types/node": "^18.0.0",
|
||||
"@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"
|
||||
},
|
||||
"packageManager": "yarn@3.2.1"
|
||||
"packageManager": "yarn@3.2.1",
|
||||
"dependencies": {
|
||||
"@sentry/node": "^7.3.0",
|
||||
"newrelic": "^8.14.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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
|
||||
|
||||
## [1.1.5](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.1.4...@standardnotes/api-gateway@1.1.5) (2022-06-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* upgrade sentry node sdk ([b6db194](https://github.com/standardnotes/api-gateway/commit/b6db194a22ff1d0afe96c291d545b408c0a5c373))
|
||||
|
||||
## [1.1.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.1.3...@standardnotes/api-gateway@1.1.4) (2022-06-24)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* newrelic deps and setup db and cache for local development purposes ([ff09ae0](https://github.com/standardnotes/api-gateway/commit/ff09ae0a47747eaf7977ce5d3937ad385101eaeb))
|
||||
|
||||
## [1.1.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.1.2...@standardnotes/api-gateway@1.1.3) (2022-06-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.1.3",
|
||||
"version": "1.2.2",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -15,13 +15,14 @@
|
||||
"prebuild": "yarn clean",
|
||||
"build": "tsc --rootDir ./",
|
||||
"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/native-metrics": "7.0.2",
|
||||
"@newrelic/winston-enricher": "^2.1.0",
|
||||
"@sentry/node": "^6.16.1",
|
||||
"@sentry/node": "^7.3.0",
|
||||
"@standardnotes/analytics": "^1.4.0",
|
||||
"@standardnotes/auth": "3.19.2",
|
||||
"@standardnotes/domain-events": "2.29.0",
|
||||
@@ -37,7 +38,7 @@
|
||||
"inversify-express-utils": "^6.4.3",
|
||||
"ioredis": "^5.0.6",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"newrelic": "8.6.0",
|
||||
"newrelic": "^8.14.1",
|
||||
"prettyjson": "1.2.1",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"winston": "3.3.3"
|
||||
@@ -48,13 +49,14 @@
|
||||
"@types/ioredis": "^4.28.10",
|
||||
"@types/jest": "^28.1.3",
|
||||
"@types/jsonwebtoken": "^8.5.0",
|
||||
"@types/newrelic": "^7.0.1",
|
||||
"@types/newrelic": "^7.0.3",
|
||||
"@types/prettyjson": "^0.0.29",
|
||||
"@typescript-eslint/eslint-plugin": "^5.29.0",
|
||||
"eslint": "^8.14.0",
|
||||
"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)
|
||||
}
|
||||
|
||||
@@ -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.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
|
||||
|
||||
## [1.1.5](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.1.4...@standardnotes/auth-server@1.1.5) (2022-06-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* upgrade sentry node sdk ([b6db194](https://github.com/standardnotes/auth/commit/b6db194a22ff1d0afe96c291d545b408c0a5c373))
|
||||
|
||||
## [1.1.4](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.1.3...@standardnotes/auth-server@1.1.4) (2022-06-24)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* newrelic deps and setup db and cache for local development purposes ([ff09ae0](https://github.com/standardnotes/auth/commit/ff09ae0a47747eaf7977ce5d3937ad385101eaeb))
|
||||
|
||||
## [1.1.3](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.1.2...@standardnotes/auth-server@1.1.3) (2022-06-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
147
packages/auth/bin/email.ts
Normal file
147
packages/auth/bin/email.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
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,
|
||||
): Promise<void> => {
|
||||
const stream = await userRepository.streamAll()
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
stream
|
||||
.pipe(
|
||||
new Stream.Transform({
|
||||
objectMode: true,
|
||||
transform: async (rawUserData, _encoding, callback) => {
|
||||
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.Default,
|
||||
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,
|
||||
),
|
||||
)
|
||||
.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.3",
|
||||
"version": "1.3.3",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -12,6 +12,7 @@
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
"clean": "rm -fr dist",
|
||||
"setup:env": "cp .env.sample .env",
|
||||
"prebuild": "yarn clean",
|
||||
"build": "tsc --rootDir ./",
|
||||
"lint": "eslint . --ext .ts",
|
||||
@@ -24,22 +25,23 @@
|
||||
"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/native-metrics": "7.0.2",
|
||||
"@newrelic/winston-enricher": "^2.1.0",
|
||||
"@sentry/node": "^6.16.1",
|
||||
"@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/common": "^1.23.1",
|
||||
"@standardnotes/domain-events": "^2.32.4",
|
||||
"@standardnotes/domain-events-infra": "^1.4.135",
|
||||
"@standardnotes/features": "^1.45.2",
|
||||
"@standardnotes/features": "^1.46.0",
|
||||
"@standardnotes/responses": "^1.6.15",
|
||||
"@standardnotes/scheduler": "^1.1.1",
|
||||
"@standardnotes/settings": "^1.14.2",
|
||||
"@standardnotes/settings": "^1.15.0",
|
||||
"@standardnotes/sncrypto-common": "^1.8.1",
|
||||
"@standardnotes/sncrypto-node": "^1.8.1",
|
||||
"@standardnotes/time": "^1.6.8",
|
||||
@@ -55,7 +57,7 @@
|
||||
"inversify-express-utils": "^6.4.3",
|
||||
"ioredis": "^5.0.6",
|
||||
"mysql2": "^2.3.3",
|
||||
"newrelic": "8.6.0",
|
||||
"newrelic": "^8.14.1",
|
||||
"otplib": "12.0.1",
|
||||
"prettyjson": "1.2.1",
|
||||
"reflect-metadata": "0.1.13",
|
||||
@@ -70,7 +72,7 @@
|
||||
"@types/express": "^4.17.11",
|
||||
"@types/ioredis": "^4.28.10",
|
||||
"@types/jest": "^28.1.3",
|
||||
"@types/newrelic": "^7.0.2",
|
||||
"@types/newrelic": "^7.0.3",
|
||||
"@types/otplib": "^10.0.0",
|
||||
"@types/prettyjson": "^0.0.29",
|
||||
"@types/ua-parser-js": "^0.7.36",
|
||||
@@ -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,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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.1.5](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.1.4...@standardnotes/files-server@1.1.5) (2022-06-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* upgrade sentry node sdk ([b6db194](https://github.com/standardnotes/files/commit/b6db194a22ff1d0afe96c291d545b408c0a5c373))
|
||||
|
||||
## [1.1.4](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.1.3...@standardnotes/files-server@1.1.4) (2022-06-24)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* newrelic deps and setup db and cache for local development purposes ([ff09ae0](https://github.com/standardnotes/files/commit/ff09ae0a47747eaf7977ce5d3937ad385101eaeb))
|
||||
|
||||
## [1.1.3](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.1.2...@standardnotes/files-server@1.1.3) (2022-06-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files-server",
|
||||
"version": "1.1.3",
|
||||
"version": "1.1.7",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -14,17 +14,18 @@
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
"clean": "rm -fr dist",
|
||||
"setup:env": "cp .env.sample .env",
|
||||
"prebuild": "yarn clean",
|
||||
"build": "tsc --rootDir ./",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"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/native-metrics": "7.0.2",
|
||||
"@sentry/node": "^6.16.1",
|
||||
"@sentry/node": "^7.3.0",
|
||||
"@standardnotes/auth": "^3.18.9",
|
||||
"@standardnotes/common": "^1.19.4",
|
||||
"@standardnotes/domain-events": "^2.27.6",
|
||||
@@ -44,7 +45,7 @@
|
||||
"inversify-express-utils": "^6.4.3",
|
||||
"ioredis": "^5.0.6",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"newrelic": "^7.3.1",
|
||||
"newrelic": "^8.14.1",
|
||||
"nodemon": "^2.0.15",
|
||||
"prettyjson": "^1.2.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
@@ -59,7 +60,7 @@
|
||||
"@types/ioredis": "^4.28.10",
|
||||
"@types/jest": "^28.1.3",
|
||||
"@types/jsonwebtoken": "^8.5.0",
|
||||
"@types/newrelic": "^7.0.1",
|
||||
"@types/newrelic": "^7.0.3",
|
||||
"@types/prettyjson": "^0.0.29",
|
||||
"@types/uuid": "^8.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.29.0",
|
||||
@@ -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,34 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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
|
||||
|
||||
* add context for requesting email message ([8d4280f](https://github.com/standardnotes/server/commit/8d4280f4ca57151198880620cc9504aba720f2f3))
|
||||
|
||||
## [1.1.5](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.1.4...@standardnotes/scheduler-server@1.1.5) (2022-06-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* upgrade sentry node sdk ([b6db194](https://github.com/standardnotes/server/commit/b6db194a22ff1d0afe96c291d545b408c0a5c373))
|
||||
|
||||
## [1.1.4](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.1.3...@standardnotes/scheduler-server@1.1.4) (2022-06-24)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* newrelic deps and setup db and cache for local development purposes ([ff09ae0](https://github.com/standardnotes/server/commit/ff09ae0a47747eaf7977ce5d3937ad385101eaeb))
|
||||
|
||||
## [1.1.3](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.1.2...@standardnotes/scheduler-server@1.1.3) (2022-06-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/scheduler-server",
|
||||
"version": "1.1.3",
|
||||
"version": "1.2.2",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -19,13 +19,14 @@
|
||||
"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/native-metrics": "7.0.2",
|
||||
"@newrelic/winston-enricher": "^2.1.0",
|
||||
"@sentry/node": "^7.3.0",
|
||||
"@standardnotes/common": "^1.23.0",
|
||||
"@standardnotes/domain-events": "^2.32.0",
|
||||
"@standardnotes/domain-events": "^2.32.3",
|
||||
"@standardnotes/domain-events-infra": "^1.5.0",
|
||||
"@standardnotes/scheduler": "^1.1.0",
|
||||
"@standardnotes/time": "^1.7.0",
|
||||
@@ -35,7 +36,7 @@
|
||||
"inversify": "5.0.5",
|
||||
"ioredis": "^5.0.6",
|
||||
"mysql2": "^2.3.3",
|
||||
"newrelic": "8.6.0",
|
||||
"newrelic": "^8.14.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"typeorm": "^0.3.6",
|
||||
"winston": "3.3.3"
|
||||
@@ -43,11 +44,12 @@
|
||||
"devDependencies": {
|
||||
"@types/ioredis": "^4.28.10",
|
||||
"@types/jest": "^28.1.2",
|
||||
"@types/newrelic": "^7.0.2",
|
||||
"@types/newrelic": "^7.0.3",
|
||||
"@types/node": "^18.0.0",
|
||||
"@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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,13 @@ describe('DomainEventFactory', () => {
|
||||
|
||||
it('should create a EMAIL_MESSAGE_REQUESTED event', () => {
|
||||
expect(
|
||||
createFactory().createEmailMessageRequestedEvent('test@test.te', EmailMessageIdentifier.ENCOURAGE_EMAIL_BACKUPS),
|
||||
createFactory().createEmailMessageRequestedEvent({
|
||||
userEmail: 'test@test.te',
|
||||
messageIdentifier: EmailMessageIdentifier.ENCOURAGE_EMAIL_BACKUPS,
|
||||
context: {
|
||||
foo: 'bar',
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
createdAt: expect.any(Date),
|
||||
meta: {
|
||||
@@ -34,6 +40,9 @@ describe('DomainEventFactory', () => {
|
||||
payload: {
|
||||
messageIdentifier: 'ENCOURAGE_EMAIL_BACKUPS',
|
||||
userEmail: 'test@test.te',
|
||||
context: {
|
||||
foo: 'bar',
|
||||
},
|
||||
},
|
||||
type: 'EMAIL_MESSAGE_REQUESTED',
|
||||
})
|
||||
|
||||
@@ -16,24 +16,22 @@ import { DomainEventFactoryInterface } from './DomainEventFactoryInterface'
|
||||
export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||
constructor(@inject(TYPES.Timer) private timer: TimerInterface) {}
|
||||
|
||||
createEmailMessageRequestedEvent(
|
||||
userEmail: string,
|
||||
messageIdentifier: EmailMessageIdentifier,
|
||||
): EmailMessageRequestedEvent {
|
||||
createEmailMessageRequestedEvent(dto: {
|
||||
userEmail: string
|
||||
messageIdentifier: EmailMessageIdentifier
|
||||
context: Record<string, unknown>
|
||||
}): EmailMessageRequestedEvent {
|
||||
return {
|
||||
type: 'EMAIL_MESSAGE_REQUESTED',
|
||||
createdAt: this.timer.getUTCDate(),
|
||||
meta: {
|
||||
correlation: {
|
||||
userIdentifier: userEmail,
|
||||
userIdentifier: dto.userEmail,
|
||||
userIdentifierType: 'email',
|
||||
},
|
||||
origin: DomainEventService.Scheduler,
|
||||
},
|
||||
payload: {
|
||||
messageIdentifier,
|
||||
userEmail,
|
||||
},
|
||||
payload: dto,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,9 @@ import { Predicate } from '../Predicate/Predicate'
|
||||
|
||||
export interface DomainEventFactoryInterface {
|
||||
createPredicateVerificationRequestedEvent(job: Job, predicate: Predicate): PredicateVerificationRequestedEvent
|
||||
createEmailMessageRequestedEvent(
|
||||
userEmail: string,
|
||||
messageIdentifier: EmailMessageIdentifier,
|
||||
): EmailMessageRequestedEvent
|
||||
createEmailMessageRequestedEvent(dto: {
|
||||
userEmail: string
|
||||
messageIdentifier: EmailMessageIdentifier
|
||||
context: Record<string, unknown>
|
||||
}): EmailMessageRequestedEvent
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -67,10 +67,11 @@ describe('JobDoneInterpreter', () => {
|
||||
|
||||
await createInterpreter().interpret('1-2-3')
|
||||
|
||||
expect(domainEventFactory.createEmailMessageRequestedEvent).toHaveBeenCalledWith(
|
||||
'test@test.te',
|
||||
'ENCOURAGE_EMAIL_BACKUPS',
|
||||
)
|
||||
expect(domainEventFactory.createEmailMessageRequestedEvent).toHaveBeenCalledWith({
|
||||
context: {},
|
||||
messageIdentifier: 'ENCOURAGE_EMAIL_BACKUPS',
|
||||
userEmail: 'test@test.te',
|
||||
})
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
@@ -110,6 +111,7 @@ describe('JobDoneInterpreter', () => {
|
||||
name: JobName.ENCOURAGE_SUBSCRIPTION_PURCHASING,
|
||||
userIdentifier: 'test@test.te',
|
||||
userIdentifierType: 'email',
|
||||
createdAt: 123,
|
||||
} as jest.Mocked<Job>)
|
||||
predicateRepository.findByJobUuid = jest
|
||||
.fn()
|
||||
@@ -119,10 +121,11 @@ describe('JobDoneInterpreter', () => {
|
||||
|
||||
await createInterpreter().interpret('1-2-3')
|
||||
|
||||
expect(domainEventFactory.createEmailMessageRequestedEvent).toHaveBeenCalledWith(
|
||||
'test@test.te',
|
||||
'ENCOURAGE_SUBSCRIPTION_PURCHASING',
|
||||
)
|
||||
expect(domainEventFactory.createEmailMessageRequestedEvent).toHaveBeenCalledWith({
|
||||
context: { userRegisteredAt: 123 },
|
||||
messageIdentifier: 'ENCOURAGE_SUBSCRIPTION_PURCHASING',
|
||||
userEmail: 'test@test.te',
|
||||
})
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
@@ -148,7 +151,11 @@ describe('JobDoneInterpreter', () => {
|
||||
|
||||
await createInterpreter().interpret('1-2-3')
|
||||
|
||||
expect(domainEventFactory.createEmailMessageRequestedEvent).toHaveBeenCalledWith('test@test.te', 'EXIT_INTERVIEW')
|
||||
expect(domainEventFactory.createEmailMessageRequestedEvent).toHaveBeenCalledWith({
|
||||
context: {},
|
||||
messageIdentifier: 'EXIT_INTERVIEW',
|
||||
userEmail: 'test@test.te',
|
||||
})
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ export class JobDoneInterpreter implements JobDoneInterpreterInterface {
|
||||
return
|
||||
case JobName.ENCOURAGE_SUBSCRIPTION_PURCHASING:
|
||||
if (job.userIdentifierType === 'email') {
|
||||
await this.requestSubscriptionPurchaseEncouragementEmail(job.userIdentifier)
|
||||
await this.requestSubscriptionPurchaseEncouragementEmail(job)
|
||||
}
|
||||
return
|
||||
case JobName.EXIT_INTERVIEW:
|
||||
@@ -56,25 +56,33 @@ export class JobDoneInterpreter implements JobDoneInterpreterInterface {
|
||||
|
||||
private async requestEmailBackupEncouragementEmail(userEmail: string): Promise<void> {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createEmailMessageRequestedEvent(
|
||||
this.domainEventFactory.createEmailMessageRequestedEvent({
|
||||
userEmail,
|
||||
EmailMessageIdentifier.ENCOURAGE_EMAIL_BACKUPS,
|
||||
),
|
||||
messageIdentifier: EmailMessageIdentifier.ENCOURAGE_EMAIL_BACKUPS,
|
||||
context: {},
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
private async requestSubscriptionPurchaseEncouragementEmail(userEmail: string): Promise<void> {
|
||||
private async requestSubscriptionPurchaseEncouragementEmail(job: Job): Promise<void> {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createEmailMessageRequestedEvent(
|
||||
userEmail,
|
||||
EmailMessageIdentifier.ENCOURAGE_SUBSCRIPTION_PURCHASING,
|
||||
),
|
||||
this.domainEventFactory.createEmailMessageRequestedEvent({
|
||||
userEmail: job.userIdentifier,
|
||||
messageIdentifier: EmailMessageIdentifier.ENCOURAGE_SUBSCRIPTION_PURCHASING,
|
||||
context: {
|
||||
userRegisteredAt: job.createdAt,
|
||||
},
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
private async requestExitInterviewEmail(userEmail: string): Promise<void> {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createEmailMessageRequestedEvent(userEmail, EmailMessageIdentifier.EXIT_INTERVIEW),
|
||||
this.domainEventFactory.createEmailMessageRequestedEvent({
|
||||
userEmail,
|
||||
messageIdentifier: EmailMessageIdentifier.EXIT_INTERVIEW,
|
||||
context: {},
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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.1.5](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.4...@standardnotes/syncing-server@1.1.5) (2022-06-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* upgrade sentry node sdk ([b6db194](https://github.com/standardnotes/syncing-server-js/commit/b6db194a22ff1d0afe96c291d545b408c0a5c373))
|
||||
|
||||
## [1.1.4](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.3...@standardnotes/syncing-server@1.1.4) (2022-06-24)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* newrelic deps and setup db and cache for local development purposes ([ff09ae0](https://github.com/standardnotes/syncing-server-js/commit/ff09ae0a47747eaf7977ce5d3937ad385101eaeb))
|
||||
|
||||
## [1.1.3](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.1.2...@standardnotes/syncing-server@1.1.3) (2022-06-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/syncing-server",
|
||||
"version": "1.1.3",
|
||||
"version": "1.1.7",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -12,18 +12,19 @@
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
"clean": "rm -fr dist",
|
||||
"setup:env": "cp .env.sample .env",
|
||||
"prebuild": "yarn clean",
|
||||
"build": "tsc --rootDir ./",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"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/native-metrics": "7.0.2",
|
||||
"@newrelic/winston-enricher": "^2.1.0",
|
||||
"@sentry/node": "^6.16.1",
|
||||
"@sentry/node": "^7.3.0",
|
||||
"@standardnotes/analytics": "^1.6.0",
|
||||
"@standardnotes/auth": "^3.19.2",
|
||||
"@standardnotes/common": "^1.22.0",
|
||||
@@ -44,7 +45,7 @@
|
||||
"ioredis": "^5.0.6",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"mysql2": "^2.3.3",
|
||||
"newrelic": "8.6.0",
|
||||
"newrelic": "^8.14.1",
|
||||
"nodemon": "2.0.7",
|
||||
"prettyjson": "1.2.1",
|
||||
"reflect-metadata": "0.1.13",
|
||||
@@ -61,7 +62,7 @@
|
||||
"@types/ioredis": "^4.28.10",
|
||||
"@types/jest": "^28.1.3",
|
||||
"@types/jsonwebtoken": "^8.5.0",
|
||||
"@types/newrelic": "^7.0.2",
|
||||
"@types/newrelic": "^7.0.3",
|
||||
"@types/prettyjson": "^0.0.29",
|
||||
"@types/ua-parser-js": "^0.7.36",
|
||||
"@types/uuid": "^8.3.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