mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-02 05:23:14 +00:00
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 <alexander@inkin.ru> Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
@@ -348,6 +348,7 @@ pub struct ClearTaskParams {
|
||||
pub package_id: PackageId,
|
||||
pub replay_id: ReplayId,
|
||||
#[arg(long)]
|
||||
#[serde(default)]
|
||||
pub force: bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -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<C: Context>() -> ParentHandler<C> {
|
||||
.no_display()
|
||||
.with_about("Remove disk from filesystem"),
|
||||
)
|
||||
.subcommand("repair", from_fn_async(|_: C| repair()).no_cli())
|
||||
.subcommand(
|
||||
"repair",
|
||||
CallRemoteHandler::<CliContext, _, _>::new(
|
||||
from_fn_async(|_: RpcContext| repair())
|
||||
.no_display()
|
||||
.with_about("Repair disk in the event of corruption"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn forget_disk<C: Context>(_: C) -> Result<(), Error> {
|
||||
|
||||
@@ -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<SrcDir: AsRef<Path>> {
|
||||
src_dir: SrcDir,
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum FileType {
|
||||
File,
|
||||
Directory,
|
||||
Infer,
|
||||
}
|
||||
impl<SrcDir: AsRef<Path>> Bind<SrcDir> {
|
||||
pub fn new(src_dir: SrcDir) -> Self {
|
||||
Self { src_dir }
|
||||
|
||||
pub struct Bind<Src: AsRef<Path>> {
|
||||
src: Src,
|
||||
filetype: FileType,
|
||||
}
|
||||
impl<Src: AsRef<Path>> Bind<Src> {
|
||||
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<SrcDir: AsRef<Path> + Send + Sync> FileSystem for Bind<SrcDir> {
|
||||
impl<Src: AsRef<Path> + Send + Sync> FileSystem for Bind<Src> {
|
||||
async fn source(&self) -> Result<Option<impl AsRef<Path>>, Error> {
|
||||
Ok(Some(&self.src_dir))
|
||||
Ok(Some(&self.src))
|
||||
}
|
||||
fn extra_args(&self) -> impl IntoIterator<Item = impl AsRef<std::ffi::OsStr>> {
|
||||
["--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<SrcDir: AsRef<Path> + Send + Sync> FileSystem for Bind<SrcDir> {
|
||||
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()
|
||||
|
||||
@@ -49,8 +49,7 @@ impl<EncryptedDir: AsRef<Path> + Send + Sync, Key: AsRef<str> + 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)
|
||||
|
||||
@@ -53,16 +53,15 @@ impl<Fs: FileSystem> FileSystem for IdMapped<Fs> {
|
||||
async fn source(&self) -> Result<Option<impl AsRef<Path>>, 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<P: AsRef<Path> + 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)
|
||||
|
||||
@@ -69,8 +69,7 @@ pub(self) async fn default_mount_impl(
|
||||
mountpoint: impl AsRef<Path> + 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<Output = Result<Option<impl AsRef<Path>>, Error>> + Send {
|
||||
async { Ok(None::<&Path>) }
|
||||
}
|
||||
fn pre_mount(&self) -> impl Future<Output = Result<(), Error>> + Send {
|
||||
async { Ok(()) }
|
||||
fn pre_mount(&self, mountpoint: &Path) -> impl Future<Output = Result<(), Error>> + Send {
|
||||
async move {
|
||||
tokio::fs::create_dir_all(mountpoint).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
fn mount<P: AsRef<Path> + Send>(
|
||||
&self,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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<PathBuf>,
|
||||
readonly: bool,
|
||||
#[ts(optional)]
|
||||
filetype: Option<FileType>,
|
||||
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 {
|
||||
|
||||
@@ -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"] =
|
||||
|
||||
Reference in New Issue
Block a user