mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-14 03:04:14 +00:00
[ENG-588] Fix total capacity on macOS (#882)
* correct macos root volume handling * DiskType enum
This commit is contained in:
parent
cc308c349f
commit
8226d5e9f0
|
@ -44,7 +44,7 @@ const StatItem: FC<{ title: string; bytes: bigint }> = ({ title, bytes }) => {
|
|||
};
|
||||
|
||||
const OverviewStats = () => {
|
||||
const { data: libraryStatistics } = useLibraryQuery(['library.getStatistics'], {
|
||||
const { data: libraryStatistics } = useLibraryQuery(['library.statistics'], {
|
||||
initialData: { ...EMPTY_STATISTICS }
|
||||
});
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ pub(crate) fn mount() -> AlphaRouter<Ctx> {
|
|||
|ctx, _: ()| async move { ctx.library_manager.get_all_libraries_config().await },
|
||||
)
|
||||
})
|
||||
.procedure("getStatistics", {
|
||||
.procedure("statistics", {
|
||||
R.with2(library()).query(|(_, library), _: ()| async move {
|
||||
let _statistics = library
|
||||
.db
|
||||
|
@ -39,6 +39,7 @@ pub(crate) fn mount() -> AlphaRouter<Ctx> {
|
|||
|
||||
let mut available_capacity: u64 = 0;
|
||||
let mut total_capacity: u64 = 0;
|
||||
|
||||
if let Ok(volumes) = volumes {
|
||||
for volume in volumes {
|
||||
total_capacity += volume.total_capacity;
|
||||
|
|
|
@ -6,12 +6,29 @@ use crate::{
|
|||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::{serde_as, DisplayFromStr};
|
||||
use specta::Type;
|
||||
use std::process::Command;
|
||||
use std::{fmt::Display, process::Command};
|
||||
use sysinfo::{DiskExt, System, SystemExt};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Type)]
|
||||
pub enum DiskType {
|
||||
SSD,
|
||||
HDD,
|
||||
Removable,
|
||||
}
|
||||
|
||||
impl Display for DiskType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
Self::SSD => "SSD",
|
||||
Self::HDD => "HDD",
|
||||
Self::Removable => "Removable",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, Type)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Type)]
|
||||
pub struct Volume {
|
||||
pub name: String,
|
||||
pub mount_point: String,
|
||||
|
@ -22,7 +39,7 @@ pub struct Volume {
|
|||
#[serde_as(as = "DisplayFromStr")]
|
||||
pub available_capacity: u64,
|
||||
pub is_removable: bool,
|
||||
pub disk_type: Option<String>,
|
||||
pub disk_type: Option<DiskType>,
|
||||
pub file_system: Option<String>,
|
||||
pub is_root_filesystem: bool,
|
||||
}
|
||||
|
@ -46,6 +63,13 @@ pub async fn save_volume(library: &Library) -> Result<(), VolumeError> {
|
|||
|
||||
// enter all volumes associate with this client add to db
|
||||
for volume in volumes {
|
||||
let params = vec![
|
||||
disk_type::set(volume.disk_type.map(|t| t.to_string())),
|
||||
filesystem::set(volume.file_system.clone()),
|
||||
total_bytes_capacity::set(volume.total_capacity.to_string()),
|
||||
total_bytes_available::set(volume.available_capacity.to_string()),
|
||||
];
|
||||
|
||||
library
|
||||
.db
|
||||
.volume()
|
||||
|
@ -59,19 +83,9 @@ pub async fn save_volume(library: &Library) -> Result<(), VolumeError> {
|
|||
library.node_local_id,
|
||||
volume.name,
|
||||
volume.mount_point,
|
||||
vec![
|
||||
disk_type::set(volume.disk_type.clone()),
|
||||
filesystem::set(volume.file_system.clone()),
|
||||
total_bytes_capacity::set(volume.total_capacity.to_string()),
|
||||
total_bytes_available::set(volume.available_capacity.to_string()),
|
||||
],
|
||||
params.clone(),
|
||||
),
|
||||
vec![
|
||||
disk_type::set(volume.disk_type),
|
||||
filesystem::set(volume.file_system),
|
||||
total_bytes_capacity::set(volume.total_capacity.to_string()),
|
||||
total_bytes_available::set(volume.available_capacity.to_string()),
|
||||
],
|
||||
params,
|
||||
)
|
||||
.exec()
|
||||
.await?;
|
||||
|
@ -88,27 +102,20 @@ pub fn get_volumes() -> Result<Vec<Volume>, VolumeError> {
|
|||
.iter()
|
||||
.filter_map(|disk| {
|
||||
let mut total_capacity = disk.total_space();
|
||||
let mut mount_point = disk.mount_point().to_str().unwrap_or("/").to_string();
|
||||
let mount_point = disk.mount_point().to_str().unwrap_or("/").to_string();
|
||||
let available_capacity = disk.available_space();
|
||||
let mut name = disk.name().to_str().unwrap_or("Volume").to_string();
|
||||
let name = disk.name().to_str().unwrap_or("Volume").to_string();
|
||||
let is_removable = disk.is_removable();
|
||||
|
||||
let file_system = String::from_utf8(disk.file_system().to_vec())
|
||||
.unwrap_or_else(|_| "Err".to_string());
|
||||
|
||||
let disk_type = match disk.type_() {
|
||||
sysinfo::DiskType::SSD => "SSD".to_string(),
|
||||
sysinfo::DiskType::HDD => "HDD".to_string(),
|
||||
_ => "Removable Disk".to_string(),
|
||||
sysinfo::DiskType::SSD => DiskType::SSD,
|
||||
sysinfo::DiskType::HDD => DiskType::HDD,
|
||||
_ => DiskType::Removable,
|
||||
};
|
||||
|
||||
if cfg!(target_os = "macos") && mount_point == "/"
|
||||
|| mount_point == "/System/Volumes/Data"
|
||||
{
|
||||
name = "Macintosh HD".to_string();
|
||||
mount_point = "/".to_string();
|
||||
}
|
||||
|
||||
if total_capacity < available_capacity && cfg!(target_os = "windows") {
|
||||
let mut caption = mount_point.clone();
|
||||
caption.pop();
|
||||
|
|
|
@ -81,7 +81,7 @@ export default () => {
|
|||
const platform = usePlatform();
|
||||
const { library } = useLibraryContext();
|
||||
|
||||
const stats = useLibraryQuery(['library.getStatistics'], {
|
||||
const stats = useLibraryQuery(['library.statistics'], {
|
||||
initialData: { ...EMPTY_STATISTICS }
|
||||
});
|
||||
mounted = true;
|
||||
|
|
|
@ -16,8 +16,8 @@ export type Procedures = {
|
|||
{ key: "keys.isUnlocked", input: LibraryArgs<null>, result: boolean } |
|
||||
{ key: "keys.list", input: LibraryArgs<null>, result: StoredKey[] } |
|
||||
{ key: "keys.listMounted", input: LibraryArgs<null>, result: string[] } |
|
||||
{ key: "library.getStatistics", input: LibraryArgs<null>, result: Statistics } |
|
||||
{ key: "library.list", input: never, result: LibraryConfigWrapped[] } |
|
||||
{ key: "library.statistics", input: LibraryArgs<null>, result: Statistics } |
|
||||
{ key: "locations.get", input: LibraryArgs<number>, result: Location | null } |
|
||||
{ key: "locations.getWithRules", input: LibraryArgs<number>, result: LocationWithIndexerRules | null } |
|
||||
{ key: "locations.indexer_rules.get", input: LibraryArgs<number>, result: IndexerRule } |
|
||||
|
@ -98,6 +98,8 @@ export type PeerMetadata = { name: string; operating_system: OperatingSystem | n
|
|||
|
||||
export type MasterPasswordChangeArgs = { password: Protected<string>; algorithm: Algorithm; hashing_algorithm: HashingAlgorithm }
|
||||
|
||||
export type Node = { id: number; pub_id: number[]; name: string; platform: number; version: string | null; last_seen: string; timezone: string | null; date_created: string }
|
||||
|
||||
/**
|
||||
* NodeConfig is the configuration for a node. This is shared between all libraries and is stored in a JSON file on disk.
|
||||
*/
|
||||
|
@ -138,10 +140,10 @@ export type Params = "Standard" | "Hardened" | "Paranoid"
|
|||
*/
|
||||
export type LocationUpdateArgs = { id: number; name: string | null; generate_preview_media: boolean | null; sync_preview_media: boolean | null; hidden: boolean | null; indexer_rules_ids: number[] }
|
||||
|
||||
export type Location = { id: number; pub_id: number[]; node_id: number; name: string; path: string; total_capacity: number | null; available_capacity: number | null; is_archived: boolean; generate_preview_media: boolean; sync_preview_media: boolean; hidden: boolean; date_created: string }
|
||||
|
||||
export type SortOrder = "Asc" | "Desc"
|
||||
|
||||
export type MediaData = { id: number; pixel_width: number | null; pixel_height: number | null; longitude: number | null; latitude: number | null; fps: number | null; capture_device_make: string | null; capture_device_model: string | null; capture_device_software: string | null; duration_seconds: number | null; codecs: string | null; streams: number | null }
|
||||
|
||||
/**
|
||||
* Represents the operating system which the remote peer is running.
|
||||
* This is not used internally and predominantly is designed to be used for display purposes by the embedding application.
|
||||
|
@ -161,12 +163,10 @@ export type OnboardingConfig = { password: Protected<string>; algorithm: Algorit
|
|||
|
||||
export type FileDecryptorJobInit = { location_id: number; path_id: number; mount_associated_key: boolean; output_path: string | null; password: string | null; save_to_library: boolean | null }
|
||||
|
||||
export type Volume = { name: string; mount_point: string; total_capacity: string; available_capacity: string; is_removable: boolean; disk_type: string | null; file_system: string | null; is_root_filesystem: boolean }
|
||||
export type Statistics = { id: number; date_captured: string; total_object_count: number; library_db_size: string; total_bytes_used: string; total_bytes_capacity: string; total_unique_bytes: string; total_bytes_free: string; preview_media_bytes: string }
|
||||
|
||||
export type TagCreateArgs = { name: string; color: string }
|
||||
|
||||
export type EditLibraryArgs = { id: string; name: string | null; description: string | null }
|
||||
|
||||
export type LightScanArgs = { location_id: number; sub_path: string }
|
||||
|
||||
export type FileEraserJobInit = { location_id: number; path_id: number; passes: string }
|
||||
|
@ -182,6 +182,8 @@ export type UnlockKeyManagerArgs = { password: Protected<string>; secret_key: Pr
|
|||
|
||||
export type NodeState = ({ id: string; name: string; p2p_port: number | null; p2p_email: string | null; p2p_img_url: string | null }) & { data_path: string }
|
||||
|
||||
export type EditLibraryArgs = { id: string; name: string | null; description: string | null }
|
||||
|
||||
export type SetNoteArgs = { id: number; note: string | null }
|
||||
|
||||
export type InvalidateOperationEvent = { key: string; arg: any; result: any | null }
|
||||
|
@ -204,18 +206,18 @@ export type Category = "Recents" | "Favorites" | "Photos" | "Videos" | "Movies"
|
|||
|
||||
export type FileCopierJobInit = { source_location_id: number; source_path_id: number; target_location_id: number; target_path: string; target_file_name_suffix: string | null }
|
||||
|
||||
export type DiskType = "SSD" | "HDD" | "Removable"
|
||||
|
||||
export type SetFavoriteArgs = { id: number; favorite: boolean }
|
||||
|
||||
export type FilePathFilterArgs = { locationId?: number | null; search?: string; extension?: string | null; createdAt?: OptionalRange<string>; path?: string | null; object?: ObjectFilterArgs | null }
|
||||
|
||||
export type RuleKind = "AcceptFilesByGlob" | "RejectFilesByGlob" | "AcceptIfChildrenDirectoriesArePresent" | "RejectIfChildrenDirectoriesArePresent"
|
||||
|
||||
export type MediaData = { id: number; pixel_width: number | null; pixel_height: number | null; longitude: number | null; latitude: number | null; fps: number | null; capture_device_make: string | null; capture_device_model: string | null; capture_device_software: string | null; duration_seconds: number | null; codecs: string | null; streams: number | null }
|
||||
export type Volume = { name: string; mount_point: string; total_capacity: string; available_capacity: string; is_removable: boolean; disk_type: DiskType | null; file_system: string | null; is_root_filesystem: boolean }
|
||||
|
||||
export type FilePathSearchOrdering = { name: SortOrder } | { sizeInBytes: SortOrder } | { dateCreated: SortOrder } | { dateModified: SortOrder } | { dateIndexed: SortOrder } | { object: ObjectSearchOrdering }
|
||||
|
||||
export type IndexerRule = { id: number; name: string; default: boolean; rules_per_kind: number[]; date_created: string; date_modified: string }
|
||||
|
||||
export type BuildInfo = { version: string; commit: string }
|
||||
|
||||
export type IdentifyUniqueFilesArgs = { id: number; path: string }
|
||||
|
@ -225,7 +227,7 @@ export type IdentifyUniqueFilesArgs = { id: number; path: string }
|
|||
*/
|
||||
export type Algorithm = "XChaCha20Poly1305" | "Aes256Gcm"
|
||||
|
||||
export type Tag = { id: number; pub_id: number[]; name: string | null; color: string | null; total_objects: number | null; redundancy_goal: number | null; date_created: string; date_modified: string }
|
||||
export type Location = { id: number; pub_id: number[]; node_id: number; name: string; path: string; total_capacity: number | null; available_capacity: number | null; is_archived: boolean; generate_preview_media: boolean; sync_preview_media: boolean; hidden: boolean; date_created: string }
|
||||
|
||||
export type OwnedOperationItem = { id: any; data: OwnedOperationData }
|
||||
|
||||
|
@ -244,6 +246,10 @@ export type MaybeNot<T> = T | { not: T }
|
|||
|
||||
export type SpacedropArgs = { peer_id: PeerId; file_path: string[] }
|
||||
|
||||
export type Object = { id: number; pub_id: number[]; kind: number; key_id: number | null; hidden: boolean; favorite: boolean; important: boolean; has_thumbnail: boolean; has_thumbstrip: boolean; has_video_preview: boolean; ipfs_id: string | null; note: string | null; date_created: string; date_accessed: string | null }
|
||||
|
||||
export type FilePath = { id: number; pub_id: number[]; is_dir: boolean; cas_id: string | null; integrity_checksum: string | null; location_id: number; materialized_path: string; name: string; extension: string; size_in_bytes: string; inode: number[]; device: number[]; object_id: number | null; key_id: number | null; date_created: string; date_modified: string; date_indexed: string }
|
||||
|
||||
export type JobReport = { id: string; name: string; action: string | null; data: number[] | null; metadata: any | null; is_background: boolean; errors_text: string[]; created_at: string | null; started_at: string | null; completed_at: string | null; parent_id: string | null; status: JobStatus; task_count: number; completed_task_count: number; message: string; estimated_completion: string }
|
||||
|
||||
export type ObjectFilterArgs = { favorite?: boolean | null; hidden?: boolean | null; dateAccessed?: MaybeNot<string | null> | null; kind?: number[]; tags?: number[] }
|
||||
|
@ -258,6 +264,8 @@ export type RelationOperationData = "Create" | { Update: { field: string; value:
|
|||
|
||||
export type FileDeleterJobInit = { location_id: number; path_id: number }
|
||||
|
||||
export type CreateLibraryArgs = { name: string }
|
||||
|
||||
/**
|
||||
* `IndexerRuleCreateArgs` is the argument received from the client using rspc to create a new indexer rule.
|
||||
* Note that `rules` field is a vector of tuples of `RuleKind` and `parameters`.
|
||||
|
@ -276,6 +284,8 @@ export type KeyAddArgs = { algorithm: Algorithm; hashing_algorithm: HashingAlgor
|
|||
|
||||
export type OptionalRange<T> = { from: T | null; to: T | null }
|
||||
|
||||
export type IndexerRule = { id: number; name: string; default: boolean; rules_per_kind: number[]; date_created: string; date_modified: string }
|
||||
|
||||
export type FileEncryptorJobInit = { location_id: number; path_id: number; key_uuid: string; algorithm: Algorithm; metadata: boolean; preview_media: boolean; output_path: string | null }
|
||||
|
||||
/**
|
||||
|
@ -294,12 +304,12 @@ export type LibraryArgs<T> = { library_id: string; arg: T }
|
|||
|
||||
export type FileCutterJobInit = { source_location_id: number; source_path_id: number; target_location_id: number; target_path: string }
|
||||
|
||||
export type Tag = { id: number; pub_id: number[]; name: string | null; color: string | null; total_objects: number | null; redundancy_goal: number | null; date_created: string; date_modified: string }
|
||||
|
||||
export type OwnedOperationData = { Create: { [key: string]: any } } | { CreateMany: { values: ([any, { [key: string]: any }])[]; skip_duplicates: boolean } } | { Update: { [key: string]: any } } | "Delete"
|
||||
|
||||
export type SharedOperationData = SharedOperationCreateData | { field: string; value: any } | null
|
||||
|
||||
export type Node = { id: number; pub_id: number[]; name: string; platform: number; version: string | null; last_seen: string; timezone: string | null; date_created: string }
|
||||
|
||||
export type TagUpdateArgs = { id: number; name: string | null; color: string | null }
|
||||
|
||||
export type ObjectValidatorArgs = { id: number; path: string }
|
||||
|
@ -326,20 +336,12 @@ export type LibraryConfig = { name: string; description: string }
|
|||
|
||||
export type SearchData<T> = { cursor: number[] | null; items: T[] }
|
||||
|
||||
export type CreateLibraryArgs = { name: string }
|
||||
|
||||
export type FilePath = { id: number; pub_id: number[]; is_dir: boolean; cas_id: string | null; integrity_checksum: string | null; location_id: number; materialized_path: string; name: string; extension: string; size_in_bytes: string; inode: number[]; device: number[]; object_id: number | null; key_id: number | null; date_created: string; date_modified: string; date_indexed: string }
|
||||
|
||||
export type AutomountUpdateArgs = { uuid: string; status: boolean }
|
||||
|
||||
export type Statistics = { id: number; date_captured: string; total_object_count: number; library_db_size: string; total_bytes_used: string; total_bytes_capacity: string; total_unique_bytes: string; total_bytes_free: string; preview_media_bytes: string }
|
||||
|
||||
export type Protected<T> = T
|
||||
|
||||
export type RestoreBackupArgs = { password: Protected<string>; secret_key: Protected<string>; path: string }
|
||||
|
||||
export type Object = { id: number; pub_id: number[]; kind: number; key_id: number | null; hidden: boolean; favorite: boolean; important: boolean; has_thumbnail: boolean; has_thumbstrip: boolean; has_video_preview: boolean; ipfs_id: string | null; note: string | null; date_created: string; date_accessed: string | null }
|
||||
|
||||
export type RelationOperation = { relation_item: string; relation_group: string; relation: string; data: RelationOperationData }
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue