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:
Mo
2023-07-03 08:30:48 -05:00
committed by GitHub
parent d79e7b14b1
commit 96f42643a9
367 changed files with 5895 additions and 570 deletions

View File

@@ -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
}
}

View File

@@ -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>
}

View 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)
}
}

View File

@@ -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[]>
}

View File

@@ -0,0 +1,5 @@
export type HomeServerStatus = {
status: 'on' | 'off'
url?: string
errorMessage?: string
}