mirror of
https://github.com/standardnotes/server
synced 2026-04-22 00:01:46 -04:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e6d8e5c5f2 | |||
| c24353cc24 | |||
| 4855e1d5f5 | |||
| 5d3fb9a537 | |||
| b55d80a7cd | |||
| 16f92bdc99 | |||
| 4c5738416a | |||
| 45d4920e0f | |||
| 94e738532a | |||
| c4ae12d53f | |||
| 4ff78452f9 | |||
| 9465f2ecd8 | |||
| 93c2f1f12f | |||
| ca8a3fc77d | |||
| 00936e06bc | |||
| a6dea50d74 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
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
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.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.33.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.32.6...@standardnotes/analytics@2.33.0) (2023-11-10)
|
||||
|
||||
### Features
|
||||
|
||||
* add graceful shutdown procedures upon SIGTERM ([#923](https://github.com/standardnotes/server/issues/923)) ([c24353c](https://github.com/standardnotes/server/commit/c24353cc24ebf4b40ff9a2cec8e37cfdef109e37))
|
||||
|
||||
## [2.32.6](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.32.5...@standardnotes/analytics@2.32.6) (2023-11-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
@@ -22,5 +22,11 @@ void container.load().then((container) => {
|
||||
|
||||
const subscriber = container.get<DomainEventSubscriberInterface>(TYPES.DomainEventSubscriber)
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
logger.info('SIGTERM received. Stopping worker...')
|
||||
subscriber.stop()
|
||||
logger.info('Worker stopped.')
|
||||
})
|
||||
|
||||
subscriber.start()
|
||||
})
|
||||
|
||||
@@ -6,12 +6,12 @@ COMMAND=$1 && shift 1
|
||||
case "$COMMAND" in
|
||||
'start-worker' )
|
||||
echo "[Docker] Starting Worker..."
|
||||
node docker/entrypoint-worker.js
|
||||
exec node docker/entrypoint-worker.js
|
||||
;;
|
||||
|
||||
'report' )
|
||||
echo "[Docker] Starting Usage Report Generation..."
|
||||
node docker/entrypoint-report.js
|
||||
exec node docker/entrypoint-report.js
|
||||
;;
|
||||
|
||||
* )
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "2.32.6",
|
||||
"version": "2.33.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,30 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.82.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.81.14...@standardnotes/api-gateway@1.82.0) (2023-11-10)
|
||||
|
||||
### Features
|
||||
|
||||
* add graceful shutdown procedures upon SIGTERM ([#923](https://github.com/standardnotes/api-gateway/issues/923)) ([c24353c](https://github.com/standardnotes/api-gateway/commit/c24353cc24ebf4b40ff9a2cec8e37cfdef109e37))
|
||||
|
||||
## [1.81.14](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.81.13...@standardnotes/api-gateway@1.81.14) (2023-11-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api-gateway:** add logs about calling web sockets with minimal format ([5d3fb9a](https://github.com/standardnotes/api-gateway/commit/5d3fb9a537f6971cfe8ae3c5ea449806cc4de8a0))
|
||||
|
||||
## [1.81.13](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.81.12...@standardnotes/api-gateway@1.81.13) (2023-11-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api-gateway:** add possibility to configure keep-alive timeout ([#920](https://github.com/standardnotes/api-gateway/issues/920)) ([16f92bd](https://github.com/standardnotes/api-gateway/commit/16f92bdc990ded5c3f1fe5af1e6e4a113a9954de))
|
||||
|
||||
## [1.81.12](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.81.11...@standardnotes/api-gateway@1.81.12) (2023-11-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* reduce websockets api communication data ([#919](https://github.com/standardnotes/api-gateway/issues/919)) ([c4ae12d](https://github.com/standardnotes/api-gateway/commit/c4ae12d53fc166879f90a4c5dbad1ab1cb4797e2))
|
||||
|
||||
## [1.81.11](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.81.10...@standardnotes/api-gateway@1.81.11) (2023-11-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -102,9 +102,18 @@ void container.load().then((container) => {
|
||||
})
|
||||
})
|
||||
|
||||
const serverInstance = server.build()
|
||||
const serverInstance = server.build().listen(env.get('PORT'))
|
||||
|
||||
serverInstance.listen(env.get('PORT'))
|
||||
const keepAliveTimeout = env.get('KEEP_ALIVE_TIMEOUT', true) ? +env.get('KEEP_ALIVE_TIMEOUT', true) : 5000
|
||||
|
||||
serverInstance.keepAliveTimeout = keepAliveTimeout
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
logger.info('SIGTERM signal received: closing HTTP server')
|
||||
serverInstance.close(() => {
|
||||
logger.info('HTTP server closed')
|
||||
})
|
||||
})
|
||||
|
||||
logger.info(`Server started on port ${process.env.PORT}`)
|
||||
})
|
||||
|
||||
@@ -6,7 +6,7 @@ COMMAND=$1 && shift 1
|
||||
case "$COMMAND" in
|
||||
'start-web' )
|
||||
echo "Starting Web..."
|
||||
node docker/entrypoint-server.js
|
||||
exec node docker/entrypoint-server.js
|
||||
;;
|
||||
|
||||
* )
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.81.11",
|
||||
"version": "1.82.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -143,7 +143,21 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||
return
|
||||
}
|
||||
|
||||
await this.callServer(this.webSocketServerUrl, request, response, endpointOrMethodIdentifier, payload)
|
||||
const isARequestComingFromApiGatewayAndShouldBeKeptInMinimalFormat = request.headers.connectionid !== undefined
|
||||
this.logger.info(
|
||||
`Calling websockets service: ${endpointOrMethodIdentifier}. Format is minimal: ${isARequestComingFromApiGatewayAndShouldBeKeptInMinimalFormat}`,
|
||||
)
|
||||
if (isARequestComingFromApiGatewayAndShouldBeKeptInMinimalFormat) {
|
||||
await this.callServerWithLegacyFormat(
|
||||
this.webSocketServerUrl,
|
||||
request,
|
||||
response,
|
||||
endpointOrMethodIdentifier,
|
||||
payload,
|
||||
)
|
||||
} else {
|
||||
await this.callServer(this.webSocketServerUrl, request, response, endpointOrMethodIdentifier, payload)
|
||||
}
|
||||
}
|
||||
|
||||
async callPaymentsServer(
|
||||
|
||||
@@ -6,4 +6,4 @@ sh supervisor/wait-for.sh localhost $AUTH_SERVER_PORT
|
||||
sh supervisor/wait-for.sh localhost $FILES_SERVER_PORT
|
||||
sh supervisor/wait-for.sh localhost $REVISIONS_SERVER_PORT
|
||||
sh supervisor/wait-for.sh localhost $SYNCING_SERVER_PORT
|
||||
node docker/entrypoint-server.js
|
||||
exec node docker/entrypoint-server.js
|
||||
|
||||
@@ -3,6 +3,30 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.168.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.167.2...@standardnotes/auth-server@1.168.0) (2023-11-10)
|
||||
|
||||
### Features
|
||||
|
||||
* add graceful shutdown procedures upon SIGTERM ([#923](https://github.com/standardnotes/server/issues/923)) ([c24353c](https://github.com/standardnotes/server/commit/c24353cc24ebf4b40ff9a2cec8e37cfdef109e37))
|
||||
|
||||
## [1.167.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.167.1...@standardnotes/auth-server@1.167.2) (2023-11-08)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add logs about sending websocket events to clients ([9465f2e](https://github.com/standardnotes/server/commit/9465f2ecd8e8f0bf3ebeeb3976227b1b105aded0))
|
||||
|
||||
## [1.167.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.167.0...@standardnotes/auth-server@1.167.1) (2023-11-08)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** path to delete accounts script ([ca8a3fc](https://github.com/standardnotes/server/commit/ca8a3fc77d91410f0dee8c3ddef29c09947c9cf5))
|
||||
|
||||
# [1.167.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.166.0...@standardnotes/auth-server@1.167.0) (2023-11-08)
|
||||
|
||||
### Features
|
||||
|
||||
* script to mass delete accounts from CSV source ([#913](https://github.com/standardnotes/server/issues/913)) ([a6dea50](https://github.com/standardnotes/server/commit/a6dea50d745ff6f051fd9ede168aef27036159c3))
|
||||
|
||||
# [1.166.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.165.4...@standardnotes/auth-server@1.166.0) (2023-11-07)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||
import TYPES from '../src/Bootstrap/Types'
|
||||
import { Env } from '../src/Bootstrap/Env'
|
||||
import { DeleteAccountsFromCSVFile } from '../src/Domain/UseCase/DeleteAccountsFromCSVFile/DeleteAccountsFromCSVFile'
|
||||
|
||||
const inputArgs = process.argv.slice(2)
|
||||
const fileName = inputArgs[0]
|
||||
const mode = inputArgs[1]
|
||||
|
||||
const deleteAccounts = async (deleteAccountsFromCSVFile: DeleteAccountsFromCSVFile): Promise<void> => {
|
||||
await deleteAccountsFromCSVFile.execute({
|
||||
fileName,
|
||||
dryRun: mode !== 'delete',
|
||||
})
|
||||
}
|
||||
|
||||
const container = new ContainerConfigLoader('worker')
|
||||
void container.load().then((container) => {
|
||||
const env: Env = new Env()
|
||||
env.load()
|
||||
|
||||
const logger: Logger = container.get(TYPES.Auth_Logger)
|
||||
|
||||
logger.info('Starting mass accounts deletion from CSV file')
|
||||
|
||||
const deleteAccountsFromCSVFile = container.get<DeleteAccountsFromCSVFile>(TYPES.Auth_DeleteAccountsFromCSVFile)
|
||||
|
||||
Promise.resolve(deleteAccounts(deleteAccountsFromCSVFile))
|
||||
.then(() => {
|
||||
logger.info('Accounts deleted.')
|
||||
|
||||
process.exit(0)
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error(`Could not delete accounts: ${error.message}`)
|
||||
|
||||
process.exit(1)
|
||||
})
|
||||
})
|
||||
@@ -64,9 +64,14 @@ void container.load().then((container) => {
|
||||
})
|
||||
})
|
||||
|
||||
const serverInstance = server.build()
|
||||
const serverInstance = server.build().listen(env.get('PORT'))
|
||||
|
||||
serverInstance.listen(env.get('PORT'))
|
||||
process.on('SIGTERM', () => {
|
||||
logger.info('SIGTERM signal received: closing HTTP server')
|
||||
serverInstance.close(() => {
|
||||
logger.info('HTTP server closed')
|
||||
})
|
||||
})
|
||||
|
||||
logger.info(`Server started on port ${process.env.PORT}`)
|
||||
})
|
||||
|
||||
@@ -22,5 +22,11 @@ void container.load().then((container) => {
|
||||
|
||||
const subscriber = container.get<DomainEventSubscriberInterface>(TYPES.Auth_DomainEventSubscriber)
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
logger.info('SIGTERM received. Stopping worker...')
|
||||
subscriber.stop()
|
||||
logger.info('Worker stopped.')
|
||||
})
|
||||
|
||||
subscriber.start()
|
||||
})
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
|
||||
const pnp = require(path.normalize(path.resolve(__dirname, '../../..', '.pnp.cjs'))).setup()
|
||||
|
||||
const index = require(path.normalize(path.resolve(__dirname, '../dist/bin/delete_accounts.js')))
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true })
|
||||
|
||||
exports.default = index
|
||||
@@ -6,38 +6,45 @@ COMMAND=$1 && shift 1
|
||||
case "$COMMAND" in
|
||||
'start-web' )
|
||||
echo "[Docker] Starting Web..."
|
||||
node docker/entrypoint-server.js
|
||||
exec node docker/entrypoint-server.js
|
||||
;;
|
||||
|
||||
'start-worker' )
|
||||
echo "[Docker] Starting Worker..."
|
||||
node docker/entrypoint-worker.js
|
||||
exec node docker/entrypoint-worker.js
|
||||
;;
|
||||
|
||||
'cleanup' )
|
||||
echo "[Docker] Starting Cleanup..."
|
||||
node docker/entrypoint-cleanup.js
|
||||
exec node docker/entrypoint-cleanup.js
|
||||
;;
|
||||
|
||||
'stats' )
|
||||
echo "[Docker] Starting Persisting Stats..."
|
||||
node docker/entrypoint-stats.js
|
||||
exec node docker/entrypoint-stats.js
|
||||
;;
|
||||
|
||||
'email-daily-backup' )
|
||||
echo "[Docker] Starting Email Daily Backup..."
|
||||
node docker/entrypoint-backup.js daily
|
||||
exec node docker/entrypoint-backup.js daily
|
||||
;;
|
||||
|
||||
'email-weekly-backup' )
|
||||
echo "[Docker] Starting Email Weekly Backup..."
|
||||
node docker/entrypoint-backup.js weekly
|
||||
exec node docker/entrypoint-backup.js weekly
|
||||
;;
|
||||
|
||||
'email-backup' )
|
||||
echo "[Docker] Starting Email Backup For Single User..."
|
||||
EMAIL=$1 && shift 1
|
||||
node docker/entrypoint-user-email-backup.js $EMAIL
|
||||
exec node docker/entrypoint-user-email-backup.js $EMAIL
|
||||
;;
|
||||
|
||||
'delete-accounts' )
|
||||
echo "[Docker] Starting Accounts Deleting from CSV..."
|
||||
FILE_NAME=$1 && shift 1
|
||||
MODE=$1 && shift 1
|
||||
exec node docker/entrypoint-delete-accounts.js $FILE_NAME $MODE
|
||||
;;
|
||||
|
||||
* )
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/auth-server",
|
||||
"version": "1.166.0",
|
||||
"version": "1.168.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -32,6 +32,7 @@
|
||||
"migrate": "yarn build && yarn typeorm migration:run -d dist/src/Bootstrap/DataSource.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.445.0",
|
||||
"@aws-sdk/client-sns": "^3.427.0",
|
||||
"@aws-sdk/client-sqs": "^3.427.0",
|
||||
"@cbor-extract/cbor-extract-linux-arm64": "^2.1.1",
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as winston from 'winston'
|
||||
import Redis from 'ioredis'
|
||||
import { SNSClient, SNSClientConfig } from '@aws-sdk/client-sns'
|
||||
import { SQSClient, SQSClientConfig } from '@aws-sdk/client-sqs'
|
||||
import { S3Client } from '@aws-sdk/client-s3'
|
||||
import { Container } from 'inversify'
|
||||
import {
|
||||
DomainEventHandlerInterface,
|
||||
@@ -276,6 +277,9 @@ import { UserInvitedToSharedVaultEventHandler } from '../Domain/Handler/UserInvi
|
||||
import { TriggerPostSettingUpdateActions } from '../Domain/UseCase/TriggerPostSettingUpdateActions/TriggerPostSettingUpdateActions'
|
||||
import { TriggerEmailBackupForUser } from '../Domain/UseCase/TriggerEmailBackupForUser/TriggerEmailBackupForUser'
|
||||
import { TriggerEmailBackupForAllUsers } from '../Domain/UseCase/TriggerEmailBackupForAllUsers/TriggerEmailBackupForAllUsers'
|
||||
import { CSVFileReaderInterface } from '../Domain/CSV/CSVFileReaderInterface'
|
||||
import { S3CsvFileReader } from '../Infra/S3/S3CsvFileReader'
|
||||
import { DeleteAccountsFromCSVFile } from '../Domain/UseCase/DeleteAccountsFromCSVFile/DeleteAccountsFromCSVFile'
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
constructor(private mode: 'server' | 'worker' = 'server') {}
|
||||
@@ -370,6 +374,19 @@ export class ContainerConfigLoader {
|
||||
}
|
||||
const sqsClient = new SQSClient(sqsConfig)
|
||||
container.bind<SQSClient>(TYPES.Auth_SQS).toConstantValue(sqsClient)
|
||||
|
||||
container.bind<S3Client>(TYPES.Auth_S3).toConstantValue(
|
||||
new S3Client({
|
||||
apiVersion: 'latest',
|
||||
region: env.get('S3_AWS_REGION', true),
|
||||
}),
|
||||
)
|
||||
|
||||
container
|
||||
.bind<CSVFileReaderInterface>(TYPES.Auth_CSVFileReader)
|
||||
.toConstantValue(
|
||||
new S3CsvFileReader(env.get('S3_AUTH_SCRIPTS_DATA_BUCKET', true), container.get<S3Client>(TYPES.Auth_S3)),
|
||||
)
|
||||
}
|
||||
|
||||
container.bind(TYPES.Auth_SNS_TOPIC_ARN).toConstantValue(env.get('SNS_TOPIC_ARN', true))
|
||||
@@ -1251,6 +1268,17 @@ export class ContainerConfigLoader {
|
||||
container.get<TriggerEmailBackupForUser>(TYPES.Auth_TriggerEmailBackupForUser),
|
||||
),
|
||||
)
|
||||
if (!isConfiguredForHomeServer) {
|
||||
container
|
||||
.bind<DeleteAccountsFromCSVFile>(TYPES.Auth_DeleteAccountsFromCSVFile)
|
||||
.toConstantValue(
|
||||
new DeleteAccountsFromCSVFile(
|
||||
container.get<CSVFileReaderInterface>(TYPES.Auth_CSVFileReader),
|
||||
container.get<DeleteAccount>(TYPES.Auth_DeleteAccount),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Controller
|
||||
container
|
||||
|
||||
@@ -3,6 +3,7 @@ const TYPES = {
|
||||
Auth_Redis: Symbol.for('Auth_Redis'),
|
||||
Auth_SNS: Symbol.for('Auth_SNS'),
|
||||
Auth_SQS: Symbol.for('Auth_SQS'),
|
||||
Auth_S3: Symbol.for('Auth_S3'),
|
||||
// Mapping
|
||||
Auth_SessionTracePersistenceMapper: Symbol.for('Auth_SessionTracePersistenceMapper'),
|
||||
Auth_AuthenticatorChallengePersistenceMapper: Symbol.for('Auth_AuthenticatorChallengePersistenceMapper'),
|
||||
@@ -167,6 +168,7 @@ const TYPES = {
|
||||
Auth_TriggerPostSettingUpdateActions: Symbol.for('Auth_TriggerPostSettingUpdateActions'),
|
||||
Auth_TriggerEmailBackupForUser: Symbol.for('Auth_TriggerEmailBackupForUser'),
|
||||
Auth_TriggerEmailBackupForAllUsers: Symbol.for('Auth_TriggerEmailBackupForAllUsers'),
|
||||
Auth_DeleteAccountsFromCSVFile: Symbol.for('Auth_DeleteAccountsFromCSVFile'),
|
||||
// Handlers
|
||||
Auth_AccountDeletionRequestedEventHandler: Symbol.for('Auth_AccountDeletionRequestedEventHandler'),
|
||||
Auth_SubscriptionPurchasedEventHandler: Symbol.for('Auth_SubscriptionPurchasedEventHandler'),
|
||||
@@ -251,6 +253,7 @@ const TYPES = {
|
||||
Auth_BaseOfflineController: Symbol.for('Auth_BaseOfflineController'),
|
||||
Auth_BaseListedController: Symbol.for('Auth_BaseListedController'),
|
||||
Auth_BaseFeaturesController: Symbol.for('Auth_BaseFeaturesController'),
|
||||
Auth_CSVFileReader: Symbol.for('Auth_CSVFileReader'),
|
||||
}
|
||||
|
||||
export default TYPES
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
|
||||
export interface CSVFileReaderInterface {
|
||||
getValues(fileName: string): Promise<Result<string[]>>
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
import { Logger } from 'winston'
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
|
||||
import { CSVFileReaderInterface } from '../../CSV/CSVFileReaderInterface'
|
||||
import { DeleteAccount } from '../DeleteAccount/DeleteAccount'
|
||||
import { DeleteAccountsFromCSVFile } from './DeleteAccountsFromCSVFile'
|
||||
|
||||
describe('DeleteAccountsFromCSVFile', () => {
|
||||
let csvFileReader: CSVFileReaderInterface
|
||||
let deleteAccount: DeleteAccount
|
||||
let logger: Logger
|
||||
|
||||
const createUseCase = () => new DeleteAccountsFromCSVFile(csvFileReader, deleteAccount, logger)
|
||||
|
||||
beforeEach(() => {
|
||||
csvFileReader = {} as jest.Mocked<CSVFileReaderInterface>
|
||||
csvFileReader.getValues = jest.fn().mockResolvedValue(Result.ok(['email1']))
|
||||
|
||||
deleteAccount = {} as jest.Mocked<DeleteAccount>
|
||||
deleteAccount.execute = jest.fn().mockResolvedValue(Result.ok(''))
|
||||
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.info = jest.fn()
|
||||
})
|
||||
|
||||
it('should delete accounts', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({ fileName: 'test.csv', dryRun: false })
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should return error if csv file is invalid', async () => {
|
||||
csvFileReader.getValues = jest.fn().mockResolvedValue(Result.fail('Oops'))
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({ fileName: 'test.csv', dryRun: false })
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should return error if csv file is empty', async () => {
|
||||
csvFileReader.getValues = jest.fn().mockResolvedValue(Result.ok([]))
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({ fileName: 'test.csv', dryRun: false })
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should do nothing on a dry run', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({ fileName: 'test.csv', dryRun: true })
|
||||
|
||||
expect(deleteAccount.execute).not.toHaveBeenCalled()
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should return error if delete account fails', async () => {
|
||||
deleteAccount.execute = jest.fn().mockResolvedValue(Result.fail('Oops'))
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({ fileName: 'test.csv', dryRun: false })
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { DeleteAccount } from '../DeleteAccount/DeleteAccount'
|
||||
import { CSVFileReaderInterface } from '../../CSV/CSVFileReaderInterface'
|
||||
import { DeleteAccountsFromCSVFileDTO } from './DeleteAccountsFromCSVFileDTO'
|
||||
|
||||
export class DeleteAccountsFromCSVFile implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private csvFileReader: CSVFileReaderInterface,
|
||||
private deleteAccount: DeleteAccount,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async execute(dto: DeleteAccountsFromCSVFileDTO): Promise<Result<void>> {
|
||||
const emailsOrError = await this.csvFileReader.getValues(dto.fileName)
|
||||
if (emailsOrError.isFailed()) {
|
||||
return Result.fail(emailsOrError.getError())
|
||||
}
|
||||
const emails = emailsOrError.getValue()
|
||||
|
||||
if (emails.length === 0) {
|
||||
return Result.fail(`No emails found in CSV file ${dto.fileName}`)
|
||||
}
|
||||
|
||||
if (dto.dryRun) {
|
||||
const firstTenEmails = emails.slice(0, 10)
|
||||
this.logger.info(
|
||||
`Dry run mode enabled. Would delete ${emails.length} accounts. First 10 emails: ${firstTenEmails}`,
|
||||
)
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
|
||||
for (const email of emails) {
|
||||
const deleteAccountOrError = await this.deleteAccount.execute({
|
||||
username: email,
|
||||
})
|
||||
|
||||
if (deleteAccountOrError.isFailed()) {
|
||||
return Result.fail(deleteAccountOrError.getError())
|
||||
}
|
||||
}
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export interface DeleteAccountsFromCSVFileDTO {
|
||||
fileName: string
|
||||
dryRun: boolean
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
|
||||
import { CSVFileReaderInterface } from '../../Domain/CSV/CSVFileReaderInterface'
|
||||
|
||||
export class S3CsvFileReader implements CSVFileReaderInterface {
|
||||
constructor(
|
||||
private s3BucketName: string,
|
||||
private s3Client: S3Client,
|
||||
) {}
|
||||
|
||||
async getValues(fileName: string): Promise<Result<string[]>> {
|
||||
if (this.s3BucketName.length === 0) {
|
||||
return Result.fail('S3 bucket name is not set')
|
||||
}
|
||||
|
||||
const response = await this.s3Client.send(
|
||||
new GetObjectCommand({
|
||||
Bucket: this.s3BucketName,
|
||||
Key: fileName,
|
||||
}),
|
||||
)
|
||||
|
||||
if (response.Body === undefined) {
|
||||
return Result.fail(`Could not find CSV file at path: ${fileName}`)
|
||||
}
|
||||
|
||||
const csvContent = await response.Body.transformToString()
|
||||
|
||||
return Result.ok(csvContent.split('\n').filter((line) => line.length > 0))
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,14 @@ import { DomainEventFactoryInterface } from '../../Domain/Event/DomainEventFacto
|
||||
import { User } from '../../Domain/User/User'
|
||||
import { ClientServiceInterface } from '../../Domain/Client/ClientServiceInterface'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
@injectable()
|
||||
export class WebSocketsClientService implements ClientServiceInterface {
|
||||
constructor(
|
||||
@inject(TYPES.Auth_DomainEventFactory) private domainEventFactory: DomainEventFactoryInterface,
|
||||
@inject(TYPES.Auth_DomainEventPublisher) private domainEventPublisher: DomainEventPublisherInterface,
|
||||
@inject(TYPES.Auth_Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
async sendUserRolesChangedEvent(user: User): Promise<void> {
|
||||
@@ -20,6 +22,8 @@ export class WebSocketsClientService implements ClientServiceInterface {
|
||||
(await user.roles).map((role) => role.name),
|
||||
)
|
||||
|
||||
this.logger.info(`[WebSockets] Requesting message ${event.type} to user ${user.uuid}`)
|
||||
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createWebSocketMessageRequestedEvent({
|
||||
userUuid: user.uuid,
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
set -euo pipefail
|
||||
|
||||
sh supervisor/wait-for.sh localhost $SYNCING_SERVER_PORT
|
||||
node docker/entrypoint-server.js
|
||||
exec node docker/entrypoint-server.js
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
set -euo pipefail
|
||||
|
||||
sh supervisor/wait-for.sh localhost $AUTH_SERVER_PORT
|
||||
node docker/entrypoint-worker.js
|
||||
exec node docker/entrypoint-worker.js
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.21.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.20.4...@standardnotes/domain-events-infra@1.21.0) (2023-11-10)
|
||||
|
||||
### Features
|
||||
|
||||
* add graceful shutdown procedures upon SIGTERM ([#923](https://github.com/standardnotes/server/issues/923)) ([c24353c](https://github.com/standardnotes/server/commit/c24353cc24ebf4b40ff9a2cec8e37cfdef109e37))
|
||||
|
||||
## [1.20.4](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.20.3...@standardnotes/domain-events-infra@1.20.4) (2023-11-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events-infra",
|
||||
"version": "1.20.4",
|
||||
"version": "1.21.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import * as IORedis from 'ioredis'
|
||||
|
||||
import { RedisDomainEventSubscriber } from './RedisDomainEventSubscriber'
|
||||
|
||||
describe('RedisDomainEventSubscriber', () => {
|
||||
let redisClient: IORedis.Redis
|
||||
const eventChannel = 'test-channel'
|
||||
|
||||
const createSubscriber = () => new RedisDomainEventSubscriber(redisClient, eventChannel)
|
||||
|
||||
beforeEach(() => {
|
||||
redisClient = {} as jest.Mocked<IORedis.Redis>
|
||||
redisClient.subscribe = jest.fn()
|
||||
})
|
||||
|
||||
it('should start the subscription', () => {
|
||||
createSubscriber().start()
|
||||
|
||||
expect(redisClient.subscribe).toHaveBeenCalledWith('test-channel')
|
||||
})
|
||||
})
|
||||
@@ -1,14 +0,0 @@
|
||||
import * as IORedis from 'ioredis'
|
||||
|
||||
import { DomainEventSubscriberInterface } from '@standardnotes/domain-events'
|
||||
|
||||
export class RedisDomainEventSubscriber implements DomainEventSubscriberInterface {
|
||||
constructor(
|
||||
private redisClient: IORedis.Redis,
|
||||
private eventChannel: string,
|
||||
) {}
|
||||
|
||||
start(): void {
|
||||
void this.redisClient.subscribe(this.eventChannel)
|
||||
}
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import * as IORedis from 'ioredis'
|
||||
|
||||
import { RedisDomainEventSubscriberFactory } from './RedisDomainEventSubscriberFactory'
|
||||
import { DomainEventMessageHandlerInterface } from '@standardnotes/domain-events'
|
||||
import { RedisDomainEventSubscriber } from './RedisDomainEventSubscriber'
|
||||
|
||||
describe('RedisDomainEventSubscriberFactory', () => {
|
||||
let redisClient: IORedis.Redis
|
||||
let domainEventMessageHandler: DomainEventMessageHandlerInterface
|
||||
const eventChannel = 'events'
|
||||
|
||||
const createFactory = () =>
|
||||
new RedisDomainEventSubscriberFactory(redisClient, domainEventMessageHandler, eventChannel)
|
||||
|
||||
beforeEach(() => {
|
||||
redisClient = {} as jest.Mocked<IORedis.Redis>
|
||||
redisClient.on = jest.fn()
|
||||
|
||||
domainEventMessageHandler = {} as jest.Mocked<DomainEventMessageHandlerInterface>
|
||||
domainEventMessageHandler.handleMessage = jest.fn()
|
||||
})
|
||||
|
||||
it('should create an event subscriber', () => {
|
||||
const subscriber = createFactory().create()
|
||||
|
||||
expect(subscriber).toBeInstanceOf(RedisDomainEventSubscriber)
|
||||
expect(redisClient.on).toHaveBeenCalledWith('message', expect.any(Function))
|
||||
})
|
||||
})
|
||||
@@ -1,29 +0,0 @@
|
||||
import * as IORedis from 'ioredis'
|
||||
|
||||
import {
|
||||
DomainEventSubscriberFactoryInterface,
|
||||
DomainEventSubscriberInterface,
|
||||
DomainEventMessageHandlerInterface,
|
||||
} from '@standardnotes/domain-events'
|
||||
|
||||
import { RedisDomainEventSubscriber } from './RedisDomainEventSubscriber'
|
||||
|
||||
export class RedisDomainEventSubscriberFactory implements DomainEventSubscriberFactoryInterface {
|
||||
constructor(
|
||||
private redisClient: IORedis.Redis,
|
||||
private domainEventMessageHandler: DomainEventMessageHandlerInterface,
|
||||
private eventChannel: string,
|
||||
) {}
|
||||
|
||||
create(): DomainEventSubscriberInterface {
|
||||
const subscriber = new RedisDomainEventSubscriber(this.redisClient, this.eventChannel)
|
||||
|
||||
this.redisClient.on(
|
||||
'message',
|
||||
/* istanbul ignore next */
|
||||
async (_channel: string, message: string) => await this.domainEventMessageHandler.handleMessage(message),
|
||||
)
|
||||
|
||||
return subscriber
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user