From b40849f67217c155e2fef814c3e5aa5a4fd41c96 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Wed, 21 May 2025 19:04:26 -0600 Subject: [PATCH] Fix/fe bugs 3 (#2943) * fix typeo in patch db seed * show all registries in updates tab, fix required dependnecy display in marketplace, update browser tab title desc * always show pointer for version select * chore: fix comments * support html in action desc and marketplace long desc, only show qr in action res if qr is true * disable save if smtp creds not edited, show better smtp success message * dont dismiss login spinner until patchDB returns * feat: redesign of service dashboard and interface (#2946) * feat: redesign of service dashboard and interface * chore: comments * re-add setup complete * dibale launch UI when not running, re-style things, rename things * back to 1000 * fix clearnet docs link and require password retype in setup wiz * faster hint display * display dependency ID if title not available * fix migration * better init progress view * fix setup success page by providing VERSION and notifications page fixes * force uninstall from service error page, soft or hard * handle error state better * chore: fixed for install and setup wizards * chore: fix issues (#2949) * enable and disable kiosk mode * minor fixes * fix dependency mounts * dismissable tasks * provide replayId * default if health check success message is null * look for wifi interface too * dash for null user agent in sessions * add disk repair to diagnostic api --------- Co-authored-by: waterplea Co-authored-by: Aiden McClelland --- container-runtime/package-lock.json | 2 +- .../DockerProcedureContainer.ts | 1 + .../Systems/SystemForEmbassy/index.ts | 2 + core/startos/src/action.rs | 1 + core/startos/src/diagnostic.rs | 10 + .../startos/src/disk/mount/filesystem/bind.rs | 67 +++++- .../src/disk/mount/filesystem/ecryptfs.rs | 3 +- .../src/disk/mount/filesystem/idmapped.rs | 7 +- core/startos/src/disk/mount/filesystem/mod.rs | 10 +- .../src/disk/mount/filesystem/overlayfs.rs | 3 +- .../startos/src/service/effects/dependency.rs | 52 +---- core/startos/src/version/v0_3_6_alpha_0.rs | 21 +- sdk/base/lib/Effects.ts | 13 +- sdk/base/lib/osBindings/FileType.ts | 2 +- sdk/base/lib/osBindings/MountTarget.ts | 2 +- sdk/package/lib/StartSdk.ts | 2 +- sdk/package/lib/util/fileHelper.ts | 9 +- sdk/package/package-lock.json | 4 +- sdk/package/package.json | 2 +- web/patchdb-ui-seed.json | 2 +- .../install-wizard/src/app/app.component.scss | 5 + .../install-wizard/src/app/app.component.ts | 7 +- .../src/pages/show/about/about.component.html | 2 +- .../dependencies/dependency-item.component.ts | 4 +- .../setup-wizard/src/app/app.component.ts | 8 + .../setup-wizard/src/app/app.module.ts | 7 + .../src/app/components/matrix.component.ts | 2 +- .../src/app/components/password.component.ts | 126 +++++++---- .../setup-wizard/src/app/pages/attach.page.ts | 10 +- .../setup-wizard/src/app/pages/home.page.ts | 6 +- .../src/app/pages/recover.page.ts | 13 +- .../src/app/pages/storage.page.ts | 11 +- .../src/app/pages/success.page.ts | 12 +- .../src/app/pages/transfer.page.ts | 10 +- .../src/app/services/state.service.ts | 3 + web/projects/setup-wizard/src/styles.scss | 10 +- .../assets/img/background_marketplace.jpg | Bin 0 -> 14539 bytes .../assets/img/background_marketplace.png | Bin 710359 -> 0 bytes .../src/components/initializing.component.ts | 4 +- .../shared/src/components/ticker.component.ts | 1 - .../src/directives/docs-link.directive.ts | 3 +- .../shared/src/i18n/dictionaries/de.ts | 32 ++- .../shared/src/i18n/dictionaries/en.ts | 34 ++- .../shared/src/i18n/dictionaries/es.ts | 34 ++- .../shared/src/i18n/dictionaries/fr.ts | 32 ++- .../shared/src/i18n/dictionaries/pl.ts | 32 ++- web/projects/shared/src/i18n/i18n.pipe.ts | 2 - web/projects/shared/src/i18n/i18n.service.ts | 8 +- .../shared/src/util/format-progress.ts | 25 ++- web/projects/shared/styles/shared.scss | 1 + web/projects/shared/styles/taiga.scss | 18 ++ .../routes/initializing/initializing.page.ts | 2 +- .../ui/src/app/routes/login/login.page.ts | 1 - .../form/form-array/form-array.component.ts | 15 +- .../components/header/menu.component.ts | 2 +- .../components/header/navigation.component.ts | 2 +- .../interfaces/actions.component.ts | 67 +++--- .../interfaces/clearnet.component.ts | 76 ++----- .../interfaces/interface.component.ts | 74 ++++++- .../components/interfaces/local.component.ts | 3 +- .../portal/components/interfaces/mask.pipe.ts | 2 +- .../components/interfaces/status.component.ts | 7 +- .../components/interfaces/tor.component.ts | 23 +- .../src/app/routes/portal/portal.component.ts | 7 + .../ui/src/app/routes/portal/portal.routes.ts | 2 +- .../logs/components/header.component.ts | 64 ++++++ .../portal/routes/logs/logs.component.ts | 202 ------------------ .../routes/portal/routes/logs/logs.routes.ts | 22 ++ .../routes/logs/routes/kernel.component.ts | 40 ++++ .../portal/routes/logs/routes/os.component.ts | 39 ++++ .../routes/logs/routes/outlet.component.ts | 96 +++++++++ .../routes/logs/routes/tor.component.ts | 39 ++++ .../marketplace/marketplace.component.ts | 9 +- .../marketplace/modals/preview.component.ts | 23 +- .../routes/metrics/temperature.component.ts | 2 +- .../routes/notifications/item.component.ts | 21 +- .../notifications/notifications.component.ts | 20 +- .../routes/notifications/table.component.ts | 12 +- .../services/components/action.component.ts | 2 +- .../components/dependencies.component.ts | 2 +- .../services/components/error.component.ts | 42 ++-- .../components/health-check.component.ts | 2 +- ...mponent.ts => interface-item.component.ts} | 90 ++++---- .../components/interfaces.component.ts | 10 +- .../services/components/status.component.ts | 5 + .../services/components/task.component.ts | 114 +++++++--- .../services/components/tasks.component.ts | 17 +- .../services/components/uptime.component.ts | 90 ++++++++ .../services/dashboard/controls.component.ts | 6 +- .../services/dashboard/dashboard.component.ts | 4 +- .../services/dashboard/status.component.ts | 9 +- ...ui.component.ts => ui-launch.component.ts} | 31 ++- .../action-success-single.component.ts | 4 +- .../services/routes/interface.component.ts | 63 +++++- .../services/routes/outlet.component.ts | 23 +- .../services/routes/service.component.ts | 73 ++++++- .../system/routes/acme/acme.component.ts | 6 + .../system/routes/email/email.component.ts | 10 +- .../routes/general/general.component.ts | 90 +++++++- .../system/routes/general/update.component.ts | 2 +- .../routes/sessions/sessions.component.ts | 45 ++-- .../system/routes/sessions/table.component.ts | 2 +- .../startos-ui.component.ts} | 24 ++- .../system/routes/wifi/wifi.component.ts | 2 +- .../portal/routes/system/system.component.ts | 10 +- .../portal/routes/system/system.const.ts | 10 +- .../portal/routes/system/system.routes.ts | 12 +- .../portal/routes/updates/item.component.ts | 5 + .../routes/updates/updates.component.ts | 13 +- web/projects/ui/src/app/routing.module.ts | 2 +- .../ui/src/app/services/api/api.fixures.ts | 65 +----- .../ui/src/app/services/api/api.types.ts | 12 +- .../app/services/api/embassy-api.service.ts | 4 + .../services/api/embassy-live-api.service.ts | 11 + .../services/api/embassy-mock-api.service.ts | 65 ++++-- .../ui/src/app/services/api/mock-patch.ts | 10 +- .../app/services/client-storage.service.ts | 15 +- .../src/app/services/marketplace.service.ts | 16 -- .../src/app/services/notification.service.ts | 3 +- .../services/pkg-status-rendering.service.ts | 19 +- .../app/services/standard-actions.service.ts | 28 ++- web/projects/ui/src/index.html | 8 +- web/projects/ui/src/styles.scss | 1 + 123 files changed, 1662 insertions(+), 964 deletions(-) create mode 100644 web/projects/shared/assets/img/background_marketplace.jpg delete mode 100644 web/projects/shared/assets/img/background_marketplace.png create mode 100644 web/projects/ui/src/app/routes/portal/routes/logs/components/header.component.ts delete mode 100644 web/projects/ui/src/app/routes/portal/routes/logs/logs.component.ts create mode 100644 web/projects/ui/src/app/routes/portal/routes/logs/logs.routes.ts create mode 100644 web/projects/ui/src/app/routes/portal/routes/logs/routes/kernel.component.ts create mode 100644 web/projects/ui/src/app/routes/portal/routes/logs/routes/os.component.ts create mode 100644 web/projects/ui/src/app/routes/portal/routes/logs/routes/outlet.component.ts create mode 100644 web/projects/ui/src/app/routes/portal/routes/logs/routes/tor.component.ts rename web/projects/ui/src/app/routes/portal/routes/services/components/{interface.component.ts => interface-item.component.ts} (58%) create mode 100644 web/projects/ui/src/app/routes/portal/routes/services/components/uptime.component.ts rename web/projects/ui/src/app/routes/portal/routes/services/dashboard/{ui.component.ts => ui-launch.component.ts} (80%) rename web/projects/ui/src/app/routes/portal/routes/system/routes/{interfaces/interfaces.component.ts => startos-ui/startos-ui.component.ts} (85%) diff --git a/container-runtime/package-lock.json b/container-runtime/package-lock.json index 1c8018fc9..2abce19f8 100644 --- a/container-runtime/package-lock.json +++ b/container-runtime/package-lock.json @@ -37,7 +37,7 @@ }, "../sdk/dist": { "name": "@start9labs/start-sdk", - "version": "0.4.0-beta.23", + "version": "0.4.0-beta.24", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts index 6da874757..b0bc32504 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts @@ -118,6 +118,7 @@ export class DockerProcedureContainer extends Drop { subpath: volumeMount.path, readonly: volumeMount.readonly, volumeId: volumeMount["volume-id"], + filetype: "directory", }, }) } else if (volumeMount.type === "backup") { diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts index b3b1af569..e314fba74 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts @@ -1022,6 +1022,7 @@ export class SystemForEmbassy implements System { volumeId: "embassy", subpath: null, readonly: true, + filetype: "directory", }, }) configFile @@ -1168,6 +1169,7 @@ async function updateConfig( volumeId: "embassy", subpath: null, readonly: true, + filetype: "directory", }, }) const remoteConfig = configFile diff --git a/core/startos/src/action.rs b/core/startos/src/action.rs index 5528f7ab1..18765a776 100644 --- a/core/startos/src/action.rs +++ b/core/startos/src/action.rs @@ -348,6 +348,7 @@ pub struct ClearTaskParams { pub package_id: PackageId, pub replay_id: ReplayId, #[arg(long)] + #[serde(default)] pub force: bool, } diff --git a/core/startos/src/diagnostic.rs b/core/startos/src/diagnostic.rs index 92733c972..2c6040e8d 100644 --- a/core/startos/src/diagnostic.rs +++ b/core/startos/src/diagnostic.rs @@ -7,6 +7,7 @@ use rpc_toolkit::{ }; use crate::context::{CliContext, DiagnosticContext, RpcContext}; +use crate::disk::repair; use crate::init::SYSTEM_REBUILD_PATH; use crate::prelude::*; use crate::shutdown::Shutdown; @@ -95,6 +96,15 @@ pub fn disk() -> ParentHandler { .no_display() .with_about("Remove disk from filesystem"), ) + .subcommand("repair", from_fn_async(|_: C| repair()).no_cli()) + .subcommand( + "repair", + CallRemoteHandler::::new( + from_fn_async(|_: RpcContext| repair()) + .no_display() + .with_about("Repair disk in the event of corruption"), + ), + ) } pub async fn forget_disk(_: C) -> Result<(), Error> { diff --git a/core/startos/src/disk/mount/filesystem/bind.rs b/core/startos/src/disk/mount/filesystem/bind.rs index 196e78a3d..f005e47cf 100644 --- a/core/startos/src/disk/mount/filesystem/bind.rs +++ b/core/startos/src/disk/mount/filesystem/bind.rs @@ -3,28 +3,73 @@ use std::path::Path; use digest::generic_array::GenericArray; use digest::{Digest, OutputSizeUser}; +use serde::{Deserialize, Serialize}; use sha2::Sha256; +use ts_rs::TS; use super::FileSystem; use crate::prelude::*; +use crate::util::io::create_file; -pub struct Bind> { - src_dir: SrcDir, +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +#[ts(export)] +#[serde(rename_all = "kebab-case")] +pub enum FileType { + File, + Directory, + Infer, } -impl> Bind { - pub fn new(src_dir: SrcDir) -> Self { - Self { src_dir } + +pub struct Bind> { + src: Src, + filetype: FileType, +} +impl> Bind { + pub fn new(src: Src) -> Self { + Self { + src, + filetype: FileType::Directory, + } + } + pub fn with_type(mut self, filetype: FileType) -> Self { + self.filetype = filetype; + self } } -impl + Send + Sync> FileSystem for Bind { +impl + Send + Sync> FileSystem for Bind { async fn source(&self) -> Result>, Error> { - Ok(Some(&self.src_dir)) + Ok(Some(&self.src)) } fn extra_args(&self) -> impl IntoIterator> { ["--bind"] } - async fn pre_mount(&self) -> Result<(), Error> { - tokio::fs::create_dir_all(self.src_dir.as_ref()).await?; + async fn pre_mount(&self, mountpoint: &Path) -> Result<(), Error> { + let from_meta = tokio::fs::metadata(&self.src).await.ok(); + let to_meta = tokio::fs::metadata(&mountpoint).await.ok(); + if matches!(self.filetype, FileType::File) + || (matches!(self.filetype, FileType::Infer) + && from_meta.as_ref().map_or(false, |m| m.is_file())) + { + if to_meta.as_ref().map_or(false, |m| m.is_dir()) { + tokio::fs::remove_dir(mountpoint).await?; + } + if from_meta.is_none() { + create_file(self.src.as_ref()).await?.sync_all().await?; + } + if to_meta.is_none() { + create_file(mountpoint).await?.sync_all().await?; + } + } else { + if to_meta.as_ref().map_or(false, |m| m.is_file()) { + tokio::fs::remove_file(mountpoint).await?; + } + if from_meta.is_none() { + tokio::fs::create_dir_all(self.src.as_ref()).await?; + } + if to_meta.is_none() { + tokio::fs::create_dir_all(mountpoint).await?; + } + } Ok(()) } async fn source_hash( @@ -33,12 +78,12 @@ impl + Send + Sync> FileSystem for Bind { let mut sha = Sha256::new(); sha.update("Bind"); sha.update( - tokio::fs::canonicalize(self.src_dir.as_ref()) + tokio::fs::canonicalize(self.src.as_ref()) .await .with_ctx(|_| { ( crate::ErrorKind::Filesystem, - self.src_dir.as_ref().display().to_string(), + self.src.as_ref().display().to_string(), ) })? .as_os_str() diff --git a/core/startos/src/disk/mount/filesystem/ecryptfs.rs b/core/startos/src/disk/mount/filesystem/ecryptfs.rs index bf2dfe6c6..bc1a0b65b 100644 --- a/core/startos/src/disk/mount/filesystem/ecryptfs.rs +++ b/core/startos/src/disk/mount/filesystem/ecryptfs.rs @@ -49,8 +49,7 @@ impl + Send + Sync, Key: AsRef + Send + Sync> Fil mountpoint: P, mount_type: super::MountType, ) -> Result<(), Error> { - self.pre_mount().await?; - tokio::fs::create_dir_all(mountpoint.as_ref()).await?; + self.pre_mount(mountpoint.as_ref()).await?; Command::new("mount") .args( default_mount_command(self, mountpoint, mount_type) diff --git a/core/startos/src/disk/mount/filesystem/idmapped.rs b/core/startos/src/disk/mount/filesystem/idmapped.rs index dc6a6e9ab..a39b08e56 100644 --- a/core/startos/src/disk/mount/filesystem/idmapped.rs +++ b/core/startos/src/disk/mount/filesystem/idmapped.rs @@ -53,16 +53,15 @@ impl FileSystem for IdMapped { async fn source(&self) -> Result>, Error> { self.filesystem.source().await } - async fn pre_mount(&self) -> Result<(), Error> { - self.filesystem.pre_mount().await + async fn pre_mount(&self, mountpoint: &Path) -> Result<(), Error> { + self.filesystem.pre_mount(mountpoint).await } async fn mount + Send>( &self, mountpoint: P, mount_type: MountType, ) -> Result<(), Error> { - self.pre_mount().await?; - tokio::fs::create_dir_all(mountpoint.as_ref()).await?; + self.pre_mount(mountpoint.as_ref()).await?; Command::new("mount.next") .args( default_mount_command(self, mountpoint, mount_type) diff --git a/core/startos/src/disk/mount/filesystem/mod.rs b/core/startos/src/disk/mount/filesystem/mod.rs index 80bfcc903..f4c85296a 100644 --- a/core/startos/src/disk/mount/filesystem/mod.rs +++ b/core/startos/src/disk/mount/filesystem/mod.rs @@ -69,8 +69,7 @@ pub(self) async fn default_mount_impl( mountpoint: impl AsRef + Send, mount_type: MountType, ) -> Result<(), Error> { - fs.pre_mount().await?; - tokio::fs::create_dir_all(mountpoint.as_ref()).await?; + fs.pre_mount(mountpoint.as_ref()).await?; Command::from(default_mount_command(fs, mountpoint, mount_type).await?) .capture(false) .invoke(ErrorKind::Filesystem) @@ -92,8 +91,11 @@ pub trait FileSystem: Send + Sync { fn source(&self) -> impl Future>, Error>> + Send { async { Ok(None::<&Path>) } } - fn pre_mount(&self) -> impl Future> + Send { - async { Ok(()) } + fn pre_mount(&self, mountpoint: &Path) -> impl Future> + Send { + async move { + tokio::fs::create_dir_all(mountpoint).await?; + Ok(()) + } } fn mount + Send>( &self, diff --git a/core/startos/src/disk/mount/filesystem/overlayfs.rs b/core/startos/src/disk/mount/filesystem/overlayfs.rs index e8d1f0b34..85df0e12c 100644 --- a/core/startos/src/disk/mount/filesystem/overlayfs.rs +++ b/core/startos/src/disk/mount/filesystem/overlayfs.rs @@ -41,9 +41,10 @@ impl< Box::new(lazy_format!("workdir={}", self.work.as_ref().display())), ] } - async fn pre_mount(&self) -> Result<(), Error> { + async fn pre_mount(&self, mountpoint: &Path) -> Result<(), Error> { tokio::fs::create_dir_all(self.upper.as_ref()).await?; tokio::fs::create_dir_all(self.work.as_ref()).await?; + tokio::fs::create_dir_all(mountpoint).await?; Ok(()) } async fn source_hash( diff --git a/core/startos/src/service/effects/dependency.rs b/core/startos/src/service/effects/dependency.rs index 17b8e3067..5bd94e108 100644 --- a/core/startos/src/service/effects/dependency.rs +++ b/core/startos/src/service/effects/dependency.rs @@ -10,10 +10,10 @@ use models::{FromStrParser, HealthCheckId, PackageId, ReplayId, VersionString, V use tokio::process::Command; use crate::db::model::package::{ - TaskEntry, CurrentDependencies, CurrentDependencyInfo, CurrentDependencyKind, - ManifestPreference, + CurrentDependencies, CurrentDependencyInfo, CurrentDependencyKind, ManifestPreference, + TaskEntry, }; -use crate::disk::mount::filesystem::bind::Bind; +use crate::disk::mount::filesystem::bind::{Bind, FileType}; use crate::disk::mount::filesystem::idmapped::IdMapped; use crate::disk::mount::filesystem::{FileSystem, MountType}; use crate::disk::mount::util::{is_mountpoint, unmount}; @@ -23,14 +23,6 @@ use crate::util::Invoke; use crate::volume::data_dir; use crate::DATA_DIR; -#[derive(Debug, Clone, Serialize, Deserialize, TS)] -#[ts(export)] -#[serde(rename_all = "camelCase")] -pub enum FileType { - File, - Directory, -} - #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[ts(export)] #[serde(rename_all = "camelCase")] @@ -39,8 +31,7 @@ pub struct MountTarget { volume_id: VolumeId, subpath: Option, readonly: bool, - #[ts(optional)] - filetype: Option, + filetype: FileType, } #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[ts(export)] @@ -67,7 +58,6 @@ pub async fn mount( let subpath = subpath.unwrap_or_default(); let subpath = subpath.strip_prefix("/").unwrap_or(&subpath); let source = data_dir(DATA_DIR, &package_id, &volume_id).join(subpath); - let from_meta = tokio::fs::metadata(&source).await.ok(); let location = location.strip_prefix("/").unwrap_or(&location); let mountpoint = context .seed @@ -77,39 +67,7 @@ pub async fn mount( .or_not_found("lxc container")? .rootfs_dir() .join(location); - let to_meta = tokio::fs::metadata(&mountpoint).await.ok(); - if matches!(filetype, Some(FileType::File)) - || (filetype.is_none() && from_meta.as_ref().map_or(false, |m| m.is_file())) - { - if to_meta.as_ref().map_or(false, |m| m.is_dir()) { - tokio::fs::remove_dir(&mountpoint).await?; - } - if from_meta.is_none() { - if let Some(parent) = source.parent() { - tokio::fs::create_dir_all(parent).await?; - } - tokio::fs::write(&source, "").await?; - } - if to_meta.is_none() { - if let Some(parent) = mountpoint.parent() { - tokio::fs::create_dir_all(parent).await?; - } - tokio::fs::write(&mountpoint, "").await?; - } - } else { - if to_meta.as_ref().map_or(false, |m| m.is_file()) { - tokio::fs::remove_file(&mountpoint).await?; - } - if from_meta.is_none() { - tokio::fs::create_dir_all(&source).await?; - } - if to_meta.is_none() { - tokio::fs::create_dir_all(&mountpoint).await?; - } - } - - tokio::fs::create_dir_all(&mountpoint).await?; if is_mountpoint(&mountpoint).await? { unmount(&mountpoint, true).await?; } @@ -118,7 +76,7 @@ pub async fn mount( .arg(&mountpoint) .invoke(crate::ErrorKind::Filesystem) .await?; - IdMapped::new(Bind::new(source), 0, 100000, 65536) + IdMapped::new(Bind::new(source).with_type(filetype), 0, 100000, 65536) .mount( mountpoint, if readonly { diff --git a/core/startos/src/version/v0_3_6_alpha_0.rs b/core/startos/src/version/v0_3_6_alpha_0.rs index b772d6c0b..3954e80db 100644 --- a/core/startos/src/version/v0_3_6_alpha_0.rs +++ b/core/startos/src/version/v0_3_6_alpha_0.rs @@ -211,27 +211,12 @@ impl VersionT for Version { } fn up(self, db: &mut Value, (account, ssh_keys, cifs): Self::PreUpRes) -> Result<(), Error> { let wifi = json!({ - "infterface": db["server-info"]["wifi"]["interface"], + "interface": db["server-info"]["wifi"]["interface"], "ssids": db["server-info"]["wifi"]["ssids"], "selected": db["server-info"]["wifi"]["selected"], - "last_region": db["server-info"]["wifi"]["last-region"], + "lastRegion": db["server-info"]["wifi"]["last-region"], }); - let ip_info = { - let mut ip_info = json!({}); - let empty = Default::default(); - for (k, v) in db["server-info"]["ip-info"].as_object().unwrap_or(&empty) { - let k: &str = k.as_ref(); - ip_info[k] = json!({ - "ipv4Range": v["ipv4-range"], - "ipv6Range": v["ipv6-range"], - "ipv4": v["ipv4"], - "ipv6": v["ipv6"], - }); - } - ip_info - }; - let status_info = json!({ "backupProgress": db["server-info"]["status-info"]["backup-progress"], "updated": db["server-info"]["status-info"]["updated"], @@ -259,7 +244,7 @@ impl VersionT for Version { .replace("https://", "") .replace("http://", "") .replace(".onion/", "")); - server_info["ipInfo"] = ip_info; + server_info["networkInterfaces"] = json!({}); server_info["statusInfo"] = status_info; server_info["wifi"] = wifi; server_info["unreadNotificationCount"] = diff --git a/sdk/base/lib/Effects.ts b/sdk/base/lib/Effects.ts index 774fc519b..91231f19c 100644 --- a/sdk/base/lib/Effects.ts +++ b/sdk/base/lib/Effects.ts @@ -1,4 +1,3 @@ -import { ExtendedVersion, VersionRange } from "./exver" import { ActionId, ActionInput, @@ -15,6 +14,7 @@ import { ServiceInterface, CreateTaskParams, MainStatus, + MountParams, } from "./osBindings" import { PackageId, @@ -23,7 +23,6 @@ import { SmtpValue, ActionResult, } from "./types" -import { UrlString } from "./util/getServiceInterface" /** Used to reach out from the pure js runtime */ @@ -80,15 +79,7 @@ export type Effects = { packageIds?: PackageId[] }): Promise /** mount a volume of a dependency */ - mount(options: { - location: string - target: { - packageId: string - volumeId: string - subpath: string | null - readonly: boolean - } - }): Promise + mount(options: MountParams): Promise /** Returns a list of the ids of all installed packages */ getInstalledPackages(): Promise diff --git a/sdk/base/lib/osBindings/FileType.ts b/sdk/base/lib/osBindings/FileType.ts index 82b9ca474..85eb97ad6 100644 --- a/sdk/base/lib/osBindings/FileType.ts +++ b/sdk/base/lib/osBindings/FileType.ts @@ -1,3 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type FileType = "file" | "directory" +export type FileType = "file" | "directory" | "infer" diff --git a/sdk/base/lib/osBindings/MountTarget.ts b/sdk/base/lib/osBindings/MountTarget.ts index e208383e3..456f17052 100644 --- a/sdk/base/lib/osBindings/MountTarget.ts +++ b/sdk/base/lib/osBindings/MountTarget.ts @@ -8,5 +8,5 @@ export type MountTarget = { volumeId: VolumeId subpath: string | null readonly: boolean - filetype?: FileType + filetype: FileType } diff --git a/sdk/package/lib/StartSdk.ts b/sdk/package/lib/StartSdk.ts index 350f26d0b..9f303fa87 100644 --- a/sdk/package/lib/StartSdk.ts +++ b/sdk/package/lib/StartSdk.ts @@ -412,7 +412,7 @@ export class StartSdk { id: string /** The human readable description. */ description: string - /** Affects how the interface appears to the user. One of: 'ui', 'api', 'p2p'. If 'ui', the user will see a "Launch UI" button */ + /** Affects how the interface appears to the user. One of: 'ui', 'api', 'p2p'. If 'ui', the user will see an option to open the UI in a new tab */ type: ServiceInterfaceType /** (optional) prepends the provided username to all URLs. */ username: null | string diff --git a/sdk/package/lib/util/fileHelper.ts b/sdk/package/lib/util/fileHelper.ts index 01de88c0b..2ff2c5d5f 100644 --- a/sdk/package/lib/util/fileHelper.ts +++ b/sdk/package/lib/util/fileHelper.ts @@ -187,10 +187,10 @@ export class FileHelper { /** * Reads the file from disk and converts it to structured data. */ - private async readOnce(): Promise { + private async readOnce(map: (value: A) => B): Promise { const data = await this.readFile() if (!data) return null - return this.validate(data) + return map(this.validate(data)) } private async readConst( @@ -224,8 +224,7 @@ export class FileHelper { persistent: false, signal: ctrl.signal, }) - const newResFull = await this.readOnce() - const newRes = newResFull ? map(newResFull) : null + const newRes = await this.readOnce(map) const listen = Promise.resolve() .then(async () => { for await (const _ of watch) { @@ -284,7 +283,7 @@ export class FileHelper { map = map ?? ((a: A) => a) eq = eq ?? ((left: any, right: any) => !partialDiff(left, right)) return { - once: () => this.readOnce(), + once: () => this.readOnce(map), const: (effects: T.Effects) => this.readConst(effects, map, eq), watch: (effects: T.Effects) => this.readWatch(effects, map, eq), onChange: ( diff --git a/sdk/package/package-lock.json b/sdk/package/package-lock.json index a25775d69..cada2299d 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.23", + "version": "0.4.0-beta.24", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@start9labs/start-sdk", - "version": "0.4.0-beta.23", + "version": "0.4.0-beta.24", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", diff --git a/sdk/package/package.json b/sdk/package/package.json index 5188305f7..36bfc09cb 100644 --- a/sdk/package/package.json +++ b/sdk/package/package.json @@ -1,6 +1,6 @@ { "name": "@start9labs/start-sdk", - "version": "0.4.0-beta.23", + "version": "0.4.0-beta.24", "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/patchdb-ui-seed.json b/web/patchdb-ui-seed.json index 5ecca58d5..2473812b4 100644 --- a/web/patchdb-ui-seed.json +++ b/web/patchdb-ui-seed.json @@ -4,7 +4,7 @@ "https://registry.start9.com/": "Start9 Registry", "https://community-registry.start9.com/": "Community Registry" }, - "startosRegisrty": "https://registry.start9.com/", + "startosRegistry": "https://registry.start9.com/", "snakeHighScore": 0, "ackInstructions": {} } diff --git a/web/projects/install-wizard/src/app/app.component.scss b/web/projects/install-wizard/src/app/app.component.scss index b907f84ba..d3f166d6b 100644 --- a/web/projects/install-wizard/src/app/app.component.scss +++ b/web/projects/install-wizard/src/app/app.component.scss @@ -61,3 +61,8 @@ main { margin-left: -100%; } } + +[tuiCell]:not(:last-of-type) { + box-shadow: 0 calc(0.5rem + 1px) 0 -0.5rem var(--tui-border-normal); +} + diff --git a/web/projects/install-wizard/src/app/app.component.ts b/web/projects/install-wizard/src/app/app.component.ts index 0a5f4e98d..75cc8fde3 100644 --- a/web/projects/install-wizard/src/app/app.component.ts +++ b/web/projects/install-wizard/src/app/app.component.ts @@ -62,7 +62,12 @@ export class AppComponent { this.dialogs .open( 'Please wait for StartOS to restart, then refresh this page', - { label: 'Rebooting', size: 's' }, + { + label: 'Rebooting', + size: 's', + closeable: false, + dismissible: false, + }, ) .subscribe() } catch (e: any) { diff --git a/web/projects/marketplace/src/pages/show/about/about.component.html b/web/projects/marketplace/src/pages/show/about/about.component.html index fd54747c1..fb90300d4 100644 --- a/web/projects/marketplace/src/pages/show/about/about.component.html +++ b/web/projects/marketplace/src/pages/show/about/about.component.html @@ -12,7 +12,7 @@ Past Release Notes

About

-

{{ pkg.description.long }}

+

- (required) - (optional) + (optional) + (required)

diff --git a/web/projects/setup-wizard/src/app/app.component.ts b/web/projects/setup-wizard/src/app/app.component.ts index cecb85eec..cd2ca9e6a 100644 --- a/web/projects/setup-wizard/src/app/app.component.ts +++ b/web/projects/setup-wizard/src/app/app.component.ts @@ -2,6 +2,8 @@ import { Component, inject } from '@angular/core' import { Router } from '@angular/router' import { ErrorService } from '@start9labs/shared' import { ApiService } from 'src/app/services/api.service' +import { StateService } from './services/state.service' +import { DOCUMENT } from '@angular/common' @Component({ selector: 'app-root', @@ -11,9 +13,15 @@ export class AppComponent { private readonly api = inject(ApiService) private readonly errorService = inject(ErrorService) private readonly router = inject(Router) + private readonly stateService = inject(StateService) + private readonly document = inject(DOCUMENT) async ngOnInit() { try { + this.stateService.kiosk = ['localhost', '127.0.0.1'].includes( + this.document.location.hostname, + ) + const inProgress = await this.api.getStatus() let route = 'home' diff --git a/web/projects/setup-wizard/src/app/app.module.ts b/web/projects/setup-wizard/src/app/app.module.ts index cbfe318e6..0ed693006 100644 --- a/web/projects/setup-wizard/src/app/app.module.ts +++ b/web/projects/setup-wizard/src/app/app.module.ts @@ -5,6 +5,7 @@ import { PreloadAllModules, RouterModule } from '@angular/router' import { provideSetupLogsService, RELATIVE_URL, + VERSION, WorkspaceConfig, } from '@start9labs/shared' import { tuiButtonOptionsProvider, TuiRoot } from '@taiga-ui/core' @@ -20,6 +21,8 @@ const { ui: { api }, } = require('../../../../config.json') as WorkspaceConfig +const version = require('../../../../package.json').version + @NgModule({ declarations: [AppComponent], imports: [ @@ -43,6 +46,10 @@ const { provide: RELATIVE_URL, useValue: `/${api.url}/${api.version}`, }, + { + provide: VERSION, + useValue: version, + }, ], bootstrap: [AppComponent], }) diff --git a/web/projects/setup-wizard/src/app/components/matrix.component.ts b/web/projects/setup-wizard/src/app/components/matrix.component.ts index 7a58e11f0..02cade784 100644 --- a/web/projects/setup-wizard/src/app/components/matrix.component.ts +++ b/web/projects/setup-wizard/src/app/components/matrix.component.ts @@ -8,7 +8,7 @@ const FADE_FACTOR = 0.07 standalone: true, selector: 'canvas[matrix]', template: 'Your browser does not support the canvas element.', - styles: ':host { position: fixed; }', + styles: ':host { position: fixed; top: 0 }', }) export class MatrixComponent implements OnInit { private readonly ngZone = inject(NgZone) diff --git a/web/projects/setup-wizard/src/app/components/password.component.ts b/web/projects/setup-wizard/src/app/components/password.component.ts index 49be43337..2b6e3b2a0 100644 --- a/web/projects/setup-wizard/src/app/components/password.component.ts +++ b/web/projects/setup-wizard/src/app/components/password.component.ts @@ -1,9 +1,27 @@ +import { AsyncPipe } from '@angular/common' import { Component, inject } from '@angular/core' -import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms' +import { + AbstractControl, + FormControl, + FormGroup, + ReactiveFormsModule, + Validators, +} from '@angular/forms' import * as argon2 from '@start9labs/argon2' import { ErrorService } from '@start9labs/shared' -import { TuiButton, TuiDialogContext, TuiError } from '@taiga-ui/core' -import { TuiInputPasswordModule } from '@taiga-ui/legacy' +import { TuiAutoFocus, TuiMapperPipe, TuiValidator } from '@taiga-ui/cdk' +import { + TuiButton, + TuiDialogContext, + TuiError, + TuiIcon, + TuiTextfield, +} from '@taiga-ui/core' +import { + TuiFieldErrorPipe, + TuiPassword, + tuiValidationErrorsProvider, +} from '@taiga-ui/kit' import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus' interface DialogData { @@ -21,18 +39,38 @@ interface DialogData { Enter the password that was used to encrypt this drive. } -
- - Enter Password - - - + + + + + + + @if (storageDrive) { - - Retype Password - - - + + + + + + }
-
`, - styles: ['footer { display: flex; gap: 1rem; margin-top: 1rem }'], + styles: ` + footer { + display: flex; + gap: 1rem; + margin-top: 1rem; + justify-content: flex-end; + } + `, imports: [ - FormsModule, + AsyncPipe, ReactiveFormsModule, TuiButton, - TuiInputPasswordModule, TuiError, + TuiAutoFocus, + TuiFieldErrorPipe, + TuiTextfield, + TuiPassword, + TuiValidator, + TuiIcon, + TuiMapperPipe, + ], + providers: [ + tuiValidationErrorsProvider({ + required: 'Required', + minlength: 'Must be 12 characters or greater', + }), ], }) export class PasswordComponent { @@ -67,31 +121,29 @@ export class PasswordComponent { injectContext>() readonly storageDrive = this.context.data.storageDrive - readonly password = new FormControl('', { nonNullable: true }) - readonly confirm = new FormControl('', { nonNullable: true }) + readonly form = new FormGroup({ + password: new FormControl('', [ + Validators.required, + Validators.minLength(12), + ]), + confirm: new FormControl('', this.storageDrive ? Validators.required : []), + }) - get passwordError(): string | null { - return this.password.touched && this.password.value.length < 12 - ? 'Must be 12 characters or greater' - : null - } - - get confirmError(): string | null { - return this.confirm.touched && this.password.value !== this.confirm.value - ? 'Passwords do not match' - : null - } + readonly validator = (value: any) => (control: AbstractControl) => + value === control.value ? null : { match: 'Passwords do not match' } submit() { + const password = this.form.controls.password.value || '' + if (this.storageDrive) { - this.context.completeWith(this.password.value) + this.context.completeWith(password) return } try { - argon2.verify(this.context.data.passwordHash || '', this.password.value) - this.context.completeWith(this.password.value) + argon2.verify(this.context.data.passwordHash || '', password) + this.context.completeWith(password) } catch (e) { this.errorService.handleError('Incorrect password provided') } diff --git a/web/projects/setup-wizard/src/app/pages/attach.page.ts b/web/projects/setup-wizard/src/app/pages/attach.page.ts index 47cdd3769..a77c1b147 100644 --- a/web/projects/setup-wizard/src/app/pages/attach.page.ts +++ b/web/projects/setup-wizard/src/app/pages/attach.page.ts @@ -17,7 +17,7 @@ import { StateService } from 'src/app/services/state.service' @Component({ standalone: true, template: ` -
+
Use existing drive
Select the physical drive containing your StartOS data
@@ -31,9 +31,11 @@ import { StateService } from 'src/app/services/state.service' valid StartOS data drive (not a backup) and is firmly connected, then refresh the page. } - +
+ +
}
`, diff --git a/web/projects/setup-wizard/src/app/pages/home.page.ts b/web/projects/setup-wizard/src/app/pages/home.page.ts index a04a1cb86..b995a64f1 100644 --- a/web/projects/setup-wizard/src/app/pages/home.page.ts +++ b/web/projects/setup-wizard/src/app/pages/home.page.ts @@ -13,7 +13,7 @@ import { StateService } from 'src/app/services/state.service' template: ` @if (!loading) { -
+
@if (recover) { @@ -49,10 +49,11 @@ import { StateService } from 'src/app/services/state.service' (password)="select($event, server)" > } - - +
+ +
}
`, diff --git a/web/projects/setup-wizard/src/app/pages/storage.page.ts b/web/projects/setup-wizard/src/app/pages/storage.page.ts index 2e45aea9a..40a6b4f51 100644 --- a/web/projects/setup-wizard/src/app/pages/storage.page.ts +++ b/web/projects/setup-wizard/src/app/pages/storage.page.ts @@ -19,7 +19,7 @@ import { StateService } from 'src/app/services/state.service' @Component({ standalone: true, template: ` -
+
@if (loading || drives.length) {
Select storage drive
This is the drive where your StartOS data will be stored. @@ -39,10 +39,11 @@ import { StateService } from 'src/app/services/state.service' } } - - +
+ +
`, imports: [TuiCardLarge, TuiLoader, TuiCell, TuiButton, DriveComponent], diff --git a/web/projects/setup-wizard/src/app/pages/success.page.ts b/web/projects/setup-wizard/src/app/pages/success.page.ts index 7d0c25343..7973980c4 100644 --- a/web/projects/setup-wizard/src/app/pages/success.page.ts +++ b/web/projects/setup-wizard/src/app/pages/success.page.ts @@ -18,15 +18,13 @@ import { StateService } from 'src/app/services/state.service' standalone: true, template: ` - @if (isKiosk) { + @if (stateService.kiosk) {

Setup Complete!

- +
} @else if (lanAddress) {
@@ -111,16 +109,12 @@ import { StateService } from 'src/app/services/state.service' export default class SuccessPage implements AfterViewInit { @ViewChild(DocumentationComponent, { read: ElementRef }) private readonly documentation?: ElementRef - private readonly document = inject(DOCUMENT) private readonly errorService = inject(ErrorService) private readonly api = inject(ApiService) private readonly downloadHtml = inject(DownloadHTMLService) readonly stateService = inject(StateService) - readonly isKiosk = ['localhost', '127.0.0.1'].includes( - this.document.location.hostname, - ) torAddresses?: string[] lanAddress?: string @@ -157,7 +151,7 @@ export default class SuccessPage implements AfterViewInit { private async complete() { try { const ret = await this.api.complete() - if (!this.isKiosk) { + if (!this.stateService.kiosk) { this.torAddresses = ret.torAddresses.map(a => a.replace(/^https:/, 'http:'), ) diff --git a/web/projects/setup-wizard/src/app/pages/transfer.page.ts b/web/projects/setup-wizard/src/app/pages/transfer.page.ts index 6f1f206f9..21764d6f1 100644 --- a/web/projects/setup-wizard/src/app/pages/transfer.page.ts +++ b/web/projects/setup-wizard/src/app/pages/transfer.page.ts @@ -21,7 +21,7 @@ import { StateService } from 'src/app/services/state.service' @Component({ standalone: true, template: ` -
+
Transfer
Select the physical drive containing your StartOS data @if (loading) { @@ -30,9 +30,11 @@ import { StateService } from 'src/app/services/state.service' @for (drive of drives; track drive) { } - +
+ +
`, imports: [TuiCardLarge, TuiCell, TuiButton, TuiLoader, DriveComponent], diff --git a/web/projects/setup-wizard/src/app/services/state.service.ts b/web/projects/setup-wizard/src/app/services/state.service.ts index 8a7084f55..fc8e3e66e 100644 --- a/web/projects/setup-wizard/src/app/services/state.service.ts +++ b/web/projects/setup-wizard/src/app/services/state.service.ts @@ -8,6 +8,7 @@ import { T } from '@start9labs/start-sdk' export class StateService { private readonly api = inject(ApiService) + kiosk?: boolean setupType?: 'fresh' | 'restore' | 'attach' | 'transfer' recoverySource?: T.RecoverySource @@ -15,6 +16,7 @@ export class StateService { await this.api.attach({ guid, startOsPassword: await this.api.encrypt(password), + kiosk: this.kiosk, }) } @@ -33,6 +35,7 @@ export class StateService { password: await this.api.encrypt(this.recoverySource.password), } : null, + kiosk: this.kiosk, }) } } diff --git a/web/projects/setup-wizard/src/styles.scss b/web/projects/setup-wizard/src/styles.scss index 632283006..818b4ea92 100644 --- a/web/projects/setup-wizard/src/styles.scss +++ b/web/projects/setup-wizard/src/styles.scss @@ -24,7 +24,7 @@ router-outlet + * { [tuiCardLarge] { width: 100%; - background: var(--tui-background-base-alt); + background: var(--tui-background-elevation-2); margin: auto; } } @@ -67,3 +67,11 @@ h2 { .g-info { color: var(--tui-status-info); } + +[tuiCardLarge] footer button { + width: 100%; +} + +[tuiCell]:not(:last-of-type) { + box-shadow: 0 calc(0.5rem + 1px) 0 -0.5rem var(--tui-border-normal); +} diff --git a/web/projects/shared/assets/img/background_marketplace.jpg b/web/projects/shared/assets/img/background_marketplace.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b4ba1aeb1d4f5405064138af66aa0ed0f4e18d97 GIT binary patch literal 14539 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%0A3+8SbX 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

dZmCBcC!LIYv7(f;ChcJICX#~f zHX-gPbE3EhO`i=WNK1`TrcblGi>9;|g5`{NdOqfyV5FsRPpCZr1|``%GFUo*Ngw!U zoj8%1HO%jNM}Tu8s4!}&;*8g$ypFimY^cpsGrfnj1mVgyerqqA7AW(1OU0QkzNmnA znzx+6tG*bP^1*F*1+G=QYx%gO)dkxA{o}4T>pf#rJXh*1h`L>0P~o>iuEh8a{kr;* zCd!_Xo4m8^!$TIrln*S849v#Xb1#}3Zfl8t0NQ?&XNXDyWeo+R_c<=@-WFg_YakvJ zDRR}-`JK}5wp0Snu1Kj%m3l9Le}0k!P|1K-l($+?@r5gEt@W=*MB{IMVfE=d=(y^rFE7aek)!zg$J$T#zbwK2yvpYDKQh6UJh+|I z5qF{5ZcO!qf3KU#=@!B5tayl+H9_6mp*VL3r_MU{R-~N{c9q#9Fy3o#cY%}d(XSX= zl&RP3e9UL{STHG{!%AB7(tp97Yze&dqpk^HuhKgZe{og#Rp7cm521th#)YPRdvGIq6aVUAwGG;76X8by%4squPmYu0tf$uC~sPuf#OMiXt zo0M~xi_l{eL+>8;{=iZI;=(e1X_0tIbuR=I9Smp-o7E3C45$w*xObR-g)fgxL%)UI z3qLp%@5=RdWsyP8!UMvwK(7{(`N5h+_zOd`sVtJq*OU-5x%>v=S8RCmGxL5xoxWN9 zuy_4HHcVfU3j>|?RW#M&h9Ju=GL}=t;SF9>-aeUFw#^p* z!+sK;x; zXD^IZuhP?3z^r%v+cUI@Nkgz7cw}gADW&ARk>zw~e&*K2pb14&#rdumXhdsW2tgBN z4=IMn@)>D?+%kjLLg7LTy72+0{t>Bkqb4{jTyZf%n-ES8p=l=**q06D02AN!4M~*o zU!T^o1%=X=ry0f}!@WKDo=13DU^tTip<@|M`tT=0Rl#FH&S2VcRQt;=p?K%a=u?2+ znz>R8d>NBnI38_$1k^@*F~(C(%pmd7+8sU_x5oJKG>Z+=yXxa>C`Ge+Q{1)F(w`z| z5cc;+l8cU8g|n zzGUQ<4O5fR{BR}g1HuW!CaFrSSr}lQ`gui+7O^Wj~2>HV`Y=iYLL? ziFfa^!;~xwlZ`Ub?uP(B&a_=1QvXM^Qlrhv%dEASh=-SRP5(Z3QtVQ(pB^D+;*Ml3 z`yaQ=TI9FxHeLNDb3dC5gSg7YUO8rbigqJMT2!=5ty@jH#3+vXb!zCv+;Zn)r@9K~ zG4qpNxxK2PUCX%*e@1^|&29Iijpc>ghadP1T~(LiAm4EbIAuBuc8cOPqGpW4JpKC2 z9^{6)Y0926wnwS>)!WHk@5&%A?(` ze|Yd4l-f6fZ-$IkULo47RPY!FFYQDCh-ke;Y=~@A5w*loB%sh+#831nGT>X~CMF$S zAq!-2w=35CClHjgx>IHGs(yb9lpg9T&iy+j)*I8hxpkmhn*F?Hf6x(l-?i(H(tmJos;rWu>!W8}cb;FB(5AT9>pTUM&3!q4f7F7o3y9qPn;=bsG!tJh3$u5g!=PuM~ z;t463WY|7cAG{i^i!kXzW7$CmB*7W|f^$%rbwG%Qoq2={-HbD#{q`%u&U}KqRn(7W z8z$C2ReTT1n%<7C%MO3*pP#3$3D4={K|5>CU+8HmZK!9k6Z$XcYDzXz_ekK8&HDKd zw}BtruObUa5$Oh^Gx>)GOm=)sjM~wgjSXHQ{r>mjeNd`EXx7R?1;$elX2VOKh_d)z z#s9-VExvLytR%oAzuCXDOckO=O;>)Gyr-Nj8DSO=YEl48gNI&)Cx%PY$W0Z6;!R=j zR0_D~JWeHb`PA6%7zVUJ7R0rbZHgPLAnQP6VC5?-1P#|@v7WQ7dfC-KYDrKphmu>~ zV5(fYDF}TsesHz{c)@+>bL*dv`jOcozW7lEUypjm$+YAA73AGh1FBHp%91^*9pa6! zU(DRGhL`8&d}FVA|Nid;@I$1Ev$XgONbX+4J7DB~#)2ua|BS=y#3sCm@LsvzR%C9U zo*fYIut=~g2;WAxna=WPrBT(y2L01~4=uQiGZFHW1z&W#O9&GCMBm(mjiBJvru~1N z<0o3KbzWX5xWebPk(^MQk?3^&VbQCWS-@oxQ*+4Rk8>zR*CfPY>I7-rVJ$bq8iUuE4-b+{F^pLKe+X|(q!T-~#1$b}&|le$pr&9NxccSlV0 zML~(<@g(+vl>*|SOl)FW4RHj9c-84G;&_49-%t4Wps`<-a5UoiL7Y&}yGxMf3zJz5 zk@jdZ;<6K#hLRNhV&PH-OKfnf(n;+fn=%pys#s$1kf4kRJ>XURHkVL~7ZAhHDpgx7 zWnA7{VJxTaaEb#C~vQnK0m8la*8oCbg;)Z=<3P)JDrW&6q?* zmyl3q^isf1z8W3kR#*9^TOT&Cs4n9X#;E8Tp1`EP58;=M_yseTEU{J)f2sxv@{!x9 z3NU2PVW;+tSTw;sPH7(_TiH+QWZmOyDMX>)YLli+O?bqmI2-dFP$d327~v9Oh3$(N zbjn@h{o0#_A!~uqt<}Bhm81ecrDg=)#$}j-%4x3B?`@$2yk zC(U9x2`0N3+h}&i+#sT2<;Q4-Kr52NHt@R)Gs`8Uv zlkU8zv&*ZQVXxEeYj?|SM2|67J;d(H3?DzDWxIUpzW*^RG0T`xCv{o85t7>VPuT5O zcC(MnR1jeIk9pOB_Q3Ne8aF?EWdCe@8zZd{@w;7}s?2G<1liHc$guxQaSu-kv#y6` zU%6wEdFzo=F2GRZ3dxl#0rDkK;)86Vsn@w}Iyt4{IK#2lyK55kR~R4!duBNGysewM zkbBa3WS`cO$(Uc(4)AfuIw8rAk1#fyL}A)W2C4&4^>I$O+!)>ayHmsWYNH?NlvP!| z94kPkM1P)VBsF-EnI1>6sQDuGJLgGw)&e9z@kOJfc3%I^`pvzNU6RB&jWJ=LI5u0@pSyIh(7XR9`uep~ z@*^*;L)O>mDF3CVX~ps~;8lG%pev{8CWqt6;USvh!o7E0RgOEm3FQrbp2JQpD=W-} zXmUV-yu|=)&YWO7VOTy&?}N?c@w%sE;kyf&+BmF|02~4ThHB}gr7Wa!EllQ#_L9M+ zKgRAmTD#k-NmAD==s{Oj+Ub|ex!tc*EfbCqYtEw}}l$2qgWG#?YSxIo3sKE6Cr7`O$D@5JWz7)!da3 z8m+oRYVJ}@3Ky0d&_7zl4Tgw@;D2K@-K2>xTe%Q%p{U~>6Ypnu~|8x zOtZpHqV1AwM5zXSQm%-1dzXgXHuV^3HeFhDL7-;fM&|BMHJ-EanwHXenCid6M;?fe z@n>q563vb-S?^@R+_Neq>`mS}8F=+Sy&$MxkN8U~yqq9;ne%}p!seLWrN4(FqNuPbP)6Dx@FR-$&VXouQcid~%D$=DF2BKz+U0lmJV>Ikxo77cc4lLpH|3`7HqWbyJy^O3K_Ne4^kjmTfU&_Xg(>wDA zB9_@<{p>M#I<4QD9g^zNhu$XR72=55Wp^+%`cq zPqI`_)5#@a>bk_T&Q~W^PSv7A5x5xl68^Ol?Gzt;N!PRmO{E_No}zmu6eD4Sy|WbY zfioJxyL9kx7A9awnv_Jnx=FLq#Gq3E^p^(+I6%I(n&{mkg@hg^R+OfYw_w~MIO+b8 zYyN|I;+mkZ%)nYl2iWbrwU?D@m4O))UR?HWf&A>{w_ae^N_FV@B0X<^`v(7czmm7F z1`@9+Iy!u~YIDZUS?^P;<5jl4yA0%b#qhS`-#?n2w@ci06hGb_F8vR22KPjAM*TK6 z0syc)>koW9dISx<*H`>9#@_-|RXFmV@cZTZnf%Q-E;zS7cc34!$i z%ZqEWDeT^tSD#ye;Ep+O0dK{J!{jIo4yW4w(o14;j~L4k-=}2qjUxgKg#_@wv*&*J zgfJ0whx5632iKm#SmC}xzo}E>^FBpymz2O=N}M7d>Q#x(gLap_o1_qh zHn&KlTN%BUP%k~h&=n2CAVs{I;qxdzxeP}zeC&61bfK7!t&jF=t%Ex~37|;90#=q& z7;`VGe;ub-+wh29kzE|^M?}x%60E5`#Dw`$K!l9)<_(jC=A+?d?}1CcgKinD5YW_) z5wnuXUm1Gd0=H}0d9vsSzw}e_WC3#EaeRahY45m2U=ML?IhDI=sran4ht*ak;q84qwL{0|G@+@30DIfjbG zi?S){ezI#g2dmcrcI?xfrRb{Va(zA|mY$yamI@tOS8;9{xTl52^a&FQ4M3oJ(D>NW zI8+zrJA);9#yTT5W!WPu#Son}QXlXbCWlF)82i>zc4-sQ=+`m_FJMtEzgS~#f$Ib1 z5MQmIZD?8oM@{^^YW=N7zQ!&C0UBxZdQqYaGdYjI?#!4yB#cY^H@D zjeWx@{lv+8LP+ef+!pPd2XF4@#718|Mke7lqP9L8+X;;h;(X#Kzu~OheC|U5#%2zn zo8aB2a=CTS*}}N#dEK3k+*1J?8T)p}UdrU#LJ@HZ;?{q;R&k84)0OPp=FF|D>|O3; z@8d5@C9OLy77cr+^>@}VKD8KFX4!Qc94OE(7qeAZFkW5yw3UgIq+Q}6`Y-Civ%ep&sZ)o+g_*7N4*;)@m z`m?dX;Nl}nl`MeQS~?#b27bw^eNiVq5AE-ZItc2kU>XUas(o zHv&owHUmNQg?H7wVPXllH}8&DA#U|82!AY3Mwe@Je}-1OWe-o5seLVf`Dc3C$t}9; z>Vos~M-*I02!^yqHszud)6b=@uKg3=a*ZDIv+g({YNg8O(6qN#(sf(rk9_kIlm8yk zlf0^%A<_S(oYK%8h^R!g%4L7$TlO(wA2q*gyzXf~3+kJC?6S~nzQ^;kUF+LJ_D#^T zYW$^i;9_3lt5=6D7BQ#Hd7cDqz0)~n{p`G41JRy{9Cn7^{$d=vn|3v@SIijLxK6N^;x(#z_LWzfu zfmqhc8yO1|Bd*GEuwV$TZhneY=CK>U|MZfCVqzcipSfvmWV_LfAdqdsnZI=08H0W9 zd(?#ZNo_Z;+>MLo^RYcx!8uh8-qV#x0sv3-hWoa*?yW|v>RG24s@<0nm^wjqG%Q}U ztov94cWqQpCtOwxx?=%j-eURqmgfrm*OTIGpq;lT$$}NF9%k$(8ZfbstwJ;){)*Jy zt-wxptL>zPlXbxAk^ipE({CFDEAh)5HqF>i{ae_3O1+jT0y9t(eDr^4aQWXcIb&4r z#}F;f=9NzJ_TR!bYy3|via^xh3X z2U?^aBIJ8xfG3vD{`PJJU5gNFYwXP=e^heMj;NepqOOL=dZE!|bsWr#DUrx9rN=F^ z`E7djbD9--kH&09i1-C?#8?kav+ItL4Ofxr`BFhPNgMN>5%dTTWD6v+j@#MNROEjf zA0B#XEL)7`&wAFqK3CvvpMAPVnpo!x{08o8c1T>t-)6aF!bAq)^PpND*ScWcy4G8&9Mz|OdY7js0^6A35XfK?-M_rDM;zmBn{JIfBU#Z7OO7)*%bA3&Kz z$>qbJ^M3exhwSe3A0-z=lYUFjN|)~3On}R`Ul-O+>?+I)8JUL;V?q~TEXccPu=g$V zT$%s(E$YX|wsy8JBf&p%OHW12S%k%IpZ^GnW z4?AehWrLh=*#-Z3fBfCyb^(W8-(%EA{@YkpBYm+^{d~W@HU~^pQG5-Yv zX7y%c=86;$1dD4WdYa}e{d@*Wo_l{PCQ%?Bm~aXonn}=#ck9Z%qXay|dcq^Kj13Kv z(Lr`p%hd-yn5xoi++xUXr+m4#q~CW=-PMPTZJE2664$=aUNHc8sbPD$@ChyU<3Guk z3DTKEbSxY>#X`hekMuw8{!aFGfxG%Q$Z!bAhV{WRC8A!J0TyWgLbIdmS)Aj@(`jcD{R0(+X9CQ`qts3AFh=trkq(+?H zI;tz5;DzZBjVV%1PqiI?v&Zx`AIeRVc2FQ)WV^Fs^FbK_H+jr>BOP+T-(ZdVX zAyYmZ@}Dm9en^K91`V_rV7m%Q?-a0{T#qP~|Jz4bm-ACN?78 zi@;5oF3biz>s~ay14kI7nQjgfw&3r#5M=<+cIjm$eO!ocRe+9+^uMoxOA*ESU(}Wh zzeo`ms>?3Z!}Z(^PU|t1W(}2M61QPd`C1HY==8%&0`Zsdx)MCGddH*s#l_B1Z_VlO z=@Qnu`bQR6eoFqJ@RY3pvaV`sMABcp@C&KfV04b@9J|KoON8~HN7^J!A>^rzVOBM^0{x=*SiummL_VdCySWDDOXb;S|FDuCJ0QvT7FMMuBn4p zZ=C0H$tbn!6X5Hj3YA`1MmTJtjr4Z^{QTJu9p{Z^FZ9|QKAC^xPZr0O>uAu+!`JYl zYU*4tWVt}xYtbae?ai0)W_&x+ks;Z_e~Izk?VbEdHQP%gsQpA=DCsQvknWUT3D5R9 z;knNIK(G0CzYtRrm(k)dr?N#LOg2qV#gmnw%*M9?L(hr7*=7)WNly&f@3nZC!iR4q z?3%bKGr#Zk*X@=qYK4wSOGXv19!>d+(i&d(DZlFLpE4$u&?wx|wIT3Y^drs}q(8;jsZc%Lu_W z$)$EWyel*Jn)X4UPI@^GJp(FE*nA#L=_bW#J<0pR1N$qs_Ivt*_VTuOT)Zvos~G8P zle|#-%AcLa$$T})$b6{42QrxZ4H-*SUz;eu+SML+n>V)@Ou}K#Iv3=p5`i&NA)f+m zsTn!s*g(A0HQTS>UiI>mez*93Isjcqj|kHP95Z!6xL4Pr+;stTFrWEZ$)y?yk+B=)odOhPPq)AM%T&$rFz97A9sYf7D`dZoj15Iq@W|Ed5TMOW;-3Lo8Z}|qd z$0MK#lO)S{lrRo=0pku|7TD^2Eiv%Dq2639z%-W5u2H?y3!7%+_vQah0Jg8LM8CFY zMWh_*=#iAw%WN2oBTlt~8o!D+Z#9)}l6-r$YlWwW0;;s+iW6ZI1NTx7D0yVR z@M=l_T%e(jcnR94EZi#=j#Ds6@cNry*e0&atp#`!5C*wXzqkXU4dStyL3^19^D*2#eIck*(F z4>9~1MYp8Rz_2=Rj`wgssR}JCAZYLAM%Jz^PSR=}+b7<+&DGj0NSN2EXxtD#E0^@> zwbsC4h(WjZhp1WWw?6jBUD2yh9nS8xzb?bAZ-@AkOnsGZ?qzNz*|Do$dquQdSt^Le z)nj5?NEt}M@hona+0=LQXjf1FEG3Wy%KnT1riuaBX$y1Ftpealq1S&uFG?8*C}oW& zd7HdBU8fQqd0Fi)%0(3LY;disZn4ND@~OBH3&c5H8nxf-4&*Hd;a;qkm2OFjJn;-4 zl*$4_BlF1U67p8?LrtC2nv#ng8Tm#x80oa>T&0!C&;~5E@oKvxFVVqx-a@Neq;#_3 zT*uppP_-u?d>s~UZbfa{e*O@+-J(8qFT^+_I`?0>-7UStJFk|<)I`EN%lnY8c+8Wz zLUJIW-TwHfbt&XHRfpBQyx(_a)(X9T8d|n&g=zF%)XOUi_~dn~0E@dGqYeHVW7q6d zgb9iXdR~T&_K6Lks87Lm!C=hm^*gGW4IzL z_fS|g*gus}^!mR6RLJwDK9cffig)U=qQXJ2$@7p3j~Uo^%wzV4iQ8=2K9NUdO%MsY zusq(t5H>@fF6XqFt7}_60_~UxvlWNe!#*1A`R@BCf5hbkGdJx;q4GEf1m0!bB|a*t zD9Rn%K}J%`y3!~}wbb^&m(4zWZJ`tx-5T?&&7;Wjt;ZlmbU}at`KLLzp35*BP}z=& zDL1z8EJ20ssiZ({vYVFV5$pk%0=Cq20O6|~W(3b(9^s-4^8tia0>7MEa*p7$Ep`fF z5qbS027`CccxI-A@hp(qjLNT+>sw1Bn=oVls2tAkRpy{5(gV7uf%kh z-Nak>@k5?CLIv@k^MbWv8AkHJ_B?Wdow2;2k4xH!Gp{mQQ{7+_nE)hcfFFxo>jMI| zd~GnsOPzxG5g)d4myUnsCvi5F?ya<5v*RNDV~?G3xwcRjBCSiKum7~Laf=K&F-Or{ z{ugN?**;5Mv<+|kD zxYld`oXh_W=;m!MM(x7u+1=eJ#bEATvL(j-52aMWn2ByF%fTxOkhJMBl0(NIMdn}B z=Z9b0+Ku3-v^8=IzEvfR+8|5&G{xJ5-m2YrYd*j=HiiZ zyB6M48_TLV(#DbwsuiL z?`!-oDGrbwr(!Ge_t5qZvwF6Uc!G35pzR+g9@Zr%7M3n&;uhYY z+GURRf^8B><8qM;Hz23Ohb)>}ZvSnvKSX(HWDCgS=WD@(^BfFfZcgy2!M);RmjhN$ zqIC0EO%m%U#hd68pG+QvJ-V2rMEe8dC_#~h{OdVanHnu;gyf>eb);qv z_~1m;A$~~dI&PeD|BtU?WOrS#?*c9S2i58LTlYvZVYz^%!bf1F z)-xo=Gg)8@Kt~r9#R$Qo2Az%(oeT@*&F$hpsCoC{DJLOWMf*)6uaenDa? zor=cf^ao+0J<~Zytu9HNBwhXi&02gpn~Y%0jrweCpMMKwh=EO!vFNewYZz*M zvoWFuOs0$?Rfuk^e#H@O)_fRMZS?&SJ;5CMqy?z@${M=v(?<$@zoDO&Y5VfVV7p+U z1%(Xo%no~DI2z#8vbEqa$u=P!J{*wa_KnGJ{wVa5__}A|I~`rJVjz=(xkCMSx(}Di zF{B^3FS<_R2q%`vb=B9nSgi)d_m2o%1+t}BjTSzz{KVxG=I;B{(p22xtsS052vkMT zkq1p$9;hCk` zu66I?bsI{-er~3J^nLPmMgrnvQty3U3RzxzMRU0_)LS^YlUAFK;6s9uv@r|`VXkj| zSj^#-&`B<#8l9$4$XHbuq{e}?5anZM^~<1KLm@qt9Rj(l6IN zon4JybZiEiEM@{#FK|f$Q^gO~tID6+%-QU+rP!tan#`ukGt>AsWYXZ?7L5-0$8w!g z(Z7T#Xm7VES5eJzty{QtE%1qp+ra-Wz;wUUbdz ziu=^+)S#w9O(-kzh3RO(nJWvKf;;d}>%f*}`EKiP6#h$1@<{R{-0H^u$LR4Flj`2P zAzH~~xsADS(v4CPH3z=gGET#!? zMKgRVL2evyfuG~ObCLU6E~uYpiAN+w*z^`#1U+QoM@5TlrCUQ1;G!hvpQYD7-&0K; zOdN+m4Eh;0an?1(94dHRu|d`1(h4Z|mPSD3`AIxmL`?p4DPg^d*9^CAzKHbuA2{={IM^dwSKC4 zzENwY4$xjp*EnZ85OgB9{0O2&#jLYa0%z}z!cJLhOWT)wT{z3zUowAeZap1sb&QP4BuEDM?0#wcA*oLPJT*?MDf=z2*Mi9P+`xqmLi``C!G*H60( z*UoTI<$)J|7T!7?tpBxL&FFel@=VZBV)9=^ME{{&tHbf6T}gN`)iScvTwdH)j}oUDQ5lZ2Ri2vgt^Ma20qZ=@E-fvb=X%(B?K2d?j?bHLL2IHa z4B9m7jtB`h4lY|yZE`e9RosL*ik0UrlZ9Q*xovbiDU@O@a$9~??GAR#>y4edS&NIt z2O-V12abq}vH^qYdYTThBNPj)*gwb)`%u0)V^aHwV*A4inj+#a5eFk(q@-ssYD3fX zJ7s!N4aFZ$3D-_*WUb53Y;$BF|#;vTGl^cWBa{nch{}0&PMC zZRW_}j(DHh2N72Y><7_@+_Mqk{BE<0_v8HpQ~K%Ud(VxGZ6=%J>RmN_4->f5uW>!g zeV$Z`VPpUDzu|CLrh+2I+@EHxEzeI*D~M67K_rU~v|C?QgsE3=Uiq(b9L}C+V$>N* z?HWBF?GE_`c&6ONnaL3Va!qX0EiRqdOl0RbQ<~$5u^$V1ae8i@T;>8E`9`MVTd3!J zv`Ov&%U8|0Lk1dxNqR@oh0@)qfudB^9y>bM(XCgnF*eU<3Ft83^5jL;s%PR`KLWJd zCPE`YHlt`IEUF?Y(F9)eso^r;?#67MRwgt|8>>pC13agokfAN;r&kF2+S{?ib9KB| z6^}`Oc2)3oe3{c>|8NrZE?)#<_Jkuc;R$&4FKw*BVazf#9-+ z=~Ri8I$}qLoDKd8u8oxrFBQ3(J=OY?gXnc=adI)-u!{HPsAQ^*F_}8EtMIc_8%>bj zq4krdFFojv4s;T|4=z=i+2wV*crW8Xg%!1*gSgk71+E*c8*Jwqmrl2PjcV%@D+sc) z{a#Qp7bY9S&u!yD%H!G)^=0V1SYo*9n-7S^brB&?91(NZiL!}-se6JY26Rl(+^e(>qJV!3HdJao{oHv!5NZ_q>s%Az2DdMdOe?711g%`crXdgfVfRY z4RC^(QhU#V_|_FuWJ|;`?&|ejkTd-J-fsS+&Ts{`Mf*leG~A%8i@XYxj@!$U8JJ!E zxC6tCINIA7g>+ZDwt2fOs1<;jfx$azmI_Fh z)+}&IvsAbv^KjSzH6{non2S~~!H0IuZ=K^zx$RBGPb?u0;sCvJ1#t{nG*J_C4`~~1 zWJmByH~`Pzi_J&>NhpVt)U{)VZ^LZa>jo;ZaxK<&gUDe)vNQoU_!yI0Jmi)CjbK{* zkA_rKhL4xbP9Vf~CeUoscszuX;ki_rG$uual+6~JzF26AEX zWPQ47g~b)mk=^Kp_C&FFnPW{lW@m(LKCwz}1*P|O#8^vF88^?TL(QyDCG@*p|6?m- zUNX~;KsrlKB;9NY;vEpQymuJ?@?|SbHy-?8?>y(}_FCubW0(F0({#B!8dlq7F}V*S zy#Jh*_ z*c;_F>b_oYa-Wb-|NKtb-hSdX*j$^om_4py4br1Id z653MJRuq<9KuKg$P2c!~PBu+_1MJl`&G5>mWpnV;oL^SNud#nfjPviQTkW-WwgqyG zIejcp_{DYddqKa(^_a--g?$N}_dVLg8OnXP$L!)qJ2t01WK|{2J5jB0f#G2=wE#&2 zRD#Ag33yyTiTK%-1NSzgNt<$`yGsu%feo%@WU11qxfCD@eU393ZOHnz_u(EflT}(L zb&onzA+4!BV46!SYx#5^YdDi>Y=d6!aWia@06T8jMC&~E!wy-3&4zGxqV4*OS*A`U z=4Yz~4m<}PIMJ{bO~RxMls<@$Cit?SL0+us8R2Grtr;OYXvcO73OVD2{q_MRlP|nf z73?R$jr7UZYR=o4+OYoOMpzO_Wqv9lhx+9`1z0izD#V6Ptb2!A)yp%f_9`c6mBcUWl~VS;(zM8S*fV^l?VMcAw;IQ+UJB-vV+WMR76`kuV0_d?ZKVfF~F3MrQwgm zZ;X+J>sN{T2|_OkbAoCXZwDFiJZbwPD_?bZY|n_by(ZlldD-akX2&=gv{eLkD7sAd zyx2vz-*+itF5FEm&=#u@MXMf(R@IOS66KVc_FI$P^ut>SbZm22?hkVvo92-{G7j%b z_OFGCT*(L0^IV)Ji(SrKawO>IzP+g$Fw9{$fJU-H_Kxr_kM9|fLM_+vZL(Z=HIW>NTLUNi`Qx+$D5zXGLwW^16&SR5Mi5c5eJ3 z0-xx;W1_=USl-;aVkx@g0&k|OdTf43EA2s_11h)9@V6_}VELUg{(e70i_znA-3Fum8M62w-EZt8|j+8_XwJQ119jJutRr}ZId-9`#eHi_piMoA+ zCD)D;JavVx$i8);$qUl(5Gq-W%=$fk1=$~Fg4lVtQi_{k=TgQe-YmC`7ZJU13A=yY zas>!yd$W3DsxFPm5wyGt^iwrrK>?2#-_iLiF>_!1sum*-hlho5?Rl}^YeAKLVYf#n zf<6<+`x9lw!X*C>3YHk52%ylE*#Pkc8b>|fv~cFDC!90>@no58f8@I_Evw-;|5f0d zzTU}~pi$kUmf`*!bLY_W&gVlzoR4CoAD%gHZE1D|y*pGGa>qh#(0<5iUqe~&y3EyP zvE`+enNEF|>4yJI`KmL^h%?a3Xq0*l<6r*CYs!N*?9u0%=;qkZ=c=BpeJyn}$cMce zyJ>t-w6FH|vw*munLPSVOeaD??$pF8|f*Yo%>mPy>3aB z*Fwe8Zm-8@h$9@l-7nsp0WA>K8g|zY1iYfbZw~#vZ59StT3u?-#|xbIEu=v$5o-LW zz9vcsYF;^aLVz7*OkGFTUSCYV28pgf9GdQ8EuUeN3#y>q1G<6DzJ?RJ!&#H$zQQg$0J#Z}NIiSi1fEPv=cwa^I zcGbJ|#&k&WHtQls{_Vs4^xnT-oGuW~Utf}n!Mu^A&#nKLDqo@3n$nV;9E`z8RmcaY z6@_N(A@?t1@TxS0lG~APP<&9SHUqTtQ!1s^Vn4KnSg-@C=^;rAc3wNk;J&~c9Q^%C z+bN7k(Y@|^nf!p(+k z^WRTj9CAz56SdJVeV2Tk|6ftcnD+2>dGQiA4b$B)Jz`WWQ2w*_e-puPlON>fHEA5mkl1Y*RCi074vNmjK z*o(F6|CrG*kEIuN;gcbi|n z;T`B=F(YNa$Pl@vp#&Nsz7dYjr@-I_V#;cb5W@vsqwti~+9hVuM`}8kcXz#M%Fu!~uyZRKNg@ph_eL@^a z_qu)cs%Hm*GD37`-$EGAFnkq*}HRaCuT}Y7DJ1Wot&G` z;V0?#hs ze74}*4{sBc9%}J*%amvSmCN&OEq;iZ!<`as_$*~0CP?q}-iikYrUdivbSx@*oWPIK zG-u`+Ck7ryE6($_dv-hs9OM|B6Z#EU%~&Q~%~=G@WO<4|`x%o}FeBj|Ja|XVg6SlQ z#r7eXS4{i&vZU|5Km1wskm%_PCvV3@JYa8_{em^cD8IKA$!ZX>e)b~*bg#2)#zt-U zpSFDElADHYH!-z8b>!6ND+dEXrpf(}zCQjz=r}`oI#ln{g@syPikzBN$pg|rDpc@ad>C`B?KhhkO}iL zSa)hqyI_C0KqvojtsiY$FRY^4L-re~z@c|)F>hK`#83UbS^2Y5C_sw&E4n7$ z=uW*Gbh-~60Mkk-h?M<$;5Ga&zKJw3FG;lK{U^Q6(DR7<^O;A>upvNeQ z4lhe&35pb`F=68c1U=vmUyDu!Lvi`j{=7SBt!0&=YYMsDej5n}Oi`}P9EYk0*kB|$ z%jN9$pUc-oIpBOyYFT@##$rtJ7a`e{8dsR|566qn1w9R@*PdMOFs}BrcSJrKW5iJf zBfCAdhL;Uf9o$HlJPg15n*{l-*7!+Em3v?S35l@8`LoIyZ5_o_T`$wuYgHOn z!Njx&MkWx?*lkJumP`nsD^L5MuWpXl@N!;|>Px!ME2wG?Fww!~I1dPgJ()2KA72_I z17{o=7u-rdYf$0ERC5E_@bZArlv8wZnwTuNd8V`|_ME}9PYRg6+KM6;>D_`qQ0=`6 zn0$MDS}82bS;@;y7?CXcW|aKl3qE)*$GMzAB1w48+Wh;WY?UgOBv5?29@4o9!SI77 zFkxcFosRUgrXTA&RN1hZVacC9advNlaxOu$)aiFJ9{Smlyens&mf!0;MS@cPOnUESX!#M7#SJ_=e6uiOT=; z=}_3H-Wz6M!AXpKl#G-63fv>_002I{^52k&Gf6kOFQV1eP|o{OnI1t?qg(2Fp(p7h ze!c01Ing;uKtFPpt~ZN=2#LXgZCatz$&muR3_n2X`b@V&mpD5oCeAKTeGc#Tn`V{s zn%<5q$~#gbySNW4e5!vQQ!cU{Rd6!CP|U&3_7hy^B|K29q>rNC&d2_mVsd?8Adhy8 zt5fWP`hN*MLQ{|&z#$KCSlE$avHb%etY|oUCkD3jQcH{Pdeae5v32cPtxHKsLMq+9 zDZi0SDf`z7^b*K8S~5MkX(6`kHteUOV?#0%n zcgJ~#{o!DiD8DG@tn-a+3HX1)a<9_MVH7PUSoz25Z5#j%>n#pbWv$}w{xsfb0=+V+ z-YYfiysOf7akxX_yJesF`jU2)!)~~Su+Z5p`Dr2rO#V7x@k4MVKjpL7 zhrHS4l-m4k^!JEnU<#R18h7~X0jH#8a-(HHOXb#^YyV0WgG-&{X0TXM@<-HKN$YPb z8VQeM_AV)Uv9_r*DLJW8;NY-tA=S30CB-&IofF?Q+^fF*M9|UlW+z6 zV9V{si(h+>S^o@w#pdm==D@c!Scd&!sjw<*U!*f2wN~Y`-gtx7^8p~u_9l!*MNNW=~Or{iq-H)PM7lUuR4)7naDb*I)p?*ufLet55R=5phUW3S9}6bX8aXx111$4^6vV?r)+ zBAMb?fz8Q`1>d*o&&n1{7T3>lB&?O<+zireF91k*m1iEUH{VR?z*)k5%5255e_Agr zC#l3dM8BYcoEI$ZlGzf*!K>6~xa>TcjP)gf>Sf#L@q46b1*nIe=KCd4!S-Oyf2W}?XXGxOg zR?@Ko%Nx{Jf1gh=n{q(1#SJol|DXWUXM@i>NB{dt*W>W|-6n{{~Kz)Y^O-w0@4L1e-pl;|C4o96ND;#mwjgTabOQ1dm$h>O!~0{I;3^_82H&AHb;h&(Dyn6qhXVqGNKQbv7oaBmbW=2 zR(qovm(`SS-%pKr`<>#b%2Yob;+BGD?(om&o^Q^EIE=CU@Ly?wg$RAfkY1z_F1Bt^2`991AUSk-L|!%Fk*3z_`e0Uw(bQezeg{F(t#djuhqc== zT`HP*`*3jlLAB?oxN3{{b|s)_;&la?v?!l^nv)uHgKP^QB|a6%@ti_fhBj{h3|k3e z?;0lKAA~1!-U{xt8i6Z(14i*2CTPFwCZ4MuO^}GSUZ}w?U!Dt{o!*xh#ZN*b4pmn; zx1J5IIgpGB-P&(>Mbk~Qnw9~|FIY?TN5eh5xeL6Uk3q_xnj zG;$CCSU%RL+NbD@7K*>KA!pye_n8-)dT{+OeMXtky3hZtyqRf?k3`t*4?yT1{(h8iE-@ac-3aBoJxZ+{mEluVWdZuv$ue&Us9-*g50;_mv z$*gB1)nJ2eVjS<$!a7TO=S#QKp1Iol*H>=BzE|y7YUQz`uYC>s=xJ!>rcugzPeJal z?*6>Wd^XPiVE$X7wEWII^XBbj29iL56t4KqQY5OC!rUmxe7t*1ojRGXgP7Z_r5^c7 z=7r0D?%O8%Q>3`BvZu$2{SHqpyX>*0Wx|RGhm0M0LRTdrd>-OOK@({+OU@@*t%bVi zE3~gKf##w7Sg6Y~?FkB82*Xsx6+eJzUVXCTCFAh5oqATKDEbV|1M$?ZW&}-+-a)90 z2DaKLT@~D;7P1QF(ua>2F{eWo6~$}R)}t$Un{B^RZ)ZWaom!8*i0w~?X*D9`MYV=? zKA27t9<$12rW1C|-t3d|m>;Lih=W0e2`2{nkaEU`-d*aeakyhaX)xO6%yB-Q=?GVB zSbq6I6E(P<1LapvxWnmnoAK0S#n;k6mS{A0XfiXlFj85dK0Pizvgo#!DR|KWtYRVs z{H@v4{o*{pdT#IkUqHL01Hc57lKZTy=SUTw!Gw3}!;WtM|Mqk9V(%&{aq59pQwkhQ z_J6^2FalHpX}BVLEJdNEO*zI$rjfeuvfRZVGJMk&?iy_$7S{vU8u(cs?Y1w#bV

`, diff --git a/web/projects/shared/src/components/ticker.component.ts b/web/projects/shared/src/components/ticker.component.ts index 0f1652a90..c3340bc70 100644 --- a/web/projects/shared/src/components/ticker.component.ts +++ b/web/projects/shared/src/components/ticker.component.ts @@ -21,7 +21,6 @@ import { &:hover { text-indent: var(--indent, 0); text-overflow: clip; - cursor: default; } } `, diff --git a/web/projects/shared/src/directives/docs-link.directive.ts b/web/projects/shared/src/directives/docs-link.directive.ts index 3aebf54d2..5c0aa74f0 100644 --- a/web/projects/shared/src/directives/docs-link.directive.ts +++ b/web/projects/shared/src/directives/docs-link.directive.ts @@ -6,7 +6,6 @@ import { input, } from '@angular/core' -const HOST = 'https://staging.docs.start9.com' export const VERSION = new InjectionToken('VERSION') @Directive({ @@ -26,6 +25,6 @@ export class DocsLinkDirective { protected readonly url = computed(() => { const path = this.href() const relative = path.startsWith('/') ? path : `/${path}` - return `${HOST}${relative}?os=${this.version}` + return `https://docs.start9.com${relative}?os=${this.version}` }) } diff --git a/web/projects/shared/src/i18n/dictionaries/de.ts b/web/projects/shared/src/i18n/dictionaries/de.ts index a68a62368..a59c6efb7 100644 --- a/web/projects/shared/src/i18n/dictionaries/de.ts +++ b/web/projects/shared/src/i18n/dictionaries/de.ts @@ -60,7 +60,7 @@ export default { 57: 'Herunterfahren wird eingeleitet', 58: 'Hinzufügen', 59: 'Ok', - 60: 'Möchten Sie diesen Eintrag wirklich löschen?', + 60: 'französisch', 61: 'Dieser Wert kann nach dem Festlegen nicht geändert werden', 62: 'Fortfahren', 63: 'Klicken oder Datei hierher ziehen', @@ -85,7 +85,7 @@ export default { 82: 'Metriken', 83: 'Protokolle', 84: 'Benachrichtigungen', - 85: 'UI starten', + 85: 'Hartes Deinstallieren', 86: 'QR-Code anzeigen', 87: 'URL kopieren', 88: 'Aktionen', @@ -230,9 +230,9 @@ export default { 227: 'Unbekannter Fehler', 228: 'Fehler', 229: '"Container neu bauen" ist eine harmlose Aktion, die nur wenige Sekunden dauert. Sie wird dieses Problem wahrscheinlich beheben.', - 230: '"Dienst deinstallieren" ist eine gefährliche Aktion, die den Dienst aus StartOS entfernt und alle zugehörigen Daten dauerhaft löscht.', + 230: '"Hartes Deinstallieren" ist eine gefährliche Aktion, die den Dienst aus StartOS entfernt und alle zugehörigen Daten dauerhaft löscht.', 231: 'Container neu bauen', - 232: 'Dienst deinstallieren', + 232: 'Weiches Deinstallieren', 233: 'Vollständige Nachricht anzeigen', 234: 'Dienstfehler', 235: 'Warte auf Ergebnis', @@ -247,7 +247,6 @@ export default { 244: 'Hosting', 245: 'Installation läuft', 246: 'Siehe unten', - 247: 'Steuerelemente', 248: 'Keine Dienste installiert', 249: 'Läuft', 250: 'Gestoppt', @@ -414,12 +413,12 @@ export default { 411: 'Weitere Netzwerke', 412: 'WiFi ist deaktiviert', 413: 'Keine drahtlose Schnittstelle erkannt', - 414: 'WiFi wird aktiviert', - 415: 'WiFi wird deaktiviert', + 414: 'wird aktiviert', + 415: 'wird deaktiviert', 416: 'Verbindung wird hergestellt. Dies kann einen Moment dauern', 417: 'Erneut versuchen', 418: 'Mehr anzeigen', - 419: 'Versionshinweise', + 419: 'Details anzeigen', 420: 'Eintrag anzeigen', 421: 'Dienste, die von folgendem abhängen:', 422: 'werden nicht mehr ordnungsgemäß funktionieren und könnten abstürzen.', @@ -503,5 +502,20 @@ export default { 500: 'Marktplatz anzeigen', 501: 'Willkommen bei', 502: 'souveränes computing', - 503: 'französisch', + 503: 'Passen Sie den Namen an, der in Ihrem Browser-Tab erscheint', + 504: 'Verwalten', + 505: 'Möchten Sie diese Adresse wirklich löschen?', + 506: '"Weiches Deinstallieren" entfernt den Dienst aus StartOS, behält jedoch die Daten bei.', + 507: 'Keine gespeicherten Anbieter', + 508: 'Kiosk-Modus', + 509: 'Aktiviert', + 510: 'Deaktiviere den Kiosk-Modus, es sei denn, du musst einen Monitor anschließen', + 511: 'Aktiviere den Kiosk-Modus, wenn du einen Monitor anschließen musst', + 512: 'Der Kiosk-Modus ist auf diesem Gerät nicht verfügbar', + 513: 'Aktivieren', + 514: 'Deaktivieren', + 515: 'Du verwendest derzeit einen Kiosk. Wenn du den Kiosk-Modus deaktivierst, wird die Verbindung zum Kiosk getrennt.', + 516: 'Empfohlen', + 517: 'Möchten Sie diese Aufgabe wirklich verwerfen?', + 518: 'Verwerfen', } satisfies i18n diff --git a/web/projects/shared/src/i18n/dictionaries/en.ts b/web/projects/shared/src/i18n/dictionaries/en.ts index 552293dd1..e012c9fe6 100644 --- a/web/projects/shared/src/i18n/dictionaries/en.ts +++ b/web/projects/shared/src/i18n/dictionaries/en.ts @@ -44,7 +44,7 @@ export const ENGLISH = { 'Beginning restart': 42, 'You are on the latest version of StartOS.': 43, 'Up to date!': 44, - 'Release Notes': 45, + 'Release notes': 45, 'Begin Update': 46, 'Beginning update': 47, 'You are currently connected over Tor. If you reset the Tor daemon, you will lose connectivity until it comes back online.': 48, @@ -59,7 +59,7 @@ export const ENGLISH = { 'Beginning shutdown': 57, 'Add': 58, 'Ok': 59, - 'Are you sure you want to delete this entry?': 60, + 'french': 60, 'This value cannot be changed once set': 61, 'Continue': 62, 'Click or drop file here': 63, @@ -84,7 +84,7 @@ export const ENGLISH = { 'Metrics': 82, // system info such as CPU, RAM, and storage usage 'Logs': 83, // as in, application logs 'Notifications': 84, - 'Launch UI': 85, + 'Hard uninstall': 85, // as in, hard reset or hard reboot, except for uninstalling 'Show QR': 86, 'Copy URL': 87, 'Actions': 88, // as in, actions available to the user @@ -229,9 +229,9 @@ export const ENGLISH = { 'Unknown error': 227, 'Error': 228, '"Rebuild container" is a harmless action that and only takes a few seconds to complete. It will likely resolve this issue.': 229, - '"Uninstall service" is a dangerous action that will remove the service from StartOS and wipe all its data.': 230, + '"Hard uninstall" is a dangerous action that will remove the service from StartOS and wipe all its data.': 230, 'Rebuild container': 231, - 'Uninstall service': 232, + 'Soft uninstall': 232, // as in, uninstall the service but preserve its data 'View full message': 233, 'Service error': 234, 'Awaiting result': 235, @@ -246,7 +246,6 @@ export const ENGLISH = { 'Hosting': 244, 'Installing': 245, 'See below': 246, - 'Controls': 247, 'No services installed': 248, 'Running': 249, 'Stopped': 250, @@ -413,12 +412,12 @@ export const ENGLISH = { 'Other Networks': 411, 'WiFi is disabled': 412, 'No wireless interface detected': 413, - 'Enabling WiFi': 414, - 'Disabling WiFi': 415, + 'Enabling': 414, + 'Disabling': 415, 'Connecting. This could take a while': 416, 'Retry': 417, 'Show more': 418, - 'Release notes': 419, + 'View details': 419, 'View listing': 420, 'Services that depend on': 421, 'will no longer work properly and may crash.': 422, @@ -502,5 +501,20 @@ export const ENGLISH = { 'View Marketplace': 500, 'Welcome to': 501, 'sovereign computing': 502, - 'french': 503, + 'Customize the name appearing in your browser tab': 503, + 'Manage': 504, // as in, administer + 'Are you sure you want to delete this address?': 505, // this address referes to a domain or URL + '"Soft uninstall" will remove the service from StartOS but preserve its data.': 506, + 'No saved providers': 507, + 'Kiosk Mode': 508, // an OS mode that permits attaching a monitor to the computer + 'Enabled': 509, + 'Disable Kiosk Mode unless you need to attach a monitor': 510, + 'Enable Kiosk Mode if you need to attach a monitor': 511, + 'Kiosk Mode is unavailable on this device': 512, + 'Enable': 513, + 'Disable': 514, + 'You are currently using a kiosk. Disabling Kiosk Mode will result in the kiosk disconnecting.': 515, + 'Recommended': 516, // as in, we recommend this + 'Are you sure you want to dismiss this task?': 517, + 'Dismiss': 518, // as in, dismiss or delete a task } as const diff --git a/web/projects/shared/src/i18n/dictionaries/es.ts b/web/projects/shared/src/i18n/dictionaries/es.ts index 59643fea8..918528436 100644 --- a/web/projects/shared/src/i18n/dictionaries/es.ts +++ b/web/projects/shared/src/i18n/dictionaries/es.ts @@ -45,7 +45,7 @@ export default { 42: 'Iniciando reinicio', 43: 'Estás usando la última versión de StartOS.', 44: '¡Actualizado!', - 45: 'Notas de la versión', + 45: 'notas de la versión', 46: 'Iniciar actualización', 47: 'Iniciando actualización', 48: 'Actualmente estás conectado a través de Tor. Si restableces el servicio Tor, perderás la conexión hasta que vuelva a estar en línea.', @@ -60,7 +60,7 @@ export default { 57: 'Iniciando apagado', 58: 'Agregar', 59: 'Ok', - 60: '¿Estás seguro de que deseas eliminar esta entrada?', + 60: 'francés', 61: 'Este valor no se puede cambiar una vez establecido', 62: 'Continuar', 63: 'Haz clic o suelta el archivo aquí', @@ -85,7 +85,7 @@ export default { 82: 'Métricas', 83: 'Registros', 84: 'Notificaciones', - 85: 'Abrir interfaz', + 85: 'Desinstalación forzada', 86: 'Mostrar QR', 87: 'Copiar URL', 88: 'Acciones', @@ -230,9 +230,9 @@ export default { 227: 'Error desconocido', 228: 'Error', 229: '"Reconstruir contenedor" es una acción inofensiva que solo toma unos segundos. Probablemente resolverá este problema.', - 230: '"Desinstalar servicio" es una acción peligrosa que eliminará el servicio de StartOS y borrará todos sus datos.', + 230: '"Desinstalación forzada" es una acción peligrosa que eliminará el servicio de StartOS y borrará todos sus datos.', 231: 'Reconstruir contenedor', - 232: 'Desinstalar servicio', + 232: 'Desinstalación suave', 233: 'Ver mensaje completo', 234: 'Error del servicio', 235: 'Esperando resultado', @@ -247,7 +247,6 @@ export default { 244: 'Alojamiento', 245: 'Instalando', 246: 'Ver abajo', - 247: 'Controles', 248: 'No hay servicios instalados', 249: 'En ejecución', 250: 'Detenido', @@ -414,12 +413,12 @@ export default { 411: 'Otras redes', 412: 'WiFi está deshabilitado', 413: 'No se detectó interfaz inalámbrica', - 414: 'Habilitando WiFi', - 415: 'Deshabilitando WiFi', + 414: 'Habilitando', + 415: 'Deshabilitando', 416: 'Conectando. Esto podría tardar un poco', 417: 'Reintentar', 418: 'Mostrar más', - 419: 'Notas de la versión', + 419: 'Ver detalles', 420: 'Ver listado', 421: 'Servicios que dependen de', 422: 'ya no funcionarán correctamente y podrían fallar.', @@ -503,5 +502,20 @@ export default { 500: 'Ver Marketplace', 501: 'Bienvenido a', 502: 'computación soberana', - 503: 'francés', + 503: 'Personaliza el nombre que aparece en la pestaña de tu navegador', + 504: 'Administrar', + 505: '¿Estás seguro de que deseas eliminar esta dirección?', + 506: '"Desinstalación suave" eliminará el servicio de StartOS pero conservará sus datos.', + 507: 'No hay proveedores guardados', + 508: 'Modo quiosco', + 509: 'Activado', + 510: 'Desactiva el modo quiosco a menos que necesites conectar un monitor', + 511: 'Activa el modo quiosco si necesitas conectar un monitor', + 512: 'El modo quiosco no está disponible en este dispositivo', + 513: 'Activar', + 514: 'Desactivar', + 515: 'Actualmente estás utilizando un quiosco. Desactivar el modo quiosco provocará su desconexión.', + 516: 'Recomendado', + 517: '¿Estás seguro de que deseas descartar esta tarea?', + 518: 'Descartar', } satisfies i18n diff --git a/web/projects/shared/src/i18n/dictionaries/fr.ts b/web/projects/shared/src/i18n/dictionaries/fr.ts index 35e5bfec4..e0e645df9 100644 --- a/web/projects/shared/src/i18n/dictionaries/fr.ts +++ b/web/projects/shared/src/i18n/dictionaries/fr.ts @@ -60,7 +60,7 @@ export default { 57: 'Arrêt initié', 58: 'Ajouter', 59: 'OK', - 60: 'Voulez-vous vraiment supprimer cette entrée ?', + 60: 'français', 61: 'Cette valeur ne peut plus être modifiée une fois définie', 62: 'Continuer', 63: 'Cliquez ou déposez le fichier ici', @@ -85,7 +85,7 @@ export default { 82: 'Métriques', 83: 'Journaux', 84: 'Notifications', - 85: 'Lancer l’interface utilisateur', + 85: 'Désinstallation forcée', 86: 'Afficher le QR', 87: 'Copier l’URL', 88: 'Actions', @@ -230,9 +230,9 @@ export default { 227: 'Erreur inconnue', 228: 'Erreur', 229: '« Reconstruire le conteneur » est une action sans risque qui ne prend que quelques secondes. Cela résoudra probablement ce problème.', - 230: '« Désinstaller le service » est une action risquée qui supprimera le service de StartOS et effacera toutes ses données.', + 230: '« Désinstallation forcée » est une action risquée qui supprimera le service de StartOS et effacera toutes ses données.', 231: 'Reconstruire le conteneur', - 232: 'Désinstaller le service', + 232: 'Désinstallation douce', 233: 'Voir le message complet', 234: 'Erreur du service', 235: 'En attente du résultat', @@ -247,7 +247,6 @@ export default { 244: 'Hébergement', 245: 'Installation', 246: 'Voir ci-dessous', - 247: 'Contrôles', 248: 'Aucun service installé', 249: 'En fonctionnement', 250: 'Arrêté', @@ -414,12 +413,12 @@ export default { 411: 'Autres réseaux', 412: 'Le WiFi est désactivé', 413: 'Aucune interface sans fil détectée', - 414: 'Activation du WiFi', - 415: 'Désactivation du WiFi', + 414: 'Activation', + 415: 'Désactivation', 416: 'Connexion en cours. Cela peut prendre un certain temps', 417: 'Réessayer', 418: 'Afficher plus', - 419: 'Notes de version', + 419: 'Voir les détails', 420: 'Voir la fiche', 421: 'Services dépendants de', 422: 'ne fonctionneront plus correctement et pourraient planter.', @@ -503,5 +502,20 @@ export default { 500: 'Voir la bibliothèque de services', 501: 'Bienvenue sur', 502: 'informatique souveraine', - 503: 'français', + 503: 'Personnalisez le nom qui apparaît dans l’onglet de votre navigateur', + 504: 'Gérer', + 505: 'Êtes-vous sûr de vouloir supprimer cette adresse ?', + 506: '« Désinstallation douce » supprimera le service de StartOS tout en conservant ses données.', + 507: 'Aucun fournisseur enregistré', + 508: 'Mode kiosque', + 509: 'Activé', + 510: 'Désactivez le mode kiosque sauf si vous devez connecter un moniteur', + 511: 'Activez le mode kiosque si vous devez connecter un moniteur', + 512: 'Le mode kiosque n’est pas disponible sur cet appareil', + 513: 'Activer', + 514: 'Désactiver', + 515: 'Vous utilisez actuellement un kiosque. Désactiver le mode kiosque entraînera sa déconnexion.', + 516: 'Recommandé', + 517: 'Êtes-vous sûr de vouloir ignorer cette tâche ?', + 518: 'Ignorer', } satisfies i18n diff --git a/web/projects/shared/src/i18n/dictionaries/pl.ts b/web/projects/shared/src/i18n/dictionaries/pl.ts index bd1ad4807..185d696da 100644 --- a/web/projects/shared/src/i18n/dictionaries/pl.ts +++ b/web/projects/shared/src/i18n/dictionaries/pl.ts @@ -60,7 +60,7 @@ export default { 57: 'Rozpoczynanie wyłączania', 58: 'Dodaj', 59: 'OK', - 60: 'Czy na pewno chcesz usunąć ten wpis?', + 60: 'francuski', 61: 'Ta wartość nie może być zmieniona po jej ustawieniu', 62: 'Kontynuuj', 63: 'Kliknij lub upuść plik tutaj', @@ -85,7 +85,7 @@ export default { 82: 'Monitorowanie', 83: 'Logi', 84: 'Powiadomienia', - 85: 'Uruchom interfejs', + 85: 'Twarde odinstalowanie', 86: 'Pokaż kod QR', 87: 'Kopiuj URL', 88: 'Akcje', @@ -230,9 +230,9 @@ export default { 227: 'Nieznany błąd', 228: 'Błąd', 229: '„Odbuduj kontener” to bezpieczna akcja, która zajmuje tylko kilka sekund. Prawdopodobnie rozwiąże ten problem.', - 230: '„Odinstaluj serwis” to niebezpieczna akcja, która usunie serwis ze StartOS i trwale usunie wszystkie jego dane.', + 230: '„Twarde odinstalowanie” to niebezpieczna akcja, która usunie serwis ze StartOS i trwale usunie wszystkie jego dane.', 231: 'Odbuduj kontener', - 232: 'Odinstaluj serwis', + 232: 'Miękkie odinstalowanie', 233: 'Zobacz pełną wiadomość', 234: 'Błąd serwisu', 235: 'Oczekiwanie na wynik', @@ -247,7 +247,6 @@ export default { 244: 'Hosting', 245: 'Instalowanie', 246: 'Zobacz poniżej', - 247: 'Sterowanie', 248: 'Brak zainstalowanych serwisów', 249: 'Uruchomiony', 250: 'Zatrzymany', @@ -414,12 +413,12 @@ export default { 411: 'Inne sieci', 412: 'WiFi jest wyłączone', 413: 'Nie wykryto interfejsu bezprzewodowego', - 414: 'Włączanie WiFi', - 415: 'Wyłączanie WiFi', + 414: 'Włączanie', + 415: 'Wyłączanie', 416: 'Łączenie. To może chwilę potrwać', 417: 'Ponów próbę', 418: 'Pokaż więcej', - 419: 'Informacje o wydaniu', + 419: 'Zobacz szczegóły', 420: 'Zobacz listę', 421: 'Serwisy zależne od', 422: 'przestaną działać poprawnie i mogą ulec awarii.', @@ -503,5 +502,20 @@ export default { 500: 'Zobacz Rynek', 501: 'Witamy w', 502: 'suwerenne przetwarzanie', - 503: 'francuski', + 503: 'Dostosuj nazwę wyświetlaną na karcie przeglądarki', + 504: 'Zarządzać', + 505: 'Czy na pewno chcesz usunąć ten adres?', + 506: '„Miękkie odinstalowanie” usunie usługę z StartOS, ale zachowa jej dane.', + 507: 'Brak zapisanych dostawców', + 508: 'Tryb kiosku', + 509: 'Włączony', + 510: 'Wyłącz tryb kiosku, chyba że potrzebujesz podłączyć monitor', + 511: 'Włącz tryb kiosku, jeśli potrzebujesz podłączyć monitor', + 512: 'Tryb kiosku jest niedostępny na tym urządzeniu', + 513: 'Włącz', + 514: 'Wyłącz', + 515: 'Obecnie używasz kiosku. Wyłączenie trybu kiosku spowoduje jego rozłączenie.', + 516: 'Zalecane', + 517: 'Czy na pewno chcesz odrzucić to zadanie?', + 518: 'Odrzuć', } satisfies i18n diff --git a/web/projects/shared/src/i18n/i18n.pipe.ts b/web/projects/shared/src/i18n/i18n.pipe.ts index 6ff5f8abc..d7cb56245 100644 --- a/web/projects/shared/src/i18n/i18n.pipe.ts +++ b/web/projects/shared/src/i18n/i18n.pipe.ts @@ -11,8 +11,6 @@ import { I18N, i18nKey } from './i18n.providers' export class i18nPipe implements PipeTransform { private readonly i18n = inject(I18N) - // @TODO uncomment to make sure translations are present - // transform(englishKey: string | null | undefined): string | undefined { transform(englishKey: i18nKey | null | undefined): string | undefined { return englishKey ? this.i18n()?.[ENGLISH[englishKey as i18nKey]] || englishKey diff --git a/web/projects/shared/src/i18n/i18n.service.ts b/web/projects/shared/src/i18n/i18n.service.ts index 6911d71e6..b627cddbf 100644 --- a/web/projects/shared/src/i18n/i18n.service.ts +++ b/web/projects/shared/src/i18n/i18n.service.ts @@ -34,5 +34,11 @@ export class i18nService extends TuiLanguageSwitcherService { } } -export const languages = ['english', 'spanish', 'polish', 'german', 'french'] as const +export const languages = [ + 'english', + 'spanish', + 'polish', + 'german', + 'french', +] as const export type Languages = (typeof languages)[number] diff --git a/web/projects/shared/src/util/format-progress.ts b/web/projects/shared/src/util/format-progress.ts index 9a6a1401e..5857e815f 100644 --- a/web/projects/shared/src/util/format-progress.ts +++ b/web/projects/shared/src/util/format-progress.ts @@ -12,13 +12,15 @@ export function formatProgress({ phases, overall }: T.FullProgress): { p, ): p is { name: string - progress: { - done: number - total: number | null - } + progress: + | false + | { + done: number + total: number | null + } } => p.progress !== true && p.progress !== null, ) - .map(p => `${p.name}${getPhaseBytes(p.progress)}`) + .map(p => `${p.name}: (${getPhaseBytes(p.progress)})`) .join(', '), } } @@ -33,8 +35,13 @@ function getDecimal(progress: T.Progress): number { } } -function getPhaseBytes(progress: T.Progress): string { - return progress === true || !progress - ? '' - : `: (${progress.done}/${progress.total})` +function getPhaseBytes( + progress: + | false + | { + done: number + total: number | null + }, +): string { + return !progress ? 'unknown' : `${progress.done}/${progress.total}` } diff --git a/web/projects/shared/styles/shared.scss b/web/projects/shared/styles/shared.scss index fb30fb3f9..884bef184 100644 --- a/web/projects/shared/styles/shared.scss +++ b/web/projects/shared/styles/shared.scss @@ -277,6 +277,7 @@ body { vertical-align: bottom; animation: ellipsis-dot 1s infinite 0.3s; animation-fill-mode: forwards; + text-align: left; width: 1em; } diff --git a/web/projects/shared/styles/taiga.scss b/web/projects/shared/styles/taiga.scss index fc3716a9f..4d4d7e4ac 100644 --- a/web/projects/shared/styles/taiga.scss +++ b/web/projects/shared/styles/taiga.scss @@ -95,6 +95,24 @@ } } +[tuiAppearance][data-appearance='primary-success'] { + color: var(--tui-text-primary-on-accent-1); + background: var(--tui-status-positive); + + @include appearance-hover { + filter: brightness(1.2); + } + + @include appearance-active { + filter: brightness(0.9); + } + + @include appearance-disabled { + background: var(--tui-status-neutral); + color: #333; + } +} + tui-hint[data-appearance='onDark'] { background: white !important; color: #222 !important; diff --git a/web/projects/ui/src/app/routes/initializing/initializing.page.ts b/web/projects/ui/src/app/routes/initializing/initializing.page.ts index be48a1dbf..057039a2f 100644 --- a/web/projects/ui/src/app/routes/initializing/initializing.page.ts +++ b/web/projects/ui/src/app/routes/initializing/initializing.page.ts @@ -48,6 +48,6 @@ export default class InitializingPage { return caught$ }), ), - { initialValue: { total: 0, message: '' } }, + { initialValue: { total: 0, message: 'waiting...' } }, ) } diff --git a/web/projects/ui/src/app/routes/login/login.page.ts b/web/projects/ui/src/app/routes/login/login.page.ts index bfabf4a9e..5a0badb78 100644 --- a/web/projects/ui/src/app/routes/login/login.page.ts +++ b/web/projects/ui/src/app/routes/login/login.page.ts @@ -51,7 +51,6 @@ export class LoginPage { } catch (e: any) { // code 7 is for incorrect password this.error = e.code === 7 ? 'Invalid password' : (e.message as i18nKey) - } finally { loader.unsubscribe() } } diff --git a/web/projects/ui/src/app/routes/portal/components/form/form-array/form-array.component.ts b/web/projects/ui/src/app/routes/portal/components/form/form-array/form-array.component.ts index 0b87734e1..b30cc499b 100644 --- a/web/projects/ui/src/app/routes/portal/components/form/form-array/form-array.component.ts +++ b/web/projects/ui/src/app/routes/portal/components/form/form-array/form-array.component.ts @@ -73,20 +73,7 @@ export class FormArrayComponent { } removeAt(index: number) { - this.dialog - .openConfirm({ - label: 'Confirm', - size: 's', - data: { - content: 'Are you sure you want to delete this entry?', - yes: 'Delete', - no: 'Cancel', - }, - }) - .pipe(filter(Boolean), takeUntilDestroyed(this.destroyRef)) - .subscribe(() => { - this.removeItem(index) - }) + this.removeItem(index) } private removeItem(index: number) { diff --git a/web/projects/ui/src/app/routes/portal/components/header/menu.component.ts b/web/projects/ui/src/app/routes/portal/components/header/menu.component.ts index fe44771a2..27f250814 100644 --- a/web/projects/ui/src/app/routes/portal/components/header/menu.component.ts +++ b/web/projects/ui/src/app/routes/portal/components/header/menu.component.ts @@ -29,7 +29,7 @@ import { ABOUT } from './about.component' appearance="" tuiHintDirection="bottom" [tuiHint]="open ? '' : ('Start Menu' | i18n)" - [tuiHintShowDelay]="1000" + [tuiHintShowDelay]="750" [tuiDropdown]="content" [(tuiDropdownOpen)]="open" [tuiDropdownMaxHeight]="9999" diff --git a/web/projects/ui/src/app/routes/portal/components/header/navigation.component.ts b/web/projects/ui/src/app/routes/portal/components/header/navigation.component.ts index cfdbe370a..5855b90ec 100644 --- a/web/projects/ui/src/app/routes/portal/components/header/navigation.component.ts +++ b/web/projects/ui/src/app/routes/portal/components/header/navigation.component.ts @@ -23,7 +23,7 @@ import { getMenu } from 'src/app/utils/system-utilities' class="link" routerLinkActive="link_active" tuiHintDirection="bottom" - [tuiHintShowDelay]="1000" + [tuiHintShowDelay]="750" [routerLink]="item.routerLink" [class.link_system]="item.routerLink === '/portal/system'" [tuiHint]="rla.isActive ? '' : (item.name | i18n)" diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/actions.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/actions.component.ts index 22493c80e..53b0eb475 100644 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/actions.component.ts +++ b/web/projects/ui/src/app/routes/portal/components/interfaces/actions.component.ts @@ -15,6 +15,7 @@ import { import { PolymorpheusComponent } from '@taiga-ui/polymorpheus' import { QRModal } from 'src/app/routes/portal/modals/qr.component' import { InterfaceComponent } from './interface.component' +import { DOCUMENT } from '@angular/common' @Component({ standalone: true, @@ -22,24 +23,30 @@ import { InterfaceComponent } from './interface.component' template: ` - + - {{ logs[key]?.title | i18n }} - } @else { - {{ 'Logs' | i18n }} - } - - @if (current(); as key) { -
- - - {{ logs[key]?.title | i18n }} - -

{{ logs[key]?.subtitle | i18n }}

-
- @for (log of logs | keyvalue; track $index) { - @if (log.key === current()) { - - } - } - } @else { - @for (log of logs | keyvalue; track $index) { - - } - } - `, - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - host: { class: 'g-page' }, - styles: [ - ` - :host { - display: flex; - align-items: center; - justify-content: center; - flex-wrap: wrap; - gap: 1rem; - padding: 1rem; - } - - header { - width: 100%; - padding: 0 1rem; - } - - strong { - font-weight: 700; - } - - logs { - height: calc(100% - 4rem); - width: 100%; - } - - .close { - position: absolute; - right: 0; - border-radius: 100%; - } - - button::before { - margin: 0 -0.25rem 0 -0.375rem; - --tui-icon-size: 1.5rem; - } - - [tuiCardMedium] { - height: 14rem; - width: 14rem; - cursor: pointer; - box-shadow: - inset 0 0 0 1px var(--tui-background-neutral-1), - var(--tui-shadow-small); - - [tuiSubtitle] { - color: var(--tui-text-secondary); - } - - tui-icon:last-child { - align-self: flex-end; - } - } - - :host-context(tui-root._mobile) { - flex-direction: column; - justify-content: flex-start; - - header { - padding: 0; - } - - .title { - display: none; - } - - logs { - height: calc(100% - 2rem); - } - - [tuiCardMedium] { - width: 100%; - height: auto; - gap: 1rem; - } - } - `, - ], - imports: [ - LogsComponent, - TitleDirective, - KeyValuePipe, - TuiTitle, - TuiCardMedium, - TuiIcon, - TuiAppearance, - TuiButton, - i18nPipe, - ], -}) -export default class SystemLogsComponent { - private readonly api = inject(ApiService) - - readonly current = signal(null) - readonly logs: Record = { - os: { - title: 'OS Logs', - subtitle: 'Raw, unfiltered operating system logs', - icon: '@tui.square-dashed-bottom-code', - follow: params => this.api.followServerLogs(params), - fetch: params => this.api.getServerLogs(params), - }, - kernel: { - title: 'Kernel Logs', - subtitle: 'Diagnostics for drivers and other kernel processes', - icon: '@tui.square-chevron-right', - follow: params => this.api.followKernelLogs(params), - fetch: params => this.api.getKernelLogs(params), - }, - tor: { - title: 'Tor Logs', - subtitle: 'Diagnostic logs for the Tor daemon on StartOS', - icon: '@tui.globe', - follow: params => this.api.followTorLogs(params), - fetch: params => this.api.getTorLogs(params), - }, - } -} diff --git a/web/projects/ui/src/app/routes/portal/routes/logs/logs.routes.ts b/web/projects/ui/src/app/routes/portal/routes/logs/logs.routes.ts new file mode 100644 index 000000000..281683fcd --- /dev/null +++ b/web/projects/ui/src/app/routes/portal/routes/logs/logs.routes.ts @@ -0,0 +1,22 @@ +import { Routes } from '@angular/router' + +export const ROUTES: Routes = [ + { + path: '', + loadComponent: () => import('./routes/outlet.component'), + }, + { + path: 'kernel', + loadComponent: () => import('./routes/kernel.component'), + }, + { + path: 'os', + loadComponent: () => import('./routes/os.component'), + }, + { + path: 'tor', + loadComponent: () => import('./routes/tor.component'), + }, +] + +export default ROUTES diff --git a/web/projects/ui/src/app/routes/portal/routes/logs/routes/kernel.component.ts b/web/projects/ui/src/app/routes/portal/routes/logs/routes/kernel.component.ts new file mode 100644 index 000000000..4a3c85990 --- /dev/null +++ b/web/projects/ui/src/app/routes/portal/routes/logs/routes/kernel.component.ts @@ -0,0 +1,40 @@ +import { ChangeDetectionStrategy, Component, inject } from '@angular/core' +import { i18nPipe } from '@start9labs/shared' +import { LogsComponent } from 'src/app/routes/portal/components/logs/logs.component' +import { RR } from 'src/app/services/api/api.types' +import { ApiService } from 'src/app/services/api/embassy-api.service' + +import { LogsHeaderComponent } from '../components/header.component' + +@Component({ + standalone: true, + template: ` + + {{ 'Diagnostics for drivers and other kernel processes' | i18n }} + + + `, + styles: ` + :host { + padding: 1rem; + } + `, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [LogsComponent, LogsHeaderComponent, i18nPipe], + host: { class: 'g-page' }, +}) +export default class SystemKernelComponent { + private readonly api = inject(ApiService) + + protected readonly follow = (params: RR.FollowServerLogsReq) => + this.api.followKernelLogs(params) + + protected readonly fetch = (params: RR.GetServerLogsReq) => + this.api.getKernelLogs(params) + + log = { + title: 'Kernel Logs', + subtitle: 'Diagnostics for drivers and other kernel processes', + icon: '@tui.square-chevron-right', + } +} diff --git a/web/projects/ui/src/app/routes/portal/routes/logs/routes/os.component.ts b/web/projects/ui/src/app/routes/portal/routes/logs/routes/os.component.ts new file mode 100644 index 000000000..ab1e861f1 --- /dev/null +++ b/web/projects/ui/src/app/routes/portal/routes/logs/routes/os.component.ts @@ -0,0 +1,39 @@ +import { ChangeDetectionStrategy, Component, inject } from '@angular/core' +import { i18nPipe } from '@start9labs/shared' +import { LogsComponent } from 'src/app/routes/portal/components/logs/logs.component' +import { LogsHeaderComponent } from 'src/app/routes/portal/routes/logs/components/header.component' +import { RR } from 'src/app/services/api/api.types' +import { ApiService } from 'src/app/services/api/embassy-api.service' + +@Component({ + standalone: true, + template: ` + + {{ 'Raw, unfiltered operating system logs' | i18n }} + + + `, + styles: ` + :host { + padding: 1rem; + } + `, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [LogsComponent, LogsHeaderComponent, i18nPipe], + host: { class: 'g-page' }, +}) +export default class SystemOSComponent { + private readonly api = inject(ApiService) + + protected readonly follow = (params: RR.FollowServerLogsReq) => + this.api.followServerLogs(params) + + protected readonly fetch = (params: RR.GetServerLogsReq) => + this.api.getServerLogs(params) + + log = { + title: 'Kernel Logs', + subtitle: 'Diagnostics for drivers and other kernel processes', + icon: '@tui.square-chevron-right', + } +} diff --git a/web/projects/ui/src/app/routes/portal/routes/logs/routes/outlet.component.ts b/web/projects/ui/src/app/routes/portal/routes/logs/routes/outlet.component.ts new file mode 100644 index 000000000..9c7c34669 --- /dev/null +++ b/web/projects/ui/src/app/routes/portal/routes/logs/routes/outlet.component.ts @@ -0,0 +1,96 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core' +import { RouterLink } from '@angular/router' +import { i18nPipe } from '@start9labs/shared' +import { TuiAppearance, TuiIcon, TuiTitle } from '@taiga-ui/core' +import { TuiCardMedium } from '@taiga-ui/layout' +import { TitleDirective } from 'src/app/services/title.service' + +@Component({ + template: ` + {{ 'Logs' | i18n }} + @for (log of logs; track $index) { + + + + {{ log.title | i18n }} + {{ log.subtitle | i18n }} + + + + } + `, + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'g-page' }, + styles: [ + ` + :host { + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + gap: 1rem; + padding: 1rem; + } + + [tuiCardMedium] { + height: 14rem; + width: 14rem; + cursor: pointer; + box-shadow: + inset 0 0 0 1px var(--tui-background-neutral-1), + var(--tui-shadow-small); + + [tuiSubtitle] { + color: var(--tui-text-secondary); + } + + tui-icon:last-child { + align-self: flex-end; + } + } + + :host-context(tui-root._mobile) { + flex-direction: column; + justify-content: flex-start; + + [tuiCardMedium] { + width: 100%; + height: auto; + gap: 1rem; + } + } + `, + ], + imports: [ + RouterLink, + TitleDirective, + TuiTitle, + TuiCardMedium, + TuiIcon, + TuiAppearance, + i18nPipe, + ], +}) +export default class SystemLogsComponent { + readonly logs = [ + { + link: 'os', + title: 'OS Logs', + subtitle: 'Raw, unfiltered operating system logs', + icon: '@tui.square-dashed-bottom-code', + }, + { + link: 'kernel', + title: 'Kernel Logs', + subtitle: 'Diagnostics for drivers and other kernel processes', + icon: '@tui.square-chevron-right', + }, + { + link: 'tor', + title: 'Tor Logs', + subtitle: 'Diagnostic logs for the Tor daemon on StartOS', + icon: '@tui.globe', + }, + ] as const +} diff --git a/web/projects/ui/src/app/routes/portal/routes/logs/routes/tor.component.ts b/web/projects/ui/src/app/routes/portal/routes/logs/routes/tor.component.ts new file mode 100644 index 000000000..b84021e0d --- /dev/null +++ b/web/projects/ui/src/app/routes/portal/routes/logs/routes/tor.component.ts @@ -0,0 +1,39 @@ +import { ChangeDetectionStrategy, Component, inject } from '@angular/core' +import { i18nPipe } from '@start9labs/shared' +import { LogsComponent } from 'src/app/routes/portal/components/logs/logs.component' +import { LogsHeaderComponent } from 'src/app/routes/portal/routes/logs/components/header.component' +import { RR } from 'src/app/services/api/api.types' +import { ApiService } from 'src/app/services/api/embassy-api.service' + +@Component({ + standalone: true, + template: ` + + {{ 'Diagnostic logs for the Tor daemon on StartOS' | i18n }} + + + `, + styles: ` + :host { + padding: 1rem; + } + `, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [LogsComponent, LogsHeaderComponent, i18nPipe], + host: { class: 'g-page' }, +}) +export default class SystemOSComponent { + private readonly api = inject(ApiService) + + protected readonly follow = (params: RR.FollowServerLogsReq) => + this.api.followServerLogs(params) + + protected readonly fetch = (params: RR.GetServerLogsReq) => + this.api.getServerLogs(params) + + log = { + title: 'Kernel Logs', + subtitle: 'Diagnostics for drivers and other kernel processes', + icon: '@tui.square-chevron-right', + } +} diff --git a/web/projects/ui/src/app/routes/portal/routes/marketplace/marketplace.component.ts b/web/projects/ui/src/app/routes/portal/routes/marketplace/marketplace.component.ts index 18a7b11c5..8bc32c58e 100644 --- a/web/projects/ui/src/app/routes/portal/routes/marketplace/marketplace.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/marketplace/marketplace.component.ts @@ -64,8 +64,15 @@ import { StorageService } from 'src/app/services/storage.service' overflow: hidden; padding: 0; background: rgb(55 58 63 / 90%) - url('/assets/img/background_marketplace.png') no-repeat top right; + url('/assets/img/background_marketplace.jpg') no-repeat top right; background-size: cover; + + &::before { + content: ''; + position: absolute; + inset: 0; + backdrop-filter: blur(2rem); + } } .marketplace-content { diff --git a/web/projects/ui/src/app/routes/portal/routes/marketplace/modals/preview.component.ts b/web/projects/ui/src/app/routes/portal/routes/marketplace/modals/preview.component.ts index a7fb73226..533245480 100644 --- a/web/projects/ui/src/app/routes/portal/routes/marketplace/modals/preview.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/marketplace/modals/preview.component.ts @@ -20,7 +20,6 @@ import { import { DialogService, Exver, - i18nKey, i18nPipe, MARKDOWN, SharedPipesModule, @@ -34,6 +33,7 @@ import { map, startWith, switchMap, + tap, } from 'rxjs' import { MarketplaceService } from 'src/app/services/marketplace.service' @@ -59,10 +59,9 @@ import { MarketplaceService } from 'src/app/services/marketplace.service' {{ 'Ok' | i18n }} @@ -91,7 +90,7 @@ import { MarketplaceService } from 'src/app/services/marketplace.service' } @else { - + } `, @@ -114,7 +113,7 @@ import { MarketplaceService } from 'src/app/services/marketplace.service' } .listing { - font-size: 0.9rem; + font-size: 0.8rem; // @TODO theme color: #8059e5; font-weight: 600; @@ -139,16 +138,6 @@ import { MarketplaceService } from 'src/app/services/marketplace.service' ::ng-deep label { cursor: pointer; } - - &_empty { - pointer-events: none; - } - } - - .loading { - min-width: 30rem; - height: 100%; - place-self: center; } marketplace-additional { @@ -254,6 +243,6 @@ export class MarketplacePreviewComponent { data: { version }, }) .pipe(filter(Boolean)) - .subscribe(version => this.version$.next(version)) + .subscribe(selected => this.version$.next(selected)) } } diff --git a/web/projects/ui/src/app/routes/portal/routes/metrics/temperature.component.ts b/web/projects/ui/src/app/routes/portal/routes/metrics/temperature.component.ts index ae324abb7..77be2f87c 100644 --- a/web/projects/ui/src/app/routes/portal/routes/metrics/temperature.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/metrics/temperature.component.ts @@ -49,7 +49,7 @@ import { ChangeDetectionStrategy, Component, input } from '@angular/core' - {{ value() || '-' }} C° + {{ value() ? value() + ' C°' : 'N/A' }} `, styles: ` @import '@taiga-ui/core/styles/taiga-ui-local'; diff --git a/web/projects/ui/src/app/routes/portal/routes/notifications/item.component.ts b/web/projects/ui/src/app/routes/portal/routes/notifications/item.component.ts index 2b4633cf3..ff64a1347 100644 --- a/web/projects/ui/src/app/routes/portal/routes/notifications/item.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/notifications/item.component.ts @@ -55,7 +55,11 @@ import { i18nPipe } from '@start9labs/shared' } @if (notificationItem.code === 1 || notificationItem.code === 2) { } @@ -66,7 +70,7 @@ import { i18nPipe } from '@start9labs/shared' '[class._new]': '!notificationItem.read', }, styles: ` - @import '@taiga-ui/core/styles/taiga-ui-local'; + @use '@taiga-ui/core/styles/taiga-ui-local' as taiga; :host { grid-template-columns: 1fr; @@ -90,8 +94,16 @@ import { i18nPipe } from '@start9labs/shared' } :host-context(tui-root._mobile) { + gap: 0.5rem; + padding: 0.75rem 1rem !important; + .checkbox { - @include fullsize(); + @include taiga.fullsize(); + @include taiga.transition(box-shadow); + + &:has(:checked) { + box-shadow: inset 0.25rem 0 var(--tui-background-accent-1); + } } .date { @@ -103,8 +115,7 @@ import { i18nPipe } from '@start9labs/shared' font-weight: bold; font-size: 1.2em; display: flex; - align-items: center; - gap: 0.75rem; + gap: 0.5rem; } .service:not(:has(a)) { diff --git a/web/projects/ui/src/app/routes/portal/routes/notifications/notifications.component.ts b/web/projects/ui/src/app/routes/portal/routes/notifications/notifications.component.ts index d028a334d..03c2ad6c3 100644 --- a/web/projects/ui/src/app/routes/portal/routes/notifications/notifications.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/notifications/notifications.component.ts @@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component, inject, + OnInit, signal, } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' @@ -66,7 +67,7 @@ import { NotificationsTableComponent } from './table.component' i18nPipe, ], }) -export default class NotificationsComponent { +export default class NotificationsComponent implements OnInit { private readonly router = inject(Router) private readonly route = inject(ActivatedRoute) @@ -74,16 +75,19 @@ export default class NotificationsComponent { readonly api = inject(ApiService) readonly errorService = inject(ErrorService) readonly notifications = signal(undefined) - readonly toast = this.route.queryParams.subscribe(params => { - this.router.navigate([], { relativeTo: this.route, queryParams: {} }) - - if (isEmptyObject(params)) { - this.getMore({}) - } - }) open = false + ngOnInit() { + this.route.queryParams.subscribe(params => { + this.router.navigate([], { relativeTo: this.route, queryParams: {} }) + + if (isEmptyObject(params)) { + this.getMore({}) + } + }) + } + async getMore(params: RR.GetNotificationsReq) { try { this.notifications.set(undefined) diff --git a/web/projects/ui/src/app/routes/portal/routes/notifications/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/notifications/table.component.ts index 40dd4d14c..60a9825a1 100644 --- a/web/projects/ui/src/app/routes/portal/routes/notifications/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/notifications/table.component.ts @@ -31,7 +31,7 @@ import { i18nPipe } from '@start9labs/shared' /> {{ 'Date' | i18n }} - {{ 'Title' | i18n }} + {{ 'Title' | i18n }} {{ 'Service' | i18n }} {{ 'Message' | i18n }} @@ -71,9 +71,13 @@ import { i18nPipe } from '@start9labs/shared' styles: ` @import '@taiga-ui/core/styles/taiga-ui-local'; - :host-context(tui-root._mobile) input { - @include fullsize(); - opacity: 0; + :host-context(tui-root._mobile) { + margin: 0 -1rem; + + input { + @include fullsize(); + opacity: 0; + } } `, changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/web/projects/ui/src/app/routes/portal/routes/services/components/action.component.ts b/web/projects/ui/src/app/routes/portal/routes/services/components/action.component.ts index 38ba7b416..2ff2d1bf6 100644 --- a/web/projects/ui/src/app/routes/portal/routes/services/components/action.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/services/components/action.component.ts @@ -14,7 +14,7 @@ interface ActionItem { template: `
{{ action.name }} -
{{ action.description }}
+
@if (disabled) {
{{ disabled }}
} diff --git a/web/projects/ui/src/app/routes/portal/routes/services/components/dependencies.component.ts b/web/projects/ui/src/app/routes/portal/routes/services/components/dependencies.component.ts index 891c1823f..3c78c2551 100644 --- a/web/projects/ui/src/app/routes/portal/routes/services/components/dependencies.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/services/components/dependencies.component.ts @@ -27,7 +27,7 @@ import { PackageDataEntry } from 'src/app/services/patch-db/data-model' /> - {{ d.value.title }} + {{ d.value.title || d.key }} @if (getError(d.key); as error) { {{ error | i18n }} } @else { diff --git a/web/projects/ui/src/app/routes/portal/routes/services/components/error.component.ts b/web/projects/ui/src/app/routes/portal/routes/services/components/error.component.ts index f2c8c2ad2..b072ab9bd 100644 --- a/web/projects/ui/src/app/routes/portal/routes/services/components/error.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/services/components/error.component.ts @@ -6,7 +6,7 @@ import { } from '@angular/core' import { DialogService, i18nKey, i18nPipe } from '@start9labs/shared' import { TuiButton, TuiIcon } from '@taiga-ui/core' -import { TuiLineClamp, TuiTooltip } from '@taiga-ui/kit' +import { TuiTooltip } from '@taiga-ui/kit' import { PackageDataEntry } from 'src/app/services/patch-db/data-model' import { StandardActionsService } from 'src/app/services/standard-actions.service' import { getManifest } from 'src/app/utils/get-package-data' @@ -15,12 +15,9 @@ import { getManifest } from 'src/app/utils/get-package-data' standalone: true, selector: 'service-error', template: ` -
{{ 'Error' | i18n }}
- +
{{ 'Service Launch Error' | i18n }}
+

{{ error?.message }}

+

{{ error?.debug }}

{{ 'Actions' | i18n }} @@ -34,7 +31,13 @@ import { getManifest } from 'src/app/utils/get-package-data'

{{ - '"Uninstall service" is a dangerous action that will remove the service from StartOS and wipe all its data.' + '"Soft uninstall" will remove the service from StartOS but preserve its data.' + | i18n + }} +

+

+ {{ + '"Hard uninstall" is a dangerous action that will remove the service from StartOS and wipe all its data.' | i18n }}

@@ -43,8 +46,11 @@ import { getManifest } from 'src/app/utils/get-package-data' - + @if (overflow) { } + `, styles: ` - @import '@taiga-ui/core/styles/taiga-ui-local'; - - :host { - cursor: pointer; - clip-path: inset(0 round var(--tui-radius-m)); - @include transition(background); - } - - [tuiLink] { - background: transparent; - } - - @media ($tui-mouse) { - :host:hover { - background: var(--tui-background-neutral-1); - } - } - strong { white-space: nowrap; } tui-badge { text-transform: uppercase; + font-weight: bold; } tui-icon { font-size: 1rem; } + td:last-child { + grid-area: 3 / span 4; + white-space: nowrap; + text-align: right; + flex-direction: row-reverse; + justify-content: flex-end; + gap: 0.5rem; + } + :host-context(tui-root._mobile) { display: grid; - grid-template-columns: repeat(3, min-content) 1fr 2rem; + grid-template-columns: repeat(3, min-content) 1fr; align-items: center; padding: 1rem 0.5rem; gap: 0.5rem; td { + display: flex; padding: 0; } } `, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [TuiButton, TuiBadge, TuiLink, TuiIcon, RouterLink, i18nPipe], + imports: [TuiButton, TuiBadge, TuiIcon, RouterLink], }) -export class ServiceInterfaceComponent { +export class ServiceInterfaceItemComponent { private readonly config = inject(ConfigService) + private readonly document = inject(DOCUMENT) @Input({ required: true }) info!: T.ServiceInterface & { @@ -116,17 +108,19 @@ export class ServiceInterfaceComponent { get appearance(): string { switch (this.info.type) { case 'ui': - return 'primary' + return 'positive' case 'api': - return 'accent' + return 'info' case 'p2p': - return 'primary-grayscale' + return 'negative' } } - get href(): string | null { - return this.disabled - ? null - : this.config.launchableAddress(this.info, this.pkg.hosts) + get href() { + return this.config.launchableAddress(this.info, this.pkg.hosts) + } + + openUI() { + this.document.defaultView?.open(this.href, '_blank', 'noreferrer') } } diff --git a/web/projects/ui/src/app/routes/portal/routes/services/components/interfaces.component.ts b/web/projects/ui/src/app/routes/portal/routes/services/components/interfaces.component.ts index a4c649852..dc6358e16 100644 --- a/web/projects/ui/src/app/routes/portal/routes/services/components/interfaces.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/services/components/interfaces.component.ts @@ -5,13 +5,12 @@ import { inject, input, } from '@angular/core' -import { RouterLink } from '@angular/router' import { TuiTable } from '@taiga-ui/addon-table' import { tuiDefaultSort } from '@taiga-ui/cdk' import { ConfigService } from 'src/app/services/config.service' import { PackageDataEntry } from 'src/app/services/patch-db/data-model' import { getAddresses } from '../../../components/interfaces/interface.utils' -import { ServiceInterfaceComponent } from './interface.component' +import { ServiceInterfaceItemComponent } from './interface-item.component' import { i18nPipe } from '@start9labs/shared' @Component({ @@ -24,8 +23,8 @@ import { i18nPipe } from '@start9labs/shared' {{ 'Name' | i18n }} {{ 'Type' | i18n }} + {{ 'Hosting' | i18n }} {{ 'Description' | i18n }} - {{ 'Hosting' | i18n }} @@ -33,7 +32,6 @@ import { i18nPipe } from '@start9labs/shared' @for (info of interfaces(); track $index) { + - {{ title() }} + {{ pkgTitle() }} - @if (actionRequest().severity === 'critical') { + {{ pkg()?.actions?.[task().actionId]?.name }} + + + @if (task().severity === 'critical') { {{ 'Required' | i18n }} - } @else { + } @else if (task().severity === 'important') { + {{ 'Recommended' | i18n }} + + } @else { + {{ 'Optional' | i18n }} } - {{ actionRequest().reason || ('No reason provided' | i18n) }} + {{ task().reason || ('No reason provided' | i18n) }} - + @if (task().severity !== 'critical') { + + } + `, styles: ` td:first-child { white-space: nowrap; - max-width: 10rem; + max-width: 15rem; overflow: hidden; - text-overflow: ellipsis; } td:last-child { + grid-area: 3 / span 4; + white-space: nowrap; text-align: right; - grid-area: span 2; + flex-direction: row-reverse; + justify-content: flex-end; + gap: 0.5rem; } span { @@ -64,32 +92,66 @@ import { getManifest } from 'src/app/utils/get-package-data' :host-context(tui-root._mobile) { display: grid; - grid-template-columns: min-content 1fr min-content; align-items: center; - padding: 1rem 0.5rem; + padding: 1rem 0rem 1rem 0.5rem; gap: 0.5rem; td { + display: flex; + align-items: center; padding: 0; } } `, changeDetection: ChangeDetectionStrategy.OnPush, - imports: [TuiButton, TuiAvatar, i18nPipe], + imports: [TuiButton, TuiAvatar, i18nPipe, TuiFade], }) export class ServiceTaskComponent { private readonly actionService = inject(ActionService) + private readonly dialog = inject(DialogService) + private readonly api = inject(ApiService) + private readonly errorService = inject(ErrorService) + private readonly loader = inject(LoadingService) - readonly actionRequest = input.required() + readonly task = input.required() readonly services = input.required>() - readonly pkg = computed(() => this.services()[this.actionRequest().packageId]) - readonly title = computed((pkg = this.pkg()) => pkg && getManifest(pkg).title) + readonly pkg = computed(() => this.services()[this.task().packageId]) + readonly pkgTitle = computed( + (pkg = this.pkg()) => pkg && getManifest(pkg).title, + ) + + async dismiss() { + this.dialog + .openConfirm({ + label: 'Confirm', + size: 's', + data: { + content: 'Are you sure you want to dismiss this task?', + yes: 'Dismiss', + no: 'Cancel', + }, + }) + .pipe(filter(Boolean)) + .subscribe(async () => { + const loader = this.loader.open().subscribe() + try { + await this.api.clearTask({ + packageId: this.task().packageId, + replayId: this.task().replayId, + }) + } catch (e: any) { + this.errorService.handleError(e) + } finally { + loader.unsubscribe() + } + }) + } async handle() { - const title = this.title() + const title = this.pkgTitle() const pkg = this.pkg() - const metadata = pkg?.actions[this.actionRequest().actionId] + const metadata = pkg?.actions[this.task().actionId] if (!title || !pkg || !metadata) { return @@ -97,16 +159,16 @@ export class ServiceTaskComponent { this.actionService.present({ pkgInfo: { - id: this.actionRequest().packageId, + id: this.task().packageId, title, mainStatus: pkg.status.main, icon: pkg.icon, }, actionInfo: { - id: this.actionRequest().actionId, + id: this.task().actionId, metadata, }, - requestInfo: this.actionRequest(), + requestInfo: this.task(), }) } } diff --git a/web/projects/ui/src/app/routes/portal/routes/services/components/tasks.component.ts b/web/projects/ui/src/app/routes/portal/routes/services/components/tasks.component.ts index 6909d1776..b088a69a2 100644 --- a/web/projects/ui/src/app/routes/portal/routes/services/components/tasks.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/services/components/tasks.component.ts @@ -19,18 +19,19 @@ import { i18nPipe } from '@start9labs/shared' {{ 'Service' | i18n }} - {{ 'Type' | i18n }} + {{ 'Action' }} + {{ 'Severity' }} {{ 'Description' | i18n }} - @for (item of requests(); track $index) { - + @for (item of tasks(); track $index) { + } - @if (!requests().length) { + @if (!tasks().length) { {{ 'All tasks complete' | i18n }} @@ -50,8 +51,12 @@ export class ServiceTasksComponent { readonly pkg = input.required() readonly services = input.required>() - readonly requests = computed(() => - Object.values(this.pkg().tasks) + readonly tasks = computed(() => + Object.entries(this.pkg().tasks) + .map(([replayId, entry]) => ({ + ...entry, + task: { ...entry.task, replayId }, + })) .filter( t => this.services()[t.task.packageId]?.actions[t.task.actionId] && diff --git a/web/projects/ui/src/app/routes/portal/routes/services/components/uptime.component.ts b/web/projects/ui/src/app/routes/portal/routes/services/components/uptime.component.ts new file mode 100644 index 000000000..98ba27d9d --- /dev/null +++ b/web/projects/ui/src/app/routes/portal/routes/services/components/uptime.component.ts @@ -0,0 +1,90 @@ +import { AsyncPipe } from '@angular/common' +import { ChangeDetectionStrategy, Component, input } from '@angular/core' +import { i18nPipe } from '@start9labs/shared' +import { map, timer } from 'rxjs' +import { distinctUntilChanged } from 'rxjs/operators' + +@Component({ + selector: 'service-uptime', + template: ` +
{{ 'Uptime' | i18n }}
+
+ @if (uptime$ | async; as time) { +
+ + {{ 'Days' | i18n }} +
+
+ + {{ 'Hours' | i18n }} +
+
+ + {{ 'Minutes' | i18n }} +
+
+ + {{ 'Seconds' | i18n }} +
+ } +
+ `, + styles: [ + ` + :host { + grid-column: span 4; + } + + h3 { + font: var(--tui-font-heading-4); + font-weight: normal; + margin: 0; + text-align: center; + } + + section { + height: 100%; + max-width: 100%; + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 1rem; + place-content: center; + margin: auto; + padding: 1rem 0; + text-align: center; + text-transform: uppercase; + color: var(--tui-text-secondary); + font: var(--tui-font-text-ui-xs); + } + + label { + display: block; + font-size: min(6vw, 2.5rem); + margin: 1rem 0; + color: var(--tui-text-primary); + } + `, + ], + host: { class: 'g-card' }, + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [i18nPipe, AsyncPipe], +}) +export class ServiceUptimeComponent { + protected readonly uptime$ = timer(0, 1000).pipe( + map(() => + this.started() + ? Math.max(Date.now() - new Date(this.started()).getTime(), 0) + : 0, + ), + distinctUntilChanged(), + map(delta => ({ + seconds: Math.floor(delta / 1000) % 60, + minutes: Math.floor(delta / (1000 * 60)) % 60, + hours: Math.floor(delta / (1000 * 60 * 60)) % 24, + days: Math.floor(delta / (1000 * 60 * 60 * 24)), + })), + ) + + readonly started = input('') +} diff --git a/web/projects/ui/src/app/routes/portal/routes/services/dashboard/controls.component.ts b/web/projects/ui/src/app/routes/portal/routes/services/dashboard/controls.component.ts index fc4afcc79..c7f8866a5 100644 --- a/web/projects/ui/src/app/routes/portal/routes/services/dashboard/controls.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/services/dashboard/controls.component.ts @@ -14,7 +14,7 @@ import { DepErrorService } from 'src/app/services/dep-error.service' import { PackageDataEntry } from 'src/app/services/patch-db/data-model' import { renderPkgStatus } from 'src/app/services/pkg-status-rendering.service' import { getManifest } from 'src/app/utils/get-package-data' -import { UILaunchComponent } from './ui.component' +import { UILaunchComponent } from './ui-launch.component' import { i18nPipe } from '@start9labs/shared' const RUNNING = ['running', 'starting', 'restarting'] @@ -23,6 +23,7 @@ const RUNNING = ['running', 'starting', 'restarting'] standalone: true, selector: 'fieldset[appControls]', template: ` + @if (running()) { @@ -39,16 +40,15 @@ import { PackageDataEntry } from 'src/app/services/patch-db/data-model' } - } @else { - - {{ first?.name }} - + {{ interfaces[0].name }} + } `, styles: ` @@ -61,6 +61,7 @@ import { PackageDataEntry } from 'src/app/services/patch-db/data-model' }) export class UILaunchComponent { private readonly config = inject(ConfigService) + private readonly document = inject(DOCUMENT) @Input() pkg!: PackageDataEntry @@ -73,10 +74,6 @@ export class UILaunchComponent { return this.pkg.status.main === 'running' } - get first(): T.ServiceInterface | undefined { - return this.interfaces[0] - } - @tuiPure getInterfaces(pkg?: PackageDataEntry): T.ServiceInterface[] { return pkg @@ -89,9 +86,11 @@ export class UILaunchComponent { : [] } - getHref(ui?: T.ServiceInterface): string | null { - return ui && this.isRunning - ? this.config.launchableAddress(ui, this.pkg.hosts) - : null + getHref(ui: T.ServiceInterface): string { + return this.config.launchableAddress(ui, this.pkg.hosts) + } + + openUI(ui: T.ServiceInterface) { + this.document.defaultView?.open(this.getHref(ui), '_blank', 'noreferrer') } } diff --git a/web/projects/ui/src/app/routes/portal/routes/services/modals/action-success/action-success-single.component.ts b/web/projects/ui/src/app/routes/portal/routes/services/modals/action-success/action-success-single.component.ts index 1843906e5..749721a34 100644 --- a/web/projects/ui/src/app/routes/portal/routes/services/modals/action-success/action-success-single.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/services/modals/action-success/action-success-single.component.ts @@ -21,7 +21,9 @@ import { i18nPipe } from '@start9labs/shared' standalone: true, selector: 'app-action-success-single', template: ` -

+ @if (single.qr) { +

+ } {{ interface()?.name }} - + - + {{ 'Dashboard' | i18n }} - - {{ interface()?.name }} - - + {{ interface()?.name }} - @if (interface(); as serviceInterface) { + @if (interface(); as value) { +
+
+

+ {{ value.name }} + + {{ value.type }} + + +

+

{{ value.description }}

+
+
} `, @@ -48,6 +63,19 @@ import { TitleDirective } from 'src/app/services/title.service' :host-context(tui-root._mobile) tui-breadcrumbs { display: none; } + + h3 { + display: flex; + align-items: center; + gap: 0.5rem; + margin: 1rem 0 0.5rem 0; + font-size: 2.4rem; + + tui-badge { + text-transform: uppercase; + font-weight: bold; + } + } `, host: { class: 'g-subpage' }, changeDetection: ChangeDetectionStrategy.OnPush, @@ -62,6 +90,8 @@ import { TitleDirective } from 'src/app/services/title.service' TuiLink, InterfaceStatusComponent, i18nPipe, + TuiBadge, + TuiHeader, ], }) export default class ServiceInterfaceRoute { @@ -74,6 +104,10 @@ export default class ServiceInterfaceRoute { inject>(PatchDB).watch$('packageData', this.pkgId), ) + readonly isRunning = computed(() => { + return this.pkg()?.status.main === 'running' + }) + readonly interface = computed(() => { const pkg = this.pkg() const id = this.interfaceId() @@ -99,4 +133,15 @@ export default class ServiceInterfaceRoute { addresses: getAddresses(item, host, this.config), } }) + + getAppearance(type: T.ServiceInterfaceType = 'ui'): string { + switch (type) { + case 'ui': + return 'positive' + case 'api': + return 'info' + case 'p2p': + return 'negative' + } + } } diff --git a/web/projects/ui/src/app/routes/portal/routes/services/routes/outlet.component.ts b/web/projects/ui/src/app/routes/portal/routes/services/routes/outlet.component.ts index a623cb3b4..13195554d 100644 --- a/web/projects/ui/src/app/routes/portal/routes/services/routes/outlet.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/services/routes/outlet.component.ts @@ -13,9 +13,21 @@ import { TuiCell } from '@taiga-ui/layout' import { PatchDB } from 'patch-db-client' import { distinctUntilChanged, filter, map, switchMap, tap } from 'rxjs' import { DataModel } from 'src/app/services/patch-db/data-model' +import { + PrimaryStatus, + renderPkgStatus, +} from 'src/app/services/pkg-status-rendering.service' import { TitleDirective } from 'src/app/services/title.service' import { getManifest } from 'src/app/utils/get-package-data' +const INACTIVE: PrimaryStatus[] = [ + 'installing', + 'updating', + 'removing', + 'restoring', + 'backingUp', +] + @Component({ template: ` @if (service()) { @@ -34,7 +46,7 @@ import { getManifest } from 'src/app/utils/get-package-data' {{ manifest()?.version }} -

+ } @empty { + + {{ 'No saved providers' | i18n }} + } } @else { @@ -113,6 +118,7 @@ import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec' TitleDirective, i18nPipe, DocsLinkDirective, + PlaceholderComponent, ], }) export default class SystemAcmeComponent { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/email/email.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/email/email.component.ts index 2dfe9ff71..27beaf870 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/email/email.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/email/email.component.ts @@ -75,7 +75,7 @@ import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec' @@ -134,6 +138,43 @@ import { SystemWipeComponent } from './wipe.component' {{ 'Download' | i18n }} +
+ + + + {{ 'Kiosk Mode' | i18n }} + + {{ server.kiosk ? ('Enabled' | i18n) : ('Disabled' | i18n) }} + + + + {{ + server.kiosk === true + ? ('Disable Kiosk Mode unless you need to attach a monitor' + | i18n) + : server.kiosk === false + ? ('Enable Kiosk Mode if you need to attach a monitor' | i18n) + : ('Kiosk Mode is unavailable on this device' | i18n) + }} + + + @if (server.kiosk !== null) { + + } +
@@ -190,11 +231,6 @@ import { SystemWipeComponent } from './wipe.component' [tuiCell] { background: var(--tui-background-neutral-1); } - - [tuiSubtitle], - tui-data-list-wrapper ::ng-deep [tuiOption] { - text-transform: capitalize; - } `, providers: [tuiCellOptionsProvider({ height: 'spacious' })], changeDetection: ChangeDetectionStrategy.OnPush, @@ -217,9 +253,11 @@ import { SystemWipeComponent } from './wipe.component' TuiTextfield, FormsModule, SnekDirective, + TuiBadge, ], }) export default class SystemGeneralComponent { + private readonly title = inject(Title) private readonly dialogs = inject(TuiResponsiveDialogService) private readonly loader = inject(LoadingService) private readonly errorService = inject(ErrorService) @@ -276,9 +314,11 @@ export default class SystemGeneralComponent { }) .subscribe(async name => { const loader = this.loader.open('Saving').subscribe() + const title = `${name || 'StartOS'} — ${this.i18n.transform('System')}` try { await this.api.setDbValue(['name'], name || null) + this.title.setTitle(title) } catch (e: any) { this.errorService.handleError(e) } finally { @@ -310,6 +350,28 @@ export default class SystemGeneralComponent { this.document.getElementById('download-ca')?.click() } + async tryToggleKiosk() { + if ( + this.server()?.kiosk && + ['localhost', '127.0.0.1'].includes(this.document.location.hostname) + ) { + return this.dialog + .openConfirm({ + label: 'Warning', + data: { + content: + 'You are currently using a kiosk. Disabling Kiosk Mode will result in the kiosk disconnecting.', + yes: 'Disable', + no: 'Cancel', + }, + }) + .pipe(filter(Boolean)) + .subscribe(async () => this.toggleKiosk()) + } + + this.toggleKiosk() + } + async onRepair() { this.dialog .openConfirm({ @@ -332,6 +394,22 @@ export default class SystemGeneralComponent { }) } + private async toggleKiosk() { + const kiosk = this.server()?.kiosk + + const loader = this.loader + .open(kiosk ? 'Disabling' : 'Enabling') + .subscribe() + + try { + await this.api.toggleKiosk(!kiosk) + } catch (e: any) { + this.errorService.handleError(e) + } finally { + loader.unsubscribe() + } + } + private async resetTor(wipeState: boolean) { const loader = this.loader.open('Resetting Tor').subscribe() diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/general/update.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/general/update.component.ts index 3226b6e28..0bca7633f 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/general/update.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/general/update.component.ts @@ -24,7 +24,7 @@ import { firstValueFrom } from 'rxjs' template: `

StartOS {{ versions[0]?.version }}

- {{ 'Release Notes' | i18n }} + {{ 'Release notes' | i18n }}

@for (v of versions; track $index) { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/sessions/sessions.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/sessions/sessions.component.ts index 465e23665..05d40be49 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/sessions/sessions.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/sessions/sessions.component.ts @@ -1,13 +1,18 @@ +import { CommonModule } from '@angular/common' +import { + ChangeDetectionStrategy, + Component, + inject, + viewChild, +} from '@angular/core' import { RouterLink } from '@angular/router' +import { ErrorService, i18nPipe, LoadingService } from '@start9labs/shared' import { TuiLet } from '@taiga-ui/cdk' import { TuiButton, TuiTitle } from '@taiga-ui/core' -import { CommonModule } from '@angular/common' -import { ChangeDetectionStrategy, Component, inject } from '@angular/core' -import { ErrorService, i18nPipe, LoadingService } from '@start9labs/shared' import { TuiHeader } from '@taiga-ui/layout' import { from, map, merge, Observable, Subject } from 'rxjs' -import { ApiService } from 'src/app/services/api/embassy-api.service' import { Session } from 'src/app/services/api/api.types' +import { ApiService } from 'src/app/services/api/embassy-api.service' import { TitleDirective } from 'src/app/services/title.service' import { SessionsTableComponent } from './table.component' @@ -36,18 +41,16 @@ import { SessionsTableComponent } from './table.component'
{{ 'Other sessions' | i18n }} - @if (table.selected$ | async; as selected) { - - } +
@@ -73,6 +76,8 @@ export default class SystemSessionsComponent { private readonly sessions$ = from(this.api.getSessions({})) private readonly local$ = new Subject() + protected sessions = viewChild>('table') + readonly current$ = this.sessions$.pipe( map(s => { const current = s.sessions[s.current] @@ -99,16 +104,14 @@ export default class SystemSessionsComponent { ), ) - async terminate( - sessions: readonly SessionWithId[], - all: readonly SessionWithId[], - ) { - const ids = sessions.map(s => s.id) + async terminate(all: readonly SessionWithId[]) { + const ids = this.sessions()?.selected$.value.map(s => s.id) || [] const loader = this.loader.open('Terminating sessions').subscribe() try { await this.api.killSessions({ ids }) this.local$.next(all.filter(s => !ids.includes(s.id))) + this.sessions()?.selected$.next([]) } catch (e: any) { this.errorService.handleError(e) } finally { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/sessions/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/sessions/table.component.ts index ae9a83165..069a95e7a 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/sessions/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/sessions/table.component.ts @@ -31,7 +31,7 @@ import { i18nPipe } from '@start9labs/shared' (ngModelChange)="onToggle(session)" /> } - {{ session.userAgent }} + {{ session.userAgent || '-' }} @if (session.userAgent | platformInfo; as platform) { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/interfaces/interfaces.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/startos-ui/startos-ui.component.ts similarity index 85% rename from web/projects/ui/src/app/routes/portal/routes/system/routes/interfaces/interfaces.component.ts rename to web/projects/ui/src/app/routes/portal/routes/system/routes/startos-ui/startos-ui.component.ts index a185ce1f7..0654ec305 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/interfaces/interfaces.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/startos-ui/startos-ui.component.ts @@ -25,11 +25,11 @@ import { TitleDirective } from 'src/app/services/title.service'
{{ 'Back' | i18n }} - StartOS UI - + {{ iface.name }} +
-
+

{{ iface.name }} @@ -38,9 +38,24 @@ import { TitleDirective } from 'src/app/services/title.service'

@if (ui(); as ui) { - + } `, + styles: ` + h3 { + display: flex; + align-items: center; + gap: 0.5rem; + margin: 1rem 0 0.5rem 0; + font-size: 2.4rem; + + tui-badge { + text-transform: uppercase; + font-weight: bold; + } + } + `, + host: { class: 'g-subpage' }, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ @@ -49,7 +64,6 @@ import { TitleDirective } from 'src/app/services/title.service' TuiButton, TitleDirective, TuiHeader, - TuiTitle, InterfaceStatusComponent, i18nPipe, ], diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/wifi/wifi.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/wifi/wifi.component.ts index 4e4335520..719a98e70 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/wifi/wifi.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/wifi/wifi.component.ts @@ -149,7 +149,7 @@ export default class SystemWifiComponent { async onToggle(enable: boolean) { const loader = this.loader - .open(enable ? 'Enabling WiFi' : 'Disabling WiFi') + .open(enable ? 'Enabling' : 'Disabling') .subscribe() try { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/system.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/system.component.ts index 385f016ed..a62ff504e 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/system.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/system.component.ts @@ -11,6 +11,7 @@ import { BadgeService } from 'src/app/services/badge.service' import { DataModel } from 'src/app/services/patch-db/data-model' import { TitleDirective } from 'src/app/services/title.service' import { SYSTEM_MENU } from './system.const' +import { map } from 'rxjs' @Component({ template: ` @@ -128,10 +129,7 @@ import { SYSTEM_MENU } from './system.const' export class SystemComponent { readonly menu = SYSTEM_MENU readonly badge = toSignal(inject(BadgeService).getCount('/portal/system')) - readonly wifiEnabled$ = inject>(PatchDB).watch$( - 'serverInfo', - 'network', - 'wifi', - 'enabled', - ) + readonly wifiEnabled$ = inject>(PatchDB) + .watch$('serverInfo', 'network', 'wifi') + .pipe(map(wifi => !!wifi.interface && wifi.enabled)) } diff --git a/web/projects/ui/src/app/routes/portal/routes/system/system.const.ts b/web/projects/ui/src/app/routes/portal/routes/system/system.const.ts index c6d0ff2ec..0bbb161b9 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/system.const.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/system.const.ts @@ -7,11 +7,6 @@ export const SYSTEM_MENU = [ item: 'General', link: 'general', }, - { - icon: '@tui.mail', - item: 'Email', - link: 'email', - }, ], [ { @@ -36,6 +31,11 @@ export const SYSTEM_MENU = [ item: 'ACME', link: 'acme', }, + { + icon: '@tui.mail', + item: 'Email', + link: 'email', + }, { icon: '@tui.wifi', item: 'WiFi', diff --git a/web/projects/ui/src/app/routes/portal/routes/system/system.routes.ts b/web/projects/ui/src/app/routes/portal/routes/system/system.routes.ts index 18e43c31d..ff05793c1 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/system.routes.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/system.routes.ts @@ -6,6 +6,7 @@ import { Routes, } from '@angular/router' import { TUI_IS_MOBILE } from '@taiga-ui/cdk' +import { titleResolver } from 'src/app/utils/title-resolver' import { SystemComponent } from './system.component' export default [ @@ -21,40 +22,49 @@ export default [ children: [ { path: 'general', + title: titleResolver, loadComponent: () => import('./routes/general/general.component'), }, { path: 'email', + title: titleResolver, loadComponent: () => import('./routes/email/email.component'), }, { path: 'backup', + title: titleResolver, loadComponent: () => import('./routes/backups/backups.component'), data: { type: 'create' }, }, { path: 'restore', + title: titleResolver, loadComponent: () => import('./routes/backups/backups.component'), data: { type: 'restore' }, }, { path: 'interfaces', - loadComponent: () => import('./routes/interfaces/interfaces.component'), + title: titleResolver, + loadComponent: () => import('./routes/startos-ui/startos-ui.component'), }, { path: 'acme', + title: titleResolver, loadComponent: () => import('./routes/acme/acme.component'), }, { path: 'wifi', + title: titleResolver, loadComponent: () => import('./routes/wifi/wifi.component'), }, { path: 'sessions', + title: titleResolver, loadComponent: () => import('./routes/sessions/sessions.component'), }, { path: 'password', + title: titleResolver, loadComponent: () => import('./routes/password/password.component'), }, // { 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 6f994d69e..f18f671bd 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 @@ -212,6 +212,11 @@ import UpdatesComponent from './updates.component' .mobile { display: flex; + gap: 0.25rem; + + [tuiSubtitle] { + color: var(--tui-text-secondary); + } } } `, diff --git a/web/projects/ui/src/app/routes/portal/routes/updates/updates.component.ts b/web/projects/ui/src/app/routes/portal/routes/updates/updates.component.ts index 9640318b0..27b6b5842 100644 --- a/web/projects/ui/src/app/routes/portal/routes/updates/updates.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/updates/updates.component.ts @@ -21,6 +21,7 @@ import { import { TuiCell } from '@taiga-ui/layout' import { PatchDB } from 'patch-db-client' import { combineLatest, map, tap } from 'rxjs' +import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component' import { TableComponent } from 'src/app/routes/portal/components/table.component' import { MarketplaceService } from 'src/app/services/marketplace.service' import { @@ -112,7 +113,9 @@ interface UpdatesData { } @empty { - {{ 'All services are up to date!' | i18n }} + + {{ 'All services are up to date!' | i18n }} + } @@ -161,6 +164,11 @@ interface UpdatesData { clip-path: inset(0.5rem round var(--tui-radius-s)); } + .g-subpage, + .g-card { + overflow: auto; + } + :host-context(tui-root._mobile) { aside { width: 100%; @@ -214,6 +222,7 @@ interface UpdatesData { TitleDirective, TableComponent, i18nPipe, + PlaceholderComponent, ], }) export default class UpdatesComponent { @@ -224,7 +233,7 @@ export default class UpdatesComponent { readonly data = toSignal( combineLatest({ - hosts: this.marketplaceService.filteredRegistries$.pipe( + hosts: this.marketplaceService.registries$.pipe( tap( ([registry]) => !this.isMobile && registry && this.current.set(registry), diff --git a/web/projects/ui/src/app/routing.module.ts b/web/projects/ui/src/app/routing.module.ts index 94d0f05da..dd0432b57 100644 --- a/web/projects/ui/src/app/routing.module.ts +++ b/web/projects/ui/src/app/routing.module.ts @@ -27,7 +27,7 @@ const routes: Routes = [ loadChildren: () => import('./routes/portal/portal.routes'), }, { - path: '', + path: '**', redirectTo: 'portal', pathMatch: 'full', }, diff --git a/web/projects/ui/src/app/services/api/api.fixures.ts b/web/projects/ui/src/app/services/api/api.fixures.ts index 2f188a904..0bfb4fa87 100644 --- a/web/projects/ui/src/app/services/api/api.fixures.ts +++ b/web/projects/ui/src/app/services/api/api.fixures.ts @@ -18,7 +18,7 @@ const mockMerkleArchiveCommitment: T.MerkleArchiveCommitment = { const mockDescription = { short: 'Lorem ipsum dolor sit amet', - long: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + long: 'Lorem ipsum dolor sit amet,

consectetur adipiscing elit

, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', } export namespace Mock { @@ -632,55 +632,6 @@ export namespace Mock { }, }, }, - 'btc-rpc-proxy': { - '=0.3.2.6:0': { - best: { - '0.3.2.6:0': { - title: 'Bitcoin Proxy', - description: mockDescription, - hardwareRequirements: { arch: null, device: [], ram: null }, - license: 'mit', - wrapperRepo: 'https://github.com/Start9Labs/btc-rpc-proxy-wrappers', - upstreamRepo: 'https://github.com/Kixunil/btc-rpc-proxy', - supportSite: 'https://github.com/Kixunil/btc-rpc-proxy/issues', - marketingSite: '', - releaseNotes: 'Upstream release and minor fixes.', - osVersion: '0.3.6', - gitHash: 'fakehash', - icon: PROXY_ICON, - sourceVersion: null, - dependencyMetadata: { - bitcoind: { - title: 'Bitcoin Core', - icon: BTC_ICON, - description: 'Used for RPC requests', - optional: false, - }, - }, - donationUrl: null, - alerts: { - install: 'test', - uninstall: 'test', - start: 'test', - stop: 'test', - restore: 'test', - }, - s9pk: { - url: 'https://github.com/Start9Labs/btc-rpc-proxy-startos/releases/download/v0.3.2.7.1/btc-rpc-proxy.s9pk', - commitment: mockMerkleArchiveCommitment, - signatures: {}, - publishedAt: Date.now().toString(), - }, - }, - }, - categories: ['bitcoin'], - otherVersions: { - '0.3.2.7:0': { - releaseNotes: 'Upstream release and minor fixes.', - }, - }, - }, - }, } export const RegistryPackages: GetPackagesRes = { @@ -857,11 +808,7 @@ export namespace Mock { }, }, categories: ['bitcoin'], - otherVersions: { - '0.3.2.6:0': { - releaseNotes: 'Upstream release and minor fixes.', - }, - }, + otherVersions: {}, }, } @@ -891,7 +838,7 @@ export namespace Mock { id: 2, packageId: null, createdAt: '2019-12-26T14:20:30.872Z', - code: 2, + code: 0, level: 'warning', title: 'SSH Key Added', message: 'A new SSH key was added. If you did not do this, shit is bad.', @@ -902,7 +849,7 @@ export namespace Mock { id: 3, packageId: null, createdAt: '2019-12-26T14:20:30.872Z', - code: 3, + code: 0, level: 'info', title: 'SSH Key Removed', message: 'A SSH key was removed.', @@ -913,7 +860,7 @@ export namespace Mock { id: 4, packageId: 'bitcoind', createdAt: '2019-12-26T14:20:30.872Z', - code: 4, + code: 0, level: 'error', title: 'Service Crashed', message: new Array(3) @@ -1339,7 +1286,7 @@ export namespace Mock { result: { type: 'single', copyable: true, - qr: true, + qr: false, masked: true, value: 'iwejdoiewdhbew', }, diff --git a/web/projects/ui/src/app/services/api/api.types.ts b/web/projects/ui/src/app/services/api/api.types.ts index 9db729445..72f36b33d 100644 --- a/web/projects/ui/src/app/services/api/api.types.ts +++ b/web/projects/ui/src/app/services/api/api.types.ts @@ -335,6 +335,12 @@ export namespace RR { } // package.action.run export type ActionRes = (T.ActionResult & { version: '1' }) | null + export type ClearTaskReq = { + packageId: string + replayId: string + } // package.action.clear-task + export type ClearTaskRes = null + export type RestorePackagesReq = { // package.backup.restore ids: string[] @@ -356,7 +362,11 @@ export namespace RR { export type RebuildPackageReq = { id: string } // package.rebuild export type RebuildPackageRes = null - export type UninstallPackageReq = { id: string } // package.uninstall + export type UninstallPackageReq = { + id: string + force: boolean + soft: boolean + } // package.uninstall export type UninstallPackageRes = null export type SideloadPackageReq = { diff --git a/web/projects/ui/src/app/services/api/embassy-api.service.ts b/web/projects/ui/src/app/services/api/embassy-api.service.ts index 0730b9d3d..98ac3a021 100644 --- a/web/projects/ui/src/app/services/api/embassy-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-api.service.ts @@ -120,6 +120,8 @@ export abstract class ApiService { abstract repairDisk(params: RR.DiskRepairReq): Promise + abstract toggleKiosk(enable: boolean): Promise + abstract resetTor(params: RR.ResetTorReq): Promise // @TODO 041 @@ -335,6 +337,8 @@ export abstract class ApiService { abstract runAction(params: RR.ActionReq): Promise + abstract clearTask(params: RR.ClearTaskReq): Promise + abstract restorePackages( params: RR.RestorePackagesReq, ): Promise diff --git a/web/projects/ui/src/app/services/api/embassy-live-api.service.ts b/web/projects/ui/src/app/services/api/embassy-live-api.service.ts index c848d16b0..8bd4b7b4b 100644 --- a/web/projects/ui/src/app/services/api/embassy-live-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-live-api.service.ts @@ -261,6 +261,13 @@ export class LiveApiService extends ApiService { return this.rpcRequest({ method: 'disk.repair', params }) } + async toggleKiosk(enable: boolean): Promise { + return this.rpcRequest({ + method: enable ? 'kiosk.enable' : 'kiosk.disable', + params: {}, + }) + } + async resetTor(params: RR.ResetTorReq): Promise { return this.rpcRequest({ method: 'net.tor.reset', params }) } @@ -577,6 +584,10 @@ export class LiveApiService extends ApiService { return this.rpcRequest({ method: 'package.action.run', params }) } + async clearTask(params: RR.ClearTaskReq): Promise { + return this.rpcRequest({ method: 'package.action.clear-task', params }) + } + async restorePackages( params: RR.RestorePackagesReq, ): Promise { diff --git a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts index a5ecda239..ba212939a 100644 --- a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts @@ -22,11 +22,7 @@ import { from, interval, map, shareReplay, startWith, Subject, tap } from 'rxjs' import { mockPatchData } from './mock-patch' import { AuthService } from '../auth.service' import { T } from '@start9labs/start-sdk' -import { - GetPackageRes, - GetPackagesRes, - MarketplacePkg, -} from '@start9labs/marketplace' +import { MarketplacePkg } from '@start9labs/marketplace' import markdown from 'raw-loader!../../../../../shared/assets/markdown/md-sample.md' import { WebSocketSubject } from 'rxjs/webSocket' import { toAcmeUrl } from 'src/app/utils/acme' @@ -166,7 +162,6 @@ export class MockApiService extends ApiService { pathArr: Array, value: T, ): Promise { - console.warn(pathArr, value) const pointer = pathFromArray(pathArr) const params: RR.SetDBValueReq = { pointer, value } await pauseFor(2000) @@ -449,6 +444,21 @@ export class MockApiService extends ApiService { return null } + async toggleKiosk(enable: boolean): Promise { + await pauseFor(2000) + + const patch = [ + { + op: PatchOp.REPLACE, + path: '/serverInfo/kiosk', + value: enable, + }, + ] + this.mockRevision(patch) + + return null + } + async resetTor(params: RR.ResetTorReq): Promise { await pauseFor(2000) return null @@ -1103,23 +1113,32 @@ export class MockApiService extends ApiService { async runAction(params: RR.ActionReq): Promise { await pauseFor(2000) - if (params.actionId === 'properties') { - // return Mock.ActionResGroup - return Mock.ActionResMessage - // return Mock.ActionResSingle - } else if (params.actionId === 'config') { - const patch: RemoveOperation[] = [ - { - op: PatchOp.REMOVE, - path: `/packageData/${params.packageId}/requestedActions/${params.packageId}-config`, - }, - ] - this.mockRevision(patch) - return null - } else { - return Mock.ActionResMessage - // return Mock.ActionResSingle - } + const patch: ReplaceOperation<{ [key: string]: T.TaskEntry }>[] = [ + { + op: PatchOp.REPLACE, + path: `/packageData/${params.packageId}/tasks`, + value: {}, + }, + ] + this.mockRevision(patch) + + // return Mock.ActionResGroup + return Mock.ActionResMessage + // return Mock.ActionResSingle + } + + async clearTask(params: RR.ClearTaskReq): Promise { + await pauseFor(2000) + + const patch: RemoveOperation[] = [ + { + op: PatchOp.REMOVE, + path: `/packageData/${params.packageId}/tasks/${params.replayId}`, + }, + ] + this.mockRevision(patch) + + return null } async restorePackages( diff --git a/web/projects/ui/src/app/services/api/mock-patch.ts b/web/projects/ui/src/app/services/api/mock-patch.ts index d30b2742f..3f75bf850 100644 --- a/web/projects/ui/src/app/services/api/mock-patch.ts +++ b/web/projects/ui/src/app/services/api/mock-patch.ts @@ -183,13 +183,7 @@ export const mockPatchData: DataModel = { pubkey: 'npub1sg6plzptd64u62a878hep2kev88swjh3tw00gjsfl8f237lmu63q0uf63m', caFingerprint: '63:2B:11:99:44:40:17:DF:37:FC:C3:DF:0F:3D:15', ntpSynced: false, - smtp: { - server: '', - port: 587, - from: '', - login: '', - password: '', - }, + smtp: null, platform: 'x86_64-nonfree', zram: true, governor: 'performance', @@ -221,7 +215,7 @@ export const mockPatchData: DataModel = { actions: { config: { name: 'Set Config', - description: 'edit bitcoin.conf', + description: 'edit bitcoin.conf, soo cool!', warning: null, visibility: 'enabled', allowedStatuses: 'any', diff --git a/web/projects/ui/src/app/services/client-storage.service.ts b/web/projects/ui/src/app/services/client-storage.service.ts index 9f4f09da1..c70d6e9dc 100644 --- a/web/projects/ui/src/app/services/client-storage.service.ts +++ b/web/projects/ui/src/app/services/client-storage.service.ts @@ -1,22 +1,18 @@ -import { Injectable } from '@angular/core' +import { inject, Injectable } from '@angular/core' import { ReplaySubject } from 'rxjs' import { StorageService } from './storage.service' const SHOW_DEV_TOOLS = 'SHOW_DEV_TOOLS' -const SHOW_DISK_REPAIR = 'SHOW_DISK_REPAIR' @Injectable({ providedIn: 'root', }) export class ClientStorageService { + private readonly storage = inject(StorageService) readonly showDevTools$ = new ReplaySubject(1) - readonly showDiskRepair$ = new ReplaySubject(1) - - constructor(private readonly storage: StorageService) {} init() { this.showDevTools$.next(!!this.storage.get(SHOW_DEV_TOOLS)) - this.showDiskRepair$.next(!!this.storage.get(SHOW_DISK_REPAIR)) } toggleShowDevTools(): boolean { @@ -25,11 +21,4 @@ export class ClientStorageService { this.showDevTools$.next(newVal) return newVal } - - toggleShowDiskRepair(): boolean { - const newVal = !this.storage.get(SHOW_DISK_REPAIR) - this.storage.set(SHOW_DISK_REPAIR, newVal) - this.showDiskRepair$.next(newVal) - return newVal - } } diff --git a/web/projects/ui/src/app/services/marketplace.service.ts b/web/projects/ui/src/app/services/marketplace.service.ts index 57b41a02d..4172bff9c 100644 --- a/web/projects/ui/src/app/services/marketplace.service.ts +++ b/web/projects/ui/src/app/services/marketplace.service.ts @@ -31,7 +31,6 @@ import { import { RR } from 'src/app/services/api/api.types' import { ApiService } from 'src/app/services/api/embassy-api.service' import { DataModel } from 'src/app/services/patch-db/data-model' -import { ClientStorageService } from './client-storage.service' const { start9, community } = defaultRegistries @@ -55,20 +54,6 @@ export class MarketplaceService { ]), ) - // option to filter out hosts containing 'alpha' or 'beta' substrings in registryURL - readonly filteredRegistries$: Observable = combineLatest([ - inject(ClientStorageService).showDevTools$, - this.registries$, - ]).pipe( - map(([devMode, registries]) => - devMode - ? registries - : registries.filter( - ({ url }) => !url.includes('alpha') && !url.includes('beta'), - ), - ), - ) - readonly currentRegistryUrl$ = new ReplaySubject(1) readonly requestErrors$ = new BehaviorSubject([]) @@ -252,7 +237,6 @@ export class MarketplaceService { oldName: string | null, newName: string, ): Promise { - console.warn(oldName, newName) if (oldName !== newName) { this.api.setDbValue(['registries', url], newName) } diff --git a/web/projects/ui/src/app/services/notification.service.ts b/web/projects/ui/src/app/services/notification.service.ts index 156a5fa6e..270ae4f10 100644 --- a/web/projects/ui/src/app/services/notification.service.ts +++ b/web/projects/ui/src/app/services/notification.service.ts @@ -93,7 +93,7 @@ export class NotificationService { { data, createdAt, code, title, message }: ServerNotification, full = false, ) { - const label = full || code === 2 ? title : 'Backup Report' + const label = code === 1 ? 'Backup Report' : title const component = code === 1 ? REPORT : MARKDOWN const content = code === 1 ? data : of(data) @@ -104,6 +104,7 @@ export class NotificationService { content, timestamp: createdAt, }, + size: code === 1 ? 'm' : 'l', }) .subscribe() } diff --git a/web/projects/ui/src/app/services/pkg-status-rendering.service.ts b/web/projects/ui/src/app/services/pkg-status-rendering.service.ts index 6050e02e4..d68d38726 100644 --- a/web/projects/ui/src/app/services/pkg-status-rendering.service.ts +++ b/web/projects/ui/src/app/services/pkg-status-rendering.service.ts @@ -1,31 +1,24 @@ -import { PackageDataEntry } from 'src/app/services/patch-db/data-model' -import { PkgDependencyErrors } from './dep-error.service' -import { T } from '@start9labs/start-sdk' import { i18nKey } from '@start9labs/shared' +import { T } from '@start9labs/start-sdk' +import { PackageDataEntry } from 'src/app/services/patch-db/data-model' export interface PackageStatus { primary: PrimaryStatus - dependency: DependencyStatus | null health: T.HealthStatus | null } -export function renderPkgStatus( - pkg: PackageDataEntry, - depErrors: PkgDependencyErrors = {}, -): PackageStatus { +export function renderPkgStatus(pkg: PackageDataEntry): PackageStatus { let primary: PrimaryStatus - let dependency: DependencyStatus | null = null let health: T.HealthStatus | null = null if (pkg.stateInfo.state === 'installed') { primary = getInstalledPrimaryStatus(pkg) - dependency = getDependencyStatus(depErrors) health = getHealthStatus(pkg.status) } else { primary = pkg.stateInfo.state } - return { primary, dependency, health } + return { primary, health } } export function getInstalledPrimaryStatus({ @@ -39,10 +32,6 @@ export function getInstalledPrimaryStatus({ : status.main } -function getDependencyStatus(depErrors: PkgDependencyErrors): DependencyStatus { - return Object.values(depErrors).some(err => !!err) ? 'warning' : 'satisfied' -} - function getHealthStatus(status: T.MainStatus): T.HealthStatus | null { if (status.main !== 'running' || !status.main) { return null diff --git a/web/projects/ui/src/app/services/standard-actions.service.ts b/web/projects/ui/src/app/services/standard-actions.service.ts index f6cffcb20..1694cd235 100644 --- a/web/projects/ui/src/app/services/standard-actions.service.ts +++ b/web/projects/ui/src/app/services/standard-actions.service.ts @@ -14,6 +14,7 @@ import { getAllPackages } from '../utils/get-package-data' import { hasCurrentDeps } from '../utils/has-deps' import { ApiService } from './api/embassy-api.service' import { DataModel } from './patch-db/data-model' +import { RR } from './api/api.types' @Injectable({ providedIn: 'root', @@ -40,13 +41,20 @@ export class StandardActionsService { } } - async uninstall({ id, title, alerts }: T.Manifest): Promise { - let content = - alerts.uninstall || - `${this.i18n.transform('Uninstalling')} ${title} ${this.i18n.transform('will permanently delete its data.')}` + async uninstall( + { id, title, alerts }: T.Manifest, + { force, soft }: { force: boolean; soft: boolean } = { + force: false, + soft: false, + }, + ): Promise { + let content = soft + ? '' + : alerts.uninstall || + `${this.i18n.transform('Uninstalling')} ${title} ${this.i18n.transform('will permanently delete its data.')}` if (hasCurrentDeps(id, await getAllPackages(this.patch))) { - content = `${content}. ${this.i18n.transform('Services that depend on')} ${title} ${this.i18n.transform('will no longer work properly and may crash.')}` + content = `${content}${content ? ' ' : ''}${this.i18n.transform('Services that depend on')} ${title} ${this.i18n.transform('will no longer work properly and may crash.')}` } this.dialog @@ -60,17 +68,15 @@ export class StandardActionsService { }, }) .pipe(filter(Boolean)) - .subscribe(() => this.doUninstall(id)) + .subscribe(() => this.doUninstall({ id, force, soft })) } - private async doUninstall(id: string) { + private async doUninstall(options: RR.UninstallPackageReq) { const loader = this.loader.open('Beginning uninstall').subscribe() try { - await this.api.uninstallPackage({ id }) - await this.api - .setDbValue(['ackInstructions', id], false) - .catch(e => console.error('Failed to mark instructions as unseen', e)) + await this.api.uninstallPackage(options) + await this.api.setDbValue(['ackInstructions', options.id], false) await this.router.navigate(['portal']) } catch (e: any) { this.errorService.handleError(e) diff --git a/web/projects/ui/src/index.html b/web/projects/ui/src/index.html index ba11b1393..c337fbcd3 100644 --- a/web/projects/ui/src/index.html +++ b/web/projects/ui/src/index.html @@ -1,4 +1,4 @@ - + @@ -55,11 +55,7 @@ - Start OS +

Loading

diff --git a/web/projects/ui/src/styles.scss b/web/projects/ui/src/styles.scss index d2dd46692..fd51ef895 100644 --- a/web/projects/ui/src/styles.scss +++ b/web/projects/ui/src/styles.scss @@ -102,6 +102,7 @@ hr { padding: 3.25rem 1rem 0.375rem; border-radius: 0.5rem; overflow: hidden; + color: var(--tui-text-primary); background-color: color-mix(in hsl, var(--start9-base-1) 50%, transparent); background-image: linear-gradient( to bottom,
- @if (interface.serviceInterface().type === 'ui') { - - {{ 'Launch UI' | i18n }} - + {{ 'Open' | i18n }} + } - @@ -55,27 +62,26 @@ import { InterfaceComponent } from './interface.component' - @if (interface.serviceInterface().type === 'ui') { - - {{ 'Launch UI' | i18n }} - - + @if (interface.value().type === 'ui') { } + + @@ -110,20 +116,27 @@ import { InterfaceComponent } from './interface.component' changeDetection: ChangeDetectionStrategy.OnPush, }) export class InterfaceActionsComponent { + private readonly document = inject(DOCUMENT) + readonly isMobile = inject(TUI_IS_MOBILE) readonly dialog = inject(DialogService) readonly copyService = inject(CopyService) readonly interface = inject(InterfaceComponent) - readonly actions = input.required() + readonly href = input.required() + readonly disabled = input.required() showQR() { this.dialog .openComponent(new PolymorpheusComponent(QRModal), { size: 'auto', closeable: this.isMobile, - data: this.actions(), + data: this.href(), }) .subscribe() } + + openUI() { + this.document.defaultView?.open(this.href(), '_blank', 'noreferrer') + } } diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet.component.ts index 3b1decabe..2f70425d6 100644 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet.component.ts +++ b/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet.component.ts @@ -1,13 +1,13 @@ import { ChangeDetectionStrategy, Component, - computed, inject, input, } from '@angular/core' import { toSignal } from '@angular/core/rxjs-interop' import { DialogService, + DocsLinkDirective, ErrorService, i18nPipe, LoadingService, @@ -59,22 +59,12 @@ type ClearnetForm = { }} {{ 'Learn more' | i18n }} - @if (clearnet().length) { +
+
+
`, styles: ` :host { @@ -19,6 +41,12 @@ import { MappedServiceInterface } from './interface.utils' display: flex; flex-direction: column; gap: 1rem; + color: var(--tui-text-secondary); + font: var(--tui-font-text-l); + } + + button { + margin: -0.5rem auto 0 0; } `, providers: [tuiButtonOptionsProvider({ size: 'xs' })], @@ -27,9 +55,43 @@ import { MappedServiceInterface } from './interface.utils' InterfaceClearnetComponent, InterfaceTorComponent, InterfaceLocalComponent, + TuiButton, + i18nPipe, ], }) export class InterfaceComponent { + private readonly loader = inject(LoadingService) + private readonly errorService = inject(ErrorService) + private readonly api = inject(ApiService) + readonly packageId = input('') - readonly serviceInterface = input.required() + readonly value = input.required() + readonly isRunning = input.required() + + async toggle() { + const loader = this.loader + .open(`Making ${this.value().public ? 'private' : 'public'}`) + .subscribe() + + const params = { + internalPort: this.value().addressInfo.internalPort, + public: !this.value().public, + } + + try { + if (this.packageId()) { + await this.api.pkgBindingSetPubic({ + ...params, + host: this.value().addressInfo.hostId, + package: this.packageId(), + }) + } else { + await this.api.serverBindingSetPubic(params) + } + } catch (e: any) { + this.errorService.handleError(e) + } finally { + loader.unsubscribe() + } + } } diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/local.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/local.component.ts index 068634762..a4d278fdf 100644 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/local.component.ts +++ b/web/projects/ui/src/app/routes/portal/components/interfaces/local.component.ts @@ -29,7 +29,7 @@ import { DocsLinkDirective, i18nPipe } from '@start9labs/shared' {{ address.nid }} {{ address.url | mask }} - + } @@ -49,4 +49,5 @@ import { DocsLinkDirective, i18nPipe } from '@start9labs/shared' }) export class InterfaceLocalComponent { readonly local = input.required() + readonly isRunning = input.required() } diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/mask.pipe.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/mask.pipe.ts index 2a47a88b3..951c273f6 100644 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/mask.pipe.ts +++ b/web/projects/ui/src/app/routes/portal/components/interfaces/mask.pipe.ts @@ -9,7 +9,7 @@ export class MaskPipe implements PipeTransform { private readonly interface = inject(InterfaceComponent) transform(value: string): string { - return this.interface.serviceInterface().masked + return this.interface.value().masked ? '●'.repeat(Math.min(64, value.length)) : value } diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/status.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/status.component.ts index 3933f854f..5075bcfb5 100644 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/status.component.ts +++ b/web/projects/ui/src/app/routes/portal/components/interfaces/status.component.ts @@ -9,13 +9,16 @@ import { TuiBadge } from '@taiga-ui/kit' {{ public() ? ('Public' | i18n) : ('Private' | i18n) }} `, + styles: ` + :host { + display: inline-flex; + } + `, changeDetection: ChangeDetectionStrategy.OnPush, imports: [TuiBadge, i18nPipe], }) diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/tor.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/tor.component.ts index 7ecfb0f44..91979b1ba 100644 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/tor.component.ts +++ b/web/projects/ui/src/app/routes/portal/components/interfaces/tor.component.ts @@ -76,11 +76,11 @@ type OnionForm = { {{ address.url | mask }}