Merge branch 'next/minor' of github.com:Start9Labs/start-os into next/major

This commit is contained in:
Matt Hill
2024-08-08 10:52:49 -06:00
765 changed files with 43858 additions and 19423 deletions

View File

@@ -134,6 +134,10 @@ export type PackageDataEntry<T extends StateInfo = StateInfo> =
nextBackup: string | null
}
export type AllPackageData = NonNullable<
T.AllPackageData & Record<string, PackageDataEntry<StateInfo>>
>
export type StateInfo = InstalledState | InstallingState | UpdatingState
export type InstalledState = {

View File

@@ -1,4 +1,4 @@
import { Bootstrapper, DBCache } from 'patch-db-client'
import { Dump } from 'patch-db-client'
import { DataModel } from 'src/app/services/patch-db/data-model'
import { Injectable } from '@angular/core'
import { StorageService } from '../storage.service'
@@ -6,20 +6,18 @@ import { StorageService } from '../storage.service'
@Injectable({
providedIn: 'root',
})
export class LocalStorageBootstrap implements Bootstrapper<DataModel> {
static CONTENT_KEY = 'patch-db-cache'
export class LocalStorageBootstrap {
static CONTENT_KEY = 'patchDB'
constructor(private readonly storage: StorageService) {}
init(): DBCache<DataModel> {
const cache = this.storage.get<DBCache<DataModel>>(
LocalStorageBootstrap.CONTENT_KEY,
)
init(): Dump<DataModel> {
const cache = this.storage.get<DataModel>(LocalStorageBootstrap.CONTENT_KEY)
return cache || { sequence: 0, data: {} as DataModel }
return cache ? { id: 1, value: cache } : { id: 0, value: {} as DataModel }
}
update(cache: DBCache<DataModel>): void {
update(cache: DataModel): void {
this.storage.set(LocalStorageBootstrap.CONTENT_KEY, cache)
}
}

View File

@@ -0,0 +1,58 @@
import { inject, Injectable, InjectionToken } from '@angular/core'
import { Dump, Revision, Update } from 'patch-db-client'
import { BehaviorSubject, EMPTY, Observable } from 'rxjs'
import {
bufferTime,
catchError,
filter,
skip,
startWith,
switchMap,
take,
} from 'rxjs/operators'
import { StateService } from 'src/app/services/state.service'
import { ApiService } from '../api/embassy-api.service'
import { AuthService } from '../auth.service'
import { DataModel } from './data-model'
import { LocalStorageBootstrap } from './local-storage-bootstrap'
export const PATCH_CACHE = new InjectionToken('', {
factory: () =>
new BehaviorSubject<Dump<DataModel>>({
id: 0,
value: {} as DataModel,
}),
})
@Injectable({
providedIn: 'root',
})
export class PatchDbSource extends Observable<Update<DataModel>[]> {
private readonly api = inject(ApiService)
private readonly state = inject(StateService)
private readonly stream$ = inject(AuthService).isVerified$.pipe(
switchMap(verified => (verified ? this.api.subscribeToPatchDB({}) : EMPTY)),
switchMap(({ dump, guid }) =>
this.api.openWebsocket$<Revision>(guid, {}).pipe(
bufferTime(250),
filter(revisions => !!revisions.length),
startWith([dump]),
),
),
catchError((_, original$) => {
this.state.retrigger()
return this.state.pipe(
skip(1), // skipping previous value stored due to shareReplay
filter(current => current === 'running'),
take(1),
switchMap(() => original$),
)
}),
startWith([inject(LocalStorageBootstrap).init()]),
)
constructor() {
super(subscriber => this.stream$.subscribe(subscriber))
}
}

View File

@@ -1,61 +0,0 @@
import { InjectionToken, Injector } from '@angular/core'
import { Update } from 'patch-db-client'
import {
bufferTime,
catchError,
filter,
switchMap,
take,
tap,
defer,
EMPTY,
from,
interval,
Observable,
} from 'rxjs'
import { DataModel } from './data-model'
import { AuthService } from '../auth.service'
import { ConnectionService } from '../connection.service'
import { ApiService } from '../api/embassy-api.service'
import { ConfigService } from '../config.service'
export const PATCH_SOURCE = new InjectionToken<Observable<Update<DataModel>[]>>(
'',
)
export function sourceFactory(
injector: Injector,
): Observable<Update<DataModel>[]> {
// defer() needed to avoid circular dependency with ApiService, since PatchDB is needed there
return defer(() => {
const api = injector.get(ApiService)
const authService = injector.get(AuthService)
const connectionService = injector.get(ConnectionService)
const configService = injector.get(ConfigService)
const isTor = configService.isTor()
const timeout = isTor ? 16000 : 4000
const websocket$ = api.openPatchWebsocket$().pipe(
bufferTime(250),
filter(updates => !!updates.length),
catchError((_, watch$) => {
connectionService.websocketConnected$.next(false)
return interval(timeout).pipe(
switchMap(() =>
from(api.echo({ message: 'ping', timeout })).pipe(
catchError(() => EMPTY),
),
),
take(1),
switchMap(() => watch$),
)
}),
tap(() => connectionService.websocketConnected$.next(true)),
)
return authService.isVerified$.pipe(
switchMap(verified => (verified ? websocket$ : EMPTY)),
)
})
}