feat(auth): add session traces
This commit is contained in:
parent
f504a8288c
commit
8bcb552783
35 changed files with 793 additions and 234 deletions
62
.pnp.cjs
generated
62
.pnp.cjs
generated
|
@ -126,7 +126,7 @@ const RAW_RUNTIME_STATE =
|
|||
["@lerna-lite/cli", "npm:1.6.0"],\
|
||||
["@lerna-lite/list", "npm:1.6.0"],\
|
||||
["@lerna-lite/run", "npm:1.6.0"],\
|
||||
["@sentry/node", "npm:7.19.0"],\
|
||||
["@sentry/node", "npm:7.27.0"],\
|
||||
["@types/jest", "npm:29.1.1"],\
|
||||
["@types/newrelic", "npm:7.0.4"],\
|
||||
["@types/node", "npm:18.11.9"],\
|
||||
|
@ -2438,16 +2438,6 @@ const RAW_RUNTIME_STATE =
|
|||
}]\
|
||||
]],\
|
||||
["@sentry/core", [\
|
||||
["npm:7.19.0", {\
|
||||
"packageLocation": "./.yarn/cache/@sentry-core-npm-7.19.0-151e6173ac-cabd7852ff.zip/node_modules/@sentry/core/",\
|
||||
"packageDependencies": [\
|
||||
["@sentry/core", "npm:7.19.0"],\
|
||||
["@sentry/types", "npm:7.19.0"],\
|
||||
["@sentry/utils", "npm:7.19.0"],\
|
||||
["tslib", "npm:1.14.1"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:7.27.0", {\
|
||||
"packageLocation": "./.yarn/cache/@sentry-core-npm-7.27.0-72a2ae90aa-1144287db2.zip/node_modules/@sentry/core/",\
|
||||
"packageDependencies": [\
|
||||
|
@ -2473,20 +2463,6 @@ const RAW_RUNTIME_STATE =
|
|||
}]\
|
||||
]],\
|
||||
["@sentry/node", [\
|
||||
["npm:7.19.0", {\
|
||||
"packageLocation": "./.yarn/cache/@sentry-node-npm-7.19.0-fd3d8dbde1-3a69647d2e.zip/node_modules/@sentry/node/",\
|
||||
"packageDependencies": [\
|
||||
["@sentry/node", "npm:7.19.0"],\
|
||||
["@sentry/core", "npm:7.19.0"],\
|
||||
["@sentry/types", "npm:7.19.0"],\
|
||||
["@sentry/utils", "npm:7.19.0"],\
|
||||
["cookie", "npm:0.4.2"],\
|
||||
["https-proxy-agent", "npm:5.0.1"],\
|
||||
["lru_map", "npm:0.3.3"],\
|
||||
["tslib", "npm:1.14.1"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:7.27.0", {\
|
||||
"packageLocation": "./.yarn/cache/@sentry-node-npm-7.27.0-f1028265b5-b85cd47555.zip/node_modules/@sentry/node/",\
|
||||
"packageDependencies": [\
|
||||
|
@ -2533,13 +2509,6 @@ const RAW_RUNTIME_STATE =
|
|||
}]\
|
||||
]],\
|
||||
["@sentry/types", [\
|
||||
["npm:7.19.0", {\
|
||||
"packageLocation": "./.yarn/cache/@sentry-types-npm-7.19.0-d6ed1960f2-541e1ef49a.zip/node_modules/@sentry/types/",\
|
||||
"packageDependencies": [\
|
||||
["@sentry/types", "npm:7.19.0"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:7.27.0", {\
|
||||
"packageLocation": "./.yarn/cache/@sentry-types-npm-7.27.0-67702fc3e1-20ace8aa51.zip/node_modules/@sentry/types/",\
|
||||
"packageDependencies": [\
|
||||
|
@ -2549,15 +2518,6 @@ const RAW_RUNTIME_STATE =
|
|||
}]\
|
||||
]],\
|
||||
["@sentry/utils", [\
|
||||
["npm:7.19.0", {\
|
||||
"packageLocation": "./.yarn/cache/@sentry-utils-npm-7.19.0-79844d4d90-50e4f391fe.zip/node_modules/@sentry/utils/",\
|
||||
"packageDependencies": [\
|
||||
["@sentry/utils", "npm:7.19.0"],\
|
||||
["@sentry/types", "npm:7.19.0"],\
|
||||
["tslib", "npm:1.14.1"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:7.27.0", {\
|
||||
"packageLocation": "./.yarn/cache/@sentry-utils-npm-7.27.0-1935a93244-760c02397d.zip/node_modules/@sentry/utils/",\
|
||||
"packageDependencies": [\
|
||||
|
@ -2621,7 +2581,7 @@ const RAW_RUNTIME_STATE =
|
|||
"packageDependencies": [\
|
||||
["@standardnotes/analytics", "workspace:packages/analytics"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.0"],\
|
||||
["@sentry/node", "npm:7.19.0"],\
|
||||
["@sentry/node", "npm:7.27.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
|
@ -2673,7 +2633,7 @@ const RAW_RUNTIME_STATE =
|
|||
"packageDependencies": [\
|
||||
["@standardnotes/api-gateway", "workspace:packages/api-gateway"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.0"],\
|
||||
["@sentry/node", "npm:7.19.0"],\
|
||||
["@sentry/node", "npm:7.27.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||
|
@ -2729,7 +2689,9 @@ const RAW_RUNTIME_STATE =
|
|||
"packageDependencies": [\
|
||||
["@standardnotes/auth-server", "workspace:packages/auth"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.0"],\
|
||||
["@sentry/node", "npm:7.19.0"],\
|
||||
["@sentry/node", "npm:7.27.0"],\
|
||||
["@sentry/profiling-node", "npm:0.0.12"],\
|
||||
["@sentry/tracing", "npm:7.27.0"],\
|
||||
["@standardnotes/api", "npm:1.19.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
|
@ -2953,7 +2915,7 @@ const RAW_RUNTIME_STATE =
|
|||
"packageLocation": "./packages/files/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/files-server", "workspace:packages/files"],\
|
||||
["@sentry/node", "npm:7.19.0"],\
|
||||
["@sentry/node", "npm:7.27.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/config", "npm:2.4.3"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
|
@ -3088,7 +3050,7 @@ const RAW_RUNTIME_STATE =
|
|||
"packageDependencies": [\
|
||||
["@standardnotes/revisions-server", "workspace:packages/revisions"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.0"],\
|
||||
["@sentry/node", "npm:7.19.0"],\
|
||||
["@sentry/node", "npm:7.27.0"],\
|
||||
["@standardnotes/api", "npm:1.19.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
|
@ -3133,7 +3095,7 @@ const RAW_RUNTIME_STATE =
|
|||
"packageDependencies": [\
|
||||
["@standardnotes/scheduler-server", "workspace:packages/scheduler"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.0"],\
|
||||
["@sentry/node", "npm:7.19.0"],\
|
||||
["@sentry/node", "npm:7.27.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
|
@ -3194,7 +3156,7 @@ const RAW_RUNTIME_STATE =
|
|||
["@lerna-lite/cli", "npm:1.6.0"],\
|
||||
["@lerna-lite/list", "npm:1.6.0"],\
|
||||
["@lerna-lite/run", "npm:1.6.0"],\
|
||||
["@sentry/node", "npm:7.19.0"],\
|
||||
["@sentry/node", "npm:7.27.0"],\
|
||||
["@types/jest", "npm:29.1.1"],\
|
||||
["@types/newrelic", "npm:7.0.4"],\
|
||||
["@types/node", "npm:18.11.9"],\
|
||||
|
@ -3362,7 +3324,7 @@ const RAW_RUNTIME_STATE =
|
|||
"packageDependencies": [\
|
||||
["@standardnotes/websockets-server", "workspace:packages/websockets"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.0"],\
|
||||
["@sentry/node", "npm:7.19.0"],\
|
||||
["@sentry/node", "npm:7.27.0"],\
|
||||
["@standardnotes/api", "npm:1.19.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
|
@ -3402,7 +3364,7 @@ const RAW_RUNTIME_STATE =
|
|||
"packageDependencies": [\
|
||||
["@standardnotes/workspace-server", "workspace:packages/workspace"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.0"],\
|
||||
["@sentry/node", "npm:7.19.0"],\
|
||||
["@sentry/node", "npm:7.27.0"],\
|
||||
["@standardnotes/api", "npm:1.19.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -61,7 +61,7 @@
|
|||
},
|
||||
"packageManager": "yarn@4.0.0-rc.25",
|
||||
"dependencies": {
|
||||
"@sentry/node": "^7.19.0",
|
||||
"@sentry/node": "^7.27.0",
|
||||
"newrelic": "^9.6.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@newrelic/winston-enricher": "^4.0.0",
|
||||
"@sentry/node": "^7.19.0",
|
||||
"@sentry/node": "^7.27.0",
|
||||
"@standardnotes/common": "workspace:*",
|
||||
"@standardnotes/domain-core": "workspace:^",
|
||||
"@standardnotes/domain-events": "workspace:*",
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@newrelic/winston-enricher": "^4.0.0",
|
||||
"@sentry/node": "^7.19.0",
|
||||
"@sentry/node": "^7.27.0",
|
||||
"@standardnotes/common": "workspace:^",
|
||||
"@standardnotes/domain-events": "workspace:*",
|
||||
"@standardnotes/domain-events-infra": "workspace:*",
|
||||
|
|
|
@ -13,8 +13,8 @@ ENCRYPTION_SERVER_KEY=change-me-!
|
|||
|
||||
PORT=3000
|
||||
|
||||
DB_HOST=127.0.0.1
|
||||
DB_REPLICA_HOST=127.0.0.1
|
||||
DB_HOST=localhost
|
||||
DB_REPLICA_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_USERNAME=auth
|
||||
DB_PASSWORD=changeme123
|
||||
|
|
|
@ -3,6 +3,8 @@ import 'reflect-metadata'
|
|||
import 'newrelic'
|
||||
|
||||
import * as Sentry from '@sentry/node'
|
||||
import * as Tracing from '@sentry/tracing'
|
||||
import { ProfilingIntegration } from '@sentry/profiling-node'
|
||||
|
||||
import '../src/Controller/HealthCheckController'
|
||||
import '../src/Controller/SessionController'
|
||||
|
@ -24,7 +26,7 @@ import '../src/Infra/InversifyExpressUtils/InversifyExpressUserRequestsControlle
|
|||
import '../src/Infra/InversifyExpressUtils/InversifyExpressWebSocketsController'
|
||||
|
||||
import * as cors from 'cors'
|
||||
import { urlencoded, json, Request, Response, NextFunction, RequestHandler, ErrorRequestHandler } from 'express'
|
||||
import { urlencoded, json, Request, Response, NextFunction, ErrorRequestHandler } from 'express'
|
||||
import * as winston from 'winston'
|
||||
import * as dayjs from 'dayjs'
|
||||
import * as utc from 'dayjs/plugin/utc'
|
||||
|
@ -53,13 +55,28 @@ void container.load().then((container) => {
|
|||
app.use(cors())
|
||||
|
||||
if (env.get('SENTRY_DSN', true)) {
|
||||
const tracesSampleRate = env.get('SENTRY_TRACE_SAMPLE_RATE', true)
|
||||
? +env.get('SENTRY_TRACE_SAMPLE_RATE', true)
|
||||
: 0
|
||||
|
||||
const profilesSampleRate = env.get('SENTRY_PROFILES_SAMPLE_RATE', true)
|
||||
? +env.get('SENTRY_PROFILES_SAMPLE_RATE', true)
|
||||
: 0
|
||||
Sentry.init({
|
||||
dsn: env.get('SENTRY_DSN'),
|
||||
integrations: [new Sentry.Integrations.Http({ tracing: false, breadcrumbs: true })],
|
||||
tracesSampleRate: 0,
|
||||
integrations: [
|
||||
new Sentry.Integrations.Http({ tracing: tracesSampleRate !== 0 }),
|
||||
new ProfilingIntegration(),
|
||||
new Tracing.Integrations.Express({
|
||||
app,
|
||||
}),
|
||||
],
|
||||
tracesSampleRate,
|
||||
profilesSampleRate,
|
||||
})
|
||||
|
||||
app.use(Sentry.Handlers.requestHandler() as RequestHandler)
|
||||
app.use(Sentry.Handlers.requestHandler())
|
||||
app.use(Sentry.Handlers.tracingHandler())
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -7,6 +7,6 @@ module.exports = {
|
|||
transform: {
|
||||
...tsjPreset.transform,
|
||||
},
|
||||
coveragePathIgnorePatterns: ['/Bootstrap/', '/InversifyExpressUtils/', '/Infra/', '/Projection/', '/Domain/Email/'],
|
||||
coveragePathIgnorePatterns: ['/Bootstrap/', '/Infra/', '/Projection/', '/Domain/Email/', '/Mapping/'],
|
||||
setupFilesAfterEnv: ['./test-setup.ts'],
|
||||
}
|
||||
|
|
17
packages/auth/migrations/1671448907955-add_session_traces.ts
Normal file
17
packages/auth/migrations/1671448907955-add_session_traces.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class addSessionTraces1671448907955 implements MigrationInterface {
|
||||
name = 'addSessionTraces1671448907955'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE `session_traces` (`uuid` varchar(36) NOT NULL, `user_uuid` varchar(36) NOT NULL, `username` varchar(255) NOT NULL, `subscription_plan_name` varchar(64) NULL, `created_at` datetime NOT NULL, `creation_date` date NOT NULL, `expires_at` datetime NOT NULL, INDEX `subscription_plan_name` (`subscription_plan_name`), INDEX `creation_date` (`creation_date`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX `creation_date` ON `session_traces`')
|
||||
await queryRunner.query('DROP INDEX `subscription_plan_name` ON `session_traces`')
|
||||
await queryRunner.query('DROP TABLE `session_traces`')
|
||||
}
|
||||
}
|
|
@ -31,7 +31,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@newrelic/winston-enricher": "^4.0.0",
|
||||
"@sentry/node": "^7.19.0",
|
||||
"@sentry/node": "^7.27.0",
|
||||
"@sentry/profiling-node": "^0.0.12",
|
||||
"@sentry/tracing": "^7.27.0",
|
||||
"@standardnotes/api": "^1.19.0",
|
||||
"@standardnotes/common": "workspace:*",
|
||||
"@standardnotes/domain-core": "workspace:^",
|
||||
|
|
|
@ -194,6 +194,13 @@ import { CreateCrossServiceToken } from '../Domain/UseCase/CreateCrossServiceTok
|
|||
import { ProcessUserRequest } from '../Domain/UseCase/ProcessUserRequest/ProcessUserRequest'
|
||||
import { UserRequestsController } from '../Controller/UserRequestsController'
|
||||
import { EmailSubscriptionUnsubscribedEventHandler } from '../Domain/Handler/EmailSubscriptionUnsubscribedEventHandler'
|
||||
import { SessionTraceRepositoryInterface } from '../Domain/Session/SessionTraceRepositoryInterface'
|
||||
import { MySQLSessionTraceRepository } from '../Infra/MySQL/MySQLSessionTraceRepository'
|
||||
import { MapperInterface } from '@standardnotes/domain-core'
|
||||
import { SessionTracePersistenceMapper } from '../Mapping/SessionTracePersistenceMapper'
|
||||
import { SessionTrace } from '../Domain/Session/SessionTrace'
|
||||
import { TypeORMSessionTrace } from '../Infra/TypeORM/TypeORMSessionTrace'
|
||||
import { TraceSession } from '../Domain/UseCase/TraceSession/TraceSession'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const newrelicFormatter = require('@newrelic/winston-enricher')
|
||||
|
@ -231,6 +238,8 @@ export class ContainerConfigLoader {
|
|||
})
|
||||
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(logger)
|
||||
|
||||
container.bind<TimerInterface>(TYPES.Timer).toConstantValue(new Timer())
|
||||
|
||||
if (env.get('SNS_TOPIC_ARN', true)) {
|
||||
const snsConfig: AWS.SNS.Types.ClientConfiguration = {
|
||||
apiVersion: 'latest',
|
||||
|
@ -265,11 +274,47 @@ export class ContainerConfigLoader {
|
|||
container.bind<AWS.SQS>(TYPES.SQS).toConstantValue(new AWS.SQS(sqsConfig))
|
||||
}
|
||||
|
||||
// Mapping
|
||||
container
|
||||
.bind<MapperInterface<SessionTrace, TypeORMSessionTrace>>(TYPES.SessionTracePersistenceMapper)
|
||||
.toConstantValue(new SessionTracePersistenceMapper())
|
||||
|
||||
// Controller
|
||||
container.bind<AuthController>(TYPES.AuthController).to(AuthController)
|
||||
container.bind<SubscriptionInvitesController>(TYPES.SubscriptionInvitesController).to(SubscriptionInvitesController)
|
||||
container.bind<UserRequestsController>(TYPES.UserRequestsController).to(UserRequestsController)
|
||||
|
||||
// ORM
|
||||
container
|
||||
.bind<Repository<OfflineSetting>>(TYPES.ORMOfflineSettingRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(OfflineSetting))
|
||||
container
|
||||
.bind<Repository<OfflineUserSubscription>>(TYPES.ORMOfflineUserSubscriptionRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(OfflineUserSubscription))
|
||||
container
|
||||
.bind<Repository<RevokedSession>>(TYPES.ORMRevokedSessionRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(RevokedSession))
|
||||
container.bind<Repository<Role>>(TYPES.ORMRoleRepository).toConstantValue(AppDataSource.getRepository(Role))
|
||||
container
|
||||
.bind<Repository<Session>>(TYPES.ORMSessionRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(Session))
|
||||
container
|
||||
.bind<Repository<Setting>>(TYPES.ORMSettingRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(Setting))
|
||||
container
|
||||
.bind<Repository<SharedSubscriptionInvitation>>(TYPES.ORMSharedSubscriptionInvitationRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(SharedSubscriptionInvitation))
|
||||
container
|
||||
.bind<Repository<SubscriptionSetting>>(TYPES.ORMSubscriptionSettingRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(SubscriptionSetting))
|
||||
container.bind<Repository<User>>(TYPES.ORMUserRepository).toConstantValue(AppDataSource.getRepository(User))
|
||||
container
|
||||
.bind<Repository<UserSubscription>>(TYPES.ORMUserSubscriptionRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(UserSubscription))
|
||||
container
|
||||
.bind<Repository<TypeORMSessionTrace>>(TYPES.ORMSessionTraceRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(TypeORMSessionTrace))
|
||||
|
||||
// Repositories
|
||||
container.bind<SessionRepositoryInterface>(TYPES.SessionRepository).to(MySQLSessionRepository)
|
||||
container.bind<RevokedSessionRepositoryInterface>(TYPES.RevokedSessionRepository).to(MySQLRevokedSessionRepository)
|
||||
|
@ -300,34 +345,14 @@ export class ContainerConfigLoader {
|
|||
.bind<SharedSubscriptionInvitationRepositoryInterface>(TYPES.SharedSubscriptionInvitationRepository)
|
||||
.to(MySQLSharedSubscriptionInvitationRepository)
|
||||
container.bind<PKCERepositoryInterface>(TYPES.PKCERepository).to(RedisPKCERepository)
|
||||
|
||||
// ORM
|
||||
container
|
||||
.bind<Repository<OfflineSetting>>(TYPES.ORMOfflineSettingRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(OfflineSetting))
|
||||
container
|
||||
.bind<Repository<OfflineUserSubscription>>(TYPES.ORMOfflineUserSubscriptionRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(OfflineUserSubscription))
|
||||
container
|
||||
.bind<Repository<RevokedSession>>(TYPES.ORMRevokedSessionRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(RevokedSession))
|
||||
container.bind<Repository<Role>>(TYPES.ORMRoleRepository).toConstantValue(AppDataSource.getRepository(Role))
|
||||
container
|
||||
.bind<Repository<Session>>(TYPES.ORMSessionRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(Session))
|
||||
container
|
||||
.bind<Repository<Setting>>(TYPES.ORMSettingRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(Setting))
|
||||
container
|
||||
.bind<Repository<SharedSubscriptionInvitation>>(TYPES.ORMSharedSubscriptionInvitationRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(SharedSubscriptionInvitation))
|
||||
container
|
||||
.bind<Repository<SubscriptionSetting>>(TYPES.ORMSubscriptionSettingRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(SubscriptionSetting))
|
||||
container.bind<Repository<User>>(TYPES.ORMUserRepository).toConstantValue(AppDataSource.getRepository(User))
|
||||
container
|
||||
.bind<Repository<UserSubscription>>(TYPES.ORMUserSubscriptionRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(UserSubscription))
|
||||
.bind<SessionTraceRepositoryInterface>(TYPES.SessionTraceRepository)
|
||||
.toConstantValue(
|
||||
new MySQLSessionTraceRepository(
|
||||
container.get(TYPES.ORMSessionTraceRepository),
|
||||
container.get(TYPES.SessionTracePersistenceMapper),
|
||||
),
|
||||
)
|
||||
|
||||
// Middleware
|
||||
container.bind<AuthMiddleware>(TYPES.AuthMiddleware).to(AuthMiddleware)
|
||||
|
@ -383,8 +408,97 @@ export class ContainerConfigLoader {
|
|||
container.bind(TYPES.SYNCING_SERVER_URL).toConstantValue(env.get('SYNCING_SERVER_URL'))
|
||||
container.bind(TYPES.VERSION).toConstantValue(env.get('VERSION'))
|
||||
container.bind(TYPES.PAYMENTS_SERVER_URL).toConstantValue(env.get('PAYMENTS_SERVER_URL', true))
|
||||
container
|
||||
.bind(TYPES.SESSION_TRACE_DAYS_TTL)
|
||||
.toConstantValue(env.get('SESSION_TRACE_DAYS_TTL', true) ? +env.get('SESSION_TRACE_DAYS_TTL', true) : 90)
|
||||
|
||||
// Services
|
||||
container.bind<UAParser>(TYPES.DeviceDetector).toConstantValue(new UAParser())
|
||||
container.bind<SessionService>(TYPES.SessionService).to(SessionService)
|
||||
container.bind<AuthResponseFactory20161215>(TYPES.AuthResponseFactory20161215).to(AuthResponseFactory20161215)
|
||||
container.bind<AuthResponseFactory20190520>(TYPES.AuthResponseFactory20190520).to(AuthResponseFactory20190520)
|
||||
container.bind<AuthResponseFactory20200115>(TYPES.AuthResponseFactory20200115).to(AuthResponseFactory20200115)
|
||||
container.bind<AuthResponseFactoryResolver>(TYPES.AuthResponseFactoryResolver).to(AuthResponseFactoryResolver)
|
||||
container.bind<KeyParamsFactory>(TYPES.KeyParamsFactory).to(KeyParamsFactory)
|
||||
container
|
||||
.bind<TokenDecoderInterface<SessionTokenData>>(TYPES.SessionTokenDecoder)
|
||||
.toConstantValue(new TokenDecoder<SessionTokenData>(container.get(TYPES.JWT_SECRET)))
|
||||
container
|
||||
.bind<TokenDecoderInterface<SessionTokenData>>(TYPES.FallbackSessionTokenDecoder)
|
||||
.toConstantValue(new TokenDecoder<SessionTokenData>(container.get(TYPES.LEGACY_JWT_SECRET)))
|
||||
container
|
||||
.bind<TokenDecoderInterface<CrossServiceTokenData>>(TYPES.CrossServiceTokenDecoder)
|
||||
.toConstantValue(new TokenDecoder<CrossServiceTokenData>(container.get(TYPES.AUTH_JWT_SECRET)))
|
||||
container
|
||||
.bind<TokenDecoderInterface<OfflineUserTokenData>>(TYPES.OfflineUserTokenDecoder)
|
||||
.toConstantValue(new TokenDecoder<OfflineUserTokenData>(container.get(TYPES.AUTH_JWT_SECRET)))
|
||||
container
|
||||
.bind<TokenDecoderInterface<WebSocketConnectionTokenData>>(TYPES.WebSocketConnectionTokenDecoder)
|
||||
.toConstantValue(
|
||||
new TokenDecoder<WebSocketConnectionTokenData>(container.get(TYPES.WEB_SOCKET_CONNECTION_TOKEN_SECRET)),
|
||||
)
|
||||
container
|
||||
.bind<TokenEncoderInterface<OfflineUserTokenData>>(TYPES.OfflineUserTokenEncoder)
|
||||
.toConstantValue(new TokenEncoder<OfflineUserTokenData>(container.get(TYPES.AUTH_JWT_SECRET)))
|
||||
container
|
||||
.bind<TokenEncoderInterface<SessionTokenData>>(TYPES.SessionTokenEncoder)
|
||||
.toConstantValue(new TokenEncoder<SessionTokenData>(container.get(TYPES.JWT_SECRET)))
|
||||
container
|
||||
.bind<TokenEncoderInterface<CrossServiceTokenData>>(TYPES.CrossServiceTokenEncoder)
|
||||
.toConstantValue(new TokenEncoder<CrossServiceTokenData>(container.get(TYPES.AUTH_JWT_SECRET)))
|
||||
container
|
||||
.bind<TokenEncoderInterface<ValetTokenData>>(TYPES.ValetTokenEncoder)
|
||||
.toConstantValue(new TokenEncoder<ValetTokenData>(container.get(TYPES.VALET_TOKEN_SECRET)))
|
||||
container.bind<AuthenticationMethodResolver>(TYPES.AuthenticationMethodResolver).to(AuthenticationMethodResolver)
|
||||
container.bind<DomainEventFactory>(TYPES.DomainEventFactory).to(DomainEventFactory)
|
||||
container.bind<AxiosInstance>(TYPES.HTTPClient).toConstantValue(axios.create())
|
||||
container.bind<CrypterInterface>(TYPES.Crypter).to(CrypterNode)
|
||||
container.bind<SettingServiceInterface>(TYPES.SettingService).to(SettingService)
|
||||
container.bind<SubscriptionSettingServiceInterface>(TYPES.SubscriptionSettingService).to(SubscriptionSettingService)
|
||||
container.bind<OfflineSettingServiceInterface>(TYPES.OfflineSettingService).to(OfflineSettingService)
|
||||
container.bind<CryptoNode>(TYPES.CryptoNode).toConstantValue(new CryptoNode())
|
||||
container.bind<ContentDecoderInterface>(TYPES.ContenDecoder).toConstantValue(new ContentDecoder())
|
||||
container.bind<ClientServiceInterface>(TYPES.WebSocketsClientService).to(WebSocketsClientService)
|
||||
container.bind<RoleServiceInterface>(TYPES.RoleService).to(RoleService)
|
||||
container.bind<RoleToSubscriptionMapInterface>(TYPES.RoleToSubscriptionMap).to(RoleToSubscriptionMap)
|
||||
container.bind<SettingsAssociationServiceInterface>(TYPES.SettingsAssociationService).to(SettingsAssociationService)
|
||||
container
|
||||
.bind<SubscriptionSettingsAssociationServiceInterface>(TYPES.SubscriptionSettingsAssociationService)
|
||||
.to(SubscriptionSettingsAssociationService)
|
||||
container.bind<FeatureServiceInterface>(TYPES.FeatureService).to(FeatureService)
|
||||
container.bind<SettingInterpreterInterface>(TYPES.SettingInterpreter).to(SettingInterpreter)
|
||||
container.bind<SettingDecrypterInterface>(TYPES.SettingDecrypter).to(SettingDecrypter)
|
||||
container
|
||||
.bind<SelectorInterface<ProtocolVersion>>(TYPES.ProtocolVersionSelector)
|
||||
.toConstantValue(new DeterministicSelector<ProtocolVersion>())
|
||||
container
|
||||
.bind<SelectorInterface<boolean>>(TYPES.BooleanSelector)
|
||||
.toConstantValue(new DeterministicSelector<boolean>())
|
||||
container.bind<UserSubscriptionServiceInterface>(TYPES.UserSubscriptionService).to(UserSubscriptionService)
|
||||
container.bind<ValidatorInterface<Uuid>>(TYPES.UuidValidator).toConstantValue(new UuidValidator())
|
||||
|
||||
if (env.get('SNS_TOPIC_ARN', true)) {
|
||||
container
|
||||
.bind<SNSDomainEventPublisher>(TYPES.DomainEventPublisher)
|
||||
.toConstantValue(new SNSDomainEventPublisher(container.get(TYPES.SNS), container.get(TYPES.SNS_TOPIC_ARN)))
|
||||
} else {
|
||||
container
|
||||
.bind<RedisDomainEventPublisher>(TYPES.DomainEventPublisher)
|
||||
.toConstantValue(
|
||||
new RedisDomainEventPublisher(container.get(TYPES.Redis), container.get(TYPES.REDIS_EVENTS_CHANNEL)),
|
||||
)
|
||||
}
|
||||
|
||||
// use cases
|
||||
container
|
||||
.bind<TraceSession>(TYPES.TraceSession)
|
||||
.toConstantValue(
|
||||
new TraceSession(
|
||||
container.get(TYPES.SessionTraceRepository),
|
||||
container.get(TYPES.Timer),
|
||||
container.get(TYPES.SESSION_TRACE_DAYS_TTL),
|
||||
),
|
||||
)
|
||||
container.bind<AuthenticateUser>(TYPES.AuthenticateUser).to(AuthenticateUser)
|
||||
container.bind<AuthenticateRequest>(TYPES.AuthenticateRequest).to(AuthenticateRequest)
|
||||
container.bind<RefreshSessionToken>(TYPES.RefreshSessionToken).to(RefreshSessionToken)
|
||||
|
@ -483,84 +597,6 @@ export class ContainerConfigLoader {
|
|||
.bind<PredicateVerificationRequestedEventHandler>(TYPES.PredicateVerificationRequestedEventHandler)
|
||||
.to(PredicateVerificationRequestedEventHandler)
|
||||
|
||||
// Services
|
||||
container.bind<UAParser>(TYPES.DeviceDetector).toConstantValue(new UAParser())
|
||||
container.bind<SessionService>(TYPES.SessionService).to(SessionService)
|
||||
container.bind<AuthResponseFactory20161215>(TYPES.AuthResponseFactory20161215).to(AuthResponseFactory20161215)
|
||||
container.bind<AuthResponseFactory20190520>(TYPES.AuthResponseFactory20190520).to(AuthResponseFactory20190520)
|
||||
container.bind<AuthResponseFactory20200115>(TYPES.AuthResponseFactory20200115).to(AuthResponseFactory20200115)
|
||||
container.bind<AuthResponseFactoryResolver>(TYPES.AuthResponseFactoryResolver).to(AuthResponseFactoryResolver)
|
||||
container.bind<KeyParamsFactory>(TYPES.KeyParamsFactory).to(KeyParamsFactory)
|
||||
container
|
||||
.bind<TokenDecoderInterface<SessionTokenData>>(TYPES.SessionTokenDecoder)
|
||||
.toConstantValue(new TokenDecoder<SessionTokenData>(container.get(TYPES.JWT_SECRET)))
|
||||
container
|
||||
.bind<TokenDecoderInterface<SessionTokenData>>(TYPES.FallbackSessionTokenDecoder)
|
||||
.toConstantValue(new TokenDecoder<SessionTokenData>(container.get(TYPES.LEGACY_JWT_SECRET)))
|
||||
container
|
||||
.bind<TokenDecoderInterface<CrossServiceTokenData>>(TYPES.CrossServiceTokenDecoder)
|
||||
.toConstantValue(new TokenDecoder<CrossServiceTokenData>(container.get(TYPES.AUTH_JWT_SECRET)))
|
||||
container
|
||||
.bind<TokenDecoderInterface<OfflineUserTokenData>>(TYPES.OfflineUserTokenDecoder)
|
||||
.toConstantValue(new TokenDecoder<OfflineUserTokenData>(container.get(TYPES.AUTH_JWT_SECRET)))
|
||||
container
|
||||
.bind<TokenDecoderInterface<WebSocketConnectionTokenData>>(TYPES.WebSocketConnectionTokenDecoder)
|
||||
.toConstantValue(
|
||||
new TokenDecoder<WebSocketConnectionTokenData>(container.get(TYPES.WEB_SOCKET_CONNECTION_TOKEN_SECRET)),
|
||||
)
|
||||
container
|
||||
.bind<TokenEncoderInterface<OfflineUserTokenData>>(TYPES.OfflineUserTokenEncoder)
|
||||
.toConstantValue(new TokenEncoder<OfflineUserTokenData>(container.get(TYPES.AUTH_JWT_SECRET)))
|
||||
container
|
||||
.bind<TokenEncoderInterface<SessionTokenData>>(TYPES.SessionTokenEncoder)
|
||||
.toConstantValue(new TokenEncoder<SessionTokenData>(container.get(TYPES.JWT_SECRET)))
|
||||
container
|
||||
.bind<TokenEncoderInterface<CrossServiceTokenData>>(TYPES.CrossServiceTokenEncoder)
|
||||
.toConstantValue(new TokenEncoder<CrossServiceTokenData>(container.get(TYPES.AUTH_JWT_SECRET)))
|
||||
container
|
||||
.bind<TokenEncoderInterface<ValetTokenData>>(TYPES.ValetTokenEncoder)
|
||||
.toConstantValue(new TokenEncoder<ValetTokenData>(container.get(TYPES.VALET_TOKEN_SECRET)))
|
||||
container.bind<AuthenticationMethodResolver>(TYPES.AuthenticationMethodResolver).to(AuthenticationMethodResolver)
|
||||
container.bind<DomainEventFactory>(TYPES.DomainEventFactory).to(DomainEventFactory)
|
||||
container.bind<AxiosInstance>(TYPES.HTTPClient).toConstantValue(axios.create())
|
||||
container.bind<CrypterInterface>(TYPES.Crypter).to(CrypterNode)
|
||||
container.bind<SettingServiceInterface>(TYPES.SettingService).to(SettingService)
|
||||
container.bind<SubscriptionSettingServiceInterface>(TYPES.SubscriptionSettingService).to(SubscriptionSettingService)
|
||||
container.bind<OfflineSettingServiceInterface>(TYPES.OfflineSettingService).to(OfflineSettingService)
|
||||
container.bind<CryptoNode>(TYPES.CryptoNode).toConstantValue(new CryptoNode())
|
||||
container.bind<TimerInterface>(TYPES.Timer).toConstantValue(new Timer())
|
||||
container.bind<ContentDecoderInterface>(TYPES.ContenDecoder).toConstantValue(new ContentDecoder())
|
||||
container.bind<ClientServiceInterface>(TYPES.WebSocketsClientService).to(WebSocketsClientService)
|
||||
container.bind<RoleServiceInterface>(TYPES.RoleService).to(RoleService)
|
||||
container.bind<RoleToSubscriptionMapInterface>(TYPES.RoleToSubscriptionMap).to(RoleToSubscriptionMap)
|
||||
container.bind<SettingsAssociationServiceInterface>(TYPES.SettingsAssociationService).to(SettingsAssociationService)
|
||||
container
|
||||
.bind<SubscriptionSettingsAssociationServiceInterface>(TYPES.SubscriptionSettingsAssociationService)
|
||||
.to(SubscriptionSettingsAssociationService)
|
||||
container.bind<FeatureServiceInterface>(TYPES.FeatureService).to(FeatureService)
|
||||
container.bind<SettingInterpreterInterface>(TYPES.SettingInterpreter).to(SettingInterpreter)
|
||||
container.bind<SettingDecrypterInterface>(TYPES.SettingDecrypter).to(SettingDecrypter)
|
||||
container
|
||||
.bind<SelectorInterface<ProtocolVersion>>(TYPES.ProtocolVersionSelector)
|
||||
.toConstantValue(new DeterministicSelector<ProtocolVersion>())
|
||||
container
|
||||
.bind<SelectorInterface<boolean>>(TYPES.BooleanSelector)
|
||||
.toConstantValue(new DeterministicSelector<boolean>())
|
||||
container.bind<UserSubscriptionServiceInterface>(TYPES.UserSubscriptionService).to(UserSubscriptionService)
|
||||
container.bind<ValidatorInterface<Uuid>>(TYPES.UuidValidator).toConstantValue(new UuidValidator())
|
||||
|
||||
if (env.get('SNS_TOPIC_ARN', true)) {
|
||||
container
|
||||
.bind<SNSDomainEventPublisher>(TYPES.DomainEventPublisher)
|
||||
.toConstantValue(new SNSDomainEventPublisher(container.get(TYPES.SNS), container.get(TYPES.SNS_TOPIC_ARN)))
|
||||
} else {
|
||||
container
|
||||
.bind<RedisDomainEventPublisher>(TYPES.DomainEventPublisher)
|
||||
.toConstantValue(
|
||||
new RedisDomainEventPublisher(container.get(TYPES.Redis), container.get(TYPES.REDIS_EVENTS_CHANNEL)),
|
||||
)
|
||||
}
|
||||
|
||||
container
|
||||
.bind<EmailSubscriptionUnsubscribedEventHandler>(TYPES.EmailSubscriptionUnsubscribedEventHandler)
|
||||
.toConstantValue(
|
||||
|
|
|
@ -10,6 +10,7 @@ import { SharedSubscriptionInvitation } from '../Domain/SharedSubscription/Share
|
|||
import { OfflineUserSubscription } from '../Domain/Subscription/OfflineUserSubscription'
|
||||
import { UserSubscription } from '../Domain/Subscription/UserSubscription'
|
||||
import { User } from '../Domain/User/User'
|
||||
import { TypeORMSessionTrace } from '../Infra/TypeORM/TypeORMSessionTrace'
|
||||
import { Env } from './Env'
|
||||
|
||||
const env: Env = new Env()
|
||||
|
@ -56,6 +57,7 @@ export const AppDataSource = new DataSource({
|
|||
OfflineSetting,
|
||||
SharedSubscriptionInvitation,
|
||||
SubscriptionSetting,
|
||||
TypeORMSessionTrace,
|
||||
],
|
||||
migrations: [env.get('DB_MIGRATIONS_PATH', true) ?? 'dist/migrations/*.js'],
|
||||
migrationsRun: true,
|
||||
|
|
|
@ -3,6 +3,8 @@ const TYPES = {
|
|||
Redis: Symbol.for('Redis'),
|
||||
SNS: Symbol.for('SNS'),
|
||||
SQS: Symbol.for('SQS'),
|
||||
// Mapping
|
||||
SessionTracePersistenceMapper: Symbol.for('SessionTracePersistenceMapper'),
|
||||
// Controller
|
||||
AuthController: Symbol.for('AuthController'),
|
||||
SubscriptionInvitesController: Symbol.for('SubscriptionInvitesController'),
|
||||
|
@ -23,6 +25,7 @@ const TYPES = {
|
|||
OfflineSubscriptionTokenRepository: Symbol.for('OfflineSubscriptionTokenRepository'),
|
||||
SharedSubscriptionInvitationRepository: Symbol.for('SharedSubscriptionInvitationRepository'),
|
||||
PKCERepository: Symbol.for('PKCERepository'),
|
||||
SessionTraceRepository: Symbol.for('SessionTraceRepository'),
|
||||
// ORM
|
||||
ORMOfflineSettingRepository: Symbol.for('ORMOfflineSettingRepository'),
|
||||
ORMOfflineUserSubscriptionRepository: Symbol.for('ORMOfflineUserSubscriptionRepository'),
|
||||
|
@ -34,6 +37,7 @@ const TYPES = {
|
|||
ORMSubscriptionSettingRepository: Symbol.for('ORMSubscriptionSettingRepository'),
|
||||
ORMUserRepository: Symbol.for('ORMUserRepository'),
|
||||
ORMUserSubscriptionRepository: Symbol.for('ORMUserSubscriptionRepository'),
|
||||
ORMSessionTraceRepository: Symbol.for('ORMSessionTraceRepository'),
|
||||
// Middleware
|
||||
AuthMiddleware: Symbol.for('AuthMiddleware'),
|
||||
ApiGatewayAuthMiddleware: Symbol.for('ApiGatewayAuthMiddleware'),
|
||||
|
@ -81,6 +85,7 @@ const TYPES = {
|
|||
SYNCING_SERVER_URL: Symbol.for('SYNCING_SERVER_URL'),
|
||||
VERSION: Symbol.for('VERSION'),
|
||||
PAYMENTS_SERVER_URL: Symbol.for('PAYMENTS_SERVER_URL'),
|
||||
SESSION_TRACE_DAYS_TTL: Symbol.for('SESSION_TRACE_DAYS_TTL'),
|
||||
// use cases
|
||||
AuthenticateUser: Symbol.for('AuthenticateUser'),
|
||||
AuthenticateRequest: Symbol.for('AuthenticateRequest'),
|
||||
|
@ -119,6 +124,7 @@ const TYPES = {
|
|||
VerifyPredicate: Symbol.for('VerifyPredicate'),
|
||||
CreateCrossServiceToken: Symbol.for('CreateCrossServiceToken'),
|
||||
ProcessUserRequest: Symbol.for('ProcessUserRequest'),
|
||||
TraceSession: Symbol.for('TraceSession'),
|
||||
// Handlers
|
||||
UserRegisteredEventHandler: Symbol.for('UserRegisteredEventHandler'),
|
||||
AccountDeletionRequestedEventHandler: Symbol.for('AccountDeletionRequestedEventHandler'),
|
||||
|
|
17
packages/auth/src/Domain/Session/SessionTrace.spec.ts
Normal file
17
packages/auth/src/Domain/Session/SessionTrace.spec.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { SubscriptionPlanName, Username, Uuid } from '@standardnotes/domain-core'
|
||||
import { SessionTrace } from './SessionTrace'
|
||||
|
||||
describe('SessionTrace', () => {
|
||||
it('should create an entity', () => {
|
||||
const entityOrError = SessionTrace.create({
|
||||
userUuid: Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue(),
|
||||
username: Username.create('foobar').getValue(),
|
||||
subscriptionPlanName: SubscriptionPlanName.create(SubscriptionPlanName.NAMES.PlusPlan).getValue(),
|
||||
createdAt: new Date(),
|
||||
expiresAt: new Date(),
|
||||
})
|
||||
|
||||
expect(entityOrError.isFailed()).toBeFalsy()
|
||||
expect(entityOrError.getValue().id).not.toBeNull()
|
||||
})
|
||||
})
|
17
packages/auth/src/Domain/Session/SessionTrace.ts
Normal file
17
packages/auth/src/Domain/Session/SessionTrace.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core'
|
||||
|
||||
import { SessionTraceProps } from './SessionTraceProps'
|
||||
|
||||
export class SessionTrace extends Entity<SessionTraceProps> {
|
||||
get id(): UniqueEntityId {
|
||||
return this._id
|
||||
}
|
||||
|
||||
private constructor(props: SessionTraceProps, id?: UniqueEntityId) {
|
||||
super(props, id)
|
||||
}
|
||||
|
||||
static create(props: SessionTraceProps, id?: UniqueEntityId): Result<SessionTrace> {
|
||||
return Result.ok<SessionTrace>(new SessionTrace(props, id))
|
||||
}
|
||||
}
|
9
packages/auth/src/Domain/Session/SessionTraceProps.ts
Normal file
9
packages/auth/src/Domain/Session/SessionTraceProps.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { SubscriptionPlanName, Username, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
export interface SessionTraceProps {
|
||||
userUuid: Uuid
|
||||
username: Username
|
||||
createdAt: Date
|
||||
expiresAt: Date
|
||||
subscriptionPlanName: SubscriptionPlanName | null
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import { Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { SessionTrace } from './SessionTrace'
|
||||
|
||||
export interface SessionTraceRepositoryInterface {
|
||||
save(sessionTrace: SessionTrace): Promise<void>
|
||||
findOneByUserUuidAndDate(userUuid: Uuid, date: Date): Promise<SessionTrace | null>
|
||||
}
|
|
@ -8,6 +8,10 @@ import { Role } from '../../Role/Role'
|
|||
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||
|
||||
import { CreateCrossServiceToken } from './CreateCrossServiceToken'
|
||||
import { RoleToSubscriptionMapInterface } from '../../Role/RoleToSubscriptionMapInterface'
|
||||
import { TraceSession } from '../TraceSession/TraceSession'
|
||||
import { Logger } from 'winston'
|
||||
import { Result, RoleName, SubscriptionPlanName } from '@standardnotes/domain-core'
|
||||
|
||||
describe('CreateCrossServiceToken', () => {
|
||||
let userProjector: ProjectorInterface<User>
|
||||
|
@ -15,6 +19,9 @@ describe('CreateCrossServiceToken', () => {
|
|||
let roleProjector: ProjectorInterface<Role>
|
||||
let tokenEncoder: TokenEncoderInterface<CrossServiceTokenData>
|
||||
let userRepository: UserRepositoryInterface
|
||||
let roleToSubscriptionMap: RoleToSubscriptionMapInterface
|
||||
let traceSession: TraceSession
|
||||
let logger: Logger
|
||||
const jwtTTL = 60
|
||||
|
||||
let session: Session
|
||||
|
@ -22,16 +29,29 @@ describe('CreateCrossServiceToken', () => {
|
|||
let role: Role
|
||||
|
||||
const createUseCase = () =>
|
||||
new CreateCrossServiceToken(userProjector, sessionProjector, roleProjector, tokenEncoder, userRepository, jwtTTL)
|
||||
new CreateCrossServiceToken(
|
||||
userProjector,
|
||||
sessionProjector,
|
||||
roleProjector,
|
||||
tokenEncoder,
|
||||
userRepository,
|
||||
jwtTTL,
|
||||
roleToSubscriptionMap,
|
||||
traceSession,
|
||||
logger,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
session = {} as jest.Mocked<Session>
|
||||
|
||||
user = {} as jest.Mocked<User>
|
||||
user = {
|
||||
uuid: '1-2-3',
|
||||
email: 'test@test.te',
|
||||
} as jest.Mocked<User>
|
||||
user.roles = Promise.resolve([role])
|
||||
|
||||
userProjector = {} as jest.Mocked<ProjectorInterface<User>>
|
||||
userProjector.projectSimple = jest.fn().mockReturnValue({ bar: 'baz' })
|
||||
userProjector.projectSimple = jest.fn().mockReturnValue({ uuid: '1-2-3', email: 'test@test.te' })
|
||||
|
||||
roleProjector = {} as jest.Mocked<ProjectorInterface<Role>>
|
||||
roleProjector.projectSimple = jest.fn().mockReturnValue({ name: 'role1', uuid: '1-3-4' })
|
||||
|
@ -45,6 +65,18 @@ describe('CreateCrossServiceToken', () => {
|
|||
|
||||
userRepository = {} as jest.Mocked<UserRepositoryInterface>
|
||||
userRepository.findOneByUuid = jest.fn().mockReturnValue(user)
|
||||
|
||||
roleToSubscriptionMap = {} as jest.Mocked<RoleToSubscriptionMapInterface>
|
||||
roleToSubscriptionMap.filterNonSubscriptionRoles = jest.fn().mockReturnValue([RoleName.NAMES.PlusUser])
|
||||
roleToSubscriptionMap.getSubscriptionNameForRoleName = jest
|
||||
.fn()
|
||||
.mockReturnValue(SubscriptionPlanName.NAMES.PlusPlan)
|
||||
|
||||
traceSession = {} as jest.Mocked<TraceSession>
|
||||
traceSession.execute = jest.fn()
|
||||
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.error = jest.fn()
|
||||
})
|
||||
|
||||
it('should create a cross service token for user', async () => {
|
||||
|
@ -53,6 +85,11 @@ describe('CreateCrossServiceToken', () => {
|
|||
session,
|
||||
})
|
||||
|
||||
expect(traceSession.execute).toHaveBeenCalledWith({
|
||||
userUuid: '1-2-3',
|
||||
username: 'test@test.te',
|
||||
subscriptionPlanName: 'PLUS_PLAN',
|
||||
})
|
||||
expect(tokenEncoder.encodeExpirableToken).toHaveBeenCalledWith(
|
||||
{
|
||||
roles: [
|
||||
|
@ -65,7 +102,8 @@ describe('CreateCrossServiceToken', () => {
|
|||
test: 'test',
|
||||
},
|
||||
user: {
|
||||
bar: 'baz',
|
||||
email: 'test@test.te',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
},
|
||||
60,
|
||||
|
@ -86,7 +124,8 @@ describe('CreateCrossServiceToken', () => {
|
|||
},
|
||||
],
|
||||
user: {
|
||||
bar: 'baz',
|
||||
email: 'test@test.te',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
},
|
||||
60,
|
||||
|
@ -107,7 +146,8 @@ describe('CreateCrossServiceToken', () => {
|
|||
},
|
||||
],
|
||||
user: {
|
||||
bar: 'baz',
|
||||
email: 'test@test.te',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
},
|
||||
60,
|
||||
|
@ -128,4 +168,126 @@ describe('CreateCrossServiceToken', () => {
|
|||
|
||||
expect(caughtError).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should trace session without a subscription role', async () => {
|
||||
roleToSubscriptionMap.filterNonSubscriptionRoles = jest.fn().mockReturnValue([])
|
||||
|
||||
await createUseCase().execute({
|
||||
user,
|
||||
session,
|
||||
})
|
||||
|
||||
expect(traceSession.execute).toHaveBeenCalledWith({
|
||||
userUuid: '1-2-3',
|
||||
username: 'test@test.te',
|
||||
subscriptionPlanName: null,
|
||||
})
|
||||
expect(tokenEncoder.encodeExpirableToken).toHaveBeenCalledWith(
|
||||
{
|
||||
roles: [
|
||||
{
|
||||
name: 'role1',
|
||||
uuid: '1-3-4',
|
||||
},
|
||||
],
|
||||
session: {
|
||||
test: 'test',
|
||||
},
|
||||
user: {
|
||||
email: 'test@test.te',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
},
|
||||
60,
|
||||
)
|
||||
})
|
||||
|
||||
it('should trace session without a subscription', async () => {
|
||||
roleToSubscriptionMap.getSubscriptionNameForRoleName = jest.fn().mockReturnValue(undefined)
|
||||
|
||||
await createUseCase().execute({
|
||||
user,
|
||||
session,
|
||||
})
|
||||
|
||||
expect(traceSession.execute).toHaveBeenCalledWith({
|
||||
userUuid: '1-2-3',
|
||||
username: 'test@test.te',
|
||||
subscriptionPlanName: null,
|
||||
})
|
||||
expect(tokenEncoder.encodeExpirableToken).toHaveBeenCalledWith(
|
||||
{
|
||||
roles: [
|
||||
{
|
||||
name: 'role1',
|
||||
uuid: '1-3-4',
|
||||
},
|
||||
],
|
||||
session: {
|
||||
test: 'test',
|
||||
},
|
||||
user: {
|
||||
email: 'test@test.te',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
},
|
||||
60,
|
||||
)
|
||||
})
|
||||
|
||||
it('should create token if tracing session throws an error', async () => {
|
||||
traceSession.execute = jest.fn().mockRejectedValue(new Error('test'))
|
||||
|
||||
await createUseCase().execute({
|
||||
user,
|
||||
session,
|
||||
})
|
||||
|
||||
expect(tokenEncoder.encodeExpirableToken).toHaveBeenCalledWith(
|
||||
{
|
||||
roles: [
|
||||
{
|
||||
name: 'role1',
|
||||
uuid: '1-3-4',
|
||||
},
|
||||
],
|
||||
session: {
|
||||
test: 'test',
|
||||
},
|
||||
user: {
|
||||
email: 'test@test.te',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
},
|
||||
60,
|
||||
)
|
||||
})
|
||||
|
||||
it('should create token if tracing session fails', async () => {
|
||||
traceSession.execute = jest.fn().mockReturnValue(Result.fail('Ooops'))
|
||||
|
||||
await createUseCase().execute({
|
||||
user,
|
||||
session,
|
||||
})
|
||||
|
||||
expect(tokenEncoder.encodeExpirableToken).toHaveBeenCalledWith(
|
||||
{
|
||||
roles: [
|
||||
{
|
||||
name: 'role1',
|
||||
uuid: '1-3-4',
|
||||
},
|
||||
],
|
||||
session: {
|
||||
test: 'test',
|
||||
},
|
||||
user: {
|
||||
email: 'test@test.te',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
},
|
||||
60,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
import { RoleName } from '@standardnotes/common'
|
||||
import { TokenEncoderInterface, CrossServiceTokenData } from '@standardnotes/security'
|
||||
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import TYPES from '../../../Bootstrap/Types'
|
||||
import { ProjectorInterface } from '../../../Projection/ProjectorInterface'
|
||||
import { Role } from '../../Role/Role'
|
||||
import { RoleToSubscriptionMapInterface } from '../../Role/RoleToSubscriptionMapInterface'
|
||||
import { Session } from '../../Session/Session'
|
||||
import { User } from '../../User/User'
|
||||
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||
import { TraceSession } from '../TraceSession/TraceSession'
|
||||
import { UseCaseInterface } from '../UseCaseInterface'
|
||||
|
||||
import { CreateCrossServiceTokenDTO } from './CreateCrossServiceTokenDTO'
|
||||
import { CreateCrossServiceTokenResponse } from './CreateCrossServiceTokenResponse'
|
||||
|
||||
|
@ -21,6 +25,9 @@ export class CreateCrossServiceToken implements UseCaseInterface {
|
|||
@inject(TYPES.CrossServiceTokenEncoder) private tokenEncoder: TokenEncoderInterface<CrossServiceTokenData>,
|
||||
@inject(TYPES.UserRepository) private userRepository: UserRepositoryInterface,
|
||||
@inject(TYPES.AUTH_JWT_TTL) private jwtTTL: number,
|
||||
@inject(TYPES.RoleToSubscriptionMap) private roleToSubscriptionMap: RoleToSubscriptionMapInterface,
|
||||
@inject(TYPES.TraceSession) private traceSession: TraceSession,
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
async execute(dto: CreateCrossServiceTokenDTO): Promise<CreateCrossServiceTokenResponse> {
|
||||
|
@ -44,6 +51,19 @@ export class CreateCrossServiceToken implements UseCaseInterface {
|
|||
authTokenData.session = this.projectSession(dto.session)
|
||||
}
|
||||
|
||||
try {
|
||||
const traceSessionResult = await this.traceSession.execute({
|
||||
userUuid: user.uuid,
|
||||
username: user.email,
|
||||
subscriptionPlanName: this.getSubscriptionNameFromRoles(roles),
|
||||
})
|
||||
if (traceSessionResult.isFailed()) {
|
||||
this.logger.error(traceSessionResult.getError())
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(`Could not trace session while creating cross service token: ${(error as Error).message}`)
|
||||
}
|
||||
|
||||
return {
|
||||
token: this.tokenEncoder.encodeExpirableToken(authTokenData, this.jwtTTL),
|
||||
}
|
||||
|
@ -80,4 +100,17 @@ export class CreateCrossServiceToken implements UseCaseInterface {
|
|||
private projectRoles(roles: Array<Role>): Array<{ uuid: string; name: RoleName }> {
|
||||
return roles.map((role) => <{ uuid: string; name: RoleName }>this.roleProjector.projectSimple(role))
|
||||
}
|
||||
|
||||
private getSubscriptionNameFromRoles(roles: Array<Role>): string | null {
|
||||
const nonSubscriptionRoles = this.roleToSubscriptionMap.filterNonSubscriptionRoles(roles)
|
||||
if (nonSubscriptionRoles.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
const subscriptionName = this.roleToSubscriptionMap.getSubscriptionNameForRoleName(
|
||||
nonSubscriptionRoles[0].name as RoleName,
|
||||
)
|
||||
|
||||
return subscriptionName === undefined ? null : subscriptionName
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
import { Result } from '@standardnotes/domain-core'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
import { SessionTrace } from '../../Session/SessionTrace'
|
||||
import { SessionTraceRepositoryInterface } from '../../Session/SessionTraceRepositoryInterface'
|
||||
import { TraceSession } from './TraceSession'
|
||||
|
||||
describe('TraceSession', () => {
|
||||
let sessionTraceRepository: SessionTraceRepositoryInterface
|
||||
let timer: TimerInterface
|
||||
const sessionTraceDaysTTL = 90
|
||||
|
||||
const createUseCase = () => new TraceSession(sessionTraceRepository, timer, sessionTraceDaysTTL)
|
||||
|
||||
beforeEach(() => {
|
||||
sessionTraceRepository = {} as jest.Mocked<SessionTraceRepositoryInterface>
|
||||
sessionTraceRepository.findOneByUserUuidAndDate = jest.fn().mockReturnValue(null)
|
||||
sessionTraceRepository.save = jest.fn()
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.getUTCDateNDaysAhead = jest.fn().mockReturnValue(new Date())
|
||||
})
|
||||
|
||||
it('should save a session trace', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
userUuid: '0702b137-4f5c-438a-915e-8f8b46572ce5',
|
||||
username: 'username',
|
||||
subscriptionPlanName: 'PRO_PLAN',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
expect(result.getValue().props.userUuid.value).toEqual('0702b137-4f5c-438a-915e-8f8b46572ce5')
|
||||
expect(sessionTraceRepository.save).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('should not save a session trace if one already exists for the same user and date', async () => {
|
||||
sessionTraceRepository.findOneByUserUuidAndDate = jest.fn().mockReturnValue({} as jest.Mocked<SessionTrace>)
|
||||
|
||||
const result = await createUseCase().execute({
|
||||
userUuid: '0702b137-4f5c-438a-915e-8f8b46572ce5',
|
||||
username: 'username',
|
||||
subscriptionPlanName: null,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
expect(sessionTraceRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return an error if userUuid is invalid', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
userUuid: 'invalid',
|
||||
username: 'username',
|
||||
subscriptionPlanName: 'PRO_PLAN',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(sessionTraceRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return an error if username is invalid', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
userUuid: '0702b137-4f5c-438a-915e-8f8b46572ce5',
|
||||
username: '',
|
||||
subscriptionPlanName: 'PRO_PLAN',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(sessionTraceRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return an error if subscriptionPlanName is invalid', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
userUuid: '0702b137-4f5c-438a-915e-8f8b46572ce5',
|
||||
username: 'username',
|
||||
subscriptionPlanName: 'foobar',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(sessionTraceRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not save a session trace if creating of the session trace fails', async () => {
|
||||
const mock = jest.spyOn(SessionTrace, 'create')
|
||||
mock.mockReturnValue(Result.fail('Oops'))
|
||||
|
||||
const result = await createUseCase().execute({
|
||||
userUuid: '0702b137-4f5c-438a-915e-8f8b46572ce5',
|
||||
username: 'username',
|
||||
subscriptionPlanName: 'PRO_PLAN',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(sessionTraceRepository.save).not.toHaveBeenCalled()
|
||||
|
||||
mock.mockRestore()
|
||||
})
|
||||
})
|
|
@ -0,0 +1,60 @@
|
|||
import { Result, SubscriptionPlanName, UseCaseInterface, Username, Uuid } from '@standardnotes/domain-core'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
import { SessionTrace } from '../../Session/SessionTrace'
|
||||
import { SessionTraceRepositoryInterface } from '../../Session/SessionTraceRepositoryInterface'
|
||||
import { TraceSessionDTO } from './TraceSessionDTO'
|
||||
|
||||
export class TraceSession implements UseCaseInterface<SessionTrace> {
|
||||
constructor(
|
||||
private sessionTraceRepository: SessionTraceRepositoryInterface,
|
||||
private timer: TimerInterface,
|
||||
private sessionTraceDaysTTL: number,
|
||||
) {}
|
||||
|
||||
async execute(dto: TraceSessionDTO): Promise<Result<SessionTrace>> {
|
||||
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return Result.fail(`Failed to trace session: ${userUuidOrError.getError()}`)
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const usernameOrError = Username.create(dto.username)
|
||||
if (usernameOrError.isFailed()) {
|
||||
return Result.fail(`Failed to trace session: ${usernameOrError.getError()}`)
|
||||
}
|
||||
const username = usernameOrError.getValue()
|
||||
|
||||
let subscriptionPlanName = null
|
||||
if (dto.subscriptionPlanName !== null) {
|
||||
const subscriptionPlanNameOrError = SubscriptionPlanName.create(dto.subscriptionPlanName)
|
||||
if (subscriptionPlanNameOrError.isFailed()) {
|
||||
return Result.fail(`Failed to trace session: ${subscriptionPlanNameOrError.getError()}`)
|
||||
}
|
||||
subscriptionPlanName = subscriptionPlanNameOrError.getValue()
|
||||
}
|
||||
|
||||
const alreadyExistingTrace = await this.sessionTraceRepository.findOneByUserUuidAndDate(userUuid, new Date())
|
||||
if (alreadyExistingTrace !== null) {
|
||||
return Result.ok<SessionTrace>(alreadyExistingTrace)
|
||||
}
|
||||
|
||||
const expiresAt = this.timer.getUTCDateNDaysAhead(this.sessionTraceDaysTTL)
|
||||
|
||||
const sessionTraceOrError = SessionTrace.create({
|
||||
userUuid,
|
||||
username,
|
||||
subscriptionPlanName,
|
||||
createdAt: new Date(),
|
||||
expiresAt,
|
||||
})
|
||||
if (sessionTraceOrError.isFailed()) {
|
||||
return Result.fail(`Failed to trace session: ${sessionTraceOrError.getError()}`)
|
||||
}
|
||||
const sessionTrace = sessionTraceOrError.getValue()
|
||||
|
||||
await this.sessionTraceRepository.save(sessionTrace)
|
||||
|
||||
return Result.ok<SessionTrace>(sessionTrace)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export interface TraceSessionDTO {
|
||||
userUuid: string
|
||||
username: string
|
||||
subscriptionPlanName: string | null
|
||||
}
|
34
packages/auth/src/Infra/MySQL/MySQLSessionTraceRepository.ts
Normal file
34
packages/auth/src/Infra/MySQL/MySQLSessionTraceRepository.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { MapperInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { Repository } from 'typeorm'
|
||||
import { SessionTrace } from '../../Domain/Session/SessionTrace'
|
||||
import { SessionTraceRepositoryInterface } from '../../Domain/Session/SessionTraceRepositoryInterface'
|
||||
import { TypeORMSessionTrace } from '../TypeORM/TypeORMSessionTrace'
|
||||
|
||||
export class MySQLSessionTraceRepository implements SessionTraceRepositoryInterface {
|
||||
constructor(
|
||||
private ormRepository: Repository<TypeORMSessionTrace>,
|
||||
private mapper: MapperInterface<SessionTrace, TypeORMSessionTrace>,
|
||||
) {}
|
||||
|
||||
async findOneByUserUuidAndDate(userUuid: Uuid, date: Date): Promise<SessionTrace | null> {
|
||||
const typeOrm = await this.ormRepository
|
||||
.createQueryBuilder('trace')
|
||||
.where('trace.user_uuid = :userUuid AND trace.creation_date = :creationDate', {
|
||||
userUuid: userUuid.value,
|
||||
creationDate: new Date(date.getFullYear(), date.getMonth(), date.getDate()),
|
||||
})
|
||||
.getOne()
|
||||
|
||||
if (typeOrm === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.mapper.toDomain(typeOrm)
|
||||
}
|
||||
|
||||
async save(sessionTrace: SessionTrace): Promise<void> {
|
||||
const persistence = this.mapper.toProjection(sessionTrace)
|
||||
|
||||
await this.ormRepository.save(persistence)
|
||||
}
|
||||
}
|
47
packages/auth/src/Infra/TypeORM/TypeORMSessionTrace.ts
Normal file
47
packages/auth/src/Infra/TypeORM/TypeORMSessionTrace.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
|
||||
|
||||
@Entity({ name: 'session_traces' })
|
||||
export class TypeORMSessionTrace {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
declare uuid: string
|
||||
|
||||
@Column({
|
||||
name: 'user_uuid',
|
||||
length: 36,
|
||||
})
|
||||
declare userUuid: string
|
||||
|
||||
@Column({
|
||||
name: 'username',
|
||||
length: 255,
|
||||
})
|
||||
declare username: string
|
||||
|
||||
@Column({
|
||||
name: 'subscription_plan_name',
|
||||
length: 64,
|
||||
type: 'varchar',
|
||||
nullable: true,
|
||||
})
|
||||
@Index('subscription_plan_name')
|
||||
declare subscriptionPlanName: string | null
|
||||
|
||||
@Column({
|
||||
name: 'created_at',
|
||||
type: 'datetime',
|
||||
})
|
||||
declare createdAt: Date
|
||||
|
||||
@Column({
|
||||
name: 'creation_date',
|
||||
type: 'date',
|
||||
})
|
||||
@Index('creation_date')
|
||||
declare creationDate: Date
|
||||
|
||||
@Column({
|
||||
name: 'expires_at',
|
||||
type: 'datetime',
|
||||
})
|
||||
declare expiresAt: Date
|
||||
}
|
62
packages/auth/src/Mapping/SessionTracePersistenceMapper.ts
Normal file
62
packages/auth/src/Mapping/SessionTracePersistenceMapper.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
import { MapperInterface, SubscriptionPlanName, UniqueEntityId, Username, Uuid } from '@standardnotes/domain-core'
|
||||
import { SessionTrace } from '../Domain/Session/SessionTrace'
|
||||
import { TypeORMSessionTrace } from '../Infra/TypeORM/TypeORMSessionTrace'
|
||||
|
||||
export class SessionTracePersistenceMapper implements MapperInterface<SessionTrace, TypeORMSessionTrace> {
|
||||
toDomain(projection: TypeORMSessionTrace): SessionTrace {
|
||||
const userUuidOrError = Uuid.create(projection.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
throw new Error('Failed to create Uuid from persistence.')
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const usernameOrError = Username.create(projection.username)
|
||||
if (usernameOrError.isFailed()) {
|
||||
throw new Error('Failed to create Username from persistence.')
|
||||
}
|
||||
const username = usernameOrError.getValue()
|
||||
|
||||
let subscriptionPlanName = null
|
||||
if (projection.subscriptionPlanName !== null) {
|
||||
const subscriptionPlanNameOrError = SubscriptionPlanName.create(projection.subscriptionPlanName)
|
||||
if (subscriptionPlanNameOrError.isFailed()) {
|
||||
throw new Error('Failed to create SubscriptionPlanName from persistence.')
|
||||
}
|
||||
subscriptionPlanName = subscriptionPlanNameOrError.getValue()
|
||||
}
|
||||
|
||||
const sessionTraceOrError = SessionTrace.create(
|
||||
{
|
||||
userUuid,
|
||||
username,
|
||||
subscriptionPlanName,
|
||||
createdAt: projection.createdAt,
|
||||
expiresAt: projection.expiresAt,
|
||||
},
|
||||
new UniqueEntityId(projection.uuid),
|
||||
)
|
||||
if (sessionTraceOrError.isFailed()) {
|
||||
throw new Error('Failed to create SessionTrace from persistence.')
|
||||
}
|
||||
|
||||
return sessionTraceOrError.getValue()
|
||||
}
|
||||
|
||||
toProjection(domain: SessionTrace): TypeORMSessionTrace {
|
||||
const typeOrm = new TypeORMSessionTrace()
|
||||
|
||||
typeOrm.uuid = domain.id.toString()
|
||||
typeOrm.userUuid = domain.props.userUuid.value
|
||||
typeOrm.username = domain.props.username.value
|
||||
typeOrm.subscriptionPlanName = domain.props.subscriptionPlanName ? domain.props.subscriptionPlanName.value : null
|
||||
typeOrm.createdAt = domain.props.createdAt
|
||||
typeOrm.creationDate = new Date(
|
||||
domain.props.createdAt.getFullYear(),
|
||||
domain.props.createdAt.getMonth(),
|
||||
domain.props.createdAt.getDate(),
|
||||
)
|
||||
typeOrm.expiresAt = domain.props.expiresAt
|
||||
|
||||
return typeOrm
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@
|
|||
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sentry/node": "^7.19.0",
|
||||
"@sentry/node": "^7.27.0",
|
||||
"@standardnotes/common": "workspace:*",
|
||||
"@standardnotes/domain-events": "workspace:*",
|
||||
"@standardnotes/domain-events-infra": "workspace:*",
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@newrelic/winston-enricher": "^4.0.0",
|
||||
"@sentry/node": "^7.19.0",
|
||||
"@sentry/node": "^7.27.0",
|
||||
"@standardnotes/api": "^1.19.0",
|
||||
"@standardnotes/common": "workspace:^",
|
||||
"@standardnotes/domain-core": "workspace:^",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@newrelic/winston-enricher": "^4.0.0",
|
||||
"@sentry/node": "^7.19.0",
|
||||
"@sentry/node": "^7.27.0",
|
||||
"@standardnotes/common": "workspace:*",
|
||||
"@standardnotes/domain-core": "workspace:^",
|
||||
"@standardnotes/domain-events": "workspace:*",
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'reflect-metadata'
|
|||
import 'newrelic'
|
||||
|
||||
import * as Sentry from '@sentry/node'
|
||||
import '@sentry/tracing'
|
||||
import * as Tracing from '@sentry/tracing'
|
||||
import { ProfilingIntegration } from '@sentry/profiling-node'
|
||||
|
||||
import '../src/Controller/HealthCheckController'
|
||||
|
@ -12,7 +12,7 @@ import '../src/Controller/ItemsController'
|
|||
|
||||
import helmet from 'helmet'
|
||||
import * as cors from 'cors'
|
||||
import { urlencoded, json, Request, Response, NextFunction, RequestHandler, ErrorRequestHandler } from 'express'
|
||||
import { urlencoded, json, Request, Response, NextFunction, ErrorRequestHandler } from 'express'
|
||||
import * as winston from 'winston'
|
||||
|
||||
import { InversifyExpressServer } from 'inversify-express-utils'
|
||||
|
@ -68,12 +68,19 @@ void container.load().then((container) => {
|
|||
: 0
|
||||
Sentry.init({
|
||||
dsn: env.get('SENTRY_DSN'),
|
||||
integrations: [new Sentry.Integrations.Http({ tracing: false, breadcrumbs: true }), new ProfilingIntegration()],
|
||||
integrations: [
|
||||
new Sentry.Integrations.Http({ tracing: tracesSampleRate !== 0 }),
|
||||
new ProfilingIntegration(),
|
||||
new Tracing.Integrations.Express({
|
||||
app,
|
||||
}),
|
||||
],
|
||||
tracesSampleRate,
|
||||
profilesSampleRate,
|
||||
})
|
||||
|
||||
app.use(Sentry.Handlers.requestHandler() as RequestHandler)
|
||||
app.use(Sentry.Handlers.requestHandler())
|
||||
app.use(Sentry.Handlers.tracingHandler())
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@newrelic/winston-enricher": "^4.0.0",
|
||||
"@sentry/node": "^7.19.0",
|
||||
"@sentry/node": "^7.27.0",
|
||||
"@standardnotes/api": "^1.19.0",
|
||||
"@standardnotes/common": "workspace:^",
|
||||
"@standardnotes/domain-events": "workspace:^",
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@newrelic/winston-enricher": "^4.0.0",
|
||||
"@sentry/node": "^7.19.0",
|
||||
"@sentry/node": "^7.27.0",
|
||||
"@standardnotes/api": "^1.19.0",
|
||||
"@standardnotes/common": "workspace:*",
|
||||
"@standardnotes/domain-core": "workspace:^",
|
||||
|
|
63
yarn.lock
63
yarn.lock
|
@ -1706,17 +1706,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sentry/core@npm:7.19.0":
|
||||
version: 7.19.0
|
||||
resolution: "@sentry/core@npm:7.19.0"
|
||||
dependencies:
|
||||
"@sentry/types": "npm:7.19.0"
|
||||
"@sentry/utils": "npm:7.19.0"
|
||||
tslib: "npm:^1.9.3"
|
||||
checksum: cabd7852ff9ea58287724678f232588f15d63437c7d0bad91543c91c821ac0f19a00835dd986645aed9d9094b2c0a8c82c1e9e4fb43342b9d31c6890010b2ec2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sentry/core@npm:7.27.0":
|
||||
version: 7.27.0
|
||||
resolution: "@sentry/core@npm:7.27.0"
|
||||
|
@ -1755,21 +1744,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sentry/node@npm:^7.19.0":
|
||||
version: 7.19.0
|
||||
resolution: "@sentry/node@npm:7.19.0"
|
||||
dependencies:
|
||||
"@sentry/core": "npm:7.19.0"
|
||||
"@sentry/types": "npm:7.19.0"
|
||||
"@sentry/utils": "npm:7.19.0"
|
||||
cookie: "npm:^0.4.1"
|
||||
https-proxy-agent: "npm:^5.0.0"
|
||||
lru_map: "npm:^0.3.3"
|
||||
tslib: "npm:^1.9.3"
|
||||
checksum: 3a69647d2ea2a8583bb77f3e30aa7cc587568b8e2f02d8ae7cf0c8928f03e4135fc3b9fcbdc90b35f190cb8973d70590fa7e4ef683d43e7e4a4fa50d22ac568a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sentry/profiling-node@npm:^0.0.12":
|
||||
version: 0.0.12
|
||||
resolution: "@sentry/profiling-node@npm:0.0.12"
|
||||
|
@ -1798,13 +1772,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sentry/types@npm:7.19.0":
|
||||
version: 7.19.0
|
||||
resolution: "@sentry/types@npm:7.19.0"
|
||||
checksum: 541e1ef49ae4482bbec605c3a2075a669930db2f707fafa431174010fcd0f2ba57637399822fbe0f4c5750696fe57e450d66c0182c1b7a0a3f4e6be9d030ed55
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sentry/types@npm:7.27.0, @sentry/types@npm:^7.16.0":
|
||||
version: 7.27.0
|
||||
resolution: "@sentry/types@npm:7.27.0"
|
||||
|
@ -1812,16 +1779,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sentry/utils@npm:7.19.0":
|
||||
version: 7.19.0
|
||||
resolution: "@sentry/utils@npm:7.19.0"
|
||||
dependencies:
|
||||
"@sentry/types": "npm:7.19.0"
|
||||
tslib: "npm:^1.9.3"
|
||||
checksum: 50e4f391fe9e6009f417c54a8ec96ba25ae77615d92c7f8c22a496901b9e3f182b78cecbc412bff0b2853812258642962b1ee38f42f9708e089756b4ebb3790e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sentry/utils@npm:7.27.0, @sentry/utils@npm:^7.16.0":
|
||||
version: 7.27.0
|
||||
resolution: "@sentry/utils@npm:7.27.0"
|
||||
|
@ -1876,7 +1833,7 @@ __metadata:
|
|||
resolution: "@standardnotes/analytics@workspace:packages/analytics"
|
||||
dependencies:
|
||||
"@newrelic/winston-enricher": "npm:^4.0.0"
|
||||
"@sentry/node": "npm:^7.19.0"
|
||||
"@sentry/node": "npm:^7.27.0"
|
||||
"@standardnotes/common": "workspace:*"
|
||||
"@standardnotes/domain-core": "workspace:^"
|
||||
"@standardnotes/domain-events": "workspace:*"
|
||||
|
@ -1910,7 +1867,7 @@ __metadata:
|
|||
resolution: "@standardnotes/api-gateway@workspace:packages/api-gateway"
|
||||
dependencies:
|
||||
"@newrelic/winston-enricher": "npm:^4.0.0"
|
||||
"@sentry/node": "npm:^7.19.0"
|
||||
"@sentry/node": "npm:^7.27.0"
|
||||
"@standardnotes/common": "workspace:^"
|
||||
"@standardnotes/domain-events": "workspace:*"
|
||||
"@standardnotes/domain-events-infra": "workspace:*"
|
||||
|
@ -1968,7 +1925,9 @@ __metadata:
|
|||
resolution: "@standardnotes/auth-server@workspace:packages/auth"
|
||||
dependencies:
|
||||
"@newrelic/winston-enricher": "npm:^4.0.0"
|
||||
"@sentry/node": "npm:^7.19.0"
|
||||
"@sentry/node": "npm:^7.27.0"
|
||||
"@sentry/profiling-node": "npm:^0.0.12"
|
||||
"@sentry/tracing": "npm:^7.27.0"
|
||||
"@standardnotes/api": "npm:^1.19.0"
|
||||
"@standardnotes/common": "workspace:*"
|
||||
"@standardnotes/domain-core": "workspace:^"
|
||||
|
@ -2188,7 +2147,7 @@ __metadata:
|
|||
version: 0.0.0-use.local
|
||||
resolution: "@standardnotes/files-server@workspace:packages/files"
|
||||
dependencies:
|
||||
"@sentry/node": "npm:^7.19.0"
|
||||
"@sentry/node": "npm:^7.27.0"
|
||||
"@standardnotes/common": "workspace:*"
|
||||
"@standardnotes/config": "npm:2.4.3"
|
||||
"@standardnotes/domain-events": "workspace:*"
|
||||
|
@ -2318,7 +2277,7 @@ __metadata:
|
|||
resolution: "@standardnotes/revisions-server@workspace:packages/revisions"
|
||||
dependencies:
|
||||
"@newrelic/winston-enricher": "npm:^4.0.0"
|
||||
"@sentry/node": "npm:^7.19.0"
|
||||
"@sentry/node": "npm:^7.27.0"
|
||||
"@standardnotes/api": "npm:^1.19.0"
|
||||
"@standardnotes/common": "workspace:^"
|
||||
"@standardnotes/domain-core": "workspace:^"
|
||||
|
@ -2361,7 +2320,7 @@ __metadata:
|
|||
resolution: "@standardnotes/scheduler-server@workspace:packages/scheduler"
|
||||
dependencies:
|
||||
"@newrelic/winston-enricher": "npm:^4.0.0"
|
||||
"@sentry/node": "npm:^7.19.0"
|
||||
"@sentry/node": "npm:^7.27.0"
|
||||
"@standardnotes/common": "workspace:*"
|
||||
"@standardnotes/domain-core": "workspace:^"
|
||||
"@standardnotes/domain-events": "workspace:*"
|
||||
|
@ -2418,7 +2377,7 @@ __metadata:
|
|||
"@lerna-lite/cli": "npm:^1.5.1"
|
||||
"@lerna-lite/list": "npm:^1.5.1"
|
||||
"@lerna-lite/run": "npm:^1.5.1"
|
||||
"@sentry/node": "npm:^7.19.0"
|
||||
"@sentry/node": "npm:^7.27.0"
|
||||
"@types/jest": "npm:^29.1.1"
|
||||
"@types/newrelic": "npm:^7.0.4"
|
||||
"@types/node": "npm:^18.11.9"
|
||||
|
@ -2575,7 +2534,7 @@ __metadata:
|
|||
resolution: "@standardnotes/websockets-server@workspace:packages/websockets"
|
||||
dependencies:
|
||||
"@newrelic/winston-enricher": "npm:^4.0.0"
|
||||
"@sentry/node": "npm:^7.19.0"
|
||||
"@sentry/node": "npm:^7.27.0"
|
||||
"@standardnotes/api": "npm:^1.19.0"
|
||||
"@standardnotes/common": "workspace:^"
|
||||
"@standardnotes/domain-events": "workspace:^"
|
||||
|
@ -2613,7 +2572,7 @@ __metadata:
|
|||
resolution: "@standardnotes/workspace-server@workspace:packages/workspace"
|
||||
dependencies:
|
||||
"@newrelic/winston-enricher": "npm:^4.0.0"
|
||||
"@sentry/node": "npm:^7.19.0"
|
||||
"@sentry/node": "npm:^7.27.0"
|
||||
"@standardnotes/api": "npm:^1.19.0"
|
||||
"@standardnotes/common": "workspace:*"
|
||||
"@standardnotes/domain-core": "workspace:^"
|
||||
|
|
Loading…
Reference in a new issue