Files
rustfs/cli/rustfs-gui/src/components/setting.rs
安正超 d552210b59 feat: translate chinese to english (#402)
* Checkpoint before follow-up message

Co-authored-by: anzhengchao <anzhengchao@gmail.com>

* Translate project documentation and comments from Chinese to English

Co-authored-by: anzhengchao <anzhengchao@gmail.com>

* Fix typo: "unparseable" to "unparsable" in version test comment

Co-authored-by: anzhengchao <anzhengchao@gmail.com>

* Refactor compression test code with minor syntax improvements

Co-authored-by: anzhengchao <anzhengchao@gmail.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-08-14 00:19:01 +08:00

217 lines
11 KiB
Rust

// Copyright 2024 RustFS Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::components::navbar::LoadingSpinner;
use dioxus::logger::tracing::{debug, error};
use dioxus::prelude::*;
const SETTINGS_JS: Asset = asset!("/assets/js/sts.js");
const TAILWIND_CSS: Asset = asset!("/assets/tailwind.css");
#[component]
pub fn Setting() -> Element {
use crate::utils::{RustFSConfig, ServiceManager};
use document::{Meta, Script, Stylesheet, Title};
#[allow(clippy::redundant_closure)]
let service = use_signal(|| ServiceManager::new());
let conf = RustFSConfig::load().unwrap_or_else(|e| {
error!("load config error: {}", e);
RustFSConfig::default_config()
});
debug!("conf address: {:?}", conf.clone().address);
let config = use_signal(|| conf.clone());
let address_state = use_signal(|| conf.address.to_string());
let mut host_state = use_signal(|| conf.host.to_string());
let mut port_state = use_signal(|| conf.port.to_string());
let mut access_key_state = use_signal(|| conf.access_key.to_string());
let mut secret_key_state = use_signal(|| conf.secret_key.to_string());
let mut volume_name_state = use_signal(|| conf.volume_name.to_string());
let loading = use_signal(|| false);
let save_and_restart = {
let host_state = host_state;
let port_state = port_state;
let access_key_state = access_key_state;
let secret_key_state = secret_key_state;
let volume_name_state = volume_name_state;
let mut loading = loading;
debug!("save_and_restart access_key:{}", access_key_state.read());
move |_| {
// set the loading status
loading.set(true);
let mut config = config;
config.write().address = format!("{}:{}", host_state.read(), port_state.read());
config.write().host = host_state.read().to_string();
config.write().port = port_state.read().to_string();
config.write().access_key = access_key_state.read().to_string();
config.write().secret_key = secret_key_state.read().to_string();
config.write().volume_name = volume_name_state.read().to_string();
// restart service
let service = service;
let config = config.read().clone();
spawn(async move {
if let Err(e) = service.read().restart(config).await {
ServiceManager::show_error(&format!("Failed to send restart command: {e}"));
}
// reset the status when you're done
loading.set(false);
});
}
};
rsx! {
Title { "Settings - RustFS App" }
Meta { name: "description", content: "Settings - RustFS App." }
// The Stylesheet component inserts a style link into the head of the document
Stylesheet { href: TAILWIND_CSS }
Script { src: SETTINGS_JS }
div { class: "bg-white p-8",
h1 { class: "text-2xl font-semibold mb-6", "Settings" }
div { class: "border-b border-gray-200 mb-6",
nav { class: "flex space-x-8",
button {
class: "tab-btn px-1 py-4 text-sm font-medium border-b-2 border-black",
"data-tab": "service",
"onclick": "switchTab('service')",
"Service "
}
button {
class: "tab-btn px-1 py-4 text-sm font-medium text-gray-500 hover:text-gray-700",
"data-tab": "user",
"onclick": "switchTab('user')",
"User "
}
button {
class: "tab-btn px-1 py-4 text-sm font-medium text-gray-500 hover:text-gray-700 hidden",
"data-tab": "logs",
"onclick": "switchTab('logs')",
"Logs "
}
}
}
div { id: "tabContent",
div { class: "tab-content", id: "service",
div { class: "mb-8",
h2 { class: "text-base font-medium mb-2", "Service address" }
p { class: "text-gray-600 mb-4",
" The service address is the IP address and port number of the service. the default address is "
code { class: "bg-gray-100 px-1 py-0.5 rounded", {address_state} }
". "
}
div { class: "flex space-x-2",
input {
class: "border rounded px-3 py-2 w-48 focus:outline-none focus:ring-2 focus:ring-blue-500",
r#type: "text",
value: host_state,
oninput: move |evt| host_state.set(evt.value().clone()),
}
span { class: "flex items-center", ":" }
input {
class: "border rounded px-3 py-2 w-20 focus:outline-none focus:ring-2 focus:ring-blue-500",
r#type: "text",
value: port_state,
oninput: move |evt| port_state.set(evt.value().clone()),
}
}
}
div { class: "mb-8",
h2 { class: "text-base font-medium mb-2", "Storage path" }
p { class: "text-gray-600 mb-4",
"Update the storage path of the service. the default path is {volume_name_state}."
}
input {
class: "border rounded px-3 py-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500",
r#type: "text",
value: volume_name_state,
oninput: move |evt| volume_name_state.set(evt.value().clone()),
}
}
}
div { class: "tab-content hidden", id: "user",
div { class: "mb-8",
h2 { class: "text-base font-medium mb-2", "User" }
p { class: "text-gray-600 mb-4",
"The user is the owner of the service. the default user is "
code { class: "bg-gray-100 px-1 py-0.5 rounded", {access_key_state} }
}
input {
class: "border rounded px-3 py-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500",
r#type: "text",
value: access_key_state,
oninput: move |evt| access_key_state.set(evt.value().clone()),
}
}
div { class: "mb-8",
h2 { class: "text-base font-medium mb-2", "Password" }
p { class: "text-gray-600 mb-4",
"The password is the password of the user. the default password is "
code { class: "bg-gray-100 px-1 py-0.5 rounded", {secret_key_state} }
}
div { class: "relative",
input {
class: "border rounded px-3 py-2 w-full pr-10 focus:outline-none focus:ring-2 focus:ring-blue-500",
r#type: "password",
value: secret_key_state,
oninput: move |evt| secret_key_state.set(evt.value().clone()),
}
button {
class: "absolute right-2 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700",
"onclick": "togglePassword(this)",
svg {
class: "h-5 w-5",
fill: "currentColor",
view_box: "0 0 20 20",
xmlns: "http://www.w3.org/2000/svg",
path { d: "M10 12a2 2 0 100-4 2 2 0 000 4z" }
path {
clip_rule: "evenodd",
d: "M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z",
fill_rule: "evenodd",
}
}
}
}
}
}
div { class: "tab-content hidden", id: "logs",
div { class: "mb-8",
h2 { class: "text-base font-medium mb-2", "Logs storage path" }
p { class: "text-gray-600 mb-4",
"The logs storage path is the path where the logs are stored. the default path is /var/log/rustfs. "
}
input {
class: "border rounded px-3 py-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500",
r#type: "text",
value: "/var/logs/rustfs",
}
}
}
}
div { class: "flex space-x-4",
button {
class: "bg-[#111827] text-white px-4 py-2 rounded hover:bg-[#1f2937]",
onclick: save_and_restart,
" Save and restart "
}
GoBackButton { "Back" }
}
LoadingSpinner {
loading: loading.read().to_owned(),
text: "Service processing...",
}
}
}
}