feat: redesign service route (#2835)

* feat: redesign service route

* chore: more changes

* remove automated backups and fix interface addresses

* fix rpc methods and slightly better mocks

---------

Co-authored-by: Matt Hill <mattnine@protonmail.com>
This commit is contained in:
Alex Inkin
2025-02-25 19:33:35 +04:00
committed by GitHub
parent 1b006599cf
commit 7fff9579c0
70 changed files with 2437 additions and 2325 deletions

View File

@@ -1,7 +1,6 @@
import {
InstalledState,
PackageDataEntry,
ServerStatusInfo,
} from 'src/app/services/patch-db/data-model'
import { RR, ServerMetrics, ServerNotifications } from './api.types'
import { BTC_ICON, LND_ICON, PROXY_ICON, REGISTRY_ICON } from './api-icons'
@@ -22,14 +21,15 @@ const mockDescription = {
long: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
}
export module Mock {
export const ServerUpdated: ServerStatusInfo = {
currentBackup: null,
export namespace Mock {
export const ServerUpdated: T.ServerStatus = {
backupProgress: null,
updateProgress: null,
updated: true,
restarting: false,
shuttingDown: false,
}
export const MarketplaceEos: RR.CheckOSUpdateRes = {
version: '0.3.6',
headline: 'Our biggest release ever.',
@@ -1012,131 +1012,192 @@ export module Mock {
}
export const BackupTargets: RR.GetBackupTargetsRes = {
unknownDisks: [
{
logicalname: 'sbc4',
label: 'My Backup Drive',
capacity: 2000000000000,
used: 100000000000,
model: 'T7',
vendor: 'Samsung',
startOs: {},
},
],
saved: {
hsbdjhasbasda: {
type: 'cifs',
name: 'Embassy Backups',
hostname: 'smb://192.169.10.0',
path: '/Desktop/embassy-backups',
username: 'TestUser',
mountable: true,
startOs: {
abcdefgh: {
hostname: 'adjective-noun.local',
version: '0.3.6',
timestamp: new Date().toISOString(),
passwordHash:
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
wrappedKey: '',
},
hsbdjhasbasda: {
type: 'cifs',
hostname: 'smb://192.169.10.0',
path: '/Desktop/startos-backups',
username: 'TestUser',
mountable: false,
startOs: {
'1234-5678-9876-5432': {
hostname: 'adjective-noun',
timestamp: new Date().toISOString(),
version: '0.3.6',
passwordHash:
// password is asdfasdf
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
wrappedKey: '',
},
},
ftcvewdnkemfksdm: {
type: 'cloud',
name: 'Dropbox 1',
provider: 'dropbox',
path: '/Home/backups',
mountable: false,
startOs: {},
},
csgashbdjkasnd: {
type: 'cifs',
name: 'Network Folder 2',
hostname: 'smb://192.169.10.0',
path: '/Desktop/embassy-backups-2',
username: 'TestUser',
mountable: true,
startOs: {},
},
powjefhjbnwhdva: {
type: 'disk',
name: 'Physical Drive 1',
logicalname: 'sdba1',
label: 'Another Drive',
capacity: 2000000000000,
used: 100000000000,
model: null,
vendor: 'SSK',
mountable: true,
path: '/HomeFolder/Documents',
startOs: {
'different-server': {
hostname: 'different-server.local',
version: '0.3.6',
timestamp: new Date().toISOString(),
passwordHash:
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
wrappedKey: '',
},
},
// 'ftcvewdnkemfksdm': {
// type: 'disk',
// logicalname: 'sdba1',
// label: 'Matt Stuff',
// capacity: 1000000000000,
// used: 0,
// model: 'Evo SATA 2.5',
// vendor: 'Samsung',
// startOs: {},
// },
csgashbdjkasnd: {
type: 'cifs',
hostname: 'smb://192.169.10.0',
path: '/Desktop/startos-backups-2',
username: 'TestUser',
mountable: true,
startOs: {},
},
powjefhjbnwhdva: {
type: 'disk',
logicalname: 'sdba1',
label: 'Another Drive',
capacity: 2000000000000,
used: 100000000000,
model: null,
vendor: 'SSK',
startOs: {
'1234-5678-9876-5432': {
hostname: 'adjective-noun',
timestamp: new Date().toISOString(),
version: '0.3.6',
passwordHash:
// password is asdfasdf
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
wrappedKey: '',
},
},
},
}
export const BackupJobs: RR.GetBackupJobsRes = [
{
id: 'lalalalalala-babababababa',
name: 'My Backup Job',
targetId: Object.keys(BackupTargets.saved)[0],
cron: '0 3 * * *',
packageIds: ['bitcoind', 'lnd'],
},
{
id: 'hahahahaha-mwmwmwmwmwmw',
name: 'Another Backup Job',
targetId: Object.keys(BackupTargets.saved)[1],
cron: '0 * * * *',
packageIds: ['lnd'],
},
]
// @TODO 041
export const BackupRuns: RR.GetBackupRunsRes = [
{
id: 'kladhbfweubdsk',
startedAt: new Date().toISOString(),
completedAt: new Date(new Date().valueOf() + 10000).toISOString(),
packageIds: ['bitcoind', 'lnd'],
job: BackupJobs[0],
report: {
server: {
attempted: true,
error: null,
},
packages: {
bitcoind: { error: null },
lnd: { error: null },
},
},
},
{
id: 'kladhbfwhrfeubdsk',
startedAt: new Date().toISOString(),
completedAt: new Date(new Date().valueOf() + 10000).toISOString(),
packageIds: ['bitcoind', 'lnd'],
job: BackupJobs[0],
report: {
server: {
attempted: true,
error: null,
},
packages: {
bitcoind: { error: null },
lnd: { error: null },
},
},
},
]
// export const BackupTargets: RR.GetBackupTargetsRes = {
// unknownDisks: [
// {
// logicalname: 'sbc4',
// label: 'My Backup Drive',
// capacity: 2000000000000,
// used: 100000000000,
// model: 'T7',
// vendor: 'Samsung',
// startOs: {},
// },
// ],
// saved: {
// hsbdjhasbasda: {
// type: 'cifs',
// name: 'Embassy Backups',
// hostname: 'smb://192.169.10.0',
// path: '/Desktop/embassy-backups',
// username: 'TestUser',
// mountable: true,
// startOs: {
// abcdefgh: {
// hostname: 'adjective-noun.local',
// version: '0.3.6',
// timestamp: new Date().toISOString(),
// passwordHash:
// '$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
// wrappedKey: '',
// },
// },
// },
// ftcvewdnkemfksdm: {
// type: 'cloud',
// name: 'Dropbox 1',
// provider: 'dropbox',
// path: '/Home/backups',
// mountable: false,
// startOs: {},
// },
// csgashbdjkasnd: {
// type: 'cifs',
// name: 'Network Folder 2',
// hostname: 'smb://192.169.10.0',
// path: '/Desktop/embassy-backups-2',
// username: 'TestUser',
// mountable: true,
// startOs: {},
// },
// powjefhjbnwhdva: {
// type: 'disk',
// name: 'Physical Drive 1',
// logicalname: 'sdba1',
// label: 'Another Drive',
// capacity: 2000000000000,
// used: 100000000000,
// model: null,
// vendor: 'SSK',
// mountable: true,
// path: '/HomeFolder/Documents',
// startOs: {
// 'different-server': {
// hostname: 'different-server.local',
// version: '0.3.6',
// timestamp: new Date().toISOString(),
// passwordHash:
// '$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
// wrappedKey: '',
// },
// },
// },
// },
// }
// export const BackupJobs: RR.GetBackupJobsRes = [
// {
// id: 'lalalalalala-babababababa',
// name: 'My Backup Job',
// targetId: Object.keys(BackupTargets.saved)[0],
// cron: '0 3 * * *',
// packageIds: ['bitcoind', 'lnd'],
// },
// {
// id: 'hahahahaha-mwmwmwmwmwmw',
// name: 'Another Backup Job',
// targetId: Object.keys(BackupTargets.saved)[1],
// cron: '0 * * * *',
// packageIds: ['lnd'],
// },
// ]
// export const BackupRuns: RR.GetBackupRunsRes = [
// {
// id: 'kladhbfweubdsk',
// startedAt: new Date().toISOString(),
// completedAt: new Date(new Date().valueOf() + 10000).toISOString(),
// packageIds: ['bitcoind', 'lnd'],
// job: BackupJobs[0],
// report: {
// server: {
// attempted: true,
// error: null,
// },
// packages: {
// bitcoind: { error: null },
// lnd: { error: null },
// },
// },
// },
// {
// id: 'kladhbfwhrfeubdsk',
// startedAt: new Date().toISOString(),
// completedAt: new Date(new Date().valueOf() + 10000).toISOString(),
// packageIds: ['bitcoind', 'lnd'],
// job: BackupJobs[0],
// report: {
// server: {
// attempted: true,
// error: null,
// },
// packages: {
// bitcoind: { error: null },
// lnd: { error: null },
// },
// },
// },
// ]
export const BackupInfo: RR.GetBackupInfoRes = {
version: '0.3.6',
@@ -1819,9 +1880,7 @@ export module Mock {
},
dataVersion: MockManifestBitcoind.version,
icon: '/assets/img/service-icons/bitcoind.svg',
installedAt: new Date().toISOString(),
lastBackup: null,
nextBackup: null,
status: {
main: 'running',
started: new Date().toISOString(),
@@ -2065,7 +2124,6 @@ export module Mock {
storeExposedDependents: [],
registry: 'https://registry.start9.com/',
developerKey: 'developer-key',
outboundProxy: null,
requestedActions: {
'bitcoind-config': {
request: {
@@ -2096,9 +2154,7 @@ export module Mock {
},
dataVersion: MockManifestBitcoinProxy.version,
icon: '/assets/img/service-icons/btc-rpc-proxy.png',
installedAt: new Date().toISOString(),
lastBackup: null,
nextBackup: null,
status: {
main: 'stopped',
},
@@ -2133,7 +2189,6 @@ export module Mock {
storeExposedDependents: [],
registry: 'https://registry.start9.com/',
developerKey: 'developer-key',
outboundProxy: null,
requestedActions: {},
}
@@ -2144,9 +2199,7 @@ export module Mock {
},
dataVersion: MockManifestLnd.version,
icon: '/assets/img/service-icons/lnd.png',
installedAt: new Date().toISOString(),
lastBackup: null,
nextBackup: null,
status: {
main: 'stopped',
},
@@ -2239,7 +2292,6 @@ export module Mock {
storeExposedDependents: [],
registry: 'https://registry.start9.com/',
developerKey: 'developer-key',
outboundProxy: null,
requestedActions: {
config: {
active: true,

View File

@@ -1,12 +1,10 @@
import { DomainInfo } from 'src/app/services/patch-db/data-model'
import { FetchLogsReq, FetchLogsRes } from '@start9labs/shared'
import { Dump } from 'patch-db-client'
import { DataModel } from 'src/app/services/patch-db/data-model'
import { StartOSDiskInfo } from '@start9labs/shared'
import { StartOSDiskInfo, FetchLogsReq, FetchLogsRes } from '@start9labs/shared'
import { IST, T } from '@start9labs/start-sdk'
import { WebSocketSubjectConfig } from 'rxjs/webSocket'
export module RR {
export namespace RR {
// websocket
export type WebsocketConfig<T> = Omit<WebSocketSubjectConfig<T>, 'url'>
@@ -70,7 +68,7 @@ export module RR {
uptime: number // seconds
}
export type GetServerLogsReq = FetchLogsReq // server.logs & server.kernel-logs & server.tor-logs
export type GetServerLogsReq = FetchLogsReq // server.logs & server.kernel-logs
export type GetServerLogsRes = FetchLogsRes
export type FollowServerLogsReq = {
@@ -83,6 +81,7 @@ export module RR {
guid: string
}
// @TODO 040 implement websocket
export type FollowServerMetricsReq = {} // server.metrics.follow
export type FollowServerMetricsRes = {
guid: string
@@ -92,9 +91,6 @@ export module RR {
export type UpdateServerReq = { registry: 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
@@ -110,11 +106,6 @@ export module RR {
} // net.tor.reset
export type ResetTorRes = null
export type SetOsOutboundProxyReq = {
proxy: string | null
} // server.proxy.set-outbound
export type SetOsOutboundProxyRes = null
// smtp
export type SetSMTPReq = T.SmtpValue // server.set-smtp
@@ -139,18 +130,13 @@ export module RR {
// notification
export type FollowNotificationsReq = {}
export type FollowNotificationsRes = {
notifications: ServerNotifications
guid: string
}
export type GetNotificationsReq = {
before?: number
limit?: number
} // notification.list
export type GetNotificationsRes = ServerNotification<number>[]
// @TODO 040 all these notification endpoints need updating
export type DeleteNotificationReq = { ids: number[] } // notification.delete
export type DeleteNotificationRes = null
@@ -163,51 +149,12 @@ export module RR {
export type MarkUnseenNotificationReq = DeleteNotificationReq // notification.mark-unseen
export type MarkUnseenNotificationRes = null
// network
export type AddProxyReq = {
name: string
config: string
} // net.proxy.add
export type AddProxyRes = null
export type UpdateProxyReq = {
name: string
} // net.proxy.update
export type UpdateProxyRes = null
export type DeleteProxyReq = { id: string } // net.proxy.delete
export type DeleteProxyRes = null
// domains
export type ClaimStart9ToReq = { networkInterfaceId: string } // 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
}
networkInterfaceId: string
} // 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
// @TODO remove for 040, set at server scope
// export type SetWifiCountryReq = { country: string }
// export type SetWifiCountryRes = null
export type GetWifiReq = {}
export type GetWifiRes = {
ssids: {
@@ -228,23 +175,16 @@ export module RR {
}
export type AddWifiRes = null
// @TODO 040
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 DeleteWifiReq = { ssid: string } // wifi.remove
export type DeleteWifiRes = null
// email
export type ConfigureEmailReq = T.SmtpValue // email.configure
export type ConfigureEmailRes = null
export type TestEmailReq = ConfigureEmailReq & { to: string } // email.test
export type TestEmailRes = null
// ssh
export type GetSSHKeysReq = {} // ssh.list
@@ -253,84 +193,44 @@ export module RR {
export type AddSSHKeyReq = { key: string } // ssh.add
export type AddSSHKeyRes = SSHKey
export type DeleteSSHKeyReq = { fingerprint: string } // ssh.delete
export type DeleteSSHKeyReq = { fingerprint: string } // ssh.remove
export type DeleteSSHKeyRes = null
// backup
export type GetBackupTargetsReq = {} // backup.target.list
export type GetBackupTargetsRes = {
unknownDisks: UnknownDisk[]
saved: Record<string, BackupTarget>
}
export type GetBackupTargetsRes = { [id: string]: BackupTarget }
export type AddCifsBackupTargetReq = {
name: string
path: string
export type AddBackupTargetReq = {
// backup.target.cifs.add
hostname: string
path: 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 = Record<string, BackupTarget>
password: string | null
}
export type AddBackupTargetRes = { [id: string]: CifsBackupTarget }
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 UpdateBackupTargetReq = AddBackupTargetReq & { id: string } // backup.target.cifs.update
export type UpdateBackupTargetRes = AddBackupTargetRes
export type RemoveBackupTargetReq = { id: string } // backup.target.remove
export type RemoveBackupTargetReq = { id: string } // backup.target.cifs.remove
export type RemoveBackupTargetRes = null
export type GetBackupJobsReq = {} // backup.job.list
export type GetBackupJobsRes = BackupJob[]
export type CreateBackupJobReq = {
name: string
export type GetBackupInfoReq = {
// backup.target.info
targetId: string
cron: string
packageIds: 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 = { targetId: string; password: string } // backup.target.info
serverId: string
password: string
}
export type GetBackupInfoRes = BackupInfo
export type CreateBackupReq = { targetId: string; packageIds: string[] } // backup.create
export type CreateBackupReq = {
// backup.create
targetId: string
packageIds: string[]
oldPassword: string | null
password: string
}
export type CreateBackupRes = null
// package
@@ -375,7 +275,7 @@ export module RR {
private: boolean
acme: string | null // "letsencrypt" | "letsencrypt-staging" | Url | null
}
export type ServerAddDomainRes = null
export type AddDomainRes = null
export type ServerRemoveDomainReq = {
// server.host.address.domain.remove
@@ -409,8 +309,8 @@ export module RR {
host: T.HostId // string
}
export type GetPackageLogsReq = GetServerLogsReq & { id: string } // package.logs
export type GetPackageLogsRes = GetServerLogsRes
export type GetPackageLogsReq = FetchLogsReq & { id: string } // package.logs
export type GetPackageLogsRes = FetchLogsRes
export type FollowPackageLogsReq = FollowServerLogsReq & { id: string } // package.logs.follow
export type FollowPackageLogsRes = FollowServerLogsRes
@@ -458,25 +358,12 @@ export module RR {
export type SideloadPackageReq = {
manifest: T.Manifest
icon: string // base64
size: number // bytes
}
export type SideloadPackageRes = {
upload: string
progress: string
upload: string // guid
progress: string // guid
}
export type SetInterfaceClearnetAddressReq = SetServerClearnetAddressReq & {
packageId: string
interfaceId: string
} // package.interface.set-clearnet
export type SetInterfaceClearnetAddressRes = null
export type SetServiceOutboundProxyReq = {
packageId: string
proxy: string | null
} // package.proxy.set-outbound
export type SetServiceOutboundProxyRes = null
// registry
/** these are returned in ASCENDING order. the newest available version will be the LAST in the object */
@@ -534,20 +421,6 @@ export type ServerMetrics = {
}
}
export type AppMetrics = {
memory: {
percentageUsed: MetricData
used: MetricData
}
cpu: {
percentageUsed: MetricData
}
disk: {
percentageUsed: MetricData
used: MetricData
}
}
export type Session = {
loggedIn: string
lastActive: string
@@ -576,59 +449,41 @@ export type PlatformType =
| 'desktop'
| 'hybrid'
export type RemoteBackupTarget = CifsBackupTarget | CloudBackupTarget
export type BackupTarget = RemoteBackupTarget | DiskBackupTarget
export type BackupTarget = DiskBackupTarget | CifsBackupTarget
export type BackupTargetType = 'disk' | 'cifs' | 'cloud'
export interface UnknownDisk {
logicalname: string
export interface DiskBackupTarget {
type: 'disk'
vendor: string | null
model: string | null
logicalname: string | null
label: string | null
capacity: number
used: number | null
startOs: Record<string, StartOSDiskInfo>
}
export interface BaseBackupTarget {
type: BackupTargetType
name: string
mountable: boolean
export interface CifsBackupTarget {
type: 'cifs'
hostname: string
path: string
username: string
mountable: boolean
startOs: Record<string, StartOSDiskInfo>
}
export interface DiskBackupTarget extends UnknownDisk, BaseBackupTarget {
export type RecoverySource = DiskRecoverySource | CifsRecoverySource
export interface DiskRecoverySource {
type: 'disk'
logicalname: string // partition logicalname
}
export interface CifsBackupTarget extends BaseBackupTarget {
export interface CifsRecoverySource {
type: 'cifs'
hostname: string
path: string
username: string
}
export interface CloudBackupTarget extends BaseBackupTarget {
type: 'cloud'
provider: 'dropbox' | 'google-drive'
}
export type BackupRun = {
id: string
startedAt: string
completedAt: string
packageIds: string[]
job: BackupJob
report: BackupReport
}
export type BackupJob = {
id: string
name: string
targetId: string
cron: string // '* * * * * *' https://cloud.google.com/scheduler/docs/configuring/cron-job-schedules
packageIds: string[]
password: string
}
export type BackupInfo = {
@@ -664,13 +519,16 @@ export type ServerNotification<T extends number> = {
packageId: string | null
createdAt: string
code: T
level: 'success' | 'info' | 'warning' | 'error'
level: NotificationLevel
title: string
message: string
data: NotificationData<T>
// @TODO 040
read: boolean
}
export type NotificationLevel = 'success' | 'info' | 'warning' | 'error'
export type NotificationData<T> = T extends 0
? null
: T extends 1
@@ -716,8 +574,6 @@ export type Encrypted = {
encrypted: string
}
export type CloudProvider = 'dropbox' | 'google-drive'
export type DependencyError =
| DependencyErrorNotInstalled
| DependencyErrorNotRunning
@@ -752,3 +608,213 @@ export type DependencyErrorHealthChecksFailed = {
export type DependencyErrorTransitive = {
type: 'transitive'
}
// **** @TODO 041 ****
// export namespace RR041 {
// // ** domains **
// export type ClaimStart9ToReq = { networkInterfaceId: string } // 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
// }
// networkInterfaceId: string
// } // 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
// // ** proxies **
// export type AddProxyReq = {
// name: string
// config: string
// } // net.proxy.add
// export type AddProxyRes = null
// export type UpdateProxyReq = {
// name: string
// } // net.proxy.update
// export type UpdateProxyRes = null
// export type DeleteProxyReq = { id: string } // net.proxy.delete
// export type DeleteProxyRes = null
// // ** set outbound proxies **
// export type SetOsOutboundProxyReq = {
// proxy: string | null
// } // server.proxy.set-outbound
// export type SetOsOutboundProxyRes = null
// export type SetServiceOutboundProxyReq = {
// packageId: string
// proxy: string | null
// } // package.proxy.set-outbound
// export type SetServiceOutboundProxyRes = null
// // ** automated backups **
// export type GetBackupTargetsReq = {} // backup.target.list
// export type GetBackupTargetsRes = {
// unknownDisks: UnknownDisk[]
// saved: Record<string, 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 = Record<string, 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
// targetId: string
// cron: string
// packageIds: 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 = { targetId: string; password: string } // backup.target.info
// export type GetBackupInfoRes = BackupInfo
// export type CreateBackupReq = { targetId: string; packageIds: string[] } // backup.create
// export type CreateBackupRes = null
// }
// @TODO 041 types
// export type AppMetrics = {
// memory: {
// percentageUsed: MetricData
// used: MetricData
// }
// cpu: {
// percentageUsed: MetricData
// }
// disk: {
// percentageUsed: MetricData
// used: MetricData
// }
// }
// 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
// startOs: Record<string, StartOSDiskInfo>
// }
// export interface BaseBackupTarget {
// type: BackupTargetType
// name: string
// mountable: boolean
// path: string
// startOs: Record<string, StartOSDiskInfo>
// }
// 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 type BackupRun = {
// id: string
// startedAt: string
// completedAt: string
// packageIds: string[]
// job: BackupJob
// report: BackupReport
// }
// export type BackupJob = {
// id: string
// name: string
// targetId: string
// cron: string // '* * * * * *' https://cloud.google.com/scheduler/docs/configuring/cron-job-schedules
// packageIds: string[]
// }
// export type CloudProvider = 'dropbox' | 'google-drive'

View File

@@ -5,8 +5,7 @@ import {
} from '@start9labs/marketplace'
import { RPCOptions } from '@start9labs/shared'
import { T } from '@start9labs/start-sdk'
import { Observable } from 'rxjs'
import { BackupTargetType, RR } from './api.types'
import { RR } from './api.types'
import { WebSocketSubject } from 'rxjs/webSocket'
export abstract class ApiService {
@@ -15,8 +14,6 @@ export abstract class ApiService {
// for sideloading packages
abstract uploadPackage(guid: string, body: Blob): Promise<void>
abstract uploadFile(body: Blob): Promise<string>
// for getting static files: ex icons, instructions, licenses
abstract getStaticProxy(
pkg: MarketplacePkg,
@@ -118,10 +115,6 @@ export abstract class ApiService {
abstract updateServer(url?: string): Promise<RR.UpdateServerRes>
abstract setServerClearnetAddress(
params: RR.SetServerClearnetAddressReq,
): Promise<RR.SetServerClearnetAddressRes>
abstract restartServer(
params: RR.RestartServerReq,
): Promise<RR.RestartServerRes>
@@ -134,9 +127,13 @@ export abstract class ApiService {
abstract resetTor(params: RR.ResetTorReq): Promise<RR.ResetTorRes>
abstract setOsOutboundProxy(
params: RR.SetOsOutboundProxyReq,
): Promise<RR.SetOsOutboundProxyRes>
// @TODO 041
// ** server outbound proxy **
// abstract setOsOutboundProxy(
// params: RR.SetOsOutboundProxyReq,
// ): Promise<RR.SetOsOutboundProxyRes>
// smtp
@@ -187,33 +184,39 @@ export abstract class ApiService {
params: RR.DeleteNotificationReq,
): Promise<RR.DeleteNotificationRes>
// network
// ** proxies **
abstract addProxy(params: RR.AddProxyReq): Promise<RR.AddProxyRes>
// @TODO 041
abstract updateProxy(params: RR.UpdateProxyReq): Promise<RR.UpdateProxyRes>
// abstract addProxy(params: RR.AddProxyReq): Promise<RR.AddProxyRes>
abstract deleteProxy(params: RR.DeleteProxyReq): Promise<RR.DeleteProxyRes>
// abstract updateProxy(params: RR.UpdateProxyReq): Promise<RR.UpdateProxyRes>
// domains
// abstract deleteProxy(params: RR.DeleteProxyReq): Promise<RR.DeleteProxyRes>
abstract claimStart9ToDomain(
params: RR.ClaimStart9ToReq,
): Promise<RR.ClaimStart9ToRes>
// ** domains **
abstract deleteStart9ToDomain(
params: RR.DeleteStart9ToReq,
): Promise<RR.DeleteStart9ToRes>
// @TODO 041
abstract addDomain(params: RR.AddDomainReq): Promise<RR.AddDomainRes>
// abstract claimStart9ToDomain(
// params: RR.ClaimStart9ToReq,
// ): Promise<RR.ClaimStart9ToRes>
abstract deleteDomain(params: RR.DeleteDomainReq): Promise<RR.DeleteDomainRes>
// abstract deleteStart9ToDomain(
// params: RR.DeleteStart9ToReq,
// ): Promise<RR.DeleteStart9ToRes>
// port forwards
// abstract addDomain(params: RR.AddDomainReq): Promise<RR.AddDomainRes>
abstract overridePortForward(
params: RR.OverridePortReq,
): Promise<RR.OverridePortRes>
// abstract deleteDomain(params: RR.DeleteDomainReq): Promise<RR.DeleteDomainRes>
// ** port forwards **
// @TODO 041
// abstract overridePortForward(
// params: RR.OverridePortReq,
// ): Promise<RR.OverridePortRes>
// wifi
@@ -245,55 +248,71 @@ export abstract class ApiService {
): Promise<RR.GetBackupTargetsRes>
abstract addBackupTarget(
type: BackupTargetType,
params:
| RR.AddCifsBackupTargetReq
| RR.AddCloudBackupTargetReq
| RR.AddDiskBackupTargetReq,
params: RR.AddBackupTargetReq,
): Promise<RR.AddBackupTargetRes>
abstract updateBackupTarget(
type: BackupTargetType,
params:
| RR.UpdateCifsBackupTargetReq
| RR.UpdateCloudBackupTargetReq
| RR.UpdateDiskBackupTargetReq,
params: RR.UpdateBackupTargetReq,
): 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>
// @TODO 041
// ** automated backups **
// 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>
// package
abstract getPackageLogs(
@@ -336,13 +355,13 @@ export abstract class ApiService {
abstract sideloadPackage(): Promise<RR.SideloadPackageRes>
abstract setInterfaceClearnetAddress(
params: RR.SetInterfaceClearnetAddressReq,
): Promise<RR.SetInterfaceClearnetAddressRes>
// @TODO 041
abstract setServiceOutboundProxy(
params: RR.SetServiceOutboundProxyReq,
): Promise<RR.SetServiceOutboundProxyRes>
// ** service outbound proxy **
// abstract setServiceOutboundProxy(
// params: RR.SetServiceOutboundProxyReq,
// ): Promise<RR.SetServiceOutboundProxyRes>
abstract initAcme(params: RR.InitAcmeReq): Promise<RR.InitAcmeRes>

View File

@@ -9,7 +9,7 @@ import {
} from '@start9labs/shared'
import { PATCH_CACHE } from 'src/app/services/patch-db/patch-db-source'
import { ApiService } from './embassy-api.service'
import { BackupTargetType, RR } from './api.types'
import { RR } from './api.types'
import { ConfigService } from '../config.service'
import { webSocket, WebSocketSubject } from 'rxjs/webSocket'
import { Observable, filter, firstValueFrom } from 'rxjs'
@@ -52,14 +52,6 @@ export class LiveApiService extends ApiService {
})
}
async uploadFile(body: Blob): Promise<string> {
return this.httpRequest({
method: Method.POST,
body,
url: `/rest/upload`,
})
}
// for getting static files: ex. instructions, licenses
async getStaticProxy(
@@ -253,7 +245,8 @@ export class LiveApiService extends ApiService {
async followServerMetrics(
params: RR.FollowServerMetricsReq,
): Promise<RR.FollowServerMetricsRes> {
return this.rpcRequest({ method: 'server.metrics', params })
// @TODO 040 implement .follow
return this.rpcRequest({ method: 'server.metrics.follow', params })
}
async updateServer(url?: string): Promise<RR.UpdateServerRes> {
@@ -263,12 +256,6 @@ export class LiveApiService extends ApiService {
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> {
@@ -289,11 +276,11 @@ export class LiveApiService extends ApiService {
return this.rpcRequest({ method: 'net.tor.reset', params })
}
async setOsOutboundProxy(
params: RR.SetOsOutboundProxyReq,
): Promise<RR.SetOsOutboundProxyRes> {
return this.rpcRequest({ method: 'server.proxy.set-outbound', params })
}
// async setOsOutboundProxy(
// params: RR.SetOsOutboundProxyReq,
// ): Promise<RR.SetOsOutboundProxyRes> {
// return this.rpcRequest({ method: 'server.proxy.set-outbound', params })
// }
// marketplace URLs
@@ -389,49 +376,49 @@ export class LiveApiService extends ApiService {
return this.rpcRequest({ method: 'notification.mark-unseen', params })
}
// network
// proxies
async addProxy(params: RR.AddProxyReq): Promise<RR.AddProxyRes> {
return this.rpcRequest({ method: 'net.proxy.add', params })
}
// 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 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 })
}
// 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 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 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 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 })
}
// 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 })
}
// async overridePortForward(
// params: RR.OverridePortReq,
// ): Promise<RR.OverridePortRes> {
// return this.rpcRequest({ method: 'net.port-forwards.override', params })
// }
// wifi
@@ -455,7 +442,7 @@ export class LiveApiService extends ApiService {
}
async deleteWifi(params: RR.DeleteWifiReq): Promise<RR.DeleteWifiRes> {
return this.rpcRequest({ method: 'wifi.delete', params })
return this.rpcRequest({ method: 'wifi.remove', params })
}
// smtp
@@ -483,7 +470,7 @@ export class LiveApiService extends ApiService {
}
async deleteSshKey(params: RR.DeleteSSHKeyReq): Promise<RR.DeleteSSHKeyRes> {
return this.rpcRequest({ method: 'ssh.delete', params })
return this.rpcRequest({ method: 'ssh.remove', params })
}
// backup
@@ -495,60 +482,22 @@ export class LiveApiService extends ApiService {
}
async addBackupTarget(
type: BackupTargetType,
params: RR.AddCifsBackupTargetReq | RR.AddCloudBackupTargetReq,
params: RR.AddBackupTargetReq,
): Promise<RR.AddBackupTargetRes> {
params.path = params.path.replace('/\\/g', '/')
return this.rpcRequest({ method: `backup.target.${type}.add`, params })
return this.rpcRequest({ method: 'backup.target.cifs.add', params })
}
async updateBackupTarget(
type: BackupTargetType,
params: RR.UpdateCifsBackupTargetReq | RR.UpdateCloudBackupTargetReq,
params: RR.UpdateBackupTargetReq,
): Promise<RR.UpdateBackupTargetRes> {
return this.rpcRequest({ method: `backup.target.${type}.update`, params })
return this.rpcRequest({ method: 'backup.target.cifs.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 })
return this.rpcRequest({ method: 'backup.target.cifs.remove', params })
}
async getBackupInfo(
@@ -561,6 +510,63 @@ export class LiveApiService extends ApiService {
return this.rpcRequest({ method: 'backup.create', 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 })
// }
// package
async getPackageLogs(
@@ -630,21 +636,15 @@ export class LiveApiService extends ApiService {
})
}
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 setServiceOutboundProxy(
// params: RR.SetServiceOutboundProxyReq,
// ): Promise<RR.SetServiceOutboundProxyRes> {
// return this.rpcRequest({ method: 'package.proxy.set-outbound', params })
// }
async removeAcme(params: RR.RemoveAcmeReq): Promise<RR.RemoveAcmeRes> {
return this.rpcRequest({
method: 'net.acme.delete',
method: 'net.acme.remove',
params,
})
}

View File

@@ -16,7 +16,7 @@ import {
StateInfo,
UpdatingState,
} from 'src/app/services/patch-db/data-model'
import { BackupTargetType, RR } from './api.types'
import { CifsBackupTarget, RR } from './api.types'
import { Mock } from './api.fixures'
import { from, interval, map, shareReplay, startWith, Subject, tap } from 'rxjs'
import { mockPatchData } from './mock-patch'
@@ -79,11 +79,6 @@ export class MockApiService extends ApiService {
await pauseFor(2000)
}
async uploadFile(body: Blob): Promise<string> {
await pauseFor(2000)
return 'returnedhash'
}
async getStaticProxy(
pkg: MarketplacePkg,
path: 'LICENSE.md' | 'instructions.md',
@@ -391,23 +386,6 @@ export class MockApiService extends ApiService {
return 'updating'
}
async setServerClearnetAddress(
params: RR.SetServerClearnetAddressReq,
): Promise<RR.SetServerClearnetAddressRes> {
await pauseFor(2000)
const patch = [
{
op: PatchOp.REPLACE,
path: '/serverInfo/ui/domainInfo',
value: params.domainInfo,
},
]
this.mockRevision(patch)
return null
}
async restartServer(
params: RR.RestartServerReq,
): Promise<RR.RestartServerRes> {
@@ -474,22 +452,22 @@ export class MockApiService extends ApiService {
return null
}
async setOsOutboundProxy(
params: RR.SetOsOutboundProxyReq,
): Promise<RR.SetOsOutboundProxyRes> {
await pauseFor(2000)
// async setOsOutboundProxy(
// params: RR.SetOsOutboundProxyReq,
// ): Promise<RR.SetOsOutboundProxyRes> {
// await pauseFor(2000)
const patch = [
{
op: PatchOp.REPLACE,
path: '/serverInfo/network/outboundProxy',
value: params.proxy,
},
]
this.mockRevision(patch)
// const patch = [
// {
// op: PatchOp.REPLACE,
// path: '/serverInfo/network/outboundProxy',
// value: params.proxy,
// },
// ]
// this.mockRevision(patch)
return null
}
// return null
// }
// marketplace URLs
@@ -570,151 +548,151 @@ export class MockApiService extends ApiService {
// network
async addProxy(params: RR.AddProxyReq): Promise<RR.AddProxyRes> {
await pauseFor(2000)
// async addProxy(params: RR.AddProxyReq): Promise<RR.AddProxyRes> {
// await pauseFor(2000)
const patch = [
{
op: PatchOp.ADD,
path: `/serverInfo/network/networkInterfaces/wga1`,
value: {
inbound: true,
outbound: true,
ipInfo: {
name: params.name,
scopeId: 3,
deviceType: 'wireguard',
subnets: [],
wanIp: '1.1.1.1',
ntpServers: [],
},
},
},
]
this.mockRevision(patch)
// const patch = [
// {
// op: PatchOp.ADD,
// path: `/serverInfo/network/networkInterfaces/wga1`,
// value: {
// inbound: true,
// outbound: true,
// ipInfo: {
// name: params.name,
// scopeId: 3,
// deviceType: 'wireguard',
// subnets: [],
// wanIp: '1.1.1.1',
// ntpServers: [],
// },
// },
// },
// ]
// this.mockRevision(patch)
return null
}
// return null
// }
async updateProxy(params: RR.UpdateProxyReq): Promise<RR.UpdateProxyRes> {
await pauseFor(2000)
// async updateProxy(params: RR.UpdateProxyReq): Promise<RR.UpdateProxyRes> {
// await pauseFor(2000)
const patch = [
{
op: PatchOp.REPLACE,
path: `/serverInfo/network/proxies/0/name`,
value: params.name,
},
]
this.mockRevision(patch)
// const patch = [
// {
// op: PatchOp.REPLACE,
// path: `/serverInfo/network/proxies/0/name`,
// value: params.name,
// },
// ]
// this.mockRevision(patch)
return null
}
// return null
// }
async deleteProxy(params: RR.DeleteProxyReq): Promise<RR.DeleteProxyRes> {
await pauseFor(2000)
const patch = [
{
op: PatchOp.REPLACE,
path: '/serverInfo/network/proxies',
value: [],
},
]
this.mockRevision(patch)
// async deleteProxy(params: RR.DeleteProxyReq): Promise<RR.DeleteProxyRes> {
// await pauseFor(2000)
// const patch = [
// {
// op: PatchOp.REPLACE,
// path: '/serverInfo/network/proxies',
// value: [],
// },
// ]
// this.mockRevision(patch)
return null
}
// return null
// }
// domains
async claimStart9ToDomain(
params: RR.ClaimStart9ToReq,
): Promise<RR.ClaimStart9ToRes> {
await pauseFor(2000)
// async claimStart9ToDomain(
// params: RR.ClaimStart9ToReq,
// ): Promise<RR.ClaimStart9ToRes> {
// await pauseFor(2000)
const patch = [
{
op: PatchOp.REPLACE,
path: '/serverInfo/network/start9To',
value: {
subdomain: 'xyz',
networkInterfaceId: params.networkInterfaceId,
},
},
]
this.mockRevision(patch)
// const patch = [
// {
// op: PatchOp.REPLACE,
// path: '/serverInfo/network/start9To',
// value: {
// subdomain: 'xyz',
// networkInterfaceId: params.networkInterfaceId,
// },
// },
// ]
// this.mockRevision(patch)
return null
}
// return null
// }
async deleteStart9ToDomain(
params: RR.DeleteStart9ToReq,
): Promise<RR.DeleteStart9ToRes> {
await pauseFor(2000)
const patch = [
{
op: PatchOp.REPLACE,
path: '/serverInfo/network/start9To',
value: null,
},
]
this.mockRevision(patch)
// async deleteStart9ToDomain(
// params: RR.DeleteStart9ToReq,
// ): Promise<RR.DeleteStart9ToRes> {
// await pauseFor(2000)
// const patch = [
// {
// op: PatchOp.REPLACE,
// path: '/serverInfo/network/start9To',
// value: null,
// },
// ]
// this.mockRevision(patch)
return null
}
// return null
// }
async addDomain(params: RR.AddDomainReq): Promise<RR.AddDomainRes> {
await pauseFor(2000)
// async addDomain(params: RR.AddDomainReq): Promise<RR.AddDomainRes> {
// await pauseFor(2000)
const patch = [
{
op: PatchOp.REPLACE,
path: `/serverInfo/network/domains`,
value: {
[params.hostname]: {
networkInterfaceId: params.networkInterfaceId,
provider: params.provider.name,
},
},
},
]
this.mockRevision(patch)
// const patch = [
// {
// op: PatchOp.REPLACE,
// path: `/serverInfo/network/domains`,
// value: {
// [params.hostname]: {
// networkInterfaceId: params.networkInterfaceId,
// provider: params.provider.name,
// },
// },
// },
// ]
// this.mockRevision(patch)
return null
}
// return null
// }
async deleteDomain(params: RR.DeleteDomainReq): Promise<RR.DeleteDomainRes> {
await pauseFor(2000)
const patch = [
{
op: PatchOp.REPLACE,
path: '/serverInfo/network/domains',
value: {},
},
]
this.mockRevision(patch)
// async deleteDomain(params: RR.DeleteDomainReq): Promise<RR.DeleteDomainRes> {
// await pauseFor(2000)
// const patch = [
// {
// op: PatchOp.REPLACE,
// path: '/serverInfo/network/domains',
// value: {},
// },
// ]
// this.mockRevision(patch)
return null
}
// return null
// }
// port forwards
async overridePortForward(
params: RR.OverridePortReq,
): Promise<RR.OverridePortRes> {
await pauseFor(2000)
// async overridePortForward(
// params: RR.OverridePortReq,
// ): Promise<RR.OverridePortRes> {
// await pauseFor(2000)
const patch = [
{
op: PatchOp.REPLACE,
path: '/serverInfo/network/wanConfig/forwards/0/override',
value: params.port,
},
]
this.mockRevision(patch)
// const patch = [
// {
// op: PatchOp.REPLACE,
// path: '/serverInfo/network/wanConfig/forwards/0/override',
// value: params.port,
// },
// ]
// this.mockRevision(patch)
return null
}
// return null
// }
// wifi
@@ -814,21 +792,16 @@ export class MockApiService extends ApiService {
}
async addBackupTarget(
type: BackupTargetType,
params:
| RR.AddCifsBackupTargetReq
| RR.AddCloudBackupTargetReq
| RR.AddDiskBackupTargetReq,
params: RR.AddBackupTargetReq,
): Promise<RR.AddBackupTargetRes> {
await pauseFor(2000)
const { path, name } = params
const { hostname, path, username } = params
return {
latfgvwdbhjsndmk: {
name,
type: 'cifs',
hostname: 'mockhotname',
hostname,
path: path.replace(/\\/g, '/'),
username: 'mockusername',
username,
mountable: true,
startOs: {},
},
@@ -836,11 +809,18 @@ export class MockApiService extends ApiService {
}
async updateBackupTarget(
type: BackupTargetType,
params: RR.UpdateCifsBackupTargetReq | RR.UpdateCloudBackupTargetReq,
params: RR.UpdateBackupTargetReq,
): Promise<RR.UpdateBackupTargetRes> {
await pauseFor(2000)
return { [params.id]: Mock.BackupTargets.saved[params.id] }
const { id, hostname, path, username } = params
return {
[id]: {
...(Mock.BackupTargets[id] as CifsBackupTarget),
hostname,
path,
username,
},
}
}
async removeBackupTarget(
@@ -850,57 +830,6 @@ export class MockApiService extends ApiService {
return null
}
async getBackupJobs(
params: RR.GetBackupJobsReq,
): Promise<RR.GetBackupJobsRes> {
await pauseFor(2000)
return Mock.BackupJobs
}
async createBackupJob(
params: RR.CreateBackupJobReq,
): Promise<RR.CreateBackupJobRes> {
await pauseFor(2000)
return {
id: 'hjdfbjsahdbn',
name: params.name,
targetId: Object.keys(Mock.BackupTargets.saved)[0],
cron: params.cron,
packageIds: params.packageIds,
}
}
async updateBackupJob(
params: RR.UpdateBackupJobReq,
): Promise<RR.UpdateBackupJobRes> {
await pauseFor(2000)
return {
...Mock.BackupJobs[0],
...params,
}
}
async deleteBackupJob(
params: RR.DeleteBackupJobReq,
): Promise<RR.DeleteBackupJobRes> {
await pauseFor(2000)
return null
}
async getBackupRuns(
params: RR.GetBackupRunsReq,
): Promise<RR.GetBackupRunsRes> {
await pauseFor(2000)
return Mock.BackupRuns
}
async deleteBackupRuns(
params: RR.DeleteBackupRunsReq,
): Promise<RR.DeleteBackupRunsRes> {
await pauseFor(2000)
return null
}
async getBackupInfo(
params: RR.GetBackupInfoReq,
): Promise<RR.GetBackupInfoRes> {
@@ -977,6 +906,94 @@ export class MockApiService extends ApiService {
return null
}
// async addBackupTarget(
// type: BackupTargetType,
// params:
// | RR.AddCifsBackupTargetReq
// | RR.AddCloudBackupTargetReq
// | RR.AddDiskBackupTargetReq,
// ): Promise<RR.AddBackupTargetRes> {
// await pauseFor(2000)
// const { path, name } = params
// return {
// latfgvwdbhjsndmk: {
// name,
// type: 'cifs',
// hostname: 'mockhotname',
// path: path.replace(/\\/g, '/'),
// username: 'mockusername',
// mountable: true,
// startOs: {},
// },
// }
// }
// async updateBackupTarget(
// type: BackupTargetType,
// params: RR.UpdateCifsBackupTargetReq | RR.UpdateCloudBackupTargetReq,
// ): Promise<RR.UpdateBackupTargetRes> {
// await pauseFor(2000)
// return { [params.id]: Mock.BackupTargets.saved[params.id] }
// }
// async removeBackupTarget(
// params: RR.RemoveBackupTargetReq,
// ): Promise<RR.RemoveBackupTargetRes> {
// await pauseFor(2000)
// return null
// }
// async getBackupJobs(
// params: RR.GetBackupJobsReq,
// ): Promise<RR.GetBackupJobsRes> {
// await pauseFor(2000)
// return Mock.BackupJobs
// }
// async createBackupJob(
// params: RR.CreateBackupJobReq,
// ): Promise<RR.CreateBackupJobRes> {
// await pauseFor(2000)
// return {
// id: 'hjdfbjsahdbn',
// name: params.name,
// targetId: Object.keys(Mock.BackupTargets.saved)[0],
// cron: params.cron,
// packageIds: params.packageIds,
// }
// }
// async updateBackupJob(
// params: RR.UpdateBackupJobReq,
// ): Promise<RR.UpdateBackupJobRes> {
// await pauseFor(2000)
// return {
// ...Mock.BackupJobs[0],
// ...params,
// }
// }
// async deleteBackupJob(
// params: RR.DeleteBackupJobReq,
// ): Promise<RR.DeleteBackupJobRes> {
// await pauseFor(2000)
// return null
// }
// async getBackupRuns(
// params: RR.GetBackupRunsReq,
// ): Promise<RR.GetBackupRunsRes> {
// await pauseFor(2000)
// return Mock.BackupRuns
// }
// async deleteBackupRuns(
// params: RR.DeleteBackupRunsReq,
// ): Promise<RR.DeleteBackupRunsRes> {
// await pauseFor(2000)
// return null
// }
// package
async getPackageLogs(
@@ -1308,37 +1325,21 @@ export class MockApiService extends ApiService {
}
}
async setInterfaceClearnetAddress(
params: RR.SetInterfaceClearnetAddressReq,
): Promise<RR.SetInterfaceClearnetAddressRes> {
await pauseFor(2000)
const patch = [
{
op: PatchOp.REPLACE,
path: `/packageData/${params.packageId}/serviceInterfaces/${params.interfaceId}/addressInfo/domainInfo`,
value: params.domainInfo,
},
]
this.mockRevision(patch)
// async setServiceOutboundProxy(
// params: RR.SetServiceOutboundProxyReq,
// ): Promise<RR.SetServiceOutboundProxyRes> {
// await pauseFor(2000)
// const patch = [
// {
// op: PatchOp.REPLACE,
// path: `/packageData/${params.packageId}/outboundProxy`,
// value: params.proxy,
// },
// ]
// this.mockRevision(patch)
return null
}
async setServiceOutboundProxy(
params: RR.SetServiceOutboundProxyReq,
): Promise<RR.SetServiceOutboundProxyRes> {
await pauseFor(2000)
const patch = [
{
op: PatchOp.REPLACE,
path: `/packageData/${params.packageId}/outboundProxy`,
value: params.proxy,
},
]
this.mockRevision(patch)
return null
}
// return null
// }
async initAcme(params: RR.InitAcmeReq): Promise<RR.InitAcmeRes> {
await pauseFor(2000)
@@ -1820,6 +1821,16 @@ export class MockApiService extends ApiService {
},
]
this.mockRevision(patch3)
// quickly revert server to "running" for continued testing
await pauseFor(100)
const patch4 = [
{
op: PatchOp.REPLACE,
path: '/serverInfo/status',
value: 'running',
},
]
this.mockRevision(patch4)
// set patch indicating update is complete
await pauseFor(100)
const patch6 = [

View File

@@ -39,13 +39,6 @@ export const mockPatchData: DataModel = {
selected: null,
lastRegion: null,
},
start9To: null,
domains: {},
wanConfig: {
upnp: true,
forwards: [],
},
outboundProxy: null,
host: {
bindings: {
80: {
@@ -225,9 +218,7 @@ export const mockPatchData: DataModel = {
},
dataVersion: '0.20.0:0',
icon: '/assets/img/service-icons/bitcoind.svg',
installedAt: new Date().toISOString(),
lastBackup: new Date(new Date().valueOf() - 604800001).toISOString(),
nextBackup: new Date(new Date().valueOf() + 100000000).toISOString(),
status: {
main: 'stopped',
},
@@ -475,18 +466,17 @@ export const mockPatchData: DataModel = {
storeExposedDependents: [],
registry: 'https://registry.start9.com/',
developerKey: 'developer-key',
outboundProxy: null,
requestedActions: {
'bitcoind-config': {
request: {
packageId: 'bitcoind',
actionId: 'config',
severity: 'critical',
reason:
'You must run Config before starting Bitcoin for the first time',
},
active: true,
},
// 'bitcoind-config': {
// request: {
// packageId: 'bitcoind',
// actionId: 'config',
// severity: 'critical',
// reason:
// 'You must run Config before starting Bitcoin for the first time',
// },
// active: true,
// },
'bitcoind-properties': {
request: {
packageId: 'bitcoind',
@@ -508,9 +498,7 @@ export const mockPatchData: DataModel = {
},
dataVersion: '0.11.0:0.0.1',
icon: '/assets/img/service-icons/lnd.png',
installedAt: new Date().toISOString(),
lastBackup: null,
nextBackup: null,
status: {
main: 'stopped',
},
@@ -604,7 +592,6 @@ export const mockPatchData: DataModel = {
storeExposedDependents: [],
registry: 'https://registry.start9.com/',
developerKey: 'developer-key',
outboundProxy: null,
requestedActions: {
config: {
active: true,

View File

@@ -1,8 +1,8 @@
import { BackupJob } from '../api/api.types'
import { T } from '@start9labs/start-sdk'
export type DataModel = {
export type DataModel = Omit<T.Public, 'serverInfo'> & {
ui: UIData
// @TODO 040
serverInfo: Omit<
T.Public['serverInfo'],
'wifi' | 'networkInterfaces' | 'host'
@@ -51,54 +51,24 @@ export type NetworkInfo = {
| null
}
}
start9To: {
subdomain: string
networkInterfaceId: string
} | null
domains: {
[key: string]: Domain
}
wanConfig: {
upnp: boolean
forwards: PortForward[]
}
outboundProxy: string | null
}
export type DomainInfo = {
domain: string
subdomain: string | null
}
export type PortForward = {
assigned: number
override: number | null
target: number
error: string | null
}
export type Domain = {
provider: string
networkInterfaceId: string
}
export interface ServerStatusInfo {
currentBackup: null | {
job: BackupJob
backupProgress: Record<string, boolean>
}
updated: boolean
updateProgress: { size: number | null; downloaded: number } | null
restarting: boolean
shuttingDown: boolean
// @TODO 041
// start9To: {
// subdomain: string
// networkInterfaceId: string
// } | null
// domains: {
// [key: string]: Domain
// }
// wanConfig: {
// upnp: boolean
// forwards: PortForward[]
// }
// outboundProxy: string | null
}
export type PackageDataEntry<T extends StateInfo = StateInfo> =
T.PackageDataEntry & {
stateInfo: T
installedAt: string
outboundProxy: string | null
nextBackup: string | null
}
export type AllPackageData = NonNullable<
@@ -129,3 +99,11 @@ export type InstallingInfo = {
progress: T.FullProgress
newManifest: T.Manifest
}
// @TODO 041
// export type ServerStatusInfo = Omit<T.ServerStatus, 'backupProgress'> & {
// currentBackup: null | {
// job: BackupJob
// backupProgress: Record<string, boolean>
// }
// }

View File

@@ -1,89 +1,91 @@
import { Injectable } from '@angular/core'
import { ErrorService, LoadingService } from '@start9labs/shared'
import { TuiDialogOptions } from '@taiga-ui/core'
import { PatchDB } from 'patch-db-client'
import { firstValueFrom } from 'rxjs'
import {
FormComponent,
FormContext,
} from 'src/app/routes/portal/components/form.component'
import { FormDialogService } from 'src/app/services/form-dialog.service'
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
import { ApiService } from './api/embassy-api.service'
import { DataModel } from './patch-db/data-model'
import { ISB } from '@start9labs/start-sdk'
// @TODO 041
@Injectable({
providedIn: 'root',
})
export class ProxyService {
constructor(
private readonly patch: PatchDB<DataModel>,
private readonly formDialog: FormDialogService,
private readonly api: ApiService,
private readonly loader: LoadingService,
private readonly errorService: ErrorService,
) {}
// import { Injectable } from '@angular/core'
// import { ErrorService, LoadingService } from '@start9labs/shared'
// import { TuiDialogOptions } from '@taiga-ui/core'
// import { PatchDB } from 'patch-db-client'
// import { firstValueFrom } from 'rxjs'
// import {
// FormComponent,
// FormContext,
// } from 'src/app/routes/portal/components/form.component'
// import { FormDialogService } from 'src/app/services/form-dialog.service'
// import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
// import { ApiService } from './api/embassy-api.service'
// import { DataModel } from './patch-db/data-model'
// import { ISB } from '@start9labs/start-sdk'
async presentModalSetOutboundProxy(current: string | null, pkgId?: string) {
const networkInterfaces = await firstValueFrom(
this.patch.watch$('serverInfo', 'network', 'networkInterfaces'),
)
const config = ISB.InputSpec.of({
proxyId: ISB.Value.select({
name: 'Select Proxy',
default: current || '',
values: Object.entries(networkInterfaces)
.filter(
([_, n]) => n.outbound && n.ipInfo?.deviceType === 'wireguard',
)
.reduce<Record<string, string>>(
(prev, [id, n]) => ({
[id]: n.ipInfo!.name,
...prev,
}),
{},
),
}),
})
// @Injectable({
// providedIn: 'root',
// })
// export class ProxyService {
// constructor(
// private readonly patch: PatchDB<DataModel>,
// private readonly formDialog: FormDialogService,
// private readonly api: ApiService,
// private readonly loader: LoadingService,
// private readonly errorService: ErrorService,
// ) {}
const options: Partial<
TuiDialogOptions<FormContext<typeof config.validator._TYPE>>
> = {
label: 'Outbound Proxy',
data: {
spec: await configBuilderToSpec(config),
buttons: [
{
text: 'Manage proxies',
link: '/portal/settings/proxies',
},
{
text: 'Save',
handler: async value => {
await this.saveOutboundProxy(value.proxyId, pkgId)
return true
},
},
],
},
}
this.formDialog.open(FormComponent, options)
}
// async presentModalSetOutboundProxy(current: string | null, pkgId?: string) {
// const networkInterfaces = await firstValueFrom(
// this.patch.watch$('serverInfo', 'network', 'networkInterfaces'),
// )
// const config = ISB.InputSpec.of({
// proxyId: ISB.Value.select({
// name: 'Select Proxy',
// default: current || '',
// values: Object.entries(networkInterfaces)
// .filter(
// ([_, n]) => n.outbound && n.ipInfo?.deviceType === 'wireguard',
// )
// .reduce<Record<string, string>>(
// (prev, [id, n]) => ({
// [id]: n.ipInfo!.name,
// ...prev,
// }),
// {},
// ),
// }),
// })
private async saveOutboundProxy(proxy: string | null, packageId?: string) {
const loader = this.loader.open(`Saving`).subscribe()
// const options: Partial<
// TuiDialogOptions<FormContext<typeof config.validator._TYPE>>
// > = {
// label: 'Outbound Proxy',
// data: {
// spec: await configBuilderToSpec(config),
// buttons: [
// {
// text: 'Manage proxies',
// link: '/portal/settings/proxies',
// },
// {
// text: 'Save',
// handler: async value => {
// await this.saveOutboundProxy(value.proxyId, pkgId)
// return true
// },
// },
// ],
// },
// }
// this.formDialog.open(FormComponent, options)
// }
try {
if (packageId) {
await this.api.setServiceOutboundProxy({ packageId, proxy })
} else {
await this.api.setOsOutboundProxy({ proxy })
}
} catch (e: any) {
this.errorService.handleError(e)
} finally {
loader.unsubscribe()
}
}
}
// private async saveOutboundProxy(proxy: string | null, packageId?: string) {
// const loader = this.loader.open(`Saving`).subscribe()
// try {
// if (packageId) {
// await this.api.setServiceOutboundProxy({ packageId, proxy })
// } else {
// await this.api.setOsOutboundProxy({ proxy })
// }
// } catch (e: any) {
// this.errorService.handleError(e)
// } finally {
// loader.unsubscribe()
// }
// }
// }