Sorgente pubblicata
Scarica sorgente ZIP
CH-J Tools
Sfoglia directory e file per una specifica release di applicazioni.
CH-J / Server Manager
1704 file
0.1.2-beta
Aggiunto: · Costruito:
1
//! Server profiles, portable import/export and private-key selection.3
use super::{logged, main_core, text, CommandResult};4
use crate::core::app::CoreSlot;5
use crate::core::error::{CoreError, CoreResult};6
use crate::core::profile_transfer::{serialize_profiles, MAX_BYTES};7
use crate::dialogs::{self, Filter};8
use serde_json::{json, Value};9
use std::io::Read;10
use tauri::{State, Webview};12
#[tauri::command]13
pub async fn core_profiles_list(webview: Webview, slot: State<'_, CoreSlot>) -> CommandResult {14
let core = main_core(&webview, &slot)?;15
logged(&core, "profiles:list", core.profiles.list().map(Value::Array))16
}18
#[tauri::command]19
pub async fn core_profiles_save(webview: Webview, slot: State<'_, CoreSlot>, payload: Option<Value>) -> CommandResult {20
let core = main_core(&webview, &slot)?;21
let payload = super::payload(payload);22
let result = core.profiles.save(&payload);23
core.services.availability.changed();24
logged(&core, "profiles:save", result)25
}27
#[tauri::command]28
pub async fn core_profiles_delete(webview: Webview, slot: State<'_, CoreSlot>, payload: Option<Value>) -> CommandResult {29
let core = main_core(&webview, &slot)?;30
let payload = super::payload(payload);31
let result = async {32
core.services.biometrics.require_sensitive_action().await?;33
core.profiles.delete(text(&payload, "id"))34
}35
.await;36
core.services.availability.changed();37
logged(&core, "profiles:delete", result)38
}40
#[tauri::command]41
pub async fn core_profiles_select_private_key(webview: Webview, slot: State<'_, CoreSlot>) -> CommandResult {42
let core = main_core(&webview, &slot)?;43
let window = dialogs::main_window(&core.app);44
let path = dialogs::pick_file(45
&core.app,46
window.as_ref(),47
Some(&core.t("native.privateKey.title")),48
// No filter: a key file can have any name, and macOS merges a filter list and hides every other name.49
&[],50
)51
.await;52
Ok(json!({ "canceled": path.is_none(), "path": path.map(|p| p.to_string_lossy().to_string()) }))53
}55
#[tauri::command]56
pub async fn core_profiles_export(webview: Webview, slot: State<'_, CoreSlot>, payload: Option<Value>) -> CommandResult {57
let core = main_core(&webview, &slot)?;58
let payload = super::payload(payload);59
let result = async {60
let generation = core.vault.unlocked_generation()?;61
let format = payload.get("format").and_then(Value::as_str).filter(|f| !f.is_empty()).unwrap_or("json").to_string();62
let profiles = core.profiles.list()?;63
let selected: Vec<Value> = match payload.get("ids") {64
None | Some(Value::Null) => profiles,65
Some(Value::Array(ids)) => profiles.into_iter().filter(|p| ids.contains(&p["id"])).collect(),66
Some(_) => Vec::new(),67
};68
if selected.is_empty() {69
return Err(CoreError::plain("Select at least one server to export."));70
}71
let content = serialize_profiles(&selected, &format)?;72
let window = dialogs::main_window(&core.app);73
let upper = format.to_uppercase();74
let Some(path) = dialogs::save_file(&core.app, window.as_ref(), None, &format!("chj-server-profiles.{format}"), &[Filter { name: &upper, extensions: &[format.as_str()] }]).await else {75
return Ok(json!({ "canceled": true }));76
};77
core.vault.with_generation(generation, || write_private(&path, content.as_bytes()))?;78
Ok(json!({ "canceled": false, "count": selected.len() }))79
}80
.await;81
logged(&core, "profiles:export", result)82
}84
pub fn write_private(path: &std::path::Path, data: &[u8]) -> CoreResult<()> {85
let mut options = std::fs::OpenOptions::new();86
options.write(true).create(true).truncate(true);87
#[cfg(unix)]88
{89
use std::os::unix::fs::OpenOptionsExt;90
options.mode(0o600);91
}92
use std::io::Write;93
options.open(path)?.write_all(data)?;94
Ok(())95
}97
#[tauri::command]98
pub async fn core_profiles_preview_import(webview: Webview, slot: State<'_, CoreSlot>) -> CommandResult {99
let core = main_core(&webview, &slot)?;100
let result = async {101
let generation = core.vault.unlocked_generation()?;102
core.profiles.list()?; // Require an unlocked vault before opening the picker.103
core.profile_transfer.cancel();104
let window = dialogs::main_window(&core.app);105
let Some(path) = dialogs::pick_file(&core.app, window.as_ref(), None, &[Filter { name: &core.t("native.profiles.importFilter"), extensions: &["json", "csv"] }]).await else {106
return Ok(json!({ "canceled": true }));107
};108
core.vault.assert_generation(generation)?;109
let mut buffer = Vec::new();110
std::fs::File::open(&path)?.take(MAX_BYTES as u64 + 1).read_to_end(&mut buffer)?;111
if buffer.len() > MAX_BYTES {112
return Err(CoreError::plain("Profile file exceeds the 5 MiB limit."));113
}114
let content = String::from_utf8(buffer).map_err(|_| CoreError::plain("The encoded data was not valid for encoding utf-8"))?;115
let mut preview = core.profile_transfer.preview(&core.profiles, &content)?;116
core.vault.assert_generation(generation)?;117
preview["canceled"] = json!(false);118
let mut ordered = serde_json::Map::new();119
ordered.insert("canceled".into(), json!(false));120
if let Value::Object(fields) = preview {121
for (k, v) in fields {122
if k != "canceled" {123
ordered.insert(k, v);124
}125
}126
}127
Ok(Value::Object(ordered))128
}129
.await;130
logged(&core, "profiles:previewImport", result)131
}133
#[tauri::command]134
pub async fn core_profiles_commit_import(webview: Webview, slot: State<'_, CoreSlot>, payload: Option<Value>) -> CommandResult {135
let core = main_core(&webview, &slot)?;136
let payload = super::payload(payload);137
let result: CoreResult<Value> = async {138
core.services.biometrics.require_sensitive_action().await?;139
core.profile_transfer.commit(&core.profiles, text(&payload, "token"), text(&payload, "policy"))140
}141
.await;142
core.services.availability.changed();143
logged(&core, "profiles:commitImport", result)144
}146
#[tauri::command]147
pub async fn core_profiles_cancel_import(webview: Webview, slot: State<'_, CoreSlot>) -> CommandResult {148
let core = main_core(&webview, &slot)?;149
core.profile_transfer.cancel();150
Ok(json!({ "canceled": true }))151
}SHA-256
ace82260ae376d692b5831bf57c8505953b9ed32d841075e8e616946c0461c70CH-J Proprietary Software License 1.14 · La sorgente è fornita sotto CH-J Proprietary Software License 1.14. La sua disponibilità non modifica i termini di licenza o concede diritti aggiuntivi.
Archivio SHA-256
ac8fe790f4ba60fc098f8c985fc2656cfcc6cdc21fb34b30e308a2b54e62258f