Compare commits

...

6 Commits

Author SHA1 Message Date
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
37 changed files with 337 additions and 51 deletions

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

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

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.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

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api-gateway",
"version": "1.63.2",
"version": "1.64.0",
"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') {
@@ -57,7 +58,7 @@ export class ContainerConfigLoader {
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

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,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [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

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.116.2",
"version": "1.119.0",
"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?: {
@@ -270,9 +271,10 @@ export class ContainerConfigLoader {
const appDataSource = new AppDataSource(env)
await appDataSource.initialize()
const isConfiguredForHomeServer = env.get('DB_TYPE') === 'sqlite'
const isConfiguredForHomeServer = env.get('MODE', true) === 'home-server'
const isConfiguredForInMemoryCache = env.get('CACHE_TYPE', true) === 'memory'
if (!isConfiguredForHomeServer) {
if (!isConfiguredForInMemoryCache) {
const redisUrl = env.get('REDIS_URL')
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
let redis
@@ -550,7 +552,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(
@@ -578,15 +589,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(
@@ -780,6 +782,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)

View File

@@ -109,7 +109,7 @@ export class AppDataSource {
const sqliteDataSourceOptions: SqliteConnectionOptions = {
...commonDataSourceOptions,
type: 'sqlite',
database: `data/${this.env.get('DB_DATABASE')}.sqlite`,
database: this.env.get('DB_DATABASE'),
}
this.dataSource = new DataSource(sqliteDataSourceOptions)

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

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

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.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

View File

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

View File

@@ -67,7 +67,8 @@ 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'
if (configuration?.logger) {
container.bind<winston.Logger>(TYPES.Files_Logger).toConstantValue(configuration.logger as winston.Logger)
@@ -77,20 +78,11 @@ export class ContainerConfigLoader {
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 +96,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 +143,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 +160,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',
}

View File

@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.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

View File

@@ -2,7 +2,7 @@ import { HomeServer } from '../src/Server/HomeServer'
const homeServer = new HomeServer()
Promise.resolve(homeServer.start())
Promise.resolve(homeServer.start({ dataDirectoryPath: `${__dirname}/../data` }))
.then(() => {
const logStream = homeServer.logs()

View File

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

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'
@@ -24,20 +24,23 @@ import { HomeServerConfiguration } from './HomeServerConfiguration'
export class HomeServer implements HomeServerInterface {
private serverInstance: http.Server | undefined
private authService: AuthServiceInterface | undefined
private logStream: PassThrough = new PassThrough()
async start(configuration?: HomeServerConfiguration): Promise<void> {
async start(configuration: HomeServerConfiguration): Promise<void> {
const controllerContainer = new ControllerContainer()
const serviceContainer = new ServiceContainer()
const directCallDomainEventPublisher = new DirectCallDomainEventPublisher()
const environmentOverrides = {
...configuration?.environment,
DB_TYPE: 'sqlite',
CACHE_TYPE: 'memory',
DB_DATABASE: `${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',
CACHE_TYPE: 'memory',
DB_TYPE: 'sqlite',
DB_DATABASE: 'home_server',
}
const env: Env = new Env(environmentOverrides)
@@ -47,6 +50,7 @@ export class HomeServer implements HomeServerInterface {
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)
@@ -150,6 +154,14 @@ export class HomeServer implements HomeServerInterface {
return this.serverInstance.address() !== null
}
async activatePremiumFeatures(username: string): Promise<Result<string>> {
if (!this.isRunning() || !this.authService) {
return Result.fail('Home server is not running.')
}
return this.authService.activatePremiumFeatures(username)
}
logs(): NodeJS.ReadableStream {
return this.logStream
}

View File

@@ -1,3 +1,4 @@
export interface HomeServerConfiguration {
environment: { [name: string]: string }
dataDirectoryPath: string
environment?: { [name: string]: string }
}

View File

@@ -1,7 +1,9 @@
import { Result } from '@standardnotes/domain-core'
import { HomeServerConfiguration } from './HomeServerConfiguration'
export interface HomeServerInterface {
start(configuration?: HomeServerConfiguration): Promise<void>
activatePremiumFeatures(username: string): Promise<Result<string>>
stop(): Promise<void>
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,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.23.0](https://github.com/standardnotes/server/compare/@standardnotes/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

View File

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

View File

@@ -61,7 +61,7 @@ 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',

View File

@@ -79,7 +79,7 @@ export class AppDataSource {
const sqliteDataSourceOptions: SqliteConnectionOptions = {
...commonDataSourceOptions,
type: 'sqlite',
database: `data/${this.env.get('DB_DATABASE')}.sqlite`,
database: this.env.get('DB_DATABASE'),
}
this.dataSource = new DataSource(sqliteDataSourceOptions)

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,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [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

View File

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

View File

@@ -98,7 +98,7 @@ export class ContainerConfigLoader {
const appDataSource = new AppDataSource(env)
await appDataSource.initialize()
const isConfiguredForHomeServer = env.get('DB_TYPE') === 'sqlite'
const isConfiguredForHomeServer = env.get('MODE', true) === 'home-server'
container.bind<Env>(TYPES.Sync_Env).toConstantValue(env)

View File

@@ -77,7 +77,7 @@ export class AppDataSource {
const sqliteDataSourceOptions: SqliteConnectionOptions = {
...commonDataSourceOptions,
type: 'sqlite',
database: `data/${this.env.get('DB_DATABASE')}.sqlite`,
database: this.env.get('DB_DATABASE'),
}
this.dataSource = new DataSource(sqliteDataSourceOptions)