mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-27 08:38:58 +00:00
2e14b32ccd
* chore: Add copyright and license headers This commit adds the Apache 2.0 license and a copyright notice to the header of all source files. This ensures that the licensing and copyright information is clearly stated within the codebase. * cargo fmt * fix * fmt * fix clippy
331 lines
13 KiB
Rust
331 lines
13 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 crate::route::Route;
|
|
use crate::utils::{RustFSConfig, ServiceManager};
|
|
use chrono::Datelike;
|
|
use dioxus::logger::tracing::debug;
|
|
use dioxus::prelude::*;
|
|
use std::time::Duration;
|
|
|
|
const HEADER_LOGO: Asset = asset!("/assets/rustfs-logo.svg");
|
|
const TAILWIND_CSS: Asset = asset!("/assets/tailwind.css");
|
|
|
|
/// Define the state of the service
|
|
#[derive(PartialEq, Debug, Clone)]
|
|
enum ServiceState {
|
|
Start,
|
|
Stop,
|
|
}
|
|
|
|
/// Define the Home component
|
|
/// The Home component is the main component of the application
|
|
/// It is responsible for starting and stopping the service
|
|
/// It also displays the service status and provides a button to toggle the service
|
|
/// The Home component also displays the footer of the application
|
|
/// The footer contains links to the official site, documentation, GitHub, and license
|
|
/// The footer also displays the version of the application
|
|
/// The Home component also contains a button to change the theme of the application
|
|
/// The Home component also contains a button to go to the settings page
|
|
#[component]
|
|
pub fn Home() -> Element {
|
|
#[allow(clippy::redundant_closure)]
|
|
let service = use_signal(|| ServiceManager::new());
|
|
let conf = RustFSConfig::load().unwrap_or_else(|e| {
|
|
ServiceManager::show_error(&format!("load config failed: {e}"));
|
|
RustFSConfig::default()
|
|
});
|
|
|
|
debug!("loaded configurations: {:?}", conf);
|
|
let config = use_signal(|| conf.clone());
|
|
|
|
use dioxus_router::prelude::Link;
|
|
use document::{Meta, Stylesheet, Title};
|
|
let mut service_state = use_signal(|| ServiceState::Start);
|
|
// Create a periodic check on the effect of the service status
|
|
use_effect(move || {
|
|
spawn(async move {
|
|
loop {
|
|
if let Some(pid) = ServiceManager::check_service_status().await {
|
|
debug!("service_running true pid: {:?}", pid);
|
|
service_state.set(ServiceState::Stop);
|
|
} else {
|
|
debug!("service_running true pid: 0");
|
|
service_state.set(ServiceState::Start);
|
|
}
|
|
tokio::time::sleep(Duration::from_secs(2)).await;
|
|
}
|
|
});
|
|
});
|
|
debug!("project start service_state: {:?}", service_state.read());
|
|
// Use 'use_signal' to manage service status
|
|
let mut loading = use_signal(|| false);
|
|
let mut start_service = move |_| {
|
|
let service = service;
|
|
let config = config.read().clone();
|
|
let mut service_state = service_state;
|
|
// set the loading status
|
|
loading.set(true);
|
|
debug!("stop loading_state: {:?}", loading.read());
|
|
spawn(async move {
|
|
match service.read().start(config).await {
|
|
Ok(result) => {
|
|
if result.success {
|
|
let duration = result.end_time - result.start_time;
|
|
debug!("The service starts successfully and takes a long time:{}ms", duration.num_milliseconds());
|
|
service_state.set(ServiceState::Stop);
|
|
} else {
|
|
ServiceManager::show_error(&result.message);
|
|
service_state.set(ServiceState::Start);
|
|
}
|
|
}
|
|
Err(e) => {
|
|
ServiceManager::show_error(&format!("start service failed: {e}"));
|
|
}
|
|
}
|
|
// Only set loading to false when it's actually done
|
|
loading.set(false);
|
|
debug!("start loading_state: {:?}", loading.read());
|
|
});
|
|
};
|
|
|
|
let mut stop_service = move |_| {
|
|
let service = service;
|
|
let mut service_state = service_state;
|
|
// set the loading status
|
|
loading.set(true);
|
|
spawn(async move {
|
|
match service.read().stop().await {
|
|
Ok(result) => {
|
|
if result.success {
|
|
let duration = result.end_time - result.start_time;
|
|
debug!("The service stops successfully and takes a long time:{}ms", duration.num_milliseconds());
|
|
service_state.set(ServiceState::Start);
|
|
} else {
|
|
ServiceManager::show_error(&result.message);
|
|
}
|
|
}
|
|
Err(e) => {
|
|
ServiceManager::show_error(&format!("stop service failed: {e}"));
|
|
}
|
|
}
|
|
debug!("service_state: {:?}", service_state.read());
|
|
// Only set loading to false when it's actually done
|
|
loading.set(false);
|
|
debug!("stop loading_state: {:?}", loading.read());
|
|
});
|
|
};
|
|
|
|
// Toggle the state when the button is clicked
|
|
let toggle_service = {
|
|
let mut service_state = service_state;
|
|
debug!("toggle_service service_state: {:?}", service_state.read());
|
|
move |_| {
|
|
if service_state.read().eq(&ServiceState::Stop) {
|
|
// If the service status is started, you need to run a command to stop the service
|
|
stop_service(());
|
|
service_state.set(ServiceState::Start);
|
|
} else {
|
|
start_service(());
|
|
service_state.set(ServiceState::Stop);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Define dynamic styles based on state
|
|
let button_class = if service_state.read().eq(&ServiceState::Start) {
|
|
"bg-[#111827] hover:bg-[#1f2937] text-white px-4 py-2 rounded-md flex items-center space-x-2"
|
|
} else {
|
|
"bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-md flex items-center space-x-2"
|
|
};
|
|
|
|
rsx! {
|
|
// The Stylesheet component inserts a style link into the head of the document
|
|
Stylesheet {href: TAILWIND_CSS,}
|
|
Title { "RustFS APP" }
|
|
Meta {
|
|
name: "description",
|
|
// TODO: translate to english
|
|
content: "RustFS RustFS 用热门安全的 Rust 语言开发,兼容 S3 协议。适用于 AI/ML 及海量数据存储、大数据、互联网、工业和保密存储等全部场景。近乎免费使用。遵循 Apache 2 协议,支持国产保密设备和系统。",
|
|
}
|
|
div { class: "min-h-screen flex flex-col items-center bg-white",
|
|
div { class: "absolute top-4 right-6 flex space-x-2",
|
|
// change theme
|
|
button { class: "p-2 hover:bg-gray-100 rounded-lg", ChangeThemeButton {} }
|
|
// setting button
|
|
Link {
|
|
class: "p-2 hover:bg-gray-100 rounded-lg",
|
|
to: Route::SettingViews {},
|
|
SettingButton {}
|
|
}
|
|
}
|
|
main { class: "flex-1 flex flex-col items-center justify-center space-y-6 p-4",
|
|
div { class: "w-24 h-24 bg-gray-900 rounded-full flex items-center justify-center",
|
|
img { alt: "Logo", class: "w-16 h-16", src: HEADER_LOGO }
|
|
}
|
|
div { class: "text-gray-600",
|
|
"Service is running on "
|
|
span { class: "text-blue-600", " 127.0.0.1:9000 " }
|
|
}
|
|
LoadingSpinner {
|
|
loading: loading.read().to_owned(),
|
|
text: "processing...",
|
|
}
|
|
button { class: button_class, onclick: toggle_service,
|
|
svg {
|
|
class: "h-4 w-4",
|
|
fill: "none",
|
|
stroke: "currentColor",
|
|
view_box: "0 0 24 24",
|
|
xmlns: "http://www.w3.org/2000/svg",
|
|
if service_state.read().eq(&ServiceState::Start) {
|
|
path {
|
|
d: "M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z",
|
|
stroke_linecap: "round",
|
|
stroke_linejoin: "round",
|
|
stroke_width: "2",
|
|
}
|
|
path {
|
|
d: "M21 12a9 9 0 11-18 0 9 9 0 0118 0z",
|
|
stroke_linecap: "round",
|
|
stroke_linejoin: "round",
|
|
stroke_width: "2",
|
|
}
|
|
} else {
|
|
path {
|
|
stroke_linecap: "round",
|
|
stroke_linejoin: "round",
|
|
stroke_width: "2",
|
|
d: "M21 12a9 9 0 11-18 0 9 9 0 0118 0z",
|
|
}
|
|
path {
|
|
stroke_linecap: "round",
|
|
stroke_linejoin: "round",
|
|
stroke_width: "2",
|
|
d: "M9 10h6v4H9z",
|
|
}
|
|
}
|
|
}
|
|
span { id: "serviceStatus",
|
|
if service_state.read().eq(&ServiceState::Start) {
|
|
"Start service"
|
|
} else {
|
|
"Stop service"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Footer { version: "v1.0.0".to_string() }
|
|
}
|
|
}
|
|
}
|
|
|
|
#[component]
|
|
pub fn Footer(version: String) -> Element {
|
|
let now = chrono::Local::now();
|
|
let year = now.naive_local().year();
|
|
rsx! {
|
|
footer { class: "w-full py-6 flex flex-col items-center space-y-4 mb-6",
|
|
nav { class: "flex space-x-4 text-gray-600",
|
|
a { class: "hover:text-gray-900", href: "https://rustfs.com", "Official Site" }
|
|
a {
|
|
class: "hover:text-gray-900",
|
|
href: "https://rustfs.com/docs",
|
|
"Documentation"
|
|
}
|
|
a {
|
|
class: "hover:text-gray-900",
|
|
href: "https://github.com/rustfs/rustfs",
|
|
"GitHub"
|
|
}
|
|
a {
|
|
class: "hover:text-gray-900",
|
|
href: "https://rustfs.com/docs/license/",
|
|
"License"
|
|
}
|
|
a { class: "hover:text-gray-900", href: "#", "Sponsors" }
|
|
}
|
|
div { class: "text-gray-500 text-sm", " © rustfs.com {year}, All rights reserved." }
|
|
div { class: "text-gray-400 text-sm mb-8", " version {version} " }
|
|
}
|
|
}
|
|
}
|
|
|
|
#[component]
|
|
pub fn GoBackButtons() -> Element {
|
|
rsx! {
|
|
button {
|
|
class: "p-2 hover:bg-gray-100 rounded-lg",
|
|
"onclick": "window.history.back()",
|
|
"Back to the Past"
|
|
}
|
|
}
|
|
}
|
|
|
|
#[component]
|
|
pub fn GoForwardButtons() -> Element {
|
|
rsx! {
|
|
button {
|
|
class: "p-2 hover:bg-gray-100 rounded-lg",
|
|
"onclick": "window.history.forward()",
|
|
"Back to the Future"
|
|
}
|
|
}
|
|
}
|
|
|
|
#[component]
|
|
pub fn ChangeThemeButton() -> Element {
|
|
rsx! {
|
|
svg {
|
|
class: "h-6 w-6 text-gray-600",
|
|
fill: "none",
|
|
stroke: "currentColor",
|
|
view_box: "0 0 24 24",
|
|
xmlns: "http://www.w3.org/2000/svg",
|
|
path {
|
|
d: "M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z",
|
|
stroke_linecap: "round",
|
|
stroke_linejoin: "round",
|
|
stroke_width: "2",
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[component]
|
|
pub fn SettingButton() -> Element {
|
|
rsx! {
|
|
svg {
|
|
class: "h-6 w-6 text-gray-600",
|
|
fill: "none",
|
|
stroke: "currentColor",
|
|
view_box: "0 0 24 24",
|
|
xmlns: "http://www.w3.org/2000/svg",
|
|
path {
|
|
d: "M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z",
|
|
stroke_linecap: "round",
|
|
stroke_linejoin: "round",
|
|
stroke_width: "2",
|
|
}
|
|
path {
|
|
d: "M15 12a3 3 0 11-6 0 3 3 0 016 0z",
|
|
stroke_linecap: "round",
|
|
stroke_linejoin: "round",
|
|
stroke_width: "2",
|
|
}
|
|
}
|
|
}
|
|
}
|