mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
address comments and more
This commit is contained in:
17
web/package-lock.json
generated
17
web/package-lock.json
generated
@@ -5254,23 +5254,6 @@
|
||||
"rxjs": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@taiga-ui/experimental": {
|
||||
"version": "4.0.0-rc.7",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": ">=16.0.0",
|
||||
"@angular/core": ">=16.0.0",
|
||||
"@taiga-ui/addon-commerce": "^4.0.0-rc.7",
|
||||
"@taiga-ui/cdk": "^4.0.0-rc.7",
|
||||
"@taiga-ui/core": "^4.0.0-rc.7",
|
||||
"@taiga-ui/kit": "^4.0.0-rc.7",
|
||||
"@taiga-ui/polymorpheus": "^4.6.4",
|
||||
"rxjs": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@taiga-ui/i18n": {
|
||||
"version": "4.0.0-rc.7",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -14,7 +14,7 @@ import { PolymorpheusContent } from '@taiga-ui/polymorpheus'
|
||||
@Component({
|
||||
selector: 'marketplace-package-screenshots',
|
||||
template: `
|
||||
<!--@TODO Matt or Lucy?-->
|
||||
<!--@TODO future release-->
|
||||
<div
|
||||
*ngIf="$any(pkg).screenshots as screenshots"
|
||||
tuiCarouselButtons
|
||||
|
||||
@@ -40,7 +40,7 @@ export default class LoadingPage {
|
||||
filter(Boolean),
|
||||
take(1),
|
||||
switchMap(({ guid, progress }) =>
|
||||
this.api.openProgressWebsocket$(guid).pipe(
|
||||
this.api.openWebsocket$<T.FullProgress>(guid).pipe(
|
||||
startWith(progress),
|
||||
catchError((_, watch$) =>
|
||||
interval(2000).pipe(
|
||||
|
||||
@@ -2,9 +2,7 @@ import * as jose from 'node-jose'
|
||||
import {
|
||||
DiskInfo,
|
||||
DiskListResponse,
|
||||
FollowLogsReq,
|
||||
FollowLogsRes,
|
||||
Log,
|
||||
PartitionInfo,
|
||||
StartOSDiskInfo,
|
||||
} from '@start9labs/shared'
|
||||
@@ -25,11 +23,8 @@ export abstract class ApiService {
|
||||
abstract execute(setupInfo: T.SetupExecuteParams): Promise<T.SetupProgress> // setup.execute
|
||||
abstract complete(): Promise<T.SetupResult> // setup.complete
|
||||
abstract exit(): Promise<void> // setup.exit
|
||||
abstract followServerLogs(params: FollowLogsReq): Promise<FollowLogsRes> // setup.logs.follow
|
||||
abstract openLogsWebsocket$(
|
||||
config: WebSocketSubjectConfig<Log>,
|
||||
): Observable<Log>
|
||||
abstract openProgressWebsocket$(guid: string): Observable<T.FullProgress>
|
||||
abstract followServerLogs(): Promise<FollowLogsRes> // setup.logs.follow
|
||||
abstract openWebsocket$<T>(guid: string): Observable<T>
|
||||
|
||||
async encrypt(toEncrypt: string): Promise<T.EncryptedWire> {
|
||||
if (!this.pubkey) throw new Error('No pubkey found!')
|
||||
|
||||
@@ -3,11 +3,9 @@ import { Inject, Injectable } from '@angular/core'
|
||||
import {
|
||||
DiskListResponse,
|
||||
encodeBase64,
|
||||
FollowLogsReq,
|
||||
FollowLogsRes,
|
||||
HttpService,
|
||||
isRpcError,
|
||||
Log,
|
||||
RpcError,
|
||||
RPCOptions,
|
||||
StartOSDiskInfo,
|
||||
@@ -15,7 +13,7 @@ import {
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import * as jose from 'node-jose'
|
||||
import { Observable } from 'rxjs'
|
||||
import { webSocket, WebSocketSubjectConfig } from 'rxjs/webSocket'
|
||||
import { webSocket } from 'rxjs/webSocket'
|
||||
import { ApiService } from './api.service'
|
||||
|
||||
@Injectable({
|
||||
@@ -29,12 +27,13 @@ export class LiveApiService extends ApiService {
|
||||
super()
|
||||
}
|
||||
|
||||
openProgressWebsocket$(guid: string): Observable<T.FullProgress> {
|
||||
openWebsocket$<T>(guid: string): Observable<T> {
|
||||
const { location } = this.document.defaultView!
|
||||
const protocol = location.protocol === 'http:' ? 'ws' : 'wss'
|
||||
const host = location.host
|
||||
|
||||
return webSocket({
|
||||
url: `ws://${host}/ws/rpc/${guid}`,
|
||||
url: `${protocol}://${host}/ws/rpc/${guid}`,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -99,12 +98,8 @@ export class LiveApiService extends ApiService {
|
||||
})
|
||||
}
|
||||
|
||||
async followServerLogs(params: FollowLogsReq): Promise<FollowLogsRes> {
|
||||
return this.rpcRequest({ method: 'setup.logs.follow', params })
|
||||
}
|
||||
|
||||
openLogsWebsocket$({ url }: WebSocketSubjectConfig<Log>): Observable<Log> {
|
||||
return webSocket(`http://start.local/ws/${url}`)
|
||||
async followServerLogs(): Promise<FollowLogsRes> {
|
||||
return this.rpcRequest({ method: 'setup.logs.follow', params: {} })
|
||||
}
|
||||
|
||||
async complete(): Promise<T.SetupResult> {
|
||||
|
||||
@@ -2,16 +2,13 @@ import { Injectable } from '@angular/core'
|
||||
import {
|
||||
DiskListResponse,
|
||||
encodeBase64,
|
||||
FollowLogsReq,
|
||||
FollowLogsRes,
|
||||
Log,
|
||||
pauseFor,
|
||||
StartOSDiskInfo,
|
||||
} from '@start9labs/shared'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import * as jose from 'node-jose'
|
||||
import { interval, map, Observable, of } from 'rxjs'
|
||||
import { WebSocketSubjectConfig } from 'rxjs/webSocket'
|
||||
import { interval, map, Observable } from 'rxjs'
|
||||
import { ApiService } from './api.service'
|
||||
|
||||
@Injectable({
|
||||
@@ -47,68 +44,102 @@ export class MockApiService extends ApiService {
|
||||
|
||||
// websocket
|
||||
|
||||
openProgressWebsocket$(guid: string): Observable<T.FullProgress> {
|
||||
return of(PROGRESS)
|
||||
// const numPhases = PROGRESS.phases.length
|
||||
// oldMockProgress$(): Promise<T.FullProgress> {
|
||||
// const numPhases = PROGRESS.phases.length
|
||||
|
||||
// return of(PROGRESS).pipe(
|
||||
// switchMap(full =>
|
||||
// from(PROGRESS.phases).pipe(
|
||||
// mergeScan((full, phase, i) => {
|
||||
// if (
|
||||
// !phase.progress ||
|
||||
// typeof phase.progress !== 'object' ||
|
||||
// !phase.progress.total
|
||||
// ) {
|
||||
// full.phases[i].progress = true
|
||||
// return of(PROGRESS).pipe(
|
||||
// switchMap(full =>
|
||||
// from(PROGRESS.phases).pipe(
|
||||
// mergeScan((full, phase, i) => {
|
||||
// if (
|
||||
// !phase.progress ||
|
||||
// typeof phase.progress !== 'object' ||
|
||||
// !phase.progress.total
|
||||
// ) {
|
||||
// full.phases[i].progress = true
|
||||
|
||||
// if (
|
||||
// full.overall &&
|
||||
// typeof full.overall === 'object' &&
|
||||
// full.overall.total
|
||||
// ) {
|
||||
// const step = full.overall.total / numPhases
|
||||
// full.overall.done += step
|
||||
// }
|
||||
// if (
|
||||
// full.overall &&
|
||||
// typeof full.overall === 'object' &&
|
||||
// full.overall.total
|
||||
// ) {
|
||||
// const step = full.overall.total / numPhases
|
||||
// full.overall.done += step
|
||||
// }
|
||||
|
||||
// return of(full).pipe(delay(2000))
|
||||
// } else {
|
||||
// const total = phase.progress.total
|
||||
// const step = total / 4
|
||||
// let done = phase.progress.done
|
||||
// return of(full).pipe(delay(2000))
|
||||
// } else {
|
||||
// const total = phase.progress.total
|
||||
// const step = total / 4
|
||||
// let done = phase.progress.done
|
||||
|
||||
// return interval(1000).pipe(
|
||||
// takeWhile(() => done < total),
|
||||
// map(() => {
|
||||
// done += step
|
||||
// return interval(1000).pipe(
|
||||
// takeWhile(() => done < total),
|
||||
// map(() => {
|
||||
// done += step
|
||||
|
||||
// console.error(done)
|
||||
// console.error(done)
|
||||
|
||||
// if (
|
||||
// full.overall &&
|
||||
// typeof full.overall === 'object' &&
|
||||
// full.overall.total
|
||||
// ) {
|
||||
// const step = full.overall.total / numPhases / 4
|
||||
// if (
|
||||
// full.overall &&
|
||||
// typeof full.overall === 'object' &&
|
||||
// full.overall.total
|
||||
// ) {
|
||||
// const step = full.overall.total / numPhases / 4
|
||||
|
||||
// full.overall.done += step
|
||||
// }
|
||||
// full.overall.done += step
|
||||
// }
|
||||
|
||||
// if (done === total) {
|
||||
// full.phases[i].progress = true
|
||||
// if (done === total) {
|
||||
// full.phases[i].progress = true
|
||||
|
||||
// if (i === numPhases - 1) {
|
||||
// full.overall = true
|
||||
// }
|
||||
// }
|
||||
// return full
|
||||
// }),
|
||||
// )
|
||||
// }
|
||||
// }, full),
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// if (i === numPhases - 1) {
|
||||
// full.overall = true
|
||||
// }
|
||||
// }
|
||||
// return full
|
||||
// }),
|
||||
// )
|
||||
// }
|
||||
// }, full),
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// }
|
||||
|
||||
openWebsocket$<T>(guid: string): Observable<T> {
|
||||
if (guid === 'logs-guid') {
|
||||
return interval(500).pipe(
|
||||
map(() => ({
|
||||
timestamp: new Date().toISOString(),
|
||||
message: 'fake log entry',
|
||||
bootId: 'boot-id',
|
||||
})),
|
||||
) as Observable<T>
|
||||
} else if (guid === 'progress-guid') {
|
||||
// @TODO mock progress
|
||||
return interval(1000).pipe(
|
||||
map(() => ({
|
||||
overall: true,
|
||||
phases: [
|
||||
{
|
||||
name: 'Preparing Data',
|
||||
progress: true,
|
||||
},
|
||||
{
|
||||
name: 'Transferring Data',
|
||||
progress: true,
|
||||
},
|
||||
{
|
||||
name: 'Finalizing Setup',
|
||||
progress: true,
|
||||
},
|
||||
],
|
||||
})),
|
||||
) as Observable<T>
|
||||
} else {
|
||||
throw new Error('invalid guid type')
|
||||
}
|
||||
}
|
||||
|
||||
private statusIndex = 0
|
||||
@@ -270,24 +301,14 @@ export class MockApiService extends ApiService {
|
||||
}
|
||||
}
|
||||
|
||||
async followServerLogs(params: FollowLogsReq): Promise<FollowLogsRes> {
|
||||
async followServerLogs(): Promise<FollowLogsRes> {
|
||||
await pauseFor(1000)
|
||||
return {
|
||||
startCursor: 'fakestartcursor',
|
||||
guid: 'fake-guid',
|
||||
guid: 'logs-guid',
|
||||
}
|
||||
}
|
||||
|
||||
openLogsWebsocket$(config: WebSocketSubjectConfig<Log>): Observable<Log> {
|
||||
return interval(500).pipe(
|
||||
map(() => ({
|
||||
timestamp: new Date().toISOString(),
|
||||
message: 'fake log entry',
|
||||
bootId: 'boot-id',
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
async complete(): Promise<T.SetupResult> {
|
||||
await pauseFor(1000)
|
||||
return {
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { StaticClassProvider } from '@angular/core'
|
||||
import { bufferTime, defer, map, Observable, scan, switchMap } from 'rxjs'
|
||||
import { WebSocketSubjectConfig } from 'rxjs/webSocket'
|
||||
import { FollowLogsReq, FollowLogsRes, Log } from '../types/api'
|
||||
import { Constructor } from '../types/constructor'
|
||||
import { convertAnsi } from '../util/convert-ansi'
|
||||
|
||||
interface Api {
|
||||
followServerLogs: (params: FollowLogsReq) => Promise<FollowLogsRes>
|
||||
openLogsWebsocket$: (config: WebSocketSubjectConfig<Log>) => Observable<Log>
|
||||
openWebsocket$: (guid: string) => Observable<Log>
|
||||
}
|
||||
|
||||
export function provideSetupLogsService(
|
||||
@@ -22,9 +21,7 @@ export function provideSetupLogsService(
|
||||
|
||||
export class SetupLogsService extends Observable<readonly string[]> {
|
||||
private readonly log$ = defer(() => this.api.followServerLogs({})).pipe(
|
||||
switchMap(({ guid }) =>
|
||||
this.api.openLogsWebsocket$({ url: `/rpc/${guid}` }),
|
||||
),
|
||||
switchMap(({ guid }) => this.api.openWebsocket$(guid)),
|
||||
bufferTime(1000),
|
||||
map(convertAnsi),
|
||||
scan((logs: readonly string[], log) => [...logs, log], []),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @TODO Matt this is T.FullProgress but shared does not depend on sdk
|
||||
// @TODO get types from sdk
|
||||
type Progress = null | boolean | { done: number; total: number | null }
|
||||
type NamedProgress = { name: string; progress: Progress }
|
||||
type FullProgress = { overall: Progress; phases: Array<NamedProgress> }
|
||||
|
||||
@@ -33,7 +33,7 @@ export default class InitializingPage {
|
||||
private readonly state = inject(StateService)
|
||||
|
||||
readonly progress = toSignal(
|
||||
defer(() => from(this.api.initGetProgress())).pipe(
|
||||
defer(() => from(this.api.initFollowProgress())).pipe(
|
||||
switchMap(({ guid, progress }) =>
|
||||
this.api
|
||||
.openWebsocket$<T.FullProgress>(guid, {})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CB, CT, T } from '@start9labs/start-sdk'
|
||||
import { CB, CT, T, utils } from '@start9labs/start-sdk'
|
||||
import { TuiDialogOptions } from '@taiga-ui/core'
|
||||
import { TuiConfirmData } from '@taiga-ui/kit'
|
||||
import { NetworkInfo } from 'src/app/services/patch-db/data-model'
|
||||
@@ -53,75 +53,44 @@ export type AddressDetails = {
|
||||
url: string
|
||||
}
|
||||
|
||||
// @TODO Matt these types have change significantly
|
||||
export function getAddresses(serviceInterface: any): {
|
||||
// T.ServiceInterface): {
|
||||
export function getMultihostAddresses(
|
||||
serviceInterface: T.ServiceInterface,
|
||||
host: T.Host,
|
||||
): {
|
||||
clearnet: AddressDetails[]
|
||||
local: AddressDetails[]
|
||||
tor: AddressDetails[]
|
||||
} {
|
||||
const host = serviceInterface.hostInfo
|
||||
const addressInfo = serviceInterface.addressInfo
|
||||
const username = addressInfo.username ? addressInfo.username + '@' : ''
|
||||
const suffix = addressInfo.suffix || ''
|
||||
|
||||
const hostnames =
|
||||
host.kind === 'multi'
|
||||
? host.hostnames
|
||||
: host.hostname
|
||||
? [host.hostname]
|
||||
: []
|
||||
const hostnamesInfo = host.hostnameInfo[addressInfo.internalPort]
|
||||
|
||||
const clearnet: AddressDetails[] = []
|
||||
const local: AddressDetails[] = []
|
||||
const tor: AddressDetails[] = []
|
||||
|
||||
hostnames.forEach((h: any) => {
|
||||
let scheme = ''
|
||||
let port = ''
|
||||
|
||||
if (h.hostname.sslPort) {
|
||||
port = h.hostname.sslPort === 443 ? '' : `:${h.hostname.sslPort}`
|
||||
scheme = addressInfo.bindOptions.addSsl?.scheme
|
||||
? `${addressInfo.bindOptions.addSsl.scheme}://`
|
||||
: ''
|
||||
}
|
||||
|
||||
if (h.hostname.port) {
|
||||
port = h.hostname.port === 80 ? '' : `:${h.hostname.port}`
|
||||
scheme = addressInfo.bindOptions.scheme
|
||||
? `${addressInfo.bindOptions.scheme}://`
|
||||
: ''
|
||||
}
|
||||
|
||||
if (h.kind === 'onion') {
|
||||
tor.push({
|
||||
label: h.hostname.sslPort ? 'HTTPS' : 'HTTP',
|
||||
url: toHref(scheme, username, h.hostname.value, port, suffix),
|
||||
})
|
||||
} else {
|
||||
const hostnameKind = h.hostname.kind
|
||||
|
||||
if (hostnameKind === 'domain') {
|
||||
tor.push({
|
||||
url: toHref(
|
||||
scheme,
|
||||
username,
|
||||
`${h.hostname.subdomain}.${h.hostname.domain}`,
|
||||
port,
|
||||
suffix,
|
||||
),
|
||||
})
|
||||
hostnamesInfo.forEach(hostnameInfo => {
|
||||
utils.addressHostToUrl(addressInfo, hostnameInfo).forEach(url => {
|
||||
// Onion
|
||||
if (hostnameInfo.kind === 'onion') {
|
||||
tor.push({ url })
|
||||
// IP
|
||||
} else {
|
||||
local.push({
|
||||
label:
|
||||
hostnameKind === 'local'
|
||||
? 'Local'
|
||||
: `${h.networkInterfaceId} (${hostnameKind})`,
|
||||
url: toHref(scheme, username, h.hostname.value, port, suffix),
|
||||
})
|
||||
// Domain
|
||||
if (hostnameInfo.hostname.kind === 'domain') {
|
||||
clearnet.push({ url })
|
||||
// Local
|
||||
} else {
|
||||
const hostnameKind = hostnameInfo.hostname.kind
|
||||
local.push({
|
||||
label:
|
||||
hostnameKind === 'local'
|
||||
? 'Local'
|
||||
: `${hostnameInfo.networkInterfaceId} (${hostnameKind})`,
|
||||
url,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
@@ -130,13 +99,3 @@ export function getAddresses(serviceInterface: any): {
|
||||
tor,
|
||||
}
|
||||
}
|
||||
|
||||
function toHref(
|
||||
scheme: string,
|
||||
username: string,
|
||||
hostname: string,
|
||||
port: string,
|
||||
suffix: string,
|
||||
): string {
|
||||
return `${scheme}${username}${hostname}${port}${suffix}`
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { inject, Pipe, PipeTransform } from '@angular/core'
|
||||
import { convertAnsi, toLocalIsoString } from '@start9labs/shared'
|
||||
import { convertAnsi, Log, toLocalIsoString } from '@start9labs/shared'
|
||||
import {
|
||||
bufferTime,
|
||||
catchError,
|
||||
@@ -44,7 +44,13 @@ export class LogsPipe implements PipeTransform {
|
||||
),
|
||||
defer(() => followLogs(this.options)).pipe(
|
||||
tap(r => this.logs.setCursor(r.startCursor)),
|
||||
switchMap(r => this.api.openLogsWebsocket$(this.toConfig(r.guid))),
|
||||
switchMap(r =>
|
||||
this.api.openWebsocket$<Log>(r.guid, {
|
||||
openObserver: {
|
||||
next: () => this.logs.status$.next('connected'),
|
||||
},
|
||||
}),
|
||||
),
|
||||
bufferTime(1000),
|
||||
filter(logs => !!logs.length),
|
||||
map(convertAnsi),
|
||||
@@ -67,15 +73,6 @@ export class LogsPipe implements PipeTransform {
|
||||
private get options() {
|
||||
return this.logs.status$.value === 'connected' ? { limit: 400 } : {}
|
||||
}
|
||||
|
||||
private toConfig(guid: string) {
|
||||
return {
|
||||
url: `/rpc/${guid}`,
|
||||
openObserver: {
|
||||
next: () => this.logs.status$.next('connected'),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getMessage(success: boolean): string {
|
||||
|
||||
@@ -3,17 +3,17 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { getPkgId } from '@start9labs/shared'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { map } from 'rxjs'
|
||||
import { combineLatest, map } from 'rxjs'
|
||||
import { InterfaceComponent } from 'src/app/routes/portal/components/interfaces/interface.component'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
import { getAddresses } from '../../../components/interfaces/interface.utils'
|
||||
import { getMultihostAddresses } from '../../../components/interfaces/interface.utils'
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<app-interface
|
||||
*ngIf="interfaceInfo$ | async as interfaceInfo"
|
||||
*ngIf="interfacesWithAddresses$ | async as serviceInterface"
|
||||
[packageContext]="context"
|
||||
[serviceInterface]="interfaceInfo"
|
||||
[serviceInterface]="serviceInterface"
|
||||
/>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
@@ -22,23 +22,25 @@ import { getAddresses } from '../../../components/interfaces/interface.utils'
|
||||
})
|
||||
export class ServiceInterfaceRoute {
|
||||
private readonly route = inject(ActivatedRoute)
|
||||
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
||||
|
||||
readonly context = {
|
||||
packageId: getPkgId(this.route),
|
||||
interfaceId: this.route.snapshot.paramMap.get('interfaceId') || '',
|
||||
}
|
||||
|
||||
readonly interfaceInfo$ = inject<PatchDB<DataModel>>(PatchDB)
|
||||
.watch$(
|
||||
readonly interfacesWithAddresses$ = combineLatest([
|
||||
this.patch.watch$(
|
||||
'packageData',
|
||||
this.context.packageId,
|
||||
'serviceInterfaces',
|
||||
this.context.interfaceId,
|
||||
)
|
||||
.pipe(
|
||||
map(info => ({
|
||||
...info,
|
||||
addresses: getAddresses(info),
|
||||
})),
|
||||
)
|
||||
),
|
||||
this.patch.watch$('packageData', this.context.packageId, 'hosts'),
|
||||
]).pipe(
|
||||
map(([iFace, hosts]) => ({
|
||||
...iFace,
|
||||
addresses: getMultihostAddresses(iFace, hosts[iFace.addressInfo.hostId]),
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import { BackupType } from '../types/backup-type'
|
||||
export class BackupsStatusComponent {
|
||||
private readonly exver = inject(Exver)
|
||||
|
||||
@Input({ required: true }) serverId!: string
|
||||
@Input({ required: true }) type!: BackupType
|
||||
@Input({ required: true }) target!: BackupTarget
|
||||
|
||||
@@ -61,8 +62,12 @@ export class BackupsStatusComponent {
|
||||
}
|
||||
|
||||
private get hasBackup(): boolean {
|
||||
return !!this.target.startOs
|
||||
// @TODO Matt types changed
|
||||
// && this.exver.compareExver(this.target.startOs.version, '0.3.0') !== -1
|
||||
return (
|
||||
this.target.startOs[this.serverId] &&
|
||||
this.exver.compareOsVersion(
|
||||
this.target.startOs[this.serverId].version,
|
||||
'0.3.6',
|
||||
) !== 'less'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ import { BackupsStatusComponent } from '../components/status.component'
|
||||
import { GetDisplayInfoPipe } from '../pipes/get-display-info.pipe'
|
||||
import { BackupType } from '../types/backup-type'
|
||||
import { TARGETS } from './targets.component'
|
||||
import { getServerInfo } from 'src/app/utils/get-server-info'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@@ -40,7 +43,12 @@ import { TARGETS } from './targets.component'
|
||||
<tui-icon [icon]="displayInfo.icon" />
|
||||
<div>
|
||||
<strong>{{ displayInfo.name }}</strong>
|
||||
<backups-status [type]="context.data.type" [target]="target" />
|
||||
<backups-status
|
||||
[type]="context.data.type"
|
||||
[target]="target"
|
||||
[serverId]="serverId"
|
||||
]
|
||||
/>
|
||||
<div [style.color]="'var(--tui-text-secondary'">
|
||||
{{ displayInfo.description }}
|
||||
<br />
|
||||
@@ -69,6 +77,7 @@ export class BackupsTargetModal {
|
||||
private readonly dialogs = inject(TuiDialogService)
|
||||
private readonly errorService = inject(ErrorService)
|
||||
private readonly api = inject(ApiService)
|
||||
private readonly patch = inject(PatchDB<DataModel>)
|
||||
|
||||
readonly context =
|
||||
inject<TuiDialogContext<BackupTarget, { type: BackupType }>>(
|
||||
@@ -81,10 +90,12 @@ export class BackupsTargetModal {
|
||||
? 'Loading Backup Targets'
|
||||
: 'Loading Backup Sources'
|
||||
|
||||
serverId = ''
|
||||
targets: BackupTarget[] = []
|
||||
|
||||
async ngOnInit() {
|
||||
try {
|
||||
this.serverId = (await getServerInfo(this.patch)).id
|
||||
this.targets = (await this.api.getBackupTargets({})).saved
|
||||
} catch (e: any) {
|
||||
this.errorService.handleError(e)
|
||||
|
||||
@@ -1,18 +1,32 @@
|
||||
import { inject, Injectable } from '@angular/core'
|
||||
import { Observable, retry, shareReplay } from 'rxjs'
|
||||
import { Metrics } from 'src/app/services/api/api.types'
|
||||
import {
|
||||
defer,
|
||||
Observable,
|
||||
retry,
|
||||
shareReplay,
|
||||
startWith,
|
||||
switchMap,
|
||||
} from 'rxjs'
|
||||
import { ServerMetrics } from 'src/app/services/api/api.types'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class MetricsService extends Observable<Metrics> {
|
||||
// @TODO get real url, 'rpc/{guid}' or something like that
|
||||
private readonly metrics$ = inject(ApiService)
|
||||
.openMetricsWebsocket$({
|
||||
url: '',
|
||||
})
|
||||
.pipe(retry(), shareReplay(1))
|
||||
export class MetricsService extends Observable<ServerMetrics> {
|
||||
private readonly api = inject(ApiService)
|
||||
|
||||
// @TODO Alex do we need to use defer? I am unsure when this is necessary.
|
||||
private readonly metrics$ = defer(() =>
|
||||
this.api.followServerMetrics({}),
|
||||
).pipe(
|
||||
switchMap(({ guid, metrics }) =>
|
||||
this.api.openWebsocket$<ServerMetrics>(guid).pipe(startWith(metrics)),
|
||||
),
|
||||
// @TODO Alex how to handle failure and reconnection here? Simple retry() will not work. Seems like we need a general solution for reconnecting websockets: patchDB, logs, metrics, progress, and any future. Reconnection should depend on server state, then we need to get a new guid, then reconnect. Similar to how patchDB websocket currently behaves on disconnect/reconnect.
|
||||
retry(),
|
||||
shareReplay(),
|
||||
)
|
||||
|
||||
constructor() {
|
||||
super(subscriber => this.metrics$.subscribe(subscriber))
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
InterfaceComponent,
|
||||
ServiceInterfaceWithAddresses,
|
||||
} from 'src/app/routes/portal/components/interfaces/interface.component'
|
||||
import { getAddresses } from 'src/app/routes/portal/components/interfaces/interface.utils'
|
||||
import { getMultihostAddresses } from 'src/app/routes/portal/components/interfaces/interface.utils'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Component({
|
||||
@@ -29,7 +29,6 @@ export class StartOsUiComponent {
|
||||
.watch$('serverInfo', 'ui')
|
||||
.pipe(
|
||||
map(hosts => {
|
||||
// @TODO Matt fix types
|
||||
const serviceInterface: T.ServiceInterface = {
|
||||
id: 'startos-ui',
|
||||
name: 'StartOS UI',
|
||||
@@ -41,31 +40,26 @@ export class StartOsUiComponent {
|
||||
addressInfo: {
|
||||
hostId: '',
|
||||
username: null,
|
||||
internalPort: 80,
|
||||
scheme: 'http',
|
||||
sslScheme: 'https',
|
||||
suffix: '',
|
||||
bindOptions: {
|
||||
scheme: 'http',
|
||||
preferredExternalPort: 80,
|
||||
addSsl: {
|
||||
scheme: 'https',
|
||||
preferredExternalPort: 443,
|
||||
// @TODO is this alpn correct?
|
||||
alpn: { specified: ['http/1.1', 'h2'] },
|
||||
},
|
||||
secure: {
|
||||
ssl: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
hostInfo: {
|
||||
id: 'start-os-ui-host',
|
||||
kind: 'multi',
|
||||
hostnames: hosts,
|
||||
}
|
||||
|
||||
// @TODO Aiden confirm this is correct
|
||||
const host: T.Host = {
|
||||
kind: 'multi',
|
||||
bindings: {},
|
||||
hostnameInfo: {
|
||||
80: hosts,
|
||||
},
|
||||
} as any
|
||||
addresses: [],
|
||||
}
|
||||
|
||||
return {
|
||||
...serviceInterface,
|
||||
addresses: getAddresses(serviceInterface),
|
||||
addresses: getMultihostAddresses(serviceInterface, host),
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -3,12 +3,7 @@ import {
|
||||
PackageDataEntry,
|
||||
ServerStatusInfo,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import {
|
||||
Metrics,
|
||||
NotificationLevel,
|
||||
RR,
|
||||
ServerNotifications,
|
||||
} from './api.types'
|
||||
import { RR, ServerMetrics, ServerNotifications } from './api.types'
|
||||
import { BTC_ICON, LND_ICON, PROXY_ICON, REGISTRY_ICON } from './api-icons'
|
||||
import { Log } from '@start9labs/shared'
|
||||
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
|
||||
@@ -748,7 +743,7 @@ export module Mock {
|
||||
packageId: null,
|
||||
createdAt: '2019-12-26T14:20:30.872Z',
|
||||
code: 1,
|
||||
level: NotificationLevel.Success,
|
||||
level: 'success',
|
||||
title: 'Backup Complete',
|
||||
message: 'StartOS and services have been successfully backed up.',
|
||||
data: {
|
||||
@@ -769,7 +764,7 @@ export module Mock {
|
||||
packageId: null,
|
||||
createdAt: '2019-12-26T14:20:30.872Z',
|
||||
code: 2,
|
||||
level: NotificationLevel.Warning,
|
||||
level: 'warning',
|
||||
title: 'SSH Key Added',
|
||||
message: 'A new SSH key was added. If you did not do this, shit is bad.',
|
||||
data: null,
|
||||
@@ -780,7 +775,7 @@ export module Mock {
|
||||
packageId: null,
|
||||
createdAt: '2019-12-26T14:20:30.872Z',
|
||||
code: 3,
|
||||
level: NotificationLevel.Info,
|
||||
level: 'info',
|
||||
title: 'SSH Key Removed',
|
||||
message: 'A SSH key was removed.',
|
||||
data: null,
|
||||
@@ -791,7 +786,7 @@ export module Mock {
|
||||
packageId: 'bitcoind',
|
||||
createdAt: '2019-12-26T14:20:30.872Z',
|
||||
code: 4,
|
||||
level: NotificationLevel.Error,
|
||||
level: 'error',
|
||||
title: 'Service Crashed',
|
||||
message: new Array(3)
|
||||
.fill(
|
||||
@@ -806,7 +801,7 @@ export module Mock {
|
||||
},
|
||||
]
|
||||
|
||||
export function getMetrics(): Metrics {
|
||||
export function getMetrics(): ServerMetrics {
|
||||
return {
|
||||
general: {
|
||||
temperature: {
|
||||
@@ -1020,8 +1015,16 @@ export module Mock {
|
||||
path: '/Desktop/embassy-backups',
|
||||
username: 'TestUser',
|
||||
mountable: false,
|
||||
// @TODO Matt Provide mock for startOs
|
||||
startOs: {},
|
||||
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: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'ftcvewdnkemfksdm',
|
||||
@@ -1054,8 +1057,16 @@ export module Mock {
|
||||
vendor: 'SSK',
|
||||
mountable: true,
|
||||
path: '/HomeFolder/Documents',
|
||||
// @TODO Matt Provide mock for startOs
|
||||
startOs: {},
|
||||
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: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export module RR {
|
||||
|
||||
// init
|
||||
|
||||
export type InitGetProgressRes = {
|
||||
export type InitFollowProgressRes = {
|
||||
progress: T.FullProgress
|
||||
guid: string
|
||||
}
|
||||
@@ -88,10 +88,10 @@ export module RR {
|
||||
guid: string
|
||||
}
|
||||
|
||||
export type GetServerMetricsReq = {} // server.metrics
|
||||
export type GetServerMetricsRes = {
|
||||
export type FollowServerMetricsReq = {} // server.metrics.follow
|
||||
export type FollowServerMetricsRes = {
|
||||
guid: string
|
||||
metrics: Metrics
|
||||
metrics: ServerMetrics
|
||||
}
|
||||
|
||||
export type UpdateServerReq = { registry: string } // server.update
|
||||
@@ -339,9 +339,11 @@ export module RR {
|
||||
export type FollowPackageLogsReq = FollowServerLogsReq & { id: string } // package.logs.follow
|
||||
export type FollowPackageLogsRes = FollowServerLogsRes
|
||||
|
||||
export type GetPackageMetricsReq = { id: string } // package.metrics
|
||||
// @TODO Matt create package metrics type
|
||||
export type GetPackageMetricsRes = any
|
||||
export type FollowPackageMetricsReq = { id: string } // package.metrics.follow
|
||||
export type FollowPackageMetricsRes = {
|
||||
guid: string
|
||||
metrics: AppMetrics
|
||||
}
|
||||
|
||||
export type InstallPackageReq = T.InstallParams
|
||||
export type InstallPackageRes = null
|
||||
@@ -415,25 +417,6 @@ export module RR {
|
||||
} // package.proxy.set-outbound
|
||||
export type SetServiceOutboundProxyRes = null
|
||||
|
||||
// marketplace
|
||||
|
||||
export type GetMarketplaceInfoReq = { serverId: string }
|
||||
// @TODO Matt fix type
|
||||
export type GetMarketplaceInfoRes = any
|
||||
|
||||
export type GetMarketplaceEosReq = { serverId: string }
|
||||
// @TODO Matt fix type
|
||||
export type GetMarketplaceEosRes = any
|
||||
|
||||
export type GetMarketplacePackagesReq = {
|
||||
ids?: { id: string; version: string }[]
|
||||
// iff !ids
|
||||
category?: string
|
||||
query?: string
|
||||
page?: number
|
||||
perPage?: number
|
||||
}
|
||||
|
||||
// registry
|
||||
|
||||
/** these are returned in ASCENDING order. the newest available version will be the LAST in the object */
|
||||
@@ -443,34 +426,34 @@ export module RR {
|
||||
export type CheckOSUpdateRes = OSUpdate
|
||||
}
|
||||
|
||||
export interface OSUpdate {
|
||||
export type OSUpdate = {
|
||||
version: string
|
||||
headline: string
|
||||
releaseNotes: { [version: string]: string }
|
||||
}
|
||||
|
||||
export interface Breakages {
|
||||
export type Breakages = {
|
||||
[id: string]: TaggedDependencyError
|
||||
}
|
||||
|
||||
export interface TaggedDependencyError {
|
||||
export type TaggedDependencyError = {
|
||||
dependency: string
|
||||
error: DependencyError
|
||||
}
|
||||
|
||||
export interface ActionResponse {
|
||||
export type ActionResponse = {
|
||||
message: string
|
||||
value: string | null
|
||||
copyable: boolean
|
||||
qr: boolean
|
||||
}
|
||||
|
||||
interface MetricData {
|
||||
type MetricData = {
|
||||
value: string
|
||||
unit: string
|
||||
}
|
||||
|
||||
export interface Metrics {
|
||||
export type ServerMetrics = {
|
||||
general: {
|
||||
temperature: MetricData | null
|
||||
}
|
||||
@@ -498,14 +481,28 @@ export interface Metrics {
|
||||
}
|
||||
}
|
||||
|
||||
export interface Session {
|
||||
export type AppMetrics = {
|
||||
memory: {
|
||||
percentageUsed: MetricData
|
||||
used: MetricData
|
||||
}
|
||||
cpu: {
|
||||
percentageUsed: MetricData
|
||||
}
|
||||
disk: {
|
||||
percentageUsed: MetricData
|
||||
used: MetricData
|
||||
}
|
||||
}
|
||||
|
||||
export type Session = {
|
||||
loggedIn: string
|
||||
lastActive: string
|
||||
userAgent: string
|
||||
metadata: SessionMetadata
|
||||
}
|
||||
|
||||
export interface SessionMetadata {
|
||||
export type SessionMetadata = {
|
||||
platforms: PlatformType[]
|
||||
}
|
||||
|
||||
@@ -565,7 +562,7 @@ export interface CloudBackupTarget extends BaseBackupTarget {
|
||||
provider: 'dropbox' | 'google-drive'
|
||||
}
|
||||
|
||||
export interface BackupRun {
|
||||
export type BackupRun = {
|
||||
id: string
|
||||
startedAt: string
|
||||
completedAt: string
|
||||
@@ -574,7 +571,7 @@ export interface BackupRun {
|
||||
report: BackupReport
|
||||
}
|
||||
|
||||
export interface BackupJob {
|
||||
export type BackupJob = {
|
||||
id: string
|
||||
name: string
|
||||
target: BackupTarget
|
||||
@@ -582,7 +579,7 @@ export interface BackupJob {
|
||||
packageIds: string[]
|
||||
}
|
||||
|
||||
export interface BackupInfo {
|
||||
export type BackupInfo = {
|
||||
version: string
|
||||
timestamp: string
|
||||
packageBackups: {
|
||||
@@ -590,18 +587,18 @@ export interface BackupInfo {
|
||||
}
|
||||
}
|
||||
|
||||
export interface PackageBackupInfo {
|
||||
export type PackageBackupInfo = {
|
||||
title: string
|
||||
version: string
|
||||
osVersion: string
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
export interface ServerSpecs {
|
||||
export type ServerSpecs = {
|
||||
[key: string]: string | number
|
||||
}
|
||||
|
||||
export interface SSHKey {
|
||||
export type SSHKey = {
|
||||
createdAt: string
|
||||
alg: string
|
||||
hostname: string
|
||||
@@ -610,32 +607,25 @@ export interface SSHKey {
|
||||
|
||||
export type ServerNotifications = ServerNotification<number>[]
|
||||
|
||||
export interface ServerNotification<T extends number> {
|
||||
export type ServerNotification<T extends number> = {
|
||||
id: number
|
||||
packageId: string | null
|
||||
createdAt: string
|
||||
code: T
|
||||
level: NotificationLevel
|
||||
level: 'success' | 'info' | 'warning' | 'error'
|
||||
title: string
|
||||
message: string
|
||||
data: NotificationData<T>
|
||||
read: boolean
|
||||
}
|
||||
|
||||
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 {
|
||||
export type BackupReport = {
|
||||
server: {
|
||||
attempted: boolean
|
||||
error: string | null
|
||||
@@ -647,7 +637,7 @@ export interface BackupReport {
|
||||
}
|
||||
}
|
||||
|
||||
export interface AvailableWifi {
|
||||
export type AvailableWifi = {
|
||||
ssid: string
|
||||
strength: number
|
||||
security: string[]
|
||||
@@ -682,29 +672,29 @@ export type DependencyError =
|
||||
| DependencyErrorHealthChecksFailed
|
||||
| DependencyErrorTransitive
|
||||
|
||||
export interface DependencyErrorNotInstalled {
|
||||
export type DependencyErrorNotInstalled = {
|
||||
type: 'notInstalled'
|
||||
}
|
||||
|
||||
export interface DependencyErrorNotRunning {
|
||||
export type DependencyErrorNotRunning = {
|
||||
type: 'notRunning'
|
||||
}
|
||||
|
||||
export interface DependencyErrorIncorrectVersion {
|
||||
export type DependencyErrorIncorrectVersion = {
|
||||
type: 'incorrectVersion'
|
||||
expected: string // version range
|
||||
received: string // version
|
||||
}
|
||||
|
||||
export interface DependencyErrorConfigUnsatisfied {
|
||||
export type DependencyErrorConfigUnsatisfied = {
|
||||
type: 'configUnsatisfied'
|
||||
}
|
||||
|
||||
export interface DependencyErrorHealthChecksFailed {
|
||||
export type DependencyErrorHealthChecksFailed = {
|
||||
type: 'healthChecksFailed'
|
||||
check: T.HealthCheckResult
|
||||
}
|
||||
|
||||
export interface DependencyErrorTransitive {
|
||||
export type DependencyErrorTransitive = {
|
||||
type: 'transitive'
|
||||
}
|
||||
|
||||
@@ -3,11 +3,10 @@ import {
|
||||
GetPackagesRes,
|
||||
MarketplacePkg,
|
||||
} from '@start9labs/marketplace'
|
||||
import { Log, RPCOptions } from '@start9labs/shared'
|
||||
import { RPCOptions } from '@start9labs/shared'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { Observable } from 'rxjs'
|
||||
import { WebSocketSubjectConfig } from 'rxjs/webSocket'
|
||||
import { BackupTargetType, Metrics, RR } from './api.types'
|
||||
import { BackupTargetType, RR } from './api.types'
|
||||
|
||||
export abstract class ApiService {
|
||||
// http
|
||||
@@ -32,7 +31,7 @@ export abstract class ApiService {
|
||||
|
||||
abstract openWebsocket$<T>(
|
||||
guid: string,
|
||||
config: RR.WebsocketConfig<T>,
|
||||
config?: RR.WebsocketConfig<T>,
|
||||
): Observable<T>
|
||||
|
||||
// state
|
||||
@@ -78,20 +77,13 @@ export abstract class ApiService {
|
||||
|
||||
// init
|
||||
|
||||
abstract initGetProgress(): Promise<RR.InitGetProgressRes>
|
||||
abstract initFollowProgress(): Promise<RR.InitFollowProgressRes>
|
||||
|
||||
abstract initFollowLogs(
|
||||
params: RR.FollowServerLogsReq,
|
||||
): Promise<RR.FollowServerLogsRes>
|
||||
|
||||
// server
|
||||
abstract openLogsWebsocket$(
|
||||
config: WebSocketSubjectConfig<Log>,
|
||||
): Observable<Log>
|
||||
|
||||
abstract openMetricsWebsocket$(
|
||||
config: WebSocketSubjectConfig<Metrics>,
|
||||
): Observable<Metrics>
|
||||
|
||||
abstract getSystemTime(
|
||||
params: RR.GetSystemTimeReq,
|
||||
@@ -119,9 +111,9 @@ export abstract class ApiService {
|
||||
params: RR.FollowServerLogsReq,
|
||||
): Promise<RR.FollowServerLogsRes>
|
||||
|
||||
abstract getServerMetrics(
|
||||
params: RR.GetServerMetricsReq,
|
||||
): Promise<RR.GetServerMetricsRes>
|
||||
abstract followServerMetrics(
|
||||
params: RR.FollowServerMetricsReq,
|
||||
): Promise<RR.FollowServerMetricsRes>
|
||||
|
||||
abstract updateServer(url?: string): Promise<RR.UpdateServerRes>
|
||||
|
||||
|
||||
@@ -3,16 +3,15 @@ import {
|
||||
HttpOptions,
|
||||
HttpService,
|
||||
isRpcError,
|
||||
Log,
|
||||
Method,
|
||||
RpcError,
|
||||
RPCOptions,
|
||||
} from '@start9labs/shared'
|
||||
import { PATCH_CACHE } from 'src/app/services/patch-db/patch-db-source'
|
||||
import { ApiService } from './embassy-api.service'
|
||||
import { BackupTargetType, Metrics, RR } from './api.types'
|
||||
import { BackupTargetType, RR } from './api.types'
|
||||
import { ConfigService } from '../config.service'
|
||||
import { webSocket, WebSocketSubjectConfig } from 'rxjs/webSocket'
|
||||
import { webSocket } from 'rxjs/webSocket'
|
||||
import { Observable, filter, firstValueFrom } from 'rxjs'
|
||||
import { AuthService } from '../auth.service'
|
||||
import { DOCUMENT } from '@angular/common'
|
||||
@@ -93,20 +92,10 @@ export class LiveApiService extends ApiService {
|
||||
}
|
||||
|
||||
// websocket
|
||||
// @TODO Matt which of these 2 APIs should we use?
|
||||
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)
|
||||
}
|
||||
|
||||
openWebsocket$<T>(
|
||||
guid: string,
|
||||
config: RR.WebsocketConfig<T>,
|
||||
config: RR.WebsocketConfig<T> = {},
|
||||
): Observable<T> {
|
||||
const { location } = this.document.defaultView!
|
||||
const protocol = location.protocol === 'http:' ? 'ws' : 'wss'
|
||||
@@ -210,7 +199,7 @@ export class LiveApiService extends ApiService {
|
||||
|
||||
// init
|
||||
|
||||
async initGetProgress(): Promise<RR.InitGetProgressRes> {
|
||||
async initFollowProgress(): Promise<RR.InitFollowProgressRes> {
|
||||
return this.rpcRequest({ method: 'init.subscribe', params: {} })
|
||||
}
|
||||
|
||||
@@ -221,15 +210,6 @@ export class LiveApiService extends ApiService {
|
||||
}
|
||||
|
||||
// server
|
||||
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,
|
||||
@@ -271,9 +251,9 @@ export class LiveApiService extends ApiService {
|
||||
return this.rpcRequest({ method: 'net.tor.logs.follow', params })
|
||||
}
|
||||
|
||||
async getServerMetrics(
|
||||
params: RR.GetServerMetricsReq,
|
||||
): Promise<RR.GetServerMetricsRes> {
|
||||
async followServerMetrics(
|
||||
params: RR.FollowServerMetricsReq,
|
||||
): Promise<RR.FollowServerMetricsRes> {
|
||||
return this.rpcRequest({ method: 'server.metrics', params })
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
StateInfo,
|
||||
UpdatingState,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { BackupTargetType, Metrics, RR } from './api.types'
|
||||
import { BackupTargetType, RR } from './api.types'
|
||||
import { Mock } from './api.fixures'
|
||||
import {
|
||||
from,
|
||||
@@ -35,7 +35,6 @@ import {
|
||||
GetPackagesRes,
|
||||
MarketplacePkg,
|
||||
} from '@start9labs/marketplace'
|
||||
import { WebSocketSubjectConfig } from 'rxjs/webSocket'
|
||||
import markdown from 'raw-loader!../../../../../shared/assets/markdown/md-sample.md'
|
||||
|
||||
const PROGRESS: T.FullProgress = {
|
||||
@@ -84,8 +83,8 @@ export class MockApiService extends ApiService {
|
||||
|
||||
async uploadPackage(guid: string, body: Blob): Promise<string> {
|
||||
await pauseFor(2000)
|
||||
// @TODO Matt what should this return?
|
||||
return ''
|
||||
// @TODO Aiden confirm this is correct
|
||||
return 'success'
|
||||
}
|
||||
|
||||
async uploadFile(body: Blob): Promise<string> {
|
||||
@@ -258,7 +257,7 @@ export class MockApiService extends ApiService {
|
||||
|
||||
// init
|
||||
|
||||
async initGetProgress(): Promise<RR.InitGetProgressRes> {
|
||||
async initFollowProgress(): Promise<RR.InitFollowProgressRes> {
|
||||
await pauseFor(250)
|
||||
return {
|
||||
progress: PROGRESS,
|
||||
@@ -276,30 +275,6 @@ export class MockApiService extends ApiService {
|
||||
|
||||
// server
|
||||
|
||||
openLogsWebsocket$(config: WebSocketSubjectConfig<Log>): Observable<Log> {
|
||||
return interval(50).pipe(
|
||||
map((_, index) => {
|
||||
// mock fire open observer
|
||||
if (index === 0) config.openObserver?.next(new Event(''))
|
||||
if (index === 100) throw new Error('HAAHHA')
|
||||
return Mock.ServerLogs[0]
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
openMetricsWebsocket$(
|
||||
config: WebSocketSubjectConfig<Metrics>,
|
||||
): Observable<Metrics> {
|
||||
return interval(2000).pipe(
|
||||
map((_, index) => {
|
||||
// mock fire open observer
|
||||
if (index === 0) config.openObserver?.next(new Event(''))
|
||||
if (index === 4) throw new Error('HAHAHA')
|
||||
return Mock.getMetrics()
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
async getSystemTime(
|
||||
params: RR.GetSystemTimeReq,
|
||||
): Promise<RR.GetSystemTimeRes> {
|
||||
@@ -386,9 +361,9 @@ export class MockApiService extends ApiService {
|
||||
return logs
|
||||
}
|
||||
|
||||
async getServerMetrics(
|
||||
params: RR.GetServerMetricsReq,
|
||||
): Promise<RR.GetServerMetricsRes> {
|
||||
async followServerMetrics(
|
||||
params: RR.FollowServerMetricsReq,
|
||||
): Promise<RR.FollowServerMetricsRes> {
|
||||
await pauseFor(2000)
|
||||
return {
|
||||
guid: 'iqudh37um-i38u3-34-a51b-jkhd783ein',
|
||||
|
||||
@@ -130,8 +130,7 @@ export const mockPatchData: DataModel = {
|
||||
password: '',
|
||||
},
|
||||
platform: 'x86_64-nonfree',
|
||||
// @TODO Matt zram needs to be added?
|
||||
// zram: true,
|
||||
zram: true,
|
||||
governor: 'performance',
|
||||
passwordHash:
|
||||
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
|
||||
|
||||
@@ -2,7 +2,6 @@ import { inject, Injectable } from '@angular/core'
|
||||
import { ErrorService } from '@start9labs/shared'
|
||||
import { TuiDialogService } from '@taiga-ui/core'
|
||||
import {
|
||||
NotificationLevel,
|
||||
ServerNotification,
|
||||
ServerNotifications,
|
||||
} from 'src/app/services/api/api.types'
|
||||
@@ -63,13 +62,13 @@ export class NotificationService {
|
||||
|
||||
getColor(notification: ServerNotification<number>): string {
|
||||
switch (notification.level) {
|
||||
case NotificationLevel.Info:
|
||||
case 'info':
|
||||
return 'var(--tui-status-info)'
|
||||
case NotificationLevel.Success:
|
||||
case 'success':
|
||||
return 'var(--tui-status-positive)'
|
||||
case NotificationLevel.Warning:
|
||||
case 'warning':
|
||||
return 'var(--tui-status-warning)'
|
||||
case NotificationLevel.Error:
|
||||
case 'error':
|
||||
return 'var(--tui-status-negative)'
|
||||
default:
|
||||
return ''
|
||||
@@ -78,12 +77,12 @@ export class NotificationService {
|
||||
|
||||
getIcon(notification: ServerNotification<number>): string {
|
||||
switch (notification.level) {
|
||||
case NotificationLevel.Info:
|
||||
case 'info':
|
||||
return '@tui.info'
|
||||
case NotificationLevel.Success:
|
||||
case 'success':
|
||||
return '@tui.circle-check'
|
||||
case NotificationLevel.Warning:
|
||||
case NotificationLevel.Error:
|
||||
case 'warning':
|
||||
case 'error':
|
||||
return '@tui.circle-alert'
|
||||
default:
|
||||
return ''
|
||||
|
||||
@@ -56,6 +56,7 @@ export type ServerInfo = {
|
||||
platform: string
|
||||
arch: string
|
||||
governor: string | null
|
||||
zram: boolean
|
||||
}
|
||||
|
||||
export type NetworkInfo = {
|
||||
|
||||
Reference in New Issue
Block a user