From c6c97491ac3467c4a602aa82cbe7ce2d3ec56a55 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Thu, 20 Jun 2024 10:07:39 -0600 Subject: [PATCH 1/3] add boot param to logs subscription --- .../ui/src/app/components/logs/logs.component.ts | 2 +- .../src/app/pages/diagnostic-routes/logs/logs.page.ts | 2 +- .../ui/src/app/pages/init/logs/logs.service.ts | 4 +++- web/projects/ui/src/app/services/api/api.types.ts | 7 ++++++- .../src/app/services/api/embassy-live-api.service.ts | 10 ++++++---- 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/web/projects/ui/src/app/components/logs/logs.component.ts b/web/projects/ui/src/app/components/logs/logs.component.ts index 3d31313cb..8ce328f91 100644 --- a/web/projects/ui/src/app/components/logs/logs.component.ts +++ b/web/projects/ui/src/app/components/logs/logs.component.ts @@ -62,7 +62,7 @@ export class LogsComponent { | 'connected' | 'reconnecting' | 'disconnected' = 'connecting' - limit = 400 + limit = 200 count = 0 constructor( diff --git a/web/projects/ui/src/app/pages/diagnostic-routes/logs/logs.page.ts b/web/projects/ui/src/app/pages/diagnostic-routes/logs/logs.page.ts index 5119b9d93..976abc2a0 100644 --- a/web/projects/ui/src/app/pages/diagnostic-routes/logs/logs.page.ts +++ b/web/projects/ui/src/app/pages/diagnostic-routes/logs/logs.page.ts @@ -18,7 +18,7 @@ export class LogsPage { loading = true needInfinite = true startCursor?: string - limit = 200 + limit = 400 isOnBottom = true constructor( diff --git a/web/projects/ui/src/app/pages/init/logs/logs.service.ts b/web/projects/ui/src/app/pages/init/logs/logs.service.ts index e06d56b42..5fe550d45 100644 --- a/web/projects/ui/src/app/pages/init/logs/logs.service.ts +++ b/web/projects/ui/src/app/pages/init/logs/logs.service.ts @@ -35,7 +35,9 @@ function convertAnsi(entries: readonly any[]): string { @Injectable({ providedIn: 'root' }) export class LogsService extends Observable { private readonly api = inject(ApiService) - private readonly log$ = defer(() => this.api.initFollowLogs({})).pipe( + private readonly log$ = defer(() => + this.api.initFollowLogs({ boot: 0 }), + ).pipe( switchMap(({ guid }) => this.api.openWebsocket$(guid, {})), bufferTime(250), filter(logs => !!logs.length), 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 a5c52e9a2..256f6848b 100644 --- a/web/projects/ui/src/app/services/api/api.types.ts +++ b/web/projects/ui/src/app/services/api/api.types.ts @@ -70,7 +70,12 @@ export module RR { export type GetServerLogsReq = ServerLogsReq // server.logs & server.kernel-logs export type GetServerLogsRes = LogsRes - export type FollowServerLogsReq = { limit?: number } // server.logs.follow & server.kernel-logs.follow + // @param limit: BE default is 50 + // @param boot: number is offset (0: current, -1 prev, +1 first), string is a specific boot id, and null is all + export type FollowServerLogsReq = { + limit?: number + boot?: number | string | null + } // server.logs.follow & server.kernel-logs.follow export type FollowServerLogsRes = { startCursor: string guid: string 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 8826b2883..b460e00ff 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 @@ -160,8 +160,10 @@ export class LiveApiService extends ApiService { return this.rpcRequest({ method: 'init.subscribe', params: {} }) } - async initFollowLogs(): Promise { - return this.rpcRequest({ method: 'init.logs.follow', params: {} }) + async initFollowLogs( + params: RR.FollowServerLogsReq, + ): Promise { + return this.rpcRequest({ method: 'init.logs.follow', params }) } // server @@ -379,8 +381,8 @@ export class LiveApiService extends ApiService { } async followPackageLogs( - params: RR.FollowServerLogsReq, - ): Promise { + params: RR.FollowPackageLogsReq, + ): Promise { return this.rpcRequest({ method: 'package.logs.follow', params }) } From f39b85abf2a36622ba7b19d1c4fa6da0ef722215 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Thu, 20 Jun 2024 10:08:00 -0600 Subject: [PATCH 2/3] bump to 036 --- web/package-lock.json | 4 +- web/package.json | 2 +- web/patchdb-ui-seed.json | 2 +- .../modals/os-welcome/os-welcome.page.html | 51 ++----------------- .../ui/src/app/services/api/api.fixures.ts | 4 +- .../ui/src/app/services/api/mock-patch.ts | 2 +- 6 files changed, 12 insertions(+), 53 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 81b612ac1..97a049b98 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "startos-ui", - "version": "0.3.5.2", + "version": "0.3.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "startos-ui", - "version": "0.3.5.2", + "version": "0.3.6", "dependencies": { "@angular/animations": "^14.1.0", "@angular/common": "^14.1.0", diff --git a/web/package.json b/web/package.json index 3ea29c6fd..6d7db5863 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "startos-ui", - "version": "0.3.5.2", + "version": "0.3.6", "author": "Start9 Labs, Inc", "homepage": "https://start9.com/", "scripts": { diff --git a/web/patchdb-ui-seed.json b/web/patchdb-ui-seed.json index 30eb8900b..7bea3ea14 100644 --- a/web/patchdb-ui-seed.json +++ b/web/patchdb-ui-seed.json @@ -1,6 +1,6 @@ { "name": null, - "ack-welcome": "0.3.5.2", + "ack-welcome": "0.3.6", "marketplace": { "selected-url": "https://registry.start9.com/", "known-hosts": { diff --git a/web/projects/ui/src/app/modals/os-welcome/os-welcome.page.html b/web/projects/ui/src/app/modals/os-welcome/os-welcome.page.html index 292480e09..9603d454a 100644 --- a/web/projects/ui/src/app/modals/os-welcome/os-welcome.page.html +++ b/web/projects/ui/src/app/modals/os-welcome/os-welcome.page.html @@ -12,40 +12,7 @@

This Release

-

0.3.5.2

-

- View the complete - - release notes - - for more details. -

-
Highlights
-
    -
  • Revert perpetual performance mode for quieter fan
  • -
  • Minor bug fixes
  • -
- -

Previous 0.3.5.x Releases

- -

0.3.5.1

-

- View the complete - - release notes - - for more details. -

- -

0.3.5

+

0.3.6

View the complete

Highlights
    -
  • - This release contains significant under-the-hood improvements to - performance and reliability -
  • -
  • Ditch Docker, replace with Podman
  • -
  • Remove locking behavior from PatchDB and optimize
  • -
  • Boost efficiency of service manager
  • -
  • Require HTTPS on LAN, and improve setup flow for trusting Root CA
  • -
  • Better default privacy settings for Firefox kiosk mode
  • -
  • Eliminate memory leak from Javascript runtime
  • -
  • Other small bug fixes
  • -
  • Update license to MIT
  • +
  • Ditch Podman, replace with LXC
  • +
  • +
  • +
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 38ee4774b..8c0f447a0 100644 --- a/web/projects/ui/src/app/services/api/api.fixures.ts +++ b/web/projects/ui/src/app/services/api/api.fixures.ts @@ -17,10 +17,10 @@ export module Mock { shuttingDown: false, } export const MarketplaceEos: RR.CheckOSUpdateRes = { - version: '0.3.5.2', + version: '0.3.6', headline: 'Our biggest release ever.', releaseNotes: { - '0.3.5.2': 'Some **Markdown** release _notes_ for 0.3.5.2', + '0.3.6': 'Some **Markdown** release _notes_ for 0.3.6', '0.3.5.1': 'Some **Markdown** release _notes_ for 0.3.5.1', '0.3.4.4': 'Some **Markdown** release _notes_ for 0.3.4.4', '0.3.4.3': 'Some **Markdown** release _notes_ for 0.3.4.3', 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 ee96f9fe2..14fdaa724 100644 --- a/web/projects/ui/src/app/services/api/mock-patch.ts +++ b/web/projects/ui/src/app/services/api/mock-patch.ts @@ -37,7 +37,7 @@ export const mockPatchData: DataModel = { arch: 'x86_64', onionAddress: 'myveryownspecialtoraddress', id: 'abcdefgh', - version: '0.3.5.2', + version: '0.3.6', lastBackup: new Date(new Date().valueOf() - 604800001).toISOString(), lanAddress: 'https://adjective-noun.local', torAddress: 'https://myveryownspecialtoraddress.onion', From e6cedc257e629da1ed1a7541bd8173ebf4fab072 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 10 Jul 2024 13:00:02 -0600 Subject: [PATCH 3/3] add boot param to logs request --- core/startos/src/logs.rs | 127 ++++++++++++++++++++++++++++++++++-- core/startos/src/net/tor.rs | 10 ++- 2 files changed, 130 insertions(+), 7 deletions(-) diff --git a/core/startos/src/logs.rs b/core/startos/src/logs.rs index bb1198451..751dd243a 100644 --- a/core/startos/src/logs.rs +++ b/core/startos/src/logs.rs @@ -1,9 +1,12 @@ +use std::convert::Infallible; use std::ops::{Deref, DerefMut}; use std::process::Stdio; +use std::str::FromStr; use std::time::{Duration, UNIX_EPOCH}; use axum::extract::ws::{self, WebSocket}; use chrono::{DateTime, Utc}; +use clap::builder::ValueParserFactory; use clap::{Args, FromArgMatches, Parser}; use color_eyre::eyre::eyre; use futures::stream::BoxStream; @@ -14,7 +17,7 @@ use rpc_toolkit::yajrc::RpcError; use rpc_toolkit::{ from_fn_async, CallRemote, Context, Empty, HandlerArgs, HandlerExt, HandlerFor, ParentHandler, }; -use serde::de::DeserializeOwned; +use serde::de::{self, DeserializeOwned}; use serde::{Deserialize, Serialize}; use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::process::{Child, Command}; @@ -27,6 +30,7 @@ use crate::error::ResultExt; use crate::lxc::ContainerId; use crate::prelude::*; use crate::rpc_continuations::{Guid, RpcContinuation, RpcContinuations}; +use crate::util::clap::FromStrParser; use crate::util::serde::Reversible; use crate::util::Invoke; @@ -227,6 +231,91 @@ pub struct PackageIdParams { id: PackageId, } +#[derive(Debug, Clone)] +pub enum BootIdentifier { + Index(i32), + Id(String), +} +impl FromStr for BootIdentifier { + type Err = Infallible; + fn from_str(s: &str) -> Result { + Ok(match s.parse() { + Ok(i) => Self::Index(i), + Err(_) => Self::Id(s.to_owned()), + }) + } +} +impl ValueParserFactory for BootIdentifier { + type Parser = FromStrParser; + fn value_parser() -> Self::Parser { + Self::Parser::new() + } +} +impl Serialize for BootIdentifier { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + Self::Index(i) => serializer.serialize_i32(*i), + Self::Id(i) => serializer.serialize_str(i), + } + } +} +impl<'de> Deserialize<'de> for BootIdentifier { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct Visitor; + impl<'de> de::Visitor<'de> for Visitor { + type Value = BootIdentifier; + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a string or integer") + } + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Ok(Self::Value::Id(v.to_owned())) + } + fn visit_string(self, v: String) -> Result + where + E: de::Error, + { + Ok(Self::Value::Id(v)) + } + fn visit_i64(self, v: i64) -> Result + where + E: de::Error, + { + Ok(Self::Value::Index(v as i32)) + } + fn visit_f64(self, v: f64) -> Result + where + E: de::Error, + { + Ok(Self::Value::Index(v as i32)) + } + fn visit_u64(self, v: u64) -> Result + where + E: de::Error, + { + Ok(Self::Value::Index(v as i32)) + } + } + deserializer.deserialize_any(Visitor) + } +} +impl From for String { + fn from(value: BootIdentifier) -> Self { + match value { + BootIdentifier::Index(i) => i.to_string(), + BootIdentifier::Id(i) => i, + } + } +} + #[derive(Deserialize, Serialize, Parser)] #[serde(rename_all = "camelCase")] #[command(rename_all = "kebab-case")] @@ -238,6 +327,9 @@ pub struct LogsParams { limit: Option, #[arg(short = 'c', long = "cursor", conflicts_with = "follow")] cursor: Option, + #[arg(short = 'b', long = "boot")] + #[serde(default)] + boot: Option, #[arg(short = 'B', long = "before", conflicts_with = "follow")] #[serde(default)] before: bool, @@ -352,12 +444,22 @@ where extra, limit, cursor, + boot, before, }, .. }: HandlerArgs>| { let f = f.clone(); - async move { fetch_logs(f.call(&context, extra).await?, limit, cursor, before).await } + async move { + fetch_logs( + f.call(&context, extra).await?, + limit, + cursor, + boot.map(String::from), + before, + ) + .await + } }, ) } @@ -377,13 +479,16 @@ fn logs_follow< from_fn_async( move |HandlerArgs { context, - inherited_params: LogsParams { extra, limit, .. }, + inherited_params: + LogsParams { + extra, limit, boot, .. + }, .. }: HandlerArgs>| { let f = f.clone(); async move { let src = f.call(&context, extra).await?; - follow_logs(context, src, limit).await + follow_logs(context, src, limit, boot.map(String::from)).await } }, ) @@ -416,6 +521,7 @@ pub async fn journalctl( id: LogSource, limit: usize, cursor: Option<&str>, + boot: Option<&str>, before: bool, follow: bool, ) -> Result { @@ -431,6 +537,12 @@ pub async fn journalctl( } } + if let Some(boot) = boot { + cmd.arg(format!("--boot={boot}")); + } else { + cmd.arg("--boot=all"); + } + let deserialized_entries = String::from_utf8(cmd.invoke(ErrorKind::Journald).await?)? .lines() .map(serde_json::from_str::) @@ -516,10 +628,12 @@ pub async fn fetch_logs( id: LogSource, limit: Option, cursor: Option, + boot: Option, before: bool, ) -> Result { let limit = limit.unwrap_or(50); - let mut stream = journalctl(id, limit, cursor.as_deref(), before, false).await?; + let mut stream = + journalctl(id, limit, cursor.as_deref(), boot.as_deref(), before, false).await?; let mut entries = Vec::with_capacity(limit); let mut start_cursor = None; @@ -563,9 +677,10 @@ pub async fn follow_logs>( ctx: Context, id: LogSource, limit: Option, + boot: Option, ) -> Result { let limit = limit.unwrap_or(50); - let mut stream = journalctl(id, limit, None, false, true).await?; + let mut stream = journalctl(id, limit, None, boot.as_deref(), false, true).await?; let mut start_cursor = None; let mut first_entry = None; diff --git a/core/startos/src/net/tor.rs b/core/startos/src/net/tor.rs index 31eae5fab..24b8ddb02 100644 --- a/core/startos/src/net/tor.rs +++ b/core/startos/src/net/tor.rs @@ -305,7 +305,15 @@ async fn torctl( .invoke(ErrorKind::Tor) .await?; - let logs = journalctl(LogSource::Unit(SYSTEMD_UNIT), 0, None, false, true).await?; + let logs = journalctl( + LogSource::Unit(SYSTEMD_UNIT), + 0, + None, + Some("0"), + false, + true, + ) + .await?; let mut tcp_stream = None; for _ in 0..60 {