Veröffentlichte Quellen
Quellen als ZIP herunterladen
CH-J Tools
Durchsuchen Sie Verzeichnisse und Dateien einer bestimmten Anwendungsausgabe.
CH-J / Server Manager
872 Dateien
core-20261030T220507Z-61e4558d889f
Hinzugefügt: · Erstellt:
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
&[Filter { name: &core.t("native.privateKey.keys"), extensions: &["pem", "key", "ppk"] }, Filter { name: &core.t("native.privateKey.all"), extensions: &["*"] }],49
)50
.await;51
Ok(json!({ "canceled": path.is_none(), "path": path.map(|p| p.to_string_lossy().to_string()) }))52
}54
#[tauri::command]55
pub async fn core_profiles_export(webview: Webview, slot: State<'_, CoreSlot>, payload: Option<Value>) -> CommandResult {56
let core = main_core(&webview, &slot)?;57
let payload = super::payload(payload);58
let result = async {59
let format = payload.get("format").and_then(Value::as_str).filter(|f| !f.is_empty()).unwrap_or("json").to_string();60
let profiles = core.profiles.list()?;61
let selected: Vec<Value> = match payload.get("ids") {62
None | Some(Value::Null) => profiles,63
Some(Value::Array(ids)) => profiles.into_iter().filter(|p| ids.contains(&p["id"])).collect(),64
Some(_) => Vec::new(),65
};66
if selected.is_empty() {67
return Err(CoreError::plain("Select at least one server to export."));68
}69
let content = serialize_profiles(&selected, &format)?;70
let window = dialogs::main_window(&core.app);71
let upper = format.to_uppercase();72
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 {73
return Ok(json!({ "canceled": true }));74
};75
write_private(&path, content.as_bytes())?;76
Ok(json!({ "canceled": false, "count": selected.len() }))77
}78
.await;79
logged(&core, "profiles:export", result)80
}82
pub fn write_private(path: &std::path::Path, data: &[u8]) -> CoreResult<()> {83
let mut options = std::fs::OpenOptions::new();84
options.write(true).create(true).truncate(true);85
#[cfg(unix)]86
{87
use std::os::unix::fs::OpenOptionsExt;88
options.mode(0o600);89
}90
use std::io::Write;91
options.open(path)?.write_all(data)?;92
Ok(())93
}95
#[tauri::command]96
pub async fn core_profiles_preview_import(webview: Webview, slot: State<'_, CoreSlot>) -> CommandResult {97
let core = main_core(&webview, &slot)?;98
let result = async {99
core.profiles.list()?; // Require an unlocked vault before opening the picker.100
core.profile_transfer.cancel();101
let window = dialogs::main_window(&core.app);102
let Some(path) = dialogs::pick_file(&core.app, window.as_ref(), None, &[Filter { name: &core.t("native.profiles.importFilter"), extensions: &["json", "csv"] }]).await else {103
return Ok(json!({ "canceled": true }));104
};105
let mut buffer = Vec::new();106
std::fs::File::open(&path)?.take(MAX_BYTES as u64 + 1).read_to_end(&mut buffer)?;107
if buffer.len() > MAX_BYTES {108
return Err(CoreError::plain("Profile file exceeds the 5 MiB limit."));109
}110
let content = String::from_utf8(buffer).map_err(|_| CoreError::plain("The encoded data was not valid for encoding utf-8"))?;111
let mut preview = core.profile_transfer.preview(&core.profiles, &content)?;112
preview["canceled"] = json!(false);113
let mut ordered = serde_json::Map::new();114
ordered.insert("canceled".into(), json!(false));115
if let Value::Object(fields) = preview {116
for (k, v) in fields {117
if k != "canceled" {118
ordered.insert(k, v);119
}120
}121
}122
Ok(Value::Object(ordered))123
}124
.await;125
logged(&core, "profiles:previewImport", result)126
}128
#[tauri::command]129
pub async fn core_profiles_commit_import(webview: Webview, slot: State<'_, CoreSlot>, payload: Option<Value>) -> CommandResult {130
let core = main_core(&webview, &slot)?;131
let payload = super::payload(payload);132
let result: CoreResult<Value> = async {133
core.services.biometrics.require_sensitive_action().await?;134
core.profile_transfer.commit(&core.profiles, text(&payload, "token"), text(&payload, "policy"))135
}136
.await;137
core.services.availability.changed();138
logged(&core, "profiles:commitImport", result)139
}141
#[tauri::command]142
pub async fn core_profiles_cancel_import(webview: Webview, slot: State<'_, CoreSlot>) -> CommandResult {143
let core = main_core(&webview, &slot)?;144
core.profile_transfer.cancel();145
Ok(json!({ "canceled": true }))146
}SHA-256
f9de07b02a3456c19e098b48a579bf00ff8c9368edcbaad9133557954783cc62CH-J Proprietary Software License 1.14 · Die Quellen werden unter der CH-J Proprietary Software License 1.14 bereitgestellt. Ihre Verfügbarkeit ändert die Lizenzbedingungen nicht und gewährt keine zusätzlichen Rechte.
SHA-256 des Archivs
77380d4805485ffa4027fbe7858b5836f197a4aa9d52358a43e2d12d018744df