Compare commits

...

13 Commits

Author SHA1 Message Date
standardci
7808cc8ed2 chore(release): publish new version
- @standardnotes/api-gateway@1.86.0
 - @standardnotes/auth-server@1.172.0
 - @standardnotes/grpc@1.2.0
 - @standardnotes/home-server@1.21.4
 - @standardnotes/syncing-server@1.124.0
2023-11-20 10:29:07 +00:00
Karol Sójko
5b84f078c6 feat(grpc): add syncing protocol buffers (#930)
* feat(grpc): add syncing protocol buffers

* wip

* feat: syncing implementation

* fix: sendign metadata

* fix: grpc sync request mapping

* fix grpc response mapper
2023-11-20 11:08:16 +01:00
standardci
cf5f44a4a5 chore(release): publish new version
- @standardnotes/api-gateway@1.85.1
 - @standardnotes/home-server@1.21.3
2023-11-16 12:17:57 +00:00
Karol Sójko
ed05ea553f fix(api-gateway): remove overly verbose debug messages 2023-11-16 12:57:07 +01:00
standardci
4418c38878 chore(release): publish new version
- @standardnotes/api-gateway@1.85.0
 - @standardnotes/auth-server@1.171.0
 - @standardnotes/home-server@1.21.2
2023-11-16 11:31:52 +00:00
Karol Sójko
6391a01b57 feat: add debug logs for grpc communication 2023-11-16 12:10:51 +01:00
standardci
9dbcec198d chore(release): publish new version
- @standardnotes/api-gateway@1.84.1
 - @standardnotes/home-server@1.21.1
2023-11-16 10:25:54 +00:00
Karol Sójko
78fbeb595f fix(api-gateway): bindings 2023-11-16 11:04:44 +01:00
standardci
d894a87e87 chore(release): publish new version
- @standardnotes/api-gateway@1.84.0
 - @standardnotes/auth-server@1.170.0
 - @standardnotes/files-server@1.35.0
 - @standardnotes/grpc@1.1.0
 - @standardnotes/home-server@1.21.0
 - @standardnotes/revisions-server@1.50.0
 - @standardnotes/syncing-server@1.123.0
 - @standardnotes/websockets-server@1.20.0
2023-11-16 09:31:37 +00:00
Karol Sójko
4f62cac213 feat: add grpc sessions validation server (#928)
* feat: add grpc sessions validation server

* feat: add client implementation on api gateway

* fix: response codes

* fix: errored response

* feat: add configuring grpc as optional service proxy

* fix env vars

* fix linter issue
2023-11-16 10:10:42 +01:00
standardci
ce081274da chore(release): publish new version
- @standardnotes/api-gateway@1.83.5
 - @standardnotes/home-server@1.20.5
2023-11-14 14:18:46 +00:00
Karol Sójko
fd997f4849 fix(api-gateway): remove unused imports 2023-11-14 14:57:41 +01:00
Karol Sójko
3ddd671c47 fix(api-gateway): remove the verify body function 2023-11-14 14:55:55 +01:00
105 changed files with 9084 additions and 182 deletions

View File

@@ -17,6 +17,8 @@ jobs:
name: (Self Hosting) E2E Test Suite
strategy:
fail-fast: false
matrix:
service_proxy_type: [http, grpc]
runs-on: ubuntu-latest
services:
@@ -42,6 +44,7 @@ jobs:
env:
DB_TYPE: mysql
CACHE_TYPE: redis
SERVICE_PROXY_TYPE: ${{ matrix.service_proxy_type }}
- name: Wait for server to start
run: docker/is-available.sh http://localhost:3123 $(pwd)/logs

97
.pnp.cjs generated
View File

@@ -45,6 +45,10 @@ const RAW_RUNTIME_STATE =
"name": "@standardnotes/files-server",\
"reference": "workspace:packages/files"\
},\
{\
"name": "@standardnotes/grpc",\
"reference": "workspace:packages/grpc"\
},\
{\
"name": "@standardnotes/home-server",\
"reference": "workspace:packages/home-server"\
@@ -97,6 +101,7 @@ const RAW_RUNTIME_STATE =
["@standardnotes/domain-events", ["workspace:packages/domain-events"]],\
["@standardnotes/domain-events-infra", ["workspace:packages/domain-events-infra"]],\
["@standardnotes/files-server", ["workspace:packages/files"]],\
["@standardnotes/grpc", ["workspace:packages/grpc"]],\
["@standardnotes/home-server", ["workspace:packages/home-server"]],\
["@standardnotes/predicates", ["workspace:packages/predicates"]],\
["@standardnotes/revisions-server", ["workspace:packages/revisions"]],\
@@ -2669,6 +2674,15 @@ const RAW_RUNTIME_STATE =
}]\
]],\
["@grpc/grpc-js", [\
["npm:1.9.11", {\
"packageLocation": "./.yarn/cache/@grpc-grpc-js-npm-1.9.11-5bb7febd65-71b8517b4f.zip/node_modules/@grpc/grpc-js/",\
"packageDependencies": [\
["@grpc/grpc-js", "npm:1.9.11"],\
["@grpc/proto-loader", "npm:0.7.10"],\
["@types/node", "npm:20.2.5"]\
],\
"linkType": "HARD"\
}],\
["npm:1.9.5", {\
"packageLocation": "./.yarn/cache/@grpc-grpc-js-npm-1.9.5-9b0cd6b5ed-5499d964d2.zip/node_modules/@grpc/grpc-js/",\
"packageDependencies": [\
@@ -3570,6 +3584,22 @@ const RAW_RUNTIME_STATE =
["tar", "npm:6.1.15"]\
],\
"linkType": "HARD"\
}],\
["npm:1.0.11", {\
"packageLocation": "./.yarn/cache/@mapbox-node-pre-gyp-npm-1.0.11-5547f15a2b-59529a2444.zip/node_modules/@mapbox/node-pre-gyp/",\
"packageDependencies": [\
["@mapbox/node-pre-gyp", "npm:1.0.11"],\
["detect-libc", "npm:2.0.1"],\
["https-proxy-agent", "npm:5.0.1"],\
["make-dir", "npm:3.1.0"],\
["node-fetch", "virtual:0f92dfe7f9dc4fd492639d4a5b7805c2b27442bf599fd4f370b22a7966ba078f5d4525e2a8e8af29369f20e1833ed084bd52be59679efaa6c1c6c10cdbcd8baa#npm:2.6.11"],\
["nopt", "npm:5.0.0"],\
["npmlog", "npm:5.0.1"],\
["rimraf", "npm:3.0.2"],\
["semver", "npm:7.5.1"],\
["tar", "npm:6.1.15"]\
],\
"linkType": "HARD"\
}]\
]],\
["@nodelib/fs.scandir", [\
@@ -6371,9 +6401,11 @@ const RAW_RUNTIME_STATE =
"packageLocation": "./packages/api-gateway/",\
"packageDependencies": [\
["@standardnotes/api-gateway", "workspace:packages/api-gateway"],\
["@grpc/grpc-js", "npm:1.9.11"],\
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
["@standardnotes/grpc", "workspace:packages/grpc"],\
["@standardnotes/security", "workspace:packages/security"],\
["@standardnotes/time", "workspace:packages/time"],\
["@types/cors", "npm:2.8.13"],\
@@ -6419,6 +6451,7 @@ const RAW_RUNTIME_STATE =
["@aws-sdk/client-sqs", "npm:3.427.0"],\
["@cbor-extract/cbor-extract-linux-arm64", "npm:2.1.1"],\
["@cbor-extract/cbor-extract-linux-x64", "npm:2.1.1"],\
["@grpc/grpc-js", "npm:1.9.11"],\
["@simplewebauthn/server", "npm:8.1.1"],\
["@simplewebauthn/typescript-types", "npm:8.0.0"],\
["@standardnotes/api", "npm:1.26.26"],\
@@ -6427,6 +6460,7 @@ const RAW_RUNTIME_STATE =
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
["@standardnotes/features", "npm:1.59.7"],\
["@standardnotes/grpc", "workspace:packages/grpc"],\
["@standardnotes/predicates", "workspace:packages/predicates"],\
["@standardnotes/responses", "npm:1.13.27"],\
["@standardnotes/security", "workspace:packages/security"],\
@@ -6639,6 +6673,21 @@ const RAW_RUNTIME_STATE =
"linkType": "SOFT"\
}]\
]],\
["@standardnotes/grpc", [\
["workspace:packages/grpc", {\
"packageLocation": "./packages/grpc/",\
"packageDependencies": [\
["@standardnotes/grpc", "workspace:packages/grpc"],\
["@grpc/grpc-js", "npm:1.9.11"],\
["@types/google-protobuf", "npm:3.15.10"],\
["google-protobuf", "npm:3.21.2"],\
["grpc-tools", "npm:1.12.4"],\
["grpc_tools_node_protoc_ts", "npm:5.3.3"],\
["typescript", "patch:typescript@npm%3A5.0.4#optional!builtin<compat/typescript>::version=5.0.4&hash=b5f058"]\
],\
"linkType": "SOFT"\
}]\
]],\
["@standardnotes/home-server", [\
["workspace:packages/home-server", {\
"packageLocation": "./packages/home-server/",\
@@ -6902,11 +6951,13 @@ const RAW_RUNTIME_STATE =
["@aws-sdk/client-s3", "npm:3.427.0"],\
["@aws-sdk/client-sns", "npm:3.427.0"],\
["@aws-sdk/client-sqs", "npm:3.427.0"],\
["@grpc/grpc-js", "npm:1.9.11"],\
["@standardnotes/api", "npm:1.26.26"],\
["@standardnotes/common", "workspace:packages/common"],\
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
["@standardnotes/grpc", "workspace:packages/grpc"],\
["@standardnotes/responses", "npm:1.13.27"],\
["@standardnotes/security", "workspace:packages/security"],\
["@standardnotes/settings", "workspace:packages/settings"],\
@@ -7284,6 +7335,15 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["@types/google-protobuf", [\
["npm:3.15.10", {\
"packageLocation": "./.yarn/cache/@types-google-protobuf-npm-3.15.10-cbaa6c3e6c-29efde966f.zip/node_modules/@types/google-protobuf/",\
"packageDependencies": [\
["@types/google-protobuf", "npm:3.15.10"]\
],\
"linkType": "HARD"\
}]\
]],\
["@types/graceful-fs", [\
["npm:4.1.6", {\
"packageLocation": "./.yarn/cache/@types-graceful-fs-npm-4.1.6-1eadcf742d-c3070ccdc9.zip/node_modules/@types/graceful-fs/",\
@@ -11296,6 +11356,22 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["google-protobuf", [\
["npm:3.15.8", {\
"packageLocation": "./.yarn/cache/google-protobuf-npm-3.15.8-75df975b6c-0b1ea24a55.zip/node_modules/google-protobuf/",\
"packageDependencies": [\
["google-protobuf", "npm:3.15.8"]\
],\
"linkType": "HARD"\
}],\
["npm:3.21.2", {\
"packageLocation": "./.yarn/cache/google-protobuf-npm-3.21.2-7c82de39ab-b376c2e47f.zip/node_modules/google-protobuf/",\
"packageDependencies": [\
["google-protobuf", "npm:3.21.2"]\
],\
"linkType": "HARD"\
}]\
]],\
["graceful-fs", [\
["npm:4.2.11", {\
"packageLocation": "./.yarn/cache/graceful-fs-npm-4.2.11-24bb648a68-bf152d0ed1.zip/node_modules/graceful-fs/",\
@@ -11314,6 +11390,27 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["grpc-tools", [\
["npm:1.12.4", {\
"packageLocation": "./.yarn/unplugged/grpc-tools-npm-1.12.4-956df6794d/node_modules/grpc-tools/",\
"packageDependencies": [\
["grpc-tools", "npm:1.12.4"],\
["@mapbox/node-pre-gyp", "npm:1.0.11"]\
],\
"linkType": "HARD"\
}]\
]],\
["grpc_tools_node_protoc_ts", [\
["npm:5.3.3", {\
"packageLocation": "./.yarn/unplugged/grpc_tools_node_protoc_ts-npm-5.3.3-297a345c26/node_modules/grpc_tools_node_protoc_ts/",\
"packageDependencies": [\
["grpc_tools_node_protoc_ts", "npm:5.3.3"],\
["google-protobuf", "npm:3.15.8"],\
["handlebars", "npm:4.7.7"]\
],\
"linkType": "HARD"\
}]\
]],\
["handlebars", [\
["npm:4.7.7", {\
"packageLocation": "./.yarn/cache/handlebars-npm-4.7.7-a9ccfabf80-617b1e689b.zip/node_modules/handlebars/",\

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -22,6 +22,7 @@ services:
environment:
DB_TYPE: "${DB_TYPE}"
CACHE_TYPE: "${CACHE_TYPE}"
SERVICE_PROXY_TYPE: "${SERVICE_PROXY_TYPE}"
container_name: server-ci
ports:
- 3123:3000

View File

@@ -14,10 +14,18 @@ if [ -z "$SYNCING_SERVER_PORT" ]; then
export SYNCING_SERVER_PORT=3101
fi
if [ -z "$SYNCING_SERVER_GRPC_PORT" ]; then
export SYNCING_SERVER_GRPC_PORT=50052
fi
if [ -z "$AUTH_SERVER_PORT" ]; then
export AUTH_SERVER_PORT=3103
fi
if [ -z "$AUTH_SERVER_GRPC_PORT" ]; then
export AUTH_SERVER_GRPC_PORT=50051
fi
export FILES_SERVER_PORT=3104
if [ -z "$REVISIONS_SERVER_PORT" ]; then
@@ -352,7 +360,9 @@ export API_GATEWAY_NODE_ENV=production
export API_GATEWAY_VERSION=local
export API_GATEWAY_SYNCING_SERVER_JS_URL=http://localhost:$SYNCING_SERVER_PORT
export API_GATEWAY_SYNCING_SERVER_GRPC_URL=0.0.0.0:$SYNCING_SERVER_GRPC_PORT
export API_GATEWAY_AUTH_SERVER_URL=http://localhost:$AUTH_SERVER_PORT
export API_GATEWAY_AUTH_SERVER_GRPC_URL=0.0.0.0:$AUTH_SERVER_GRPC_PORT
export API_GATEWAY_REVISIONS_SERVER_URL=http://localhost:$REVISIONS_SERVER_PORT
if [ -z "$PUBLIC_FILES_SERVER_URL" ]; then
export PUBLIC_FILES_SERVER_URL=http://localhost:3125

View File

@@ -39,5 +39,13 @@
"ts-node": "^10.9.1",
"typescript": "^5.0.4"
},
"packageManager": "yarn@4.0.0-rc.51"
"packageManager": "yarn@4.0.0-rc.51",
"dependenciesMeta": {
"grpc-tools@1.12.4": {
"unplugged": true
},
"grpc_tools_node_protoc_ts@5.3.3": {
"unplugged": true
}
}
}

View File

@@ -6,7 +6,9 @@ VERSION=development
PORT=3000
SYNCING_SERVER_JS_URL=http://syncing_server_js:3000
SYNCING_SERVER_GRPC_URL=http://syncing_server_js:50052
AUTH_SERVER_URL=http://auth:3000
AUTH_SERVER_GRPC_URL=http://auth:50051
WEB_SOCKET_SERVER_URL=http://websockets:3000
PAYMENTS_SERVER_URL=http://payments:3000
FILES_SERVER_URL=http://files:3000

View File

@@ -3,6 +3,43 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.86.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.85.1...@standardnotes/api-gateway@1.86.0) (2023-11-20)
### Features
* **grpc:** add syncing protocol buffers ([#930](https://github.com/standardnotes/api-gateway/issues/930)) ([5b84f07](https://github.com/standardnotes/api-gateway/commit/5b84f078c6ae6330706895f7c57b67ff8c8d18ae))
## [1.85.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.85.0...@standardnotes/api-gateway@1.85.1) (2023-11-16)
### Bug Fixes
* **api-gateway:** remove overly verbose debug messages ([ed05ea5](https://github.com/standardnotes/api-gateway/commit/ed05ea553f605234cd8803e633f3c07429877dbb))
# [1.85.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.84.1...@standardnotes/api-gateway@1.85.0) (2023-11-16)
### Features
* add debug logs for grpc communication ([6391a01](https://github.com/standardnotes/api-gateway/commit/6391a01b5703db23b566710d0520c1197c46144b))
## [1.84.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.84.0...@standardnotes/api-gateway@1.84.1) (2023-11-16)
### Bug Fixes
* **api-gateway:** bindings ([78fbeb5](https://github.com/standardnotes/api-gateway/commit/78fbeb595f9e213688bcb2a031fba2aa3974cc6a))
# [1.84.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.83.5...@standardnotes/api-gateway@1.84.0) (2023-11-16)
### Features
* add grpc sessions validation server ([#928](https://github.com/standardnotes/api-gateway/issues/928)) ([4f62cac](https://github.com/standardnotes/api-gateway/commit/4f62cac213a6b5f503040ef6319e5198967974ce))
## [1.83.5](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.83.4...@standardnotes/api-gateway@1.83.5) (2023-11-14)
### Bug Fixes
* **api-gateway:** remove the verify body function ([3ddd671](https://github.com/standardnotes/api-gateway/commit/3ddd671c4797482a396844e804b4b45b82dbff2d))
* **api-gateway:** remove unused imports ([fd997f4](https://github.com/standardnotes/api-gateway/commit/fd997f4849ed01ef3ae4baf52b5895012fa711d4))
## [1.83.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.83.3...@standardnotes/api-gateway@1.83.4) (2023-11-14)
### Bug Fixes

View File

@@ -36,7 +36,6 @@ import { InversifyExpressServer } from 'inversify-express-utils'
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
import { TYPES } from '../src/Bootstrap/Types'
import { Env } from '../src/Bootstrap/Env'
import { IncomingMessage, ServerResponse } from 'http'
const container = new ContainerConfigLoader()
void container.load().then((container) => {
@@ -73,20 +72,7 @@ void container.load().then((container) => {
}),
)
app.use(
json({
limit: '50mb',
verify: (_req: IncomingMessage, _res: ServerResponse, buf: Buffer, encoding: string): void => {
try {
if (buf && buf.length > 0) {
JSON.parse(buf.toString(encoding as BufferEncoding | 'utf8'))
}
} catch (error) {
logger.error(`Invalid JSON: ${(error as Error).message}. Request body: ${buf.toString()}`)
}
},
}),
)
app.use(json({ limit: '50mb' }))
app.use(
text({
type: ['text/plain', 'application/x-www-form-urlencoded', 'application/x-www-form-urlencoded; charset=utf-8'],
@@ -125,7 +111,7 @@ void container.load().then((container) => {
const serverInstance = server.build().listen(env.get('PORT'))
const keepAliveTimeout = env.get('KEEP_ALIVE_TIMEOUT', true) ? +env.get('KEEP_ALIVE_TIMEOUT', true) : 5000
const keepAliveTimeout = env.get('HTTP_KEEP_ALIVE_TIMEOUT', true) ? +env.get('HTTP_KEEP_ALIVE_TIMEOUT', true) : 5000
serverInstance.keepAliveTimeout = keepAliveTimeout

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api-gateway",
"version": "1.83.4",
"version": "1.86.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -26,9 +26,11 @@
"start": "yarn node dist/bin/server.js"
},
"dependencies": {
"@grpc/grpc-js": "^1.9.11",
"@standardnotes/domain-core": "workspace:^",
"@standardnotes/domain-events": "workspace:*",
"@standardnotes/domain-events-infra": "workspace:*",
"@standardnotes/grpc": "workspace:^",
"@standardnotes/security": "workspace:*",
"@standardnotes/time": "workspace:*",
"agentkeepalive": "^4.5.0",

View File

@@ -1,5 +1,6 @@
import * as winston from 'winston'
import * as AgentKeepAlive from 'agentkeepalive'
import * as grpc from '@grpc/grpc-js'
import axios, { AxiosInstance } from 'axios'
import Redis from 'ioredis'
import { Container } from 'inversify'
@@ -7,20 +8,33 @@ import { Timer, TimerInterface } from '@standardnotes/time'
import { Env } from './Env'
import { TYPES } from './Types'
import { ServiceProxyInterface } from '../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../Service/Proxy/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 { DirectCallServiceProxy } from '../Service/DirectCall/DirectCallServiceProxy'
import { MapperInterface, 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'
import { Transform } from 'stream'
import {
ISessionsClient,
ISyncingClient,
SessionsClient,
SyncRequest,
SyncResponse,
SyncingClient,
} from '@standardnotes/grpc'
import { GRPCServiceProxy } from '../Service/gRPC/GRPCServiceProxy'
import { GRPCSyncingServerServiceProxy } from '../Service/gRPC/GRPCSyncingServerServiceProxy'
import { SyncResponseHttpRepresentation } from '../Mapping/Sync/Http/SyncResponseHttpRepresentation'
import { SyncRequestGRPCMapper } from '../Mapping/Sync/GRPC/SyncRequestGRPCMapper'
import { SyncResponseGRPCMapper } from '../Mapping/Sync/GRPC/SyncResponseGRPCMapper'
export class ContainerConfigLoader {
async load(configuration?: {
@@ -69,14 +83,16 @@ export class ContainerConfigLoader {
container.bind(TYPES.ApiGateway_Redis).toConstantValue(redis)
}
const httpAgentKeepAliveTimeout = env.get('HTTP_AGENT_KEEP_ALIVE_TIMEOUT', true)
? +env.get('HTTP_AGENT_KEEP_ALIVE_TIMEOUT', true)
: 4_000
container.bind<AxiosInstance>(TYPES.ApiGateway_HTTPClient).toConstantValue(
axios.create({
httpAgent: new AgentKeepAlive({
keepAlive: true,
timeout: env.get('AGENT_KEEP_ALIVE_TIMEOUT', true) ? +env.get('AGENT_KEEP_ALIVE_TIMEOUT', true) : 8_000,
freeSocketTimeout: env.get('AGENT_KEEP_ALIVE_FREE_SOCKET_TIMEOUT', true)
? +env.get('AGENT_KEEP_ALIVE_FREE_SOCKET_TIMEOUT', true)
: 4_000,
timeout: 2 * httpAgentKeepAliveTimeout,
freeSocketTimeout: httpAgentKeepAliveTimeout,
}),
}),
)
@@ -114,19 +130,6 @@ export class ContainerConfigLoader {
// Services
container.bind<TimerInterface>(TYPES.ApiGateway_Timer).toConstantValue(new Timer())
if (isConfiguredForHomeServer) {
if (!configuration?.serviceContainer) {
throw new Error('Service container is required when configured for home server')
}
container
.bind<ServiceProxyInterface>(TYPES.ApiGateway_ServiceProxy)
.toConstantValue(
new DirectCallServiceProxy(configuration.serviceContainer, container.get(TYPES.ApiGateway_FILES_SERVER_URL)),
)
} else {
container.bind<ServiceProxyInterface>(TYPES.ApiGateway_ServiceProxy).to(HttpServiceProxy)
}
if (isConfiguredForHomeServer) {
container
.bind<CrossServiceTokenCacheInterface>(TYPES.ApiGateway_CrossServiceTokenCache)
@@ -140,6 +143,87 @@ export class ContainerConfigLoader {
.bind<EndpointResolverInterface>(TYPES.ApiGateway_EndpointResolver)
.toConstantValue(new EndpointResolver(isConfiguredForHomeServer))
if (isConfiguredForHomeServer) {
if (!configuration?.serviceContainer) {
throw new Error('Service container is required when configured for home server')
}
container
.bind<ServiceProxyInterface>(TYPES.ApiGateway_ServiceProxy)
.toConstantValue(
new DirectCallServiceProxy(configuration.serviceContainer, container.get(TYPES.ApiGateway_FILES_SERVER_URL)),
)
} else {
const isConfiguredForGRPCProxy = env.get('SERVICE_PROXY_TYPE', true) === 'grpc'
if (isConfiguredForGRPCProxy) {
container.bind(TYPES.ApiGateway_AUTH_SERVER_GRPC_URL).toConstantValue(env.get('AUTH_SERVER_GRPC_URL'))
container.bind(TYPES.ApiGateway_SYNCING_SERVER_GRPC_URL).toConstantValue(env.get('SYNCING_SERVER_GRPC_URL'))
const grpcAgentKeepAliveTimeout = env.get('GRPC_AGENT_KEEP_ALIVE_TIMEOUT', true)
? +env.get('GRPC_AGENT_KEEP_ALIVE_TIMEOUT', true)
: 8_000
container.bind<ISessionsClient>(TYPES.ApiGateway_GRPCSessionsClient).toConstantValue(
new SessionsClient(
container.get<string>(TYPES.ApiGateway_AUTH_SERVER_GRPC_URL),
grpc.credentials.createInsecure(),
{
'grpc.keepalive_time_ms': grpcAgentKeepAliveTimeout * 2,
'grpc.keepalive_timeout_ms': grpcAgentKeepAliveTimeout,
},
),
)
container.bind<ISyncingClient>(TYPES.ApiGateway_GRPCSyncingClient).toConstantValue(
new SyncingClient(
container.get<string>(TYPES.ApiGateway_SYNCING_SERVER_GRPC_URL),
grpc.credentials.createInsecure(),
{
'grpc.keepalive_time_ms': grpcAgentKeepAliveTimeout * 2,
'grpc.keepalive_timeout_ms': grpcAgentKeepAliveTimeout,
},
),
)
container
.bind<MapperInterface<Record<string, unknown>, SyncRequest>>(TYPES.Mapper_SyncRequestGRPCMapper)
.toConstantValue(new SyncRequestGRPCMapper())
container
.bind<MapperInterface<SyncResponse, SyncResponseHttpRepresentation>>(TYPES.Mapper_SyncResponseGRPCMapper)
.toConstantValue(new SyncResponseGRPCMapper())
container
.bind<GRPCSyncingServerServiceProxy>(TYPES.ApiGateway_GRPCSyncingServerServiceProxy)
.toConstantValue(
new GRPCSyncingServerServiceProxy(
container.get<ISyncingClient>(TYPES.ApiGateway_GRPCSyncingClient),
container.get<MapperInterface<Record<string, unknown>, SyncRequest>>(TYPES.Mapper_SyncRequestGRPCMapper),
container.get<MapperInterface<SyncResponse, SyncResponseHttpRepresentation>>(
TYPES.Mapper_SyncResponseGRPCMapper,
),
),
)
container
.bind<ServiceProxyInterface>(TYPES.ApiGateway_ServiceProxy)
.toConstantValue(
new GRPCServiceProxy(
container.get<AxiosInstance>(TYPES.ApiGateway_HTTPClient),
container.get<string>(TYPES.ApiGateway_AUTH_SERVER_URL),
container.get<string>(TYPES.ApiGateway_SYNCING_SERVER_JS_URL),
container.get<string>(TYPES.ApiGateway_PAYMENTS_SERVER_URL),
container.get<string>(TYPES.ApiGateway_FILES_SERVER_URL),
container.get<string>(TYPES.ApiGateway_WEB_SOCKET_SERVER_URL),
container.get<string>(TYPES.ApiGateway_REVISIONS_SERVER_URL),
container.get<string>(TYPES.ApiGateway_EMAIL_SERVER_URL),
container.get<number>(TYPES.ApiGateway_HTTP_CALL_TIMEOUT),
container.get<CrossServiceTokenCacheInterface>(TYPES.ApiGateway_CrossServiceTokenCache),
container.get<winston.Logger>(TYPES.ApiGateway_Logger),
container.get<TimerInterface>(TYPES.ApiGateway_Timer),
container.get<ISessionsClient>(TYPES.ApiGateway_GRPCSessionsClient),
container.get<GRPCSyncingServerServiceProxy>(TYPES.ApiGateway_GRPCSyncingServerServiceProxy),
),
)
} else {
container.bind<ServiceProxyInterface>(TYPES.ApiGateway_ServiceProxy).to(HttpServiceProxy)
}
}
logger.debug('Configuration complete')
return container

View File

@@ -5,6 +5,8 @@ export const TYPES = {
// env vars
ApiGateway_SYNCING_SERVER_JS_URL: Symbol.for('ApiGateway_SYNCING_SERVER_JS_URL'),
ApiGateway_AUTH_SERVER_URL: Symbol.for('ApiGateway_AUTH_SERVER_URL'),
ApiGateway_AUTH_SERVER_GRPC_URL: Symbol.for('ApiGateway_AUTH_SERVER_GRPC_URL'),
ApiGateway_SYNCING_SERVER_GRPC_URL: Symbol.for('ApiGateway_SYNCING_SERVER_GRPC_URL'),
ApiGateway_PAYMENTS_SERVER_URL: Symbol.for('ApiGateway_PAYMENTS_SERVER_URL'),
ApiGateway_FILES_SERVER_URL: Symbol.for('ApiGateway_FILES_SERVER_URL'),
ApiGateway_REVISIONS_SERVER_URL: Symbol.for('ApiGateway_REVISIONS_SERVER_URL'),
@@ -23,9 +25,15 @@ export const TYPES = {
ApiGateway_OptionalCrossServiceTokenMiddleware: Symbol.for('ApiGateway_OptionalCrossServiceTokenMiddleware'),
ApiGateway_WebSocketAuthMiddleware: Symbol.for('ApiGateway_WebSocketAuthMiddleware'),
ApiGateway_SubscriptionTokenAuthMiddleware: Symbol.for('ApiGateway_SubscriptionTokenAuthMiddleware'),
// Mapping
Mapper_SyncRequestGRPCMapper: Symbol.for('Mapper_SyncRequestGRPCMapper'),
Mapper_SyncResponseGRPCMapper: Symbol.for('Mapper_SyncResponseGRPCMapper'),
// Services
ApiGateway_GRPCSyncingServerServiceProxy: Symbol.for('ApiGateway_GRPCSyncingServerServiceProxy'),
ApiGateway_ServiceProxy: Symbol.for('ApiGateway_ServiceProxy'),
ApiGateway_CrossServiceTokenCache: Symbol.for('ApiGateway_CrossServiceTokenCache'),
ApiGateway_Timer: Symbol.for('ApiGateway_Timer'),
ApiGateway_EndpointResolver: Symbol.for('ApiGateway_EndpointResolver'),
ApiGateway_GRPCSessionsClient: Symbol.for('ApiGateway_GRPCSessionsClient'),
ApiGateway_GRPCSyncingClient: Symbol.for('ApiGateway_GRPCSyncingClient'),
}

View File

@@ -7,7 +7,7 @@ import { AxiosError } from 'axios'
import { Logger } from 'winston'
import { CrossServiceTokenCacheInterface } from '../Service/Cache/CrossServiceTokenCacheInterface'
import { ServiceProxyInterface } from '../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../Service/Proxy/ServiceProxyInterface'
export abstract class AuthMiddleware extends BaseMiddleware {
constructor(
@@ -49,6 +49,8 @@ export abstract class AuthMiddleware extends BaseMiddleware {
return
}
this.logger.debug('[AuthMiddleware] Fetched cross-service token from underlying service')
crossServiceToken = (authResponse.data as { authToken: string }).authToken
crossServiceTokenFetchedFromCache = false
}

View File

@@ -2,7 +2,7 @@ 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 { ServiceProxyInterface } from '../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../Service/Proxy/ServiceProxyInterface'
@controller('')
export class LegacyController extends BaseHttpController {

View File

@@ -5,7 +5,7 @@ import { Logger } from 'winston'
import { TYPES } from '../Bootstrap/Types'
import { CrossServiceTokenCacheInterface } from '../Service/Cache/CrossServiceTokenCacheInterface'
import { ServiceProxyInterface } from '../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../Service/Proxy/ServiceProxyInterface'
import { AuthMiddleware } from './AuthMiddleware'
@injectable()

View File

@@ -5,7 +5,7 @@ import { Logger } from 'winston'
import { TYPES } from '../Bootstrap/Types'
import { CrossServiceTokenCacheInterface } from '../Service/Cache/CrossServiceTokenCacheInterface'
import { ServiceProxyInterface } from '../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../Service/Proxy/ServiceProxyInterface'
import { AuthMiddleware } from './AuthMiddleware'
@injectable()

View File

@@ -2,7 +2,7 @@ import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpGet, httpPost } from 'inversify-express-utils'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1')

View File

@@ -3,7 +3,7 @@ import { Request, Response } from 'express'
import { controller, BaseHttpController, httpPost, httpGet, httpDelete } from 'inversify-express-utils'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/authenticators')

View File

@@ -3,7 +3,7 @@ import { inject } from 'inversify'
import { BaseHttpController, controller, httpPost } from 'inversify-express-utils'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/files')

View File

@@ -2,7 +2,7 @@ import { Request, Response } from 'express'
import { BaseHttpController, controller, httpPost } from 'inversify-express-utils'
import { inject } from 'inversify'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
@controller('/v1')
export class InvoicesController extends BaseHttpController {

View File

@@ -2,7 +2,7 @@ import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpGet, httpPost } from 'inversify-express-utils'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/items', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)

View File

@@ -2,7 +2,7 @@ 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 { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/messages', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)

View File

@@ -3,7 +3,7 @@ import { inject } from 'inversify'
import { BaseHttpController, controller, httpGet, httpPost } from 'inversify-express-utils'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/offline')

View File

@@ -2,7 +2,7 @@ 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 { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
@controller('/v1')
export class PaymentsController extends BaseHttpController {

View File

@@ -2,7 +2,7 @@ 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 { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/sessions')

View File

@@ -2,7 +2,7 @@ import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpDelete, httpGet, httpPatch, httpPost } from 'inversify-express-utils'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/shared-vaults', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)

View File

@@ -2,7 +2,7 @@ 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 { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/shared-vaults/:sharedVaultUuid/users', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)

View File

@@ -2,7 +2,7 @@ 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 { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/shared-vaults', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)

View File

@@ -3,7 +3,7 @@ import { inject } from 'inversify'
import { BaseHttpController, controller, httpDelete, httpGet, httpPost } from 'inversify-express-utils'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/subscription-invites')

View File

@@ -3,7 +3,7 @@ import { inject } from 'inversify'
import { BaseHttpController, controller, httpPost } from 'inversify-express-utils'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/subscription-tokens')

View File

@@ -13,7 +13,7 @@ import {
} from 'inversify-express-utils'
import { Logger } from 'winston'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
import { TokenAuthenticationMethod } from '../TokenAuthenticationMethod'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'

View File

@@ -4,7 +4,7 @@ import { BaseHttpController, controller, httpDelete, httpPost } from 'inversify-
import { Logger } from 'winston'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v1/sockets')

View File

@@ -3,7 +3,7 @@ import { inject } from 'inversify'
import { BaseHttpController, controller, httpPost } from 'inversify-express-utils'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v2')

View File

@@ -2,7 +2,7 @@ import { Request, Response } from 'express'
import { BaseHttpController, controller, httpDelete, httpGet, httpPatch, httpPost } from 'inversify-express-utils'
import { inject } from 'inversify'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
@controller('/v2')
export class PaymentsControllerV2 extends BaseHttpController {

View File

@@ -3,7 +3,7 @@ import { inject } from 'inversify'
import { BaseHttpController, controller, httpDelete, httpGet } from 'inversify-express-utils'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@controller('/v2', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)

View File

@@ -0,0 +1,94 @@
import { MapperInterface, Validator } from '@standardnotes/domain-core'
import { ItemHash, SyncRequest } from '@standardnotes/grpc'
export class SyncRequestGRPCMapper implements MapperInterface<Record<string, unknown>, SyncRequest> {
toDomain(_projection: SyncRequest): Record<string, unknown> {
throw new Error('Method not implemented.')
}
toProjection(domain: Record<string, unknown>): SyncRequest {
const syncRequest = new SyncRequest()
if ('items' in domain) {
syncRequest.setItemsList((domain.items as Record<string, unknown>[]).map((item) => this.createItemHash(item)))
}
if ('shared_vault_uuids' in domain) {
const sharedVaultUuidsValidation = Validator.isNotEmpty(domain.shared_vault_uuids)
if (!sharedVaultUuidsValidation.isFailed()) {
syncRequest.setSharedVaultUuidsList(domain.shared_vault_uuids as string[])
}
}
if ('compute_integrity' in domain) {
syncRequest.setComputeIntegrity(!!domain.compute_integrity)
}
if ('sync_token' in domain) {
syncRequest.setSyncToken(domain.sync_token as string)
}
if ('cursor_token' in domain) {
syncRequest.setCursorToken(domain.cursor_token as string)
}
if ('limit' in domain) {
syncRequest.setLimit(domain.limit as number)
}
if ('content_type' in domain) {
syncRequest.setContentType(domain.content_type as string)
}
if ('api' in domain) {
syncRequest.setApiVersion(domain.api as string)
}
return syncRequest
}
private createItemHash(record: Record<string, unknown>): ItemHash {
const itemHash = new ItemHash()
itemHash.setUuid(record.uuid as string)
if (record.content) {
itemHash.setContent(record.content as string)
}
if (record.content_type) {
itemHash.setContentType(record.content_type as string)
}
if (record.deleted !== undefined) {
itemHash.setDeleted(!!record.deleted)
}
if (record.duplicate_of) {
itemHash.setDuplicateOf(record.duplicate_of as string)
}
if (record.auth_hash) {
itemHash.setAuthHash(record.auth_hash as string)
}
if (record.enc_item_key) {
itemHash.setEncItemKey(record.enc_item_key as string)
}
if (record.items_key_id) {
itemHash.setItemsKeyId(record.items_key_id as string)
}
if (record.key_system_identifier) {
itemHash.setKeySystemIdentifier(record.key_system_identifier as string)
}
if (record.shared_vault_uuid) {
itemHash.setSharedVaultUuid(record.shared_vault_uuid as string)
}
if (record.created_at) {
itemHash.setCreatedAt(record.created_at as string)
}
if (record.created_at_timestamp) {
itemHash.setCreatedAtTimestamp(record.created_at_timestamp as number)
}
if (record.updated_at) {
itemHash.setUpdatedAt(record.updated_at as string)
}
if (record.updated_at_timestamp) {
itemHash.setUpdatedAtTimestamp(record.updated_at_timestamp as number)
}
return itemHash
}
}

View File

@@ -0,0 +1,164 @@
import { MapperInterface } from '@standardnotes/domain-core'
import {
ItemConflictRepresentation,
ItemHashRepresentation,
ItemRepresentation,
MessageRepresentation,
NotificationRepresentation,
SavedItemRepresentation,
SharedVaultInviteRepresentation,
SharedVaultRepresentation,
SyncResponse,
} from '@standardnotes/grpc'
import { ItemHttpRepresentation } from '../Http/ItemHttpRepresentation'
import { SavedItemHttpRepresentation } from '../Http/SavedItemHttpRepresentation'
import { ItemConflictHttpRepresentation } from '../Http/ItemConflictHttpRepresentation'
import { ItemHashHttpRepresentation } from '../Http/ItemHashHttpRepresentation'
import { MessageHttpRepresentation } from '../Http/MessageHttpRepresentation'
import { SharedVaultHttpRepresentation } from '../Http/SharedVaultHttpRepresentation'
import { SharedVaultInviteHttpRepresentation } from '../Http/SharedVaultInviteHttpRepresentation'
import { NotificationHttpRepresentation } from '../Http/NotificationHttpRepresentation'
import { SyncResponseHttpRepresentation } from '../Http/SyncResponseHttpRepresentation'
export class SyncResponseGRPCMapper implements MapperInterface<SyncResponse, SyncResponseHttpRepresentation> {
toDomain(_projection: SyncResponseHttpRepresentation): SyncResponse {
throw new Error('Method not implemented.')
}
toProjection(domain: SyncResponse): SyncResponseHttpRepresentation {
return {
retrieved_items: domain.getRetrievedItemsList().map((item) => this.createItem(item)),
saved_items: domain.getSavedItemsList().map((item) => this.createSavedItem(item)),
conflicts: domain.getConflictsList().map((conflict) => this.createConflict(conflict)),
sync_token: domain.getSyncToken(),
cursor_token: domain.getCursorToken(),
messages: domain.getMessagesList().map((message) => this.createMessage(message)),
shared_vaults: domain.getSharedVaultsList().map((sharedVault) => this.createSharedVault(sharedVault)),
shared_vault_invites: domain
.getSharedVaultInvitesList()
.map((sharedVaultInvite) => this.createSharedVaultInvite(sharedVaultInvite)),
notifications: domain.getNotificationsList().map((notification) => this.createNotification(notification)),
}
}
private createNotification(notification: NotificationRepresentation): NotificationHttpRepresentation {
return {
uuid: notification.getUuid(),
user_uuid: notification.getUserUuid(),
type: notification.getType(),
payload: notification.getPayload(),
created_at_timestamp: notification.getCreatedAtTimestamp(),
updated_at_timestamp: notification.getUpdatedAtTimestamp(),
}
}
private createSharedVaultInvite(
sharedVaultInvite: SharedVaultInviteRepresentation,
): SharedVaultInviteHttpRepresentation {
return {
uuid: sharedVaultInvite.getUuid(),
shared_vault_uuid: sharedVaultInvite.getSharedVaultUuid(),
user_uuid: sharedVaultInvite.getUserUuid(),
sender_uuid: sharedVaultInvite.getSenderUuid(),
encrypted_message: sharedVaultInvite.getEncryptedMessage(),
permission: sharedVaultInvite.getPermission(),
created_at_timestamp: sharedVaultInvite.getCreatedAtTimestamp(),
updated_at_timestamp: sharedVaultInvite.getUpdatedAtTimestamp(),
}
}
private createSharedVault(sharedVault: SharedVaultRepresentation): SharedVaultHttpRepresentation {
return {
uuid: sharedVault.getUuid(),
user_uuid: sharedVault.getUserUuid(),
file_upload_bytes_used: sharedVault.getFileUploadBytesUsed(),
created_at_timestamp: sharedVault.getCreatedAtTimestamp(),
updated_at_timestamp: sharedVault.getUpdatedAtTimestamp(),
}
}
private createMessage(message: MessageRepresentation): MessageHttpRepresentation {
return {
uuid: message.getUuid(),
recipient_uuid: message.getRecipientUuid(),
sender_uuid: message.getSenderUuid(),
encrypted_message: message.getEncryptedMessage(),
replaceability_identifier: message.getReplaceabilityIdentifier() ?? null,
created_at_timestamp: message.getCreatedAtTimestamp(),
updated_at_timestamp: message.getUpdatedAtTimestamp(),
}
}
private createConflict(conflict: ItemConflictRepresentation): ItemConflictHttpRepresentation {
return {
server_item: conflict.hasServerItem()
? this.createItem(conflict.getServerItem() as ItemRepresentation)
: undefined,
unsaved_item: conflict.hasUnsavedItem()
? this.createItemHash(conflict.getUnsavedItem() as ItemHashRepresentation)
: undefined,
type: conflict.getType(),
}
}
private createItemHash(itemHash: ItemHashRepresentation): ItemHashHttpRepresentation {
return {
uuid: itemHash.getUuid(),
user_uuid: itemHash.getUserUuid(),
content: itemHash.hasContent() ? (itemHash.getContent() as string) : undefined,
content_type: itemHash.hasContentType() ? (itemHash.getContentType() as string) : null,
deleted: itemHash.hasDeleted() ? (itemHash.getDeleted() as boolean) : false,
duplicate_of: itemHash.hasDuplicateOf() ? (itemHash.getDuplicateOf() as string) : null,
auth_hash: itemHash.hasAuthHash() ? (itemHash.getAuthHash() as string) : undefined,
enc_item_key: itemHash.hasEncItemKey() ? (itemHash.getEncItemKey() as string) : undefined,
items_key_id: itemHash.hasItemsKeyId() ? (itemHash.getItemsKeyId() as string) : undefined,
key_system_identifier: itemHash.hasKeySystemIdentifier() ? (itemHash.getKeySystemIdentifier() as string) : null,
shared_vault_uuid: itemHash.hasSharedVaultUuid() ? (itemHash.getSharedVaultUuid() as string) : null,
created_at: itemHash.hasCreatedAt() ? (itemHash.getCreatedAt() as string) : undefined,
created_at_timestamp: itemHash.hasCreatedAtTimestamp() ? (itemHash.getCreatedAtTimestamp() as number) : undefined,
updated_at: itemHash.hasUpdatedAt() ? (itemHash.getUpdatedAt() as string) : undefined,
updated_at_timestamp: itemHash.hasUpdatedAtTimestamp() ? (itemHash.getUpdatedAtTimestamp() as number) : undefined,
}
}
private createSavedItem(item: SavedItemRepresentation): SavedItemHttpRepresentation {
return {
uuid: item.getUuid(),
duplicate_of: item.hasDuplicateOf() ? (item.getDuplicateOf() as string) : null,
content_type: item.getContentType(),
auth_hash: item.hasAuthHash() ? (item.getAuthHash() as string) : null,
deleted: item.getDeleted(),
created_at: item.getCreatedAt(),
created_at_timestamp: item.getCreatedAtTimestamp(),
updated_at: item.getUpdatedAt(),
updated_at_timestamp: item.getUpdatedAtTimestamp(),
key_system_identifier: item.hasKeySystemIdentifier() ? (item.getKeySystemIdentifier() as string) : null,
shared_vault_uuid: item.hasSharedVaultUuid() ? (item.getSharedVaultUuid() as string) : null,
user_uuid: item.hasUserUuid() ? (item.getUserUuid() as string) : null,
last_edited_by_uuid: item.hasLastEditedByUuid() ? (item.getLastEditedByUuid() as string) : null,
}
}
private createItem(item: ItemRepresentation): ItemHttpRepresentation {
return {
uuid: item.getUuid(),
items_key_id: item.hasItemsKeyId() ? (item.getItemsKeyId() as string) : null,
duplicate_of: item.hasDuplicateOf() ? (item.getDuplicateOf() as string) : null,
enc_item_key: item.hasEncItemKey() ? (item.getEncItemKey() as string) : null,
content: item.hasContent() ? (item.getContent() as string) : null,
content_type: item.getContentType(),
auth_hash: item.hasAuthHash() ? (item.getAuthHash() as string) : null,
deleted: item.getDeleted(),
created_at: item.getCreatedAt(),
created_at_timestamp: item.getCreatedAtTimestamp(),
updated_at: item.getUpdatedAt(),
updated_at_timestamp: item.getUpdatedAtTimestamp(),
updated_with_session: item.hasUpdatedWithSession() ? (item.getUpdatedWithSession() as string) : null,
key_system_identifier: item.hasKeySystemIdentifier() ? (item.getKeySystemIdentifier() as string) : null,
shared_vault_uuid: item.hasSharedVaultUuid() ? (item.getSharedVaultUuid() as string) : null,
user_uuid: item.hasUserUuid() ? (item.getUserUuid() as string) : null,
last_edited_by_uuid: item.hasLastEditedByUuid() ? (item.getLastEditedByUuid() as string) : null,
}
}
}

View File

@@ -0,0 +1,8 @@
import { ItemHttpRepresentation } from './ItemHttpRepresentation'
import { ItemHashHttpRepresentation } from './ItemHashHttpRepresentation'
export interface ItemConflictHttpRepresentation {
server_item?: ItemHttpRepresentation
unsaved_item?: ItemHashHttpRepresentation
type: string
}

View File

@@ -0,0 +1,17 @@
export interface ItemHashHttpRepresentation {
uuid: string
user_uuid: string
content?: string
content_type: string | null
deleted?: boolean
duplicate_of?: string | null
auth_hash?: string
enc_item_key?: string
items_key_id?: string
key_system_identifier: string | null
shared_vault_uuid: string | null
created_at?: string
created_at_timestamp?: number
updated_at?: string
updated_at_timestamp?: number
}

View File

@@ -0,0 +1,19 @@
export interface ItemHttpRepresentation {
uuid: string
items_key_id: string | null
duplicate_of: string | null
enc_item_key: string | null
content: string | null
content_type: string
auth_hash: string | null
deleted: boolean
created_at: string
created_at_timestamp: number
updated_at: string
updated_at_timestamp: number
updated_with_session: string | null
key_system_identifier: string | null
shared_vault_uuid: string | null
user_uuid: string | null
last_edited_by_uuid: string | null
}

View File

@@ -0,0 +1,9 @@
export interface MessageHttpRepresentation {
uuid: string
recipient_uuid: string
sender_uuid: string
encrypted_message: string
replaceability_identifier: string | null
created_at_timestamp: number
updated_at_timestamp: number
}

View File

@@ -0,0 +1,8 @@
export interface NotificationHttpRepresentation {
uuid: string
user_uuid: string
type: string
payload: string
created_at_timestamp: number
updated_at_timestamp: number
}

View File

@@ -0,0 +1,15 @@
export interface SavedItemHttpRepresentation {
uuid: string
duplicate_of: string | null
content_type: string
auth_hash: string | null
deleted: boolean
created_at: string
created_at_timestamp: number
updated_at: string
updated_at_timestamp: number
key_system_identifier: string | null
shared_vault_uuid: string | null
user_uuid: string | null
last_edited_by_uuid: string | null
}

View File

@@ -0,0 +1,7 @@
export interface SharedVaultHttpRepresentation {
uuid: string
user_uuid: string
file_upload_bytes_used: number
created_at_timestamp: number
updated_at_timestamp: number
}

View File

@@ -0,0 +1,10 @@
export interface SharedVaultInviteHttpRepresentation {
uuid: string
shared_vault_uuid: string
user_uuid: string
sender_uuid: string
encrypted_message: string
permission: string
created_at_timestamp: number
updated_at_timestamp: number
}

View File

@@ -0,0 +1,19 @@
import { ItemConflictHttpRepresentation } from './ItemConflictHttpRepresentation'
import { ItemHttpRepresentation } from './ItemHttpRepresentation'
import { MessageHttpRepresentation } from './MessageHttpRepresentation'
import { NotificationHttpRepresentation } from './NotificationHttpRepresentation'
import { SavedItemHttpRepresentation } from './SavedItemHttpRepresentation'
import { SharedVaultHttpRepresentation } from './SharedVaultHttpRepresentation'
import { SharedVaultInviteHttpRepresentation } from './SharedVaultInviteHttpRepresentation'
export type SyncResponseHttpRepresentation = {
retrieved_items: ItemHttpRepresentation[]
saved_items: SavedItemHttpRepresentation[]
conflicts: ItemConflictHttpRepresentation[]
sync_token: string
cursor_token?: string
messages: MessageHttpRepresentation[]
shared_vaults: SharedVaultHttpRepresentation[]
shared_vault_invites: SharedVaultInviteHttpRepresentation[]
notifications: NotificationHttpRepresentation[]
}

View File

@@ -1,7 +1,7 @@
import { Request, Response } from 'express'
import { ServiceContainerInterface, ServiceIdentifier } from '@standardnotes/domain-core'
import { ServiceProxyInterface } from '../Http/ServiceProxyInterface'
import { ServiceProxyInterface } from '../Proxy/ServiceProxyInterface'
export class DirectCallServiceProxy implements ServiceProxyInterface {
constructor(
@@ -42,7 +42,7 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
}
}
async callEmailServer(_request: Request, response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
async callEmailServer(_request: Request, response: Response, _methodIdentifier: string): Promise<void> {
response.status(400).send({
error: {
message: 'Email server is not available.',
@@ -50,13 +50,13 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
})
}
async callAuthServer(request: never, response: never, endpointOrMethodIdentifier: string): Promise<void> {
async callAuthServer(request: never, response: never, methodIdentifier: string): Promise<void> {
const authService = this.serviceContainer.get(ServiceIdentifier.create(ServiceIdentifier.NAMES.Auth).getValue())
if (!authService) {
throw new Error('Auth service not found')
}
const serviceResponse = (await authService.handleRequest(request, response, endpointOrMethodIdentifier)) as {
const serviceResponse = (await authService.handleRequest(request, response, methodIdentifier)) as {
statusCode: number
json: Record<string, unknown>
}
@@ -67,7 +67,7 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
async callAuthServerWithLegacyFormat(
_request: Request,
response: Response,
_endpointOrMethodIdentifier: string,
_methodIdentifier: string,
): Promise<void> {
response.status(400).send({
error: {
@@ -76,13 +76,13 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
})
}
async callRevisionsServer(request: never, response: never, endpointOrMethodIdentifier: string): Promise<void> {
async callRevisionsServer(request: never, response: never, methodIdentifier: string): Promise<void> {
const service = this.serviceContainer.get(ServiceIdentifier.create(ServiceIdentifier.NAMES.Revisions).getValue())
if (!service) {
throw new Error('Revisions service not found')
}
const serviceResponse = (await service.handleRequest(request, response, endpointOrMethodIdentifier)) as {
const serviceResponse = (await service.handleRequest(request, response, methodIdentifier)) as {
statusCode: number
json: Record<string, unknown>
}
@@ -90,7 +90,7 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
this.sendDecoratedResponse(response, serviceResponse)
}
async callSyncingServer(request: never, response: never, endpointOrMethodIdentifier: string): Promise<void> {
async callSyncingServer(request: never, response: never, methodIdentifier: string): Promise<void> {
const service = this.serviceContainer.get(
ServiceIdentifier.create(ServiceIdentifier.NAMES.SyncingServer).getValue(),
)
@@ -98,7 +98,7 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
throw new Error('Syncing service not found')
}
const serviceResponse = (await service.handleRequest(request, response, endpointOrMethodIdentifier)) as {
const serviceResponse = (await service.handleRequest(request, response, methodIdentifier)) as {
statusCode: number
json: Record<string, unknown>
}
@@ -106,11 +106,7 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
this.sendDecoratedResponse(response, serviceResponse)
}
async callLegacySyncingServer(
_request: Request,
response: Response,
_endpointOrMethodIdentifier: string,
): Promise<void> {
async callLegacySyncingServer(_request: Request, response: Response, _methodIdentifier: string): Promise<void> {
response.status(400).send({
error: {
message: 'Legacy syncing server endpoints are no longer available.',
@@ -118,7 +114,7 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
})
}
async callPaymentsServer(_request: Request, response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
async callPaymentsServer(_request: Request, response: Response, _methodIdentifier: string): Promise<void> {
response.status(400).send({
error: {
message: 'Payments server is not available.',
@@ -126,7 +122,7 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
})
}
async callWebSocketServer(_request: Request, response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
async callWebSocketServer(_request: Request, response: Response, _methodIdentifier: string): Promise<void> {
response.status(400).send({
error: {
message: 'Websockets server is not available.',

View File

@@ -6,7 +6,7 @@ import { Logger } from 'winston'
import { TYPES } from '../../Bootstrap/Types'
import { CrossServiceTokenCacheInterface } from '../Cache/CrossServiceTokenCacheInterface'
import { ServiceProxyInterface } from './ServiceProxyInterface'
import { ServiceProxyInterface } from '../Proxy/ServiceProxyInterface'
import { TimerInterface } from '@standardnotes/time'
@injectable()
@@ -72,16 +72,16 @@ export class HttpServiceProxy implements ServiceProxyInterface {
async callSyncingServer(
request: Request,
response: Response,
endpointOrMethodIdentifier: string,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
await this.callServer(this.syncingServerJsUrl, request, response, endpointOrMethodIdentifier, payload)
await this.callServer(this.syncingServerJsUrl, request, response, endpoint, payload)
}
async callRevisionsServer(
request: Request,
response: Response,
endpointOrMethodIdentifier: string,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
if (!this.revisionsServerUrl) {
@@ -89,37 +89,31 @@ export class HttpServiceProxy implements ServiceProxyInterface {
return
}
await this.callServer(this.revisionsServerUrl, request, response, endpointOrMethodIdentifier, payload)
await this.callServer(this.revisionsServerUrl, request, response, endpoint, payload)
}
async callLegacySyncingServer(
request: Request,
response: Response,
endpointOrMethodIdentifier: string,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
await this.callServerWithLegacyFormat(
this.syncingServerJsUrl,
request,
response,
endpointOrMethodIdentifier,
payload,
)
await this.callServerWithLegacyFormat(this.syncingServerJsUrl, request, response, endpoint, payload)
}
async callAuthServer(
request: Request,
response: Response,
endpointOrMethodIdentifier: string,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
await this.callServer(this.authServerUrl, request, response, endpointOrMethodIdentifier, payload)
await this.callServer(this.authServerUrl, request, response, endpoint, payload)
}
async callEmailServer(
request: Request,
response: Response,
endpointOrMethodIdentifier: string,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
if (!this.emailServerUrl) {
@@ -128,13 +122,13 @@ export class HttpServiceProxy implements ServiceProxyInterface {
return
}
await this.callServer(this.emailServerUrl, request, response, endpointOrMethodIdentifier, payload)
await this.callServer(this.emailServerUrl, request, response, endpoint, payload)
}
async callWebSocketServer(
request: Request,
response: Response,
endpointOrMethodIdentifier: string,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
if (!this.webSocketServerUrl) {
@@ -144,26 +138,17 @@ export class HttpServiceProxy implements ServiceProxyInterface {
}
const isARequestComingFromApiGatewayAndShouldBeKeptInMinimalFormat = request.headers.connectionid !== undefined
this.logger.debug(
`Calling websockets service: ${endpointOrMethodIdentifier}. Format is minimal: ${isARequestComingFromApiGatewayAndShouldBeKeptInMinimalFormat}`,
)
if (isARequestComingFromApiGatewayAndShouldBeKeptInMinimalFormat) {
await this.callServerWithLegacyFormat(
this.webSocketServerUrl,
request,
response,
endpointOrMethodIdentifier,
payload,
)
await this.callServerWithLegacyFormat(this.webSocketServerUrl, request, response, endpoint, payload)
} else {
await this.callServer(this.webSocketServerUrl, request, response, endpointOrMethodIdentifier, payload)
await this.callServer(this.webSocketServerUrl, request, response, endpoint, payload)
}
}
async callPaymentsServer(
request: Request,
response: Response,
endpointOrMethodIdentifier: string,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void | Response<unknown, Record<string, unknown>>> {
if (!this.paymentsServerUrl) {
@@ -172,29 +157,23 @@ export class HttpServiceProxy implements ServiceProxyInterface {
return
}
await this.callServerWithLegacyFormat(
this.paymentsServerUrl,
request,
response,
endpointOrMethodIdentifier,
payload,
)
await this.callServerWithLegacyFormat(this.paymentsServerUrl, request, response, endpoint, payload)
}
async callAuthServerWithLegacyFormat(
request: Request,
response: Response,
endpointOrMethodIdentifier: string,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
await this.callServerWithLegacyFormat(this.authServerUrl, request, response, endpointOrMethodIdentifier, payload)
await this.callServerWithLegacyFormat(this.authServerUrl, request, response, endpoint, payload)
}
private async getServerResponse(
serverUrl: string,
request: Request,
response: Response,
endpointOrMethodIdentifier: string,
endpoint: string,
payload?: Record<string, unknown> | string,
retryAttempt?: number,
): Promise<AxiosResponse | undefined> {
@@ -215,15 +194,10 @@ export class HttpServiceProxy implements ServiceProxyInterface {
headers['X-Auth-Offline-Token'] = response.locals.offlineAuthToken
}
this.logger.debug(`Calling [${request.method}] ${serverUrl}/${endpointOrMethodIdentifier},
headers: ${JSON.stringify(headers)},
query: ${JSON.stringify(request.query)},
payload: ${JSON.stringify(payload)}`)
const serviceResponse = await this.httpClient.request({
method: request.method as Method,
headers,
url: `${serverUrl}/${endpointOrMethodIdentifier}`,
url: `${serverUrl}/${endpoint}`,
data: this.getRequestData(payload),
maxContentLength: Infinity,
maxBodyLength: Infinity,
@@ -240,9 +214,7 @@ export class HttpServiceProxy implements ServiceProxyInterface {
}
if (retryAttempt) {
this.logger.debug(
`Request to ${serverUrl}/${endpointOrMethodIdentifier} succeeded after ${retryAttempt} retries`,
)
this.logger.debug(`Request to ${serverUrl}/${endpoint} succeeded after ${retryAttempt} retries`)
}
return serviceResponse
@@ -254,18 +226,9 @@ export class HttpServiceProxy implements ServiceProxyInterface {
const nextRetryAttempt = retryAttempt ? retryAttempt + 1 : 1
this.logger.debug(
`Retrying request to ${serverUrl}/${endpointOrMethodIdentifier} for the ${nextRetryAttempt} time`,
)
this.logger.debug(`Retrying request to ${serverUrl}/${endpoint} for the ${nextRetryAttempt} time`)
return this.getServerResponse(
serverUrl,
request,
response,
endpointOrMethodIdentifier,
payload,
nextRetryAttempt,
)
return this.getServerResponse(serverUrl, request, response, endpoint, payload, nextRetryAttempt)
}
let detailedErrorMessage = (error as Error).message
@@ -275,8 +238,8 @@ export class HttpServiceProxy implements ServiceProxyInterface {
this.logger.error(
tooManyRetryAttempts
? `Request to ${serverUrl}/${endpointOrMethodIdentifier} timed out after ${retryAttempt} retries`
: `Could not pass the request to ${serverUrl}/${endpointOrMethodIdentifier} on underlying service: ${detailedErrorMessage}`,
? `Request to ${serverUrl}/${endpoint} timed out after ${retryAttempt} retries`
: `Could not pass the request to ${serverUrl}/${endpoint} on underlying service: ${detailedErrorMessage}`,
)
this.logger.debug(`Response error: ${JSON.stringify(error)}`)
@@ -307,19 +270,10 @@ export class HttpServiceProxy implements ServiceProxyInterface {
serverUrl: string,
request: Request,
response: Response,
endpointOrMethodIdentifier: string,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
const serviceResponse = await this.getServerResponse(
serverUrl,
request,
response,
endpointOrMethodIdentifier,
payload,
)
this.logger.debug(`Response from underlying server: ${JSON.stringify(serviceResponse?.data)},
headers: ${JSON.stringify(serviceResponse?.headers)}`)
const serviceResponse = await this.getServerResponse(serverUrl, request, response, endpoint, payload)
if (!serviceResponse) {
return
@@ -351,16 +305,10 @@ export class HttpServiceProxy implements ServiceProxyInterface {
serverUrl: string,
request: Request,
response: Response,
endpointOrMethodIdentifier: string,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void | Response<unknown, Record<string, unknown>>> {
const serviceResponse = await this.getServerResponse(
serverUrl,
request,
response,
endpointOrMethodIdentifier,
payload,
)
const serviceResponse = await this.getServerResponse(serverUrl, request, response, endpoint, payload)
if (!serviceResponse) {
return

View File

@@ -0,0 +1,400 @@
import { AxiosInstance, AxiosError, AxiosResponse, Method } from 'axios'
import { Request, Response } from 'express'
import { Logger } from 'winston'
import { TimerInterface } from '@standardnotes/time'
import { ISessionsClient, AuthorizationHeader, SessionValidationResponse } from '@standardnotes/grpc'
import * as grpc from '@grpc/grpc-js'
import { CrossServiceTokenCacheInterface } from '../Cache/CrossServiceTokenCacheInterface'
import { ServiceProxyInterface } from '../Proxy/ServiceProxyInterface'
import { GRPCSyncingServerServiceProxy } from './GRPCSyncingServerServiceProxy'
export class GRPCServiceProxy implements ServiceProxyInterface {
constructor(
private httpClient: AxiosInstance,
private authServerUrl: string,
private syncingServerJsUrl: string,
private paymentsServerUrl: string,
private filesServerUrl: string,
private webSocketServerUrl: string,
private revisionsServerUrl: string,
private emailServerUrl: string,
private httpCallTimeout: number,
private crossServiceTokenCache: CrossServiceTokenCacheInterface,
private logger: Logger,
private timer: TimerInterface,
private sessionsClient: ISessionsClient,
private gRPCSyncingServerServiceProxy: GRPCSyncingServerServiceProxy,
) {}
async validateSession(headers: {
authorization: string
sharedVaultOwnerContext?: string
}): Promise<{ status: number; data: unknown; headers: { contentType: string } }> {
return new Promise((resolve, reject) => {
try {
const request = new AuthorizationHeader()
request.setBearerToken(headers.authorization)
const metadata = new grpc.Metadata()
metadata.set('x-shared-vault-owner-context', headers.sharedVaultOwnerContext ?? '')
this.logger.debug('[GRPCServiceProxy] Validating session via gRPC')
this.sessionsClient.validate(
request,
metadata,
(error: grpc.ServiceError | null, response: SessionValidationResponse) => {
if (error) {
const responseCode = error.metadata.get('x-auth-error-response-code').pop()
if (responseCode) {
return resolve({
status: +responseCode,
data: {
error: {
message: error.metadata.get('x-auth-error-message').pop(),
tag: error.metadata.get('x-auth-error-tag').pop(),
},
},
headers: {
contentType: 'application/json',
},
})
}
return reject(error)
}
return resolve({
status: 200,
data: {
authToken: response.getCrossServiceToken(),
},
headers: {
contentType: 'application/json',
},
})
},
)
} catch (error) {
return reject(error)
}
})
}
async callSyncingServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
if (endpoint === 'items/sync') {
const result = await this.gRPCSyncingServerServiceProxy.sync(request, response, payload)
response.status(result.status).send(result.data)
return
}
await this.callServer(this.syncingServerJsUrl, request, response, endpoint, payload)
}
async callRevisionsServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
if (!this.revisionsServerUrl) {
response.status(400).send({ message: 'Revisions Server not configured' })
return
}
await this.callServer(this.revisionsServerUrl, request, response, endpoint, payload)
}
async callLegacySyncingServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
await this.callServerWithLegacyFormat(this.syncingServerJsUrl, request, response, endpoint, payload)
}
async callAuthServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
await this.callServer(this.authServerUrl, request, response, endpoint, payload)
}
async callEmailServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
if (!this.emailServerUrl) {
response.status(400).send({ message: 'Email Server not configured' })
return
}
await this.callServer(this.emailServerUrl, request, response, endpoint, payload)
}
async callWebSocketServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
if (!this.webSocketServerUrl) {
this.logger.debug('Websockets Server URL not defined. Skipped request to WebSockets API.')
return
}
const isARequestComingFromApiGatewayAndShouldBeKeptInMinimalFormat = request.headers.connectionid !== undefined
if (isARequestComingFromApiGatewayAndShouldBeKeptInMinimalFormat) {
await this.callServerWithLegacyFormat(this.webSocketServerUrl, request, response, endpoint, payload)
} else {
await this.callServer(this.webSocketServerUrl, request, response, endpoint, payload)
}
}
async callPaymentsServer(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void | Response<unknown, Record<string, unknown>>> {
if (!this.paymentsServerUrl) {
this.logger.debug('Payments Server URL not defined. Skipped request to Payments API.')
return
}
await this.callServerWithLegacyFormat(this.paymentsServerUrl, request, response, endpoint, payload)
}
async callAuthServerWithLegacyFormat(
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
await this.callServerWithLegacyFormat(this.authServerUrl, request, response, endpoint, payload)
}
private async getServerResponse(
serverUrl: string,
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
retryAttempt?: number,
): Promise<AxiosResponse | undefined> {
try {
const headers: Record<string, string> = {}
for (const headerName of Object.keys(request.headers)) {
headers[headerName] = request.headers[headerName] as string
}
delete headers.host
delete headers['content-length']
if (response.locals.authToken) {
headers['X-Auth-Token'] = response.locals.authToken
}
if (response.locals.offlineAuthToken) {
headers['X-Auth-Offline-Token'] = response.locals.offlineAuthToken
}
const serviceResponse = await this.httpClient.request({
method: request.method as Method,
headers,
url: `${serverUrl}/${endpoint}`,
data: this.getRequestData(payload),
maxContentLength: Infinity,
maxBodyLength: Infinity,
params: request.query,
timeout: this.httpCallTimeout,
validateStatus: (status: number) => {
return status >= 200 && status < 500
},
})
if (serviceResponse.headers['x-invalidate-cache']) {
const userUuid = serviceResponse.headers['x-invalidate-cache']
await this.crossServiceTokenCache.invalidate(userUuid)
}
if (retryAttempt) {
this.logger.debug(`Request to ${serverUrl}/${endpoint} succeeded after ${retryAttempt} retries`)
}
return serviceResponse
} catch (error) {
const requestDidNotMakeIt = this.requestTimedOutOrDidNotReachDestination(error as Record<string, unknown>)
const tooManyRetryAttempts = retryAttempt && retryAttempt > 2
if (!tooManyRetryAttempts && requestDidNotMakeIt) {
await this.timer.sleep(50)
const nextRetryAttempt = retryAttempt ? retryAttempt + 1 : 1
this.logger.debug(`Retrying request to ${serverUrl}/${endpoint} for the ${nextRetryAttempt} time`)
return this.getServerResponse(serverUrl, request, response, endpoint, payload, nextRetryAttempt)
}
let detailedErrorMessage = (error as Error).message
if (error instanceof AxiosError) {
detailedErrorMessage = `Status: ${error.status}, code: ${error.code}, message: ${error.message}`
}
this.logger.error(
tooManyRetryAttempts
? `Request to ${serverUrl}/${endpoint} timed out after ${retryAttempt} retries`
: `Could not pass the request to ${serverUrl}/${endpoint} on underlying service: ${detailedErrorMessage}`,
)
this.logger.debug(`Response error: ${JSON.stringify(error)}`)
if ((error as AxiosError).response?.headers['content-type']) {
response.setHeader('content-type', (error as AxiosError).response?.headers['content-type'] as string)
}
const errorCode =
(error as AxiosError).isAxiosError && !isNaN(+((error as AxiosError).code as string))
? +((error as AxiosError).code as string)
: 500
const responseErrorMessage = (error as AxiosError).response?.data
response
.status(errorCode)
.send(
responseErrorMessage ??
"Unfortunately, we couldn't handle your request. Please try again or contact our support if the error persists.",
)
}
return
}
private async callServer(
serverUrl: string,
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void> {
const serviceResponse = await this.getServerResponse(serverUrl, request, response, endpoint, payload)
if (!serviceResponse) {
return
}
this.applyResponseHeaders(serviceResponse, response)
if (this.responseShouldNotBeDecorated(serviceResponse)) {
response.status(serviceResponse.status).send(serviceResponse.data)
return
}
response.status(serviceResponse.status).send({
meta: {
auth: {
userUuid: response.locals.user?.uuid,
roles: response.locals.roles,
},
server: {
filesServerUrl: this.filesServerUrl,
},
},
data: serviceResponse.data,
})
}
private async callServerWithLegacyFormat(
serverUrl: string,
request: Request,
response: Response,
endpoint: string,
payload?: Record<string, unknown> | string,
): Promise<void | Response<unknown, Record<string, unknown>>> {
const serviceResponse = await this.getServerResponse(serverUrl, request, response, endpoint, payload)
if (!serviceResponse) {
return
}
this.applyResponseHeaders(serviceResponse, response)
if (serviceResponse.request._redirectable._redirectCount > 0) {
response.status(302)
response.redirect(serviceResponse.request.res.responseUrl)
} else {
response.status(serviceResponse.status)
response.send(serviceResponse.data)
}
}
private getRequestData(
payload: Record<string, unknown> | string | undefined,
): Record<string, unknown> | string | undefined {
if (
payload === '' ||
payload === null ||
payload === undefined ||
(typeof payload === 'object' && Object.keys(payload).length === 0)
) {
return undefined
}
return payload
}
private responseShouldNotBeDecorated(serviceResponse: AxiosResponse): boolean {
return (
serviceResponse.headers['content-type'] !== undefined &&
serviceResponse.headers['content-type'].toLowerCase().includes('text/html')
)
}
private applyResponseHeaders(serviceResponse: AxiosResponse, response: Response): void {
const returnedHeadersFromUnderlyingService = [
'access-control-allow-methods',
'access-control-allow-origin',
'access-control-expose-headers',
'authorization',
'content-type',
'x-ssjs-version',
'x-auth-version',
]
returnedHeadersFromUnderlyingService.map((headerName) => {
const headerValue = serviceResponse.headers[headerName]
if (headerValue) {
response.setHeader(headerName, headerValue)
}
})
}
private requestTimedOutOrDidNotReachDestination(error: Record<string, unknown>): boolean {
return (
('code' in error && error.code === 'ETIMEDOUT') ||
('response' in error &&
'status' in (error.response as Record<string, unknown>) &&
[503, 504].includes((error.response as Record<string, unknown>).status as number))
)
}
}

View File

@@ -0,0 +1,52 @@
import { Request, Response } from 'express'
import { ISyncingClient, SyncRequest, SyncResponse } from '@standardnotes/grpc'
import { MapperInterface } from '@standardnotes/domain-core'
import { Metadata } from '@grpc/grpc-js'
import { SyncResponseHttpRepresentation } from '../../Mapping/Sync/Http/SyncResponseHttpRepresentation'
export class GRPCSyncingServerServiceProxy {
constructor(
private syncingClient: ISyncingClient,
private syncRequestGRPCMapper: MapperInterface<Record<string, unknown>, SyncRequest>,
private syncResponseGRPCMapper: MapperInterface<SyncResponse, SyncResponseHttpRepresentation>,
) {}
async sync(
request: Request,
response: Response,
payload?: Record<string, unknown> | string,
): Promise<{ status: number; data: unknown }> {
return new Promise((resolve, reject) => {
try {
const syncRequest = this.syncRequestGRPCMapper.toProjection(payload as Record<string, unknown>)
const metadata = new Metadata()
metadata.set('x-user-uuid', response.locals.user.uuid)
metadata.set('x-snjs-version', request.headers['x-snjs-version'] as string)
metadata.set('x-read-only-access', response.locals.readonlyAccess ? 'true' : 'false')
if (response.locals.session) {
metadata.set('x-session-uuid', response.locals.session.uuid)
}
this.syncingClient.syncItems(syncRequest, metadata, (error, syncResponse) => {
if (error) {
const responseCode = error.metadata.get('x-sync-error-response-code').pop()
if (responseCode) {
return resolve({
status: +responseCode,
data: { error: { message: error.metadata.get('x-sync-error-message').pop() } },
})
}
return reject(error)
}
return resolve({ status: 200, data: this.syncResponseGRPCMapper.toProjection(syncResponse) })
})
} catch (error) {
reject(error)
}
})
}
}

View File

@@ -13,6 +13,7 @@ AUTH_JWT_TTL=60000
ENCRYPTION_SERVER_KEY=change-me-!
PORT=3000
GRPC_PORT=50051
DB_HOST=127.0.0.1
DB_REPLICA_HOST=127.0.0.1

View File

@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.172.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.171.0...@standardnotes/auth-server@1.172.0) (2023-11-20)
### Features
* **grpc:** add syncing protocol buffers ([#930](https://github.com/standardnotes/server/issues/930)) ([5b84f07](https://github.com/standardnotes/server/commit/5b84f078c6ae6330706895f7c57b67ff8c8d18ae))
# [1.171.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.170.0...@standardnotes/auth-server@1.171.0) (2023-11-16)
### Features
* add debug logs for grpc communication ([6391a01](https://github.com/standardnotes/server/commit/6391a01b5703db23b566710d0520c1197c46144b))
# [1.170.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.169.2...@standardnotes/auth-server@1.170.0) (2023-11-16)
### Features
* add grpc sessions validation server ([#928](https://github.com/standardnotes/server/issues/928)) ([4f62cac](https://github.com/standardnotes/server/commit/4f62cac213a6b5f503040ef6319e5198967974ce))
## [1.169.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.169.1...@standardnotes/auth-server@1.169.2) (2023-11-13)
**Note:** Version bump only for package @standardnotes/auth-server

View File

@@ -20,6 +20,7 @@ import '../src/Infra/InversifyExpressUtils/AnnotatedHealthCheckController'
import '../src/Infra/InversifyExpressUtils/AnnotatedFeaturesController'
import * as cors from 'cors'
import * as grpc from '@grpc/grpc-js'
import { urlencoded, json, Request, Response, NextFunction } from 'express'
import * as winston from 'winston'
import * as dayjs from 'dayjs'
@@ -29,6 +30,10 @@ import { InversifyExpressServer } from 'inversify-express-utils'
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
import TYPES from '../src/Bootstrap/Types'
import { Env } from '../src/Bootstrap/Env'
import { SessionsServer } from '../src/Infra/gRPC/SessionsServer'
import { SessionsService } from '@standardnotes/grpc'
import { AuthenticateRequest } from '../src/Domain/UseCase/AuthenticateRequest'
import { CreateCrossServiceToken } from '../src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken'
const container = new ContainerConfigLoader()
void container.load().then((container) => {
@@ -66,15 +71,58 @@ void container.load().then((container) => {
const serverInstance = server.build().listen(env.get('PORT'))
const keepAliveTimeout = env.get('KEEP_ALIVE_TIMEOUT', true) ? +env.get('KEEP_ALIVE_TIMEOUT', true) : 5000
const httpKeepAliveTimeout = env.get('HTTP_KEEP_ALIVE_TIMEOUT', true)
? +env.get('HTTP_KEEP_ALIVE_TIMEOUT', true)
: 10_000
serverInstance.keepAliveTimeout = keepAliveTimeout
serverInstance.keepAliveTimeout = httpKeepAliveTimeout
const grpcKeepAliveTimeout = env.get('GRPC_KEEP_ALIVE_TIMEOUT', true)
? +env.get('GRPC_KEEP_ALIVE_TIMEOUT', true)
: 10_000
const grpcServer = new grpc.Server({
'grpc.keepalive_time_ms': grpcKeepAliveTimeout * 2,
'grpc.keepalive_timeout_ms': grpcKeepAliveTimeout,
})
const gRPCPort = env.get('GRPC_PORT', true) ? +env.get('GRPC_PORT', true) : 50051
const sessionsServer = new SessionsServer(
container.get<AuthenticateRequest>(TYPES.Auth_AuthenticateRequest),
container.get<CreateCrossServiceToken>(TYPES.Auth_CreateCrossServiceToken),
container.get<winston.Logger>(TYPES.Auth_Logger),
)
grpcServer.addService(SessionsService, {
validate: sessionsServer.validate.bind(sessionsServer),
})
grpcServer.bindAsync(`0.0.0.0:${gRPCPort}`, grpc.ServerCredentials.createInsecure(), (error, port) => {
if (error) {
logger.error(`Failed to bind gRPC server: ${error.message}`)
return
}
logger.info(`gRPC server bound on port ${port}`)
grpcServer.start()
logger.info('gRPC server started')
})
process.on('SIGTERM', () => {
logger.info('SIGTERM signal received: closing HTTP server')
serverInstance.close(() => {
logger.info('HTTP server closed')
})
grpcServer.tryShutdown((error?: Error) => {
if (error) {
logger.error(`Failed to shutdown gRPC server: ${error.message}`)
} else {
logger.info('gRPC server closed')
}
})
})
logger.info(`Server started on port ${process.env.PORT}`)

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.169.2",
"version": "1.172.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -37,6 +37,7 @@
"@aws-sdk/client-sqs": "^3.427.0",
"@cbor-extract/cbor-extract-linux-arm64": "^2.1.1",
"@cbor-extract/cbor-extract-linux-x64": "^2.1.1",
"@grpc/grpc-js": "^1.9.11",
"@simplewebauthn/server": "^8.1.1",
"@simplewebauthn/typescript-types": "^8.0.0",
"@standardnotes/api": "^1.26.26",
@@ -45,6 +46,7 @@
"@standardnotes/domain-events": "workspace:*",
"@standardnotes/domain-events-infra": "workspace:*",
"@standardnotes/features": "^1.59.7",
"@standardnotes/grpc": "workspace:^",
"@standardnotes/predicates": "workspace:*",
"@standardnotes/responses": "^1.13.27",
"@standardnotes/security": "workspace:*",

View File

@@ -0,0 +1,80 @@
import * as grpc from '@grpc/grpc-js'
import { Status } from '@grpc/grpc-js/build/src/constants'
import { AuthorizationHeader, ISessionsServer, SessionValidationResponse } from '@standardnotes/grpc'
import { AuthenticateRequest } from '../../Domain/UseCase/AuthenticateRequest'
import { User } from '../../Domain/User/User'
import { CreateCrossServiceToken } from '../../Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken'
import { Logger } from 'winston'
export class SessionsServer implements ISessionsServer {
constructor(
private authenticateRequest: AuthenticateRequest,
private createCrossServiceToken: CreateCrossServiceToken,
private logger: Logger,
) {}
async validate(
call: grpc.ServerUnaryCall<AuthorizationHeader, SessionValidationResponse>,
callback: grpc.sendUnaryData<SessionValidationResponse>,
): Promise<void> {
this.logger.debug('[SessionsServer] Validating session via gRPC')
const authenticateRequestResponse = await this.authenticateRequest.execute({
authorizationHeader: call.request.getBearerToken(),
})
if (!authenticateRequestResponse.success) {
const metadata = new grpc.Metadata()
metadata.set('x-auth-error-message', authenticateRequestResponse.errorMessage as string)
metadata.set('x-auth-error-tag', authenticateRequestResponse.errorTag as string)
metadata.set('x-auth-error-response-code', authenticateRequestResponse.responseCode.toString())
return callback(
{
code: Status.PERMISSION_DENIED,
message: authenticateRequestResponse.errorMessage,
name: authenticateRequestResponse.errorTag,
metadata,
},
null,
)
}
const user = authenticateRequestResponse.user as User
const sharedVaultOwnerMetadata = call.metadata.get('x-shared-vault-owner-context')
let sharedVaultOwnerContext = undefined
if (sharedVaultOwnerMetadata.length > 0 && sharedVaultOwnerMetadata[0].length > 0) {
sharedVaultOwnerContext = sharedVaultOwnerMetadata[0].toString()
}
const resultOrError = await this.createCrossServiceToken.execute({
user,
session: authenticateRequestResponse.session,
sharedVaultOwnerContext,
})
if (resultOrError.isFailed()) {
const metadata = new grpc.Metadata()
metadata.set('x-auth-error-message', resultOrError.getError())
metadata.set('x-auth-error-response-code', '400')
return callback(
{
code: Status.INVALID_ARGUMENT,
message: resultOrError.getError(),
name: 'INVALID_ARGUMENT',
metadata,
},
null,
)
}
const response = new SessionValidationResponse()
response.setCrossServiceToken(resultOrError.getValue())
this.logger.debug('[SessionsServer] Session validated via gRPC')
callback(null, response)
}
}

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.35.0](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.34.2...@standardnotes/files-server@1.35.0) (2023-11-16)
### Features
* add grpc sessions validation server ([#928](https://github.com/standardnotes/files/issues/928)) ([4f62cac](https://github.com/standardnotes/files/commit/4f62cac213a6b5f503040ef6319e5198967974ce))
## [1.34.2](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.34.1...@standardnotes/files-server@1.34.2) (2023-11-13)
**Note:** Version bump only for package @standardnotes/files-server

View File

@@ -91,7 +91,7 @@ void container.load().then((container) => {
const serverInstance = server.build().listen(env.get('PORT'))
const keepAliveTimeout = env.get('KEEP_ALIVE_TIMEOUT', true) ? +env.get('KEEP_ALIVE_TIMEOUT', true) : 5000
const keepAliveTimeout = env.get('HTTP_KEEP_ALIVE_TIMEOUT', true) ? +env.get('HTTP_KEEP_ALIVE_TIMEOUT', true) : 5000
serverInstance.keepAliveTimeout = keepAliveTimeout

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files-server",
"version": "1.34.2",
"version": "1.35.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -0,0 +1,16 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.2.0](https://github.com/standardnotes/server/compare/@standardnotes/grpc@1.1.0...@standardnotes/grpc@1.2.0) (2023-11-20)
### Features
* **grpc:** add syncing protocol buffers ([#930](https://github.com/standardnotes/server/issues/930)) ([5b84f07](https://github.com/standardnotes/server/commit/5b84f078c6ae6330706895f7c57b67ff8c8d18ae))
# 1.1.0 (2023-11-16)
### Features
* add grpc sessions validation server ([#928](https://github.com/standardnotes/server/issues/928)) ([4f62cac](https://github.com/standardnotes/server/commit/4f62cac213a6b5f503040ef6319e5198967974ce))

41
packages/grpc/lib/auth_grpc_pb.d.ts vendored Normal file
View File

@@ -0,0 +1,41 @@
// package: auth
// file: auth.proto
/* tslint:disable */
/* eslint-disable */
import * as grpc from "@grpc/grpc-js";
import * as auth_pb from "./auth_pb";
interface ISessionsService extends grpc.ServiceDefinition<grpc.UntypedServiceImplementation> {
validate: ISessionsService_Ivalidate;
}
interface ISessionsService_Ivalidate extends grpc.MethodDefinition<auth_pb.AuthorizationHeader, auth_pb.SessionValidationResponse> {
path: "/auth.Sessions/validate";
requestStream: false;
responseStream: false;
requestSerialize: grpc.serialize<auth_pb.AuthorizationHeader>;
requestDeserialize: grpc.deserialize<auth_pb.AuthorizationHeader>;
responseSerialize: grpc.serialize<auth_pb.SessionValidationResponse>;
responseDeserialize: grpc.deserialize<auth_pb.SessionValidationResponse>;
}
export const SessionsService: ISessionsService;
export interface ISessionsServer {
validate: grpc.handleUnaryCall<auth_pb.AuthorizationHeader, auth_pb.SessionValidationResponse>;
}
export interface ISessionsClient {
validate(request: auth_pb.AuthorizationHeader, callback: (error: grpc.ServiceError | null, response: auth_pb.SessionValidationResponse) => void): grpc.ClientUnaryCall;
validate(request: auth_pb.AuthorizationHeader, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: auth_pb.SessionValidationResponse) => void): grpc.ClientUnaryCall;
validate(request: auth_pb.AuthorizationHeader, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: auth_pb.SessionValidationResponse) => void): grpc.ClientUnaryCall;
}
export class SessionsClient extends grpc.Client implements ISessionsClient {
constructor(address: string, credentials: grpc.ChannelCredentials, options?: object);
public validate(request: auth_pb.AuthorizationHeader, callback: (error: grpc.ServiceError | null, response: auth_pb.SessionValidationResponse) => void): grpc.ClientUnaryCall;
public validate(request: auth_pb.AuthorizationHeader, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: auth_pb.SessionValidationResponse) => void): grpc.ClientUnaryCall;
public validate(request: auth_pb.AuthorizationHeader, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: auth_pb.SessionValidationResponse) => void): grpc.ClientUnaryCall;
}

View File

@@ -0,0 +1,44 @@
// GENERATED CODE -- DO NOT EDIT!
'use strict';
var grpc = require('@grpc/grpc-js');
var auth_pb = require('./auth_pb.js');
function serialize_auth_AuthorizationHeader(arg) {
if (!(arg instanceof auth_pb.AuthorizationHeader)) {
throw new Error('Expected argument of type auth.AuthorizationHeader');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_auth_AuthorizationHeader(buffer_arg) {
return auth_pb.AuthorizationHeader.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_auth_SessionValidationResponse(arg) {
if (!(arg instanceof auth_pb.SessionValidationResponse)) {
throw new Error('Expected argument of type auth.SessionValidationResponse');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_auth_SessionValidationResponse(buffer_arg) {
return auth_pb.SessionValidationResponse.deserializeBinary(new Uint8Array(buffer_arg));
}
var SessionsService = exports.SessionsService = {
validate: {
path: '/auth.Sessions/validate',
requestStream: false,
responseStream: false,
requestType: auth_pb.AuthorizationHeader,
responseType: auth_pb.SessionValidationResponse,
requestSerialize: serialize_auth_AuthorizationHeader,
requestDeserialize: deserialize_auth_AuthorizationHeader,
responseSerialize: serialize_auth_SessionValidationResponse,
responseDeserialize: deserialize_auth_SessionValidationResponse,
},
};
exports.SessionsClient = grpc.makeGenericClientConstructor(SessionsService);

47
packages/grpc/lib/auth_pb.d.ts vendored Normal file
View File

@@ -0,0 +1,47 @@
// package: auth
// file: auth.proto
/* tslint:disable */
/* eslint-disable */
import * as jspb from "google-protobuf";
export class AuthorizationHeader extends jspb.Message {
getBearerToken(): string;
setBearerToken(value: string): AuthorizationHeader;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): AuthorizationHeader.AsObject;
static toObject(includeInstance: boolean, msg: AuthorizationHeader): AuthorizationHeader.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: AuthorizationHeader, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): AuthorizationHeader;
static deserializeBinaryFromReader(message: AuthorizationHeader, reader: jspb.BinaryReader): AuthorizationHeader;
}
export namespace AuthorizationHeader {
export type AsObject = {
bearerToken: string,
}
}
export class SessionValidationResponse extends jspb.Message {
getCrossServiceToken(): string;
setCrossServiceToken(value: string): SessionValidationResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): SessionValidationResponse.AsObject;
static toObject(includeInstance: boolean, msg: SessionValidationResponse): SessionValidationResponse.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: SessionValidationResponse, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): SessionValidationResponse;
static deserializeBinaryFromReader(message: SessionValidationResponse, reader: jspb.BinaryReader): SessionValidationResponse;
}
export namespace SessionValidationResponse {
export type AsObject = {
crossServiceToken: string,
}
}

View File

@@ -0,0 +1,328 @@
// source: auth.proto
/**
* @fileoverview
* @enhanceable
* @suppress {missingRequire} reports error on implicit type usages.
* @suppress {messageConventions} JS Compiler reports an error if a variable or
* field starts with 'MSG_' and isn't a translatable message.
* @public
*/
// GENERATED CODE -- DO NOT EDIT!
/* eslint-disable */
// @ts-nocheck
var jspb = require('google-protobuf');
var goog = jspb;
var global = (function() {
if (this) { return this; }
if (typeof window !== 'undefined') { return window; }
if (typeof global !== 'undefined') { return global; }
if (typeof self !== 'undefined') { return self; }
return Function('return this')();
}.call(null));
goog.exportSymbol('proto.auth.AuthorizationHeader', null, global);
goog.exportSymbol('proto.auth.SessionValidationResponse', null, global);
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* @extends {jspb.Message}
* @constructor
*/
proto.auth.AuthorizationHeader = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.auth.AuthorizationHeader, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.auth.AuthorizationHeader.displayName = 'proto.auth.AuthorizationHeader';
}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* @extends {jspb.Message}
* @constructor
*/
proto.auth.SessionValidationResponse = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.auth.SessionValidationResponse, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.auth.SessionValidationResponse.displayName = 'proto.auth.SessionValidationResponse';
}
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.
* Field names that are reserved in JavaScript and will be renamed to pb_name.
* Optional fields that are not set will be set to undefined.
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
* For the list of reserved names please see:
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
* JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @return {!Object}
*/
proto.auth.AuthorizationHeader.prototype.toObject = function(opt_includeInstance) {
return proto.auth.AuthorizationHeader.toObject(opt_includeInstance, this);
};
/**
* Static version of the {@see toObject} method.
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
* the JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @param {!proto.auth.AuthorizationHeader} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.auth.AuthorizationHeader.toObject = function(includeInstance, msg) {
var f, obj = {
bearerToken: jspb.Message.getFieldWithDefault(msg, 1, "")
};
if (includeInstance) {
obj.$jspbMessageInstance = msg;
}
return obj;
};
}
/**
* Deserializes binary data (in protobuf wire format).
* @param {jspb.ByteSource} bytes The bytes to deserialize.
* @return {!proto.auth.AuthorizationHeader}
*/
proto.auth.AuthorizationHeader.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.auth.AuthorizationHeader;
return proto.auth.AuthorizationHeader.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object.
* @param {!proto.auth.AuthorizationHeader} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.auth.AuthorizationHeader}
*/
proto.auth.AuthorizationHeader.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
}
var field = reader.getFieldNumber();
switch (field) {
case 1:
var value = /** @type {string} */ (reader.readString());
msg.setBearerToken(value);
break;
default:
reader.skipField();
break;
}
}
return msg;
};
/**
* Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array}
*/
proto.auth.AuthorizationHeader.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.auth.AuthorizationHeader.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer();
};
/**
* Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter.
* @param {!proto.auth.AuthorizationHeader} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.auth.AuthorizationHeader.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getBearerToken();
if (f.length > 0) {
writer.writeString(
1,
f
);
}
};
/**
* optional string bearer_token = 1;
* @return {string}
*/
proto.auth.AuthorizationHeader.prototype.getBearerToken = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
};
/**
* @param {string} value
* @return {!proto.auth.AuthorizationHeader} returns this
*/
proto.auth.AuthorizationHeader.prototype.setBearerToken = function(value) {
return jspb.Message.setProto3StringField(this, 1, value);
};
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.
* Field names that are reserved in JavaScript and will be renamed to pb_name.
* Optional fields that are not set will be set to undefined.
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
* For the list of reserved names please see:
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
* JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @return {!Object}
*/
proto.auth.SessionValidationResponse.prototype.toObject = function(opt_includeInstance) {
return proto.auth.SessionValidationResponse.toObject(opt_includeInstance, this);
};
/**
* Static version of the {@see toObject} method.
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
* the JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @param {!proto.auth.SessionValidationResponse} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.auth.SessionValidationResponse.toObject = function(includeInstance, msg) {
var f, obj = {
crossServiceToken: jspb.Message.getFieldWithDefault(msg, 1, "")
};
if (includeInstance) {
obj.$jspbMessageInstance = msg;
}
return obj;
};
}
/**
* Deserializes binary data (in protobuf wire format).
* @param {jspb.ByteSource} bytes The bytes to deserialize.
* @return {!proto.auth.SessionValidationResponse}
*/
proto.auth.SessionValidationResponse.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.auth.SessionValidationResponse;
return proto.auth.SessionValidationResponse.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object.
* @param {!proto.auth.SessionValidationResponse} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.auth.SessionValidationResponse}
*/
proto.auth.SessionValidationResponse.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
}
var field = reader.getFieldNumber();
switch (field) {
case 1:
var value = /** @type {string} */ (reader.readString());
msg.setCrossServiceToken(value);
break;
default:
reader.skipField();
break;
}
}
return msg;
};
/**
* Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array}
*/
proto.auth.SessionValidationResponse.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.auth.SessionValidationResponse.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer();
};
/**
* Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter.
* @param {!proto.auth.SessionValidationResponse} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.auth.SessionValidationResponse.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getCrossServiceToken();
if (f.length > 0) {
writer.writeString(
1,
f
);
}
};
/**
* optional string cross_service_token = 1;
* @return {string}
*/
proto.auth.SessionValidationResponse.prototype.getCrossServiceToken = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
};
/**
* @param {string} value
* @return {!proto.auth.SessionValidationResponse} returns this
*/
proto.auth.SessionValidationResponse.prototype.setCrossServiceToken = function(value) {
return jspb.Message.setProto3StringField(this, 1, value);
};
goog.object.extend(exports, proto.auth);

4
packages/grpc/lib/index.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
export * from './auth_grpc_pb'
export * from './auth_pb'
export * from './sync_grpc_pb'
export * from './sync_pb'

View File

@@ -0,0 +1,20 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./auth_grpc_pb"), exports);
__exportStar(require("./auth_pb"), exports);
__exportStar(require("./sync_grpc_pb"), exports);
__exportStar(require("./sync_pb"), exports);

41
packages/grpc/lib/sync_grpc_pb.d.ts vendored Normal file
View File

@@ -0,0 +1,41 @@
// package: sync
// file: sync.proto
/* tslint:disable */
/* eslint-disable */
import * as grpc from "@grpc/grpc-js";
import * as sync_pb from "./sync_pb";
interface ISyncingService extends grpc.ServiceDefinition<grpc.UntypedServiceImplementation> {
syncItems: ISyncingService_IsyncItems;
}
interface ISyncingService_IsyncItems extends grpc.MethodDefinition<sync_pb.SyncRequest, sync_pb.SyncResponse> {
path: "/sync.Syncing/syncItems";
requestStream: false;
responseStream: false;
requestSerialize: grpc.serialize<sync_pb.SyncRequest>;
requestDeserialize: grpc.deserialize<sync_pb.SyncRequest>;
responseSerialize: grpc.serialize<sync_pb.SyncResponse>;
responseDeserialize: grpc.deserialize<sync_pb.SyncResponse>;
}
export const SyncingService: ISyncingService;
export interface ISyncingServer {
syncItems: grpc.handleUnaryCall<sync_pb.SyncRequest, sync_pb.SyncResponse>;
}
export interface ISyncingClient {
syncItems(request: sync_pb.SyncRequest, callback: (error: grpc.ServiceError | null, response: sync_pb.SyncResponse) => void): grpc.ClientUnaryCall;
syncItems(request: sync_pb.SyncRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: sync_pb.SyncResponse) => void): grpc.ClientUnaryCall;
syncItems(request: sync_pb.SyncRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: sync_pb.SyncResponse) => void): grpc.ClientUnaryCall;
}
export class SyncingClient extends grpc.Client implements ISyncingClient {
constructor(address: string, credentials: grpc.ChannelCredentials, options?: object);
public syncItems(request: sync_pb.SyncRequest, callback: (error: grpc.ServiceError | null, response: sync_pb.SyncResponse) => void): grpc.ClientUnaryCall;
public syncItems(request: sync_pb.SyncRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: sync_pb.SyncResponse) => void): grpc.ClientUnaryCall;
public syncItems(request: sync_pb.SyncRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: sync_pb.SyncResponse) => void): grpc.ClientUnaryCall;
}

View File

@@ -0,0 +1,44 @@
// GENERATED CODE -- DO NOT EDIT!
'use strict';
var grpc = require('@grpc/grpc-js');
var sync_pb = require('./sync_pb.js');
function serialize_sync_SyncRequest(arg) {
if (!(arg instanceof sync_pb.SyncRequest)) {
throw new Error('Expected argument of type sync.SyncRequest');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_sync_SyncRequest(buffer_arg) {
return sync_pb.SyncRequest.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_sync_SyncResponse(arg) {
if (!(arg instanceof sync_pb.SyncResponse)) {
throw new Error('Expected argument of type sync.SyncResponse');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_sync_SyncResponse(buffer_arg) {
return sync_pb.SyncResponse.deserializeBinary(new Uint8Array(buffer_arg));
}
var SyncingService = exports.SyncingService = {
syncItems: {
path: '/sync.Syncing/syncItems',
requestStream: false,
responseStream: false,
requestType: sync_pb.SyncRequest,
responseType: sync_pb.SyncResponse,
requestSerialize: serialize_sync_SyncRequest,
requestDeserialize: deserialize_sync_SyncRequest,
responseSerialize: serialize_sync_SyncResponse,
responseDeserialize: deserialize_sync_SyncResponse,
},
};
exports.SyncingClient = grpc.makeGenericClientConstructor(SyncingService);

683
packages/grpc/lib/sync_pb.d.ts vendored Normal file
View File

@@ -0,0 +1,683 @@
// package: sync
// file: sync.proto
/* tslint:disable */
/* eslint-disable */
import * as jspb from "google-protobuf";
export class ItemHashRepresentation extends jspb.Message {
getUuid(): string;
setUuid(value: string): ItemHashRepresentation;
getUserUuid(): string;
setUserUuid(value: string): ItemHashRepresentation;
hasContent(): boolean;
clearContent(): void;
getContent(): string | undefined;
setContent(value: string): ItemHashRepresentation;
hasContentType(): boolean;
clearContentType(): void;
getContentType(): string | undefined;
setContentType(value: string): ItemHashRepresentation;
hasDeleted(): boolean;
clearDeleted(): void;
getDeleted(): boolean | undefined;
setDeleted(value: boolean): ItemHashRepresentation;
hasDuplicateOf(): boolean;
clearDuplicateOf(): void;
getDuplicateOf(): string | undefined;
setDuplicateOf(value: string): ItemHashRepresentation;
hasAuthHash(): boolean;
clearAuthHash(): void;
getAuthHash(): string | undefined;
setAuthHash(value: string): ItemHashRepresentation;
hasEncItemKey(): boolean;
clearEncItemKey(): void;
getEncItemKey(): string | undefined;
setEncItemKey(value: string): ItemHashRepresentation;
hasItemsKeyId(): boolean;
clearItemsKeyId(): void;
getItemsKeyId(): string | undefined;
setItemsKeyId(value: string): ItemHashRepresentation;
hasKeySystemIdentifier(): boolean;
clearKeySystemIdentifier(): void;
getKeySystemIdentifier(): string | undefined;
setKeySystemIdentifier(value: string): ItemHashRepresentation;
hasSharedVaultUuid(): boolean;
clearSharedVaultUuid(): void;
getSharedVaultUuid(): string | undefined;
setSharedVaultUuid(value: string): ItemHashRepresentation;
hasCreatedAt(): boolean;
clearCreatedAt(): void;
getCreatedAt(): string | undefined;
setCreatedAt(value: string): ItemHashRepresentation;
hasCreatedAtTimestamp(): boolean;
clearCreatedAtTimestamp(): void;
getCreatedAtTimestamp(): number | undefined;
setCreatedAtTimestamp(value: number): ItemHashRepresentation;
hasUpdatedAt(): boolean;
clearUpdatedAt(): void;
getUpdatedAt(): string | undefined;
setUpdatedAt(value: string): ItemHashRepresentation;
hasUpdatedAtTimestamp(): boolean;
clearUpdatedAtTimestamp(): void;
getUpdatedAtTimestamp(): number | undefined;
setUpdatedAtTimestamp(value: number): ItemHashRepresentation;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ItemHashRepresentation.AsObject;
static toObject(includeInstance: boolean, msg: ItemHashRepresentation): ItemHashRepresentation.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: ItemHashRepresentation, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): ItemHashRepresentation;
static deserializeBinaryFromReader(message: ItemHashRepresentation, reader: jspb.BinaryReader): ItemHashRepresentation;
}
export namespace ItemHashRepresentation {
export type AsObject = {
uuid: string,
userUuid: string,
content?: string,
contentType?: string,
deleted?: boolean,
duplicateOf?: string,
authHash?: string,
encItemKey?: string,
itemsKeyId?: string,
keySystemIdentifier?: string,
sharedVaultUuid?: string,
createdAt?: string,
createdAtTimestamp?: number,
updatedAt?: string,
updatedAtTimestamp?: number,
}
}
export class ItemConflictRepresentation extends jspb.Message {
hasServerItem(): boolean;
clearServerItem(): void;
getServerItem(): ItemRepresentation | undefined;
setServerItem(value?: ItemRepresentation): ItemConflictRepresentation;
hasUnsavedItem(): boolean;
clearUnsavedItem(): void;
getUnsavedItem(): ItemHashRepresentation | undefined;
setUnsavedItem(value?: ItemHashRepresentation): ItemConflictRepresentation;
getType(): string;
setType(value: string): ItemConflictRepresentation;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ItemConflictRepresentation.AsObject;
static toObject(includeInstance: boolean, msg: ItemConflictRepresentation): ItemConflictRepresentation.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: ItemConflictRepresentation, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): ItemConflictRepresentation;
static deserializeBinaryFromReader(message: ItemConflictRepresentation, reader: jspb.BinaryReader): ItemConflictRepresentation;
}
export namespace ItemConflictRepresentation {
export type AsObject = {
serverItem?: ItemRepresentation.AsObject,
unsavedItem?: ItemHashRepresentation.AsObject,
type: string,
}
}
export class ItemRepresentation extends jspb.Message {
getUuid(): string;
setUuid(value: string): ItemRepresentation;
hasItemsKeyId(): boolean;
clearItemsKeyId(): void;
getItemsKeyId(): string | undefined;
setItemsKeyId(value: string): ItemRepresentation;
hasDuplicateOf(): boolean;
clearDuplicateOf(): void;
getDuplicateOf(): string | undefined;
setDuplicateOf(value: string): ItemRepresentation;
hasEncItemKey(): boolean;
clearEncItemKey(): void;
getEncItemKey(): string | undefined;
setEncItemKey(value: string): ItemRepresentation;
hasContent(): boolean;
clearContent(): void;
getContent(): string | undefined;
setContent(value: string): ItemRepresentation;
getContentType(): string;
setContentType(value: string): ItemRepresentation;
hasAuthHash(): boolean;
clearAuthHash(): void;
getAuthHash(): string | undefined;
setAuthHash(value: string): ItemRepresentation;
getDeleted(): boolean;
setDeleted(value: boolean): ItemRepresentation;
getCreatedAt(): string;
setCreatedAt(value: string): ItemRepresentation;
getCreatedAtTimestamp(): number;
setCreatedAtTimestamp(value: number): ItemRepresentation;
getUpdatedAt(): string;
setUpdatedAt(value: string): ItemRepresentation;
getUpdatedAtTimestamp(): number;
setUpdatedAtTimestamp(value: number): ItemRepresentation;
hasUpdatedWithSession(): boolean;
clearUpdatedWithSession(): void;
getUpdatedWithSession(): string | undefined;
setUpdatedWithSession(value: string): ItemRepresentation;
hasKeySystemIdentifier(): boolean;
clearKeySystemIdentifier(): void;
getKeySystemIdentifier(): string | undefined;
setKeySystemIdentifier(value: string): ItemRepresentation;
hasSharedVaultUuid(): boolean;
clearSharedVaultUuid(): void;
getSharedVaultUuid(): string | undefined;
setSharedVaultUuid(value: string): ItemRepresentation;
hasUserUuid(): boolean;
clearUserUuid(): void;
getUserUuid(): string | undefined;
setUserUuid(value: string): ItemRepresentation;
hasLastEditedByUuid(): boolean;
clearLastEditedByUuid(): void;
getLastEditedByUuid(): string | undefined;
setLastEditedByUuid(value: string): ItemRepresentation;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ItemRepresentation.AsObject;
static toObject(includeInstance: boolean, msg: ItemRepresentation): ItemRepresentation.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: ItemRepresentation, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): ItemRepresentation;
static deserializeBinaryFromReader(message: ItemRepresentation, reader: jspb.BinaryReader): ItemRepresentation;
}
export namespace ItemRepresentation {
export type AsObject = {
uuid: string,
itemsKeyId?: string,
duplicateOf?: string,
encItemKey?: string,
content?: string,
contentType: string,
authHash?: string,
deleted: boolean,
createdAt: string,
createdAtTimestamp: number,
updatedAt: string,
updatedAtTimestamp: number,
updatedWithSession?: string,
keySystemIdentifier?: string,
sharedVaultUuid?: string,
userUuid?: string,
lastEditedByUuid?: string,
}
}
export class SavedItemRepresentation extends jspb.Message {
getUuid(): string;
setUuid(value: string): SavedItemRepresentation;
hasDuplicateOf(): boolean;
clearDuplicateOf(): void;
getDuplicateOf(): string | undefined;
setDuplicateOf(value: string): SavedItemRepresentation;
getContentType(): string;
setContentType(value: string): SavedItemRepresentation;
hasAuthHash(): boolean;
clearAuthHash(): void;
getAuthHash(): string | undefined;
setAuthHash(value: string): SavedItemRepresentation;
getDeleted(): boolean;
setDeleted(value: boolean): SavedItemRepresentation;
getCreatedAt(): string;
setCreatedAt(value: string): SavedItemRepresentation;
getCreatedAtTimestamp(): number;
setCreatedAtTimestamp(value: number): SavedItemRepresentation;
getUpdatedAt(): string;
setUpdatedAt(value: string): SavedItemRepresentation;
getUpdatedAtTimestamp(): number;
setUpdatedAtTimestamp(value: number): SavedItemRepresentation;
hasKeySystemIdentifier(): boolean;
clearKeySystemIdentifier(): void;
getKeySystemIdentifier(): string | undefined;
setKeySystemIdentifier(value: string): SavedItemRepresentation;
hasSharedVaultUuid(): boolean;
clearSharedVaultUuid(): void;
getSharedVaultUuid(): string | undefined;
setSharedVaultUuid(value: string): SavedItemRepresentation;
hasUserUuid(): boolean;
clearUserUuid(): void;
getUserUuid(): string | undefined;
setUserUuid(value: string): SavedItemRepresentation;
hasLastEditedByUuid(): boolean;
clearLastEditedByUuid(): void;
getLastEditedByUuid(): string | undefined;
setLastEditedByUuid(value: string): SavedItemRepresentation;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): SavedItemRepresentation.AsObject;
static toObject(includeInstance: boolean, msg: SavedItemRepresentation): SavedItemRepresentation.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: SavedItemRepresentation, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): SavedItemRepresentation;
static deserializeBinaryFromReader(message: SavedItemRepresentation, reader: jspb.BinaryReader): SavedItemRepresentation;
}
export namespace SavedItemRepresentation {
export type AsObject = {
uuid: string,
duplicateOf?: string,
contentType: string,
authHash?: string,
deleted: boolean,
createdAt: string,
createdAtTimestamp: number,
updatedAt: string,
updatedAtTimestamp: number,
keySystemIdentifier?: string,
sharedVaultUuid?: string,
userUuid?: string,
lastEditedByUuid?: string,
}
}
export class MessageRepresentation extends jspb.Message {
getUuid(): string;
setUuid(value: string): MessageRepresentation;
getRecipientUuid(): string;
setRecipientUuid(value: string): MessageRepresentation;
getSenderUuid(): string;
setSenderUuid(value: string): MessageRepresentation;
getEncryptedMessage(): string;
setEncryptedMessage(value: string): MessageRepresentation;
hasReplaceabilityIdentifier(): boolean;
clearReplaceabilityIdentifier(): void;
getReplaceabilityIdentifier(): string | undefined;
setReplaceabilityIdentifier(value: string): MessageRepresentation;
getCreatedAtTimestamp(): number;
setCreatedAtTimestamp(value: number): MessageRepresentation;
getUpdatedAtTimestamp(): number;
setUpdatedAtTimestamp(value: number): MessageRepresentation;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): MessageRepresentation.AsObject;
static toObject(includeInstance: boolean, msg: MessageRepresentation): MessageRepresentation.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: MessageRepresentation, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): MessageRepresentation;
static deserializeBinaryFromReader(message: MessageRepresentation, reader: jspb.BinaryReader): MessageRepresentation;
}
export namespace MessageRepresentation {
export type AsObject = {
uuid: string,
recipientUuid: string,
senderUuid: string,
encryptedMessage: string,
replaceabilityIdentifier?: string,
createdAtTimestamp: number,
updatedAtTimestamp: number,
}
}
export class SharedVaultRepresentation extends jspb.Message {
getUuid(): string;
setUuid(value: string): SharedVaultRepresentation;
getUserUuid(): string;
setUserUuid(value: string): SharedVaultRepresentation;
getFileUploadBytesUsed(): number;
setFileUploadBytesUsed(value: number): SharedVaultRepresentation;
getCreatedAtTimestamp(): number;
setCreatedAtTimestamp(value: number): SharedVaultRepresentation;
getUpdatedAtTimestamp(): number;
setUpdatedAtTimestamp(value: number): SharedVaultRepresentation;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): SharedVaultRepresentation.AsObject;
static toObject(includeInstance: boolean, msg: SharedVaultRepresentation): SharedVaultRepresentation.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: SharedVaultRepresentation, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): SharedVaultRepresentation;
static deserializeBinaryFromReader(message: SharedVaultRepresentation, reader: jspb.BinaryReader): SharedVaultRepresentation;
}
export namespace SharedVaultRepresentation {
export type AsObject = {
uuid: string,
userUuid: string,
fileUploadBytesUsed: number,
createdAtTimestamp: number,
updatedAtTimestamp: number,
}
}
export class SharedVaultInviteRepresentation extends jspb.Message {
getUuid(): string;
setUuid(value: string): SharedVaultInviteRepresentation;
getSharedVaultUuid(): string;
setSharedVaultUuid(value: string): SharedVaultInviteRepresentation;
getUserUuid(): string;
setUserUuid(value: string): SharedVaultInviteRepresentation;
getSenderUuid(): string;
setSenderUuid(value: string): SharedVaultInviteRepresentation;
getEncryptedMessage(): string;
setEncryptedMessage(value: string): SharedVaultInviteRepresentation;
getPermission(): string;
setPermission(value: string): SharedVaultInviteRepresentation;
getCreatedAtTimestamp(): number;
setCreatedAtTimestamp(value: number): SharedVaultInviteRepresentation;
getUpdatedAtTimestamp(): number;
setUpdatedAtTimestamp(value: number): SharedVaultInviteRepresentation;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): SharedVaultInviteRepresentation.AsObject;
static toObject(includeInstance: boolean, msg: SharedVaultInviteRepresentation): SharedVaultInviteRepresentation.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: SharedVaultInviteRepresentation, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): SharedVaultInviteRepresentation;
static deserializeBinaryFromReader(message: SharedVaultInviteRepresentation, reader: jspb.BinaryReader): SharedVaultInviteRepresentation;
}
export namespace SharedVaultInviteRepresentation {
export type AsObject = {
uuid: string,
sharedVaultUuid: string,
userUuid: string,
senderUuid: string,
encryptedMessage: string,
permission: string,
createdAtTimestamp: number,
updatedAtTimestamp: number,
}
}
export class NotificationRepresentation extends jspb.Message {
getUuid(): string;
setUuid(value: string): NotificationRepresentation;
getUserUuid(): string;
setUserUuid(value: string): NotificationRepresentation;
getType(): string;
setType(value: string): NotificationRepresentation;
getPayload(): string;
setPayload(value: string): NotificationRepresentation;
getCreatedAtTimestamp(): number;
setCreatedAtTimestamp(value: number): NotificationRepresentation;
getUpdatedAtTimestamp(): number;
setUpdatedAtTimestamp(value: number): NotificationRepresentation;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): NotificationRepresentation.AsObject;
static toObject(includeInstance: boolean, msg: NotificationRepresentation): NotificationRepresentation.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: NotificationRepresentation, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): NotificationRepresentation;
static deserializeBinaryFromReader(message: NotificationRepresentation, reader: jspb.BinaryReader): NotificationRepresentation;
}
export namespace NotificationRepresentation {
export type AsObject = {
uuid: string,
userUuid: string,
type: string,
payload: string,
createdAtTimestamp: number,
updatedAtTimestamp: number,
}
}
export class ItemHash extends jspb.Message {
getUuid(): string;
setUuid(value: string): ItemHash;
hasContent(): boolean;
clearContent(): void;
getContent(): string | undefined;
setContent(value: string): ItemHash;
hasContentType(): boolean;
clearContentType(): void;
getContentType(): string | undefined;
setContentType(value: string): ItemHash;
hasDeleted(): boolean;
clearDeleted(): void;
getDeleted(): boolean | undefined;
setDeleted(value: boolean): ItemHash;
hasDuplicateOf(): boolean;
clearDuplicateOf(): void;
getDuplicateOf(): string | undefined;
setDuplicateOf(value: string): ItemHash;
hasAuthHash(): boolean;
clearAuthHash(): void;
getAuthHash(): string | undefined;
setAuthHash(value: string): ItemHash;
hasEncItemKey(): boolean;
clearEncItemKey(): void;
getEncItemKey(): string | undefined;
setEncItemKey(value: string): ItemHash;
hasItemsKeyId(): boolean;
clearItemsKeyId(): void;
getItemsKeyId(): string | undefined;
setItemsKeyId(value: string): ItemHash;
hasKeySystemIdentifier(): boolean;
clearKeySystemIdentifier(): void;
getKeySystemIdentifier(): string | undefined;
setKeySystemIdentifier(value: string): ItemHash;
hasSharedVaultUuid(): boolean;
clearSharedVaultUuid(): void;
getSharedVaultUuid(): string | undefined;
setSharedVaultUuid(value: string): ItemHash;
hasCreatedAt(): boolean;
clearCreatedAt(): void;
getCreatedAt(): string | undefined;
setCreatedAt(value: string): ItemHash;
hasCreatedAtTimestamp(): boolean;
clearCreatedAtTimestamp(): void;
getCreatedAtTimestamp(): number | undefined;
setCreatedAtTimestamp(value: number): ItemHash;
hasUpdatedAt(): boolean;
clearUpdatedAt(): void;
getUpdatedAt(): string | undefined;
setUpdatedAt(value: string): ItemHash;
hasUpdatedAtTimestamp(): boolean;
clearUpdatedAtTimestamp(): void;
getUpdatedAtTimestamp(): number | undefined;
setUpdatedAtTimestamp(value: number): ItemHash;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ItemHash.AsObject;
static toObject(includeInstance: boolean, msg: ItemHash): ItemHash.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: ItemHash, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): ItemHash;
static deserializeBinaryFromReader(message: ItemHash, reader: jspb.BinaryReader): ItemHash;
}
export namespace ItemHash {
export type AsObject = {
uuid: string,
content?: string,
contentType?: string,
deleted?: boolean,
duplicateOf?: string,
authHash?: string,
encItemKey?: string,
itemsKeyId?: string,
keySystemIdentifier?: string,
sharedVaultUuid?: string,
createdAt?: string,
createdAtTimestamp?: number,
updatedAt?: string,
updatedAtTimestamp?: number,
}
}
export class SyncResponse extends jspb.Message {
clearRetrievedItemsList(): void;
getRetrievedItemsList(): Array<ItemRepresentation>;
setRetrievedItemsList(value: Array<ItemRepresentation>): SyncResponse;
addRetrievedItems(value?: ItemRepresentation, index?: number): ItemRepresentation;
clearSavedItemsList(): void;
getSavedItemsList(): Array<SavedItemRepresentation>;
setSavedItemsList(value: Array<SavedItemRepresentation>): SyncResponse;
addSavedItems(value?: SavedItemRepresentation, index?: number): SavedItemRepresentation;
clearConflictsList(): void;
getConflictsList(): Array<ItemConflictRepresentation>;
setConflictsList(value: Array<ItemConflictRepresentation>): SyncResponse;
addConflicts(value?: ItemConflictRepresentation, index?: number): ItemConflictRepresentation;
getSyncToken(): string;
setSyncToken(value: string): SyncResponse;
hasCursorToken(): boolean;
clearCursorToken(): void;
getCursorToken(): string | undefined;
setCursorToken(value: string): SyncResponse;
clearMessagesList(): void;
getMessagesList(): Array<MessageRepresentation>;
setMessagesList(value: Array<MessageRepresentation>): SyncResponse;
addMessages(value?: MessageRepresentation, index?: number): MessageRepresentation;
clearSharedVaultsList(): void;
getSharedVaultsList(): Array<SharedVaultRepresentation>;
setSharedVaultsList(value: Array<SharedVaultRepresentation>): SyncResponse;
addSharedVaults(value?: SharedVaultRepresentation, index?: number): SharedVaultRepresentation;
clearSharedVaultInvitesList(): void;
getSharedVaultInvitesList(): Array<SharedVaultInviteRepresentation>;
setSharedVaultInvitesList(value: Array<SharedVaultInviteRepresentation>): SyncResponse;
addSharedVaultInvites(value?: SharedVaultInviteRepresentation, index?: number): SharedVaultInviteRepresentation;
clearNotificationsList(): void;
getNotificationsList(): Array<NotificationRepresentation>;
setNotificationsList(value: Array<NotificationRepresentation>): SyncResponse;
addNotifications(value?: NotificationRepresentation, index?: number): NotificationRepresentation;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): SyncResponse.AsObject;
static toObject(includeInstance: boolean, msg: SyncResponse): SyncResponse.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: SyncResponse, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): SyncResponse;
static deserializeBinaryFromReader(message: SyncResponse, reader: jspb.BinaryReader): SyncResponse;
}
export namespace SyncResponse {
export type AsObject = {
retrievedItemsList: Array<ItemRepresentation.AsObject>,
savedItemsList: Array<SavedItemRepresentation.AsObject>,
conflictsList: Array<ItemConflictRepresentation.AsObject>,
syncToken: string,
cursorToken?: string,
messagesList: Array<MessageRepresentation.AsObject>,
sharedVaultsList: Array<SharedVaultRepresentation.AsObject>,
sharedVaultInvitesList: Array<SharedVaultInviteRepresentation.AsObject>,
notificationsList: Array<NotificationRepresentation.AsObject>,
}
}
export class SyncRequest extends jspb.Message {
clearItemsList(): void;
getItemsList(): Array<ItemHash>;
setItemsList(value: Array<ItemHash>): SyncRequest;
addItems(value?: ItemHash, index?: number): ItemHash;
clearSharedVaultUuidsList(): void;
getSharedVaultUuidsList(): Array<string>;
setSharedVaultUuidsList(value: Array<string>): SyncRequest;
addSharedVaultUuids(value: string, index?: number): string;
hasComputeIntegrity(): boolean;
clearComputeIntegrity(): void;
getComputeIntegrity(): boolean | undefined;
setComputeIntegrity(value: boolean): SyncRequest;
hasSyncToken(): boolean;
clearSyncToken(): void;
getSyncToken(): string | undefined;
setSyncToken(value: string): SyncRequest;
hasCursorToken(): boolean;
clearCursorToken(): void;
getCursorToken(): string | undefined;
setCursorToken(value: string): SyncRequest;
hasLimit(): boolean;
clearLimit(): void;
getLimit(): number | undefined;
setLimit(value: number): SyncRequest;
hasContentType(): boolean;
clearContentType(): void;
getContentType(): string | undefined;
setContentType(value: string): SyncRequest;
hasApiVersion(): boolean;
clearApiVersion(): void;
getApiVersion(): string | undefined;
setApiVersion(value: string): SyncRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): SyncRequest.AsObject;
static toObject(includeInstance: boolean, msg: SyncRequest): SyncRequest.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: SyncRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): SyncRequest;
static deserializeBinaryFromReader(message: SyncRequest, reader: jspb.BinaryReader): SyncRequest;
}
export namespace SyncRequest {
export type AsObject = {
itemsList: Array<ItemHash.AsObject>,
sharedVaultUuidsList: Array<string>,
computeIntegrity?: boolean,
syncToken?: string,
cursorToken?: string,
limit?: number,
contentType?: string,
apiVersion?: string,
}
}

5675
packages/grpc/lib/sync_pb.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,33 @@
{
"name": "@standardnotes/grpc",
"version": "1.2.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
"description": "gRPC definitions for Standard Notes",
"author": "Standard Notes",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"lib/**/*.js",
"lib/**/*.d.ts"
],
"publishConfig": {
"access": "public"
},
"license": "AGPL-3.0-or-later",
"scripts": {
"clean": "rm -fr dist",
"build": "tsc --build"
},
"dependencies": {
"@grpc/grpc-js": "^1.9.11",
"google-protobuf": "^3.21.2"
},
"devDependencies": {
"@types/google-protobuf": "^3",
"grpc-tools": "^1.12.4",
"grpc_tools_node_protoc_ts": "^5.3.3",
"typescript": "^5.0.4"
}
}

View File

@@ -0,0 +1,15 @@
syntax = "proto3";
package auth;
message AuthorizationHeader {
string bearer_token = 1;
}
message SessionValidationResponse {
string cross_service_token = 1;
}
service Sessions {
rpc validate(AuthorizationHeader) returns (SessionValidationResponse) {}
}

View File

@@ -0,0 +1,146 @@
syntax = "proto3";
package sync;
message ItemHashRepresentation {
string uuid = 1;
string user_uuid = 2;
optional string content = 3;
optional string content_type = 4;
optional bool deleted = 5;
optional string duplicate_of = 6;
optional string auth_hash = 7;
optional string enc_item_key = 8;
optional string items_key_id = 9;
optional string key_system_identifier = 10;
optional string shared_vault_uuid = 11;
optional string created_at = 12;
optional uint64 created_at_timestamp = 13;
optional string updated_at = 14;
optional uint64 updated_at_timestamp = 15;
}
message ItemConflictRepresentation {
optional ItemRepresentation server_item = 1;
optional ItemHashRepresentation unsaved_item = 2;
string type = 3;
}
message ItemRepresentation {
string uuid = 1;
optional string items_key_id = 2;
optional string duplicate_of = 3;
optional string enc_item_key = 4;
optional string content = 5;
string content_type = 6;
optional string auth_hash = 7;
bool deleted = 8;
string created_at = 9;
uint64 created_at_timestamp = 10;
string updated_at = 11;
uint64 updated_at_timestamp = 12;
optional string updated_with_session = 13;
optional string key_system_identifier = 14;
optional string shared_vault_uuid = 15;
optional string user_uuid = 16;
optional string last_edited_by_uuid = 17;
}
message SavedItemRepresentation {
string uuid = 1;
optional string duplicate_of = 2;
string content_type = 3;
optional string auth_hash = 4;
bool deleted = 5;
string created_at = 6;
uint64 created_at_timestamp = 7;
string updated_at = 8;
uint64 updated_at_timestamp = 9;
optional string key_system_identifier = 10;
optional string shared_vault_uuid = 11;
optional string user_uuid = 12;
optional string last_edited_by_uuid = 13;
}
message MessageRepresentation {
string uuid = 1;
string recipient_uuid = 2;
string sender_uuid = 3;
string encrypted_message = 4;
optional string replaceability_identifier = 5;
uint64 created_at_timestamp = 6;
uint64 updated_at_timestamp = 7;
}
message SharedVaultRepresentation {
string uuid = 1;
string user_uuid = 2;
uint32 file_upload_bytes_used = 3;
uint64 created_at_timestamp = 4;
uint64 updated_at_timestamp = 5;
}
message SharedVaultInviteRepresentation {
string uuid = 1;
string shared_vault_uuid = 2;
string user_uuid = 3;
string sender_uuid = 4;
string encrypted_message = 5;
string permission = 6;
uint64 created_at_timestamp = 7;
uint64 updated_at_timestamp = 8;
}
message NotificationRepresentation {
string uuid = 1;
string user_uuid = 2;
string type = 3;
string payload = 4;
uint64 created_at_timestamp = 5;
uint64 updated_at_timestamp = 6;
}
message ItemHash {
string uuid = 1;
optional string content = 2;
optional string content_type = 3;
optional bool deleted = 4;
optional string duplicate_of = 5;
optional string auth_hash = 6;
optional string enc_item_key = 7;
optional string items_key_id = 8;
optional string key_system_identifier = 9;
optional string shared_vault_uuid = 10;
optional string created_at = 11;
optional uint64 created_at_timestamp = 12;
optional string updated_at = 13;
optional uint64 updated_at_timestamp = 14;
}
message SyncResponse {
repeated ItemRepresentation retrieved_items = 1;
repeated SavedItemRepresentation saved_items = 2;
repeated ItemConflictRepresentation conflicts = 3;
string sync_token = 4;
optional string cursor_token = 5;
repeated MessageRepresentation messages = 6;
repeated SharedVaultRepresentation shared_vaults = 7;
repeated SharedVaultInviteRepresentation shared_vault_invites = 8;
repeated NotificationRepresentation notifications = 9;
}
message SyncRequest {
repeated ItemHash items = 1;
repeated string shared_vault_uuids = 2;
optional bool compute_integrity = 3;
optional string sync_token = 4;
optional string cursor_token = 5;
optional uint32 limit = 6;
optional string content_type = 7;
optional string api_version = 8;
}
service Syncing {
rpc syncItems(SyncRequest) returns (SyncResponse) {}
}

View File

@@ -0,0 +1,20 @@
#!/bin/bash
PROTO_DEST=./lib
mkdir -p ${PROTO_DEST}
# generate js codes via grpc-tools
yarn run grpc_tools_node_protoc \
--js_out=import_style=commonjs,binary:${PROTO_DEST} \
--grpc_out=${PROTO_DEST} \
--plugin=protoc-gen-grpc=../../.yarn/unplugged/grpc-tools-npm-1.12.4-956df6794d/node_modules/grpc-tools/bin/protoc_plugin.js \
-I ./proto \
proto/*.proto
# generate d.ts codes
yarn run grpc_tools_node_protoc \
--plugin=protoc-gen-ts=../../.yarn/unplugged/grpc_tools_node_protoc_ts-npm-5.3.3-297a345c26/node_modules/grpc_tools_node_protoc_ts/bin/protoc-gen-ts \
--ts_out=${PROTO_DEST} \
-I ./proto \
proto/*.proto

View File

@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"composite": true,
"outDir": "./lib",
},
"include": [],
"references": []
}

View File

@@ -3,6 +3,32 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.21.4](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.21.3...@standardnotes/home-server@1.21.4) (2023-11-20)
**Note:** Version bump only for package @standardnotes/home-server
## [1.21.3](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.21.2...@standardnotes/home-server@1.21.3) (2023-11-16)
**Note:** Version bump only for package @standardnotes/home-server
## [1.21.2](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.21.1...@standardnotes/home-server@1.21.2) (2023-11-16)
**Note:** Version bump only for package @standardnotes/home-server
## [1.21.1](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.21.0...@standardnotes/home-server@1.21.1) (2023-11-16)
**Note:** Version bump only for package @standardnotes/home-server
# [1.21.0](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.20.5...@standardnotes/home-server@1.21.0) (2023-11-16)
### Features
* add grpc sessions validation server ([#928](https://github.com/standardnotes/server/issues/928)) ([4f62cac](https://github.com/standardnotes/server/commit/4f62cac213a6b5f503040ef6319e5198967974ce))
## [1.20.5](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.20.4...@standardnotes/home-server@1.20.5) (2023-11-14)
**Note:** Version bump only for package @standardnotes/home-server
## [1.20.4](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.20.3...@standardnotes/home-server@1.20.4) (2023-11-14)
**Note:** Version bump only for package @standardnotes/home-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/home-server",
"version": "1.20.4",
"version": "1.21.4",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -176,7 +176,9 @@ export class HomeServer implements HomeServerInterface {
const serverInstance = server.build().listen(port)
const keepAliveTimeout = env.get('KEEP_ALIVE_TIMEOUT', true) ? +env.get('KEEP_ALIVE_TIMEOUT', true) : 5000
const keepAliveTimeout = env.get('HTTP_KEEP_ALIVE_TIMEOUT', true)
? +env.get('HTTP_KEEP_ALIVE_TIMEOUT', true)
: 5000
serverInstance.keepAliveTimeout = keepAliveTimeout

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.50.0](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.49.2...@standardnotes/revisions-server@1.50.0) (2023-11-16)
### Features
* add grpc sessions validation server ([#928](https://github.com/standardnotes/server/issues/928)) ([4f62cac](https://github.com/standardnotes/server/commit/4f62cac213a6b5f503040ef6319e5198967974ce))
## [1.49.2](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.49.1...@standardnotes/revisions-server@1.49.2) (2023-11-13)
**Note:** Version bump only for package @standardnotes/revisions-server

View File

@@ -45,7 +45,7 @@ void container.load().then((container) => {
const serverInstance = server.build().listen(env.get('PORT'))
const keepAliveTimeout = env.get('KEEP_ALIVE_TIMEOUT', true) ? +env.get('KEEP_ALIVE_TIMEOUT', true) : 5000
const keepAliveTimeout = env.get('HTTP_KEEP_ALIVE_TIMEOUT', true) ? +env.get('HTTP_KEEP_ALIVE_TIMEOUT', true) : 5000
serverInstance.keepAliveTimeout = keepAliveTimeout

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/revisions-server",
"version": "1.49.2",
"version": "1.50.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -6,6 +6,7 @@ VERSION=development
AUTH_JWT_SECRET=auth_jwt_secret
PORT=3000
GRPC_PORT=50052
DB_HOST=db
DB_REPLICA_HOST=db

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.124.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.123.0...@standardnotes/syncing-server@1.124.0) (2023-11-20)
### Features
* **grpc:** add syncing protocol buffers ([#930](https://github.com/standardnotes/syncing-server-js/issues/930)) ([5b84f07](https://github.com/standardnotes/syncing-server-js/commit/5b84f078c6ae6330706895f7c57b67ff8c8d18ae))
# [1.123.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.122.2...@standardnotes/syncing-server@1.123.0) (2023-11-16)
### Features
* add grpc sessions validation server ([#928](https://github.com/standardnotes/syncing-server-js/issues/928)) ([4f62cac](https://github.com/standardnotes/syncing-server-js/commit/4f62cac213a6b5f503040ef6319e5198967974ce))
## [1.122.2](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.122.1...@standardnotes/syncing-server@1.122.2) (2023-11-13)
**Note:** Version bump only for package @standardnotes/syncing-server

View File

@@ -10,13 +10,20 @@ import '../src/Infra/InversifyExpressUtils/AnnotatedSharedVaultsController'
import helmet from 'helmet'
import * as cors from 'cors'
import * as grpc from '@grpc/grpc-js'
import { urlencoded, json, Request, Response, NextFunction } from 'express'
import * as winston from 'winston'
import { InversifyExpressServer } from 'inversify-express-utils'
import { MapperInterface } from '@standardnotes/domain-core'
import { SyncResponse, SyncingService } from '@standardnotes/grpc'
import TYPES from '../src/Bootstrap/Types'
import { Env } from '../src/Bootstrap/Env'
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
import { SyncingServer } from '../src/Infra/gRPC/SyncingServer'
import { SyncItems } from '../src/Domain/UseCase/Syncing/SyncItems/SyncItems'
import { SyncResponseFactoryResolverInterface } from '../src/Domain/Item/SyncResponse/SyncResponseFactoryResolverInterface'
import { SyncResponse20200115 } from '../src/Domain/Item/SyncResponse/SyncResponse20200115'
const container = new ContainerConfigLoader()
void container.load().then((container) => {
@@ -74,15 +81,57 @@ void container.load().then((container) => {
const serverInstance = server.build().listen(env.get('PORT'))
const keepAliveTimeout = env.get('KEEP_ALIVE_TIMEOUT', true) ? +env.get('KEEP_ALIVE_TIMEOUT', true) : 5000
const keepAliveTimeout = env.get('HTTP_KEEP_ALIVE_TIMEOUT', true) ? +env.get('HTTP_KEEP_ALIVE_TIMEOUT', true) : 5000
serverInstance.keepAliveTimeout = keepAliveTimeout
const grpcKeepAliveTimeout = env.get('GRPC_KEEP_ALIVE_TIMEOUT', true)
? +env.get('GRPC_KEEP_ALIVE_TIMEOUT', true)
: 10_000
const grpcServer = new grpc.Server({
'grpc.keepalive_time_ms': grpcKeepAliveTimeout * 2,
'grpc.keepalive_timeout_ms': grpcKeepAliveTimeout,
})
const gRPCPort = env.get('GRPC_PORT', true) ? +env.get('GRPC_PORT', true) : 50051
const syncingServer = new SyncingServer(
container.get<SyncItems>(TYPES.Sync_SyncItems),
container.get<SyncResponseFactoryResolverInterface>(TYPES.Sync_SyncResponseFactoryResolver),
container.get<MapperInterface<SyncResponse20200115, SyncResponse>>(TYPES.Sync_SyncResponseGRPCMapper),
container.get<winston.Logger>(TYPES.Sync_Logger),
)
grpcServer.addService(SyncingService, {
syncItems: syncingServer.syncItems.bind(syncingServer),
})
grpcServer.bindAsync(`0.0.0.0:${gRPCPort}`, grpc.ServerCredentials.createInsecure(), (error, port) => {
if (error) {
logger.error(`Failed to bind gRPC server: ${error.message}`)
return
}
logger.info(`gRPC server bound on port ${port}`)
grpcServer.start()
logger.info('gRPC server started')
})
process.on('SIGTERM', () => {
logger.info('SIGTERM signal received: closing HTTP server')
serverInstance.close(() => {
logger.info('HTTP server closed')
})
grpcServer.tryShutdown((error?: Error) => {
if (error) {
logger.error(`Failed to shutdown gRPC server: ${error.message}`)
} else {
logger.info('gRPC server closed')
}
})
})
logger.info(`Server started on port ${process.env.PORT}`)

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/syncing-server",
"version": "1.122.2",
"version": "1.124.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -30,11 +30,13 @@
"@aws-sdk/client-s3": "^3.427.0",
"@aws-sdk/client-sns": "^3.427.0",
"@aws-sdk/client-sqs": "^3.427.0",
"@grpc/grpc-js": "^1.9.11",
"@standardnotes/api": "^1.26.26",
"@standardnotes/common": "workspace:*",
"@standardnotes/domain-core": "workspace:^",
"@standardnotes/domain-events": "workspace:*",
"@standardnotes/domain-events-infra": "workspace:*",
"@standardnotes/grpc": "workspace:^",
"@standardnotes/responses": "^1.13.27",
"@standardnotes/security": "workspace:*",
"@standardnotes/settings": "workspace:*",

View File

@@ -157,6 +157,9 @@ import { RemoveUserFromSharedVaults } from '../Domain/UseCase/SharedVaults/Remov
import { TransferSharedVault } from '../Domain/UseCase/SharedVaults/TransferSharedVault/TransferSharedVault'
import { TransferSharedVaultItems } from '../Domain/UseCase/SharedVaults/TransferSharedVaultItems/TransferSharedVaultItems'
import { DumpItem } from '../Domain/UseCase/Syncing/DumpItem/DumpItem'
import { SyncResponse20200115 } from '../Domain/Item/SyncResponse/SyncResponse20200115'
import { SyncResponse } from '@standardnotes/grpc'
import { SyncResponseGRPCMapper } from '../Mapping/gRPC/SyncResponseGRPCMapper'
export class ContainerConfigLoader {
private readonly DEFAULT_CONTENT_SIZE_TRANSFER_LIMIT = 10_000_000
@@ -351,6 +354,9 @@ export class ContainerConfigLoader {
container
.bind<MapperInterface<Notification, NotificationHttpRepresentation>>(TYPES.Sync_NotificationHttpMapper)
.toConstantValue(new NotificationHttpMapper())
container
.bind<MapperInterface<SyncResponse20200115, SyncResponse>>(TYPES.Sync_SyncResponseGRPCMapper)
.toConstantValue(new SyncResponseGRPCMapper())
// ORM
container

View File

@@ -126,6 +126,7 @@ const TYPES = {
Sync_MessagePersistenceMapper: Symbol.for('Sync_MessagePersistenceMapper'),
Sync_MessageHttpMapper: Symbol.for('Sync_MessageHttpMapper'),
Sync_NotificationHttpMapper: Symbol.for('Sync_NotificationHttpMapper'),
Sync_SyncResponseGRPCMapper: Symbol.for('Sync_SyncResponseGRPCMapper'),
Sync_SQLItemPersistenceMapper: Symbol.for('Sync_SQLItemPersistenceMapper'),
Sync_ItemHttpMapper: Symbol.for('Sync_ItemHttpMapper'),
Sync_ItemHashHttpMapper: Symbol.for('Sync_ItemHashHttpMapper'),

View File

@@ -184,7 +184,7 @@ describe('GetItems', () => {
})
expect(result.isFailed()).toBeTruthy()
expect(result.getError()).toEqual('Given value is not a valid uuid: invalid')
expect(result.getError()).toEqual('User uuid is invalid: Given value is not a valid uuid: invalid')
})
it('should filter shared vault uuids user wants to sync with the ones it has access to', async () => {

View File

@@ -31,7 +31,7 @@ export class GetItems implements UseCaseInterface<GetItemsResult> {
const userUuidOrError = Uuid.create(dto.userUuid)
if (userUuidOrError.isFailed()) {
return Result.fail(userUuidOrError.getError())
return Result.fail(`User uuid is invalid: ${userUuidOrError.getError()}`)
}
const userUuid = userUuidOrError.getValue()

View File

@@ -29,7 +29,7 @@ export class SaveNewItem implements UseCaseInterface<Item> {
async execute(dto: SaveNewItemDTO): Promise<Result<Item>> {
const uuidOrError = Uuid.create(dto.itemHash.props.uuid)
if (uuidOrError.isFailed()) {
return Result.fail(uuidOrError.getError())
return Result.fail(`Item uuid is invalid: ${uuidOrError.getError()}`)
}
const uuid = uuidOrError.getValue()
@@ -37,13 +37,13 @@ export class SaveNewItem implements UseCaseInterface<Item> {
if (dto.sessionUuid) {
const sessionUuidOrError = Uuid.create(dto.sessionUuid)
if (sessionUuidOrError.isFailed()) {
return Result.fail(sessionUuidOrError.getError())
return Result.fail(`Session uuid is invalid: ${sessionUuidOrError.getError()}`)
}
updatedWithSession = sessionUuidOrError.getValue()
}
const userUuidOrError = Uuid.create(dto.userUuid)
if (userUuidOrError.isFailed()) {
return Result.fail(userUuidOrError.getError())
return Result.fail(`User uuid is invalid: ${userUuidOrError.getError()}`)
}
const userUuid = userUuidOrError.getValue()
@@ -57,7 +57,7 @@ export class SaveNewItem implements UseCaseInterface<Item> {
if (dto.itemHash.props.duplicate_of) {
const duplicateOfOrError = Uuid.create(dto.itemHash.props.duplicate_of)
if (duplicateOfOrError.isFailed()) {
return Result.fail(duplicateOfOrError.getError())
return Result.fail(`Duplicate of uuid is invalid: ${duplicateOfOrError.getError()}`)
}
duplicateOf = duplicateOfOrError.getValue()
}

View File

@@ -4,7 +4,7 @@ export type SyncItemsDTO = {
userUuid: string
itemHashes: Array<ItemHash>
computeIntegrityHash: boolean
limit: number
limit?: number
sharedVaultUuids?: string[]
syncToken?: string | null
cursorToken?: string | null

View File

@@ -40,7 +40,7 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
async execute(dto: UpdateExistingItemDTO): Promise<Result<Item>> {
const userUuidOrError = Uuid.create(dto.performingUserUuid)
if (userUuidOrError.isFailed()) {
return Result.fail(userUuidOrError.getError())
return Result.fail(`User uuid is invalid: ${userUuidOrError.getError()}`)
}
const userUuid = userUuidOrError.getValue()
@@ -61,7 +61,7 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
if (dto.sessionUuid) {
const sessionUuidOrError = Uuid.create(dto.sessionUuid)
if (sessionUuidOrError.isFailed()) {
return Result.fail(sessionUuidOrError.getError())
return Result.fail(`Session uuid is invalid: ${sessionUuidOrError.getError()}`)
}
sessionUuid = sessionUuidOrError.getValue()
}
@@ -88,7 +88,7 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
if (dto.itemHash.props.duplicate_of) {
const duplicateOfOrError = Uuid.create(dto.itemHash.props.duplicate_of)
if (duplicateOfOrError.isFailed()) {
return Result.fail(duplicateOfOrError.getError())
return Result.fail(`Duplicate of uuid is invalid: ${duplicateOfOrError.getError()}`)
}
wasMarkedAsDuplicate = dto.existingItem.props.duplicateOf === null
dto.existingItem.props.duplicateOf = duplicateOfOrError.getValue()

View File

@@ -0,0 +1,113 @@
import * as grpc from '@grpc/grpc-js'
import { Status } from '@grpc/grpc-js/build/src/constants'
import { ISyncingServer, SyncRequest, SyncResponse } from '@standardnotes/grpc'
import { Logger } from 'winston'
import { MapperInterface } from '@standardnotes/domain-core'
import { ItemHash } from '../../Domain/Item/ItemHash'
import { SyncItems } from '../../Domain/UseCase/Syncing/SyncItems/SyncItems'
import { ApiVersion } from '../../Domain/Api/ApiVersion'
import { SyncResponseFactoryResolverInterface } from '../../Domain/Item/SyncResponse/SyncResponseFactoryResolverInterface'
import { SyncResponse20200115 } from '../../Domain/Item/SyncResponse/SyncResponse20200115'
export class SyncingServer implements ISyncingServer {
constructor(
private syncItemsUseCase: SyncItems,
private syncResponseFactoryResolver: SyncResponseFactoryResolverInterface,
private mapper: MapperInterface<SyncResponse20200115, SyncResponse>,
private logger: Logger,
) {}
async syncItems(
call: grpc.ServerUnaryCall<SyncRequest, SyncResponse>,
callback: grpc.sendUnaryData<SyncResponse>,
): Promise<void> {
this.logger.debug('[SyncingServer] Syncing items via gRPC')
const itemHashesRPC = call.request.getItemsList()
const itemHashes: ItemHash[] = []
for (const itemHash of itemHashesRPC) {
const itemHashOrError = ItemHash.create({
uuid: itemHash.getUuid(),
content: itemHash.hasContent() ? itemHash.getContent() : undefined,
content_type: itemHash.hasContentType() ? (itemHash.getContentType() as string) : null,
deleted: itemHash.hasDeleted() ? itemHash.getDeleted() : undefined,
duplicate_of: itemHash.hasDuplicateOf() ? itemHash.getDuplicateOf() : undefined,
auth_hash: itemHash.hasAuthHash() ? itemHash.getAuthHash() : undefined,
enc_item_key: itemHash.hasEncItemKey() ? itemHash.getEncItemKey() : undefined,
items_key_id: itemHash.hasItemsKeyId() ? itemHash.getItemsKeyId() : undefined,
created_at: itemHash.hasCreatedAt() ? itemHash.getCreatedAt() : undefined,
created_at_timestamp: itemHash.hasCreatedAtTimestamp() ? itemHash.getCreatedAtTimestamp() : undefined,
updated_at: itemHash.hasUpdatedAt() ? itemHash.getUpdatedAt() : undefined,
updated_at_timestamp: itemHash.hasUpdatedAtTimestamp() ? itemHash.getUpdatedAtTimestamp() : undefined,
user_uuid: call.metadata.get('userUuid').pop() as string,
key_system_identifier: itemHash.hasKeySystemIdentifier() ? (itemHash.getKeySystemIdentifier() as string) : null,
shared_vault_uuid: itemHash.hasSharedVaultUuid() ? (itemHash.getSharedVaultUuid() as string) : null,
})
if (itemHashOrError.isFailed()) {
const metadata = new grpc.Metadata()
metadata.set('x-sync-error-message', itemHashOrError.getError())
metadata.set('x-sync-error-response-code', '400')
return callback(
{
code: Status.INVALID_ARGUMENT,
message: itemHashOrError.getError(),
name: 'INVALID_ARGUMENT',
metadata,
},
null,
)
}
itemHashes.push(itemHashOrError.getValue())
}
let sharedVaultUuids: string[] | undefined = undefined
const sharedVaultUuidsList = call.request.getSharedVaultUuidsList()
if (sharedVaultUuidsList.length > 0) {
sharedVaultUuids = sharedVaultUuidsList
}
const apiVersion = call.request.hasApiVersion() ? (call.request.getApiVersion() as string) : ApiVersion.v20161215
const syncResult = await this.syncItemsUseCase.execute({
userUuid: call.metadata.get('x-user-uuid').pop() as string,
itemHashes,
computeIntegrityHash: call.request.hasComputeIntegrity() ? call.request.getComputeIntegrity() === true : false,
syncToken: call.request.hasSyncToken() ? call.request.getSyncToken() : undefined,
cursorToken: call.request.getCursorToken() ? call.request.getCursorToken() : undefined,
limit: call.request.hasLimit() ? call.request.getLimit() : undefined,
contentType: call.request.hasContentType() ? call.request.getContentType() : undefined,
apiVersion,
snjsVersion: call.metadata.get('x-snjs-version').pop() as string,
readOnlyAccess: call.metadata.get('x-read-only-access').pop() === 'true',
sessionUuid: call.metadata.get('x-session-uuid').pop() as string,
sharedVaultUuids,
})
if (syncResult.isFailed()) {
const metadata = new grpc.Metadata()
metadata.set('x-sync-error-message', syncResult.getError())
metadata.set('x-sync-error-response-code', '400')
return callback(
{
code: Status.INVALID_ARGUMENT,
message: syncResult.getError(),
name: 'INVALID_ARGUMENT',
metadata,
},
null,
)
}
const syncResponse = await this.syncResponseFactoryResolver
.resolveSyncResponseFactoryVersion(apiVersion)
.createResponse(syncResult.getValue())
const projection = this.mapper.toProjection(syncResponse as SyncResponse20200115)
callback(null, projection)
}
}

View File

@@ -0,0 +1,250 @@
import { MapperInterface } from '@standardnotes/domain-core'
import {
ItemConflictRepresentation,
ItemHashRepresentation,
ItemRepresentation,
MessageRepresentation,
NotificationRepresentation,
SavedItemRepresentation,
SharedVaultInviteRepresentation,
SharedVaultRepresentation,
SyncResponse,
} from '@standardnotes/grpc'
import { ItemHttpRepresentation } from '../Http/ItemHttpRepresentation'
import { SyncResponse20200115 } from '../../Domain/Item/SyncResponse/SyncResponse20200115'
import { SavedItemHttpRepresentation } from '../Http/SavedItemHttpRepresentation'
import { ItemConflictHttpRepresentation } from '../Http/ItemConflictHttpRepresentation'
import { ItemHashHttpRepresentation } from '../Http/ItemHashHttpRepresentation'
import { MessageHttpRepresentation } from '../Http/MessageHttpRepresentation'
import { SharedVaultHttpRepresentation } from '../Http/SharedVaultHttpRepresentation'
import { SharedVaultInviteHttpRepresentation } from '../Http/SharedVaultInviteHttpRepresentation'
import { NotificationHttpRepresentation } from '../Http/NotificationHttpRepresentation'
export class SyncResponseGRPCMapper implements MapperInterface<SyncResponse20200115, SyncResponse> {
toDomain(_projection: SyncResponse): SyncResponse20200115 {
throw new Error('Method not implemented.')
}
toProjection(domain: SyncResponse20200115): SyncResponse {
const syncResponse = new SyncResponse()
const retrievedItems = domain.retrieved_items.map((item) => this.createRetrievedItem(item))
syncResponse.setRetrievedItemsList(retrievedItems)
const savedItems = domain.saved_items.map((item) => this.createSavedItem(item))
syncResponse.setSavedItemsList(savedItems)
const conflicts = domain.conflicts.map((conflict) => this.createConflict(conflict))
syncResponse.setConflictsList(conflicts)
syncResponse.setSyncToken(domain.sync_token)
if (domain.cursor_token) {
syncResponse.setCursorToken(domain.cursor_token)
}
const messages = domain.messages.map((message) => this.createMessage(message))
syncResponse.setMessagesList(messages)
const sharedVaults = domain.shared_vaults.map((sharedVault) => this.createSharedVault(sharedVault))
syncResponse.setSharedVaultsList(sharedVaults)
const sharedVaultInvites = domain.shared_vault_invites.map((sharedVaultInvite) =>
this.createSharedVaultInvite(sharedVaultInvite),
)
syncResponse.setSharedVaultInvitesList(sharedVaultInvites)
const notifications = domain.notifications.map((notification) => this.createNotification(notification))
syncResponse.setNotificationsList(notifications)
return syncResponse
}
private createNotification(notification: NotificationHttpRepresentation): NotificationRepresentation {
const notificationRepresentation = new NotificationRepresentation()
notificationRepresentation.setUuid(notification.uuid)
notificationRepresentation.setUserUuid(notification.user_uuid)
notificationRepresentation.setType(notification.type)
notificationRepresentation.setPayload(notification.payload)
notificationRepresentation.setCreatedAtTimestamp(notification.created_at_timestamp)
notificationRepresentation.setUpdatedAtTimestamp(notification.updated_at_timestamp)
return notificationRepresentation
}
private createSharedVaultInvite(
sharedVaultInvite: SharedVaultInviteHttpRepresentation,
): SharedVaultInviteRepresentation {
const sharedVaultInviteRepresentation = new SharedVaultInviteRepresentation()
sharedVaultInviteRepresentation.setUuid(sharedVaultInvite.uuid)
sharedVaultInviteRepresentation.setSharedVaultUuid(sharedVaultInvite.shared_vault_uuid)
sharedVaultInviteRepresentation.setUserUuid(sharedVaultInvite.user_uuid)
sharedVaultInviteRepresentation.setSenderUuid(sharedVaultInvite.sender_uuid)
sharedVaultInviteRepresentation.setEncryptedMessage(sharedVaultInvite.encrypted_message)
sharedVaultInviteRepresentation.setPermission(sharedVaultInvite.permission)
sharedVaultInviteRepresentation.setCreatedAtTimestamp(sharedVaultInvite.created_at_timestamp)
sharedVaultInviteRepresentation.setUpdatedAtTimestamp(sharedVaultInvite.updated_at_timestamp)
return sharedVaultInviteRepresentation
}
private createSharedVault(sharedVault: SharedVaultHttpRepresentation): SharedVaultRepresentation {
const sharedVaultRepresentation = new SharedVaultRepresentation()
sharedVaultRepresentation.setUuid(sharedVault.uuid)
sharedVaultRepresentation.setUserUuid(sharedVault.user_uuid)
sharedVaultRepresentation.setFileUploadBytesUsed(sharedVault.file_upload_bytes_used)
sharedVaultRepresentation.setCreatedAtTimestamp(sharedVault.created_at_timestamp)
sharedVaultRepresentation.setUpdatedAtTimestamp(sharedVault.updated_at_timestamp)
return sharedVaultRepresentation
}
private createMessage(message: MessageHttpRepresentation): MessageRepresentation {
const messageRepresentation = new MessageRepresentation()
messageRepresentation.setUuid(message.uuid)
messageRepresentation.setRecipientUuid(message.recipient_uuid)
messageRepresentation.setSenderUuid(message.sender_uuid)
messageRepresentation.setEncryptedMessage(message.encrypted_message)
if (message.replaceability_identifier) {
messageRepresentation.setReplaceabilityIdentifier(message.replaceability_identifier)
}
messageRepresentation.setCreatedAtTimestamp(message.created_at_timestamp)
messageRepresentation.setUpdatedAtTimestamp(message.updated_at_timestamp)
return messageRepresentation
}
private createConflict(conflict: ItemConflictHttpRepresentation): ItemConflictRepresentation {
const itemConflictRepresentation = new ItemConflictRepresentation()
if (conflict.server_item) {
itemConflictRepresentation.setServerItem(this.createRetrievedItem(conflict.server_item))
}
if (conflict.unsaved_item) {
itemConflictRepresentation.setUnsavedItem(this.createItemHash(conflict.unsaved_item))
}
itemConflictRepresentation.setType(conflict.type)
return itemConflictRepresentation
}
private createItemHash(itemHash: ItemHashHttpRepresentation): ItemHashRepresentation {
const itemHashRepresentation = new ItemHashRepresentation()
itemHashRepresentation.setUuid(itemHash.uuid)
itemHashRepresentation.setUserUuid(itemHash.user_uuid)
if (itemHash.content) {
itemHashRepresentation.setContent(itemHash.content)
}
if (itemHash.content_type) {
itemHashRepresentation.setContentType(itemHash.content_type)
}
if (itemHash.deleted) {
itemHashRepresentation.setDeleted(itemHash.deleted)
}
if (itemHash.duplicate_of) {
itemHashRepresentation.setDuplicateOf(itemHash.duplicate_of)
}
if (itemHash.auth_hash) {
itemHashRepresentation.setAuthHash(itemHash.auth_hash)
}
if (itemHash.enc_item_key) {
itemHashRepresentation.setEncItemKey(itemHash.enc_item_key)
}
if (itemHash.items_key_id) {
itemHashRepresentation.setItemsKeyId(itemHash.items_key_id)
}
if (itemHash.key_system_identifier) {
itemHashRepresentation.setKeySystemIdentifier(itemHash.key_system_identifier)
}
if (itemHash.shared_vault_uuid) {
itemHashRepresentation.setSharedVaultUuid(itemHash.shared_vault_uuid)
}
if (itemHash.created_at) {
itemHashRepresentation.setCreatedAt(itemHash.created_at)
}
if (itemHash.created_at_timestamp) {
itemHashRepresentation.setCreatedAtTimestamp(itemHash.created_at_timestamp)
}
if (itemHash.updated_at) {
itemHashRepresentation.setUpdatedAt(itemHash.updated_at)
}
if (itemHash.updated_at_timestamp) {
itemHashRepresentation.setUpdatedAtTimestamp(itemHash.updated_at_timestamp)
}
return itemHashRepresentation
}
private createSavedItem(item: SavedItemHttpRepresentation): SavedItemRepresentation {
const savedItemRepresentation = new SavedItemRepresentation()
savedItemRepresentation.setUuid(item.uuid)
if (item.duplicate_of) {
savedItemRepresentation.setDuplicateOf(item.duplicate_of)
}
savedItemRepresentation.setContentType(item.content_type)
if (item.auth_hash) {
savedItemRepresentation.setAuthHash(item.auth_hash)
}
savedItemRepresentation.setDeleted(item.deleted)
savedItemRepresentation.setCreatedAt(item.created_at)
savedItemRepresentation.setCreatedAtTimestamp(item.created_at_timestamp)
savedItemRepresentation.setUpdatedAt(item.updated_at)
savedItemRepresentation.setUpdatedAtTimestamp(item.updated_at_timestamp)
if (item.key_system_identifier) {
savedItemRepresentation.setKeySystemIdentifier(item.key_system_identifier)
}
if (item.shared_vault_uuid) {
savedItemRepresentation.setSharedVaultUuid(item.shared_vault_uuid)
}
if (item.user_uuid) {
savedItemRepresentation.setUserUuid(item.user_uuid)
}
if (item.last_edited_by_uuid) {
savedItemRepresentation.setLastEditedByUuid(item.last_edited_by_uuid)
}
return savedItemRepresentation
}
private createRetrievedItem(item: ItemHttpRepresentation): ItemRepresentation {
const itemRepresentation = new ItemRepresentation()
itemRepresentation.setUuid(item.uuid)
if (item.items_key_id) {
itemRepresentation.setItemsKeyId(item.items_key_id)
}
if (item.duplicate_of) {
itemRepresentation.setDuplicateOf(item.duplicate_of)
}
if (item.enc_item_key) {
itemRepresentation.setEncItemKey(item.enc_item_key)
}
if (item.content) {
itemRepresentation.setContent(item.content)
}
itemRepresentation.setContentType(item.content_type)
if (item.auth_hash) {
itemRepresentation.setAuthHash(item.auth_hash)
}
itemRepresentation.setDeleted(item.deleted)
itemRepresentation.setCreatedAt(item.created_at)
itemRepresentation.setCreatedAtTimestamp(item.created_at_timestamp)
itemRepresentation.setUpdatedAt(item.updated_at)
itemRepresentation.setUpdatedAtTimestamp(item.updated_at_timestamp)
if (item.updated_with_session) {
itemRepresentation.setUpdatedWithSession(item.updated_with_session)
}
if (item.key_system_identifier) {
itemRepresentation.setKeySystemIdentifier(item.key_system_identifier)
}
if (item.shared_vault_uuid) {
itemRepresentation.setSharedVaultUuid(item.shared_vault_uuid)
}
if (item.user_uuid) {
itemRepresentation.setUserUuid(item.user_uuid)
}
if (item.last_edited_by_uuid) {
itemRepresentation.setLastEditedByUuid(item.last_edited_by_uuid)
}
return itemRepresentation
}
}

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