diff --git a/container-runtime/package-lock.json b/container-runtime/package-lock.json index 4bf89a417..bb1b39e16 100644 --- a/container-runtime/package-lock.json +++ b/container-runtime/package-lock.json @@ -38,7 +38,7 @@ }, "../sdk/dist": { "name": "@start9labs/start-sdk", - "version": "0.4.0-beta.32", + "version": "0.4.0-beta.33", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", diff --git a/core/startos/src/context/rpc.rs b/core/startos/src/context/rpc.rs index c43db5047..6e7d1618a 100644 --- a/core/startos/src/context/rpc.rs +++ b/core/startos/src/context/rpc.rs @@ -394,7 +394,9 @@ impl RpcContext { if let Some(service) = self.services.get(&package_id).await.as_ref() { if let Some(input) = service .get_action_input(procedure_id.clone(), action_id.clone()) - .await? + .await + .log_err() + .flatten() .and_then(|i| i.value) { action_input diff --git a/core/startos/src/init.rs b/core/startos/src/init.rs index 5261d2646..22a286fa3 100644 --- a/core/startos/src/init.rs +++ b/core/startos/src/init.rs @@ -38,7 +38,7 @@ use crate::rpc_continuations::{Guid, RpcContinuation}; use crate::s9pk::v2::pack::{CONTAINER_DATADIR, CONTAINER_TOOL}; use crate::ssh::SSH_DIR; use crate::system::{get_mem_info, sync_kiosk}; -use crate::util::io::{create_file, IOHook}; +use crate::util::io::{create_file, open_file, IOHook}; use crate::util::lshw::lshw; use crate::util::net::WebSocketExt; use crate::util::{cpupower, Invoke}; @@ -399,6 +399,11 @@ pub async fn init( .invoke(crate::ErrorKind::Journald) .await?; mount_logs.complete(); + tokio::io::copy( + &mut open_file("/run/startos/init.log").await?, + &mut tokio::io::stderr(), + ) + .await?; tracing::info!("Mounted Logs"); load_ca_cert.start(); diff --git a/core/startos/src/lxc/mod.rs b/core/startos/src/lxc/mod.rs index 29a14028a..eddd5971b 100644 --- a/core/startos/src/lxc/mod.rs +++ b/core/startos/src/lxc/mod.rs @@ -89,8 +89,13 @@ impl LxcManager { log_mount: Option<&Path>, config: LxcConfig, ) -> Result { - let container = LxcContainer::new(self, log_mount, config).await?; let mut guard = self.containers.lock().await; + let container = tokio::time::timeout( + Duration::from_secs(30), + LxcContainer::new(self, log_mount, config), + ) + .await + .with_kind(ErrorKind::Timeout)??; *guard = std::mem::take(&mut *guard) .into_iter() .filter(|g| g.strong_count() > 0) @@ -223,6 +228,17 @@ impl LxcContainer { .arg(&log_mount_point) .invoke(crate::ErrorKind::Filesystem) .await?; + match Command::new("chattr") + .arg("-R") + .arg("+C") + .arg(&log_mount_point) + .invoke(ErrorKind::Filesystem) + .await + { + Ok(_) => Ok(()), + Err(e) if e.source.to_string().contains("Operation not supported") => Ok(()), + Err(e) => Err(e), + }?; Some(log_mount) } else { None diff --git a/core/startos/src/net/host/binding.rs b/core/startos/src/net/host/binding.rs index f82c1a1cf..96423b9f9 100644 --- a/core/startos/src/net/host/binding.rs +++ b/core/startos/src/net/host/binding.rs @@ -62,12 +62,14 @@ impl BindInfo { pub fn new(available_ports: &mut AvailablePorts, options: BindOptions) -> Result { let mut assigned_port = None; let mut assigned_ssl_port = None; - if options.secure.is_some() { - assigned_port = Some(available_ports.alloc()?); - } if options.add_ssl.is_some() { assigned_ssl_port = Some(available_ports.alloc()?); } + if let Some(secure) = options.secure { + if !secure.ssl || !options.add_ssl.is_some() { + assigned_port = Some(available_ports.alloc()?); + } + } Ok(Self { enabled: true, options, diff --git a/core/startos/src/service/action.rs b/core/startos/src/service/action.rs index 4c27a4f93..b2c11260c 100644 --- a/core/startos/src/service/action.rs +++ b/core/startos/src/service/action.rs @@ -176,29 +176,44 @@ impl Handler for ServiceActor { ) .await .with_kind(ErrorKind::Action)?; - if self + let package_id = package_id.clone(); + for to_stop in self .0 .ctx .db .mutate(|db| { - let mut critical_activated = false; - for (_, pde) in db.as_public_mut().as_package_data_mut().as_entries_mut()? { - critical_activated |= pde.as_tasks_mut().mutate(|tasks| { - Ok(update_tasks(tasks, package_id, action_id, &input, true)) - })?; + let mut to_stop = Vec::new(); + for (id, pde) in db.as_public_mut().as_package_data_mut().as_entries_mut()? { + if pde.as_tasks_mut().mutate(|tasks| { + Ok(update_tasks(tasks, &package_id, action_id, &input, true)) + })? { + to_stop.push(id) + } } - Ok(critical_activated) + Ok(to_stop) }) .await .result? { - >::handle( - self, - id, - super::control::Stop { wait: false }, - jobs, - ) - .await; + if to_stop == package_id { + >::handle( + self, + id.clone(), + super::control::Stop { wait: false }, + jobs, + ) + .await; + } else { + self.0 + .ctx + .services + .get(&to_stop) + .await + .as_ref() + .or_not_found(&to_stop)? + .stop(id.clone(), false) + .await?; + } } Ok(result) } diff --git a/image-recipe/run-local-build.sh b/image-recipe/run-local-build.sh index 7f7ecd3d4..e5cf7645c 100755 --- a/image-recipe/run-local-build.sh +++ b/image-recipe/run-local-build.sh @@ -17,9 +17,6 @@ fi STARTOS_ENV="$(dpkg-deb --fsys-tarfile $DEB_PATH | tar --to-stdout -xvf - ./usr/lib/startos/ENVIRONMENT.txt)" PLATFORM="$(dpkg-deb --fsys-tarfile $DEB_PATH | tar --to-stdout -xvf - ./usr/lib/startos/PLATFORM.txt)" -if [ -z "$1" ]; then - PLATFORM="$(uname -m)" -fi if [ "$PLATFORM" = "x86_64" ] || [ "$PLATFORM" = "x86_64-nonfree" ]; then ARCH=amd64 QEMU_ARCH=x86_64 diff --git a/sdk/base/lib/actions/setupActions.ts b/sdk/base/lib/actions/setupActions.ts index 1cb3a8a3b..91f93f688 100644 --- a/sdk/base/lib/actions/setupActions.ts +++ b/sdk/base/lib/actions/setupActions.ts @@ -104,7 +104,11 @@ export class Action> this.cachedParser = built.validator return { spec: built.spec, - value: (await this.getInputFn(options)) || null, + value: + ((await this.getInputFn(options)) as + | Record + | null + | undefined) || null, } } async run(options: { diff --git a/sdk/base/lib/dependencies/dependencies.ts b/sdk/base/lib/dependencies/dependencies.ts index 921be7f50..7002f54de 100644 --- a/sdk/base/lib/dependencies/dependencies.ts +++ b/sdk/base/lib/dependencies/dependencies.ts @@ -1,8 +1,18 @@ import { ExtendedVersion, VersionRange } from "../exver" -import { PackageId, HealthCheckId } from "../types" +import { + PackageId, + HealthCheckId, + DependencyRequirement, + CheckDependenciesResult, +} from "../types" import { Effects } from "../Effects" export type CheckDependencies = { + infoFor: (packageId: DependencyId) => { + requirement: DependencyRequirement + result: CheckDependenciesResult + } + installedSatisfied: (packageId: DependencyId) => boolean installedVersionSatisfied: (packageId: DependencyId) => boolean runningSatisfied: (packageId: DependencyId) => boolean @@ -41,7 +51,7 @@ export async function checkDependencies< ) } - const find = (packageId: DependencyId) => { + const infoFor = (packageId: DependencyId) => { const dependencyRequirement = dependencies.find((d) => d.id === packageId) const dependencyResult = results.find((d) => d.packageId === packageId) if (!dependencyRequirement || !dependencyResult) { @@ -51,9 +61,9 @@ export async function checkDependencies< } const installedSatisfied = (packageId: DependencyId) => - !!find(packageId).result.installedVersion + !!infoFor(packageId).result.installedVersion const installedVersionSatisfied = (packageId: DependencyId) => { - const dep = find(packageId) + const dep = infoFor(packageId) return ( !!dep.result.installedVersion && ExtendedVersion.parse(dep.result.installedVersion).satisfies( @@ -62,18 +72,18 @@ export async function checkDependencies< ) } const runningSatisfied = (packageId: DependencyId) => { - const dep = find(packageId) + const dep = infoFor(packageId) return dep.requirement.kind !== "running" || dep.result.isRunning } const tasksSatisfied = (packageId: DependencyId) => - Object.entries(find(packageId).result.tasks).filter( + Object.entries(infoFor(packageId).result.tasks).filter( ([_, t]) => t.active && t.task.severity === "critical", ).length === 0 const healthCheckSatisfied = ( packageId: DependencyId, healthCheckId?: HealthCheckId, ) => { - const dep = find(packageId) + const dep = infoFor(packageId) if ( healthCheckId && (dep.requirement.kind !== "running" || @@ -102,14 +112,14 @@ export async function checkDependencies< : dependencies.every((d) => pkgSatisfied(d.id as DependencyId)) const throwIfInstalledNotSatisfied = (packageId: DependencyId) => { - const dep = find(packageId) + const dep = infoFor(packageId) if (!dep.result.installedVersion) { throw new Error(`${dep.result.title || packageId} is not installed`) } return null } const throwIfInstalledVersionNotSatisfied = (packageId: DependencyId) => { - const dep = find(packageId) + const dep = infoFor(packageId) if (!dep.result.installedVersion) { throw new Error(`${dep.result.title || packageId} is not installed`) } @@ -127,14 +137,14 @@ export async function checkDependencies< return null } const throwIfRunningNotSatisfied = (packageId: DependencyId) => { - const dep = find(packageId) + const dep = infoFor(packageId) if (dep.requirement.kind === "running" && !dep.result.isRunning) { throw new Error(`${dep.result.title || packageId} is not running`) } return null } const throwIfTasksNotSatisfied = (packageId: DependencyId) => { - const dep = find(packageId) + const dep = infoFor(packageId) const reqs = Object.entries(dep.result.tasks) .filter(([_, t]) => t.active && t.task.severity === "critical") .map(([id, _]) => id) @@ -149,7 +159,7 @@ export async function checkDependencies< packageId: DependencyId, healthCheckId?: HealthCheckId, ) => { - const dep = find(packageId) + const dep = infoFor(packageId) if ( healthCheckId && (dep.requirement.kind !== "running" || @@ -205,6 +215,7 @@ export async function checkDependencies< })() return { + infoFor, installedSatisfied, installedVersionSatisfied, runningSatisfied, diff --git a/sdk/base/lib/exver/index.ts b/sdk/base/lib/exver/index.ts index c855e1534..2590ca163 100644 --- a/sdk/base/lib/exver/index.ts +++ b/sdk/base/lib/exver/index.ts @@ -753,7 +753,10 @@ export class Version { return "less" } - const prereleaseLen = Math.max(this.number.length, other.number.length) + const prereleaseLen = Math.max( + this.prerelease.length, + other.prerelease.length, + ) for (let i = 0; i < prereleaseLen; i++) { if (typeof this.prerelease[i] === typeof other.prerelease[i]) { if (this.prerelease[i] > other.prerelease[i]) { diff --git a/sdk/base/lib/types.ts b/sdk/base/lib/types.ts index e6a9af811..87bc3f11a 100644 --- a/sdk/base/lib/types.ts +++ b/sdk/base/lib/types.ts @@ -180,8 +180,8 @@ export type KnownError = export type Dependencies = Array -export type DeepPartial = T extends unknown[] - ? T +export type DeepPartial = T extends [infer A, ...infer Rest] + ? [DeepPartial, ...DeepPartial] : T extends {} ? { [P in keyof T]?: DeepPartial } : T diff --git a/sdk/base/lib/util/deepMerge.ts b/sdk/base/lib/util/deepMerge.ts index 055b3085f..452a8549d 100644 --- a/sdk/base/lib/util/deepMerge.ts +++ b/sdk/base/lib/util/deepMerge.ts @@ -26,7 +26,11 @@ export function partialDiff( } else if (typeof prev === "object" && typeof next === "object") { if (prev === null || next === null) return { diff: next } const res = { diff: {} as Record } - for (let key in next) { + const keys = Object.keys(next) as (keyof T)[] + for (let key in prev) { + if (!keys.includes(key)) keys.push(key) + } + for (let key of keys) { const diff = partialDiff(prev[key], next[key]) if (diff) { res.diff[key] = diff.diff diff --git a/sdk/package/lib/StartSdk.ts b/sdk/package/lib/StartSdk.ts index c65e64747..422c4527c 100644 --- a/sdk/package/lib/StartSdk.ts +++ b/sdk/package/lib/StartSdk.ts @@ -710,7 +710,7 @@ export class StartSdk { image, mounts, name, - ) + ).then((subc) => subc.rc()) }, /** * @description Run a function with a temporary SubContainer diff --git a/sdk/package/package-lock.json b/sdk/package/package-lock.json index f9a92462e..d2da630aa 100644 --- a/sdk/package/package-lock.json +++ b/sdk/package/package-lock.json @@ -1,12 +1,12 @@ { "name": "@start9labs/start-sdk", - "version": "0.4.0-beta.32", + "version": "0.4.0-beta.33", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@start9labs/start-sdk", - "version": "0.4.0-beta.32", + "version": "0.4.0-beta.33", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", diff --git a/sdk/package/package.json b/sdk/package/package.json index df85202bc..0fb5673d0 100644 --- a/sdk/package/package.json +++ b/sdk/package/package.json @@ -1,6 +1,6 @@ { "name": "@start9labs/start-sdk", - "version": "0.4.0-beta.32", + "version": "0.4.0-beta.33", "description": "Software development kit to facilitate packaging services for StartOS", "main": "./package/lib/index.js", "types": "./package/lib/index.d.ts", diff --git a/web/projects/ui/src/app/app.providers.ts b/web/projects/ui/src/app/app.providers.ts index 4ab83bfd0..c7b5a9814 100644 --- a/web/projects/ui/src/app/app.providers.ts +++ b/web/projects/ui/src/app/app.providers.ts @@ -46,6 +46,7 @@ import { ClientStorageService } from './services/client-storage.service' import { DateTransformerService } from './services/date-transformer.service' import { DatetimeTransformerService } from './services/datetime-transformer.service' import { StorageService } from './services/storage.service' +import { FilterUpdatesPipe } from './routes/portal/routes/updates/filter-updates.pipe' const { useMocks, @@ -56,6 +57,7 @@ export const APP_PROVIDERS = [ provideEventPlugins(), I18N_PROVIDERS, FilterPackagesPipe, + FilterUpdatesPipe, UntypedFormBuilder, tuiNumberFormatProvider({ decimalSeparator: '.', thousandSeparator: '' }), tuiButtonOptionsProvider({ size: 'm' }), diff --git a/web/projects/ui/src/app/routes/portal/components/header/header.component.ts b/web/projects/ui/src/app/routes/portal/components/header/header.component.ts index db63f0739..a4de82c14 100644 --- a/web/projects/ui/src/app/routes/portal/components/header/header.component.ts +++ b/web/projects/ui/src/app/routes/portal/components/header/header.component.ts @@ -103,7 +103,6 @@ import { HeaderStatusComponent } from './status.component' &:has([data-status='neutral']) { --status: var(--tui-status-neutral); - filter: none; } &:has([data-status='success']) { diff --git a/web/projects/ui/src/app/routes/portal/components/logs/logs.component.html b/web/projects/ui/src/app/routes/portal/components/logs/logs.component.html index 5b65455e5..64c915b96 100644 --- a/web/projects/ui/src/app/routes/portal/components/logs/logs.component.html +++ b/web/projects/ui/src/app/routes/portal/components/logs/logs.component.html @@ -18,18 +18,18 @@ @if (followLogs | logs | async; as logs) {
- @for (log of logs; track log) { + @for (log of logs; track $index) {

       }
 
       @if ((status$ | async) !== 'connected') {
-        

+

{{ status$.value === 'reconnecting' ? ('Reconnecting' | i18n) : ('Waiting for network connectivity' | i18n) }} -

+
}
} @else { diff --git a/web/projects/ui/src/app/routes/portal/components/logs/logs.component.scss b/web/projects/ui/src/app/routes/portal/components/logs/logs.component.scss index 0e12ddae2..10e63f8a5 100644 --- a/web/projects/ui/src/app/routes/portal/components/logs/logs.component.scss +++ b/web/projects/ui/src/app/routes/portal/components/logs/logs.component.scss @@ -48,4 +48,5 @@ pre { overflow: visible; white-space: normal; + margin: 0; } diff --git a/web/projects/ui/src/app/routes/portal/components/logs/logs.pipe.ts b/web/projects/ui/src/app/routes/portal/components/logs/logs.pipe.ts index 5093274cb..76ca18cab 100644 --- a/web/projects/ui/src/app/routes/portal/components/logs/logs.pipe.ts +++ b/web/projects/ui/src/app/routes/portal/components/logs/logs.pipe.ts @@ -8,16 +8,19 @@ import { import { bufferTime, catchError, + concat, defer, + delay, + EMPTY, filter, ignoreElements, map, merge, Observable, + of, repeat, scan, skipWhile, - startWith, switchMap, take, tap, @@ -62,12 +65,19 @@ export class LogsPipe implements PipeTransform { ), ).pipe( catchError(() => - this.connection.pipe( - tap(v => this.logs.status$.next(v ? 'reconnecting' : 'disconnected')), - filter(Boolean), - take(1), - ignoreElements(), - startWith(this.getMessage(false)), + concat( + this.logs.status$.value === 'connected' + ? of(this.getMessage(false)) + : EMPTY, + this.connection.pipe( + tap(v => + this.logs.status$.next(v ? 'reconnecting' : 'disconnected'), + ), + filter(Boolean), + delay(1000), + take(1), + ignoreElements(), + ), ), ), repeat(), @@ -76,11 +86,11 @@ export class LogsPipe implements PipeTransform { } private getMessage(success: boolean): string { - return `

${this.i18n.transform( success ? 'Reconnected' : 'Disconnected', - )} at ${toLocalIsoString(new Date())}

` + )} at ${toLocalIsoString(new Date())}` } private get options() { diff --git a/web/projects/ui/src/app/routes/portal/portal.component.ts b/web/projects/ui/src/app/routes/portal/portal.component.ts index 6d905f461..e0ad431ec 100644 --- a/web/projects/ui/src/app/routes/portal/portal.component.ts +++ b/web/projects/ui/src/app/routes/portal/portal.component.ts @@ -26,7 +26,7 @@ import { HeaderComponent } from './components/header/header.component' @if (update(); as update) { - + @if (update === true) { Download complete, restart to apply changes @@ -77,8 +77,7 @@ import { HeaderComponent } from './components/header/header.component' @include taiga.transition(filter); - header:has([data-status='success']) + &, - header:has([data-status='neutral']) + & { + header:has([data-status='success']) + & { filter: none; } } @@ -104,7 +103,7 @@ export class PortalComponent { readonly name = toSignal(this.patch.watch$('ui', 'name')) readonly update = toSignal(inject(OSService).updating$) - bar = true + readonly bar = signal(true) getProgress(size: number, downloaded: number): number { return Math.round((100 * downloaded) / (size || 1)) @@ -114,7 +113,7 @@ export class PortalComponent { const loader = this.loader.open('Beginning restart').subscribe() try { - this.bar = false + this.bar.set(false) await this.api.restartServer({}) } catch (e: any) { this.errorService.handleError(e) diff --git a/web/projects/ui/src/app/routes/portal/routes/marketplace/components/controls.component.ts b/web/projects/ui/src/app/routes/portal/routes/marketplace/components/controls.component.ts index 99bd375f1..7c6442a02 100644 --- a/web/projects/ui/src/app/routes/portal/routes/marketplace/components/controls.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/marketplace/components/controls.component.ts @@ -3,11 +3,9 @@ import { ChangeDetectionStrategy, Component, inject, - Input, + input, } from '@angular/core' -import { toSignal } from '@angular/core/rxjs-interop' import { Router } from '@angular/router' -import { MarketplacePkgBase } from '@start9labs/marketplace' import { ErrorService, Exver, @@ -30,21 +28,20 @@ import { import { dryUpdate } from 'src/app/utils/dry-update' import { getAllPackages, getManifest } from 'src/app/utils/get-package-data' import { hasCurrentDeps } from 'src/app/utils/has-deps' - import { MarketplacePreviewComponent } from '../modals/preview.component' import { MarketplaceAlertsService } from '../services/alerts.service' @Component({ selector: 'marketplace-controls', template: ` - @if (localPkg) { - @if (localPkg | toManifest; as localManifest) { - @switch (localManifest.version | compareExver: version() || '') { + @if (localPkg(); as local) { + @if (local.stateInfo.state === 'installed') { + @switch ((local | toManifest).version | compareExver: version()) { @case (1) { } @else { } `, @@ -116,29 +113,23 @@ export class MarketplaceControlsComponent { private readonly api = inject(ApiService) private readonly preview = inject(MarketplacePreviewComponent) - protected readonly version = toSignal(this.preview.version$) - - @Input({ required: true }) - pkg!: MarketplacePkgBase - - @Input() - localPkg!: PackageDataEntry | null - - @Input() - localFlavor!: boolean - + version = input.required() + installAlert = input.required() + localPkg = input.required() + localFlavor = input.required() // only present if side loading - @Input() - file?: File + file = input() async tryInstall() { - const currentUrl = this.file + const localPkg = this.localPkg() + + const currentUrl = this.file() ? null : await firstValueFrom(this.marketplaceService.currentRegistryUrl$) - const originalUrl = this.localPkg?.registry || null + const originalUrl = localPkg?.registry || null - if (!this.localPkg) { - if (await this.alerts.alertInstall(this.pkg)) { + if (!localPkg) { + if (await this.alerts.alertInstall(this.installAlert() || '')) { this.installOrUpload(currentUrl) } return @@ -152,12 +143,11 @@ export class MarketplaceControlsComponent { return } - const localManifest = getManifest(this.localPkg) - const version = this.version() || '' + const localManifest = getManifest(localPkg) if ( hasCurrentDeps(localManifest.id, await getAllPackages(this.patch)) && - this.exver.compareExver(localManifest.version, version) !== 0 + this.exver.compareExver(localManifest.version, this.version()) !== 0 ) { this.dryInstall(currentUrl) } else { @@ -171,9 +161,8 @@ export class MarketplaceControlsComponent { private async dryInstall(url: string | null) { const id = this.preview.pkgId - const version = this.version() || '' const breakages = dryUpdate( - { id, version }, + { id, version: this.version() }, await getAllPackages(this.patch), this.exver, ) @@ -187,7 +176,7 @@ export class MarketplaceControlsComponent { } private async installOrUpload(url: string | null) { - if (this.file) { + if (this.file()) { await this.upload() this.router.navigate(['/portal', 'services']) } else if (url) { @@ -197,11 +186,10 @@ export class MarketplaceControlsComponent { private async install(url: string) { const loader = this.loader.open('Beginning install').subscribe() - const version = this.version() || '' const id = this.preview.pkgId try { - await this.marketplaceService.installPackage(id, version, url) + await this.marketplaceService.installPackage(id, this.version(), url) } catch (e: any) { this.errorService.handleError(e) } finally { @@ -210,11 +198,14 @@ export class MarketplaceControlsComponent { } private async upload() { + const file = this.file() + if (!file) throw new Error('no file detected') + const loader = this.loader.open('Starting upload').subscribe() try { const { upload } = await this.api.sideloadPackage() - this.api.uploadPackage(upload, this.file!).catch(console.error) + this.api.uploadPackage(upload, file).catch(console.error) } catch (e: any) { this.errorService.handleError(e) } finally { diff --git a/web/projects/ui/src/app/routes/portal/routes/marketplace/components/tile.component.ts b/web/projects/ui/src/app/routes/portal/routes/marketplace/components/tile.component.ts index 593136887..ce5313d14 100644 --- a/web/projects/ui/src/app/routes/portal/routes/marketplace/components/tile.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/marketplace/components/tile.component.ts @@ -56,7 +56,8 @@ import { MarketplaceControlsComponent } from './controls.component' diff --git a/web/projects/ui/src/app/routes/portal/routes/marketplace/services/alerts.service.ts b/web/projects/ui/src/app/routes/portal/routes/marketplace/services/alerts.service.ts index 3db5d5e3d..f38095347 100644 --- a/web/projects/ui/src/app/routes/portal/routes/marketplace/services/alerts.service.ts +++ b/web/projects/ui/src/app/routes/portal/routes/marketplace/services/alerts.service.ts @@ -62,9 +62,7 @@ export class MarketplaceAlertsService { }) } - async alertInstall({ alerts }: MarketplacePkgBase): Promise { - const content = alerts.install - + async alertInstall(content: string): Promise { return ( !content || (!!content && diff --git a/web/projects/ui/src/app/routes/portal/routes/services/modals/action-input.component.ts b/web/projects/ui/src/app/routes/portal/routes/services/modals/action-input.component.ts index 79e46c6c6..8c8820d24 100644 --- a/web/projects/ui/src/app/routes/portal/routes/services/modals/action-input.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/services/modals/action-input.component.ts @@ -90,17 +90,20 @@ export type PackageActionData = { `, styles: ` tui-notification { - font-size: 1rem; - margin-bottom: 1.4rem; + margin-bottom: 1.5rem; } + .service-title { display: inline-flex; align-items: center; - margin-bottom: 1.4rem; + margin-bottom: 1.5rem; + img { - height: 20px; - margin-right: 4px; + height: 1.25rem; + margin-right: 0.25rem; + border-radius: 100%; } + h4 { margin: 0; } @@ -192,7 +195,7 @@ export class ActionInputModal { task.when?.condition === 'input-not-matches' && task.input && json - .compare(input, task.input) + .compare(input, task.input.value) .some(op => op.op === 'add' || op.op === 'replace'), ), ) @@ -201,9 +204,8 @@ export class ActionInputModal { if (!breakages.length) return true const message = `${this.i18n.transform('As a result of this change, the following services will no longer work properly and may crash')}:
    ` - const content = `${message}${breakages.map( - id => `
  • ${getManifest(packages[id]!).title}
  • `, - )}
` as i18nKey + const content = + `${message}${breakages.map(id => `
  • ${getManifest(packages[id]!).title}
  • `)}` as i18nKey return firstValueFrom( this.dialog diff --git a/web/projects/ui/src/app/routes/portal/routes/sideload/package.component.ts b/web/projects/ui/src/app/routes/portal/routes/sideload/package.component.ts index 56a186df8..5a8e73ccb 100644 --- a/web/projects/ui/src/app/routes/portal/routes/sideload/package.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/sideload/package.component.ts @@ -29,7 +29,8 @@ import { MarketplacePkgSideload } from './sideload.utils' - > = {}, + local: Record = {}, ): MarketplacePkg[] { - return pkgs.filter(({ id, version, flavor }) => { + return pkgs.filter(({ id, flavor, version }) => { const localPkg = local[id] return ( - localPkg && + !!localPkg && + (localPkg.stateInfo.state === 'installed' || + localPkg.stateInfo.state === 'updating') && this.exver.getFlavor(localPkg.stateInfo.manifest.version) === flavor && this.exver.compareExver( version, diff --git a/web/projects/ui/src/app/routes/portal/routes/updates/item.component.ts b/web/projects/ui/src/app/routes/portal/routes/updates/item.component.ts index 4ae1a5e71..cccbd1ff8 100644 --- a/web/projects/ui/src/app/routes/portal/routes/updates/item.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/updates/item.component.ts @@ -44,7 +44,7 @@ import UpdatesComponent from './updates.component' template: ` -
    +
    {{ item().title }} @@ -81,7 +81,6 @@ import UpdatesComponent from './updates.component' @if (local().stateInfo.state === 'updating') { /rpc/v1" + "target": "http://" }, - "/ws/*": { - "target": "http://", + "/ws/**": { + "target": "wss://159.223.45.210", "secure": false, "ws": true }, - "/public/*": { - "target": "http:///public", - "pathRewrite": { - "^/public": "" - } + "/public/**": { + "target": "http://" }, - "/rest/rpc/*": { - "target": "http:///rest/rpc", - "pathRewrite": { - "^/rest/rpc": "" - } + "/rest/rpc/**": { + "target": "http://" } }