diff --git a/backend/src/api/setup.rs b/backend/src/api/setup.rs index 21a78d2..29912f5 100644 --- a/backend/src/api/setup.rs +++ b/backend/src/api/setup.rs @@ -33,7 +33,7 @@ pub async fn setup_sql( .into_app_result()?; config::Config::write(config).into_app_result()?; - state.restart_server().await.into_app_result()?; + state.trigger_restart().await.into_app_result()?; Ok("Database installation successful".to_string()) } @@ -123,7 +123,7 @@ pub async fn setup_account( .into_app_result()?; config.init.administrator = true; config::Config::write(config).into_app_result()?; - state.restart_server().await.into_app_result()?; + state.trigger_restart().await.into_app_result()?; Ok(status::Custom( Status::Ok, diff --git a/backend/src/api/users.rs b/backend/src/api/users.rs index 8bf71cf..4727f1c 100644 --- a/backend/src/api/users.rs +++ b/backend/src/api/users.rs @@ -1,6 +1,7 @@ use crate::common::error::{CustomErrorInto, CustomResult}; use crate::security::bcrypt; use crate::storage::{sql, sql::builder}; +use regex::Regex; use rocket::{get, http::Status, post, response::status, serde::json::Json, State}; use serde::{Deserialize, Serialize}; @@ -30,6 +31,12 @@ pub async fn insert_user(sql: &sql::Database, data: RegisterData) -> CustomResul let password_hash = bcrypt::generate_hash(&data.password)?; + let re = Regex::new(r"([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)")?; + + if false == re.is_match(&data.email) { + return Err("邮箱格式不正确".into_custom_error()); + } + let mut builder = builder::QueryBuilder::new( builder::SqlOperation::Insert, sql.table_name("users"), diff --git a/backend/src/main.rs b/backend/src/main.rs index 76bb1f6..b00063b 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -13,7 +13,6 @@ pub struct AppState { db: Arc>>, shutdown: Arc>>, restart_progress: Arc>, - restart_attempts: Arc>, } impl AppState { @@ -22,7 +21,6 @@ impl AppState { db: Arc::new(Mutex::new(None)), shutdown: Arc::new(Mutex::new(None)), restart_progress: Arc::new(Mutex::new(false)), - restart_attempts: Arc::new(Mutex::new(0)), } } @@ -35,8 +33,15 @@ impl AppState { } pub async fn sql_link(&self, config: &config::SqlConfig) -> CustomResult<()> { - *self.db.lock().await = Some(sql::Database::link(config).await?); - Ok(()) + match sql::Database::link(config).await { + Ok(db) => { + *self.db.lock().await = Some(db); + Ok(()) + } + Err(e) => { + Err(e) + } + } } pub async fn set_shutdown(&self, shutdown: Shutdown) { @@ -54,27 +59,6 @@ impl AppState { Ok(()) } - pub async fn restart_server(&self) -> CustomResult<()> { - const MAX_RESTART_ATTEMPTS: u32 = 3; - const RESTART_DELAY_MS: u64 = 1000; - - let mut attempts = self.restart_attempts.lock().await; - if *attempts >= MAX_RESTART_ATTEMPTS { - return Err("达到最大重启尝试次数".into_custom_error()); - } - *attempts += 1; - - *self.restart_progress.lock().await = true; - - self.shutdown - .lock() - .await - .take() - .ok_or_else(|| "未能获取rocket的shutdown".into_custom_error())? - .notify(); - - Ok(()) - } } #[rocket::main] @@ -115,31 +99,15 @@ async fn main() -> CustomResult<()> { rocket.launch().await?; if *state.restart_progress.lock().await { - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; - if let Ok(current_exe) = std::env::current_exe() { - println!("正在尝试重启服务器..."); - - let mut command = std::process::Command::new(current_exe); - command.env("RUST_BACKTRACE", "1"); - - match command.spawn() { - Ok(child) => { - println!("成功启动新进程 (PID: {})", child.id()); - tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; - } - Err(e) => { - eprintln!("启动新进程失败: {}", e); - *state.restart_progress.lock().await = false; - return Err(format!("重启失败: {}", e).into_custom_error()); - } + match std::process::Command::new(current_exe).spawn() { + Ok(_) => println!("成功启动新进程"), + Err(e) => eprintln!("启动新进程失败: {}", e), }; } else { eprintln!("获取当前可执行文件路径失败"); - return Err("重启失败: 无法获取可执行文件路径".into_custom_error()); } } - println!("服务器正常退出"); std::process::exit(0); } diff --git a/backend/src/storage/sql/builder.rs b/backend/src/storage/sql/builder.rs index 0c72c49..54ee929 100644 --- a/backend/src/storage/sql/builder.rs +++ b/backend/src/storage/sql/builder.rs @@ -77,7 +77,7 @@ impl Default for TextValidator { impl TextValidator { pub fn validate(&self, text: &str, level: ValidationLevel) -> CustomResult<()> { if level == ValidationLevel::Raw { - return self.validate_sql_patterns(text); + return Ok(()); } let max_length = self .level_max_lengths diff --git a/frontend/app/index.css b/frontend/app/index.css index bd6213e..e825148 100644 --- a/frontend/app/index.css +++ b/frontend/app/index.css @@ -1,3 +1,44 @@ @tailwind base; @tailwind components; -@tailwind utilities; \ No newline at end of file +@tailwind utilities; + +@layer utilities { + .animate-slideIn { + animation: slideIn 150ms cubic-bezier(0.16, 1, 0.3, 1); + } + + .animate-hide { + animation: hide 100ms ease-in; + } + + .animate-swipeOut { + animation: swipeOut 100ms ease-out; + } +} + +@keyframes hide { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +@keyframes slideIn { + from { + transform: translateX(calc(100% + 1rem)); + } + to { + transform: translateX(0); + } +} + +@keyframes swipeOut { + from { + transform: translateX(var(--radix-toast-swipe-end-x)); + } + to { + transform: translateX(calc(100% + 1rem)); + } +} \ No newline at end of file diff --git a/frontend/app/init.tsx b/frontend/app/init.tsx index b950465..37c9743 100644 --- a/frontend/app/init.tsx +++ b/frontend/app/init.tsx @@ -131,7 +131,7 @@ const DatabaseConfig: React.FC = ({ onNext }) => { default: return field; } }); - message.error(`请填写以下必填项:${fieldNames.join('、')}`); + message.error(`请填写以下必填项:${fieldNames.join('、')}`, '验证失败'); return false; } return true; @@ -180,11 +180,11 @@ const DatabaseConfig: React.FC = ({ onNext }) => { Object.assign( newEnv) - message.success('数据库配置成功!'); + message.success('数据库配置已保存', '配置成功'); setTimeout(() => onNext(), 1000); } catch (error: any) { console.error( error); - message.error(error.message ); + message.error(error.message , error.title || '配置失败'); } finally { setLoading(false); } @@ -369,11 +369,11 @@ const AdminConfig: React.FC = ({ onNext }) => { body: JSON.stringify(newEnv), }); - message.success('管理员账号创建成功!'); + message.success('管理员账号已创建,即将进入下一步', '创建成功'); onNext(); } catch (error: any) { console.error(error); - message.error(error.message); + message.error(error.message, error.title || '创建失败'); } finally { setLoading(false); } diff --git a/frontend/app/root.tsx b/frontend/app/root.tsx index ae76c45..c6e4ce9 100644 --- a/frontend/app/root.tsx +++ b/frontend/app/root.tsx @@ -7,8 +7,7 @@ import { } from "@remix-run/react"; import { BaseProvider } from "hooks/servicesProvider"; -import { MessageProvider } from "hooks/message"; -import { MessageContainer } from "hooks/message"; + import "~/index.css"; @@ -24,42 +23,40 @@ export function Layout({ children }: { children: React.ReactNode }) { - - - - -