feat: New one-click Home Server, now in Labs. Launch your own self-hosted server instance with just 1 click from the Preferences window. (#2341)
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
export interface HomeServerEnvironmentConfiguration {
|
||||
jwtSecret: string
|
||||
authJwtSecret: string
|
||||
encryptionServerKey: string
|
||||
pseudoKeyParamsKey: string
|
||||
valetTokenSecret: string
|
||||
port: number
|
||||
logLevel?: string
|
||||
databaseEngine: 'sqlite' | 'mysql'
|
||||
mysqlConfiguration?: {
|
||||
host: string
|
||||
port: number
|
||||
username: string
|
||||
password: string
|
||||
database: string
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
export interface HomeServerManagerInterface {
|
||||
startHomeServer(): Promise<string | undefined>
|
||||
setHomeServerConfiguration(configurationJSONString: string): Promise<void>
|
||||
getHomeServerConfiguration(): Promise<string | undefined>
|
||||
setHomeServerDataLocation(location: string): Promise<void>
|
||||
stopHomeServer(): Promise<string | undefined>
|
||||
activatePremiumFeatures(username: string): Promise<string | undefined>
|
||||
getHomeServerLogs(): Promise<string[]>
|
||||
isHomeServerRunning(): Promise<boolean>
|
||||
getHomeServerUrl(): Promise<string | undefined>
|
||||
getHomeServerLastErrorMessage(): Promise<string | undefined>
|
||||
}
|
||||
171
packages/services/src/Domain/HomeServer/HomeServerService.ts
Normal file
171
packages/services/src/Domain/HomeServer/HomeServerService.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
|
||||
import { ApplicationStage } from '../Application/ApplicationStage'
|
||||
import { DesktopDeviceInterface } from '../Device/DesktopDeviceInterface'
|
||||
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
|
||||
import { AbstractService } from '../Service/AbstractService'
|
||||
import { RawStorageKey } from '../Storage/StorageKeys'
|
||||
|
||||
import { HomeServerServiceInterface } from './HomeServerServiceInterface'
|
||||
import { HomeServerEnvironmentConfiguration } from './HomeServerEnvironmentConfiguration'
|
||||
import { HomeServerStatus } from './HomeServerStatus'
|
||||
|
||||
export class HomeServerService extends AbstractService implements HomeServerServiceInterface {
|
||||
private readonly HOME_SERVER_DATA_DIRECTORY_NAME = '.homeserver'
|
||||
|
||||
constructor(
|
||||
private desktopDevice: DesktopDeviceInterface,
|
||||
protected override internalEventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(internalEventBus)
|
||||
}
|
||||
|
||||
override deinit() {
|
||||
;(this.desktopDevice as unknown) = undefined
|
||||
super.deinit()
|
||||
}
|
||||
|
||||
override async handleApplicationStage(stage: ApplicationStage) {
|
||||
await super.handleApplicationStage(stage)
|
||||
|
||||
switch (stage) {
|
||||
case ApplicationStage.StorageDecrypted_09: {
|
||||
await this.setHomeServerDataLocationOnDevice()
|
||||
break
|
||||
}
|
||||
case ApplicationStage.Launched_10: {
|
||||
await this.startHomeServerIfItIsEnabled()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getHomeServerStatus(): Promise<HomeServerStatus> {
|
||||
const isHomeServerRunning = await this.desktopDevice.isHomeServerRunning()
|
||||
|
||||
if (!isHomeServerRunning) {
|
||||
return { status: 'off', errorMessage: await this.desktopDevice.getHomeServerLastErrorMessage() }
|
||||
}
|
||||
|
||||
return {
|
||||
status: 'on',
|
||||
url: await this.getHomeServerUrl(),
|
||||
}
|
||||
}
|
||||
|
||||
async getHomeServerLogs(): Promise<string[]> {
|
||||
return this.desktopDevice.getHomeServerLogs()
|
||||
}
|
||||
|
||||
async getHomeServerUrl(): Promise<string | undefined> {
|
||||
return this.desktopDevice.getHomeServerUrl()
|
||||
}
|
||||
|
||||
async startHomeServer(): Promise<string | undefined> {
|
||||
return this.desktopDevice.startHomeServer()
|
||||
}
|
||||
|
||||
async stopHomeServer(): Promise<string | undefined> {
|
||||
return this.desktopDevice.stopHomeServer()
|
||||
}
|
||||
|
||||
async isHomeServerRunning(): Promise<boolean> {
|
||||
return this.desktopDevice.isHomeServerRunning()
|
||||
}
|
||||
|
||||
async activatePremiumFeatures(username: string): Promise<Result<string>> {
|
||||
const result = await this.desktopDevice.activatePremiumFeatures(username)
|
||||
|
||||
if (result !== undefined) {
|
||||
return Result.fail(result)
|
||||
}
|
||||
|
||||
return Result.ok('Premium features activated')
|
||||
}
|
||||
|
||||
async setHomeServerConfiguration(config: HomeServerEnvironmentConfiguration): Promise<void> {
|
||||
await this.desktopDevice.setHomeServerConfiguration(JSON.stringify(config))
|
||||
}
|
||||
|
||||
async getHomeServerConfiguration(): Promise<HomeServerEnvironmentConfiguration | undefined> {
|
||||
const configurationJSONString = await this.desktopDevice.getHomeServerConfiguration()
|
||||
if (!configurationJSONString) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return JSON.parse(configurationJSONString) as HomeServerEnvironmentConfiguration
|
||||
}
|
||||
|
||||
async enableHomeServer(): Promise<void> {
|
||||
await this.desktopDevice.setRawStorageValue(RawStorageKey.HomeServerEnabled, 'true')
|
||||
|
||||
await this.startHomeServer()
|
||||
}
|
||||
|
||||
async isHomeServerEnabled(): Promise<boolean> {
|
||||
const value = await this.desktopDevice.getRawStorageValue(RawStorageKey.HomeServerEnabled)
|
||||
|
||||
return value === 'true'
|
||||
}
|
||||
|
||||
async getHomeServerDataLocation(): Promise<string | undefined> {
|
||||
return this.desktopDevice.getRawStorageValue(RawStorageKey.HomeServerDataLocation)
|
||||
}
|
||||
|
||||
async disableHomeServer(): Promise<Result<string>> {
|
||||
await this.desktopDevice.setRawStorageValue(RawStorageKey.HomeServerEnabled, 'false')
|
||||
|
||||
const result = await this.stopHomeServer()
|
||||
if (result !== undefined) {
|
||||
return Result.fail(result)
|
||||
}
|
||||
|
||||
return Result.ok('Home server disabled')
|
||||
}
|
||||
|
||||
async changeHomeServerDataLocation(): Promise<Result<string>> {
|
||||
const oldLocation = await this.getHomeServerDataLocation()
|
||||
const newLocation = await this.desktopDevice.presentDirectoryPickerForLocationChangeAndTransferOld(
|
||||
this.HOME_SERVER_DATA_DIRECTORY_NAME,
|
||||
oldLocation,
|
||||
)
|
||||
|
||||
if (!newLocation) {
|
||||
const lastErrorMessage = await this.desktopDevice.getDirectoryManagerLastErrorMessage()
|
||||
|
||||
return Result.fail(lastErrorMessage ?? 'No location selected')
|
||||
}
|
||||
|
||||
await this.desktopDevice.setRawStorageValue(RawStorageKey.HomeServerDataLocation, newLocation)
|
||||
|
||||
await this.desktopDevice.setHomeServerDataLocation(newLocation)
|
||||
|
||||
return Result.ok(newLocation)
|
||||
}
|
||||
|
||||
async openHomeServerDataLocation(): Promise<void> {
|
||||
const location = await this.getHomeServerDataLocation()
|
||||
if (location) {
|
||||
void this.desktopDevice.openLocation(location)
|
||||
}
|
||||
}
|
||||
|
||||
private async startHomeServerIfItIsEnabled(): Promise<void> {
|
||||
const homeServerIsEnabled = await this.isHomeServerEnabled()
|
||||
if (homeServerIsEnabled) {
|
||||
await this.startHomeServer()
|
||||
}
|
||||
}
|
||||
|
||||
private async setHomeServerDataLocationOnDevice(): Promise<void> {
|
||||
let location = await this.getHomeServerDataLocation()
|
||||
if (!location) {
|
||||
const documentsDirectory = await this.desktopDevice.getUserDocumentsDirectory()
|
||||
location = `${documentsDirectory}/${this.HOME_SERVER_DATA_DIRECTORY_NAME}`
|
||||
}
|
||||
|
||||
await this.desktopDevice.setRawStorageValue(RawStorageKey.HomeServerDataLocation, location)
|
||||
|
||||
await this.desktopDevice.setHomeServerDataLocation(location)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
|
||||
import { HomeServerEnvironmentConfiguration } from './HomeServerEnvironmentConfiguration'
|
||||
import { HomeServerStatus } from './HomeServerStatus'
|
||||
|
||||
export interface HomeServerServiceInterface {
|
||||
activatePremiumFeatures(username: string): Promise<Result<string>>
|
||||
isHomeServerRunning(): Promise<boolean>
|
||||
isHomeServerEnabled(): Promise<boolean>
|
||||
getHomeServerDataLocation(): Promise<string | undefined>
|
||||
enableHomeServer(): Promise<void>
|
||||
disableHomeServer(): Promise<Result<string>>
|
||||
startHomeServer(): Promise<string | undefined>
|
||||
stopHomeServer(): Promise<string | undefined>
|
||||
changeHomeServerDataLocation(): Promise<Result<string>>
|
||||
openHomeServerDataLocation(): Promise<void>
|
||||
getHomeServerConfiguration(): Promise<HomeServerEnvironmentConfiguration | undefined>
|
||||
setHomeServerConfiguration(config: HomeServerEnvironmentConfiguration): Promise<void>
|
||||
getHomeServerUrl(): Promise<string | undefined>
|
||||
getHomeServerStatus(): Promise<HomeServerStatus>
|
||||
getHomeServerLogs(): Promise<string[]>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export type HomeServerStatus = {
|
||||
status: 'on' | 'off'
|
||||
url?: string
|
||||
errorMessage?: string
|
||||
}
|
||||
Reference in New Issue
Block a user