Compare commits

...

45 Commits

Author SHA1 Message Date
standardci
f58f90667c chore(release): publish new version
- @standardnotes/analytics@2.24.2
 - @standardnotes/auth-server@1.119.4
 - @standardnotes/common@1.48.3
 - @standardnotes/home-server@1.11.12
 - @standardnotes/revisions-server@1.23.3
 - @standardnotes/syncing-server@1.44.3
 - @standardnotes/websockets-server@1.9.2
2023-06-28 11:17:34 +00:00
Mo
a388e1a802 chore: add new content types 2023-06-28 06:02:18 -05:00
standardci
8811d10a73 chore(release): publish new version
- @standardnotes/home-server@1.11.11
2023-06-22 12:10:36 +00:00
Karol Sójko
c7a394cd1a fix(home-server): destroy winston loggers upon server shutdown 2023-06-22 13:56:05 +02:00
standardci
b7615a7f2e chore(release): publish new version
- @standardnotes/home-server@1.11.10
2023-06-22 11:30:12 +00:00
Karol Sójko
1ca70c1e50 fix(home-server): pass the log stream callback before loggers are created 2023-06-22 13:15:33 +02:00
standardci
253cbb1d0c chore(release): publish new version
- @standardnotes/home-server@1.11.9
2023-06-22 10:52:11 +00:00
Karol Sójko
e38a16404c fix(home-server): listening on log stream 2023-06-22 12:37:19 +02:00
Karol Sójko
f17a1f875c fix(home-server): passthrough stream for loggers 2023-06-22 12:33:23 +02:00
standardci
2237e0f5df chore(release): publish new version
- @standardnotes/api-gateway@1.64.2
 - @standardnotes/auth-server@1.119.3
 - @standardnotes/files-server@1.18.3
 - @standardnotes/home-server@1.11.8
 - @standardnotes/revisions-server@1.23.2
 - @standardnotes/syncing-server@1.44.2
2023-06-22 10:05:28 +00:00
Karol Sójko
0df471585f fix(home-server): add debug logs about container initalizations 2023-06-22 11:50:59 +02:00
standardci
95aac1a7bf chore(release): publish new version
- @standardnotes/home-server@1.11.7
2023-06-22 08:23:45 +00:00
Karol Sójko
c078bc958d fix(home-server): add default log level to overrides 2023-06-22 10:06:54 +02:00
Karol Sójko
49c27924ea fix(home-server): add log leve information to starting the home server information 2023-06-22 09:08:37 +02:00
standardci
c9dd8e7338 chore(release): publish new version
- @standardnotes/home-server@1.11.6
2023-06-16 09:44:09 +00:00
Karol Sójko
5ef90cc75b fix(home-server): unref the server instance when stopping the home server 2023-06-16 11:30:04 +02:00
standardci
063c61d96c chore(release): publish new version
- @standardnotes/auth-server@1.119.2
 - @standardnotes/home-server@1.11.5
 - @standardnotes/revisions-server@1.23.1
 - @standardnotes/syncing-server@1.44.1
2023-06-14 06:19:56 +00:00
Karol Sójko
0cb5e36b20 fix(home-server): env var determining the sqlite database location (#626) 2023-06-14 08:05:48 +02:00
standardci
319bab5b34 chore(release): publish new version
- @standardnotes/home-server@1.11.4
2023-06-13 12:19:56 +00:00
Karol Sójko
90a4f2111f fix(home-server): encapsulate starting the server with a result return 2023-06-13 14:03:39 +02:00
standardci
3aba202970 chore(release): publish new version
- @standardnotes/files-server@1.18.2
 - @standardnotes/home-server@1.11.3
2023-06-12 10:09:28 +00:00
Karol Sójko
c8974b7fa2 fix(home-server): accept application/octet-stream requests for files (#625)
* fix(home-server): accept application/octet-stream requests for files

* fix(files): check for empty chunks
2023-06-12 11:55:18 +02:00
standardci
3654a19586 chore(release): publish new version
- @standardnotes/files-server@1.18.1
 - @standardnotes/home-server@1.11.2
2023-06-09 11:51:44 +00:00
Karol Sójko
5f0929c1aa fix(files): add debug logs for checking chunks upon finishing upload session 2023-06-09 13:37:30 +02:00
standardci
c0fa83bce6 chore(release): publish new version
- @standardnotes/api-gateway@1.64.1
 - @standardnotes/auth-server@1.119.1
 - @standardnotes/home-server@1.11.1
2023-06-09 11:14:28 +00:00
Karol Sójko
c201ee42a0 fix(home-server): add default value for valet token ttl 2023-06-09 12:56:57 +02:00
Karol Sójko
e6a4cc3098 fix(api-gateway): direct call service proxy to return 400 responses instead of throwing errors 2023-06-09 12:52:58 +02:00
standardci
39f2fe2ba1 chore(release): publish new version
- @standardnotes/auth-server@1.119.0
 - @standardnotes/home-server@1.11.0
2023-06-09 06:13:41 +00:00
Karol Sójko
72ce190996 feat(home-server): add activating premium features (#624) 2023-06-09 07:59:07 +02:00
standardci
527dd1b61b chore(release): publish new version
- @standardnotes/auth-server@1.118.0
 - @standardnotes/home-server@1.10.0
 - @standardnotes/revisions-server@1.23.0
 - @standardnotes/syncing-server@1.44.0
2023-06-07 08:53:34 +00:00
Karol Sójko
af8feaadfe feat: configurable path for uploads and db (#623)
* fix(home-server): passing the database path

* fix(home-server): passing the file upload path
2023-06-07 10:39:33 +02:00
standardci
3164f76662 chore(release): publish new version
- @standardnotes/api-gateway@1.64.0
 - @standardnotes/auth-server@1.117.0
 - @standardnotes/files-server@1.18.0
 - @standardnotes/home-server@1.9.0
 - @standardnotes/revisions-server@1.22.0
 - @standardnotes/syncing-server@1.43.0
2023-06-05 10:01:28 +00:00
Karol Sójko
d6e531d4b6 feat(home-server): allow running the home server with a mysql and redis configuration (#622)
* feat(home-server): allow running the home server with a mysql and redis configuration

* fix(files): config for file uploader

* fix(files): if condition on fs file uploader bootstrap
2023-06-05 11:47:10 +02:00
standardci
af76878dad chore(release): publish new version
- @standardnotes/analytics@2.24.1
 - @standardnotes/api-gateway@1.63.2
 - @standardnotes/auth-server@1.116.2
 - @standardnotes/event-store@1.10.1
 - @standardnotes/files-server@1.17.1
 - @standardnotes/home-server@1.8.5
 - @standardnotes/revisions-server@1.21.3
 - @standardnotes/scheduler-server@1.20.1
 - @standardnotes/syncing-server@1.42.2
 - @standardnotes/websockets-server@1.9.1
2023-06-02 12:30:06 +00:00
Karol Sójko
28cce39fe7 fix(home-server): linter issues 2023-06-02 14:15:39 +02:00
Karol Sójko
a8b806af08 fix(home-server): streaming logs 2023-06-02 14:10:20 +02:00
standardci
fa0b0294b4 chore(release): publish new version
- @standardnotes/home-server@1.8.4
2023-06-02 11:41:59 +00:00
Karol Sójko
58ab410b0a fix(home-server): remove redundant restart method 2023-06-02 13:27:45 +02:00
standardci
51c8b20506 chore(release): publish new version
- @standardnotes/api-gateway@1.63.1
 - @standardnotes/home-server@1.8.3
 - @standardnotes/revisions-server@1.21.2
2023-06-02 11:26:26 +00:00
Karol Sójko
1e62a3760e fix(home-server): displaying the port on which it is running 2023-06-02 13:08:23 +02:00
Karol Sójko
2f569d4104 fix(home-server): add default for VERSION environment variable 2023-06-02 12:52:51 +02:00
standardci
f23e444ed0 chore(release): publish new version
- @standardnotes/home-server@1.8.2
2023-06-02 10:43:32 +00:00
Karol Sójko
e6e9a32f03 fix(home-server): default configuration variables 2023-06-02 12:29:24 +02:00
standardci
8237df33a7 chore(release): publish new version
- @standardnotes/auth-server@1.116.1
 - @standardnotes/home-server@1.8.1
 - @standardnotes/revisions-server@1.21.1
 - @standardnotes/syncing-server@1.42.1
2023-06-02 09:37:30 +00:00
Karol Sójko
624b574013 fix: initializing data source with already configured environment 2023-06-02 11:20:34 +02:00
58 changed files with 1215 additions and 478 deletions

1
.github/ci.env vendored
View File

@@ -4,6 +4,7 @@ DB_USERNAME=std_notes_user
DB_PASSWORD=changeme123
DB_DATABASE=standard_notes_db
DB_PORT=3306
DB_SQLITE_DATABASE_PATH=standard_notes_db
REDIS_PORT=6379
REDIS_HOST=cache
AUTH_SERVER_ACCESS_TOKEN_AGE=4

View File

@@ -54,6 +54,11 @@ jobs:
e2e-home-server:
name: (Home Server) E2E Test Suite
strategy:
matrix:
db_type: [mysql, sqlite]
cache_type: [redis, memory]
runs-on: ubuntu-latest
services:
@@ -61,6 +66,19 @@ jobs:
image: standardnotes/snjs:${{ inputs.snjs_image_tag }}
ports:
- 9001:9001
cache:
image: redis
ports:
- 6379:6379
db:
image: mysql
ports:
- 3306:3306
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: standardnotes
MYSQL_USER: standardnotes
MYSQL_PASSWORD: standardnotes
steps:
- uses: actions/checkout@v3
@@ -90,6 +108,13 @@ jobs:
echo "ACCESS_TOKEN_AGE=4" >> packages/home-server/.env
echo "REFRESH_TOKEN_AGE=7" >> packages/home-server/.env
echo "REVISIONS_FREQUENCY=5" >> packages/home-server/.env
echo "DB_HOST=db" >> packages/home-server/.env
echo "DB_PORT=3306" >> packages/home-server/.env
echo "DB_USERNAME=standardnotes" >> packages/home-server/.env
echo "DB_PASSWORD=standardnotes" >> packages/home-server/.env
echo "DB_TYPE=${{ matrix.db_type }}" >> packages/home-server/.env
echo "REDIS_URL=redis://cache" >> packages/home-server/.env
echo "CACHE_TYPE=${{ matrix.cache_type }}" >> packages/home-server/.env
- name: Run Server
run: nohup yarn workspace @standardnotes/home-server start &

View File

@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.24.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.1...@standardnotes/analytics@2.24.2) (2023-06-28)
**Note:** Version bump only for package @standardnotes/analytics
## [2.24.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.0...@standardnotes/analytics@2.24.1) (2023-06-02)
### Bug Fixes
* **home-server:** streaming logs ([a8b806a](https://github.com/standardnotes/server/commit/a8b806af084b3e3fe8707ff0cb041a74042ee049))
# [2.24.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.23.7...@standardnotes/analytics@2.24.0) (2023-06-02)
### Features

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/analytics",
"version": "2.24.0",
"version": "2.24.2",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -88,9 +88,9 @@ export class ContainerConfigLoader {
}
const logger = winston.createLogger({
level: env.get('LOG_LEVEL') || 'info',
level: env.get('LOG_LEVEL', true) || 'info',
format: winston.format.combine(...winstonFormatters),
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL') || 'info' })],
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL', true) || 'info' })],
})
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(logger)

View File

@@ -1,3 +1,4 @@
MODE=microservice # microservice | home-server
LOG_LEVEL=debug
NODE_ENV=development
VERSION=development

View File

@@ -3,6 +3,36 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.64.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.64.1...@standardnotes/api-gateway@1.64.2) (2023-06-22)
### Bug Fixes
* **home-server:** add debug logs about container initalizations ([0df4715](https://github.com/standardnotes/api-gateway/commit/0df471585fd5b4626ec2972f3b9a3e33b2830e65))
## [1.64.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.64.0...@standardnotes/api-gateway@1.64.1) (2023-06-09)
### Bug Fixes
* **api-gateway:** direct call service proxy to return 400 responses instead of throwing errors ([e6a4cc3](https://github.com/standardnotes/api-gateway/commit/e6a4cc3098bdf84fc9d48ed0d9098ebb52afb0e7))
# [1.64.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.63.2...@standardnotes/api-gateway@1.64.0) (2023-06-05)
### Features
* **home-server:** allow running the home server with a mysql and redis configuration ([#622](https://github.com/standardnotes/api-gateway/issues/622)) ([d6e531d](https://github.com/standardnotes/api-gateway/commit/d6e531d4b6c1c80a894f6d7ec93632595268dd64))
## [1.63.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.63.1...@standardnotes/api-gateway@1.63.2) (2023-06-02)
### Bug Fixes
* **home-server:** streaming logs ([a8b806a](https://github.com/standardnotes/api-gateway/commit/a8b806af084b3e3fe8707ff0cb041a74042ee049))
## [1.63.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.63.0...@standardnotes/api-gateway@1.63.1) (2023-06-02)
### Bug Fixes
* **home-server:** add default for VERSION environment variable ([2f569d4](https://github.com/standardnotes/api-gateway/commit/2f569d41047a802eb72ef1a3618ffe4df28a709c))
# [1.63.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.62.4...@standardnotes/api-gateway@1.63.0) (2023-06-02)
### Features

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api-gateway",
"version": "1.63.0",
"version": "1.64.2",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -34,7 +34,8 @@ export class ContainerConfigLoader {
const container = new Container()
const isConfiguredForHomeServer = env.get('CACHE_TYPE') === 'memory'
const isConfiguredForHomeServer = env.get('MODE', true) === 'home-server'
const isConfiguredForInMemoryCache = env.get('CACHE_TYPE', true) === 'memory'
const winstonFormatters = [winston.format.splat(), winston.format.json()]
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
@@ -45,19 +46,20 @@ export class ContainerConfigLoader {
winstonFormatters.push(newrelicWinstonFormatter())
}
let logger: winston.Logger
if (configuration?.logger) {
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(configuration.logger as winston.Logger)
logger = configuration.logger as winston.Logger
} else {
const logger = winston.createLogger({
level: env.get('LOG_LEVEL') || 'info',
logger = winston.createLogger({
level: env.get('LOG_LEVEL', true) || 'info',
format: winston.format.combine(...winstonFormatters),
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL') || 'info' })],
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL', true) || 'info' })],
defaultMeta: { service: 'api-gateway' },
})
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(logger)
}
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(logger)
if (!isConfiguredForHomeServer) {
if (!isConfiguredForInMemoryCache) {
const redisUrl = env.get('REDIS_URL')
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
let redis
@@ -83,7 +85,7 @@ export class ContainerConfigLoader {
container
.bind(TYPES.HTTP_CALL_TIMEOUT)
.toConstantValue(env.get('HTTP_CALL_TIMEOUT', true) ? +env.get('HTTP_CALL_TIMEOUT', true) : 60_000)
container.bind(TYPES.VERSION).toConstantValue(env.get('VERSION'))
container.bind(TYPES.VERSION).toConstantValue(env.get('VERSION', true) ?? 'development')
container.bind(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL).toConstantValue(+env.get('CROSS_SERVICE_TOKEN_CACHE_TTL', true))
// Middleware
@@ -124,6 +126,8 @@ export class ContainerConfigLoader {
.bind<EndpointResolverInterface>(TYPES.EndpointResolver)
.toConstantValue(new EndpointResolver(isConfiguredForHomeServer))
logger.debug('Configuration complete')
return container
}
}

View File

@@ -1,7 +1,7 @@
import { Request, Response } from 'express'
import { ServiceContainerInterface, ServiceIdentifier } from '@standardnotes/domain-core'
import { ServiceProxyInterface } from '../Http/ServiceProxyInterface'
import { ServiceContainerInterface, ServiceIdentifier } from '@standardnotes/domain-core'
export class DirectCallServiceProxy implements ServiceProxyInterface {
constructor(private serviceContainer: ServiceContainerInterface, private filesServerUrl: string) {}
@@ -34,8 +34,12 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
}
}
async callEmailServer(_request: Request, _response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
throw new Error('Email server is not available.')
async callEmailServer(_request: Request, response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
response.status(400).send({
error: {
message: 'Email server is not available.',
},
})
}
async callAuthServer(request: never, response: never, endpointOrMethodIdentifier: string): Promise<void> {
@@ -54,10 +58,14 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
async callAuthServerWithLegacyFormat(
_request: Request,
_response: Response,
response: Response,
_endpointOrMethodIdentifier: string,
): Promise<void> {
throw new Error('Legacy auth endpoints are no longer available.')
response.status(400).send({
error: {
message: 'Legacy auth endpoints are no longer available.',
},
})
}
async callRevisionsServer(request: never, response: never, endpointOrMethodIdentifier: string): Promise<void> {
@@ -92,22 +100,30 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
async callLegacySyncingServer(
_request: Request,
_response: Response,
response: Response,
_endpointOrMethodIdentifier: string,
): Promise<void> {
throw new Error('Legacy syncing server endpoints are no longer available.')
response.status(400).send({
error: {
message: 'Legacy syncing server endpoints are no longer available.',
},
})
}
async callPaymentsServer(_request: Request, _response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
throw new Error('Payments server is not available.')
async callPaymentsServer(_request: Request, response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
response.status(400).send({
error: {
message: 'Payments server is not available.',
},
})
}
async callWebSocketServer(
_request: Request,
_response: Response,
_endpointOrMethodIdentifier: string,
): Promise<void> {
throw new Error('Websockets server is not available.')
async callWebSocketServer(_request: Request, response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
response.status(400).send({
error: {
message: 'Websockets server is not available.',
},
})
}
private sendDecoratedResponse(

View File

@@ -1,3 +1,4 @@
MODE=microservice # microservice | home-server
LOG_LEVEL=debug
NODE_ENV=development
VERSION=development
@@ -20,8 +21,10 @@ DB_USERNAME=auth
DB_PASSWORD=changeme123
DB_DATABASE=auth
DB_DEBUG_LEVEL=all # "all" | "query" | "schema" | "error" | "warn" | "info" | "log" | "migration"
DB_TYPE=mysql
REDIS_URL=redis://cache
CACHE_TYPE=redis
DISABLE_USER_REGISTRATION=false

View File

@@ -3,6 +3,58 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.119.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.119.3...@standardnotes/auth-server@1.119.4) (2023-06-28)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.119.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.119.2...@standardnotes/auth-server@1.119.3) (2023-06-22)
### Bug Fixes
* **home-server:** add debug logs about container initalizations ([0df4715](https://github.com/standardnotes/server/commit/0df471585fd5b4626ec2972f3b9a3e33b2830e65))
## [1.119.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.119.1...@standardnotes/auth-server@1.119.2) (2023-06-14)
### Bug Fixes
* **home-server:** env var determining the sqlite database location ([#626](https://github.com/standardnotes/server/issues/626)) ([0cb5e36](https://github.com/standardnotes/server/commit/0cb5e36b20d9b095ea0edbcd877387e6c0069856))
## [1.119.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.119.0...@standardnotes/auth-server@1.119.1) (2023-06-09)
### Bug Fixes
* **home-server:** add default value for valet token ttl ([c201ee4](https://github.com/standardnotes/server/commit/c201ee42a00d9e5402afea2f2c5848a362c1529e))
# [1.119.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.118.0...@standardnotes/auth-server@1.119.0) (2023-06-09)
### Features
* **home-server:** add activating premium features ([#624](https://github.com/standardnotes/server/issues/624)) ([72ce190](https://github.com/standardnotes/server/commit/72ce1909960fbd2ec6a47b8dbdfbe53a4f10e776))
# [1.118.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.117.0...@standardnotes/auth-server@1.118.0) (2023-06-07)
### Features
* configurable path for uploads and db ([#623](https://github.com/standardnotes/server/issues/623)) ([af8feaa](https://github.com/standardnotes/server/commit/af8feaadfe2dd58baab4cca217d6307b4a221326))
# [1.117.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.116.2...@standardnotes/auth-server@1.117.0) (2023-06-05)
### Features
* **home-server:** allow running the home server with a mysql and redis configuration ([#622](https://github.com/standardnotes/server/issues/622)) ([d6e531d](https://github.com/standardnotes/server/commit/d6e531d4b6c1c80a894f6d7ec93632595268dd64))
## [1.116.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.116.1...@standardnotes/auth-server@1.116.2) (2023-06-02)
### Bug Fixes
* **home-server:** streaming logs ([a8b806a](https://github.com/standardnotes/server/commit/a8b806af084b3e3fe8707ff0cb041a74042ee049))
## [1.116.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.116.0...@standardnotes/auth-server@1.116.1) (2023-06-02)
### Bug Fixes
* initializing data source with already configured environment ([624b574](https://github.com/standardnotes/server/commit/624b574013157e9e044d4a8ed53cadb7fcc567ae))
# [1.116.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.115.5...@standardnotes/auth-server@1.116.0) (2023-06-02)
### Features

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.116.0",
"version": "1.119.4",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -0,0 +1,5 @@
import { Result, ServiceInterface } from '@standardnotes/domain-core'
export interface AuthServiceInterface extends ServiceInterface {
activatePremiumFeatures(username: string): Promise<Result<string>>
}

View File

@@ -251,6 +251,7 @@ import { HomeServerValetTokenController } from '../Infra/InversifyExpressUtils/H
import { HomeServerWebSocketsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerWebSocketsController'
import { HomeServerSessionsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerSessionsController'
import { Transform } from 'stream'
import { ActivatePremiumFeatures } from '../Domain/UseCase/ActivatePremiumFeatures/ActivatePremiumFeatures'
export class ContainerConfigLoader {
async load(configuration?: {
@@ -267,11 +268,37 @@ export class ContainerConfigLoader {
const container = new Container()
await AppDataSource.initialize()
const winstonFormatters = [winston.format.splat(), winston.format.json()]
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
await import('newrelic')
// eslint-disable-next-line @typescript-eslint/no-var-requires
const newrelicFormatter = require('@newrelic/winston-enricher')
const newrelicWinstonFormatter = newrelicFormatter(winston)
winstonFormatters.push(newrelicWinstonFormatter())
}
const isConfiguredForHomeServer = env.get('DB_TYPE') === 'sqlite'
let logger: winston.Logger
if (configuration?.logger) {
logger = configuration.logger as winston.Logger
} else {
logger = winston.createLogger({
level: env.get('LOG_LEVEL', true) || 'info',
format: winston.format.combine(...winstonFormatters),
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL', true) || 'info' })],
defaultMeta: { service: 'auth' },
})
}
container.bind<winston.Logger>(TYPES.Auth_Logger).toConstantValue(logger)
if (!isConfiguredForHomeServer) {
const appDataSource = new AppDataSource(env)
await appDataSource.initialize()
logger.debug('Database initialized')
const isConfiguredForHomeServer = env.get('MODE', true) === 'home-server'
const isConfiguredForInMemoryCache = env.get('CACHE_TYPE', true) === 'memory'
if (!isConfiguredForInMemoryCache) {
const redisUrl = env.get('REDIS_URL')
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
let redis
@@ -284,27 +311,6 @@ export class ContainerConfigLoader {
container.bind(TYPES.Auth_Redis).toConstantValue(redis)
}
const winstonFormatters = [winston.format.splat(), winston.format.json()]
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
await import('newrelic')
// eslint-disable-next-line @typescript-eslint/no-var-requires
const newrelicFormatter = require('@newrelic/winston-enricher')
const newrelicWinstonFormatter = newrelicFormatter(winston)
winstonFormatters.push(newrelicWinstonFormatter())
}
if (configuration?.logger) {
container.bind<winston.Logger>(TYPES.Auth_Logger).toConstantValue(configuration.logger as winston.Logger)
} else {
const logger = winston.createLogger({
level: env.get('LOG_LEVEL') || 'info',
format: winston.format.combine(...winstonFormatters),
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL') || 'info' })],
defaultMeta: { service: 'auth' },
})
container.bind<winston.Logger>(TYPES.Auth_Logger).toConstantValue(logger)
}
container.bind<TimerInterface>(TYPES.Auth_Timer).toConstantValue(new Timer())
if (!isConfiguredForHomeServer) {
@@ -359,42 +365,42 @@ export class ContainerConfigLoader {
// ORM
container
.bind<Repository<OfflineSetting>>(TYPES.Auth_ORMOfflineSettingRepository)
.toConstantValue(AppDataSource.getRepository(OfflineSetting))
.toConstantValue(appDataSource.getRepository(OfflineSetting))
container
.bind<Repository<OfflineUserSubscription>>(TYPES.Auth_ORMOfflineUserSubscriptionRepository)
.toConstantValue(AppDataSource.getRepository(OfflineUserSubscription))
.toConstantValue(appDataSource.getRepository(OfflineUserSubscription))
container
.bind<Repository<RevokedSession>>(TYPES.Auth_ORMRevokedSessionRepository)
.toConstantValue(AppDataSource.getRepository(RevokedSession))
container.bind<Repository<Role>>(TYPES.Auth_ORMRoleRepository).toConstantValue(AppDataSource.getRepository(Role))
.toConstantValue(appDataSource.getRepository(RevokedSession))
container.bind<Repository<Role>>(TYPES.Auth_ORMRoleRepository).toConstantValue(appDataSource.getRepository(Role))
container
.bind<Repository<Session>>(TYPES.Auth_ORMSessionRepository)
.toConstantValue(AppDataSource.getRepository(Session))
.toConstantValue(appDataSource.getRepository(Session))
container
.bind<Repository<Setting>>(TYPES.Auth_ORMSettingRepository)
.toConstantValue(AppDataSource.getRepository(Setting))
.toConstantValue(appDataSource.getRepository(Setting))
container
.bind<Repository<SharedSubscriptionInvitation>>(TYPES.Auth_ORMSharedSubscriptionInvitationRepository)
.toConstantValue(AppDataSource.getRepository(SharedSubscriptionInvitation))
.toConstantValue(appDataSource.getRepository(SharedSubscriptionInvitation))
container
.bind<Repository<SubscriptionSetting>>(TYPES.Auth_ORMSubscriptionSettingRepository)
.toConstantValue(AppDataSource.getRepository(SubscriptionSetting))
container.bind<Repository<User>>(TYPES.Auth_ORMUserRepository).toConstantValue(AppDataSource.getRepository(User))
.toConstantValue(appDataSource.getRepository(SubscriptionSetting))
container.bind<Repository<User>>(TYPES.Auth_ORMUserRepository).toConstantValue(appDataSource.getRepository(User))
container
.bind<Repository<UserSubscription>>(TYPES.Auth_ORMUserSubscriptionRepository)
.toConstantValue(AppDataSource.getRepository(UserSubscription))
.toConstantValue(appDataSource.getRepository(UserSubscription))
container
.bind<Repository<TypeORMSessionTrace>>(TYPES.Auth_ORMSessionTraceRepository)
.toConstantValue(AppDataSource.getRepository(TypeORMSessionTrace))
.toConstantValue(appDataSource.getRepository(TypeORMSessionTrace))
container
.bind<Repository<TypeORMAuthenticator>>(TYPES.Auth_ORMAuthenticatorRepository)
.toConstantValue(AppDataSource.getRepository(TypeORMAuthenticator))
.toConstantValue(appDataSource.getRepository(TypeORMAuthenticator))
container
.bind<Repository<TypeORMAuthenticatorChallenge>>(TYPES.Auth_ORMAuthenticatorChallengeRepository)
.toConstantValue(AppDataSource.getRepository(TypeORMAuthenticatorChallenge))
.toConstantValue(appDataSource.getRepository(TypeORMAuthenticatorChallenge))
container
.bind<Repository<TypeORMCacheEntry>>(TYPES.Auth_ORMCacheEntryRepository)
.toConstantValue(AppDataSource.getRepository(TypeORMCacheEntry))
.toConstantValue(appDataSource.getRepository(TypeORMCacheEntry))
// Repositories
container.bind<SessionRepositoryInterface>(TYPES.Auth_SessionRepository).to(TypeORMSessionRepository)
@@ -487,7 +493,9 @@ export class ContainerConfigLoader {
.bind(TYPES.Auth_AUTH_JWT_TTL)
.toConstantValue(env.get('AUTH_JWT_TTL', true) ? +env.get('AUTH_JWT_TTL') : 60_000)
container.bind(TYPES.Auth_VALET_TOKEN_SECRET).toConstantValue(env.get('VALET_TOKEN_SECRET', true))
container.bind(TYPES.Auth_VALET_TOKEN_TTL).toConstantValue(+env.get('VALET_TOKEN_TTL', true))
container
.bind(TYPES.Auth_VALET_TOKEN_TTL)
.toConstantValue(env.get('VALET_TOKEN_TTL', true) ? +env.get('VALET_TOKEN_TTL', true) : 7200)
container
.bind(TYPES.Auth_WEB_SOCKET_CONNECTION_TOKEN_SECRET)
.toConstantValue(env.get('WEB_SOCKET_CONNECTION_TOKEN_SECRET', true))
@@ -549,7 +557,16 @@ export class ContainerConfigLoader {
.bind(TYPES.Auth_READONLY_USERS)
.toConstantValue(env.get('READONLY_USERS', true) ? env.get('READONLY_USERS', true).split(',') : [])
if (isConfiguredForHomeServer) {
if (isConfiguredForInMemoryCache) {
container
.bind<PKCERepositoryInterface>(TYPES.Auth_PKCERepository)
.toConstantValue(
new TypeORMPKCERepository(
container.get(TYPES.Auth_CacheEntryRepository),
container.get(TYPES.Auth_Logger),
container.get(TYPES.Auth_Timer),
),
)
container
.bind<LockRepositoryInterface>(TYPES.Auth_LockRepository)
.toConstantValue(
@@ -577,15 +594,6 @@ export class ContainerConfigLoader {
container.get(TYPES.Auth_Timer),
),
)
container
.bind<PKCERepositoryInterface>(TYPES.Auth_PKCERepository)
.toConstantValue(
new TypeORMPKCERepository(
container.get(TYPES.Auth_CacheEntryRepository),
container.get(TYPES.Auth_Logger),
container.get(TYPES.Auth_Timer),
),
)
container
.bind<SubscriptionTokenRepositoryInterface>(TYPES.Auth_SubscriptionTokenRepository)
.toConstantValue(
@@ -779,6 +787,16 @@ export class ContainerConfigLoader {
container.get(TYPES.Auth_CryptoNode),
),
)
container
.bind<ActivatePremiumFeatures>(TYPES.Auth_ActivatePremiumFeatures)
.toConstantValue(
new ActivatePremiumFeatures(
container.get(TYPES.Auth_UserRepository),
container.get(TYPES.Auth_UserSubscriptionRepository),
container.get(TYPES.Auth_RoleService),
container.get(TYPES.Auth_Timer),
),
)
container
.bind<CleanupSessionTraces>(TYPES.Auth_CleanupSessionTraces)
@@ -1186,6 +1204,8 @@ export class ContainerConfigLoader {
)
}
logger.debug('Configuration complete')
return container
}
}

View File

@@ -1,4 +1,4 @@
import { DataSource, LoggerOptions } from 'typeorm'
import { DataSource, EntityTarget, LoggerOptions, ObjectLiteral, Repository } from 'typeorm'
import { MysqlConnectionOptions } from 'typeorm/driver/mysql/MysqlConnectionOptions'
import { Permission } from '../Domain/Permission/Permission'
import { Role } from '../Domain/Role/Role'
@@ -19,88 +19,102 @@ import { TypeORMSessionTrace } from '../Infra/TypeORM/TypeORMSessionTrace'
import { Env } from './Env'
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
const env: Env = new Env()
env.load()
export class AppDataSource {
private dataSource: DataSource | undefined
const isConfiguredForMySQL = env.get('DB_TYPE') === 'mysql'
constructor(private env: Env) {}
const maxQueryExecutionTime = env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
? +env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
: 45_000
getRepository<Entity extends ObjectLiteral>(target: EntityTarget<Entity>): Repository<Entity> {
if (!this.dataSource) {
throw new Error('DataSource not initialized')
}
const commonDataSourceOptions = {
maxQueryExecutionTime,
entities: [
User,
UserSubscription,
OfflineUserSubscription,
Session,
RevokedSession,
Role,
Permission,
Setting,
OfflineSetting,
SharedSubscriptionInvitation,
SubscriptionSetting,
TypeORMSessionTrace,
TypeORMAuthenticator,
TypeORMAuthenticatorChallenge,
TypeORMEmergencyAccessInvitation,
TypeORMCacheEntry,
],
migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
migrationsRun: true,
logging: <LoggerOptions>env.get('DB_DEBUG_LEVEL', true) ?? 'info',
return this.dataSource.getRepository(target)
}
async initialize(): Promise<void> {
this.env.load()
const isConfiguredForMySQL = this.env.get('DB_TYPE') === 'mysql'
const maxQueryExecutionTime = this.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
? +this.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
: 45_000
const commonDataSourceOptions = {
maxQueryExecutionTime,
entities: [
User,
UserSubscription,
OfflineUserSubscription,
Session,
RevokedSession,
Role,
Permission,
Setting,
OfflineSetting,
SharedSubscriptionInvitation,
SubscriptionSetting,
TypeORMSessionTrace,
TypeORMAuthenticator,
TypeORMAuthenticatorChallenge,
TypeORMEmergencyAccessInvitation,
TypeORMCacheEntry,
],
migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
migrationsRun: true,
logging: <LoggerOptions>this.env.get('DB_DEBUG_LEVEL', true) ?? 'info',
}
if (isConfiguredForMySQL) {
const inReplicaMode = this.env.get('DB_REPLICA_HOST', true) ? true : false
const replicationConfig = {
master: {
host: this.env.get('DB_HOST'),
port: parseInt(this.env.get('DB_PORT')),
username: this.env.get('DB_USERNAME'),
password: this.env.get('DB_PASSWORD'),
database: this.env.get('DB_DATABASE'),
},
slaves: [
{
host: this.env.get('DB_REPLICA_HOST', true),
port: parseInt(this.env.get('DB_PORT')),
username: this.env.get('DB_USERNAME'),
password: this.env.get('DB_PASSWORD'),
database: this.env.get('DB_DATABASE'),
},
],
removeNodeErrorCount: 10,
restoreNodeTimeout: 5,
}
const mySQLDataSourceOptions: MysqlConnectionOptions = {
...commonDataSourceOptions,
type: 'mysql',
charset: 'utf8mb4',
supportBigNumbers: true,
bigNumberStrings: false,
replication: inReplicaMode ? replicationConfig : undefined,
host: inReplicaMode ? undefined : this.env.get('DB_HOST'),
port: inReplicaMode ? undefined : parseInt(this.env.get('DB_PORT')),
username: inReplicaMode ? undefined : this.env.get('DB_USERNAME'),
password: inReplicaMode ? undefined : this.env.get('DB_PASSWORD'),
database: inReplicaMode ? undefined : this.env.get('DB_DATABASE'),
}
this.dataSource = new DataSource(mySQLDataSourceOptions)
} else {
const sqliteDataSourceOptions: SqliteConnectionOptions = {
...commonDataSourceOptions,
type: 'sqlite',
database: this.env.get('DB_SQLITE_DATABASE_PATH'),
}
this.dataSource = new DataSource(sqliteDataSourceOptions)
}
await this.dataSource.initialize()
}
}
let dataSource: DataSource
if (isConfiguredForMySQL) {
const inReplicaMode = env.get('DB_REPLICA_HOST', true) ? true : false
const replicationConfig = {
master: {
host: env.get('DB_HOST'),
port: parseInt(env.get('DB_PORT')),
username: env.get('DB_USERNAME'),
password: env.get('DB_PASSWORD'),
database: env.get('DB_DATABASE'),
},
slaves: [
{
host: env.get('DB_REPLICA_HOST', true),
port: parseInt(env.get('DB_PORT')),
username: env.get('DB_USERNAME'),
password: env.get('DB_PASSWORD'),
database: env.get('DB_DATABASE'),
},
],
removeNodeErrorCount: 10,
restoreNodeTimeout: 5,
}
const mySQLDataSourceOptions: MysqlConnectionOptions = {
...commonDataSourceOptions,
type: 'mysql',
charset: 'utf8mb4',
supportBigNumbers: true,
bigNumberStrings: false,
replication: inReplicaMode ? replicationConfig : undefined,
host: inReplicaMode ? undefined : env.get('DB_HOST'),
port: inReplicaMode ? undefined : parseInt(env.get('DB_PORT')),
username: inReplicaMode ? undefined : env.get('DB_USERNAME'),
password: inReplicaMode ? undefined : env.get('DB_PASSWORD'),
database: inReplicaMode ? undefined : env.get('DB_DATABASE'),
}
dataSource = new DataSource(mySQLDataSourceOptions)
} else {
const sqliteDataSourceOptions: SqliteConnectionOptions = {
...commonDataSourceOptions,
type: 'sqlite',
database: `data/${env.get('DB_DATABASE')}.sqlite`,
}
dataSource = new DataSource(sqliteDataSourceOptions)
}
export const AppDataSource = dataSource

View File

@@ -1,15 +1,21 @@
import {
ControllerContainerInterface,
Result,
ServiceConfiguration,
ServiceContainerInterface,
ServiceIdentifier,
ServiceInterface,
} from '@standardnotes/domain-core'
import { ContainerConfigLoader } from './Container'
import { DirectCallDomainEventPublisher } from '@standardnotes/domain-events-infra'
import TYPES from './Types'
import { Container } from 'inversify'
import { ActivatePremiumFeatures } from '../Domain/UseCase/ActivatePremiumFeatures/ActivatePremiumFeatures'
import { AuthServiceInterface } from './AuthServiceInterface'
export class Service implements AuthServiceInterface {
private container: Container | undefined
export class Service implements ServiceInterface {
constructor(
private serviceContainer: ServiceContainerInterface,
private controllerContainer: ControllerContainerInterface,
@@ -18,6 +24,16 @@ export class Service implements ServiceInterface {
this.serviceContainer.register(this.getId(), this)
}
async activatePremiumFeatures(username: string): Promise<Result<string>> {
if (!this.container) {
return Result.fail('Container not initialized')
}
const activatePremiumFeatures = this.container.get(TYPES.Auth_ActivatePremiumFeatures) as ActivatePremiumFeatures
return activatePremiumFeatures.execute({ username })
}
async handleRequest(request: never, response: never, endpointOrMethodIdentifier: string): Promise<unknown> {
const method = this.controllerContainer.get(endpointOrMethodIdentifier)
@@ -31,12 +47,16 @@ export class Service implements ServiceInterface {
async getContainer(configuration?: ServiceConfiguration): Promise<unknown> {
const config = new ContainerConfigLoader()
return config.load({
const container = await config.load({
controllerConatiner: this.controllerContainer,
directCallDomainEventPublisher: this.directCallDomainEventPublisher,
logger: configuration?.logger,
environmentOverrides: configuration?.environmentOverrides,
})
this.container = container
return container
}
getId(): ServiceIdentifier {

View File

@@ -149,6 +149,7 @@ const TYPES = {
Auth_ListAuthenticators: Symbol.for('Auth_ListAuthenticators'),
Auth_DeleteAuthenticator: Symbol.for('Auth_DeleteAuthenticator'),
Auth_GenerateRecoveryCodes: Symbol.for('Auth_GenerateRecoveryCodes'),
Auth_ActivatePremiumFeatures: Symbol.for('Auth_ActivatePremiumFeatures'),
Auth_SignInWithRecoveryCodes: Symbol.for('Auth_SignInWithRecoveryCodes'),
Auth_GetUserKeyParamsRecovery: Symbol.for('Auth_GetUserKeyParamsRecovery'),
// Handlers

View File

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

View File

@@ -0,0 +1,67 @@
import { TimerInterface } from '@standardnotes/time'
import { RoleServiceInterface } from '../../Role/RoleServiceInterface'
import { UserSubscriptionRepositoryInterface } from '../../Subscription/UserSubscriptionRepositoryInterface'
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
import { ActivatePremiumFeatures } from './ActivatePremiumFeatures'
import { User } from '../../User/User'
describe('ActivatePremiumFeatures', () => {
let userRepository: UserRepositoryInterface
let userSubscriptionRepository: UserSubscriptionRepositoryInterface
let roleService: RoleServiceInterface
let timer: TimerInterface
let user: User
const createUseCase = () =>
new ActivatePremiumFeatures(userRepository, userSubscriptionRepository, roleService, timer)
beforeEach(() => {
user = {} as jest.Mocked<User>
userRepository = {} as jest.Mocked<UserRepositoryInterface>
userRepository.findOneByUsernameOrEmail = jest.fn().mockResolvedValue(user)
userSubscriptionRepository = {} as jest.Mocked<UserSubscriptionRepositoryInterface>
userSubscriptionRepository.save = jest.fn()
roleService = {} as jest.Mocked<RoleServiceInterface>
roleService.addUserRole = jest.fn()
timer = {} as jest.Mocked<TimerInterface>
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(123456789)
timer.convertDateToMicroseconds = jest.fn().mockReturnValue(123456789)
timer.getUTCDateNDaysAhead = jest.fn().mockReturnValue(new Date('2024-01-01T00:00:00.000Z'))
})
it('should return error when username is invalid', async () => {
const useCase = createUseCase()
const result = await useCase.execute({ username: '' })
expect(result.isFailed()).toBe(true)
expect(result.getError()).toBe('Username cannot be empty')
})
it('should return error when user is not found', async () => {
userRepository.findOneByUsernameOrEmail = jest.fn().mockResolvedValue(null)
const useCase = createUseCase()
const result = await useCase.execute({ username: 'test@test.te' })
expect(result.isFailed()).toBe(true)
expect(result.getError()).toBe('User not found with username: test@test.te')
})
it('should save a subscription and add role to user', async () => {
const useCase = createUseCase()
const result = await useCase.execute({ username: 'test@test.te' })
expect(result.isFailed()).toBe(false)
expect(userSubscriptionRepository.save).toHaveBeenCalled()
expect(roleService.addUserRole).toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,49 @@
import { Result, SubscriptionPlanName, UseCaseInterface, Username } from '@standardnotes/domain-core'
import { TimerInterface } from '@standardnotes/time'
import { RoleServiceInterface } from '../../Role/RoleServiceInterface'
import { UserSubscriptionRepositoryInterface } from '../../Subscription/UserSubscriptionRepositoryInterface'
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
import { UserSubscription } from '../../Subscription/UserSubscription'
import { UserSubscriptionType } from '../../Subscription/UserSubscriptionType'
import { ActivatePremiumFeaturesDTO } from './ActivatePremiumFeaturesDTO'
export class ActivatePremiumFeatures implements UseCaseInterface<string> {
constructor(
private userRepository: UserRepositoryInterface,
private userSubscriptionRepository: UserSubscriptionRepositoryInterface,
private roleService: RoleServiceInterface,
private timer: TimerInterface,
) {}
async execute(dto: ActivatePremiumFeaturesDTO): Promise<Result<string>> {
const usernameOrError = Username.create(dto.username)
if (usernameOrError.isFailed()) {
return Result.fail(usernameOrError.getError())
}
const username = usernameOrError.getValue()
const user = await this.userRepository.findOneByUsernameOrEmail(username)
if (user === null) {
return Result.fail(`User not found with username: ${username.value}`)
}
const timestamp = this.timer.getTimestampInMicroseconds()
const subscription = new UserSubscription()
subscription.planName = SubscriptionPlanName.NAMES.ProPlan
subscription.user = Promise.resolve(user)
subscription.createdAt = timestamp
subscription.updatedAt = timestamp
subscription.endsAt = this.timer.convertDateToMicroseconds(this.timer.getUTCDateNDaysAhead(365))
subscription.cancelled = false
subscription.subscriptionId = 1
subscription.subscriptionType = UserSubscriptionType.Regular
await this.userSubscriptionRepository.save(subscription)
await this.roleService.addUserRole(user, SubscriptionPlanName.NAMES.ProPlan)
return Result.ok('Premium features activated.')
}
}

View File

@@ -0,0 +1,3 @@
export interface ActivatePremiumFeaturesDTO {
username: string
}

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.48.3](https://github.com/standardnotes/server/compare/@standardnotes/common@1.48.2...@standardnotes/common@1.48.3) (2023-06-28)
**Note:** Version bump only for package @standardnotes/common
## [1.48.2](https://github.com/standardnotes/server/compare/@standardnotes/common@1.48.0...@standardnotes/common@1.48.2) (2023-05-31)
### Bug Fixes

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/common",
"version": "1.48.2",
"version": "1.48.3",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -2,6 +2,10 @@
export enum ContentType {
Any = '*',
Item = 'SF|Item',
KeySystemItemsKey = 'SN|KeySystemItemsKey',
KeySystemRootKey = 'SN|KeySystemRootKey',
TrustedContact = 'SN|TrustedContact',
VaultListing = 'SN|VaultListing',
RootKey = 'SN|RootKey|NoSync',
ItemsKey = 'SN|ItemsKey',
EncryptedStorage = 'SN|EncryptedStorage',

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.10.1](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.10.0...@standardnotes/event-store@1.10.1) (2023-06-02)
### Bug Fixes
* **home-server:** streaming logs ([a8b806a](https://github.com/standardnotes/server/commit/a8b806af084b3e3fe8707ff0cb041a74042ee049))
# [1.10.0](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.9.6...@standardnotes/event-store@1.10.0) (2023-06-02)
### Features

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/event-store",
"version": "1.10.0",
"version": "1.10.1",
"description": "Event Store Service",
"private": true,
"main": "dist/src/index.js",

View File

@@ -49,9 +49,9 @@ export class ContainerConfigLoader {
}
const logger = winston.createLogger({
level: env.get('LOG_LEVEL') || 'info',
level: env.get('LOG_LEVEL', true) || 'info',
format: winston.format.combine(winston.format.splat(), winston.format.json()),
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL') || 'info' })],
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL', true) || 'info' })],
})
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(logger)

View File

@@ -1,3 +1,4 @@
MODE=microservice # microservice | home-server
LOG_LEVEL=debug
NODE_ENV=development
VERSION=development

View File

@@ -3,6 +3,36 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.18.3](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.18.2...@standardnotes/files-server@1.18.3) (2023-06-22)
### Bug Fixes
* **home-server:** add debug logs about container initalizations ([0df4715](https://github.com/standardnotes/files/commit/0df471585fd5b4626ec2972f3b9a3e33b2830e65))
## [1.18.2](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.18.1...@standardnotes/files-server@1.18.2) (2023-06-12)
### Bug Fixes
* **home-server:** accept application/octet-stream requests for files ([#625](https://github.com/standardnotes/files/issues/625)) ([c8974b7](https://github.com/standardnotes/files/commit/c8974b7fa229ff4f1e026e1ebff50d1081cc5f8b))
## [1.18.1](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.18.0...@standardnotes/files-server@1.18.1) (2023-06-09)
### Bug Fixes
* **files:** add debug logs for checking chunks upon finishing upload session ([5f0929c](https://github.com/standardnotes/files/commit/5f0929c1aa7b83661fb102bad34644db69ddf7eb))
# [1.18.0](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.17.1...@standardnotes/files-server@1.18.0) (2023-06-05)
### Features
* **home-server:** allow running the home server with a mysql and redis configuration ([#622](https://github.com/standardnotes/files/issues/622)) ([d6e531d](https://github.com/standardnotes/files/commit/d6e531d4b6c1c80a894f6d7ec93632595268dd64))
## [1.17.1](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.17.0...@standardnotes/files-server@1.17.1) (2023-06-02)
### Bug Fixes
* **home-server:** streaming logs ([a8b806a](https://github.com/standardnotes/files/commit/a8b806af084b3e3fe8707ff0cb041a74042ee049))
# [1.17.0](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.16.5...@standardnotes/files-server@1.17.0) (2023-06-02)
### Features

View File

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

View File

@@ -67,30 +67,24 @@ export class ContainerConfigLoader {
await import('newrelic')
}
const isConfiguredForHomeServer = env.get('CACHE_TYPE') === 'memory'
const isConfiguredForHomeServer = env.get('MODE', true) === 'home-server'
const isConfiguredForInMemoryCache = env.get('CACHE_TYPE', true) === 'memory'
let logger: winston.Logger
if (configuration?.logger) {
container.bind<winston.Logger>(TYPES.Files_Logger).toConstantValue(configuration.logger as winston.Logger)
logger = configuration.logger as winston.Logger
} else {
container.bind<winston.Logger>(TYPES.Files_Logger).toConstantValue(this.createLogger({ env }))
logger = this.createLogger({ env })
}
container.bind<winston.Logger>(TYPES.Files_Logger).toConstantValue(logger)
container.bind<TimerInterface>(TYPES.Files_Timer).toConstantValue(new Timer())
if (isConfiguredForHomeServer) {
if (isConfiguredForInMemoryCache) {
container
.bind<UploadRepositoryInterface>(TYPES.Files_UploadRepository)
.toConstantValue(new InMemoryUploadRepository(container.get(TYPES.Files_Timer)))
container
.bind<DomainEventPublisherInterface>(TYPES.Files_DomainEventPublisher)
.toConstantValue(directCallDomainEventPublisher)
} else {
container.bind(TYPES.Files_S3_BUCKET_NAME).toConstantValue(env.get('S3_BUCKET_NAME', true))
container.bind(TYPES.Files_S3_AWS_REGION).toConstantValue(env.get('S3_AWS_REGION', true))
container.bind(TYPES.Files_SNS_TOPIC_ARN).toConstantValue(env.get('SNS_TOPIC_ARN'))
container.bind(TYPES.Files_SNS_AWS_REGION).toConstantValue(env.get('SNS_AWS_REGION', true))
container.bind(TYPES.Files_SQS_QUEUE_URL).toConstantValue(env.get('SQS_QUEUE_URL'))
container.bind(TYPES.Files_REDIS_URL).toConstantValue(env.get('REDIS_URL'))
const redisUrl = container.get(TYPES.Files_REDIS_URL) as string
@@ -104,6 +98,20 @@ export class ContainerConfigLoader {
container.bind(TYPES.Files_Redis).toConstantValue(redis)
container.bind<UploadRepositoryInterface>(TYPES.Files_UploadRepository).to(RedisUploadRepository)
}
if (isConfiguredForHomeServer) {
container
.bind<DomainEventPublisherInterface>(TYPES.Files_DomainEventPublisher)
.toConstantValue(directCallDomainEventPublisher)
} else {
container.bind(TYPES.Files_S3_BUCKET_NAME).toConstantValue(env.get('S3_BUCKET_NAME', true))
container.bind(TYPES.Files_S3_AWS_REGION).toConstantValue(env.get('S3_AWS_REGION', true))
container.bind(TYPES.Files_SNS_TOPIC_ARN).toConstantValue(env.get('SNS_TOPIC_ARN'))
container.bind(TYPES.Files_SNS_AWS_REGION).toConstantValue(env.get('SNS_AWS_REGION', true))
container.bind(TYPES.Files_SQS_QUEUE_URL).toConstantValue(env.get('SQS_QUEUE_URL'))
if (env.get('SNS_TOPIC_ARN', true)) {
const snsConfig: SNSClientConfig = {
apiVersion: 'latest',
@@ -137,8 +145,6 @@ export class ContainerConfigLoader {
container.bind<SQSClient>(TYPES.Files_SQS).toConstantValue(new SQSClient(sqsConfig))
}
container.bind<UploadRepositoryInterface>(TYPES.Files_UploadRepository).to(RedisUploadRepository)
container
.bind<DomainEventPublisherInterface>(TYPES.Files_DomainEventPublisher)
.toConstantValue(
@@ -156,7 +162,7 @@ export class ContainerConfigLoader {
.bind(TYPES.Files_FILE_UPLOAD_PATH)
.toConstantValue(env.get('FILE_UPLOAD_PATH', true) ?? `${__dirname}/../../uploads`)
if (env.get('S3_AWS_REGION', true) || env.get('S3_ENDPOINT', true)) {
if (!isConfiguredForHomeServer && (env.get('S3_AWS_REGION', true) || env.get('S3_ENDPOINT', true))) {
const s3Opts: S3ClientConfig = {
apiVersion: 'latest',
}
@@ -245,14 +251,16 @@ export class ContainerConfigLoader {
)
}
logger.debug('Configuration complete')
return container
}
createLogger({ env }: { env: Env }): winston.Logger {
return winston.createLogger({
level: env.get('LOG_LEVEL') || 'info',
level: env.get('LOG_LEVEL', true) || 'info',
format: winston.format.combine(winston.format.splat(), winston.format.json()),
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL') || 'info' })],
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL', true) || 'info' })],
defaultMeta: { service: 'files' },
})
}

View File

@@ -26,6 +26,24 @@ describe('UploadFileChunk', () => {
logger.warn = jest.fn()
})
it('should not upload a data chunk with 0 bytes', async () => {
expect(
await createUseCase().execute({
chunkId: 2,
data: new Uint8Array([]),
resourceRemoteIdentifier: '2-3-4',
resourceUnencryptedFileSize: 123,
userUuid: '1-2-3',
}),
).toEqual({
success: false,
message: 'Empty file chunk',
})
expect(fileUploader.uploadFileChunk).not.toHaveBeenCalled()
expect(uploadRepository.storeUploadChunkResult).not.toHaveBeenCalled()
})
it('should not upload a data chunk to a non existing file upload session', async () => {
uploadRepository.retrieveUploadSessionId = jest.fn().mockReturnValue(undefined)

View File

@@ -18,6 +18,17 @@ export class UploadFileChunk implements UseCaseInterface {
async execute(dto: UploadFileChunkDTO): Promise<UploadFileChunkResponse> {
try {
if (!dto.data.byteLength || dto.data.byteLength === 0) {
this.logger.debug(
`Skipping upload file chunk ${dto.chunkId} with 0 bytes for resource: ${dto.resourceRemoteIdentifier}`,
)
return {
success: false,
message: 'Empty file chunk',
}
}
this.logger.debug(
`Starting upload file chunk ${dto.chunkId} with ${dto.data.byteLength} bytes for resource: ${dto.resourceRemoteIdentifier}`,
)

View File

@@ -39,7 +39,9 @@ export class FSFileUploader implements FileUploaderInterface {
)
}
this.logger.debug(`FS storing file chunk ${dto.chunkId} in memory for ${dto.uploadId}`)
this.logger.debug(
`FS storing file chunk ${dto.chunkId} in memory for ${dto.uploadId}: ${dto.data}, ${dto.data.byteLength}`,
)
fileChunks.set(dto.chunkId, dto.data)
@@ -60,7 +62,14 @@ export class FSFileUploader implements FileUploaderInterface {
const orderedKeys = [...fileChunks.keys()].sort((a, b) => a - b)
for (const orderedKey of orderedKeys) {
await promises.appendFile(`${this.fileUploadPath}/${filePath}`, fileChunks.get(orderedKey) as Uint8Array)
const chunk = fileChunks.get(orderedKey)
if (!chunk || chunk.byteLength === 0) {
throw new Error(`Could not find chunk ${orderedKey} for upload ${uploadId}`)
}
this.logger.debug(`FS writing chunk ${orderedKey} for ${uploadId}: ${chunk.toString()} ${chunk.byteLength}}`)
await promises.appendFile(`${this.fileUploadPath}/${filePath}`, chunk)
}
this.inMemoryChunks.delete(uploadId)

View File

@@ -1,15 +1,5 @@
LOG_LEVEL=debug
NODE_ENV=development
VERSION=development
# (Optional) New Relic Setup
NEW_RELIC_ENABLED=false
NEW_RELIC_APP_NAME="Home Server"
CACHE_TYPE=memory
DB_TYPE=sqlite
DB_DATABASE=home_server
JWT_SECRET=
AUTH_JWT_SECRET=

View File

@@ -3,6 +3,122 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.11.12](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.11...@standardnotes/home-server@1.11.12) (2023-06-28)
**Note:** Version bump only for package @standardnotes/home-server
## [1.11.11](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.10...@standardnotes/home-server@1.11.11) (2023-06-22)
### Bug Fixes
* **home-server:** destroy winston loggers upon server shutdown ([c7a394c](https://github.com/standardnotes/server/commit/c7a394cd1a696305796362cca25fea93e695a86a))
## [1.11.10](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.9...@standardnotes/home-server@1.11.10) (2023-06-22)
### Bug Fixes
* **home-server:** pass the log stream callback before loggers are created ([1ca70c1](https://github.com/standardnotes/server/commit/1ca70c1e504257a3753203c0b5630db3d446d393))
## [1.11.9](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.8...@standardnotes/home-server@1.11.9) (2023-06-22)
### Bug Fixes
* **home-server:** listening on log stream ([e38a164](https://github.com/standardnotes/server/commit/e38a16404c1b335ab0ef4d8383f6f644e51934ad))
* **home-server:** passthrough stream for loggers ([f17a1f8](https://github.com/standardnotes/server/commit/f17a1f875cd087115b06c8224342f6d102042767))
## [1.11.8](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.7...@standardnotes/home-server@1.11.8) (2023-06-22)
### Bug Fixes
* **home-server:** add debug logs about container initalizations ([0df4715](https://github.com/standardnotes/server/commit/0df471585fd5b4626ec2972f3b9a3e33b2830e65))
## [1.11.7](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.6...@standardnotes/home-server@1.11.7) (2023-06-22)
### Bug Fixes
* **home-server:** add default log level to overrides ([c078bc9](https://github.com/standardnotes/server/commit/c078bc958d96e856f6e607406d5d817d422572fb))
* **home-server:** add log leve information to starting the home server information ([49c2792](https://github.com/standardnotes/server/commit/49c27924eafa50c164854946053fd95e6429df9e))
## [1.11.6](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.5...@standardnotes/home-server@1.11.6) (2023-06-16)
### Bug Fixes
* **home-server:** unref the server instance when stopping the home server ([5ef90cc](https://github.com/standardnotes/server/commit/5ef90cc75b44133bf8065ce16f36d5b347c68122))
## [1.11.5](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.4...@standardnotes/home-server@1.11.5) (2023-06-14)
### Bug Fixes
* **home-server:** env var determining the sqlite database location ([#626](https://github.com/standardnotes/server/issues/626)) ([0cb5e36](https://github.com/standardnotes/server/commit/0cb5e36b20d9b095ea0edbcd877387e6c0069856))
## [1.11.4](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.3...@standardnotes/home-server@1.11.4) (2023-06-13)
### Bug Fixes
* **home-server:** encapsulate starting the server with a result return ([90a4f21](https://github.com/standardnotes/server/commit/90a4f2111f9e050e5fb500261c3e43eacc5622e4))
## [1.11.3](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.2...@standardnotes/home-server@1.11.3) (2023-06-12)
### Bug Fixes
* **home-server:** accept application/octet-stream requests for files ([#625](https://github.com/standardnotes/server/issues/625)) ([c8974b7](https://github.com/standardnotes/server/commit/c8974b7fa229ff4f1e026e1ebff50d1081cc5f8b))
## [1.11.2](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.1...@standardnotes/home-server@1.11.2) (2023-06-09)
**Note:** Version bump only for package @standardnotes/home-server
## [1.11.1](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.0...@standardnotes/home-server@1.11.1) (2023-06-09)
**Note:** Version bump only for package @standardnotes/home-server
# [1.11.0](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.10.0...@standardnotes/home-server@1.11.0) (2023-06-09)
### Features
* **home-server:** add activating premium features ([#624](https://github.com/standardnotes/server/issues/624)) ([72ce190](https://github.com/standardnotes/server/commit/72ce1909960fbd2ec6a47b8dbdfbe53a4f10e776))
# [1.10.0](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.9.0...@standardnotes/home-server@1.10.0) (2023-06-07)
### Features
* configurable path for uploads and db ([#623](https://github.com/standardnotes/server/issues/623)) ([af8feaa](https://github.com/standardnotes/server/commit/af8feaadfe2dd58baab4cca217d6307b4a221326))
# [1.9.0](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.8.5...@standardnotes/home-server@1.9.0) (2023-06-05)
### Features
* **home-server:** allow running the home server with a mysql and redis configuration ([#622](https://github.com/standardnotes/server/issues/622)) ([d6e531d](https://github.com/standardnotes/server/commit/d6e531d4b6c1c80a894f6d7ec93632595268dd64))
## [1.8.5](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.8.4...@standardnotes/home-server@1.8.5) (2023-06-02)
### Bug Fixes
* **home-server:** linter issues ([28cce39](https://github.com/standardnotes/server/commit/28cce39fe7a75fec035f920573271e1e56421818))
* **home-server:** streaming logs ([a8b806a](https://github.com/standardnotes/server/commit/a8b806af084b3e3fe8707ff0cb041a74042ee049))
## [1.8.4](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.8.3...@standardnotes/home-server@1.8.4) (2023-06-02)
### Bug Fixes
* **home-server:** remove redundant restart method ([58ab410](https://github.com/standardnotes/server/commit/58ab410b0afb1d811247cd65b2585d06f9c8807a))
## [1.8.3](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.8.2...@standardnotes/home-server@1.8.3) (2023-06-02)
### Bug Fixes
* **home-server:** add default for VERSION environment variable ([2f569d4](https://github.com/standardnotes/server/commit/2f569d41047a802eb72ef1a3618ffe4df28a709c))
* **home-server:** displaying the port on which it is running ([1e62a37](https://github.com/standardnotes/server/commit/1e62a3760e3b9601478c851cf33db2f2b348d7fb))
## [1.8.2](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.8.1...@standardnotes/home-server@1.8.2) (2023-06-02)
### Bug Fixes
* **home-server:** default configuration variables ([e6e9a32](https://github.com/standardnotes/server/commit/e6e9a32f0385789e5e772e5cabcc0da0b8ccbb01))
## [1.8.1](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.8.0...@standardnotes/home-server@1.8.1) (2023-06-02)
**Note:** Version bump only for package @standardnotes/home-server
# [1.8.0](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.7.5...@standardnotes/home-server@1.8.0) (2023-06-02)
### Features

View File

@@ -2,7 +2,15 @@ import { HomeServer } from '../src/Server/HomeServer'
const homeServer = new HomeServer()
Promise.resolve(homeServer.start()).catch((error) => {
Promise.resolve(
homeServer.start({
dataDirectoryPath: `${__dirname}/../data`,
logStreamCallback: (chunk: Buffer) => {
// eslint-disable-next-line no-console
console.log(chunk.toString())
},
}),
).catch((error) => {
// eslint-disable-next-line no-console
console.log(`Could not start server: ${error.message}`)
})

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/home-server",
"version": "1.8.0",
"version": "1.11.12",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -17,6 +17,7 @@
"clean": "rm -fr dist",
"build": "tsc --build",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"start": "yarn node dist/bin/server.js"
},
"dependencies": {

View File

@@ -1,10 +1,10 @@
import 'reflect-metadata'
import { ControllerContainer, ServiceContainer } from '@standardnotes/domain-core'
import { ControllerContainer, Result, ServiceContainer } from '@standardnotes/domain-core'
import { Service as ApiGatewayService } from '@standardnotes/api-gateway'
import { Service as FilesService } from '@standardnotes/files-server'
import { DirectCallDomainEventPublisher } from '@standardnotes/domain-events-infra'
import { Service as AuthService } from '@standardnotes/auth-server'
import { Service as AuthService, AuthServiceInterface } from '@standardnotes/auth-server'
import { Service as SyncingService } from '@standardnotes/syncing-server'
import { Service as RevisionsService } from '@standardnotes/revisions-server'
import { Container } from 'inversify'
@@ -12,7 +12,7 @@ import { InversifyExpressServer } from 'inversify-express-utils'
import helmet from 'helmet'
import * as cors from 'cors'
import * as http from 'http'
import { text, json, Request, Response, NextFunction } from 'express'
import { text, json, Request, Response, NextFunction, raw } from 'express'
import * as winston from 'winston'
import { PassThrough } from 'stream'
// eslint-disable-next-line @typescript-eslint/no-var-requires
@@ -24,115 +24,172 @@ import { HomeServerConfiguration } from './HomeServerConfiguration'
export class HomeServer implements HomeServerInterface {
private serverInstance: http.Server | undefined
private authService: AuthServiceInterface | undefined
private logStream: PassThrough | undefined
private readonly loggerNames = [
'auth-server',
'syncing-server',
'revisions-server',
'files-server',
'api-gateway',
'home-server',
]
async start(configuration?: HomeServerConfiguration): Promise<void> {
const controllerContainer = new ControllerContainer()
const serviceContainer = new ServiceContainer()
const directCallDomainEventPublisher = new DirectCallDomainEventPublisher()
async start(configuration: HomeServerConfiguration): Promise<Result<string>> {
try {
const controllerContainer = new ControllerContainer()
const serviceContainer = new ServiceContainer()
const directCallDomainEventPublisher = new DirectCallDomainEventPublisher()
const env: Env = new Env(configuration?.environment)
env.load()
const environmentOverrides = {
DB_TYPE: 'sqlite',
CACHE_TYPE: 'memory',
DB_SQLITE_DATABASE_PATH: `${configuration.dataDirectoryPath}/database/home_server.sqlite`,
FILE_UPLOAD_PATH: `${configuration.dataDirectoryPath}/uploads`,
...configuration.environment,
MODE: 'home-server',
NEW_RELIC_ENABLED: 'false',
NEW_RELIC_APP_NAME: 'Home Server',
}
this.configureLoggers(env)
const env: Env = new Env(environmentOverrides)
env.load()
const apiGatewayService = new ApiGatewayService(serviceContainer)
const authService = new AuthService(serviceContainer, controllerContainer, directCallDomainEventPublisher)
const syncingService = new SyncingService(serviceContainer, controllerContainer, directCallDomainEventPublisher)
const revisionsService = new RevisionsService(serviceContainer, controllerContainer, directCallDomainEventPublisher)
const filesService = new FilesService(serviceContainer, directCallDomainEventPublisher)
this.configureLoggers(env, configuration)
const container = Container.merge(
(await apiGatewayService.getContainer({
logger: winston.loggers.get('api-gateway'),
environmentOverrides: configuration?.environment,
})) as Container,
(await authService.getContainer({
logger: winston.loggers.get('auth-server'),
environmentOverrides: configuration?.environment,
})) as Container,
(await syncingService.getContainer({
logger: winston.loggers.get('syncing-server'),
environmentOverrides: configuration?.environment,
})) as Container,
(await revisionsService.getContainer({
logger: winston.loggers.get('revisions-server'),
environmentOverrides: configuration?.environment,
})) as Container,
(await filesService.getContainer({
logger: winston.loggers.get('files-server'),
environmentOverrides: configuration?.environment,
})) as Container,
)
const apiGatewayService = new ApiGatewayService(serviceContainer)
const authService = new AuthService(serviceContainer, controllerContainer, directCallDomainEventPublisher)
this.authService = authService
const syncingService = new SyncingService(serviceContainer, controllerContainer, directCallDomainEventPublisher)
const revisionsService = new RevisionsService(
serviceContainer,
controllerContainer,
directCallDomainEventPublisher,
)
const filesService = new FilesService(serviceContainer, directCallDomainEventPublisher)
const server = new InversifyExpressServer(container)
const container = Container.merge(
(await apiGatewayService.getContainer({
logger: winston.loggers.get('api-gateway'),
environmentOverrides,
})) as Container,
(await authService.getContainer({
logger: winston.loggers.get('auth-server'),
environmentOverrides,
})) as Container,
(await syncingService.getContainer({
logger: winston.loggers.get('syncing-server'),
environmentOverrides,
})) as Container,
(await revisionsService.getContainer({
logger: winston.loggers.get('revisions-server'),
environmentOverrides,
})) as Container,
(await filesService.getContainer({
logger: winston.loggers.get('files-server'),
environmentOverrides,
})) as Container,
)
server.setConfig((app) => {
/* eslint-disable */
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["https: 'self'"],
baseUri: ["'self'"],
childSrc: ["*", "blob:"],
connectSrc: ["*"],
fontSrc: ["*", "'self'"],
formAction: ["'self'"],
frameAncestors: ["*", "*.standardnotes.org", "*.standardnotes.com"],
frameSrc: ["*", "blob:"],
imgSrc: ["'self'", "*", "data:"],
manifestSrc: ["'self'"],
mediaSrc: ["'self'"],
objectSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'"]
const server = new InversifyExpressServer(container)
server.setConfig((app) => {
/* eslint-disable */
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["https: 'self'"],
baseUri: ["'self'"],
childSrc: ["*", "blob:"],
connectSrc: ["*"],
fontSrc: ["*", "'self'"],
formAction: ["'self'"],
frameAncestors: ["*", "*.standardnotes.org", "*.standardnotes.com"],
frameSrc: ["*", "blob:"],
imgSrc: ["'self'", "*", "data:"],
manifestSrc: ["'self'"],
mediaSrc: ["'self'"],
objectSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'"]
}
}
}
}))
/* eslint-enable */
app.use(json({ limit: '50mb' }))
app.use(
text({
type: ['text/plain', 'application/x-www-form-urlencoded', 'application/x-www-form-urlencoded; charset=utf-8'],
}),
)
app.use(cors())
app.use(
robots({
UserAgent: '*',
Disallow: '/',
}),
)
})
}))
/* eslint-enable */
app.use(json({ limit: '50mb' }))
app.use(raw({ limit: '50mb', type: 'application/octet-stream' }))
app.use(
text({
type: [
'text/plain',
'application/x-www-form-urlencoded',
'application/x-www-form-urlencoded; charset=utf-8',
],
}),
)
app.use(
cors({
exposedHeaders: ['Content-Range', 'Accept-Ranges'],
}),
)
app.use(
robots({
UserAgent: '*',
Disallow: '/',
}),
)
})
const logger: winston.Logger = winston.loggers.get('home-server')
const logger: winston.Logger = winston.loggers.get('home-server')
server.setErrorConfig((app) => {
app.use((error: Record<string, unknown>, _request: Request, response: Response, _next: NextFunction) => {
logger.error(error.stack)
server.setErrorConfig((app) => {
app.use((error: Record<string, unknown>, _request: Request, response: Response, _next: NextFunction) => {
logger.error(error.stack)
response.status(500).send({
error: {
message:
"Unfortunately, we couldn't handle your request. Please try again or contact our support if the error persists.",
},
response.status(500).send({
error: {
message:
"Unfortunately, we couldn't handle your request. Please try again or contact our support if the error persists.",
},
})
})
})
})
this.serverInstance = server.build().listen(env.get('PORT', true) ? +env.get('PORT', true) : 3000)
const port = env.get('PORT', true) ? +env.get('PORT', true) : 3000
logger.info(`Server started on port ${process.env.PORT}`)
}
this.serverInstance = server.build().listen(port)
async stop(): Promise<void> {
if (this.serverInstance) {
this.serverInstance.close()
logger.info(`Server started on port ${port}. Log level: ${env.get('LOG_LEVEL', true)}.`)
return Result.ok('Server started.')
} catch (error) {
return Result.fail((error as Error).message)
}
}
async restart(): Promise<void> {
await this.stop()
await this.start()
async stop(): Promise<Result<string>> {
try {
if (!this.serverInstance) {
return Result.fail('Home server is not running.')
}
for (const loggerName of this.loggerNames) {
winston.loggers.close(loggerName)
}
if (this.logStream) {
this.logStream.end()
}
this.serverInstance.close()
this.serverInstance.unref()
this.serverInstance = undefined
return Result.ok('Server stopped.')
} catch (error) {
return Result.fail((error as Error).message)
}
}
async isRunning(): Promise<boolean> {
@@ -143,31 +200,35 @@ export class HomeServer implements HomeServerInterface {
return this.serverInstance.address() !== null
}
logs(): NodeJS.ReadableStream {
const passThroughStream = new PassThrough()
for (const logger of winston.loggers.loggers.values()) {
logger.stream({ start: -1 }).pipe(passThroughStream, { end: false })
async activatePremiumFeatures(username: string): Promise<Result<string>> {
if (!this.isRunning() || !this.authService) {
return Result.fail('Home server is not running.')
}
return passThroughStream
return this.authService.activatePremiumFeatures(username)
}
private configureLoggers(env: Env): void {
private configureLoggers(env: Env, configuration: HomeServerConfiguration): void {
this.logStream = new PassThrough()
if (configuration.logStreamCallback) {
this.logStream.on('data', configuration.logStreamCallback)
}
const winstonFormatters = [winston.format.splat(), winston.format.json()]
for (const loggerName of [
'auth-server',
'syncing-server',
'revisions-server',
'files-server',
'api-gateway',
'home-server',
]) {
const level = env.get('LOG_LEVEL', true) || 'info'
for (const loggerName of this.loggerNames) {
winston.loggers.add(loggerName, {
level: env.get('LOG_LEVEL') || 'info',
level,
format: winston.format.combine(...winstonFormatters),
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL') || 'info' })],
transports: [
new winston.transports.Stream({
level,
stream: this.logStream,
}),
],
defaultMeta: { service: loggerName },
})
}

View File

@@ -1,3 +1,5 @@
export interface HomeServerConfiguration {
environment: { [name: string]: string }
dataDirectoryPath: string
environment?: { [name: string]: string }
logStreamCallback?: (chunk: Buffer) => void
}

View File

@@ -1,9 +1,9 @@
import { Result } from '@standardnotes/domain-core'
import { HomeServerConfiguration } from './HomeServerConfiguration'
export interface HomeServerInterface {
start(configuration?: HomeServerConfiguration): Promise<void>
stop(): Promise<void>
restart(): Promise<void>
start(configuration?: HomeServerConfiguration): Promise<Result<string>>
activatePremiumFeatures(username: string): Promise<Result<string>>
stop(): Promise<Result<string>>
isRunning(): Promise<boolean>
logs(): NodeJS.ReadableStream
}

View File

@@ -1,3 +1,4 @@
MODE=microservice # microservice | home-server
LOG_LEVEL=info
NODE_ENV=development
VERSION=development
@@ -14,8 +15,10 @@ DB_PASSWORD=revisionspassword
DB_DATABASE=revisions
DB_DEBUG_LEVEL=all # "all" | "query" | "schema" | "error" | "warn" | "info" | "log" | "migration"
DB_MIGRATIONS_PATH=dist/migrations/*.js
DB_TYPE=mysql
REDIS_URL=redis://cache
CACHE_TYPE=redis
SQS_QUEUE_URL=
SQS_AWS_REGION=

View File

@@ -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.23.3](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.23.2...@standardnotes/revisions-server@1.23.3) (2023-06-28)
**Note:** Version bump only for package @standardnotes/revisions-server
## [1.23.2](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.23.1...@standardnotes/revisions-server@1.23.2) (2023-06-22)
### Bug Fixes
* **home-server:** add debug logs about container initalizations ([0df4715](https://github.com/standardnotes/server/commit/0df471585fd5b4626ec2972f3b9a3e33b2830e65))
## [1.23.1](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.23.0...@standardnotes/revisions-server@1.23.1) (2023-06-14)
### Bug Fixes
* **home-server:** env var determining the sqlite database location ([#626](https://github.com/standardnotes/server/issues/626)) ([0cb5e36](https://github.com/standardnotes/server/commit/0cb5e36b20d9b095ea0edbcd877387e6c0069856))
# [1.23.0](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.22.0...@standardnotes/revisions-server@1.23.0) (2023-06-07)
### Features
* configurable path for uploads and db ([#623](https://github.com/standardnotes/server/issues/623)) ([af8feaa](https://github.com/standardnotes/server/commit/af8feaadfe2dd58baab4cca217d6307b4a221326))
# [1.22.0](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.21.3...@standardnotes/revisions-server@1.22.0) (2023-06-05)
### Features
* **home-server:** allow running the home server with a mysql and redis configuration ([#622](https://github.com/standardnotes/server/issues/622)) ([d6e531d](https://github.com/standardnotes/server/commit/d6e531d4b6c1c80a894f6d7ec93632595268dd64))
## [1.21.3](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.21.2...@standardnotes/revisions-server@1.21.3) (2023-06-02)
### Bug Fixes
* **home-server:** streaming logs ([a8b806a](https://github.com/standardnotes/server/commit/a8b806af084b3e3fe8707ff0cb041a74042ee049))
## [1.21.2](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.21.1...@standardnotes/revisions-server@1.21.2) (2023-06-02)
### Bug Fixes
* **home-server:** add default for VERSION environment variable ([2f569d4](https://github.com/standardnotes/server/commit/2f569d41047a802eb72ef1a3618ffe4df28a709c))
## [1.21.1](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.21.0...@standardnotes/revisions-server@1.21.1) (2023-06-02)
### Bug Fixes
* initializing data source with already configured environment ([624b574](https://github.com/standardnotes/server/commit/624b574013157e9e044d4a8ed53cadb7fcc567ae))
# [1.21.0](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.20.4...@standardnotes/revisions-server@1.21.0) (2023-06-02)
### Features

View File

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

View File

@@ -61,18 +61,15 @@ export class ContainerConfigLoader {
const env: Env = new Env(configuration?.environmentOverrides)
env.load()
const isConfiguredForHomeServer = env.get('DB_TYPE') === 'sqlite'
const isConfiguredForHomeServer = env.get('MODE', true) === 'home-server'
const container = new Container({
defaultScope: 'Singleton',
})
await AppDataSource.initialize()
container.bind<Env>(TYPES.Revisions_Env).toConstantValue(env)
let logger: winston.Logger
if (configuration?.logger) {
container.bind<winston.Logger>(TYPES.Revisions_Logger).toConstantValue(configuration.logger as winston.Logger)
logger = configuration.logger as winston.Logger
} else {
const winstonFormatters = [winston.format.splat(), winston.format.json()]
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
@@ -83,18 +80,24 @@ export class ContainerConfigLoader {
winstonFormatters.push(newrelicWinstonFormatter())
}
const logger = winston.createLogger({
level: env.get('LOG_LEVEL') || 'info',
logger = winston.createLogger({
level: env.get('LOG_LEVEL', true) || 'info',
format: winston.format.combine(...winstonFormatters),
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL') || 'info' })],
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL', true) || 'info' })],
defaultMeta: { service: 'revisions' },
})
container.bind<winston.Logger>(TYPES.Revisions_Logger).toConstantValue(logger)
}
container.bind<winston.Logger>(TYPES.Revisions_Logger).toConstantValue(logger)
const appDataSource = new AppDataSource(env)
await appDataSource.initialize()
logger.debug('Database initialized')
container.bind<Env>(TYPES.Revisions_Env).toConstantValue(env)
container.bind(TYPES.Revisions_NEW_RELIC_ENABLED).toConstantValue(env.get('NEW_RELIC_ENABLED', true))
container.bind(TYPES.Revisions_VERSION).toConstantValue(env.get('VERSION'))
container.bind(TYPES.Revisions_VERSION).toConstantValue(env.get('VERSION', true) ?? 'development')
// Map
container
@@ -107,7 +110,7 @@ export class ContainerConfigLoader {
// ORM
container
.bind<Repository<TypeORMRevision>>(TYPES.Revisions_ORMRevisionRepository)
.toDynamicValue(() => AppDataSource.getRepository(TypeORMRevision))
.toDynamicValue(() => appDataSource.getRepository(TypeORMRevision))
// Repositories
container
@@ -349,6 +352,8 @@ export class ContainerConfigLoader {
)
}
logger.debug('Configuration complete')
return container
}
}

View File

@@ -1,4 +1,4 @@
import { DataSource, LoggerOptions } from 'typeorm'
import { DataSource, EntityTarget, LoggerOptions, ObjectLiteral, Repository } from 'typeorm'
import { MysqlConnectionOptions } from 'typeorm/driver/mysql/MysqlConnectionOptions'
import { TypeORMRevision } from '../Infra/TypeORM/TypeORMRevision'
@@ -6,71 +6,85 @@ import { TypeORMRevision } from '../Infra/TypeORM/TypeORMRevision'
import { Env } from './Env'
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
const env: Env = new Env()
env.load()
export class AppDataSource {
private dataSource: DataSource | undefined
const isConfiguredForMySQL = env.get('DB_TYPE') === 'mysql'
constructor(private env: Env) {}
const maxQueryExecutionTime = env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
? +env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
: 45_000
getRepository<Entity extends ObjectLiteral>(target: EntityTarget<Entity>): Repository<Entity> {
if (!this.dataSource) {
throw new Error('DataSource not initialized')
}
const commonDataSourceOptions = {
maxQueryExecutionTime,
entities: [TypeORMRevision],
migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
migrationsRun: true,
logging: <LoggerOptions>env.get('DB_DEBUG_LEVEL', true) ?? 'info',
return this.dataSource.getRepository(target)
}
async initialize(): Promise<void> {
this.env.load()
const isConfiguredForMySQL = this.env.get('DB_TYPE') === 'mysql'
const maxQueryExecutionTime = this.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
? +this.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
: 45_000
const commonDataSourceOptions = {
maxQueryExecutionTime,
entities: [TypeORMRevision],
migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
migrationsRun: true,
logging: <LoggerOptions>this.env.get('DB_DEBUG_LEVEL', true) ?? 'info',
}
if (isConfiguredForMySQL) {
const inReplicaMode = this.env.get('DB_REPLICA_HOST', true) ? true : false
const replicationConfig = {
master: {
host: this.env.get('DB_HOST'),
port: parseInt(this.env.get('DB_PORT')),
username: this.env.get('DB_USERNAME'),
password: this.env.get('DB_PASSWORD'),
database: this.env.get('DB_DATABASE'),
},
slaves: [
{
host: this.env.get('DB_REPLICA_HOST', true),
port: parseInt(this.env.get('DB_PORT')),
username: this.env.get('DB_USERNAME'),
password: this.env.get('DB_PASSWORD'),
database: this.env.get('DB_DATABASE'),
},
],
removeNodeErrorCount: 10,
restoreNodeTimeout: 5,
}
const mySQLDataSourceOptions: MysqlConnectionOptions = {
...commonDataSourceOptions,
type: 'mysql',
charset: 'utf8mb4',
supportBigNumbers: true,
bigNumberStrings: false,
replication: inReplicaMode ? replicationConfig : undefined,
host: inReplicaMode ? undefined : this.env.get('DB_HOST'),
port: inReplicaMode ? undefined : parseInt(this.env.get('DB_PORT')),
username: inReplicaMode ? undefined : this.env.get('DB_USERNAME'),
password: inReplicaMode ? undefined : this.env.get('DB_PASSWORD'),
database: inReplicaMode ? undefined : this.env.get('DB_DATABASE'),
}
this.dataSource = new DataSource(mySQLDataSourceOptions)
} else {
const sqliteDataSourceOptions: SqliteConnectionOptions = {
...commonDataSourceOptions,
type: 'sqlite',
database: this.env.get('DB_SQLITE_DATABASE_PATH'),
}
this.dataSource = new DataSource(sqliteDataSourceOptions)
}
await this.dataSource.initialize()
}
}
let dataSource: DataSource
if (isConfiguredForMySQL) {
const inReplicaMode = env.get('DB_REPLICA_HOST', true) ? true : false
const replicationConfig = {
master: {
host: env.get('DB_HOST'),
port: parseInt(env.get('DB_PORT')),
username: env.get('DB_USERNAME'),
password: env.get('DB_PASSWORD'),
database: env.get('DB_DATABASE'),
},
slaves: [
{
host: env.get('DB_REPLICA_HOST', true),
port: parseInt(env.get('DB_PORT')),
username: env.get('DB_USERNAME'),
password: env.get('DB_PASSWORD'),
database: env.get('DB_DATABASE'),
},
],
removeNodeErrorCount: 10,
restoreNodeTimeout: 5,
}
const mySQLDataSourceOptions: MysqlConnectionOptions = {
...commonDataSourceOptions,
type: 'mysql',
charset: 'utf8mb4',
supportBigNumbers: true,
bigNumberStrings: false,
replication: inReplicaMode ? replicationConfig : undefined,
host: inReplicaMode ? undefined : env.get('DB_HOST'),
port: inReplicaMode ? undefined : parseInt(env.get('DB_PORT')),
username: inReplicaMode ? undefined : env.get('DB_USERNAME'),
password: inReplicaMode ? undefined : env.get('DB_PASSWORD'),
database: inReplicaMode ? undefined : env.get('DB_DATABASE'),
}
dataSource = new DataSource(mySQLDataSourceOptions)
} else {
const sqliteDataSourceOptions: SqliteConnectionOptions = {
...commonDataSourceOptions,
type: 'sqlite',
database: `data/${env.get('DB_DATABASE')}.sqlite`,
}
dataSource = new DataSource(sqliteDataSourceOptions)
}
export const AppDataSource = dataSource

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.20.1](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.0...@standardnotes/scheduler-server@1.20.1) (2023-06-02)
### Bug Fixes
* **home-server:** streaming logs ([a8b806a](https://github.com/standardnotes/server/commit/a8b806af084b3e3fe8707ff0cb041a74042ee049))
# [1.20.0](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.19.6...@standardnotes/scheduler-server@1.20.0) (2023-06-02)
### Features

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/scheduler-server",
"version": "1.20.0",
"version": "1.20.1",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -66,9 +66,9 @@ export class ContainerConfigLoader {
}
const logger = winston.createLogger({
level: env.get('LOG_LEVEL') || 'info',
level: env.get('LOG_LEVEL', true) || 'info',
format: winston.format.combine(...winstonFormatters),
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL') || 'info' })],
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL', true) || 'info' })],
})
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(logger)

View File

@@ -1,3 +1,4 @@
MODE=microservice # microservice | home-server
LOG_LEVEL=info
NODE_ENV=development
VERSION=development
@@ -14,8 +15,10 @@ DB_PASSWORD=changeme123
DB_DATABASE=standard_notes_db
DB_DEBUG_LEVEL=all # "all" | "query" | "schema" | "error" | "warn" | "info" | "log" | "migration"
DB_MIGRATIONS_PATH=dist/migrations/*.js
DB_TYPE=mysql
REDIS_URL=redis://cache
CACHE_TYPE=redis
SNS_TOPIC_ARN=
SNS_AWS_REGION=

View File

@@ -3,6 +3,46 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.44.3](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.44.2...@standardnotes/syncing-server@1.44.3) (2023-06-28)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.44.2](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.44.1...@standardnotes/syncing-server@1.44.2) (2023-06-22)
### Bug Fixes
* **home-server:** add debug logs about container initalizations ([0df4715](https://github.com/standardnotes/syncing-server-js/commit/0df471585fd5b4626ec2972f3b9a3e33b2830e65))
## [1.44.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.44.0...@standardnotes/syncing-server@1.44.1) (2023-06-14)
### Bug Fixes
* **home-server:** env var determining the sqlite database location ([#626](https://github.com/standardnotes/syncing-server-js/issues/626)) ([0cb5e36](https://github.com/standardnotes/syncing-server-js/commit/0cb5e36b20d9b095ea0edbcd877387e6c0069856))
# [1.44.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.43.0...@standardnotes/syncing-server@1.44.0) (2023-06-07)
### Features
* configurable path for uploads and db ([#623](https://github.com/standardnotes/syncing-server-js/issues/623)) ([af8feaa](https://github.com/standardnotes/syncing-server-js/commit/af8feaadfe2dd58baab4cca217d6307b4a221326))
# [1.43.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.42.2...@standardnotes/syncing-server@1.43.0) (2023-06-05)
### Features
* **home-server:** allow running the home server with a mysql and redis configuration ([#622](https://github.com/standardnotes/syncing-server-js/issues/622)) ([d6e531d](https://github.com/standardnotes/syncing-server-js/commit/d6e531d4b6c1c80a894f6d7ec93632595268dd64))
## [1.42.2](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.42.1...@standardnotes/syncing-server@1.42.2) (2023-06-02)
### Bug Fixes
* **home-server:** streaming logs ([a8b806a](https://github.com/standardnotes/syncing-server-js/commit/a8b806af084b3e3fe8707ff0cb041a74042ee049))
## [1.42.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.42.0...@standardnotes/syncing-server@1.42.1) (2023-06-02)
### Bug Fixes
* initializing data source with already configured environment ([624b574](https://github.com/standardnotes/syncing-server-js/commit/624b574013157e9e044d4a8ed53cadb7fcc567ae))
# [1.42.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.41.4...@standardnotes/syncing-server@1.42.0) (2023-06-02)
### Features

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/syncing-server",
"version": "1.42.0",
"version": "1.44.3",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -95,14 +95,9 @@ export class ContainerConfigLoader {
defaultScope: 'Singleton',
})
await AppDataSource.initialize()
const isConfiguredForHomeServer = env.get('DB_TYPE') === 'sqlite'
container.bind<Env>(TYPES.Sync_Env).toConstantValue(env)
let logger: winston.Logger
if (configuration?.logger) {
container.bind<winston.Logger>(TYPES.Sync_Logger).toConstantValue(configuration.logger as winston.Logger)
logger = configuration.logger as winston.Logger
} else {
const winstonFormatters = [winston.format.splat(), winston.format.json()]
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
@@ -113,15 +108,23 @@ export class ContainerConfigLoader {
winstonFormatters.push(newrelicWinstonFormatter())
}
const logger = winston.createLogger({
level: env.get('LOG_LEVEL') || 'info',
logger = winston.createLogger({
level: env.get('LOG_LEVEL', true) || 'info',
format: winston.format.combine(...winstonFormatters),
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL') || 'info' })],
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL', true) || 'info' })],
defaultMeta: { service: 'syncing-server' },
})
container.bind<winston.Logger>(TYPES.Sync_Logger).toConstantValue(logger)
}
container.bind<winston.Logger>(TYPES.Sync_Logger).toConstantValue(logger)
const appDataSource = new AppDataSource(env)
await appDataSource.initialize()
logger.debug('Database initialized')
const isConfiguredForHomeServer = env.get('MODE', true) === 'home-server'
container.bind<Env>(TYPES.Sync_Env).toConstantValue(env)
if (isConfiguredForHomeServer) {
container
@@ -206,7 +209,7 @@ export class ContainerConfigLoader {
// ORM
container
.bind<Repository<Item>>(TYPES.Sync_ORMItemRepository)
.toDynamicValue(() => AppDataSource.getRepository(Item))
.toDynamicValue(() => appDataSource.getRepository(Item))
// Projectors
container
@@ -516,6 +519,8 @@ export class ContainerConfigLoader {
)
}
logger.debug('Configuration complete')
return container
}
}

View File

@@ -1,74 +1,88 @@
import { DataSource, LoggerOptions } from 'typeorm'
import { DataSource, EntityTarget, LoggerOptions, ObjectLiteral, Repository } from 'typeorm'
import { MysqlConnectionOptions } from 'typeorm/driver/mysql/MysqlConnectionOptions'
import { Item } from '../Domain/Item/Item'
import { Env } from './Env'
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
const env: Env = new Env()
env.load()
export class AppDataSource {
private dataSource: DataSource | undefined
const isConfiguredForMySQL = env.get('DB_TYPE') === 'mysql'
constructor(private env: Env) {}
const maxQueryExecutionTime = env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
? +env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
: 45_000
getRepository<Entity extends ObjectLiteral>(target: EntityTarget<Entity>): Repository<Entity> {
if (!this.dataSource) {
throw new Error('DataSource not initialized')
}
const commonDataSourceOptions = {
maxQueryExecutionTime,
entities: [Item],
migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
migrationsRun: true,
logging: <LoggerOptions>env.get('DB_DEBUG_LEVEL', true) ?? 'info',
return this.dataSource.getRepository(target)
}
async initialize(): Promise<void> {
this.env.load()
const isConfiguredForMySQL = this.env.get('DB_TYPE') === 'mysql'
const maxQueryExecutionTime = this.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
? +this.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
: 45_000
const commonDataSourceOptions = {
maxQueryExecutionTime,
entities: [Item],
migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
migrationsRun: true,
logging: <LoggerOptions>this.env.get('DB_DEBUG_LEVEL', true) ?? 'info',
}
if (isConfiguredForMySQL) {
const inReplicaMode = this.env.get('DB_REPLICA_HOST', true) ? true : false
const replicationConfig = {
master: {
host: this.env.get('DB_HOST'),
port: parseInt(this.env.get('DB_PORT')),
username: this.env.get('DB_USERNAME'),
password: this.env.get('DB_PASSWORD'),
database: this.env.get('DB_DATABASE'),
},
slaves: [
{
host: this.env.get('DB_REPLICA_HOST', true),
port: parseInt(this.env.get('DB_PORT')),
username: this.env.get('DB_USERNAME'),
password: this.env.get('DB_PASSWORD'),
database: this.env.get('DB_DATABASE'),
},
],
removeNodeErrorCount: 10,
restoreNodeTimeout: 5,
}
const mySQLDataSourceOptions: MysqlConnectionOptions = {
...commonDataSourceOptions,
type: 'mysql',
charset: 'utf8mb4',
supportBigNumbers: true,
bigNumberStrings: false,
replication: inReplicaMode ? replicationConfig : undefined,
host: inReplicaMode ? undefined : this.env.get('DB_HOST'),
port: inReplicaMode ? undefined : parseInt(this.env.get('DB_PORT')),
username: inReplicaMode ? undefined : this.env.get('DB_USERNAME'),
password: inReplicaMode ? undefined : this.env.get('DB_PASSWORD'),
database: inReplicaMode ? undefined : this.env.get('DB_DATABASE'),
}
this.dataSource = new DataSource(mySQLDataSourceOptions)
} else {
const sqliteDataSourceOptions: SqliteConnectionOptions = {
...commonDataSourceOptions,
type: 'sqlite',
database: this.env.get('DB_SQLITE_DATABASE_PATH'),
}
this.dataSource = new DataSource(sqliteDataSourceOptions)
}
await this.dataSource.initialize()
}
}
let dataSource: DataSource
if (isConfiguredForMySQL) {
const inReplicaMode = env.get('DB_REPLICA_HOST', true) ? true : false
const replicationConfig = {
master: {
host: env.get('DB_HOST'),
port: parseInt(env.get('DB_PORT')),
username: env.get('DB_USERNAME'),
password: env.get('DB_PASSWORD'),
database: env.get('DB_DATABASE'),
},
slaves: [
{
host: env.get('DB_REPLICA_HOST', true),
port: parseInt(env.get('DB_PORT')),
username: env.get('DB_USERNAME'),
password: env.get('DB_PASSWORD'),
database: env.get('DB_DATABASE'),
},
],
removeNodeErrorCount: 10,
restoreNodeTimeout: 5,
}
const mySQLDataSourceOptions: MysqlConnectionOptions = {
...commonDataSourceOptions,
type: 'mysql',
charset: 'utf8mb4',
supportBigNumbers: true,
bigNumberStrings: false,
replication: inReplicaMode ? replicationConfig : undefined,
host: inReplicaMode ? undefined : env.get('DB_HOST'),
port: inReplicaMode ? undefined : parseInt(env.get('DB_PORT')),
username: inReplicaMode ? undefined : env.get('DB_USERNAME'),
password: inReplicaMode ? undefined : env.get('DB_PASSWORD'),
database: inReplicaMode ? undefined : env.get('DB_DATABASE'),
}
dataSource = new DataSource(mySQLDataSourceOptions)
} else {
const sqliteDataSourceOptions: SqliteConnectionOptions = {
...commonDataSourceOptions,
type: 'sqlite',
database: `data/${env.get('DB_DATABASE')}.sqlite`,
}
dataSource = new DataSource(sqliteDataSourceOptions)
}
export const AppDataSource = dataSource

View File

@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.9.2](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.9.1...@standardnotes/websockets-server@1.9.2) (2023-06-28)
**Note:** Version bump only for package @standardnotes/websockets-server
## [1.9.1](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.9.0...@standardnotes/websockets-server@1.9.1) (2023-06-02)
### Bug Fixes
* **home-server:** streaming logs ([a8b806a](https://github.com/standardnotes/server/commit/a8b806af084b3e3fe8707ff0cb041a74042ee049))
# [1.9.0](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.8.6...@standardnotes/websockets-server@1.9.0) (2023-06-02)
### Features

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/websockets-server",
"version": "1.9.0",
"version": "1.9.2",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -66,9 +66,9 @@ export class ContainerConfigLoader {
}
const logger = winston.createLogger({
level: env.get('LOG_LEVEL') || 'info',
level: env.get('LOG_LEVEL', true) || 'info',
format: winston.format.combine(...winstonFormatters),
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL') || 'info' })],
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL', true) || 'info' })],
})
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(logger)