Compare commits

...

40 Commits

Author SHA1 Message Date
standardci
d25e14147c chore(release): publish new version
- @standardnotes/auth-server@1.3.6
2022-06-29 11:56:00 +00:00
Karol Sójko
1d4bce7309 fix: mute marketing value encryption version setting 2022-06-29 13:55:09 +02:00
standardci
8c0c56930e chore(release): publish new version
- @standardnotes/auth-server@1.3.5
2022-06-29 11:31:29 +00:00
Karol Sójko
2e9255344e fix: add logs for processing email campaigns 2022-06-29 13:30:41 +02:00
standardci
346a7cabe1 chore(release): publish new version
- @standardnotes/api-gateway@1.3.2
2022-06-28 12:55:13 +00:00
Karol Sójko
4a430b2701 fix: checking for html content-type 2022-06-28 14:54:38 +02:00
standardci
bb53e88a4e chore(release): publish new version
- @standardnotes/api-gateway@1.3.1
 - @standardnotes/auth-server@1.3.4
 - @standardnotes/files-server@1.1.8
 - @standardnotes/scheduler-server@1.2.3
 - @standardnotes/syncing-server@1.1.8
2022-06-28 12:20:39 +00:00
Karol Sójko
cfd04a5b39 Merge pull request #2 from standardnotes/create-pull-request/patch
chore(deps): upgrade snjs
2022-06-28 14:19:54 +02:00
standardci
e0f2d5e202 chore(release): publish new version
- @standardnotes/api-gateway@1.3.0
2022-06-28 12:11:19 +00:00
Karol Sójko
3035cbc5de feat: remove api metadata decorating html responses 2022-06-28 14:10:40 +02:00
standardci
7c271be310 chore(release): publish new version
- @standardnotes/auth-server@1.3.3
2022-06-28 10:09:14 +00:00
Karol Sójko
ba373ebc6b fix: change response type to html for muting marketing emails 2022-06-28 12:08:36 +02:00
Karol Sójko
2450d88a29 fix: install to be immutable 2022-06-28 11:13:06 +02:00
Karol Sójko
376a59c182 fix: add origin meta property to daily analytics event 2022-06-28 11:12:18 +02:00
standardci
d584ca57f1 chore(deps): upgrade snjs 2022-06-28 08:50:24 +00:00
Mo
c50662849b Merge pull request #4 from standardnotes/chore/pr-workflow
chore: add pr workflow
2022-06-27 17:12:23 -05:00
Mo
82da690139 chore: add pr workflow 2022-06-27 17:04:39 -05:00
standardci
b11a9b0eac chore(release): publish new version
- @standardnotes/auth-server@1.3.2
2022-06-27 22:02:46 +00:00
Mo
d7653474c3 Merge pull request #3 from standardnotes/chore/upgrade-features
chore: upgrade features package
2022-06-27 17:01:41 -05:00
Mo
ce2fd86ca3 chore: upgrade features package 2022-06-27 17:01:21 -05:00
standardci
c9c496c63f chore(release): publish new version
- @standardnotes/api-gateway@1.2.2
 - @standardnotes/auth-server@1.3.1
 - @standardnotes/files-server@1.1.7
 - @standardnotes/scheduler-server@1.2.2
 - @standardnotes/syncing-server@1.1.7
2022-06-27 21:57:06 +00:00
Mo
b1c9f8ca6e Merge pull request #1 from standardnotes/chore/snjs-workflow
chore: upgrade snjs event workflow
2022-06-27 16:56:33 -05:00
Mo
e84bd73a39 chore: add snjs workflow event handler 2022-06-27 16:53:57 -05:00
Mo
c69d1b02fb chore: add ini dep for ncu 2022-06-27 16:52:20 -05:00
Mo
b590d33b88 chore: ncu version 2022-06-27 16:48:02 -05:00
Mo
c1e7a3eb7e chore: add ncu to each package 2022-06-27 16:47:19 -05:00
Mo
fd743a9d5e chore: add npm-check-updates dev dep 2022-06-27 16:44:43 -05:00
standardci
4c40fd5186 chore(release): publish new version
- @standardnotes/api-gateway@1.2.1
2022-06-27 10:27:36 +00:00
Karol Sójko
2cb470b99e fix: issue with NaN error code responses 2022-06-27 12:27:00 +02:00
standardci
b6539f8795 chore(release): publish new version
- @standardnotes/auth-server@1.3.0
2022-06-27 10:18:23 +00:00
Karol Sójko
6d7de4a8da feat: add endpoint for muting marketing emails 2022-06-27 12:17:47 +02:00
standardci
41999f36f0 chore(release): publish new version
- @standardnotes/api-gateway@1.2.0
 - @standardnotes/auth-server@1.2.1
2022-06-27 10:07:19 +00:00
Karol Sójko
fa2a8da17b feat: add endpoint to mute marketing emails 2022-06-27 12:06:33 +02:00
Karol Sójko
214684eae7 fix: add setting uuid to email requests 2022-06-27 12:06:33 +02:00
standardci
b3a92af04d chore(release): publish new version
- @standardnotes/auth-server@1.2.0
2022-06-27 09:57:22 +00:00
Karol Sójko
210a314c81 feat: add docker commands to start sending emails 2022-06-27 11:56:42 +02:00
standardci
46cba52bcb chore(release): publish new version
- @standardnotes/auth-server@1.1.7
2022-06-27 08:44:03 +00:00
Karol Sójko
bf14ec05f9 fix: add sending out email campaigns 2022-06-27 10:43:23 +02:00
standardci
6f88a96c3e chore(release): publish new version
- @standardnotes/scheduler-server@1.2.1
2022-06-27 07:42:47 +00:00
Karol Sójko
e12c9c47a7 fix: change subscription encouragement scheduled from 14 to 30 days 2022-06-27 09:42:08 +02:00
94 changed files with 1682 additions and 569 deletions

21
.github/workflows/pr.yml vendored Normal file
View 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

View 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>

803
.pnp.cjs generated

File diff suppressed because it is too large Load Diff

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.

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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -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"

View File

@@ -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

View File

@@ -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(),

View File

@@ -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"
}
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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,
)
}
}

View File

@@ -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',

View File

@@ -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
View 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)
})
})

View File

@@ -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"
;;

View File

@@ -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"
}
}

View File

@@ -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)

View File

@@ -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'),

View File

@@ -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')
})
})

View File

@@ -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)
}
}

View File

@@ -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({

View File

@@ -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

View File

@@ -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

View File

@@ -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')
})

View File

@@ -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,
{

View File

@@ -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',
})
})
})

View File

@@ -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.',
}
}
}

View File

@@ -0,0 +1,3 @@
export type MuteMarketingEmailsDTO = {
settingUuid: string
}

View File

@@ -0,0 +1,4 @@
export type MuteMarketingEmailsResponse = {
success: boolean
message: string
}

View File

@@ -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

View File

@@ -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"
}

View File

@@ -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

View File

@@ -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"
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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"
}
}

724
yarn.lock

File diff suppressed because it is too large Load Diff