mirror of
https://github.com/standardnotes/server
synced 2026-01-21 08:04:27 -05:00
Compare commits
25 Commits
@standardn
...
@standardn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3946f56261 | ||
|
|
fc53dab007 | ||
|
|
e836abdef7 | ||
|
|
826482b1f0 | ||
|
|
45bd00919c | ||
|
|
4e1bae6daf | ||
|
|
8f23c8ab3f | ||
|
|
4d32f26631 | ||
|
|
c11abe1bd3 | ||
|
|
4d12566b0d | ||
|
|
2200dca69d | ||
|
|
d41dd3bdda | ||
|
|
c5c24b3ac9 | ||
|
|
462ade2145 | ||
|
|
bfef16ce37 | ||
|
|
aa4351c8e9 | ||
|
|
2dff6a2ed3 | ||
|
|
7808cc8ed2 | ||
|
|
5b84f078c6 | ||
|
|
cf5f44a4a5 | ||
|
|
ed05ea553f | ||
|
|
4418c38878 | ||
|
|
6391a01b57 | ||
|
|
9dbcec198d | ||
|
|
78fbeb595f |
3
.github/workflows/publish.yml
vendored
3
.github/workflows/publish.yml
vendored
@@ -4,6 +4,9 @@ on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: contains(github.event.head_commit.message, 'chore(release)') == false
|
||||
|
||||
14
.pnp.cjs
generated
14
.pnp.cjs
generated
@@ -2674,10 +2674,10 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["@grpc/grpc-js", [\
|
||||
["npm:1.9.10", {\
|
||||
"packageLocation": "./.yarn/cache/@grpc-grpc-js-npm-1.9.10-28317a9d2d-243cf994e6.zip/node_modules/@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.10"],\
|
||||
["@grpc/grpc-js", "npm:1.9.11"],\
|
||||
["@grpc/proto-loader", "npm:0.7.10"],\
|
||||
["@types/node", "npm:20.2.5"]\
|
||||
],\
|
||||
@@ -6401,7 +6401,7 @@ const RAW_RUNTIME_STATE =
|
||||
"packageLocation": "./packages/api-gateway/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/api-gateway", "workspace:packages/api-gateway"],\
|
||||
["@grpc/grpc-js", "npm:1.9.10"],\
|
||||
["@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"],\
|
||||
@@ -6451,7 +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.10"],\
|
||||
["@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"],\
|
||||
@@ -6678,7 +6678,7 @@ const RAW_RUNTIME_STATE =
|
||||
"packageLocation": "./packages/grpc/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/grpc", "workspace:packages/grpc"],\
|
||||
["@grpc/grpc-js", "npm:1.9.10"],\
|
||||
["@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"],\
|
||||
@@ -6951,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"],\
|
||||
|
||||
Binary file not shown.
@@ -22,11 +22,11 @@ services:
|
||||
environment:
|
||||
DB_TYPE: "${DB_TYPE}"
|
||||
CACHE_TYPE: "${CACHE_TYPE}"
|
||||
SERVICE_PROXY_TYPE: "${SERVICE_PROXY_TYPE}"
|
||||
container_name: server-ci
|
||||
ports:
|
||||
- 3123:3000
|
||||
- 3125:3104
|
||||
- 50051:50051
|
||||
volumes:
|
||||
- ./logs:/var/lib/server/logs
|
||||
networks:
|
||||
|
||||
@@ -14,6 +14,10 @@ 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
|
||||
@@ -356,6 +360,7 @@ 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
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.34.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.33.4...@standardnotes/analytics@2.34.0) (2023-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/server/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
|
||||
|
||||
## [2.33.4](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.33.3...@standardnotes/analytics@2.33.4) (2023-11-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.33.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.33.2...@standardnotes/analytics@2.33.3) (2023-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.33.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.33.1...@standardnotes/analytics@2.33.2) (2023-11-13)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "2.33.2",
|
||||
"version": "2.34.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -10,7 +10,8 @@
|
||||
"author": "Standard Notes",
|
||||
"types": "dist/src/index.d.ts",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
|
||||
@@ -6,6 +6,7 @@ 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
|
||||
|
||||
@@ -3,6 +3,68 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.87.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.86.6...@standardnotes/api-gateway@1.87.0) (2023-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/api-gateway/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
|
||||
|
||||
## [1.86.6](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.86.5...@standardnotes/api-gateway@1.86.6) (2023-11-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.86.5](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.86.4...@standardnotes/api-gateway@1.86.5) (2023-11-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* error handling on gRPC ([#937](https://github.com/standardnotes/api-gateway/issues/937)) ([8f23c8a](https://github.com/standardnotes/api-gateway/commit/8f23c8ab3f03e9c23adfb31a33c5805492bc2f5b))
|
||||
|
||||
## [1.86.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.86.3...@standardnotes/api-gateway@1.86.4) (2023-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.86.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.86.2...@standardnotes/api-gateway@1.86.3) (2023-11-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api-gateway:** add meta field to grpc sync calls ([#934](https://github.com/standardnotes/api-gateway/issues/934)) ([c5c24b3](https://github.com/standardnotes/api-gateway/commit/c5c24b3ac9dbd559d96adc56270d724a3156ebd4))
|
||||
|
||||
## [1.86.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.86.1...@standardnotes/api-gateway@1.86.2) (2023-11-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* define grpc max message size ([bfef16c](https://github.com/standardnotes/api-gateway/commit/bfef16ce3757b57ea1cb0cb7417d6bc935a52321))
|
||||
|
||||
## [1.86.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.86.0...@standardnotes/api-gateway@1.86.1) (2023-11-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* setting gzip as default compression on grpc calls ([#933](https://github.com/standardnotes/api-gateway/issues/933)) ([2dff6a2](https://github.com/standardnotes/api-gateway/commit/2dff6a2ed3d105ca65996d47321a811e22e25099))
|
||||
|
||||
# [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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.84.0",
|
||||
"version": "1.87.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -15,7 +15,8 @@
|
||||
"author": "Karol Sójko <karol@standardnotes.com>",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -fr dist",
|
||||
@@ -26,7 +27,7 @@
|
||||
"start": "yarn node dist/bin/server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^1.9.10",
|
||||
"@grpc/grpc-js": "^1.9.11",
|
||||
"@standardnotes/domain-core": "workspace:^",
|
||||
"@standardnotes/domain-events": "workspace:*",
|
||||
"@standardnotes/domain-events-infra": "workspace:*",
|
||||
|
||||
@@ -16,14 +16,25 @@ import { RedisCrossServiceTokenCache } from '../Infra/Redis/RedisCrossServiceTok
|
||||
import { WebSocketAuthMiddleware } from '../Controller/WebSocketAuthMiddleware'
|
||||
import { InMemoryCrossServiceTokenCache } from '../Infra/InMemory/InMemoryCrossServiceTokenCache'
|
||||
import { DirectCallServiceProxy } from '../Service/DirectCall/DirectCallServiceProxy'
|
||||
import { ServiceContainerInterface } from '@standardnotes/domain-core'
|
||||
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, SessionsClient } from '@standardnotes/grpc'
|
||||
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?: {
|
||||
@@ -119,6 +130,19 @@ export class ContainerConfigLoader {
|
||||
// Services
|
||||
container.bind<TimerInterface>(TYPES.ApiGateway_Timer).toConstantValue(new Timer())
|
||||
|
||||
if (isConfiguredForHomeServer) {
|
||||
container
|
||||
.bind<CrossServiceTokenCacheInterface>(TYPES.ApiGateway_CrossServiceTokenCache)
|
||||
.toConstantValue(new InMemoryCrossServiceTokenCache(container.get(TYPES.ApiGateway_Timer)))
|
||||
} else {
|
||||
container
|
||||
.bind<CrossServiceTokenCacheInterface>(TYPES.ApiGateway_CrossServiceTokenCache)
|
||||
.to(RedisCrossServiceTokenCache)
|
||||
}
|
||||
container
|
||||
.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')
|
||||
@@ -132,9 +156,15 @@ export class ContainerConfigLoader {
|
||||
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
|
||||
|
||||
const grpcMaxMessageSize = env.get('GRPC_MAX_MESSAGE_SIZE', true)
|
||||
? +env.get('GRPC_MAX_MESSAGE_SIZE', true)
|
||||
: 1024 * 1024 * 50
|
||||
|
||||
container.bind<ISessionsClient>(TYPES.ApiGateway_GRPCSessionsClient).toConstantValue(
|
||||
new SessionsClient(
|
||||
container.get<string>(TYPES.ApiGateway_AUTH_SERVER_GRPC_URL),
|
||||
@@ -142,9 +172,46 @@ export class ContainerConfigLoader {
|
||||
{
|
||||
'grpc.keepalive_time_ms': grpcAgentKeepAliveTimeout * 2,
|
||||
'grpc.keepalive_timeout_ms': grpcAgentKeepAliveTimeout,
|
||||
'grpc.default_compression_algorithm': grpc.compressionAlgorithms.gzip,
|
||||
'grpc.default_compression_level': 2,
|
||||
'grpc.max_receive_message_length': grpcMaxMessageSize,
|
||||
'grpc.max_send_message_length': grpcMaxMessageSize,
|
||||
},
|
||||
),
|
||||
)
|
||||
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,
|
||||
'grpc.default_compression_algorithm': grpc.compressionAlgorithms.gzip,
|
||||
'grpc.default_compression_level': 2,
|
||||
'grpc.max_receive_message_length': grpcMaxMessageSize,
|
||||
'grpc.max_send_message_length': grpcMaxMessageSize,
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
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(
|
||||
@@ -162,6 +229,7 @@ export class ContainerConfigLoader {
|
||||
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 {
|
||||
@@ -169,19 +237,6 @@ export class ContainerConfigLoader {
|
||||
}
|
||||
}
|
||||
|
||||
if (isConfiguredForHomeServer) {
|
||||
container
|
||||
.bind<CrossServiceTokenCacheInterface>(TYPES.ApiGateway_CrossServiceTokenCache)
|
||||
.toConstantValue(new InMemoryCrossServiceTokenCache(container.get(TYPES.ApiGateway_Timer)))
|
||||
} else {
|
||||
container
|
||||
.bind<CrossServiceTokenCacheInterface>(TYPES.ApiGateway_CrossServiceTokenCache)
|
||||
.to(RedisCrossServiceTokenCache)
|
||||
}
|
||||
container
|
||||
.bind<EndpointResolverInterface>(TYPES.ApiGateway_EndpointResolver)
|
||||
.toConstantValue(new EndpointResolver(isConfiguredForHomeServer))
|
||||
|
||||
logger.debug('Configuration complete')
|
||||
|
||||
return container
|
||||
|
||||
@@ -6,6 +6,7 @@ export const TYPES = {
|
||||
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'),
|
||||
@@ -24,10 +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'),
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ItemHttpRepresentation } from './ItemHttpRepresentation'
|
||||
import { ItemHashHttpRepresentation } from './ItemHashHttpRepresentation'
|
||||
|
||||
export interface ItemConflictHttpRepresentation {
|
||||
server_item?: ItemHttpRepresentation
|
||||
unsaved_item?: ItemHashHttpRepresentation
|
||||
type: string
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface NotificationHttpRepresentation {
|
||||
uuid: string
|
||||
user_uuid: string
|
||||
type: string
|
||||
payload: string
|
||||
created_at_timestamp: number
|
||||
updated_at_timestamp: number
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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[]
|
||||
}
|
||||
@@ -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.',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7,6 +7,7 @@ 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(
|
||||
@@ -23,6 +24,7 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||
private logger: Logger,
|
||||
private timer: TimerInterface,
|
||||
private sessionsClient: ISessionsClient,
|
||||
private gRPCSyncingServerServiceProxy: GRPCSyncingServerServiceProxy,
|
||||
) {}
|
||||
|
||||
async validateSession(headers: {
|
||||
@@ -37,6 +39,8 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||
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,
|
||||
@@ -81,16 +85,38 @@ export class GRPCServiceProxy 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)
|
||||
const requestIsUsingLatestApiVersions =
|
||||
payload !== undefined && typeof payload !== 'string' && 'api' in payload && payload.api === '20200115'
|
||||
|
||||
if (requestIsUsingLatestApiVersions && endpoint === 'items/sync') {
|
||||
const result = await this.gRPCSyncingServerServiceProxy.sync(request, response, payload)
|
||||
|
||||
response.status(result.status).send({
|
||||
meta: {
|
||||
auth: {
|
||||
userUuid: response.locals.user?.uuid,
|
||||
roles: response.locals.roles,
|
||||
},
|
||||
server: {
|
||||
filesServerUrl: this.filesServerUrl,
|
||||
},
|
||||
},
|
||||
data: result.data,
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -98,37 +124,31 @@ export class GRPCServiceProxy 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) {
|
||||
@@ -137,13 +157,13 @@ export class GRPCServiceProxy 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) {
|
||||
@@ -153,26 +173,17 @@ export class GRPCServiceProxy 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) {
|
||||
@@ -181,29 +192,23 @@ export class GRPCServiceProxy 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> {
|
||||
@@ -224,15 +229,10 @@ export class GRPCServiceProxy 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,
|
||||
@@ -249,9 +249,7 @@ export class GRPCServiceProxy 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
|
||||
@@ -263,18 +261,9 @@ export class GRPCServiceProxy 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
|
||||
@@ -284,8 +273,8 @@ export class GRPCServiceProxy 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)}`)
|
||||
@@ -316,19 +305,10 @@ export class GRPCServiceProxy 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
|
||||
@@ -360,16 +340,10 @@ export class GRPCServiceProxy 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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,52 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.174.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.173.2...@standardnotes/auth-server@1.174.0) (2023-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/server/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
|
||||
|
||||
## [1.173.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.173.1...@standardnotes/auth-server@1.173.2) (2023-11-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.173.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.173.0...@standardnotes/auth-server@1.173.1) (2023-11-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* error handling on gRPC ([#937](https://github.com/standardnotes/server/issues/937)) ([8f23c8a](https://github.com/standardnotes/server/commit/8f23c8ab3f03e9c23adfb31a33c5805492bc2f5b))
|
||||
|
||||
# [1.173.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.172.2...@standardnotes/auth-server@1.173.0) (2023-11-22)
|
||||
|
||||
### Features
|
||||
|
||||
* add verifiying if user has no items before mass deleting spam accounts ([#936](https://github.com/standardnotes/server/issues/936)) ([c11abe1](https://github.com/standardnotes/server/commit/c11abe1bd36de7c0fb9850c20a8157c066fa9379))
|
||||
|
||||
## [1.172.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.172.1...@standardnotes/auth-server@1.172.2) (2023-11-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* define grpc max message size ([bfef16c](https://github.com/standardnotes/server/commit/bfef16ce3757b57ea1cb0cb7417d6bc935a52321))
|
||||
|
||||
## [1.172.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.172.0...@standardnotes/auth-server@1.172.1) (2023-11-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* setting gzip as default compression on grpc calls ([#933](https://github.com/standardnotes/server/issues/933)) ([2dff6a2](https://github.com/standardnotes/server/commit/2dff6a2ed3d105ca65996d47321a811e22e25099))
|
||||
|
||||
# [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
|
||||
|
||||
@@ -81,9 +81,16 @@ void container.load().then((container) => {
|
||||
? +env.get('GRPC_KEEP_ALIVE_TIMEOUT', true)
|
||||
: 10_000
|
||||
|
||||
const grpcMaxMessageSize = env.get('GRPC_MAX_MESSAGE_SIZE', true)
|
||||
? +env.get('GRPC_MAX_MESSAGE_SIZE', true)
|
||||
: 1024 * 1024 * 50
|
||||
|
||||
const grpcServer = new grpc.Server({
|
||||
'grpc.keepalive_time_ms': grpcKeepAliveTimeout * 2,
|
||||
'grpc.keepalive_timeout_ms': grpcKeepAliveTimeout,
|
||||
'grpc.default_compression_algorithm': grpc.compressionAlgorithms.gzip,
|
||||
'grpc.max_receive_message_length': grpcMaxMessageSize,
|
||||
'grpc.max_send_message_length': grpcMaxMessageSize,
|
||||
})
|
||||
|
||||
const gRPCPort = env.get('GRPC_PORT', true) ? +env.get('GRPC_PORT', true) : 50051
|
||||
@@ -91,6 +98,7 @@ void container.load().then((container) => {
|
||||
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, {
|
||||
@@ -99,6 +107,8 @@ void container.load().then((container) => {
|
||||
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}`)
|
||||
@@ -113,6 +123,13 @@ void container.load().then((container) => {
|
||||
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}`)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/auth-server",
|
||||
"version": "1.170.0",
|
||||
"version": "1.174.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -10,7 +10,8 @@
|
||||
"author": "Karol Sójko <karol@standardnotes.com>",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -fr dist",
|
||||
@@ -20,7 +21,6 @@
|
||||
"lint:fix": "eslint . --fix --ext .ts",
|
||||
"pretest": "yarn lint && yarn build",
|
||||
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
|
||||
"grpc": "yarn node dist/bin/grpc.js",
|
||||
"start": "yarn node dist/bin/server.js",
|
||||
"worker": "yarn node dist/bin/worker.js",
|
||||
"cleanup": "yarn node dist/bin/cleanup.js",
|
||||
@@ -38,7 +38,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.10",
|
||||
"@grpc/grpc-js": "^1.9.11",
|
||||
"@simplewebauthn/server": "^8.1.1",
|
||||
"@simplewebauthn/typescript-types": "^8.0.0",
|
||||
"@standardnotes/api": "^1.26.26",
|
||||
|
||||
@@ -280,6 +280,7 @@ import { TriggerEmailBackupForAllUsers } from '../Domain/UseCase/TriggerEmailBac
|
||||
import { CSVFileReaderInterface } from '../Domain/CSV/CSVFileReaderInterface'
|
||||
import { S3CsvFileReader } from '../Infra/S3/S3CsvFileReader'
|
||||
import { DeleteAccountsFromCSVFile } from '../Domain/UseCase/DeleteAccountsFromCSVFile/DeleteAccountsFromCSVFile'
|
||||
import { AccountDeletionVerificationPassedEventHandler } from '../Domain/Handler/AccountDeletionVerificationPassedEventHandler'
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
constructor(private mode: 'server' | 'worker' = 'server') {}
|
||||
@@ -1274,7 +1275,9 @@ export class ContainerConfigLoader {
|
||||
.toConstantValue(
|
||||
new DeleteAccountsFromCSVFile(
|
||||
container.get<CSVFileReaderInterface>(TYPES.Auth_CSVFileReader),
|
||||
container.get<DeleteAccount>(TYPES.Auth_DeleteAccount),
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Auth_DomainEventPublisher),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Auth_DomainEventFactory),
|
||||
container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
@@ -1328,6 +1331,14 @@ export class ContainerConfigLoader {
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<AccountDeletionVerificationPassedEventHandler>(TYPES.Auth_AccountDeletionVerificationPassedEventHandler)
|
||||
.toConstantValue(
|
||||
new AccountDeletionVerificationPassedEventHandler(
|
||||
container.get<DeleteAccount>(TYPES.Auth_DeleteAccount),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<SubscriptionPurchasedEventHandler>(TYPES.Auth_SubscriptionPurchasedEventHandler)
|
||||
.toConstantValue(
|
||||
@@ -1516,6 +1527,7 @@ export class ContainerConfigLoader {
|
||||
|
||||
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
|
||||
['ACCOUNT_DELETION_REQUESTED', container.get(TYPES.Auth_AccountDeletionRequestedEventHandler)],
|
||||
['ACCOUNT_DELETION_VERIFICATION_PASSED', container.get(TYPES.Auth_AccountDeletionVerificationPassedEventHandler)],
|
||||
['SUBSCRIPTION_PURCHASED', container.get(TYPES.Auth_SubscriptionPurchasedEventHandler)],
|
||||
['SUBSCRIPTION_CANCELLED', container.get(TYPES.Auth_SubscriptionCancelledEventHandler)],
|
||||
['SUBSCRIPTION_RENEWED', container.get(TYPES.Auth_SubscriptionRenewedEventHandler)],
|
||||
|
||||
@@ -171,6 +171,7 @@ const TYPES = {
|
||||
Auth_DeleteAccountsFromCSVFile: Symbol.for('Auth_DeleteAccountsFromCSVFile'),
|
||||
// Handlers
|
||||
Auth_AccountDeletionRequestedEventHandler: Symbol.for('Auth_AccountDeletionRequestedEventHandler'),
|
||||
Auth_AccountDeletionVerificationPassedEventHandler: Symbol.for('Auth_AccountDeletionVerificationPassedEventHandler'),
|
||||
Auth_SubscriptionPurchasedEventHandler: Symbol.for('Auth_SubscriptionPurchasedEventHandler'),
|
||||
Auth_SubscriptionCancelledEventHandler: Symbol.for('Auth_SubscriptionCancelledEventHandler'),
|
||||
Auth_SubscriptionReassignedEventHandler: Symbol.for('Auth_SubscriptionReassignedEventHandler'),
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
StatisticPersistenceRequestedEvent,
|
||||
SessionCreatedEvent,
|
||||
SessionRefreshedEvent,
|
||||
AccountDeletionVerificationRequestedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { Predicate, PredicateVerificationResult } from '@standardnotes/predicates'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
@@ -33,6 +34,24 @@ import { KeyParamsData } from '@standardnotes/responses'
|
||||
export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||
constructor(@inject(TYPES.Auth_Timer) private timer: TimerInterface) {}
|
||||
|
||||
createAccountDeletionVerificationRequestedEvent(dto: {
|
||||
userUuid: string
|
||||
email: string
|
||||
}): AccountDeletionVerificationRequestedEvent {
|
||||
return {
|
||||
type: 'ACCOUNT_DELETION_VERIFICATION_REQUESTED',
|
||||
createdAt: this.timer.getUTCDate(),
|
||||
meta: {
|
||||
correlation: {
|
||||
userIdentifier: dto.userUuid,
|
||||
userIdentifierType: 'uuid',
|
||||
},
|
||||
origin: DomainEventService.Auth,
|
||||
},
|
||||
payload: dto,
|
||||
}
|
||||
}
|
||||
|
||||
createSessionCreatedEvent(dto: { userUuid: string }): SessionCreatedEvent {
|
||||
return {
|
||||
type: 'SESSION_CREATED',
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
StatisticPersistenceRequestedEvent,
|
||||
SessionCreatedEvent,
|
||||
SessionRefreshedEvent,
|
||||
AccountDeletionVerificationRequestedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { InviteeIdentifierType } from '../SharedSubscription/InviteeIdentifierType'
|
||||
import { KeyParamsData } from '@standardnotes/responses'
|
||||
@@ -56,6 +57,10 @@ export interface DomainEventFactoryInterface {
|
||||
ownerUuid: string
|
||||
}
|
||||
}): AccountDeletionRequestedEvent
|
||||
createAccountDeletionVerificationRequestedEvent(dto: {
|
||||
userUuid: string
|
||||
email: string
|
||||
}): AccountDeletionVerificationRequestedEvent
|
||||
createUserRolesChangedEvent(userUuid: string, email: string, currentRoles: string[]): UserRolesChangedEvent
|
||||
createUserEmailChangedEvent(userUuid: string, fromEmail: string, toEmail: string): UserEmailChangedEvent
|
||||
createUserDisabledSessionUserAgentLoggingEvent(dto: {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { AccountDeletionVerificationPassedEvent, DomainEventHandlerInterface } from '@standardnotes/domain-events'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { DeleteAccount } from '../UseCase/DeleteAccount/DeleteAccount'
|
||||
|
||||
export class AccountDeletionVerificationPassedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
private deleteAccount: DeleteAccount,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: AccountDeletionVerificationPassedEvent): Promise<void> {
|
||||
const result = await this.deleteAccount.execute({
|
||||
userUuid: event.payload.userUuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(`AccountDeletionVerificationPassedEventHandler failed: ${result.getError()}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,51 @@
|
||||
import { Logger } from 'winston'
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
import { AccountDeletionVerificationRequestedEvent, DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
|
||||
import { CSVFileReaderInterface } from '../../CSV/CSVFileReaderInterface'
|
||||
import { DeleteAccount } from '../DeleteAccount/DeleteAccount'
|
||||
import { DeleteAccountsFromCSVFile } from './DeleteAccountsFromCSVFile'
|
||||
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
|
||||
import { User } from '../../User/User'
|
||||
|
||||
describe('DeleteAccountsFromCSVFile', () => {
|
||||
let csvFileReader: CSVFileReaderInterface
|
||||
let deleteAccount: DeleteAccount
|
||||
let userRepository: UserRepositoryInterface
|
||||
let domainEventPublisher: DomainEventPublisherInterface
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
let logger: Logger
|
||||
|
||||
const createUseCase = () => new DeleteAccountsFromCSVFile(csvFileReader, deleteAccount, logger)
|
||||
const createUseCase = () =>
|
||||
new DeleteAccountsFromCSVFile(csvFileReader, domainEventPublisher, domainEventFactory, userRepository, logger)
|
||||
|
||||
beforeEach(() => {
|
||||
const user = {} as jest.Mocked<User>
|
||||
|
||||
csvFileReader = {} as jest.Mocked<CSVFileReaderInterface>
|
||||
csvFileReader.getValues = jest.fn().mockResolvedValue(Result.ok(['email1']))
|
||||
|
||||
deleteAccount = {} as jest.Mocked<DeleteAccount>
|
||||
deleteAccount.execute = jest.fn().mockResolvedValue(Result.ok(''))
|
||||
userRepository = {} as jest.Mocked<UserRepositoryInterface>
|
||||
userRepository.findAllByUsernameOrEmail = jest.fn().mockResolvedValue([user])
|
||||
|
||||
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
|
||||
domainEventPublisher.publish = jest.fn()
|
||||
|
||||
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
|
||||
domainEventFactory.createAccountDeletionVerificationRequestedEvent = jest
|
||||
.fn()
|
||||
.mockReturnValue({} as jest.Mocked<AccountDeletionVerificationRequestedEvent>)
|
||||
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.info = jest.fn()
|
||||
})
|
||||
|
||||
it('should delete accounts', async () => {
|
||||
it('should request account deletion verification', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({ fileName: 'test.csv', dryRun: false })
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
})
|
||||
|
||||
@@ -56,12 +74,12 @@ describe('DeleteAccountsFromCSVFile', () => {
|
||||
|
||||
const result = await useCase.execute({ fileName: 'test.csv', dryRun: true })
|
||||
|
||||
expect(deleteAccount.execute).not.toHaveBeenCalled()
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should return error if delete account fails', async () => {
|
||||
deleteAccount.execute = jest.fn().mockResolvedValue(Result.fail('Oops'))
|
||||
it('should return error username is invalid', async () => {
|
||||
csvFileReader.getValues = jest.fn().mockResolvedValue(Result.ok(['']))
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
@@ -69,4 +87,15 @@ describe('DeleteAccountsFromCSVFile', () => {
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should do nothing if users could not be found', async () => {
|
||||
userRepository.findAllByUsernameOrEmail = jest.fn().mockResolvedValue([])
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({ fileName: 'test.csv', dryRun: false })
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
|
||||
import { Result, UseCaseInterface, Username } from '@standardnotes/domain-core'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { DeleteAccount } from '../DeleteAccount/DeleteAccount'
|
||||
import { CSVFileReaderInterface } from '../../CSV/CSVFileReaderInterface'
|
||||
import { DeleteAccountsFromCSVFileDTO } from './DeleteAccountsFromCSVFileDTO'
|
||||
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
|
||||
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||
|
||||
export class DeleteAccountsFromCSVFile implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private csvFileReader: CSVFileReaderInterface,
|
||||
private deleteAccount: DeleteAccount,
|
||||
private domainEventPublisher: DomainEventPublisherInterface,
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
private userRepository: UserRepositoryInterface,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
@@ -33,12 +37,20 @@ export class DeleteAccountsFromCSVFile implements UseCaseInterface<void> {
|
||||
}
|
||||
|
||||
for (const email of emails) {
|
||||
const deleteAccountOrError = await this.deleteAccount.execute({
|
||||
username: email,
|
||||
})
|
||||
const usernameOrError = Username.create(email)
|
||||
if (usernameOrError.isFailed()) {
|
||||
return Result.fail(usernameOrError.getError())
|
||||
}
|
||||
const username = usernameOrError.getValue()
|
||||
|
||||
if (deleteAccountOrError.isFailed()) {
|
||||
return Result.fail(deleteAccountOrError.getError())
|
||||
const users = await this.userRepository.findAllByUsernameOrEmail(username)
|
||||
for (const user of users) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createAccountDeletionVerificationRequestedEvent({
|
||||
userUuid: user.uuid,
|
||||
email: user.email,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ export interface UserRepositoryInterface {
|
||||
streamTeam(memberEmail?: Email): Promise<ReadStream>
|
||||
findOneByUuid(uuid: Uuid): Promise<User | null>
|
||||
findOneByUsernameOrEmail(usernameOrEmail: Email | Username): Promise<User | null>
|
||||
findAllByUsernameOrEmail(usernameOrEmail: Email | Username): Promise<User[]>
|
||||
findAllCreatedBetween(dto: { start: Date; end: Date; offset: number; limit: number }): Promise<User[]>
|
||||
countAllCreatedBetween(start: Date, end: Date): Promise<number>
|
||||
save(user: User): Promise<User>
|
||||
|
||||
@@ -69,7 +69,13 @@ export class TypeORMUserRepository implements UserRepositoryInterface {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder('user')
|
||||
.where('user.email = :email', { email: usernameOrEmail.value })
|
||||
.cache(`user_email_${usernameOrEmail.value}`, 60000)
|
||||
.getOne()
|
||||
}
|
||||
|
||||
async findAllByUsernameOrEmail(usernameOrEmail: Email | Username): Promise<User[]> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder('user')
|
||||
.where('user.email = :email', { email: usernameOrEmail.value })
|
||||
.getMany()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,69 +6,88 @@ import { AuthorizationHeader, ISessionsServer, SessionValidationResponse } from
|
||||
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> {
|
||||
const authenticateRequestResponse = await this.authenticateRequest.execute({
|
||||
authorizationHeader: call.request.getBearerToken(),
|
||||
})
|
||||
try {
|
||||
this.logger.debug('[SessionsServer] Validating session via gRPC')
|
||||
|
||||
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(
|
||||
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)
|
||||
} catch (error) {
|
||||
this.logger.error(`[SessionsServer] Error validating session via gRPC: ${(error as Error).message}`)
|
||||
|
||||
callback(
|
||||
{
|
||||
code: Status.PERMISSION_DENIED,
|
||||
message: authenticateRequestResponse.errorMessage,
|
||||
name: authenticateRequestResponse.errorTag,
|
||||
metadata,
|
||||
code: Status.UNKNOWN,
|
||||
message: 'An error occurred while validating session',
|
||||
name: 'UNKNOWN',
|
||||
},
|
||||
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())
|
||||
|
||||
callback(null, response)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.52.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.51.0...@standardnotes/common@1.52.0) (2023-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/server/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
|
||||
|
||||
# [1.51.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.50.4...@standardnotes/common@1.51.0) (2023-09-26)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/common",
|
||||
"version": "1.51.0",
|
||||
"version": "1.52.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -13,7 +13,8 @@
|
||||
"dist/src/**/*.d.ts"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
|
||||
@@ -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.41.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.40.0...@standardnotes/domain-core@1.41.0) (2023-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/server/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
|
||||
|
||||
# [1.40.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.39.0...@standardnotes/domain-core@1.40.0) (2023-10-26)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-core",
|
||||
"version": "1.40.0",
|
||||
"version": "1.41.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -12,7 +12,8 @@
|
||||
"dist/src/**/*.d.ts"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"author": "Standard Notes",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.22.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.21.4...@standardnotes/domain-events-infra@1.22.0) (2023-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/server/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
|
||||
|
||||
## [1.21.4](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.21.3...@standardnotes/domain-events-infra@1.21.4) (2023-11-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.21.3](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.21.2...@standardnotes/domain-events-infra@1.21.3) (2023-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.21.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.21.1...@standardnotes/domain-events-infra@1.21.2) (2023-11-13)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events-infra",
|
||||
"version": "1.21.2",
|
||||
"version": "1.22.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -12,7 +12,8 @@
|
||||
"dist/src/**/*.d.ts"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"author": "Standard Notes",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -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.
|
||||
|
||||
# [2.137.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.136.0...@standardnotes/domain-events@2.137.0) (2023-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/server/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
|
||||
|
||||
# [2.136.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.135.0...@standardnotes/domain-events@2.136.0) (2023-11-23)
|
||||
|
||||
### Features
|
||||
|
||||
* **domain-events:** add email campaign send out requested event ([45bd009](https://github.com/standardnotes/server/commit/45bd00919c0062ac4bddd3f858f3ab89166f4e69))
|
||||
|
||||
# [2.135.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.134.2...@standardnotes/domain-events@2.135.0) (2023-11-22)
|
||||
|
||||
### Features
|
||||
|
||||
* add verifiying if user has no items before mass deleting spam accounts ([#936](https://github.com/standardnotes/server/issues/936)) ([c11abe1](https://github.com/standardnotes/server/commit/c11abe1bd36de7c0fb9850c20a8157c066fa9379))
|
||||
|
||||
## [2.134.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.134.1...@standardnotes/domain-events@2.134.2) (2023-11-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events",
|
||||
"version": "2.134.2",
|
||||
"version": "2.137.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -12,7 +12,8 @@
|
||||
"dist/src/**/*.d.ts"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"author": "Standard Notes",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
import { AccountDeletionVerificationPassedEventPayload } from './AccountDeletionVerificationPassedEventPayload'
|
||||
|
||||
export interface AccountDeletionVerificationPassedEvent extends DomainEventInterface {
|
||||
type: 'ACCOUNT_DELETION_VERIFICATION_PASSED'
|
||||
payload: AccountDeletionVerificationPassedEventPayload
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface AccountDeletionVerificationPassedEventPayload {
|
||||
userUuid: string
|
||||
email: string
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
import { AccountDeletionVerificationRequestedEventPayload } from './AccountDeletionVerificationRequestedEventPayload'
|
||||
|
||||
export interface AccountDeletionVerificationRequestedEvent extends DomainEventInterface {
|
||||
type: 'ACCOUNT_DELETION_VERIFICATION_REQUESTED'
|
||||
payload: AccountDeletionVerificationRequestedEventPayload
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface AccountDeletionVerificationRequestedEventPayload {
|
||||
userUuid: string
|
||||
email: string
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
import { EmailCampaignSendOutRequestedEventPayload } from './EmailCampaignSendOutRequestedEventPayload'
|
||||
|
||||
export interface EmailCampaignSendOutRequestedEvent extends DomainEventInterface {
|
||||
type: 'EMAIL_CAMPAIGN_SEND_OUT_REQUESTED'
|
||||
payload: EmailCampaignSendOutRequestedEventPayload
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface EmailCampaignSendOutRequestedEventPayload {
|
||||
limit: number
|
||||
page: number
|
||||
campaignFileName: string
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
export * from './Event/AccountDeletionRequestedEvent'
|
||||
export * from './Event/AccountDeletionRequestedEventPayload'
|
||||
export * from './Event/AccountDeletionVerificationPassedEvent'
|
||||
export * from './Event/AccountDeletionVerificationPassedEventPayload'
|
||||
export * from './Event/AccountDeletionVerificationRequestedEvent'
|
||||
export * from './Event/AccountDeletionVerificationRequestedEventPayload'
|
||||
export * from './Event/DiscountApplyRequestedEvent'
|
||||
export * from './Event/DiscountApplyRequestedEventPayload'
|
||||
export * from './Event/DiscountWithdrawRequestedEvent'
|
||||
@@ -12,6 +16,8 @@ export * from './Event/EmailBackupRequestedEvent'
|
||||
export * from './Event/EmailBackupRequestedEventPayload'
|
||||
export * from './Event/EmailBouncedEvent'
|
||||
export * from './Event/EmailBouncedEventPayload'
|
||||
export * from './Event/EmailCampaignSendOutRequestedEvent'
|
||||
export * from './Event/EmailCampaignSendOutRequestedEventPayload'
|
||||
export * from './Event/EmailRequestedEvent'
|
||||
export * from './Event/EmailRequestedEventPayload'
|
||||
export * from './Event/EmailSubscriptionUnsubscribedEvent'
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.36.0](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.35.2...@standardnotes/files-server@1.36.0) (2023-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/files/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
|
||||
|
||||
## [1.35.2](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.35.1...@standardnotes/files-server@1.35.2) (2023-11-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.35.1](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.35.0...@standardnotes/files-server@1.35.1) (2023-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
# [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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files-server",
|
||||
"version": "1.35.0",
|
||||
"version": "1.36.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -13,7 +13,8 @@
|
||||
],
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -fr dist",
|
||||
|
||||
@@ -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.3.0](https://github.com/standardnotes/server/compare/@standardnotes/grpc@1.2.0...@standardnotes/grpc@1.3.0) (2023-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/server/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
|
||||
|
||||
# [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
|
||||
|
||||
2
packages/grpc/lib/index.d.ts
vendored
2
packages/grpc/lib/index.d.ts
vendored
@@ -1,2 +1,4 @@
|
||||
export * from './auth_grpc_pb'
|
||||
export * from './auth_pb'
|
||||
export * from './sync_grpc_pb'
|
||||
export * from './sync_pb'
|
||||
|
||||
@@ -16,3 +16,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
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
41
packages/grpc/lib/sync_grpc_pb.d.ts
vendored
Normal 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;
|
||||
}
|
||||
44
packages/grpc/lib/sync_grpc_pb.js
Normal file
44
packages/grpc/lib/sync_grpc_pb.js
Normal 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
683
packages/grpc/lib/sync_pb.d.ts
vendored
Normal 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
5675
packages/grpc/lib/sync_pb.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/grpc",
|
||||
"version": "1.1.0",
|
||||
"version": "1.3.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -13,7 +13,8 @@
|
||||
"lib/**/*.d.ts"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
@@ -21,7 +22,7 @@
|
||||
"build": "tsc --build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^1.9.10",
|
||||
"@grpc/grpc-js": "^1.9.11",
|
||||
"google-protobuf": "^3.21.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
146
packages/grpc/proto/sync.proto
Normal file
146
packages/grpc/proto/sync.proto
Normal 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) {}
|
||||
}
|
||||
@@ -3,6 +3,56 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.22.0](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.21.11...@standardnotes/home-server@1.22.0) (2023-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/server/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
|
||||
|
||||
## [1.21.11](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.21.10...@standardnotes/home-server@1.21.11) (2023-11-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.21.10](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.21.9...@standardnotes/home-server@1.21.10) (2023-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.21.9](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.21.8...@standardnotes/home-server@1.21.9) (2023-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.21.8](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.21.7...@standardnotes/home-server@1.21.8) (2023-11-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.21.7](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.21.6...@standardnotes/home-server@1.21.7) (2023-11-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.21.6](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.21.5...@standardnotes/home-server@1.21.6) (2023-11-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.21.5](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.21.4...@standardnotes/home-server@1.21.5) (2023-11-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/home-server",
|
||||
"version": "1.21.0",
|
||||
"version": "1.22.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -10,7 +10,8 @@
|
||||
"repository": "git@github.com:standardnotes/server.git",
|
||||
"author": "Karol Sójko <karol@standardnotes.com>",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
|
||||
@@ -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.8.0](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.7.0...@standardnotes/predicates@1.8.0) (2023-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/server/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
|
||||
|
||||
# [1.7.0](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.6.11...@standardnotes/predicates@1.7.0) (2023-09-26)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/predicates",
|
||||
"version": "1.7.0",
|
||||
"version": "1.8.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -13,7 +13,8 @@
|
||||
"dist/src/**/*.d.ts"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.51.0](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.50.2...@standardnotes/revisions-server@1.51.0) (2023-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/server/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
|
||||
|
||||
## [1.50.2](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.50.1...@standardnotes/revisions-server@1.50.2) (2023-11-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.50.1](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.50.0...@standardnotes/revisions-server@1.50.1) (2023-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
# [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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/revisions-server",
|
||||
"version": "1.50.0",
|
||||
"version": "1.51.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -11,7 +11,8 @@
|
||||
"author": "Karol Sójko <karol@standardnotes.com>",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -fr dist",
|
||||
|
||||
@@ -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.27.5](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.27.4...@standardnotes/scheduler-server@1.27.5) (2023-11-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.27.4](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.27.3...@standardnotes/scheduler-server@1.27.4) (2023-11-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.27.3](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.27.2...@standardnotes/scheduler-server@1.27.3) (2023-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.27.2](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.27.1...@standardnotes/scheduler-server@1.27.2) (2023-11-13)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/scheduler-server",
|
||||
"version": "1.27.2",
|
||||
"version": "1.27.5",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -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.17.0](https://github.com/standardnotes/server/compare/@standardnotes/security@1.16.0...@standardnotes/security@1.17.0) (2023-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/server/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
|
||||
|
||||
# [1.16.0](https://github.com/standardnotes/server/compare/@standardnotes/security@1.15.0...@standardnotes/security@1.16.0) (2023-10-19)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/security",
|
||||
"version": "1.16.0",
|
||||
"version": "1.17.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -13,7 +13,8 @@
|
||||
"dist/src/**/*.d.ts"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
|
||||
@@ -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.23.0](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.22.0...@standardnotes/settings@1.23.0) (2023-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/server/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
|
||||
|
||||
# [1.22.0](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.47...@standardnotes/settings@1.22.0) (2023-10-26)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/settings",
|
||||
"version": "1.22.0",
|
||||
"version": "1.23.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -13,7 +13,8 @@
|
||||
"dist/src/**/*.d.ts"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
|
||||
@@ -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.16.0](https://github.com/standardnotes/server/compare/@standardnotes/sncrypto-node@1.15.6...@standardnotes/sncrypto-node@1.16.0) (2023-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/server/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
|
||||
|
||||
## [1.15.6](https://github.com/standardnotes/server/compare/@standardnotes/sncrypto-node@1.15.5...@standardnotes/sncrypto-node@1.15.6) (2023-09-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/sncrypto-node
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/sncrypto-node",
|
||||
"version": "1.15.6",
|
||||
"version": "1.16.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -14,7 +14,8 @@
|
||||
],
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -fr dist",
|
||||
|
||||
@@ -6,6 +6,7 @@ VERSION=development
|
||||
AUTH_JWT_SECRET=auth_jwt_secret
|
||||
|
||||
PORT=3000
|
||||
GRPC_PORT=50052
|
||||
|
||||
DB_HOST=db
|
||||
DB_REPLICA_HOST=db
|
||||
|
||||
@@ -3,6 +3,52 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.126.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.125.2...@standardnotes/syncing-server@1.126.0) (2023-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/syncing-server-js/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
|
||||
|
||||
## [1.125.2](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.125.1...@standardnotes/syncing-server@1.125.2) (2023-11-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
## [1.125.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.125.0...@standardnotes/syncing-server@1.125.1) (2023-11-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* error handling on gRPC ([#937](https://github.com/standardnotes/syncing-server-js/issues/937)) ([8f23c8a](https://github.com/standardnotes/syncing-server-js/commit/8f23c8ab3f03e9c23adfb31a33c5805492bc2f5b))
|
||||
|
||||
# [1.125.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.124.3...@standardnotes/syncing-server@1.125.0) (2023-11-22)
|
||||
|
||||
### Features
|
||||
|
||||
* add verifiying if user has no items before mass deleting spam accounts ([#936](https://github.com/standardnotes/syncing-server-js/issues/936)) ([c11abe1](https://github.com/standardnotes/syncing-server-js/commit/c11abe1bd36de7c0fb9850c20a8157c066fa9379))
|
||||
|
||||
## [1.124.3](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.124.2...@standardnotes/syncing-server@1.124.3) (2023-11-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** front load themes and user prefs as high priority load items ([#935](https://github.com/standardnotes/syncing-server-js/issues/935)) ([2200dca](https://github.com/standardnotes/syncing-server-js/commit/2200dca69dc809f45463e3338ad7a6fdc66f07d9))
|
||||
|
||||
## [1.124.2](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.124.1...@standardnotes/syncing-server@1.124.2) (2023-11-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* define grpc max message size ([bfef16c](https://github.com/standardnotes/syncing-server-js/commit/bfef16ce3757b57ea1cb0cb7417d6bc935a52321))
|
||||
|
||||
## [1.124.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.124.0...@standardnotes/syncing-server@1.124.1) (2023-11-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* setting gzip as default compression on grpc calls ([#933](https://github.com/standardnotes/syncing-server-js/issues/933)) ([2dff6a2](https://github.com/standardnotes/syncing-server-js/commit/2dff6a2ed3d105ca65996d47321a811e22e25099))
|
||||
|
||||
# [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
|
||||
|
||||
@@ -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) => {
|
||||
@@ -78,11 +85,60 @@ void container.load().then((container) => {
|
||||
|
||||
serverInstance.keepAliveTimeout = keepAliveTimeout
|
||||
|
||||
const grpcKeepAliveTimeout = env.get('GRPC_KEEP_ALIVE_TIMEOUT', true)
|
||||
? +env.get('GRPC_KEEP_ALIVE_TIMEOUT', true)
|
||||
: 10_000
|
||||
|
||||
const grpcMaxMessageSize = env.get('GRPC_MAX_MESSAGE_SIZE', true)
|
||||
? +env.get('GRPC_MAX_MESSAGE_SIZE', true)
|
||||
: 1024 * 1024 * 50
|
||||
|
||||
const grpcServer = new grpc.Server({
|
||||
'grpc.keepalive_time_ms': grpcKeepAliveTimeout * 2,
|
||||
'grpc.keepalive_timeout_ms': grpcKeepAliveTimeout,
|
||||
'grpc.default_compression_algorithm': grpc.compressionAlgorithms.gzip,
|
||||
'grpc.max_receive_message_length': grpcMaxMessageSize,
|
||||
'grpc.max_send_message_length': grpcMaxMessageSize,
|
||||
})
|
||||
|
||||
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}`)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/syncing-server",
|
||||
"version": "1.123.0",
|
||||
"version": "1.126.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -11,7 +11,8 @@
|
||||
"author": "Karol Sójko <karol@standardnotes.com>",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -fr dist",
|
||||
@@ -30,11 +31,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:*",
|
||||
|
||||
@@ -157,6 +157,10 @@ 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'
|
||||
import { AccountDeletionVerificationRequestedEventHandler } from '../Domain/Handler/AccountDeletionVerificationRequestedEventHandler'
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
private readonly DEFAULT_CONTENT_SIZE_TRANSFER_LIMIT = 10_000_000
|
||||
@@ -351,6 +355,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
|
||||
@@ -901,6 +908,18 @@ export class ContainerConfigLoader {
|
||||
container.get<Logger>(TYPES.Sync_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<AccountDeletionVerificationRequestedEventHandler>(
|
||||
TYPES.Sync_AccountDeletionVerificationRequestedEventHandler,
|
||||
)
|
||||
.toConstantValue(
|
||||
new AccountDeletionVerificationRequestedEventHandler(
|
||||
container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Sync_DomainEventPublisher),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),
|
||||
container.get<Logger>(TYPES.Sync_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<ItemRevisionCreationRequestedEventHandler>(TYPES.Sync_ItemRevisionCreationRequestedEventHandler)
|
||||
.toConstantValue(
|
||||
@@ -951,6 +970,10 @@ export class ContainerConfigLoader {
|
||||
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
|
||||
['DUPLICATE_ITEM_SYNCED', container.get(TYPES.Sync_DuplicateItemSyncedEventHandler)],
|
||||
['ACCOUNT_DELETION_REQUESTED', container.get(TYPES.Sync_AccountDeletionRequestedEventHandler)],
|
||||
[
|
||||
'ACCOUNT_DELETION_VERIFICATION_REQUESTED',
|
||||
container.get(TYPES.Sync_AccountDeletionVerificationRequestedEventHandler),
|
||||
],
|
||||
['ITEM_REVISION_CREATION_REQUESTED', container.get(TYPES.Sync_ItemRevisionCreationRequestedEventHandler)],
|
||||
[
|
||||
'SHARED_VAULT_FILE_UPLOADED',
|
||||
|
||||
@@ -85,6 +85,9 @@ const TYPES = {
|
||||
Sync_DumpItem: Symbol.for('Sync_DumpItem'),
|
||||
// Handlers
|
||||
Sync_AccountDeletionRequestedEventHandler: Symbol.for('Sync_AccountDeletionRequestedEventHandler'),
|
||||
Sync_AccountDeletionVerificationRequestedEventHandler: Symbol.for(
|
||||
'Sync_AccountDeletionVerificationRequestedEventHandler',
|
||||
),
|
||||
Sync_DuplicateItemSyncedEventHandler: Symbol.for('Sync_DuplicateItemSyncedEventHandler'),
|
||||
Sync_EmailBackupRequestedEventHandler: Symbol.for('Sync_EmailBackupRequestedEventHandler'),
|
||||
Sync_ItemRevisionCreationRequestedEventHandler: Symbol.for('Sync_ItemRevisionCreationRequestedEventHandler'),
|
||||
@@ -126,6 +129,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'),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* istanbul ignore file */
|
||||
import {
|
||||
AccountDeletionVerificationPassedEvent,
|
||||
DomainEventService,
|
||||
DuplicateItemSyncedEvent,
|
||||
EmailRequestedEvent,
|
||||
@@ -22,6 +23,24 @@ import { DomainEventFactoryInterface } from './DomainEventFactoryInterface'
|
||||
export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||
constructor(private timer: TimerInterface) {}
|
||||
|
||||
createAccountDeletionVerificationPassedEvent(dto: {
|
||||
userUuid: string
|
||||
email: string
|
||||
}): AccountDeletionVerificationPassedEvent {
|
||||
return {
|
||||
type: 'ACCOUNT_DELETION_VERIFICATION_PASSED',
|
||||
createdAt: this.timer.getUTCDate(),
|
||||
meta: {
|
||||
correlation: {
|
||||
userIdentifier: dto.userUuid,
|
||||
userIdentifierType: 'uuid',
|
||||
},
|
||||
origin: DomainEventService.SyncingServer,
|
||||
},
|
||||
payload: dto,
|
||||
}
|
||||
}
|
||||
|
||||
createUserDesignatedAsSurvivorInSharedVaultEvent(dto: {
|
||||
sharedVaultUuid: string
|
||||
userUuid: string
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
AccountDeletionVerificationPassedEvent,
|
||||
DuplicateItemSyncedEvent,
|
||||
EmailRequestedEvent,
|
||||
ItemDumpedEvent,
|
||||
@@ -93,4 +94,8 @@ export interface DomainEventFactoryInterface {
|
||||
userUuid: string
|
||||
timestamp: number
|
||||
}): UserDesignatedAsSurvivorInSharedVaultEvent
|
||||
createAccountDeletionVerificationPassedEvent(dto: {
|
||||
userUuid: string
|
||||
email: string
|
||||
}): AccountDeletionVerificationPassedEvent
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import {
|
||||
AccountDeletionVerificationRequestedEvent,
|
||||
DomainEventHandlerInterface,
|
||||
DomainEventPublisherInterface,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { ItemRepositoryInterface } from '../Item/ItemRepositoryInterface'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
|
||||
export class AccountDeletionVerificationRequestedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
private itemRepository: ItemRepositoryInterface,
|
||||
private domainEventPublisher: DomainEventPublisherInterface,
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: AccountDeletionVerificationRequestedEvent): Promise<void> {
|
||||
const userUuidOrError = Uuid.create(event.payload.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
this.logger.error(`AccountDeletionVerificationRequestedEventHandler failed: ${userUuidOrError.getError()}`)
|
||||
|
||||
return
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const itemsCount = await this.itemRepository.countAll({
|
||||
userUuid: userUuid.value,
|
||||
})
|
||||
|
||||
if (itemsCount !== 0) {
|
||||
this.logger.warn(
|
||||
`AccountDeletionVerificationRequestedEventHandler: User ${userUuid.value} has ${itemsCount} items and cannot be deleted.`,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createAccountDeletionVerificationPassedEvent({
|
||||
userUuid: userUuid.value,
|
||||
email: event.payload.email,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ export class SyncItems implements UseCaseInterface<SyncItemsResponse> {
|
||||
)
|
||||
const isSharedVaultExclusiveSync = dto.sharedVaultUuids && dto.sharedVaultUuids.length > 0
|
||||
if (this.isFirstSync(dto) && !isSharedVaultExclusiveSync) {
|
||||
retrievedItems = await this.frontLoadKeysItemsToTop(dto.userUuid, retrievedItems)
|
||||
retrievedItems = await this.frontLoadHighLoadingPriorityItemsToTop(dto.userUuid, retrievedItems)
|
||||
}
|
||||
|
||||
const sharedVaultsOrError = await this.getSharedVaultsUseCase.execute({
|
||||
@@ -139,19 +139,22 @@ export class SyncItems implements UseCaseInterface<SyncItemsResponse> {
|
||||
return retrievedItems.filter((item: Item) => syncConflictIds.indexOf(item.id.toString()) === -1)
|
||||
}
|
||||
|
||||
private async frontLoadKeysItemsToTop(userUuid: string, retrievedItems: Array<Item>): Promise<Array<Item>> {
|
||||
const itemsKeys = await this.itemRepository.findAll({
|
||||
private async frontLoadHighLoadingPriorityItemsToTop(
|
||||
userUuid: string,
|
||||
retrievedItems: Array<Item>,
|
||||
): Promise<Array<Item>> {
|
||||
const highPriorityItems = await this.itemRepository.findAll({
|
||||
userUuid,
|
||||
contentType: ContentType.TYPES.ItemsKey,
|
||||
contentType: [ContentType.TYPES.ItemsKey, ContentType.TYPES.UserPrefs, ContentType.TYPES.Theme],
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'ASC',
|
||||
})
|
||||
|
||||
const retrievedItemsIds: Array<string> = retrievedItems.map((item: Item) => item.id.toString())
|
||||
|
||||
itemsKeys.forEach((itemKey: Item) => {
|
||||
if (retrievedItemsIds.indexOf(itemKey.id.toString()) === -1) {
|
||||
retrievedItems.unshift(itemKey)
|
||||
highPriorityItems.forEach((highPriorityItem: Item) => {
|
||||
if (retrievedItemsIds.indexOf(highPriorityItem.id.toString()) === -1) {
|
||||
retrievedItems.unshift(highPriorityItem)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
128
packages/syncing-server/src/Infra/gRPC/SyncingServer.ts
Normal file
128
packages/syncing-server/src/Infra/gRPC/SyncingServer.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
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> {
|
||||
try {
|
||||
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)
|
||||
} catch (error) {
|
||||
this.logger.error(`[SyncingServer] Error syncing items via gRPC: ${(error as Error).message}`)
|
||||
|
||||
return callback(
|
||||
{
|
||||
code: Status.UNKNOWN,
|
||||
message: 'An error occurred while syncing items',
|
||||
name: 'UNKNOWN',
|
||||
},
|
||||
null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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.18.0](https://github.com/standardnotes/server/compare/@standardnotes/time@1.17.0...@standardnotes/time@1.18.0) (2023-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/server/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
|
||||
|
||||
# [1.17.0](https://github.com/standardnotes/server/compare/@standardnotes/time@1.16.0...@standardnotes/time@1.17.0) (2023-09-28)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/time",
|
||||
"version": "1.17.0",
|
||||
"version": "1.18.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -12,7 +12,8 @@
|
||||
"dist/src/**/*.d.ts"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"author": "Standard Notes",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user