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