rename frontend to web

This commit is contained in:
Matt Hill
2023-11-13 15:59:16 -07:00
parent 09303ab2fb
commit 862ca375ee
869 changed files with 0 additions and 66 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,674 @@
import { Dump, Revision } from 'patch-db-client'
import { MarketplacePkg, StoreInfo, Manifest } from '@start9labs/marketplace'
import { InputSpec } from '@start9labs/start-sdk/lib/config/configTypes'
import {
DataModel,
DomainInfo,
NetworkStrategy,
OsOutboundProxy,
ServiceOutboundProxy,
HealthCheckResult,
} from 'src/app/services/patch-db/data-model'
import { StartOSDiskInfo, LogsRes, ServerLogsReq } from '@start9labs/shared'
import { customSmtp } from '@start9labs/start-sdk/lib/config/configConstants'
export module RR {
// DB
export type GetRevisionsRes = Revision[] | Dump<DataModel>
export type GetDumpRes = Dump<DataModel>
export type SetDBValueReq<T> = { pointer: string; value: T } // db.put.ui
export type SetDBValueRes = null
// auth
export type LoginReq = {
password: string
metadata: SessionMetadata
} // auth.login - unauthed
export type loginRes = null
export type LogoutReq = {} // auth.logout
export type LogoutRes = null
export type ResetPasswordReq = {
'old-password': string
'new-password': string
} // auth.reset-password
export type ResetPasswordRes = null
// server
export type EchoReq = { message: string; timeout?: number } // server.echo
export type EchoRes = string
export type GetSystemTimeReq = {} // server.time
export type GetSystemTimeRes = {
now: string
uptime: number // seconds
}
export type GetServerLogsReq = ServerLogsReq // server.logs & server.kernel-logs
export type GetServerLogsRes = LogsRes
export type FollowServerLogsReq = { limit?: number } // server.logs.follow & server.kernel-logs.follow
export type FollowServerLogsRes = {
'start-cursor': string
guid: string
}
export type GetServerMetricsReq = {} // server.metrics
export type GetServerMetricsRes = {
guid: string
metrics: Metrics
}
export type UpdateServerReq = { 'marketplace-url': string } // server.update
export type UpdateServerRes = 'updating' | 'no-updates'
export type SetServerClearnetAddressReq = { domainInfo: DomainInfo | null } // server.set-clearnet
export type SetServerClearnetAddressRes = null
export type RestartServerReq = {} // server.restart
export type RestartServerRes = null
export type ShutdownServerReq = {} // server.shutdown
export type ShutdownServerRes = null
export type SystemRebuildReq = {} // server.rebuild
export type SystemRebuildRes = null
export type ResetTorReq = {
'wipe-state': boolean
reason: string
} // net.tor.reset
export type ResetTorRes = null
export type ToggleZramReq = {
enable: boolean
} // server.experimental.zram
export type ToggleZramRes = null
export type SetOsOutboundProxyReq = {
proxy: OsOutboundProxy
} // server.proxy.set-outbound
export type SetOsOutboundProxyRes = null
// sessions
export type GetSessionsReq = {} // sessions.list
export type GetSessionsRes = {
current: string
sessions: { [hash: string]: Session }
}
export type KillSessionsReq = { ids: string[] } // sessions.kill
export type KillSessionsRes = null
// notification
export type GetNotificationsReq = {
before?: number
limit?: number
} // notification.list
export type GetNotificationsRes = ServerNotification<number>[]
export type DeleteNotificationReq = { id: number } // notification.delete
export type DeleteNotificationRes = null
export type DeleteAllNotificationsReq = { before: number } // notification.delete-before
export type DeleteAllNotificationsRes = null
// network
export type AddProxyReq = {
name: string
config: string
} // net.proxy.add
export type AddProxyRes = null
export type UpdateProxyReq = {
name?: string
primaryInbound?: true
primaryOutbound?: true
} // net.proxy.update
export type UpdateProxyRes = null
export type DeleteProxyReq = { id: string } // net.proxy.delete
export type DeleteProxyRes = null
// domains
export type ClaimStart9ToReq = { networkStrategy: NetworkStrategy } // net.domain.me.claim
export type ClaimStart9ToRes = null
export type DeleteStart9ToReq = {} // net.domain.me.delete
export type DeleteStart9ToRes = null
export type AddDomainReq = {
hostname: string
provider: {
name: string
username: string | null
password: string | null
}
networkStrategy: NetworkStrategy
} // net.domain.add
export type AddDomainRes = null
export type DeleteDomainReq = { hostname: string } // net.domain.delete
export type DeleteDomainRes = null
// port forwards
export type OverridePortReq = { target: number; port: number } // net.port-forwards.override
export type OverridePortRes = null
// wifi
export type GetWifiReq = {}
export type GetWifiRes = {
ssids: {
[ssid: string]: number
}
connected: string | null
country: string | null
ethernet: boolean
'available-wifi': AvailableWifi[]
}
export type AddWifiReq = {
// wifi.add
ssid: string
password: string
priority: number
connect: boolean
}
export type AddWifiRes = null
export type EnableWifiReq = { enable: boolean } // wifi.enable
export type EnableWifiRes = null
export type ConnectWifiReq = { ssid: string } // wifi.connect
export type ConnectWifiRes = null
export type DeleteWifiReq = { ssid: string } // wifi.delete
export type DeleteWifiRes = null
// email
export type ConfigureEmailReq = typeof customSmtp.validator._TYPE // email.configure
export type ConfigureEmailRes = null
export type TestEmailReq = ConfigureEmailReq & { to: string } // email.test
export type TestEmailRes = null
// ssh
export type GetSSHKeysReq = {} // ssh.list
export type GetSSHKeysRes = SSHKey[]
export type AddSSHKeyReq = { key: string } // ssh.add
export type AddSSHKeyRes = SSHKey
export type DeleteSSHKeyReq = { fingerprint: string } // ssh.delete
export type DeleteSSHKeyRes = null
// backup
export type GetBackupTargetsReq = {} // backup.target.list
export type GetBackupTargetsRes = {
'unknown-disks': UnknownDisk[]
saved: BackupTarget[]
}
export type AddCifsBackupTargetReq = {
name: string
path: string
hostname: string
username: string
password?: string
} // backup.target.cifs.add
export type AddCloudBackupTargetReq = {
name: string
path: string
provider: CloudProvider
[params: string]: any
} // backup.target.cloud.add
export type AddDiskBackupTargetReq = {
logicalname: string
name: string
path: string
} // backup.target.disk.add
export type AddBackupTargetRes = BackupTarget
export type UpdateCifsBackupTargetReq = AddCifsBackupTargetReq & {
id: string
} // backup.target.cifs.update
export type UpdateCloudBackupTargetReq = AddCloudBackupTargetReq & {
id: string
} // backup.target.cloud.update
export type UpdateDiskBackupTargetReq = Omit<
AddDiskBackupTargetReq,
'logicalname'
> & {
id: string
} // backup.target.disk.update
export type UpdateBackupTargetRes = AddBackupTargetRes
export type RemoveBackupTargetReq = { id: string } // backup.target.remove
export type RemoveBackupTargetRes = null
export type GetBackupJobsReq = {} // backup.job.list
export type GetBackupJobsRes = BackupJob[]
export type CreateBackupJobReq = {
name: string
'target-id': string
cron: string
'package-ids': string[]
now: boolean
} // backup.job.create
export type CreateBackupJobRes = BackupJob
export type UpdateBackupJobReq = Omit<CreateBackupJobReq, 'now'> & {
id: string
} // backup.job.update
export type UpdateBackupJobRes = CreateBackupJobRes
export type DeleteBackupJobReq = { id: string } // backup.job.delete
export type DeleteBackupJobRes = null
export type GetBackupRunsReq = {} // backup.runs
export type GetBackupRunsRes = BackupRun[]
export type DeleteBackupRunsReq = { ids: string[] } // backup.runs.delete
export type DeleteBackupRunsRes = null
export type GetBackupInfoReq = { 'target-id': string; password: string } // backup.target.info
export type GetBackupInfoRes = BackupInfo
export type CreateBackupReq = { 'target-id': string; 'package-ids': string[] } // backup.create
export type CreateBackupRes = null
// package
export type GetPackageCredentialsReq = { id: string } // package.credentials
export type GetPackageCredentialsRes = Record<string, string>
export type GetPackageLogsReq = ServerLogsReq & { id: string } // package.logs
export type GetPackageLogsRes = LogsRes
export type FollowPackageLogsReq = FollowServerLogsReq & { id: string } // package.logs.follow
export type FollowPackageLogsRes = FollowServerLogsRes
export type InstallPackageReq = {
id: string
'version-spec'?: string
'version-priority'?: 'min' | 'max'
'marketplace-url': string
} // package.install
export type InstallPackageRes = null
export type GetPackageConfigReq = { id: string } // package.config.get
export type GetPackageConfigRes = { spec: InputSpec; config: object }
export type DrySetPackageConfigReq = { id: string; config: object } // package.config.set.dry
export type DrySetPackageConfigRes = Breakages
export type SetPackageConfigReq = DrySetPackageConfigReq // package.config.set
export type SetPackageConfigRes = null
export type RestorePackagesReq = {
// package.backup.restore
ids: string[]
'target-id': string
password: string
}
export type RestorePackagesRes = null
export type ExecutePackageActionReq = {
id: string
'action-id': string
input?: object
} // package.action
export type ExecutePackageActionRes = ActionResponse
export type StartPackageReq = { id: string } // package.start
export type StartPackageRes = null
export type RestartPackageReq = { id: string } // package.restart
export type RestartPackageRes = null
export type StopPackageReq = { id: string } // package.stop
export type StopPackageRes = null
export type UninstallPackageReq = { id: string } // package.uninstall
export type UninstallPackageRes = null
export type DryConfigureDependencyReq = {
'dependency-id': string
'dependent-id': string
} // package.dependency.configure.dry
export type DryConfigureDependencyRes = {
'old-config': object
'new-config': object
spec: InputSpec
}
export type SideloadPackageReq = {
manifest: Manifest
icon: string // base64
size: number // bytes
}
export type SideloadPacakgeRes = string //guid
export type SetInterfaceClearnetAddressReq = SetServerClearnetAddressReq & {
packageId: string
interfaceId: string
} // package.interface.set-clearnet
export type SetInterfaceClearnetAddressRes = null
export type SetServiceOutboundProxyReq = {
packageId: string
proxy: ServiceOutboundProxy
} // package.proxy.set-outbound
export type SetServiceOutboundProxyRes = null
// marketplace
export type GetMarketplaceInfoReq = { 'server-id': string }
export type GetMarketplaceInfoRes = StoreInfo
export type GetMarketplaceEosReq = { 'server-id': string }
export type GetMarketplaceEosRes = MarketplaceEOS
export type GetMarketplacePackagesReq = {
ids?: { id: string; version: string }[]
// iff !ids
category?: string
query?: string
page?: number
'per-page'?: number
}
export type GetMarketplacePackagesRes = MarketplacePkg[]
export type GetReleaseNotesReq = { id: string }
export type GetReleaseNotesRes = { [version: string]: string }
}
export interface MarketplaceEOS {
version: string
headline: string
'release-notes': { [version: string]: string }
}
export interface Breakages {
[id: string]: TaggedDependencyError
}
export interface TaggedDependencyError {
dependency: string
error: DependencyError
}
export interface ActionResponse {
message: string
value: string | null
copyable: boolean
qr: boolean
}
interface MetricData {
value: string
unit: string
}
export interface Metrics {
general: {
temperature: MetricData | null
}
memory: {
total: MetricData
'percentage-used': MetricData
used: MetricData
available: MetricData
'zram-total': MetricData
'zram-used': MetricData
'zram-available': MetricData
}
cpu: {
'percentage-used': MetricData
idle: MetricData
'user-space': MetricData
'kernel-space': MetricData
wait: MetricData
}
disk: {
capacity: MetricData
'percentage-used': MetricData
used: MetricData
available: MetricData
}
}
export interface Session {
'last-active': string
'user-agent': string
metadata: SessionMetadata
}
export interface SessionMetadata {
platforms: PlatformType[]
}
export type PlatformType =
| 'cli'
| 'ios'
| 'ipad'
| 'iphone'
| 'android'
| 'phablet'
| 'tablet'
| 'cordova'
| 'capacitor'
| 'electron'
| 'pwa'
| 'mobile'
| 'mobileweb'
| 'desktop'
| 'hybrid'
export type RemoteBackupTarget = CifsBackupTarget | CloudBackupTarget
export type BackupTarget = RemoteBackupTarget | DiskBackupTarget
export type BackupTargetType = 'disk' | 'cifs' | 'cloud'
export interface UnknownDisk {
logicalname: string
vendor: string | null
model: string | null
label: string | null
capacity: number
used: number | null
}
export interface BaseBackupTarget {
id: string
type: BackupTargetType
name: string
mountable: boolean
path: string
'embassy-os': StartOSDiskInfo | null
}
export interface DiskBackupTarget extends UnknownDisk, BaseBackupTarget {
type: 'disk'
}
export interface CifsBackupTarget extends BaseBackupTarget {
type: 'cifs'
hostname: string
username: string
}
export interface CloudBackupTarget extends BaseBackupTarget {
type: 'cloud'
provider: 'dropbox' | 'google-drive'
}
export interface BackupRun {
id: string
'started-at': string
'completed-at': string
'package-ids': string[]
job: BackupJob
report: BackupReport
}
export interface BackupJob {
id: string
name: string
target: BackupTarget
cron: string // '* * * * * *' https://cloud.google.com/scheduler/docs/configuring/cron-job-schedules
'package-ids': string[]
}
export interface BackupInfo {
version: string
timestamp: string
'package-backups': {
[id: string]: PackageBackupInfo
}
}
export interface PackageBackupInfo {
title: string
version: string
'os-version': string
timestamp: string
}
export interface ServerSpecs {
[key: string]: string | number
}
export interface SSHKey {
'created-at': string
alg: string
hostname: string
fingerprint: string
}
export type ServerNotifications = ServerNotification<any>[]
export interface ServerNotification<T extends number> {
id: number
'package-id': string | null
'created-at': string
code: T
level: NotificationLevel
title: string
message: string
data: NotificationData<T>
}
export enum NotificationLevel {
Success = 'success',
Info = 'info',
Warning = 'warning',
Error = 'error',
}
export type NotificationData<T> = T extends 0
? null
: T extends 1
? BackupReport
: any
export interface BackupReport {
server: {
attempted: boolean
error: string | null
}
packages: {
[id: string]: {
error: string | null
}
}
}
export interface AvailableWifi {
ssid: string
strength: number
security: string[]
}
declare global {
type Stringified<T> = string & {
[P in keyof T]: T[P]
}
interface JSON {
stringify<T>(
value: T,
replacer?: (key: string, value: any) => any,
space?: string | number,
): string & Stringified<T>
parse<T>(text: Stringified<T>, reviver?: (key: any, value: any) => any): T
}
}
export type Encrypted = {
encrypted: string
}
export type CloudProvider = 'dropbox' | 'google-drive'
export type DependencyError =
| DependencyErrorNotInstalled
| DependencyErrorNotRunning
| DependencyErrorIncorrectVersion
| DependencyErrorConfigUnsatisfied
| DependencyErrorHealthChecksFailed
| DependencyErrorTransitive
export enum DependencyErrorType {
NotInstalled = 'not-installed',
NotRunning = 'not-running',
IncorrectVersion = 'incorrect-version',
ConfigUnsatisfied = 'config-unsatisfied',
HealthChecksFailed = 'health-checks-failed',
InterfaceHealthChecksFailed = 'interface-health-checks-failed',
Transitive = 'transitive',
}
export interface DependencyErrorNotInstalled {
type: DependencyErrorType.NotInstalled
}
export interface DependencyErrorNotRunning {
type: DependencyErrorType.NotRunning
}
export interface DependencyErrorIncorrectVersion {
type: DependencyErrorType.IncorrectVersion
expected: string // version range
received: string // version
}
export interface DependencyErrorConfigUnsatisfied {
type: DependencyErrorType.ConfigUnsatisfied
error: string
}
export interface DependencyErrorHealthChecksFailed {
type: DependencyErrorType.HealthChecksFailed
check: HealthCheckResult
}
export interface DependencyErrorTransitive {
type: DependencyErrorType.Transitive
}

View File

@@ -0,0 +1,320 @@
import { Observable } from 'rxjs'
import { Update } from 'patch-db-client'
import { RR, BackupTargetType, Metrics } from './api.types'
import { DataModel } from 'src/app/services/patch-db/data-model'
import { Log, SetupStatus } from '@start9labs/shared'
import { WebSocketSubjectConfig } from 'rxjs/webSocket'
export abstract class ApiService {
// http
// for getting static files: ex icons, instructions, licenses
abstract getStatic(url: string): Promise<string>
// for sideloading packages
abstract uploadPackage(guid: string, body: Blob): Promise<void>
abstract uploadFile(body: Blob): Promise<string>
// db
abstract setDbValue<T>(
pathArr: Array<string | number>,
value: T,
): Promise<RR.SetDBValueRes>
// auth
abstract login(params: RR.LoginReq): Promise<RR.loginRes>
abstract logout(params: RR.LogoutReq): Promise<RR.LogoutRes>
abstract getSessions(params: RR.GetSessionsReq): Promise<RR.GetSessionsRes>
abstract killSessions(params: RR.KillSessionsReq): Promise<RR.KillSessionsRes>
abstract resetPassword(
params: RR.ResetPasswordReq,
): Promise<RR.ResetPasswordRes>
// server
abstract echo(params: RR.EchoReq, urlOverride?: string): Promise<RR.EchoRes>
abstract openPatchWebsocket$(): Observable<Update<DataModel>>
abstract openLogsWebsocket$(
config: WebSocketSubjectConfig<Log>,
): Observable<Log>
abstract openMetricsWebsocket$(
config: WebSocketSubjectConfig<Metrics>,
): Observable<Metrics>
abstract getSystemTime(
params: RR.GetSystemTimeReq,
): Promise<RR.GetSystemTimeRes>
abstract getServerLogs(
params: RR.GetServerLogsReq,
): Promise<RR.GetServerLogsRes>
abstract getKernelLogs(
params: RR.GetServerLogsReq,
): Promise<RR.GetServerLogsRes>
abstract getTorLogs(params: RR.GetServerLogsReq): Promise<RR.GetServerLogsRes>
abstract followServerLogs(
params: RR.FollowServerLogsReq,
): Promise<RR.FollowServerLogsRes>
abstract followKernelLogs(
params: RR.FollowServerLogsReq,
): Promise<RR.FollowServerLogsRes>
abstract followTorLogs(
params: RR.FollowServerLogsReq,
): Promise<RR.FollowServerLogsRes>
abstract getServerMetrics(
params: RR.GetServerMetricsReq,
): Promise<RR.GetServerMetricsRes>
abstract updateServer(url?: string): Promise<RR.UpdateServerRes>
abstract setServerClearnetAddress(
params: RR.SetServerClearnetAddressReq,
): Promise<RR.SetServerClearnetAddressRes>
abstract restartServer(
params: RR.RestartServerReq,
): Promise<RR.RestartServerRes>
abstract shutdownServer(
params: RR.ShutdownServerReq,
): Promise<RR.ShutdownServerRes>
abstract systemRebuild(
params: RR.SystemRebuildReq,
): Promise<RR.SystemRebuildRes>
abstract repairDisk(params: RR.SystemRebuildReq): Promise<RR.SystemRebuildRes>
abstract resetTor(params: RR.ResetTorReq): Promise<RR.ResetTorRes>
abstract toggleZram(params: RR.ToggleZramReq): Promise<RR.ToggleZramRes>
abstract setOsOutboundProxy(
params: RR.SetOsOutboundProxyReq,
): Promise<RR.SetOsOutboundProxyRes>
// marketplace URLs
abstract marketplaceProxy<T>(
path: string,
params: Record<string, unknown>,
url: string,
): Promise<T>
abstract getEos(): Promise<RR.GetMarketplaceEosRes>
// notification
abstract getNotifications(
params: RR.GetNotificationsReq,
): Promise<RR.GetNotificationsRes>
abstract deleteNotification(
params: RR.DeleteNotificationReq,
): Promise<RR.DeleteNotificationRes>
abstract deleteAllNotifications(
params: RR.DeleteAllNotificationsReq,
): Promise<RR.DeleteAllNotificationsRes>
// network
abstract addProxy(params: RR.AddProxyReq): Promise<RR.AddProxyRes>
abstract updateProxy(params: RR.UpdateProxyReq): Promise<RR.UpdateProxyRes>
abstract deleteProxy(params: RR.DeleteProxyReq): Promise<RR.DeleteProxyRes>
// domains
abstract claimStart9ToDomain(
params: RR.ClaimStart9ToReq,
): Promise<RR.ClaimStart9ToRes>
abstract deleteStart9ToDomain(
params: RR.DeleteStart9ToReq,
): Promise<RR.DeleteStart9ToRes>
abstract addDomain(params: RR.AddDomainReq): Promise<RR.AddDomainRes>
abstract deleteDomain(params: RR.DeleteDomainReq): Promise<RR.DeleteDomainRes>
// port forwards
abstract overridePortForward(
params: RR.OverridePortReq,
): Promise<RR.OverridePortRes>
// wifi
abstract enableWifi(params: RR.EnableWifiReq): Promise<RR.EnableWifiRes>
abstract getWifi(
params: RR.GetWifiReq,
timeout: number,
): Promise<RR.GetWifiRes>
abstract addWifi(params: RR.AddWifiReq): Promise<RR.AddWifiRes>
abstract connectWifi(params: RR.ConnectWifiReq): Promise<RR.ConnectWifiRes>
abstract deleteWifi(params: RR.DeleteWifiReq): Promise<RR.DeleteWifiRes>
// email
abstract testEmail(params: RR.TestEmailReq): Promise<RR.TestEmailRes>
abstract configureEmail(
params: RR.ConfigureEmailReq,
): Promise<RR.ConfigureEmailRes>
// ssh
abstract getSshKeys(params: RR.GetSSHKeysReq): Promise<RR.GetSSHKeysRes>
abstract addSshKey(params: RR.AddSSHKeyReq): Promise<RR.AddSSHKeyRes>
abstract deleteSshKey(params: RR.DeleteSSHKeyReq): Promise<RR.DeleteSSHKeyRes>
// backup
abstract getBackupTargets(
params: RR.GetBackupTargetsReq,
): Promise<RR.GetBackupTargetsRes>
abstract addBackupTarget(
type: BackupTargetType,
params:
| RR.AddCifsBackupTargetReq
| RR.AddCloudBackupTargetReq
| RR.AddDiskBackupTargetReq,
): Promise<RR.AddBackupTargetRes>
abstract updateBackupTarget(
type: BackupTargetType,
params:
| RR.UpdateCifsBackupTargetReq
| RR.UpdateCloudBackupTargetReq
| RR.UpdateDiskBackupTargetReq,
): Promise<RR.UpdateBackupTargetRes>
abstract removeBackupTarget(
params: RR.RemoveBackupTargetReq,
): Promise<RR.RemoveBackupTargetRes>
abstract getBackupJobs(
params: RR.GetBackupJobsReq,
): Promise<RR.GetBackupJobsRes>
abstract createBackupJob(
params: RR.CreateBackupJobReq,
): Promise<RR.CreateBackupJobRes>
abstract updateBackupJob(
params: RR.UpdateBackupJobReq,
): Promise<RR.UpdateBackupJobRes>
abstract deleteBackupJob(
params: RR.DeleteBackupJobReq,
): Promise<RR.DeleteBackupJobRes>
abstract getBackupRuns(
params: RR.GetBackupRunsReq,
): Promise<RR.GetBackupRunsRes>
abstract deleteBackupRuns(
params: RR.DeleteBackupRunsReq,
): Promise<RR.DeleteBackupRunsRes>
abstract getBackupInfo(
params: RR.GetBackupInfoReq,
): Promise<RR.GetBackupInfoRes>
abstract createBackup(params: RR.CreateBackupReq): Promise<RR.CreateBackupRes>
// package
abstract getPackageCredentials(
params: RR.GetPackageCredentialsReq,
): Promise<RR.GetPackageCredentialsRes>
abstract getPackageLogs(
params: RR.GetPackageLogsReq,
): Promise<RR.GetPackageLogsRes>
abstract followPackageLogs(
params: RR.FollowPackageLogsReq,
): Promise<RR.FollowPackageLogsRes>
abstract installPackage(
params: RR.InstallPackageReq,
): Promise<RR.InstallPackageRes>
abstract getPackageConfig(
params: RR.GetPackageConfigReq,
): Promise<RR.GetPackageConfigRes>
abstract drySetPackageConfig(
params: RR.DrySetPackageConfigReq,
): Promise<RR.DrySetPackageConfigRes>
abstract setPackageConfig(
params: RR.SetPackageConfigReq,
): Promise<RR.SetPackageConfigRes>
abstract restorePackages(
params: RR.RestorePackagesReq,
): Promise<RR.RestorePackagesRes>
abstract executePackageAction(
params: RR.ExecutePackageActionReq,
): Promise<RR.ExecutePackageActionRes>
abstract startPackage(params: RR.StartPackageReq): Promise<RR.StartPackageRes>
abstract restartPackage(
params: RR.RestartPackageReq,
): Promise<RR.RestartPackageRes>
abstract stopPackage(params: RR.StopPackageReq): Promise<RR.StopPackageRes>
abstract uninstallPackage(
params: RR.UninstallPackageReq,
): Promise<RR.UninstallPackageRes>
abstract dryConfigureDependency(
params: RR.DryConfigureDependencyReq,
): Promise<RR.DryConfigureDependencyRes>
abstract sideloadPackage(
params: RR.SideloadPackageReq,
): Promise<RR.SideloadPacakgeRes>
abstract getSetupStatus(): Promise<SetupStatus | null>
abstract followLogs(): Promise<string>
abstract setInterfaceClearnetAddress(
params: RR.SetInterfaceClearnetAddressReq,
): Promise<RR.SetInterfaceClearnetAddressRes>
abstract setServiceOutboundProxy(
params: RR.SetServiceOutboundProxyReq,
): Promise<RR.SetServiceOutboundProxyRes>
}

View File

@@ -0,0 +1,600 @@
import { Inject, Injectable } from '@angular/core'
import {
HttpOptions,
HttpService,
isRpcError,
Log,
Method,
RpcError,
RPCOptions,
SetupStatus,
} from '@start9labs/shared'
import { ApiService } from './embassy-api.service'
import { BackupTargetType, Metrics, RR } from './api.types'
import { ConfigService } from '../config.service'
import { webSocket, WebSocketSubjectConfig } from 'rxjs/webSocket'
import { Observable, filter, firstValueFrom } from 'rxjs'
import { AuthService } from '../auth.service'
import { DOCUMENT } from '@angular/common'
import { DataModel } from '../patch-db/data-model'
import { PatchDB, pathFromArray, Update } from 'patch-db-client'
import { getServerInfo } from 'src/app/util/get-server-info'
@Injectable()
export class LiveApiService extends ApiService {
constructor(
@Inject(DOCUMENT) private readonly document: Document,
private readonly http: HttpService,
private readonly config: ConfigService,
private readonly auth: AuthService,
private readonly patch: PatchDB<DataModel>,
) {
super()
// @ts-ignore
this.document.defaultView.rpcClient = this
}
// for getting static files: ex icons, instructions, licenses
async getStatic(url: string): Promise<string> {
return this.httpRequest({
method: Method.GET,
url,
responseType: 'text',
})
}
// for sideloading packages
async uploadPackage(guid: string, body: Blob): Promise<void> {
return this.httpRequest({
method: Method.POST,
body,
url: `/rest/rpc/${guid}`,
responseType: 'text',
})
}
async uploadFile(body: Blob): Promise<string> {
return this.httpRequest({
method: Method.POST,
body,
url: `/rest/upload`,
responseType: 'text',
})
}
// db
async setDbValue<T>(
pathArr: Array<string | number>,
value: T,
): Promise<RR.SetDBValueRes> {
const pointer = pathFromArray(pathArr)
const params: RR.SetDBValueReq<T> = { pointer, value }
return this.rpcRequest({ method: 'db.put.ui', params })
}
// auth
async login(params: RR.LoginReq): Promise<RR.loginRes> {
return this.rpcRequest({ method: 'auth.login', params })
}
async logout(params: RR.LogoutReq): Promise<RR.LogoutRes> {
return this.rpcRequest({ method: 'auth.logout', params })
}
async getSessions(params: RR.GetSessionsReq): Promise<RR.GetSessionsRes> {
return this.rpcRequest({ method: 'auth.session.list', params })
}
async killSessions(params: RR.KillSessionsReq): Promise<RR.KillSessionsRes> {
return this.rpcRequest({ method: 'auth.session.kill', params })
}
async resetPassword(
params: RR.ResetPasswordReq,
): Promise<RR.ResetPasswordRes> {
return this.rpcRequest({ method: 'auth.reset-password', params })
}
// server
async echo(params: RR.EchoReq, urlOverride?: string): Promise<RR.EchoRes> {
return this.rpcRequest({ method: 'echo', params }, urlOverride)
}
openPatchWebsocket$(): Observable<Update<DataModel>> {
const config: WebSocketSubjectConfig<Update<DataModel>> = {
url: `/db`,
closeObserver: {
next: val => {
if (val.reason === 'UNAUTHORIZED') this.auth.setUnverified()
},
},
}
return this.openWebsocket(config)
}
async followLogs(): Promise<string> {
return this.rpcRequest({ method: 'setup.logs.follow', params: {} })
}
openLogsWebsocket$(config: WebSocketSubjectConfig<Log>): Observable<Log> {
return this.openWebsocket(config)
}
openMetricsWebsocket$(
config: WebSocketSubjectConfig<Metrics>,
): Observable<Metrics> {
return this.openWebsocket(config)
}
async getSystemTime(
params: RR.GetSystemTimeReq,
): Promise<RR.GetSystemTimeRes> {
return this.rpcRequest({ method: 'server.time', params })
}
async getServerLogs(
params: RR.GetServerLogsReq,
): Promise<RR.GetServerLogsRes> {
return this.rpcRequest({ method: 'server.logs', params })
}
async getKernelLogs(
params: RR.GetServerLogsReq,
): Promise<RR.GetServerLogsRes> {
return this.rpcRequest({ method: 'server.kernel-logs', params })
}
async getTorLogs(params: RR.GetServerLogsReq): Promise<RR.GetServerLogsRes> {
return this.rpcRequest({ method: 'net.tor.logs', params })
}
async followServerLogs(
params: RR.FollowServerLogsReq,
): Promise<RR.FollowServerLogsRes> {
return this.rpcRequest({ method: 'server.logs.follow', params })
}
async followKernelLogs(
params: RR.FollowServerLogsReq,
): Promise<RR.FollowServerLogsRes> {
return this.rpcRequest({ method: 'server.kernel-logs.follow', params })
}
async followTorLogs(
params: RR.FollowServerLogsReq,
): Promise<RR.FollowServerLogsRes> {
return this.rpcRequest({ method: 'net.tor.logs.follow', params })
}
async getServerMetrics(
params: RR.GetServerMetricsReq,
): Promise<RR.GetServerMetricsRes> {
return this.rpcRequest({ method: 'server.metrics', params })
}
async updateServer(url?: string): Promise<RR.UpdateServerRes> {
const params = {
'marketplace-url': url || this.config.marketplace.start9,
}
return this.rpcRequest({ method: 'server.update', params })
}
async setServerClearnetAddress(
params: RR.SetServerClearnetAddressReq,
): Promise<RR.SetServerClearnetAddressRes> {
return this.rpcRequest({ method: 'server.set-clearnet', params })
}
async restartServer(
params: RR.RestartServerReq,
): Promise<RR.RestartServerRes> {
return this.rpcRequest({ method: 'server.restart', params })
}
async shutdownServer(
params: RR.ShutdownServerReq,
): Promise<RR.ShutdownServerRes> {
return this.rpcRequest({ method: 'server.shutdown', params })
}
async systemRebuild(
params: RR.RestartServerReq,
): Promise<RR.RestartServerRes> {
return this.rpcRequest({ method: 'server.rebuild', params })
}
async repairDisk(params: RR.RestartServerReq): Promise<RR.RestartServerRes> {
return this.rpcRequest({ method: 'disk.repair', params })
}
async resetTor(params: RR.ResetTorReq): Promise<RR.ResetTorRes> {
return this.rpcRequest({ method: 'net.tor.reset', params })
}
async toggleZram(params: RR.ToggleZramReq): Promise<RR.ToggleZramRes> {
return this.rpcRequest({ method: 'server.experimental.zram', params })
}
async setOsOutboundProxy(
params: RR.SetOsOutboundProxyReq,
): Promise<RR.SetOsOutboundProxyRes> {
return this.rpcRequest({ method: 'server.proxy.set-outbound', params })
}
// marketplace URLs
async marketplaceProxy<T>(
path: string,
qp: Record<string, string>,
baseUrl: string,
): Promise<T> {
const fullUrl = `${baseUrl}${path}?${new URLSearchParams(qp).toString()}`
return this.rpcRequest({
method: 'marketplace.get',
params: { url: fullUrl },
})
}
async getEos(): Promise<RR.GetMarketplaceEosRes> {
const { id } = await getServerInfo(this.patch)
const qp: RR.GetMarketplaceEosReq = { 'server-id': id }
return this.marketplaceProxy(
'/eos/v0/latest',
qp,
this.config.marketplace.start9,
)
}
// notification
async getNotifications(
params: RR.GetNotificationsReq,
): Promise<RR.GetNotificationsRes> {
return this.rpcRequest({ method: 'notification.list', params })
}
async deleteNotification(
params: RR.DeleteNotificationReq,
): Promise<RR.DeleteNotificationRes> {
return this.rpcRequest({ method: 'notification.delete', params })
}
async deleteAllNotifications(
params: RR.DeleteAllNotificationsReq,
): Promise<RR.DeleteAllNotificationsRes> {
return this.rpcRequest({
method: 'notification.delete-before',
params,
})
}
// network
async addProxy(params: RR.AddProxyReq): Promise<RR.AddProxyRes> {
return this.rpcRequest({ method: 'net.proxy.add', params })
}
async updateProxy(params: RR.UpdateProxyReq): Promise<RR.UpdateProxyRes> {
return this.rpcRequest({ method: 'net.proxy.update', params })
}
async deleteProxy(params: RR.DeleteProxyReq): Promise<RR.DeleteProxyRes> {
return this.rpcRequest({ method: 'net.proxy.delete', params })
}
// domains
async claimStart9ToDomain(
params: RR.ClaimStart9ToReq,
): Promise<RR.ClaimStart9ToRes> {
return this.rpcRequest({ method: 'net.domain.me.claim', params })
}
async deleteStart9ToDomain(
params: RR.DeleteStart9ToReq,
): Promise<RR.DeleteStart9ToRes> {
return this.rpcRequest({ method: 'net.domain.me.delete', params })
}
async addDomain(params: RR.AddDomainReq): Promise<RR.AddDomainRes> {
return this.rpcRequest({ method: 'net.domain.add', params })
}
async deleteDomain(params: RR.DeleteDomainReq): Promise<RR.DeleteDomainRes> {
return this.rpcRequest({ method: 'net.domain.delete', params })
}
// port forwards
async overridePortForward(
params: RR.OverridePortReq,
): Promise<RR.OverridePortRes> {
return this.rpcRequest({ method: 'net.port-forwards.override', params })
}
// wifi
async enableWifi(params: RR.EnableWifiReq): Promise<RR.EnableWifiRes> {
return this.rpcRequest({ method: 'wifi.enable', params })
}
async getWifi(
params: RR.GetWifiReq,
timeout?: number,
): Promise<RR.GetWifiRes> {
return this.rpcRequest({ method: 'wifi.get', params, timeout })
}
async addWifi(params: RR.AddWifiReq): Promise<RR.AddWifiRes> {
return this.rpcRequest({ method: 'wifi.add', params })
}
async connectWifi(params: RR.ConnectWifiReq): Promise<RR.ConnectWifiRes> {
return this.rpcRequest({ method: 'wifi.connect', params })
}
async deleteWifi(params: RR.DeleteWifiReq): Promise<RR.DeleteWifiRes> {
return this.rpcRequest({ method: 'wifi.delete', params })
}
// email
async testEmail(params: RR.TestEmailReq): Promise<RR.TestEmailRes> {
return this.rpcRequest({ method: 'email.test', params })
}
async configureEmail(
params: RR.ConfigureEmailReq,
): Promise<RR.ConfigureEmailRes> {
return this.rpcRequest({ method: 'email.configure', params })
}
// ssh
async getSshKeys(params: RR.GetSSHKeysReq): Promise<RR.GetSSHKeysRes> {
return this.rpcRequest({ method: 'ssh.list', params })
}
async addSshKey(params: RR.AddSSHKeyReq): Promise<RR.AddSSHKeyRes> {
return this.rpcRequest({ method: 'ssh.add', params })
}
async deleteSshKey(params: RR.DeleteSSHKeyReq): Promise<RR.DeleteSSHKeyRes> {
return this.rpcRequest({ method: 'ssh.delete', params })
}
// backup
async getBackupTargets(
params: RR.GetBackupTargetsReq,
): Promise<RR.GetBackupTargetsRes> {
return this.rpcRequest({ method: 'backup.target.list', params })
}
async addBackupTarget(
type: BackupTargetType,
params: RR.AddCifsBackupTargetReq | RR.AddCloudBackupTargetReq,
): Promise<RR.AddBackupTargetRes> {
params.path = params.path.replace('/\\/g', '/')
return this.rpcRequest({ method: `backup.target.${type}.add`, params })
}
async updateBackupTarget(
type: BackupTargetType,
params: RR.UpdateCifsBackupTargetReq | RR.UpdateCloudBackupTargetReq,
): Promise<RR.UpdateBackupTargetRes> {
return this.rpcRequest({ method: `backup.target.${type}.update`, params })
}
async removeBackupTarget(
params: RR.RemoveBackupTargetReq,
): Promise<RR.RemoveBackupTargetRes> {
return this.rpcRequest({ method: 'backup.target.remove', params })
}
async getBackupJobs(
params: RR.GetBackupJobsReq,
): Promise<RR.GetBackupJobsRes> {
return this.rpcRequest({ method: 'backup.job.list', params })
}
async createBackupJob(
params: RR.CreateBackupJobReq,
): Promise<RR.CreateBackupJobRes> {
return this.rpcRequest({ method: 'backup.job.create', params })
}
async updateBackupJob(
params: RR.UpdateBackupJobReq,
): Promise<RR.UpdateBackupJobRes> {
return this.rpcRequest({ method: 'backup.job.update', params })
}
async deleteBackupJob(
params: RR.DeleteBackupJobReq,
): Promise<RR.DeleteBackupJobRes> {
return this.rpcRequest({ method: 'backup.job.delete', params })
}
async getBackupRuns(
params: RR.GetBackupRunsReq,
): Promise<RR.GetBackupRunsRes> {
return this.rpcRequest({ method: 'backup.runs.list', params })
}
async deleteBackupRuns(
params: RR.DeleteBackupRunsReq,
): Promise<RR.DeleteBackupRunsRes> {
return this.rpcRequest({ method: 'backup.runs.delete', params })
}
async getBackupInfo(
params: RR.GetBackupInfoReq,
): Promise<RR.GetBackupInfoRes> {
return this.rpcRequest({ method: 'backup.target.info', params })
}
async createBackup(params: RR.CreateBackupReq): Promise<RR.CreateBackupRes> {
return this.rpcRequest({ method: 'backup.create', params })
}
// package
async getPackageCredentials(
params: RR.GetPackageCredentialsReq,
): Promise<RR.GetPackageCredentialsRes> {
return this.rpcRequest({ method: 'package.credentials', params })
}
async getPackageLogs(
params: RR.GetPackageLogsReq,
): Promise<RR.GetPackageLogsRes> {
return this.rpcRequest({ method: 'package.logs', params })
}
async followPackageLogs(
params: RR.FollowServerLogsReq,
): Promise<RR.FollowServerLogsRes> {
return this.rpcRequest({ method: 'package.logs.follow', params })
}
async installPackage(
params: RR.InstallPackageReq,
): Promise<RR.InstallPackageRes> {
return this.rpcRequest({ method: 'package.install', params })
}
async getPackageConfig(
params: RR.GetPackageConfigReq,
): Promise<RR.GetPackageConfigRes> {
return this.rpcRequest({ method: 'package.config.get', params })
}
async drySetPackageConfig(
params: RR.DrySetPackageConfigReq,
): Promise<RR.DrySetPackageConfigRes> {
return this.rpcRequest({ method: 'package.config.set.dry', params })
}
async setPackageConfig(
params: RR.SetPackageConfigReq,
): Promise<RR.SetPackageConfigRes> {
return this.rpcRequest({ method: 'package.config.set', params })
}
async restorePackages(
params: RR.RestorePackagesReq,
): Promise<RR.RestorePackagesRes> {
return this.rpcRequest({ method: 'package.backup.restore', params })
}
async executePackageAction(
params: RR.ExecutePackageActionReq,
): Promise<RR.ExecutePackageActionRes> {
return this.rpcRequest({ method: 'package.action', params })
}
async startPackage(params: RR.StartPackageReq): Promise<RR.StartPackageRes> {
return this.rpcRequest({ method: 'package.start', params })
}
async restartPackage(
params: RR.RestartPackageReq,
): Promise<RR.RestartPackageRes> {
return this.rpcRequest({ method: 'package.restart', params })
}
async stopPackage(params: RR.StopPackageReq): Promise<RR.StopPackageRes> {
return this.rpcRequest({ method: 'package.stop', params })
}
async uninstallPackage(
params: RR.UninstallPackageReq,
): Promise<RR.UninstallPackageRes> {
return this.rpcRequest({ method: 'package.uninstall', params })
}
async dryConfigureDependency(
params: RR.DryConfigureDependencyReq,
): Promise<RR.DryConfigureDependencyRes> {
return this.rpcRequest({
method: 'package.dependency.configure.dry',
params,
})
}
async sideloadPackage(
params: RR.SideloadPackageReq,
): Promise<RR.SideloadPacakgeRes> {
return this.rpcRequest({
method: 'package.sideload',
params,
})
}
async setInterfaceClearnetAddress(
params: RR.SetInterfaceClearnetAddressReq,
): Promise<RR.SetInterfaceClearnetAddressRes> {
return this.rpcRequest({ method: 'package.interface.set-clearnet', params })
}
async setServiceOutboundProxy(
params: RR.SetServiceOutboundProxyReq,
): Promise<RR.SetServiceOutboundProxyRes> {
return this.rpcRequest({ method: 'package.proxy.set-outbound', params })
}
async getSetupStatus() {
return this.rpcRequest<SetupStatus | null>({
method: 'setup.status',
params: {},
})
}
private openWebsocket<T>(config: WebSocketSubjectConfig<T>): Observable<T> {
const { location } = this.document.defaultView!
const protocol = location.protocol === 'http:' ? 'ws' : 'wss'
const host = location.host
config.url = `${protocol}://${host}/ws${config.url}`
return webSocket(config)
}
private async rpcRequest<T>(
options: RPCOptions,
urlOverride?: string,
): Promise<T> {
const res = await this.http.rpcRequest<T>(options, urlOverride)
const body = res.body
if (isRpcError(body)) {
if (body.error.code === 34) {
console.error('Unauthenticated, logging out')
this.auth.setUnverified()
}
throw new RpcError(body.error)
}
const patchSequence = res.headers.get('x-patch-sequence')
if (patchSequence)
await firstValueFrom(
this.patch.cache$.pipe(
filter(({ sequence }) => sequence >= Number(patchSequence)),
),
)
return body.result
}
private async httpRequest<T>(opts: HttpOptions): Promise<T> {
const res = await this.http.httpRequest<T>(opts)
return res.body
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,131 @@
import { DataModel } from 'src/app/services/patch-db/data-model'
import { BUILT_IN_WIDGETS } from 'src/app/apps/ui/pages/widgets/built-in/widgets'
import { Mock } from './api.fixures'
export const mockPatchData: DataModel = {
ui: {
name: `Matt's Server`,
'ack-welcome': '1.0.0',
theme: 'Dark',
desktop: ['lnd'],
widgets: BUILT_IN_WIDGETS.filter(
({ id }) =>
id === 'favorites' ||
id === 'health' ||
id === 'network' ||
id === 'metrics',
),
marketplace: {
'selected-url': 'https://registry.start9.com/',
'known-hosts': {
'https://registry.start9.com/': {
name: 'Start9 Registry',
},
'https://community-registry.start9.com/': {},
'https://beta-registry.start9.com/': {
name: 'Dark9',
},
},
},
gaming: {
snake: {
'high-score': 0,
},
},
'ack-instructions': {},
},
'server-info': {
id: 'abcdefgh',
version: '0.3.5',
country: 'us',
ui: {
lanHostname: 'adjective-noun.local',
torHostname: 'myveryownspecialtoraddress.onion',
ipInfo: {
eth0: {
wireless: false,
ipv4: '10.0.0.1',
ipv6: 'FE80:CD00:0000:0CDE:1257:0000:211E:729CD',
},
},
domainInfo: null,
},
network: {
domains: [],
start9ToSubdomain: null,
wifi: {
enabled: false,
lastRegion: null,
},
wanConfig: {
upnp: false,
forwards: [
{
assigned: 443,
override: null,
target: 443,
error: null,
},
{
assigned: 80,
override: null,
target: 80,
error: null,
},
{
assigned: 8332,
override: null,
target: 8332,
error: null,
},
],
},
proxies: [],
primaryProxies: {
inbound: null,
outbound: null,
},
outboundProxy: null,
},
'last-backup': new Date(new Date().valueOf() - 604800001).toISOString(),
'unread-notification-count': 4,
'eos-version-compat': '>=0.3.0 <=0.3.0.1',
'status-info': {
'current-backup': null,
updated: false,
'update-progress': null,
restarting: false,
'shutting-down': false,
},
pubkey: 'npub1sg6plzptd64u62a878hep2kev88swjh3tw00gjsfl8f237lmu63q0uf63m',
'ca-fingerprint': 'SHA-256: 63 2B 11 99 44 40 17 DF 37 FC C3 DF 0F 3D 15',
'ntp-synced': false,
zram: false,
smtp: {
server: '',
port: 587,
from: '',
login: '',
password: '',
},
'password-hash':
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
platform: 'x86_64-nonfree',
},
'package-data': {
bitcoind: {
...Mock.bitcoind,
manifest: {
...Mock.bitcoind.manifest,
version: '0.19.0',
},
},
lnd: {
...Mock.lnd,
manifest: {
...Mock.lnd.manifest,
version: '0.11.0',
},
},
},
}