Compare commits

..

37 Commits

Author SHA1 Message Date
standardci
f9183b4c62 chore(release): publish new version
- @standardnotes/analytics@2.22.5
 - @standardnotes/api-gateway@1.59.0
 - @standardnotes/auth-server@1.112.0
 - @standardnotes/domain-core@1.16.0
 - @standardnotes/files-server@1.13.0
 - @standardnotes/home-server@1.6.0
 - @standardnotes/revisions-server@1.17.0
 - @standardnotes/scheduler-server@1.18.5
 - @standardnotes/settings@1.21.5
 - @standardnotes/syncing-server@1.38.0
 - @standardnotes/websockets-server@1.7.5
2023-05-29 12:59:40 +00:00
Karol Sójko
c7d575a0ff feat: add files server as a service to home-server (#614)
* wip: add files server as a service to home-server

* wip: introduce home-server controllers without inversify-express-utils decorators. Move in progress

* fix(auth): move remaining home server controllers

* fix(syncing-server): home server controllers

* fix(revisions): home server controllers

* fix: specs

* fix: import for legacy controller

* fix: remove router debug
2023-05-29 14:45:49 +02:00
Karol Sójko
a575e62519 fix: waiting for home server to start in ci 2023-05-29 14:05:15 +02:00
Karol Sójko
3761d60f41 fix: add VALET_TOKEN_SECRET env var to ci e2e test suite 2023-05-29 13:29:07 +02:00
standardci
fd629d43ba chore(release): publish new version
- @standardnotes/home-server@1.5.1
 - @standardnotes/revisions-server@1.16.1
2023-05-25 11:44:41 +00:00
Karol Sójko
76b1cb0f5a fix(revisions): container bindings 2023-05-25 13:17:53 +02:00
standardci
2f94abc9f7 chore(release): publish new version
- @standardnotes/api-gateway@1.58.0
 - @standardnotes/auth-server@1.111.0
 - @standardnotes/home-server@1.5.0
 - @standardnotes/revisions-server@1.16.0
2023-05-25 11:11:19 +00:00
Karol Sójko
c70040fe5d feat: add revisions service to home server (#613)
* feat: add revisions service to home server

* fix: make e2e test suite on home server non-optional

* fix(auth): specs
2023-05-25 12:57:05 +02:00
standardci
4b8a9e448a chore(release): publish new version
- @standardnotes/api-gateway@1.57.0
 - @standardnotes/auth-server@1.110.0
 - @standardnotes/home-server@1.4.4
2023-05-25 07:56:05 +00:00
Karol Sójko
1e4c7d0f31 feat: refactor auth middleware to handle required and optional cross service token scenarios (#612)
* wip: fix variable name

* wip: remove redundant middleware in auth

* fix: auth middleware refactor

* fix(auth): fetching user for key params

* fix(auth): specs

* fix(auth): registering session controller endpoints
2023-05-25 09:43:00 +02:00
Karol Sójko
ec75795a02 fix: session tokens ttl on home-server e2e suite 2023-05-24 13:56:09 +02:00
standardci
ad26b64b28 chore(release): publish new version
- @standardnotes/auth-server@1.109.2
 - @standardnotes/home-server@1.4.3
2023-05-18 10:55:29 +00:00
Karol Sójko
9e4715ebbd fix: skip paid features on the home server e2e test suite 2023-05-18 12:42:20 +02:00
Karol Sójko
cc612296d0 fix(auth): changing user credentials to work both on http proxy and direct code call 2023-05-18 12:42:20 +02:00
standardci
1148b3948c chore(release): publish new version
- @standardnotes/api-gateway@1.56.2
 - @standardnotes/home-server@1.4.2
2023-05-18 09:19:56 +00:00
Karol Sójko
c7e605fd60 fix(api-gateway): pkce endpoints resolution for direct code calls 2023-05-18 11:03:13 +02:00
Karol Sójko
4ab32c670e fix(api-gateway): decorating responses for direct call proxy 2023-05-18 11:03:12 +02:00
standardci
2d810568a8 chore(release): publish new version
- @standardnotes/api-gateway@1.56.1
 - @standardnotes/auth-server@1.109.1
 - @standardnotes/files-server@1.12.5
 - @standardnotes/home-server@1.4.1
 - @standardnotes/revisions-server@1.15.1
 - @standardnotes/syncing-server@1.37.1
2023-05-18 06:07:38 +00:00
Karol Sójko
b8353aa817 chore: add metadata to winston loggers 2023-05-18 07:54:36 +02:00
standardci
7924f63e28 chore(release): publish new version
- @standardnotes/api-gateway@1.56.0
 - @standardnotes/auth-server@1.109.0
 - @standardnotes/home-server@1.4.0
 - @standardnotes/syncing-server@1.37.0
2023-05-17 13:50:56 +00:00
Karol Sójko
b3b617ea0b feat: bundle syncing server into home server setup (#611)
* feat(syncing-server): move inversify express controllers to new structure

* wip: syncing server service binding for home server

* fix(syncing-server): container bindings

* fix(api-gateway): rename https service to service proxy

* fix: proxying requests to syncing server

* fix: responses and version binding
2023-05-17 15:38:12 +02:00
standardci
18a5071618 chore(release): publish new version
- @standardnotes/auth-server@1.108.0
 - @standardnotes/home-server@1.3.1
 - @standardnotes/revisions-server@1.15.0
 - @standardnotes/syncing-server@1.36.0
2023-05-17 10:07:01 +00:00
Karol Sójko
fea58029b9 feat(auth): move inversify express controllers to different structure (#610)
* wip: move valet token controller

* wip: move users controller

* wip: move admin controller

* wip: move subscription tokens controller

* wip: move subscription settings controller

* wip: move settings controller

* wip: move middleware

* wip: move session controller

* wip: move offline controller

* wip: move listed controller

* wip: move internal controller

* wip: move healthcheck controller

* wip: move features controller

* fix: bind inversify express controllers only for home server

* fix: inversify deps
2023-05-17 11:54:18 +02:00
standardci
e748723209 chore(release): publish new version
- @standardnotes/analytics@2.22.4
 - @standardnotes/api-gateway@1.55.0
 - @standardnotes/auth-server@1.107.0
 - @standardnotes/domain-events-infra@1.12.0
 - @standardnotes/domain-events@2.111.0
 - @standardnotes/event-store@1.8.3
 - @standardnotes/files-server@1.12.4
 - @standardnotes/home-server@1.3.0
 - @standardnotes/revisions-server@1.14.4
 - @standardnotes/scheduler-server@1.18.4
 - @standardnotes/syncing-server@1.35.4
 - @standardnotes/websockets-server@1.7.4
2023-05-17 07:39:38 +00:00
Karol Sójko
8a47d81936 feat: add direct event handling for home server (#608)
* feat(domain-events-infra): add direct call event message handler

* feat: add direct publishing of events into handlers

* fix: validating sessions with direct calls
2023-05-17 09:23:48 +02:00
standardci
7ae9f5694d chore(release): publish new version
- @standardnotes/analytics@2.22.3
 - @standardnotes/api-gateway@1.54.0
 - @standardnotes/auth-server@1.106.0
 - @standardnotes/domain-core@1.15.0
 - @standardnotes/files-server@1.12.3
 - @standardnotes/home-server@1.2.0
 - @standardnotes/revisions-server@1.14.3
 - @standardnotes/scheduler-server@1.18.3
 - @standardnotes/settings@1.21.4
 - @standardnotes/syncing-server@1.35.3
 - @standardnotes/websockets-server@1.7.3
2023-05-16 09:59:30 +00:00
Karol Sójko
9031379469 chore: fix test suite names 2023-05-16 11:44:43 +02:00
Karol Sójko
dc71e6777f feat: home-server package initial setup with Api Gateway and Auth services (#605)
* fix(api-gateway): reduce exports

* wip controllers

* fix: imports of controllers

* fix(api-gateway): rename http service interface to proxy interface

* wip: self-registering services and controllers

* wip: add registering controller method bindings and services in container

* feat: merge two services together

* wip: resolving endpoints to direct code calls

* wip: bind controller container to a singleton

* fix: controller binding to instantiate and self-register on controller container

* fix: move signout endpoint to auth controller

* wip: define inversify controllers in the controller container

* fix(auth): bind inversify controllers to controller container

* fix(auth): linter issues

* fix(auth): specs

* fix(auth): inversify controllers bindings

* wip: endpoint resolving

* wip: add endpoint for more auth controllers

* wip: add sessions controller endpoint resolvings

* wip: add subscription invites endpoint resolvings

* wip: add subscription tokens endpoint resolvings

* wip: add all binding for auth server controllers

* wip: fix migrations path

* fix: configure default env vars and ci setup
2023-05-16 11:38:56 +02:00
Karol Sójko
89eb798fa8 chore: fix nohup execution 2023-05-16 11:23:01 +02:00
Karol Sójko
4304e068b9 chore: run home server in the background with nohup 2023-05-16 11:22:09 +02:00
Karol Sójko
126bc6de6a chore: fix home server e2e suite with non installed driver 2023-05-16 11:13:13 +02:00
Karol Sójko
c2b9107f13 chore: add running home server e2e test suite without docker 2023-05-16 11:07:37 +02:00
Karol Sójko
1471f4a839 chore: disable running e2e for home server with docker 2023-05-16 11:06:10 +02:00
standardci
b0b3c6671d chore(release): publish new version
- @standardnotes/analytics@2.22.2
 - @standardnotes/api-gateway@1.53.1
 - @standardnotes/auth-server@1.105.2
 - @standardnotes/domain-events-infra@1.11.2
 - @standardnotes/event-store@1.8.2
 - @standardnotes/files-server@1.12.2
 - @standardnotes/home-server@1.1.1
 - @standardnotes/revisions-server@1.14.2
 - @standardnotes/scheduler-server@1.18.2
 - @standardnotes/syncing-server@1.35.2
 - @standardnotes/websockets-server@1.7.2
2023-05-15 10:32:14 +00:00
Karol Sójko
6ddb8fb6c2 chore(deps): upgrade aws sdk libs 2023-05-15 12:16:44 +02:00
standardci
66da49b0df chore(release): publish new version
- @standardnotes/api-gateway@1.53.0
 - @standardnotes/home-server@1.1.0
2023-05-09 11:07:23 +00:00
Karol Sójko
750cd26c36 feat(home-server): add boilerplate (#601) 2023-05-09 12:49:34 +02:00
394 changed files with 8033 additions and 4864 deletions

View File

@@ -15,6 +15,7 @@ DB_TYPE=mysql
REDIS_PORT=6379
REDIS_HOST=cache
CACHE_TYPE=redis
########
# KEYS #

View File

@@ -19,15 +19,7 @@ on:
jobs:
e2e:
strategy:
matrix:
database: [ "mysql", "sqlite" ]
include:
- cache: "redis"
database: "mysql"
- cache: "memory"
database: "sqlite"
name: (Docker) E2E Test Suite
runs-on: ubuntu-latest
services:
@@ -48,11 +40,61 @@ jobs:
- name: Run Server
run: docker compose -f docker-compose.ci.yml up -d
env:
DB_TYPE: ${{ matrix.database }}
CACHE_TYPE: ${{ matrix.cache }}
DB_TYPE: mysql
CACHE_TYPE: redis
- name: Wait for server to start
run: docker/is-available.sh http://localhost:3123 $(pwd)/logs
- name: Run E2E Test Suite
run: yarn dlx mocha-headless-chrome --timeout 1200000 -f http://localhost:9001/mocha/test.html
e2e-home-server:
name: (Home Server) E2E Test Suite
runs-on: ubuntu-latest
services:
snjs:
image: standardnotes/snjs:${{ inputs.snjs_image_tag }}
ports:
- 9001:9001
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Install Dependencies
run: yarn install --immutable
- name: Build
run: yarn build
- name: Copy dotenv file
run: cp packages/home-server/.env.sample packages/home-server/.env
- name: Fill in env variables
run: |
sed -i "s/JWT_SECRET=/JWT_SECRET=$(openssl rand -hex 32)/g" packages/home-server/.env
sed -i "s/AUTH_JWT_SECRET=/AUTH_JWT_SECRET=$(openssl rand -hex 32)/g" packages/home-server/.env
sed -i "s/ENCRYPTION_SERVER_KEY=/ENCRYPTION_SERVER_KEY=$(openssl rand -hex 32)/g" packages/home-server/.env
sed -i "s/PSEUDO_KEY_PARAMS_KEY=/PSEUDO_KEY_PARAMS_KEY=$(openssl rand -hex 32)/g" packages/home-server/.env
sed -i "s/VALET_TOKEN_SECRET=/VALET_TOKEN_SECRET=$(openssl rand -hex 32)/g" packages/home-server/.env
echo "ACCESS_TOKEN_AGE=4" >> packages/home-server/.env
echo "REFRESH_TOKEN_AGE=7" >> packages/home-server/.env
echo "REVISIONS_FREQUENCY=5" >> packages/home-server/.env
- name: Run Server
run: nohup yarn workspace @standardnotes/home-server start &
env:
PORT: 3123
- name: Wait for server to start
run: for i in {1..30}; do curl -s http://localhost:3123/healthcheck && break || sleep 1; done
- name: Run E2E Test Suite
run: yarn dlx mocha-headless-chrome --timeout 1200000 -f http://localhost:9001/mocha/test.html?skip_paid_features=true

1138
.pnp.cjs generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

View File

@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.22.5](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.22.4...@standardnotes/analytics@2.22.5) (2023-05-29)
**Note:** Version bump only for package @standardnotes/analytics
## [2.22.4](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.22.3...@standardnotes/analytics@2.22.4) (2023-05-17)
**Note:** Version bump only for package @standardnotes/analytics
## [2.22.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.22.2...@standardnotes/analytics@2.22.3) (2023-05-16)
**Note:** Version bump only for package @standardnotes/analytics
## [2.22.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.22.1...@standardnotes/analytics@2.22.2) (2023-05-15)
**Note:** Version bump only for package @standardnotes/analytics
## [2.22.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.22.0...@standardnotes/analytics@2.22.1) (2023-05-09)
### Bug Fixes

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/analytics",
"version": "2.22.1",
"version": "2.22.5",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -40,8 +40,8 @@
"typescript": "^5.0.4"
},
"dependencies": {
"@aws-sdk/client-sns": "^3.328.0",
"@aws-sdk/client-sqs": "^3.328.0",
"@aws-sdk/client-sns": "^3.332.0",
"@aws-sdk/client-sqs": "^3.332.0",
"@newrelic/winston-enricher": "^4.0.1",
"@standardnotes/common": "workspace:*",
"@standardnotes/domain-core": "workspace:^",

View File

@@ -3,6 +3,63 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.59.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.58.0...@standardnotes/api-gateway@1.59.0) (2023-05-29)
### Features
* add files server as a service to home-server ([#614](https://github.com/standardnotes/api-gateway/issues/614)) ([c7d575a](https://github.com/standardnotes/api-gateway/commit/c7d575a0ffc7eb3e8799c3835da5727584f4f67b))
# [1.58.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.57.0...@standardnotes/api-gateway@1.58.0) (2023-05-25)
### Features
* add revisions service to home server ([#613](https://github.com/standardnotes/api-gateway/issues/613)) ([c70040f](https://github.com/standardnotes/api-gateway/commit/c70040fe5dfd35663b9811fbbaa9370bd0298482))
# [1.57.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.56.2...@standardnotes/api-gateway@1.57.0) (2023-05-25)
### Features
* refactor auth middleware to handle required and optional cross service token scenarios ([#612](https://github.com/standardnotes/api-gateway/issues/612)) ([1e4c7d0](https://github.com/standardnotes/api-gateway/commit/1e4c7d0f317d5c2d98065da12ffeb950b10ee5dc))
## [1.56.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.56.1...@standardnotes/api-gateway@1.56.2) (2023-05-18)
### Bug Fixes
* **api-gateway:** decorating responses for direct call proxy ([4ab32c6](https://github.com/standardnotes/api-gateway/commit/4ab32c670eedcfc64611a191bc25566d43372b23))
* **api-gateway:** pkce endpoints resolution for direct code calls ([c7e605f](https://github.com/standardnotes/api-gateway/commit/c7e605fd6046e8476c493658c6feaed365e82e5d))
## [1.56.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.56.0...@standardnotes/api-gateway@1.56.1) (2023-05-18)
**Note:** Version bump only for package @standardnotes/api-gateway
# [1.56.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.55.0...@standardnotes/api-gateway@1.56.0) (2023-05-17)
### Features
* bundle syncing server into home server setup ([#611](https://github.com/standardnotes/api-gateway/issues/611)) ([b3b617e](https://github.com/standardnotes/api-gateway/commit/b3b617ea0b4f4574f6aa7cfae0e9fa8f868f1f4c))
# [1.55.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.54.0...@standardnotes/api-gateway@1.55.0) (2023-05-17)
### Features
* add direct event handling for home server ([#608](https://github.com/standardnotes/api-gateway/issues/608)) ([8a47d81](https://github.com/standardnotes/api-gateway/commit/8a47d81936acd765224e74fd083810579a83c9a7))
# [1.54.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.53.1...@standardnotes/api-gateway@1.54.0) (2023-05-16)
### Features
* home-server package initial setup with Api Gateway and Auth services ([#605](https://github.com/standardnotes/api-gateway/issues/605)) ([dc71e67](https://github.com/standardnotes/api-gateway/commit/dc71e6777fc4c51234b79f6fb409f9f3111cc6a5))
## [1.53.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.53.0...@standardnotes/api-gateway@1.53.1) (2023-05-15)
**Note:** Version bump only for package @standardnotes/api-gateway
# [1.53.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.52.1...@standardnotes/api-gateway@1.53.0) (2023-05-09)
### Features
* **home-server:** add boilerplate ([#601](https://github.com/standardnotes/api-gateway/issues/601)) ([750cd26](https://github.com/standardnotes/api-gateway/commit/750cd26c369e7d93fa3da29dbe41823059252639))
## [1.52.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.52.0...@standardnotes/api-gateway@1.52.1) (2023-05-09)
### Bug Fixes

View File

@@ -32,7 +32,7 @@ const robots = require('express-robots-txt')
import { InversifyExpressServer } from 'inversify-express-utils'
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
import TYPES from '../src/Bootstrap/Types'
import { TYPES } from '../src/Bootstrap/Types'
import { Env } from '../src/Bootstrap/Env'
const container = new ContainerConfigLoader()

View File

@@ -1,13 +1,17 @@
{
"name": "@standardnotes/api-gateway",
"version": "1.52.1",
"version": "1.59.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
"private": true,
"description": "API Gateway For Standard Notes Services",
"main": "dist/src/index.js",
"typings": "dist/src/index.d.ts",
"types": "dist/src/index.d.ts",
"files": [
"dist/src/**/*.js",
"dist/src/**/*.d.ts"
],
"repository": "git@github.com:standardnotes/api-gateway.git",
"author": "Karol Sójko <karolsojko@standardnotes.com>",
"license": "AGPL-3.0-or-later",

View File

@@ -7,21 +7,26 @@ import { Container } from 'inversify'
import { Timer, TimerInterface } from '@standardnotes/time'
import { Env } from './Env'
import TYPES from './Types'
import { AuthMiddleware } from '../Controller/AuthMiddleware'
import { HttpServiceInterface } from '../Service/Http/HttpServiceInterface'
import { HttpService } from '../Service/Http/HttpService'
import { TYPES } from './Types'
import { ServiceProxyInterface } from '../Service/Http/ServiceProxyInterface'
import { HttpServiceProxy } from '../Service/Http/HttpServiceProxy'
import { SubscriptionTokenAuthMiddleware } from '../Controller/SubscriptionTokenAuthMiddleware'
import { CrossServiceTokenCacheInterface } from '../Service/Cache/CrossServiceTokenCacheInterface'
import { RedisCrossServiceTokenCache } from '../Infra/Redis/RedisCrossServiceTokenCache'
import { WebSocketAuthMiddleware } from '../Controller/WebSocketAuthMiddleware'
import { InMemoryCrossServiceTokenCache } from '../Infra/InMemory/InMemoryCrossServiceTokenCache'
import { DirectCallServiceProxy } from '../Service/Proxy/DirectCallServiceProxy'
import { ServiceContainerInterface } from '@standardnotes/domain-core'
import { EndpointResolverInterface } from '../Service/Resolver/EndpointResolverInterface'
import { EndpointResolver } from '../Service/Resolver/EndpointResolver'
import { RequiredCrossServiceTokenMiddleware } from '../Controller/RequiredCrossServiceTokenMiddleware'
import { OptionalCrossServiceTokenMiddleware } from '../Controller/OptionalCrossServiceTokenMiddleware'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const newrelicFormatter = require('@newrelic/winston-enricher')
export class ContainerConfigLoader {
async load(): Promise<Container> {
async load(serviceContainer?: ServiceContainerInterface): Promise<Container> {
const env: Env = new Env()
env.load()
@@ -39,30 +44,33 @@ export class ContainerConfigLoader {
level: env.get('LOG_LEVEL') || 'info',
format: winston.format.combine(...winstonFormatters),
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL') || 'info' })],
defaultMeta: { service: 'api-gateway' },
})
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(logger)
const redisUrl = env.get('REDIS_URL')
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
let redis
if (isRedisInClusterMode) {
redis = new Redis.Cluster(redisUrl.split(','))
} else {
redis = new Redis(redisUrl)
if (!isConfiguredForHomeServer) {
const redisUrl = env.get('REDIS_URL')
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
let redis
if (isRedisInClusterMode) {
redis = new Redis.Cluster(redisUrl.split(','))
} else {
redis = new Redis(redisUrl)
}
container.bind(TYPES.Redis).toConstantValue(redis)
}
container.bind(TYPES.Redis).toConstantValue(redis)
container.bind<AxiosInstance>(TYPES.HTTPClient).toConstantValue(axios.create())
// env vars
container.bind(TYPES.SYNCING_SERVER_JS_URL).toConstantValue(env.get('SYNCING_SERVER_JS_URL'))
container.bind(TYPES.AUTH_SERVER_URL).toConstantValue(env.get('AUTH_SERVER_URL'))
container.bind(TYPES.SYNCING_SERVER_JS_URL).toConstantValue(env.get('SYNCING_SERVER_JS_URL', true))
container.bind(TYPES.AUTH_SERVER_URL).toConstantValue(env.get('AUTH_SERVER_URL', true))
container.bind(TYPES.REVISIONS_SERVER_URL).toConstantValue(env.get('REVISIONS_SERVER_URL', true))
container.bind(TYPES.EMAIL_SERVER_URL).toConstantValue(env.get('EMAIL_SERVER_URL', true))
container.bind(TYPES.PAYMENTS_SERVER_URL).toConstantValue(env.get('PAYMENTS_SERVER_URL', true))
container.bind(TYPES.FILES_SERVER_URL).toConstantValue(env.get('FILES_SERVER_URL', true))
container.bind(TYPES.AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET'))
container.bind(TYPES.WEB_SOCKET_SERVER_URL).toConstantValue(env.get('WEB_SOCKET_SERVER_URL', true))
container.bind(TYPES.AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET'))
container
.bind(TYPES.HTTP_CALL_TIMEOUT)
.toConstantValue(env.get('HTTP_CALL_TIMEOUT', true) ? +env.get('HTTP_CALL_TIMEOUT', true) : 60_000)
@@ -70,14 +78,28 @@ export class ContainerConfigLoader {
container.bind(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL).toConstantValue(+env.get('CROSS_SERVICE_TOKEN_CACHE_TTL', true))
// Middleware
container.bind<AuthMiddleware>(TYPES.AuthMiddleware).to(AuthMiddleware)
container
.bind<RequiredCrossServiceTokenMiddleware>(TYPES.RequiredCrossServiceTokenMiddleware)
.to(RequiredCrossServiceTokenMiddleware)
container
.bind<OptionalCrossServiceTokenMiddleware>(TYPES.OptionalCrossServiceTokenMiddleware)
.to(OptionalCrossServiceTokenMiddleware)
container.bind<WebSocketAuthMiddleware>(TYPES.WebSocketAuthMiddleware).to(WebSocketAuthMiddleware)
container
.bind<SubscriptionTokenAuthMiddleware>(TYPES.SubscriptionTokenAuthMiddleware)
.to(SubscriptionTokenAuthMiddleware)
// Services
container.bind<HttpServiceInterface>(TYPES.HTTPService).to(HttpService)
if (isConfiguredForHomeServer) {
if (!serviceContainer) {
throw new Error('Service container is required when configured for home server')
}
container
.bind<ServiceProxyInterface>(TYPES.ServiceProxy)
.toConstantValue(new DirectCallServiceProxy(serviceContainer, container.get(TYPES.FILES_SERVER_URL)))
} else {
container.bind<ServiceProxyInterface>(TYPES.ServiceProxy).to(HttpServiceProxy)
}
container.bind<TimerInterface>(TYPES.Timer).toConstantValue(new Timer())
if (isConfiguredForHomeServer) {
@@ -87,6 +109,9 @@ export class ContainerConfigLoader {
} else {
container.bind<CrossServiceTokenCacheInterface>(TYPES.CrossServiceTokenCache).to(RedisCrossServiceTokenCache)
}
container
.bind<EndpointResolverInterface>(TYPES.EndpointResolver)
.toConstantValue(new EndpointResolver(isConfiguredForHomeServer))
return container
}

View File

@@ -0,0 +1,23 @@
import { ServiceContainerInterface, ServiceIdentifier, ServiceInterface } from '@standardnotes/domain-core'
import { ContainerConfigLoader } from './Container'
export class Service implements ServiceInterface {
constructor(private serviceContainer: ServiceContainerInterface) {
this.serviceContainer.register(this.getId(), this)
}
async handleRequest(_request: never, _response: never, _endpointOrMethodIdentifier: string): Promise<unknown> {
throw new Error('Requests are handled via inversify-express at ApiGateway level')
}
async getContainer(): Promise<unknown> {
const config = new ContainerConfigLoader()
return config.load(this.serviceContainer)
}
getId(): ServiceIdentifier {
return ServiceIdentifier.create(ServiceIdentifier.NAMES.Auth).getValue()
}
}

View File

@@ -1,4 +1,4 @@
const TYPES = {
export const TYPES = {
Logger: Symbol.for('Logger'),
Redis: Symbol.for('Redis'),
HTTPClient: Symbol.for('HTTPClient'),
@@ -15,13 +15,15 @@ const TYPES = {
VERSION: Symbol.for('VERSION'),
CROSS_SERVICE_TOKEN_CACHE_TTL: Symbol.for('CROSS_SERVICE_TOKEN_CACHE_TTL'),
// Middleware
AuthMiddleware: Symbol.for('AuthMiddleware'),
RequiredCrossServiceTokenMiddleware: Symbol.for('RequiredCrossServiceTokenMiddleware'),
OptionalCrossServiceTokenMiddleware: Symbol.for('OptionalCrossServiceTokenMiddleware'),
WebSocketAuthMiddleware: Symbol.for('WebSocketAuthMiddleware'),
SubscriptionTokenAuthMiddleware: Symbol.for('SubscriptionTokenAuthMiddleware'),
// Services
HTTPService: Symbol.for('HTTPService'),
ServiceProxy: Symbol.for('ServiceProxy'),
CrossServiceTokenCache: Symbol.for('CrossServiceTokenCache'),
Timer: Symbol.for('Timer'),
EndpointResolver: Symbol.for('EndpointResolver'),
}
export default TYPES
// export default TYPES

View File

@@ -0,0 +1,2 @@
export * from './Service'
export * from './Types'

View File

@@ -2,43 +2,33 @@ import { CrossServiceTokenData } from '@standardnotes/security'
import { RoleName } from '@standardnotes/domain-core'
import { TimerInterface } from '@standardnotes/time'
import { NextFunction, Request, Response } from 'express'
import { inject, injectable } from 'inversify'
import { BaseMiddleware } from 'inversify-express-utils'
import { verify } from 'jsonwebtoken'
import { AxiosError, AxiosInstance } from 'axios'
import { AxiosError } from 'axios'
import { Logger } from 'winston'
import TYPES from '../Bootstrap/Types'
import { CrossServiceTokenCacheInterface } from '../Service/Cache/CrossServiceTokenCacheInterface'
import { ServiceProxyInterface } from '../Service/Http/ServiceProxyInterface'
@injectable()
export class AuthMiddleware extends BaseMiddleware {
export abstract class AuthMiddleware extends BaseMiddleware {
constructor(
@inject(TYPES.HTTPClient) private httpClient: AxiosInstance,
@inject(TYPES.AUTH_SERVER_URL) private authServerUrl: string,
@inject(TYPES.AUTH_JWT_SECRET) private jwtSecret: string,
@inject(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL) private crossServiceTokenCacheTTL: number,
@inject(TYPES.CrossServiceTokenCache) private crossServiceTokenCache: CrossServiceTokenCacheInterface,
@inject(TYPES.Timer) private timer: TimerInterface,
@inject(TYPES.Logger) private logger: Logger,
private serviceProxy: ServiceProxyInterface,
private jwtSecret: string,
private crossServiceTokenCacheTTL: number,
private crossServiceTokenCache: CrossServiceTokenCacheInterface,
private timer: TimerInterface,
private logger: Logger,
) {
super()
}
async handler(request: Request, response: Response, next: NextFunction): Promise<void> {
const authHeaderValue = request.headers.authorization as string
if (!authHeaderValue) {
response.status(401).send({
error: {
tag: 'invalid-auth',
message: 'Invalid login credentials.',
},
})
if (!this.handleMissingAuthHeader(request.headers.authorization, response, next)) {
return
}
const authHeaderValue = request.headers.authorization as string
try {
let crossServiceTokenFetchedFromCache = true
let crossServiceToken = null
@@ -47,26 +37,13 @@ export class AuthMiddleware extends BaseMiddleware {
}
if (crossServiceToken === null) {
const authResponse = await this.httpClient.request({
method: 'POST',
headers: {
Authorization: authHeaderValue,
Accept: 'application/json',
},
validateStatus: (status: number) => {
return status >= 200 && status < 500
},
url: `${this.authServerUrl}/sessions/validate`,
})
if (authResponse.status > 200) {
response.setHeader('content-type', authResponse.headers['content-type'] as string)
response.status(authResponse.status).send(authResponse.data)
const authResponse = await this.serviceProxy.validateSession(authHeaderValue)
if (!this.handleSessionValidationResponse(authResponse, response, next)) {
return
}
crossServiceToken = authResponse.data.authToken
crossServiceToken = (authResponse.data as { authToken: string }).authToken
crossServiceTokenFetchedFromCache = false
}
@@ -87,16 +64,15 @@ export class AuthMiddleware extends BaseMiddleware {
})
}
response.locals.userUuid = decodedToken.user.uuid
response.locals.user = decodedToken.user
response.locals.session = decodedToken.session
response.locals.roles = decodedToken.roles
} catch (error) {
const errorMessage = (error as AxiosError).isAxiosError
? JSON.stringify((error as AxiosError).response?.data)
: (error as Error).message
this.logger.error(
`Could not pass the request to ${this.authServerUrl}/sessions/validate on underlying service: ${errorMessage}`,
)
this.logger.error(`Could not pass the request to sessions/validate on underlying service: ${errorMessage}`)
this.logger.debug('Response error: %O', (error as AxiosError).response ?? error)
@@ -117,6 +93,24 @@ export class AuthMiddleware extends BaseMiddleware {
return next()
}
protected abstract handleSessionValidationResponse(
authResponse: {
status: number
data: unknown
headers: {
contentType: string
}
},
response: Response,
next: NextFunction,
): boolean
protected abstract handleMissingAuthHeader(
authHeaderValue: string | undefined,
response: Response,
next: NextFunction,
): boolean
private getCrossServiceTokenCacheExpireTimestamp(token: CrossServiceTokenData): number {
const crossServiceTokenDefaultCacheExpiration = this.timer.getTimestampInSeconds() + this.crossServiceTokenCacheTTL

View File

@@ -1,15 +1,15 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { controller, all, BaseHttpController, httpPost, httpGet, results, httpDelete } from 'inversify-express-utils'
import TYPES from '../Bootstrap/Types'
import { HttpServiceInterface } from '../Service/Http/HttpServiceInterface'
import { TYPES } from '../Bootstrap/Types'
import { ServiceProxyInterface } from '../Service/Http/ServiceProxyInterface'
@controller('')
export class LegacyController extends BaseHttpController {
private AUTH_ROUTES: Map<string, string>
private PARAMETRIZED_AUTH_ROUTES: Map<string, string>
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
constructor(@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface) {
super()
this.AUTH_ROUTES = new Map([
@@ -29,17 +29,17 @@ export class LegacyController extends BaseHttpController {
])
}
@httpPost('/items/sync', TYPES.AuthMiddleware)
@httpPost('/items/sync', TYPES.RequiredCrossServiceTokenMiddleware)
async legacyItemsSync(request: Request, response: Response): Promise<void> {
await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body)
}
@httpGet('/items/:item_id/revisions', TYPES.AuthMiddleware)
@httpGet('/items/:item_id/revisions', TYPES.RequiredCrossServiceTokenMiddleware)
async legacyGetRevisions(request: Request, response: Response): Promise<void> {
await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body)
}
@httpGet('/items/:item_id/revisions/:id', TYPES.AuthMiddleware)
@httpGet('/items/:item_id/revisions/:id', TYPES.RequiredCrossServiceTokenMiddleware)
async legacyGetRevision(request: Request, response: Response): Promise<void> {
await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body)
}

View File

@@ -0,0 +1,51 @@
import { TimerInterface } from '@standardnotes/time'
import { NextFunction, Response } from 'express'
import { inject, injectable } from 'inversify'
import { Logger } from 'winston'
import { TYPES } from '../Bootstrap/Types'
import { CrossServiceTokenCacheInterface } from '../Service/Cache/CrossServiceTokenCacheInterface'
import { ServiceProxyInterface } from '../Service/Http/ServiceProxyInterface'
import { AuthMiddleware } from './AuthMiddleware'
@injectable()
export class OptionalCrossServiceTokenMiddleware extends AuthMiddleware {
constructor(
@inject(TYPES.ServiceProxy) serviceProxy: ServiceProxyInterface,
@inject(TYPES.AUTH_JWT_SECRET) jwtSecret: string,
@inject(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL) crossServiceTokenCacheTTL: number,
@inject(TYPES.CrossServiceTokenCache) crossServiceTokenCache: CrossServiceTokenCacheInterface,
@inject(TYPES.Timer) timer: TimerInterface,
@inject(TYPES.Logger) logger: Logger,
) {
super(serviceProxy, jwtSecret, crossServiceTokenCacheTTL, crossServiceTokenCache, timer, logger)
}
protected override handleSessionValidationResponse(
authResponse: { status: number; data: unknown; headers: { contentType: string } },
_response: Response,
next: NextFunction,
): boolean {
if (authResponse.status > 200) {
next()
return false
}
return true
}
protected override handleMissingAuthHeader(
authHeaderValue: string | undefined,
_response: Response,
next: NextFunction,
): boolean {
if (!authHeaderValue) {
next()
return false
}
return true
}
}

View File

@@ -0,0 +1,57 @@
import { TimerInterface } from '@standardnotes/time'
import { NextFunction, Response } from 'express'
import { inject, injectable } from 'inversify'
import { Logger } from 'winston'
import { TYPES } from '../Bootstrap/Types'
import { CrossServiceTokenCacheInterface } from '../Service/Cache/CrossServiceTokenCacheInterface'
import { ServiceProxyInterface } from '../Service/Http/ServiceProxyInterface'
import { AuthMiddleware } from './AuthMiddleware'
@injectable()
export class RequiredCrossServiceTokenMiddleware extends AuthMiddleware {
constructor(
@inject(TYPES.ServiceProxy) serviceProxy: ServiceProxyInterface,
@inject(TYPES.AUTH_JWT_SECRET) jwtSecret: string,
@inject(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL) crossServiceTokenCacheTTL: number,
@inject(TYPES.CrossServiceTokenCache) crossServiceTokenCache: CrossServiceTokenCacheInterface,
@inject(TYPES.Timer) timer: TimerInterface,
@inject(TYPES.Logger) logger: Logger,
) {
super(serviceProxy, jwtSecret, crossServiceTokenCacheTTL, crossServiceTokenCache, timer, logger)
}
protected override handleSessionValidationResponse(
authResponse: { status: number; data: unknown; headers: { contentType: string } },
response: Response,
_next: NextFunction,
): boolean {
if (authResponse.status > 200) {
response.setHeader('content-type', authResponse.headers.contentType)
response.status(authResponse.status).send(authResponse.data)
return false
}
return true
}
protected override handleMissingAuthHeader(
authHeaderValue: string | undefined,
response: Response,
_next: NextFunction,
): boolean {
if (!authHeaderValue) {
response.status(401).send({
error: {
tag: 'invalid-auth',
message: 'Invalid login credentials.',
},
})
return false
}
return true
}
}

View File

@@ -5,7 +5,7 @@ import { BaseMiddleware } from 'inversify-express-utils'
import { verify } from 'jsonwebtoken'
import { AxiosError, AxiosInstance, AxiosResponse } from 'axios'
import { Logger } from 'winston'
import TYPES from '../Bootstrap/Types'
import { TYPES } from '../Bootstrap/Types'
import { TokenAuthenticationMethod } from './TokenAuthenticationMethod'
@injectable()
@@ -118,7 +118,7 @@ export class SubscriptionTokenAuthMiddleware extends BaseMiddleware {
verify(authResponse.data.authToken, this.jwtSecret, { algorithms: ['HS256'] })
)
response.locals.userUuid = decodedToken.user.uuid
response.locals.user = decodedToken.user
response.locals.roles = decodedToken.roles
}
}

View File

@@ -7,7 +7,7 @@ import { verify } from 'jsonwebtoken'
import { AxiosError, AxiosInstance } from 'axios'
import { Logger } from 'winston'
import TYPES from '../Bootstrap/Types'
import { TYPES } from '../Bootstrap/Types'
@injectable()
export class WebSocketAuthMiddleware extends BaseMiddleware {
@@ -63,7 +63,7 @@ export class WebSocketAuthMiddleware extends BaseMiddleware {
response.locals.freeUser =
decodedToken.roles.length === 1 &&
decodedToken.roles.find((role) => role.name === RoleName.NAMES.CoreUser) !== undefined
response.locals.userUuid = decodedToken.user.uuid
response.locals.user = decodedToken.user
response.locals.roles = decodedToken.roles
} catch (error) {
const errorMessage = (error as AxiosError).isAxiosError

View File

@@ -0,0 +1,21 @@
export * from './AuthMiddleware'
export * from './HealthCheckController'
export * from './SubscriptionTokenAuthMiddleware'
export * from './TokenAuthenticationMethod'
export * from './WebSocketAuthMiddleware'
export * from './v1/ActionsController'
export * from './v1/AuthenticatorsController'
export * from './v1/FilesController'
export * from './v1/InvoicesController'
export * from './v1/ItemsController'
export * from './v1/OfflineController'
export * from './v1/PaymentsController'
export * from './v1/RevisionsController'
export * from './v1/SessionsController'
export * from './v1/SubscriptionInvitesController'
export * from './v1/TokensController'
export * from './v1/UsersController'
export * from './v1/WebSocketsController'
export * from './v2/ActionsControllerV2'
export * from './v2/PaymentsControllerV2'
export * from './v2/RevisionsControllerV2'

View File

@@ -1,38 +1,52 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpGet, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1')
export class ActionsController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
constructor(
@inject(TYPES.ServiceProxy) private serviceProxy: ServiceProxyInterface,
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
) {
super()
}
@httpPost('/login')
async login(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'auth/sign_in', request.body)
await this.serviceProxy.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'auth/sign_in'),
request.body,
)
}
@httpGet('/login-params')
@httpGet('/login-params', TYPES.OptionalCrossServiceTokenMiddleware)
async loginParams(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'auth/params', request.body)
await this.serviceProxy.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'auth/params'),
request.body,
)
}
@httpPost('/logout')
@httpPost('/logout', TYPES.OptionalCrossServiceTokenMiddleware)
async logout(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'auth/sign_out', request.body)
}
@httpGet('/auth/methods')
async methods(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'auth/methods', request.body)
await this.serviceProxy.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'auth/sign_out'),
request.body,
)
}
@httpGet('/unsubscribe/:token')
async emailUnsubscribe(request: Request, response: Response): Promise<void> {
await this.httpService.callEmailServer(
await this.serviceProxy.callEmailServer(
request,
response,
`subscriptions/actions/unsubscribe/${request.params.token}`,
@@ -40,18 +54,33 @@ export class ActionsController extends BaseHttpController {
)
}
@httpPost('/recovery/codes', TYPES.AuthMiddleware)
@httpPost('/recovery/codes', TYPES.RequiredCrossServiceTokenMiddleware)
async recoveryCodes(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'auth/recovery/codes', request.body)
await this.serviceProxy.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'auth/recovery/codes'),
request.body,
)
}
@httpPost('/recovery/login')
async recoveryLogin(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'auth/recovery/login', request.body)
await this.serviceProxy.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'auth/recovery/login'),
request.body,
)
}
@httpPost('/recovery/login-params')
async recoveryParams(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'auth/recovery/params', request.body)
await this.serviceProxy.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'auth/recovery/params'),
request.body,
)
}
}

View File

@@ -2,36 +2,49 @@ import { inject } from 'inversify'
import { Request, Response } from 'express'
import { controller, BaseHttpController, httpPost, httpGet, httpDelete } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/authenticators')
export class AuthenticatorsController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
constructor(
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
) {
super()
}
@httpDelete('/:authenticatorId', TYPES.AuthMiddleware)
@httpDelete('/:authenticatorId', TYPES.RequiredCrossServiceTokenMiddleware)
async delete(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(
request,
response,
`authenticators/${request.params.authenticatorId}`,
this.endpointResolver.resolveEndpointOrMethodIdentifier(
'DELETE',
'authenticators/:authenticatorId',
request.params.authenticatorId,
),
request.body,
)
}
@httpGet('/', TYPES.AuthMiddleware)
@httpGet('/', TYPES.RequiredCrossServiceTokenMiddleware)
async list(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'authenticators/', request.body)
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'authenticators/'),
request.body,
)
}
@httpGet('/generate-registration-options', TYPES.AuthMiddleware)
@httpGet('/generate-registration-options', TYPES.RequiredCrossServiceTokenMiddleware)
async generateRegistrationOptions(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(
request,
response,
'authenticators/generate-registration-options',
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'authenticators/generate-registration-options'),
request.body,
)
}
@@ -41,13 +54,18 @@ export class AuthenticatorsController extends BaseHttpController {
await this.httpService.callAuthServer(
request,
response,
'authenticators/generate-authentication-options',
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'authenticators/generate-authentication-options'),
request.body,
)
}
@httpPost('/verify-registration', TYPES.AuthMiddleware)
@httpPost('/verify-registration', TYPES.RequiredCrossServiceTokenMiddleware)
async verifyRegistration(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'authenticators/verify-registration', request.body)
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'authenticators/verify-registration'),
request.body,
)
}
}

View File

@@ -2,17 +2,26 @@ import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/files')
export class FilesController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
constructor(
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
) {
super()
}
@httpPost('/valet-tokens', TYPES.AuthMiddleware)
@httpPost('/valet-tokens', TYPES.RequiredCrossServiceTokenMiddleware)
async createToken(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'valet-tokens', request.body)
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'valet-tokens'),
request.body,
)
}
}

View File

@@ -1,12 +1,12 @@
import { Request, Response } from 'express'
import { BaseHttpController, controller, httpPost } from 'inversify-express-utils'
import { inject } from 'inversify'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
@controller('/v1')
export class InvoicesController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
constructor(@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface) {
super()
}

View File

@@ -1,27 +1,46 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpGet, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/items', TYPES.AuthMiddleware)
@controller('/v1/items', TYPES.RequiredCrossServiceTokenMiddleware)
export class ItemsController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
constructor(
@inject(TYPES.ServiceProxy) private serviceProxy: ServiceProxyInterface,
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
) {
super()
}
@httpPost('/')
async sync(request: Request, response: Response): Promise<void> {
await this.httpService.callSyncingServer(request, response, 'items/sync', request.body)
await this.serviceProxy.callSyncingServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'items/sync'),
request.body,
)
}
@httpPost('/check-integrity')
async checkIntegrity(request: Request, response: Response): Promise<void> {
await this.httpService.callSyncingServer(request, response, 'items/check-integrity', request.body)
await this.serviceProxy.callSyncingServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'items/check-integrity'),
request.body,
)
}
@httpGet('/:uuid')
async getItem(request: Request, response: Response): Promise<void> {
await this.httpService.callSyncingServer(request, response, `items/${request.params.uuid}`, request.body)
await this.serviceProxy.callSyncingServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'items/:uuid', request.params.uuid),
request.body,
)
}
}

View File

@@ -2,23 +2,37 @@ import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpGet, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/offline')
export class OfflineController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
constructor(
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
) {
super()
}
@httpGet('/features')
async getOfflineFeatures(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'offline/features', request.body)
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'offline/features'),
request.body,
)
}
@httpPost('/subscription-tokens')
async createOfflineSubscriptionToken(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'offline/subscription-tokens', request.body)
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'offline/subscription-tokens'),
request.body,
)
}
@httpPost('/payments/stripe-setup-intent')

View File

@@ -1,12 +1,12 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { all, BaseHttpController, controller, httpDelete, httpGet, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
@controller('/v1')
export class PaymentsController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
constructor(@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface) {
super()
}

View File

@@ -1,7 +1,7 @@
import { BaseHttpController, controller, httpDelete, httpGet, results } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { TYPES } from '../../Bootstrap/Types'
@controller('/v1/items/:item_id/revisions', TYPES.AuthMiddleware)
@controller('/v1/items/:item_id/revisions', TYPES.RequiredCrossServiceTokenMiddleware)
export class RevisionsController extends BaseHttpController {
@httpGet('/')
async getRevisions(): Promise<results.JsonResult> {

View File

@@ -1,34 +1,56 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpDelete, httpGet, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/sessions')
export class SessionsController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
constructor(
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
) {
super()
}
@httpGet('/', TYPES.AuthMiddleware)
@httpGet('/', TYPES.RequiredCrossServiceTokenMiddleware)
async getSessions(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'sessions')
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'sessions'),
)
}
@httpDelete('/:uuid', TYPES.AuthMiddleware)
@httpDelete('/:uuid', TYPES.RequiredCrossServiceTokenMiddleware)
async deleteSession(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'session', {
uuid: request.params.uuid,
})
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('DELETE', 'session'),
{
uuid: request.params.uuid,
},
)
}
@httpDelete('/', TYPES.AuthMiddleware)
@httpDelete('/', TYPES.RequiredCrossServiceTokenMiddleware)
async deleteSessions(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'session/all')
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('DELETE', 'session/all'),
)
}
@httpPost('/refresh')
async refreshSession(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'session/refresh', request.body)
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'session/refresh'),
request.body,
)
}
}

View File

@@ -2,32 +2,62 @@ import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpDelete, httpGet, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/subscription-invites')
export class SubscriptionInvitesController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
constructor(
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
) {
super()
}
@httpPost('/', TYPES.AuthMiddleware)
@httpPost('/', TYPES.RequiredCrossServiceTokenMiddleware)
async inviteToSubscriptionSharing(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'subscription-invites', request.body)
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'subscription-invites'),
request.body,
)
}
@httpGet('/', TYPES.AuthMiddleware)
@httpGet('/', TYPES.RequiredCrossServiceTokenMiddleware)
async listInvites(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'subscription-invites', request.body)
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'subscription-invites'),
request.body,
)
}
@httpDelete('/:inviteUuid', TYPES.AuthMiddleware)
@httpDelete('/:inviteUuid', TYPES.RequiredCrossServiceTokenMiddleware)
async cancelSubscriptionSharing(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, `subscription-invites/${request.params.inviteUuid}`)
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier(
'DELETE',
'subscription-invites/:inviteUuid',
request.params.inviteUuid,
),
)
}
@httpPost('/:inviteUuid/accept', TYPES.AuthMiddleware)
@httpPost('/:inviteUuid/accept', TYPES.RequiredCrossServiceTokenMiddleware)
async acceptInvite(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, `subscription-invites/${request.params.inviteUuid}/accept`)
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier(
'POST',
'subscription-invites/:inviteUuid/accept',
request.params.inviteUuid,
),
)
}
}

View File

@@ -2,17 +2,26 @@ import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpPost } from 'inversify-express-utils'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/subscription-tokens')
export class TokensController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
constructor(
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
) {
super()
}
@httpPost('/', TYPES.AuthMiddleware)
@httpPost('/', TYPES.RequiredCrossServiceTokenMiddleware)
async createToken(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'subscription-tokens', request.body)
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'subscription-tokens'),
request.body,
)
}
}

View File

@@ -12,14 +12,16 @@ import {
results,
} from 'inversify-express-utils'
import { Logger } from 'winston'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { TokenAuthenticationMethod } from '../TokenAuthenticationMethod'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/users')
export class UsersController extends BaseHttpController {
constructor(
@inject(TYPES.HTTPService) private httpService: HttpServiceInterface,
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
@inject(TYPES.Logger) private logger: Logger,
) {
super()
@@ -35,12 +37,17 @@ export class UsersController extends BaseHttpController {
await this.httpService.callPaymentsServer(request, response, 'api/pro_users/send-activation-code', request.body)
}
@httpPatch('/:userId', TYPES.AuthMiddleware)
@httpPatch('/:userId', TYPES.RequiredCrossServiceTokenMiddleware)
async updateUser(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, `users/${request.params.userId}`, request.body)
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('PATCH', 'users/:userId', request.params.userId),
request.body,
)
}
@httpPut('/:userUuid/password', TYPES.AuthMiddleware)
@httpPut('/:userUuid/password', TYPES.RequiredCrossServiceTokenMiddleware)
async changePassword(request: Request, response: Response): Promise<void> {
this.logger.debug(
'[DEPRECATED] use endpoint /v1/users/:userUuid/attributes/credentials instead of /v1/users/:userUuid/password',
@@ -49,107 +56,198 @@ export class UsersController extends BaseHttpController {
await this.httpService.callAuthServer(
request,
response,
`users/${request.params.userUuid}/attributes/credentials`,
this.endpointResolver.resolveEndpointOrMethodIdentifier(
'PUT',
'users/:userUuid/attributes/credentials',
request.params.userUuid,
),
request.body,
)
}
@httpPut('/:userUuid/attributes/credentials', TYPES.AuthMiddleware)
@httpPut('/:userUuid/attributes/credentials', TYPES.RequiredCrossServiceTokenMiddleware)
async changeCredentials(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(
request,
response,
`users/${request.params.userUuid}/attributes/credentials`,
this.endpointResolver.resolveEndpointOrMethodIdentifier(
'PUT',
'users/:userUuid/attributes/credentials',
request.params.userUuid,
),
request.body,
)
}
@httpGet('/:userId/params', TYPES.AuthMiddleware)
@httpGet('/:userId/params', TYPES.RequiredCrossServiceTokenMiddleware)
async getKeyParams(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'auth/params')
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'auth/params'),
)
}
@all('/:userId/mfa', TYPES.AuthMiddleware)
@all('/:userId/mfa', TYPES.RequiredCrossServiceTokenMiddleware)
async blockMFA(): Promise<results.StatusCodeResult> {
return this.statusCode(401)
}
@httpPost('/:userUuid/integrations/listed', TYPES.AuthMiddleware)
@httpPost('/:userUuid/integrations/listed', TYPES.RequiredCrossServiceTokenMiddleware)
async createListedAccount(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'listed', request.body)
}
@httpPost('/')
async register(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, 'auth', request.body)
}
@httpGet('/:userUuid/settings', TYPES.AuthMiddleware)
async listSettings(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, `users/${request.params.userUuid}/settings`)
}
@httpPut('/:userUuid/settings', TYPES.AuthMiddleware)
async putSetting(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, `users/${request.params.userUuid}/settings`, request.body)
}
@httpGet('/:userUuid/settings/:settingName', TYPES.AuthMiddleware)
async getSetting(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(
request,
response,
`users/${request.params.userUuid}/settings/${request.params.settingName}`,
)
}
@httpDelete('/:userUuid/settings/:settingName', TYPES.AuthMiddleware)
async deleteSetting(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(
request,
response,
`users/${request.params.userUuid}/settings/${request.params.settingName}`,
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'listed'),
request.body,
)
}
@httpGet('/:userUuid/subscription-settings/:subscriptionSettingName', TYPES.AuthMiddleware)
@httpPost('/')
async register(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'auth'),
request.body,
)
}
@httpGet('/:userUuid/settings', TYPES.RequiredCrossServiceTokenMiddleware)
async listSettings(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier(
'GET',
'users/:userUuid/settings',
request.params.userUuid,
),
)
}
@httpPut('/:userUuid/settings', TYPES.RequiredCrossServiceTokenMiddleware)
async putSetting(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier(
'PUT',
'users/:userUuid/settings',
request.params.userUuid,
),
request.body,
)
}
@httpGet('/:userUuid/settings/:settingName', TYPES.RequiredCrossServiceTokenMiddleware)
async getSetting(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier(
'GET',
'users/:userUuid/settings/:settingName',
request.params.userUuid,
request.params.settingName,
),
)
}
@httpDelete('/:userUuid/settings/:settingName', TYPES.RequiredCrossServiceTokenMiddleware)
async deleteSetting(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier(
'DELETE',
'users/:userUuid/settings/:settingName',
request.params.userUuid,
request.params.settingName,
),
request.body,
)
}
@httpGet('/:userUuid/subscription-settings/:subscriptionSettingName', TYPES.RequiredCrossServiceTokenMiddleware)
async getSubscriptionSetting(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(
request,
response,
`users/${request.params.userUuid}/subscription-settings/${request.params.subscriptionSettingName}`,
this.endpointResolver.resolveEndpointOrMethodIdentifier(
'GET',
'users/:userUuid/subscription-settings/:subscriptionSettingName',
request.params.userUuid,
request.params.subscriptionSettingName,
),
)
}
@httpGet('/:userUuid/features', TYPES.AuthMiddleware)
@httpGet('/:userUuid/features', TYPES.RequiredCrossServiceTokenMiddleware)
async getFeatures(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, `users/${request.params.userUuid}/features`)
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier(
'GET',
'users/:userUuid/features',
request.params.userUuid,
),
)
}
@httpGet('/:userUuid/subscription', TYPES.AuthMiddleware)
@httpGet('/:userUuid/subscription', TYPES.RequiredCrossServiceTokenMiddleware)
async getSubscription(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, `users/${request.params.userUuid}/subscription`)
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier(
'GET',
'users/:userUuid/subscription',
request.params.userUuid,
),
)
}
@httpGet('/subscription', TYPES.SubscriptionTokenAuthMiddleware)
async getSubscriptionBySubscriptionToken(request: Request, response: Response): Promise<void> {
if (response.locals.tokenAuthenticationMethod === TokenAuthenticationMethod.OfflineSubscriptionToken) {
await this.httpService.callAuthServer(request, response, 'offline/users/subscription')
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'offline/users/subscription'),
)
return
}
await this.httpService.callAuthServer(request, response, `users/${response.locals.userUuid}/subscription`)
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier(
'GET',
'users/:userUuid/subscription',
response.locals.user.uuid,
),
)
}
@httpDelete('/:userUuid', TYPES.AuthMiddleware)
@httpDelete('/:userUuid', TYPES.RequiredCrossServiceTokenMiddleware)
async deleteUser(request: Request, response: Response): Promise<void> {
await this.httpService.callPaymentsServer(request, response, 'api/account', request.body)
}
@httpPost('/:userUuid/requests', TYPES.AuthMiddleware)
@httpPost('/:userUuid/requests', TYPES.RequiredCrossServiceTokenMiddleware)
async submitRequest(request: Request, response: Response): Promise<void> {
await this.httpService.callAuthServer(request, response, `users/${request.params.userUuid}/requests`, request.body)
await this.httpService.callAuthServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier(
'POST',
'users/:userUuid/requests',
request.params.userUuid,
),
request.body,
)
}
}

Some files were not shown because too many files have changed in this diff Show More