chore: 官网拆分 homepage + 关于页瘦身 + 端口 5175(当前可用状态基线)

This commit is contained in:
Logan.Li committed 2026-10-14 20:31:51 +08:00
1 parent 5eb11826db
commit be151cb68f
85 files changed
+4420 -1530

No files matched your search

+247
View File
@@ -0,0 +1,247 @@
use crate::domain::{error::AppError, files::*, ssh::OperationResponse};
use base64::{engine::general_purpose::STANDARD, Engine};
use std::{
fs,
path::{Path, PathBuf},
};
pub fn read_file(file_path: String) -> ReadFileResponse {
match fs::read(expand_home(&file_path)) {
Ok(data) => ReadFileResponse {
success: true,
data: Some(STANDARD.encode(data)),
error: None,
},
Err(error) => ReadFileResponse {
success: false,
data: None,
error: Some(error.to_string()),
},
}
}
pub fn write_file(file_path: String, data: String) -> OperationResponse {
let result = (|| -> Result<(), AppError> {
let bytes = STANDARD.decode(&data)?;
fs::write(expand_home(&file_path), bytes)?;
Ok(())
})();
match result {
Ok(()) => OperationResponse::ok(),
Err(error) => error.into(),
}
}
pub fn local_read_dir(path: String) -> LocalFilesResponse {
match read_local_dir(&expand_home(&path)) {
Ok(files) => LocalFilesResponse {
success: true,
files: Some(files),
error: None,
},
Err(error) => LocalFilesResponse {
success: false,
files: None,
error: Some(error),
},
}
}
pub fn local_get_stats(path: String) -> LocalStatsResponse {
let path = expand_home(&path);
match local_entry(&path) {
Ok(stats) => LocalStatsResponse {
success: true,
stats: Some(stats),
error: None,
},
Err(error) => LocalStatsResponse {
success: false,
stats: None,
error: Some(error),
},
}
}
pub fn local_mkdir(path: String) -> OperationResponse {
operation(fs::create_dir_all(expand_home(&path)).map_err(|error| error.to_string()))
}
pub fn local_delete(path: String) -> OperationResponse {
let path = expand_home(&path);
let result = if path.is_dir() {
fs::remove_dir_all(path)
} else {
fs::remove_file(path)
};
operation(result.map_err(|error| error.to_string()))
}
pub fn local_rename(old_path: String, new_path: String) -> OperationResponse {
operation(
fs::rename(expand_home(&old_path), expand_home(&new_path))
.map_err(|error| error.to_string()),
)
}
fn read_local_dir(path: &Path) -> Result<Vec<LocalFileEntry>, String> {
let mut files = Vec::new();
for entry in fs::read_dir(path).map_err(|error| error.to_string())? {
let entry = entry.map_err(|error| error.to_string())?;
files.push(local_entry(&entry.path())?);
}
files.sort_by(|a, b| {
((a.entry_type == "file"), a.name.to_lowercase())
.cmp(&((b.entry_type == "file"), b.name.to_lowercase()))
});
Ok(files)
}
fn local_entry(path: &Path) -> Result<LocalFileEntry, String> {
let metadata = fs::symlink_metadata(path).map_err(|error| error.to_string())?;
let entry_type = if metadata.file_type().is_symlink() {
"symlink"
} else if metadata.is_dir() {
"directory"
} else {
"file"
};
Ok(LocalFileEntry {
name: path
.file_name()
.unwrap_or_default()
.to_string_lossy()
.to_string(),
path: path.to_string_lossy().to_string(),
entry_type: entry_type.to_string(),
size: metadata.len(),
modified: metadata
.modified()
.ok()
.and_then(|time| time.duration_since(std::time::UNIX_EPOCH).ok())
.map(|duration| duration.as_secs())
.unwrap_or(0),
permissions: permissions(&metadata, entry_type),
})
}
#[cfg(unix)]
fn permissions(metadata: &fs::Metadata, kind: &str) -> String {
use std::os::unix::fs::PermissionsExt;
let mode = metadata.permissions().mode();
let prefix = if kind == "directory" {
'd'
} else if kind == "symlink" {
'l'
} else {
'-'
};
format!("{}{}", prefix, permission_bits(mode))
}
#[cfg(not(unix))]
fn permissions(_metadata: &fs::Metadata, kind: &str) -> String {
if kind == "directory" {
"drwxr-xr-x".to_string()
} else {
"-rw-r--r--".to_string()
}
}
#[cfg(unix)]
fn permission_bits(mode: u32) -> String {
let mut output = String::new();
for shift in [6, 3, 0] {
let bits = (mode >> shift) & 7;
output.push(if bits & 4 != 0 { 'r' } else { '-' });
output.push(if bits & 2 != 0 { 'w' } else { '-' });
output.push(if bits & 1 != 0 { 'x' } else { '-' });
}
output
}
pub fn expand_home(value: &str) -> PathBuf {
if value == "~" {
return dirs::home_dir().unwrap_or_else(|| PathBuf::from(value));
}
let relative = value
.strip_prefix("~/")
.or_else(|| value.strip_prefix("~\\"));
if let Some(relative) = relative {
if let Some(home) = dirs::home_dir() {
return home.join(std::path::Path::new(relative));
}
}
PathBuf::from(value)
}
fn operation(result: Result<(), String>) -> OperationResponse {
match result {
Ok(()) => OperationResponse::ok(),
Err(error) => OperationResponse::error(error),
}
}
/// Resolve the native downloads path in the command, then perform I/O off the UI thread.
pub fn open_downloads_folder(path: PathBuf) -> Result<(), String> {
fs::create_dir_all(&path).map_err(|error| format!("创建下载目录失败: {error}"))?;
open::that(&path).map_err(|error| format!("打开下载目录失败: {error}"))
}
#[cfg(test)]
mod tests {
use super::*;
struct TestDirectory(PathBuf);
impl TestDirectory {
fn new() -> Self {
let path =
std::env::temp_dir().join(format!("opsxshell-files-{}", uuid::Uuid::new_v4()));
fs::create_dir_all(&path).unwrap();
Self(path)
}
fn path(&self, name: &str) -> String {
self.0.join(name).to_string_lossy().into_owned()
}
}
impl Drop for TestDirectory {
fn drop(&mut self) {
let _ = fs::remove_dir_all(&self.0);
}
}
#[test]
fn binary_file_roundtrip_and_directory_operations() {
let dir = TestDirectory::new();
let bytes = [0, 255, 128, 10];
let file = dir.path("z.bin");
assert!(write_file(file.clone(), STANDARD.encode(bytes)).success);
assert_eq!(read_file(file.clone()).data, Some(STANDARD.encode(bytes)));
assert!(local_mkdir(dir.path("folder")).success);
let listing = local_read_dir(dir.path(""));
assert!(listing.success);
let entries = listing.files.unwrap();
assert_eq!(entries[0].entry_type, "directory");
assert_eq!(entries[1].name, "z.bin");
assert_eq!(local_get_stats(file.clone()).stats.unwrap().size, 4);
let renamed = dir.path("renamed.bin");
assert!(local_rename(file.clone(), renamed.clone()).success);
assert!(!Path::new(&file).exists());
assert!(local_delete(renamed.clone()).success);
assert!(!read_file(renamed).success);
}
#[test]
fn invalid_base64_never_overwrites_existing_file() {
let dir = TestDirectory::new();
let path = dir.path("keep.bin");
fs::write(&path, b"keep").unwrap();
let response = write_file(path.clone(), "%%%".into());
assert!(!response.success);
assert_eq!(
response.error_code.as_deref(),
Some(crate::domain::error::codes::INVALID_BASE64)
);
assert_eq!(fs::read(path).unwrap(), b"keep");
}
}
+1
View File
@@ -4,6 +4,7 @@ pub mod ai;
pub mod credential_store;
pub mod deep_link;
pub mod desktop;
pub mod files;
pub mod license;
pub mod local_terminal;
pub mod serial;