From 7e4d9c1b488ebd1c27a92ea617c0833501781ed0 Mon Sep 17 00:00:00 2001 From: lsy Date: Tue, 19 Nov 2024 00:20:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0readme=EF=BC=8C=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E4=BA=86=E6=B3=A8=E9=87=8A=20=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=EF=BC=9A=E5=AE=8C=E5=96=84=E4=BA=86=E6=8F=92=E4=BB=B6=E5=92=8C?= =?UTF-8?q?=E4=B8=BB=E9=A2=98=E7=9A=84=E5=A5=91=E7=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 19 ++- backend/src/config.rs | 40 +++--- backend/src/database/relational/mod.rs | 107 ++------------- .../database/relational/postgresql/init.sql | 125 ++++++++---------- .../src/database/relational/postgresql/mod.rs | 45 ------- backend/src/main.rs | 89 +++---------- backend/src/secret.rs | 88 +++++------- frontend/components/ErrorBoundary.tsx | 3 - frontend/components/LoadingBoundary.tsx | 1 - frontend/contracts/capabilityContract.ts | 8 -- frontend/contracts/generalContract.ts | 19 ++- frontend/contracts/pluginContract.ts | 46 ++----- frontend/contracts/templateContract.ts | 12 +- frontend/contracts/themeContract.ts | 61 +++------ frontend/hooks/createServiceContext.tsx | 17 --- frontend/hooks/servicesProvider.tsx | 7 - frontend/hooks/useAsyncError.tsx | 15 --- frontend/services/apiService.ts | 49 ++----- frontend/services/capabilityService.ts | 41 +----- frontend/services/pluginService.ts | 13 +- frontend/services/routeManager.ts | 82 +++--------- frontend/services/themeService.ts | 28 ---- 22 files changed, 226 insertions(+), 689 deletions(-) delete mode 100644 frontend/hooks/useAsyncError.tsx diff --git a/README.md b/README.md index 20aaf85..d48d8d0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,17 @@ -能力是主题和系统向插件暴露的接口,契约则规定了开发者在开发主题或插件时的限制和模板。 -系统使用 Remix,后端采用 Rust 的 Rocket。 \ No newline at end of file +## 项目概述 + +本项目是一个基于 Rust 和 TypeScript 的博客系统,旨在提供一个高效、可扩展的博客平台。系统分为前端和后端,前端使用 React 框架构建,后端使用 Rocket 框架实现 RESTful API。 + +## 技术栈 + +- **后端**: Rust, Rocket, SQLx, PostgreSQL +- **前端**: TypeScript, React, Axios, Tailwind CSS, Vite, Remix +- **身份验证**: JWT (JSON Web Tokens) + +## 功能特性 + +- **用户管理**: 支持用户注册、登录和角色管理。 +- **文章管理**: 支持文章的创建、编辑、删除和查看,支持标签和分类管理。 +- **主题与插件支持**: 允许用户自定义主题和插件,增强系统的灵活性。 +- **API 服务**: 提供 RESTful API 接口,供前端调用,实现数据交互。 +- **错误处理与加载状态管理**: 提升用户体验。 diff --git a/backend/src/config.rs b/backend/src/config.rs index f91dd8a..3194ae6 100644 --- a/backend/src/config.rs +++ b/backend/src/config.rs @@ -1,47 +1,39 @@ -// config/mod.rs -/* - 配置文件结构和操作 -*/ - use serde::Deserialize; use std::{ env, fs}; #[derive(Deserialize,Debug,Clone)] pub struct Config { - pub info: Info, // 配置信息 - pub sql_config: SqlConfig, // 关系型数据库配置 - // pub no_sql_config:NoSqlConfig, 非关系型数据库配置 + pub info: Info, + pub sql_config: SqlConfig, } #[derive(Deserialize,Debug,Clone)] pub struct Info { - pub install: bool, // 是否安装 - pub non_relational: bool, // 是否非关系型 + pub install: bool, + pub non_relational: bool, } #[derive(Deserialize,Debug,Clone)] pub struct SqlConfig { - pub db_type: String, // 数据库类型 - pub address: String, // 地址 - pub port: u32, // 端口 - pub user: String, // 用户名 - pub password: String, // 密码 - pub db_name: String, // 数据库名称 + pub db_type: String, + pub address: String, + pub port: u32, + pub user: String, + pub password: String, + pub db_name: String, } #[derive(Deserialize,Debug,Clone)] pub struct NoSqlConfig { - pub db_type: String, // 数据库类型 - pub address: String, // 地址 - pub port: u32, // 端口 - pub user: String, // 用户名 - pub password: String, // 密码 - pub db_name: String, // 数据库名称 + pub db_type: String, + pub address: String, + pub port: u32, + pub user: String, + pub password: String, + pub db_name: String, } - impl Config { - /// 读取配置文件 pub fn read() -> Result> { let path = env::current_dir()? .join("assets") diff --git a/backend/src/database/relational/mod.rs b/backend/src/database/relational/mod.rs index 3580e93..4f44484 100644 --- a/backend/src/database/relational/mod.rs +++ b/backend/src/database/relational/mod.rs @@ -1,10 +1,3 @@ -// File path: src/database/relational/mod.rs - -/** - 本模块定义了数据库的特征和方法,包括查询构建器和数据库连接。 - 提供了对不同类型数据库的支持,如PostgreSQL。 -*/ - mod postgresql; use std::collections::HashMap; use crate::config; @@ -14,70 +7,42 @@ use std::sync::Arc; #[derive(Debug, Clone, PartialEq)] pub enum SqlOperation { - Select, // 查询操作 - Insert, // 插入操作 - Update, // 更新操作 - Delete, // 删除操作 + Select, + Insert, + Update, + Delete, } -/// 查询构建器结构 pub struct QueryBuilder { - operation: SqlOperation, // SQL操作类型 - table: String, // 表名 - fields: Vec, // 查询字段 - params: HashMap, // 插入或更新的参数 - where_conditions: HashMap, // WHERE条件 - order_by: Option, // 排序字段 - limit: Option, // 限制返回的记录数 + operation: SqlOperation, + table: String, + fields: Vec, + params: HashMap, + where_conditions: HashMap, + order_by: Option, + limit: Option, } #[async_trait] pub trait DatabaseTrait: Send + Sync { - /** - 连接数据库 - @param database 数据库配置 - @return Result> 返回数据库实例或错误 - */ async fn connect(database: config::SqlConfig) -> Result> where Self: Sized; - - /** - 执行查询 - @param query SQL查询语句 - @return Result>, Box> 返回查询结果或错误 - */ async fn execute_query<'a>( &'a self, builder: &QueryBuilder, ) -> Result>, Box>; - - /** - 初始化数据库 - @param database 数据库配置 - @return Result<(), Box> 返回初始化结果或错误 - */ async fn initialization(database: config::SqlConfig) -> Result<(), Box> where Self: Sized; } #[derive(Clone)] pub struct Database { - // 数据库实例 pub db: Arc>, } impl Database { - /** - 获取当前数据库实例 - @return &Box 返回数据库实例的引用 - */ pub fn get_db(&self) -> &Box { &self.db } - /** - 初始化数据库 - @param database 数据库配置 - @return Result> 返回数据库实例或错误 - */ pub async fn link(database: config::SqlConfig) -> Result> { let db = match database.db_type.as_str() { "postgresql" => postgresql::Postgresql::connect(database).await?, @@ -87,11 +52,6 @@ impl Database { Ok(Self { db: Arc::new(Box::new(db)) }) } - /** - 执行数据库初始化设置 - @param database 数据库配置 - @return Result<(), Box> 返回初始化结果或错误 - */ pub async fn initial_setup(database: config::SqlConfig) -> Result<(), Box> { match database.db_type.as_str() { "postgresql" => postgresql::Postgresql::initialization(database).await?, @@ -102,12 +62,6 @@ impl Database { } impl QueryBuilder { - /** - 创建新的查询构建器 - @param operation SQL操作类型 - @param table 表名 - @return Self 返回新的查询构建器实例 - */ pub fn new(operation: SqlOperation, table: &str) -> Self { QueryBuilder { operation, @@ -120,10 +74,6 @@ impl QueryBuilder { } } - /** - 构建SQL语句和参数 - @return (String, Vec) 返回构建的SQL语句和参数值 - */ pub fn build(&self) -> (String, Vec) { let mut query = String::new(); let mut values = Vec::new(); @@ -131,7 +81,6 @@ impl QueryBuilder { match self.operation { SqlOperation::Select => { - // SELECT 操作 let fields = if self.fields.is_empty() { "*".to_string() } else { @@ -140,7 +89,6 @@ impl QueryBuilder { query.push_str(&format!("SELECT {} FROM {}", fields, self.table)); - // 添加 WHERE 条件 if !self.where_conditions.is_empty() { let conditions: Vec = self.where_conditions .iter() @@ -156,7 +104,6 @@ impl QueryBuilder { } }, SqlOperation::Insert => { - // INSERT 操作 let fields: Vec = self.params.keys().cloned().collect(); let placeholders: Vec = (1..=self.params.len()) .map(|i| format!("${}", i)) @@ -169,13 +116,11 @@ impl QueryBuilder { placeholders.join(", ") )); - // 收集参数值 for field in fields { values.push(self.params[&field].clone()); } }, SqlOperation::Update => { - // UPDATE 操作 query.push_str(&format!("UPDATE {}", self.table)); let set_clauses: Vec = self.params @@ -191,7 +136,6 @@ impl QueryBuilder { query.push_str(" SET "); query.push_str(&set_clauses.join(", ")); - // 添加 WHERE 条件 if !self.where_conditions.is_empty() { let conditions: Vec = self.where_conditions .iter() @@ -207,10 +151,8 @@ impl QueryBuilder { } }, SqlOperation::Delete => { - // DELETE 操作 query.push_str(&format!("DELETE FROM {}", self.table)); - // 添加 WHERE 条件 if !self.where_conditions.is_empty() { let conditions: Vec = self.where_conditions .iter() @@ -227,12 +169,10 @@ impl QueryBuilder { } } - // 添加 ORDER BY if let Some(order) = &self.order_by { query.push_str(&format!(" ORDER BY {}", order)); } - // 添加 LIMIT if let Some(limit) = self.limit { query.push_str(&format!(" LIMIT {}", limit)); } @@ -240,51 +180,26 @@ impl QueryBuilder { (query, values) } - /** - 设置查询字段 - @param fields 字段列表 - @return &mut Self 返回可变引用以便链式调用 - */ pub fn fields(&mut self, fields: Vec) -> &mut Self { self.fields = fields; self } - /** - 设置参数 - @param params 参数键值对 - @return &mut Self 返回可变引用以便链式调用 - */ pub fn params(&mut self, params: HashMap) -> &mut Self { self.params = params; self } - /** - 设置WHERE条件 - @param conditions 条件键值对 - @return &mut Self 返回可变引用以便链式调用 - */ pub fn where_conditions(&mut self, conditions: HashMap) -> &mut Self { self.where_conditions = conditions; self } - /** - 设置排序 - @param order 排序字段 - @return &mut Self 返回可变引用以便链式调用 - */ pub fn order_by(&mut self, order: &str) -> &mut Self { self.order_by = Some(order.to_string()); self } - /** - 设置限制 - @param limit 限制记录数 - @return &mut Self 返回可变引用以便链式调用 - */ pub fn limit(&mut self, limit: i32) -> &mut Self { self.limit = Some(limit); self diff --git a/backend/src/database/relational/postgresql/init.sql b/backend/src/database/relational/postgresql/init.sql index 1bf2ae4..1583a83 100644 --- a/backend/src/database/relational/postgresql/init.sql +++ b/backend/src/database/relational/postgresql/init.sql @@ -1,99 +1,84 @@ ---- 安装自动生成uuid插件 CREATE EXTENSION IF NOT EXISTS pgcrypto; ---- 用户权限枚举 CREATE TYPE privilege_level AS ENUM ( 'contributor', 'administrators'); ---- 用户信息表 CREATE TABLE persons ( - person_name VARCHAR(100) PRIMARY KEY, --- 用户名 - person_email VARCHAR(255) UNIQUE NOT NULL, --- 用户邮箱 - person_icon VARCHAR(255), --- 用户头像 - person_password VARCHAR(255) NOT NULL, --- 用户密码 - person_created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, --- 用户创建时间 - person_updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, --- 用户更新时间 - person_avatar VARCHAR(255), --- 用户头像URL - person_role VARCHAR(50), --- 用户角色 - person_last_login TIMESTAMP DEFAULT CURRENT_TIMESTAMP, --- 最后登录时间 - person_level privilege_level NOT NULL DEFAULT 'contributor' --- 用户权限 + person_name VARCHAR(100) PRIMARY KEY, + person_email VARCHAR(255) UNIQUE NOT NULL, + person_icon VARCHAR(255), + person_password VARCHAR(255) NOT NULL, + person_created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + person_updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + person_avatar VARCHAR(255), + person_role VARCHAR(50), + person_last_login TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + person_level privilege_level NOT NULL DEFAULT 'contributor' ); ---- 页面状态枚举 CREATE TYPE publication_status AS ENUM ('draft', 'published', 'private','hide'); ---- 独立页面表 CREATE TABLE pages ( - page_id SERIAL PRIMARY KEY, --- 独立页面唯一id主键 - page_meta_keywords VARCHAR(255) NOT NULL, ---页面mata关键字 - page_meta_description VARCHAR(255) NOT NULL, ---页面mata描述 - page_title VARCHAR(255) NOT NULL, --- 文章标题 - page_content TEXT NOT NULL, --- 独立页面内容 - page_mould VARCHAR(50), --- 独立页面模板名称 - page_fields JSON, --- 自定义字段 - page_status publication_status DEFAULT 'draft' --- 文章状态 + page_id SERIAL PRIMARY KEY, + page_meta_keywords VARCHAR(255) NOT NULL, + page_meta_description VARCHAR(255) NOT NULL, + page_title VARCHAR(255) NOT NULL, + page_content TEXT NOT NULL, + page_mould VARCHAR(50), + page_fields JSON, + page_status publication_status DEFAULT 'draft' ); ---- 文章表 CREATE TABLE posts ( - post_author VARCHAR(100) NOT NULL REFERENCES persons (person_name) ON DELETE CASCADE, --- 文章作者 - post_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), --- 文章主键 - post_picture VARCHAR(255), --- 文章头图 - post_title VARCHAR(255) NOT NULL, --- 文章标题 - post_meta_keywords VARCHAR(255) NOT NULL, ---文章mata关键字 - post_meta_description VARCHAR(255) NOT NULL, ---文章mata描述 - post_content TEXT NOT NULL, --- 文章内容 - post_status publication_status DEFAULT 'draft', --- 文章状态 - post_editor BOOLEAN DEFAULT FALSE, --- 文章是否编辑未保存 - post_unsaved_content TEXT, --- 未保存的文章 - post_created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, --- 文章创建时间 - post_updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, --- 文章更新时间 - post_published_at TIMESTAMP, --- 文章发布时间 - CONSTRAINT post_updated_at_check CHECK (post_updated_at >= post_created_at) --- 文章时间约束 + post_author VARCHAR(100) NOT NULL REFERENCES persons (person_name) ON DELETE CASCADE, + post_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + post_picture VARCHAR(255), + post_title VARCHAR(255) NOT NULL, + post_meta_keywords VARCHAR(255) NOT NULL, + post_meta_description VARCHAR(255) NOT NULL, + post_content TEXT NOT NULL, + post_status publication_status DEFAULT 'draft', + post_editor BOOLEAN DEFAULT FALSE, + post_unsaved_content TEXT, + post_created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + post_updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + post_published_at TIMESTAMP, + CONSTRAINT post_updated_at_check CHECK (post_updated_at >= post_created_at) ); ---- 标签表 CREATE TABLE tags ( - tag_name VARCHAR(50) PRIMARY KEY CHECK (LOWER(tag_name) = tag_name), --- 标签名称主键 - tag_icon VARCHAR(255) --- 标签图标 + tag_name VARCHAR(50) PRIMARY KEY CHECK (LOWER(tag_name) = tag_name), + tag_icon VARCHAR(255) ); ---- 文章与标签的关系表 CREATE TABLE post_tags ( - post_id UUID REFERENCES posts (post_id) ON DELETE CASCADE, --- 外键约束,引用posts的主键 - tag_name VARCHAR(50) REFERENCES tags (tag_name) ON DELETE CASCADE, --- 外键约束,引用tag的主键 - PRIMARY KEY (post_id, tag_name) --- 复合主键 + post_id UUID REFERENCES posts (post_id) ON DELETE CASCADE, + tag_name VARCHAR(50) REFERENCES tags (tag_name) ON DELETE CASCADE, + PRIMARY KEY (post_id, tag_name) ); ---- 分类表 CREATE TABLE categories ( - category_name VARCHAR(50) PRIMARY KEY, ---分类表名称主键 - parent_category_name VARCHAR(50), -- 父类分类 ID(可以为空) - FOREIGN KEY (parent_category_name) REFERENCES categories (category_name) -- 外键约束,引用同一表的 category_id + category_name VARCHAR(50) PRIMARY KEY, + parent_category_name VARCHAR(50), + FOREIGN KEY (parent_category_name) REFERENCES categories (category_name) ); ---- 文章与分类的关系表 CREATE TABLE post_categories ( - post_id UUID REFERENCES posts (post_id) ON DELETE CASCADE, --- 外键约束,引用posts的主键 - category_id VARCHAR(50) REFERENCES categories (category_name) ON DELETE CASCADE, --- 外键约束,引用categories的主键 - PRIMARY KEY (post_id, category_id) --- 复合主键 + post_id UUID REFERENCES posts (post_id) ON DELETE CASCADE, + category_id VARCHAR(50) REFERENCES categories (category_name) ON DELETE CASCADE, + PRIMARY KEY (post_id, category_id) ); - ---- 资源库 CREATE TABLE library ( - library_author VARCHAR(100) NOT NULL REFERENCES persons (person_name) ON DELETE CASCADE, --- 资源作者 - library_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), --- 资源ID - library_name VARCHAR(255) NOT NULL, --- 资源名称 - library_size BIGINT NOT NULL, --- 大小 - library_storage_path VARCHAR(255) NOT NULL UNIQUE, --- 储存路径 - library_type VARCHAR(50) NOT NULL, --- 资源类别 - library_class VARCHAR(50), --- 资源类 - library_description VARCHAR(255), --- 资源描述 - library_created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP --- 创建时间 - + library_author VARCHAR(100) NOT NULL REFERENCES persons (person_name) ON DELETE CASCADE, + library_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + library_name VARCHAR(255) NOT NULL, + library_size BIGINT NOT NULL, + library_storage_path VARCHAR(255) NOT NULL UNIQUE, + library_type VARCHAR(50) NOT NULL, + library_class VARCHAR(50), + library_description VARCHAR(255), + library_created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); - ---- 配置文件库 CREATE TABLE config ( - config_name VARCHAR(50) PRIMARY KEY CHECK (LOWER(config_name) = config_name), --- 配置文件名称 - config_config JSON --- 配置文件 -) + config_name VARCHAR(50) PRIMARY KEY CHECK (LOWER(config_name) = config_name), + config_config JSON +); diff --git a/backend/src/database/relational/postgresql/mod.rs b/backend/src/database/relational/postgresql/mod.rs index 869ce27..f0dc17b 100644 --- a/backend/src/database/relational/postgresql/mod.rs +++ b/backend/src/database/relational/postgresql/mod.rs @@ -1,9 +1,3 @@ -// File path: src/database/relational/postgresql/mod.rs -/** - * 该模块实现了与PostgreSQL数据库的交互功能。 - * 包含连接池的结构体定义,提供数据库操作的基础。 - */ - use super::{DatabaseTrait, QueryBuilder}; use crate::config; use async_trait::async_trait; @@ -12,23 +6,12 @@ use std::{collections::HashMap, error::Error}; use std::{env, fs}; #[derive(Clone)] -/// PostgreSQL数据库连接池结构体 pub struct Postgresql { - /// 数据库连接池 pool: PgPool, } #[async_trait] impl DatabaseTrait for Postgresql { - /** - * 初始化数据库并执行初始化脚本。 - * - * # 参数 - * - `db_config`: 数据库配置 - * - * # 返回 - * - `Result<(), Box>`: 初始化结果 - */ async fn initialization(db_config: config::SqlConfig) -> Result<(), Box> { let path = env::current_dir()? .join("src") @@ -38,77 +21,49 @@ impl DatabaseTrait for Postgresql { .join("init.sql"); let grammar = fs::read_to_string(&path)?; - // 创建初始连接(不指定数据库) let connection_str = format!("postgres://{}:{}@{}:{}", db_config.user, db_config.password, db_config.address, db_config.port); let pool = PgPool::connect(&connection_str).await?; - // 创建数据库 pool.execute(format!("CREATE DATABASE {}", db_config.db_name).as_str()).await?; - // 连接到新数据库 let new_connection_str = format!("postgres://{}:{}@{}:{}/{}", db_config.user, db_config.password, db_config.address, db_config.port, db_config.db_name); let new_pool = PgPool::connect(&new_connection_str).await?; - // 执行初始化脚本 new_pool.execute(grammar.as_str()).await?; Ok(()) } - /** - * 连接到PostgreSQL数据库并返回Postgresql实例。 - * - * # 参数 - * - `db_config`: 数据库配置 - * - * # 返回 - * - `Result>`: 连接结果 - */ async fn connect(db_config: config::SqlConfig) -> Result> { let connection_str = format!( "postgres://{}:{}@{}:{}/{}", db_config.user, db_config.password, db_config.address, db_config.port, db_config.db_name ); - // 连接到数据库池 let pool = PgPool::connect(&connection_str) .await .map_err(|e| Box::new(e) as Box)?; - // 返回Postgresql实例 Ok(Postgresql { pool }) } - /** - * 异步执行查询并返回结果。 - * - * # 参数 - * - `builder`: 查询构建器 - * - * # 返回 - * - `Result>, Box>`: 查询结果 - */ async fn execute_query<'a>( &'a self, builder: &QueryBuilder, ) -> Result>, Box> { let (query, values) = builder.build(); - // 构建查询 let mut sqlx_query = sqlx::query(&query); - // 绑定参数 for value in values { sqlx_query = sqlx_query.bind(value); } - // 执行查询 let rows = sqlx_query .fetch_all(&self.pool) .await .map_err(|e| Box::new(e) as Box)?; - // 处理结果 let mut results = Vec::new(); for row in rows { let mut map = HashMap::new(); diff --git a/backend/src/main.rs b/backend/src/main.rs index 254eb1d..35746c8 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -1,71 +1,36 @@ -// File path: /d:/data/echoes/backend/src/main.rs +mod config; +mod database; +mod secret; -/** - * 主程序入口,提供数据库连接和相关API接口。 - * - * 接口: - * - GET /api/install: 测试数据库连接 - * - GET /api/sql: 执行SQL查询并返回结果 - */ +use chrono::Duration; +use database::relational; +use once_cell::sync::Lazy; +use rocket::{get, post, http::Status, launch, response::status, routes, serde::json::Json}; +use std::sync::Arc; +use tokio::sync::Mutex; -mod config; // 配置模块 -mod database; // 数据库模块 -mod secret; // 密钥模块 - -use chrono::Duration; // 引入时间持续时间 -use database::relational; // 引入关系型数据库 -use once_cell::sync::Lazy; // 用于延迟初始化 -use rocket::{get, post, http::Status, launch, response::status, routes, serde::json::Json}; // 引入Rocket框架相关功能 -use std::sync::Arc; // 引入Arc用于线程安全的引用计数 -use tokio::sync::Mutex; // 引入Mutex用于异步锁 - -// 全局数据库连接变量 static SQL: Lazy>>> = Lazy::new(|| Arc::new(Mutex::new(None))); -/** - * 初始化数据库连接 - * - * # 参数 - * - `database`: 数据库配置 - * - * # 返回 - * - `Result<(), Box>`: 初始化结果 - */ async fn init_sql(database: config::SqlConfig) -> Result<(), Box> { - let database = relational::Database::link(database).await?; // 初始化数据库 - *SQL.lock().await = Some(database); // 保存数据库实例 + let database = relational::Database::link(database).await?; + *SQL.lock().await = Some(database); Ok(()) } -/** - * 获取数据库的引用 - * - * # 返回 - * - `Result>`: 数据库引用或错误信息 - */ async fn get_sql() -> Result> { SQL.lock() .await .clone() - .ok_or_else(|| "Database not initialized".into()) // 返回错误信息 + .ok_or_else(|| "Database not initialized".into()) } -/** - * 数据库安装接口 - * - * # 参数 - * - `data`: 数据库配置 - * - * # 返回 - * - `Result, status::Custom>`: 安装结果 - */ #[post("/install", format = "json", data = "")] async fn install(data: Json) -> Result, status::Custom> { relational::Database::initial_setup(data.into_inner()).await.map_err(|e| { status::Custom( Status::InternalServerError, - format!("Database initialization failed: {}", e), // 连接失败信息 + format!("Database initialization failed: {}", e), ) })?; @@ -75,48 +40,34 @@ async fn install(data: Json) -> Result )) } -/** - * 生成系统JWT接口 - * - * # 返回 - * - `Result, status::Custom>`: JWT令牌或错误信息 - */ #[get("/system")] async fn token_system() -> Result, status::Custom> { - // 创建 Claims let claims = secret::CustomClaims { - user_id: String::from("system"), // 用户ID - device_ua: String::from("system"), // 设备用户代理 + user_id: String::from("system"), + device_ua: String::from("system"), }; - // 生成JWT let token = secret::generate_jwt(claims, Duration::seconds(1)).map_err(|e| { status::Custom( Status::InternalServerError, - format!("JWT generation failed: {}", e), // JWT生成失败信息 + format!("JWT generation failed: {}", e), ) })?; - Ok(status::Custom(Status::Ok, token)) // 返回JWT令牌 + Ok(status::Custom(Status::Ok, token)) } -/** - * 启动Rocket应用 - * - * # 返回 - * - `rocket::Rocket`: Rocket应用实例 - */ #[launch] async fn rocket() -> _ { - let config = config::Config::read().expect("Failed to read config"); // 读取配置 + let config = config::Config::read().expect("Failed to read config"); if config.info.install { init_sql(config.sql_config) .await - .expect("Failed to connect to database"); // 初始化数据库连接 + .expect("Failed to connect to database"); rocket::build() .mount("/auth/token", routes![token_system]) } else { rocket::build() - .mount("/", routes![install]) // 挂载API路由 + .mount("/", routes![install]) } } diff --git a/backend/src/secret.rs b/backend/src/secret.rs index 455c118..d898a6c 100644 --- a/backend/src/secret.rs +++ b/backend/src/secret.rs @@ -1,10 +1,3 @@ -// File path: src/secret.rs - -/** - * 本文件包含JWT的生成和验证功能。 - * 提供了生成密钥、生成JWT、验证JWT的相关函数。 - */ - use jwt_compact::{alg::Ed25519, AlgorithmExt, Header, Token, UntrustedToken, TimeOptions}; use serde::{Serialize, Deserialize}; use chrono::{Duration, Utc}; @@ -15,16 +8,15 @@ use std::{env, fs}; use std::error::Error; use rand::{SeedableRng, RngCore}; -// 定义JWT的Claims结构体(有效载荷) #[derive(Debug, Serialize, Deserialize, Clone)] pub struct CustomClaims { - pub user_id: String, // 用户ID - pub device_ua: String, // 用户UA + pub user_id: String, + pub device_ua: String, } pub enum SecretKey { - Signing, // 签名密钥 - Verifying, // 验证密钥 + Signing, + Verifying, } impl SecretKey { @@ -36,84 +28,72 @@ impl SecretKey { } } -/** - * 生成签名密钥和验证密钥,并将其保存到文件中。 - */ pub fn generate_key() -> Result<(), Box> { - let mut csprng = rand::rngs::StdRng::from_entropy(); // 使用系统熵创建随机数生成器 + let mut csprng = rand::rngs::StdRng::from_entropy(); - let mut private_key_bytes = [0u8; 32]; // 存储签名密钥的字节数组 - csprng.fill_bytes(&mut private_key_bytes); // 生成签名密钥 + let mut private_key_bytes = [0u8; 32]; + csprng.fill_bytes(&mut private_key_bytes); - let signing_key = SigningKey::from_bytes(&private_key_bytes); // 从签名密钥获取SigningKey - let verifying_key = signing_key.verifying_key(); // 获取验证密钥 + let signing_key = SigningKey::from_bytes(&private_key_bytes); + let verifying_key = signing_key.verifying_key(); let base_path = env::current_dir()? .join("assets") .join("key"); - fs::create_dir_all(&base_path)?; // 创建目录 + fs::create_dir_all(&base_path)?; File::create(base_path.join(SecretKey::Signing.as_string()))? - .write_all(signing_key.as_bytes())?; // 保存签名密钥 + .write_all(signing_key.as_bytes())?; File::create(base_path.join(SecretKey::Verifying.as_string()))? - .write_all(verifying_key.as_bytes())?; // 保存验证密钥 + .write_all(verifying_key.as_bytes())?; Ok(()) } -/** - * 从文件中读取指定类型的密钥。 - */ pub fn get_key(key_type: SecretKey) -> Result<[u8; 32], Box> { let path = env::current_dir()? .join("assets") .join("key") .join(key_type.as_string()); - let key_bytes = fs::read(path)?; // 读取密钥文件 - let mut key = [0u8; 32]; // 固定长度的数组 - key.copy_from_slice(&key_bytes[..32]); // 拷贝前32个字节 + let key_bytes = fs::read(path)?; + let mut key = [0u8; 32]; + key.copy_from_slice(&key_bytes[..32]); Ok(key) } -/** - * 生成JWT,包含自定义声明和有效期。 - */ pub fn generate_jwt(claims: CustomClaims, duration: Duration) -> Result> { - let key_bytes = get_key(SecretKey::Signing)?; // 从文件读取私钥 - let signing_key = SigningKey::from_bytes(&key_bytes); // 创建SigningKey + let key_bytes = get_key(SecretKey::Signing)?; + let signing_key = SigningKey::from_bytes(&key_bytes); let time_options = TimeOptions::new( - Duration::seconds(0), // 设置时间容差为0 - Utc::now // 使用当前UTC时间作为时钟函数 - ); // 设置时间容差为); // 默认时间选项 - let claims = jwt_compact::Claims::new(claims) // 创建JWT的有效载荷 + Duration::seconds(0), + Utc::now + ); + let claims = jwt_compact::Claims::new(claims) .set_duration_and_issuance(&time_options, duration) - .set_not_before(Utc::now()); // 设置不早于时间 + .set_not_before(Utc::now()); - let header = Header::empty(); // 创建自定义的头部 + let header = Header::empty(); - let token = Ed25519.token(&header, &claims, &signing_key)?; // 使用Ed25519签名JWT + let token = Ed25519.token(&header, &claims, &signing_key)?; Ok(token) } -/** - * 验证JWT并返回自定义声明。 - */ pub fn validate_jwt(token: &str) -> Result> { - let key_bytes = get_key(SecretKey::Verifying)?; // 从文件读取验证密钥 - let verifying = VerifyingKey::from_bytes(&key_bytes)?; // 创建VerifyingKey - let token = UntrustedToken::new(token)?; // 创建未受信任的Token - let token: Token = Ed25519.validator(&verifying).validate(&token)?; // 验证Token + let key_bytes = get_key(SecretKey::Verifying)?; + let verifying = VerifyingKey::from_bytes(&key_bytes)?; + let token = UntrustedToken::new(token)?; + let token: Token = Ed25519.validator(&verifying).validate(&token)?; let time_options = TimeOptions::new( - Duration::seconds(0), // 设置时间容差为0 - Utc::now // 使用当前UTC时间作为时钟函数 - ); // 设置时间容差为); // 默认时间选项 + Duration::seconds(0), + Utc::now + ); token.claims() - .validate_expiration(&time_options)? // 验证过期时间 - .validate_maturity(&time_options)?; // 验证成熟时间 - let claims = token.claims().custom.clone(); // 获取自定义声明 + .validate_expiration(&time_options)? + .validate_maturity(&time_options)?; + let claims = token.claims().custom.clone(); Ok(claims) } \ No newline at end of file diff --git a/frontend/components/ErrorBoundary.tsx b/frontend/components/ErrorBoundary.tsx index 645a385..0af1cf9 100644 --- a/frontend/components/ErrorBoundary.tsx +++ b/frontend/components/ErrorBoundary.tsx @@ -1,4 +1,3 @@ -// File path: components/ErrorBoundary.tsx import React, { Component, ErrorInfo, ReactNode } from 'react'; import { ThemeService } from '../services/themeService'; @@ -28,11 +27,9 @@ export default class ErrorBoundary extends Component { if (this.state.hasError) { const themeService = ThemeService.getInstance(); try { - // 尝试使用主题的错误模板 const errorTemplate = themeService.getTemplate('error'); return
; } catch (e) { - // 如果无法获取主题模板,显示默认错误页面 return (

Something went wrong

diff --git a/frontend/components/LoadingBoundary.tsx b/frontend/components/LoadingBoundary.tsx index c590ccc..21919dc 100644 --- a/frontend/components/LoadingBoundary.tsx +++ b/frontend/components/LoadingBoundary.tsx @@ -1,4 +1,3 @@ -// File path: components/LoadingBoundary.tsx import React, { Suspense } from 'react'; interface LoadingBoundaryProps { diff --git a/frontend/contracts/capabilityContract.ts b/frontend/contracts/capabilityContract.ts index 7d95372..b6c7930 100644 --- a/frontend/contracts/capabilityContract.ts +++ b/frontend/contracts/capabilityContract.ts @@ -1,13 +1,5 @@ -// src/contracts/CapabilityContract.ts -/** - * 能力契约接口 - */ export interface CapabilityProps { - // 能力名称 name: string; - // 能力描述 description?: string; - // 能力执行函数 execute: (...args: any[]) => Promise; } - \ No newline at end of file diff --git a/frontend/contracts/generalContract.ts b/frontend/contracts/generalContract.ts index c839311..ce6dcd4 100644 --- a/frontend/contracts/generalContract.ts +++ b/frontend/contracts/generalContract.ts @@ -1,12 +1,9 @@ -// File path: contracts\generalContract.ts -/** - * 表示可以序列化的类型。 - * 可以是以下类型之一: - * - null - * - number - * - string - * - boolean - * - 对象,键为字符串,值为可序列化类型 - * - 数组,元素为可序列化类型 - */ export type SerializeType = null | number | string | boolean | { [key: string]: SerializeType } | Array; +export interface Configuration { + [key: string]: { + title: string; + description?: string; + data: SerializeType; + }; +} + diff --git a/frontend/contracts/pluginContract.ts b/frontend/contracts/pluginContract.ts index d88a326..906dd2f 100644 --- a/frontend/contracts/pluginContract.ts +++ b/frontend/contracts/pluginContract.ts @@ -1,40 +1,18 @@ -// File path: contracts\pluginContract.ts - -/** - * 插件配置接口 - * - * 该接口定义了插件的基本配置,包括插件的名称、版本、描述、作者等信息。 - * 还包括插件的生命周期钩子和依赖项的配置。 - */ -import { SerializeType } from "contracts/generalContract"; - +import { Configuration } from "contracts/generalContract"; export interface PluginConfig { - name: string; // 插件名称 - version: string; // 插件版本 - displayName: string; // 插件显示名称 - description?: string; // 插件描述(可选) - author?: string; // 插件作者(可选) - enabled: boolean; // 插件是否启用 - icon?: string; // 插件图标URL(可选) - managePath?: string; // 插件管理页面路径(可选) - configuration?: PluginConfiguration; // 插件配置 + name: string; + version: string; + displayName: string; + description?: string; + author?: string; + enabled: boolean; + icon?: string; + managePath?: string; + configuration?: Configuration; routs: Set<{ - description?: string; // 路由描述(可选) - path: string; // 路由路径 + description?: string; + path: string; }>; } -/** - * 插件配置接口 - * - * 该接口定义了插件的配置类型及其属性。 - */ -export interface PluginConfiguration { - [key: string]: { - title: string; // 属性标题 - description?: string; // 属性描述(可选) - data: SerializeType; // 额外数据(可选),支持序列化 - }; -} - diff --git a/frontend/contracts/templateContract.ts b/frontend/contracts/templateContract.ts index 69e1adf..082dba6 100644 --- a/frontend/contracts/templateContract.ts +++ b/frontend/contracts/templateContract.ts @@ -1,17 +1,11 @@ export interface TemplateContract { - // 模板名称 name: string; - // 模板描述 description?: string; - // 模板配置 config: { - // 模板布局 layout?: string; - // 模板样式 styles?: string[]; - // 模板脚本 scripts?: string[]; }; - // 渲染函数 - render: (props: any) => React.ReactNode; - } \ No newline at end of file + loader: () => Promise; + element: () => React.ReactNode; +} \ No newline at end of file diff --git a/frontend/contracts/themeContract.ts b/frontend/contracts/themeContract.ts index 08ddcb3..ac08ebd 100644 --- a/frontend/contracts/themeContract.ts +++ b/frontend/contracts/themeContract.ts @@ -1,50 +1,29 @@ -// File path: contracts\themeTypeContract.ts -/** - * 主题配置和模板接口定义文件 - * 该文件包含主题配置接口和主题模板接口的定义,用于主题管理和渲染。 - */ - -/** - * 主题配置接口 - * 定义主题的基本信息、模板、全局配置、依赖、钩子和路由。 - */ -import { SerializeType } from "contracts/generalContract"; +import { Configuration } from "contracts/generalContract"; export interface ThemeConfig { - name: string; // 主题的名称 - displayName: string; // 主题的显示名称 - icon?: string; // 主题图标URL(可选) - version: string; // 主题的版本号 - description?: string; // 主题的描述信息 - author?: string; // 主题的作者信息 - templates: Map; // 主题模板的映射表 - /** 主题全局配置 */ + name: string; + displayName: string; + icon?: string; + version: string; + description?: string; + author?: string; + templates: Map; globalSettings?: { - layout?: string; // 主题的布局配置 - css?: string; // 主题的CSS配置 + layout?: string; + css?: string; }; - /** 主题配置文件 */ - settingsSchema: Record; - /** 路由 */ + configuration: Configuration; routes: { - index: string; // 首页使用的模板 - post: string; // 文章使用的模板 - tag: string; // 标签使用的模板 - category: string; // 分类使用的模板 - error: string; // 错误页面用的模板 - page: Map; // 独立页面模板 + index: string; + post: string; + tag: string; + category: string; + error: string; + page: Map; } } -/** - * 主题模板接口 - * 定义主题模板的基本信息,包括路径、名称和描述。 - */ export interface ThemeTemplate { - path: string; // 模板文件的路径 - name: string; // 模板的名称 - description?: string; // 模板的描述信息 + path: string; + name: string; + description?: string; } diff --git a/frontend/hooks/createServiceContext.tsx b/frontend/hooks/createServiceContext.tsx index bf5934a..4e20a48 100644 --- a/frontend/hooks/createServiceContext.tsx +++ b/frontend/hooks/createServiceContext.tsx @@ -1,40 +1,23 @@ -// File path: /hooks/createServiceContext.tsx - import { createContext, useContext, ReactNode, FC } from 'react'; -/** - * 创建服务上下文的返回类型 - */ type ServiceContextReturn = { [K in `${N}Provider`]: FC<{ children: ReactNode }>; } & { [K in `use${N}`]: () => T; }; -/** - * 创建服务上下文和相关钩子的工厂函数 - * - * @param serviceName - 服务名称,用于错误信息 - * @param ServiceClass - 服务类(包含静态 getInstance 方法) - */ export function createServiceContext( serviceName: N, getServiceInstance: () => T ): ServiceContextReturn { const ServiceContext = createContext(undefined); - /** - * 服务提供者组件 - */ const Provider: FC<{ children: ReactNode }> = ({ children }) => ( {children} ); - /** - * 自定义钩子,用于获取服务实例 - */ const useService = (): T => { const context = useContext(ServiceContext); if (context === undefined) { diff --git a/frontend/hooks/servicesProvider.tsx b/frontend/hooks/servicesProvider.tsx index 5df67b2..6f16ddd 100644 --- a/frontend/hooks/servicesProvider.tsx +++ b/frontend/hooks/servicesProvider.tsx @@ -16,13 +16,6 @@ export const { ApiProvider, useApi } = createServiceContext( "Api", () => ThemeService.getInstance(), ); - -// File path:hooks/servicesProvider.tsx -/** - * ServiceProvider 组件用于提供扩展和主题上下文给其子组件。 - * - * @param children - 要渲染的子组件。 - */ export const ServiceProvider = ({ children }: { children: ReactNode }) => ( diff --git a/frontend/hooks/useAsyncError.tsx b/frontend/hooks/useAsyncError.tsx deleted file mode 100644 index 7f16f37..0000000 --- a/frontend/hooks/useAsyncError.tsx +++ /dev/null @@ -1,15 +0,0 @@ -// File path: hooks/useAsyncError.tsx -import { useState, useCallback } from 'react'; - -export function useAsyncError() { - const [, setError] = useState(); - - return useCallback( - (e: Error) => { - setError(() => { - throw e; - }); - }, - [setError], - ); -} \ No newline at end of file diff --git a/frontend/services/apiService.ts b/frontend/services/apiService.ts index 9b4ef4d..6cd7421 100644 --- a/frontend/services/apiService.ts +++ b/frontend/services/apiService.ts @@ -1,32 +1,18 @@ -// File path: /d:/data/echoes/frontend/services/apiService.ts - -/** - * ApiConfig接口用于配置API服务的基本信息。 - */ interface ApiConfig { - baseURL: string; // API的基础URL - timeout?: number; // 请求超时时间(可选) + baseURL: string; + timeout?: number; } export class ApiService { - private static instance: ApiService; // ApiService的单例实例 - private baseURL: string; // API的基础URL - private timeout: number; // 请求超时时间 + private static instance: ApiService; + private baseURL: string; + private timeout: number; - /** - * 构造函数用于初始化ApiService实例。 - * @param config ApiConfig配置对象 - */ private constructor(config: ApiConfig) { this.baseURL = config.baseURL; - this.timeout = config.timeout || 10000; // 默认超时时间为10000毫秒 + this.timeout = config.timeout || 10000; } - /** - * 获取ApiService的单例实例。 - * @param config 可选的ApiConfig配置对象 - * @returns ApiService实例 - */ public static getInstance(config?: ApiConfig): ApiService { if (!this.instance && config) { this.instance = new ApiService(config); @@ -34,11 +20,6 @@ export class ApiService { return this.instance; } - /** - * 获取系统令牌。 - * @returns Promise 返回系统令牌 - * @throws Error 如果未找到凭据或请求失败 - */ private async getSystemToken(): Promise { const username = import.meta.env.VITE_SYSTEM_USERNAME; const password = import.meta.env.VITE_SYSTEM_PASSWORD; @@ -63,25 +44,17 @@ export class ApiService { } const data = await response.text(); - return data; // Assuming the token is in the 'token' field of the response + return data; } catch (error) { console.error('Error getting system token:', error); throw error; } } - /** - * 发起API请求。 - * @param endpoint 请求的API端点 - * @param options 请求选项 - * @param requiresAuth 是否需要身份验证(默认为true) - * @returns Promise 返回API响应数据 - * @throws Error 如果请求超时或发生其他错误 - */ public async request( endpoint: string, options: RequestInit = {}, - auth ?: string + toekn ?: string ): Promise { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.timeout); @@ -89,8 +62,8 @@ export class ApiService { try { const headers = new Headers(options.headers); - if (auth) { - headers.set('Authorization', `Bearer ${auth}`); + if (toekn) { + headers.set('Authorization', `Bearer ${toekn}`); } const response = await fetch(`${this.baseURL}${endpoint}`, { @@ -118,4 +91,4 @@ export class ApiService { export default ApiService.getInstance({ baseURL: import.meta.env.VITE_API_BASE_URL, -}); \ No newline at end of file +}); diff --git a/frontend/services/capabilityService.ts b/frontend/services/capabilityService.ts index 3b5e2c5..1a2c4be 100644 --- a/frontend/services/capabilityService.ts +++ b/frontend/services/capabilityService.ts @@ -1,29 +1,14 @@ -// File path: services/capabilityService.ts - -/** - * CapabilityService 是一个单例类,用于管理能力的实例。 - * 提供注册、执行和移除能力的功能。 - */ import { CapabilityProps } from "contracts/capabilityContract"; export class CapabilityService { - // 存储能力的映射,键为能力名称,值为能力源和能力属性的集合 private capabilities: Map}>> = new Map(); - // CapabilityService 的唯一实例 private static instance: CapabilityService; - /** - * 私有构造函数,防止外部实例化 - */ private constructor() { } - /** - * 获取 CapabilityService 的唯一实例。 - * @returns {CapabilityService} 返回 CapabilityService 的唯一实例。 - */ public static getInstance(): CapabilityService { if (!this.instance) { this.instance = new CapabilityService(); @@ -31,23 +16,11 @@ export class CapabilityService { return this.instance; } - /** - * 注册能力 - * @param capabilityName 能力名称 - * @param source 能力来源 - * @param capability 能力属性 - */ private register(capabilityName: string, source: string, capability: CapabilityProps) { const handlers = this.capabilities.get(capabilityName) || new Set(); handlers.add({ source, capability }); } - /** - * 执行指定能力的方法 - * @param capabilityName 能力名称 - * @param args 方法参数 - * @returns {Set} 执行结果的集合 - */ private executeCapabilityMethod(capabilityName: string, ...args: any[]): Set { const results = new Set(); const handlers = this.capabilities.get(capabilityName); @@ -65,10 +38,6 @@ export class CapabilityService { return results; } - /** - * 移除指定来源的能力 - * @param source 能力来源 - */ private removeCapability(source: string) { this.capabilities.forEach((capability_s, capabilityName) => { const newHandlers = new Set( @@ -78,28 +47,20 @@ export class CapabilityService { }); } - /** - * 移除指定能力 - * @param capability 能力名称 - */ private removeCapabilitys(capability: string) { this.capabilities.delete(capability); } public validateCapability(capability: CapabilityProps): boolean { - // 验证能力是否符合基本要求 if (!capability.name || !capability.execute) { return false; } - // 验证能力名称格式 const namePattern = /^[a-z][a-zA-Z0-9_]*$/; if (!namePattern.test(capability.name)) { return false; } return true; - } - - + } } diff --git a/frontend/services/pluginService.ts b/frontend/services/pluginService.ts index 7098c7e..b36ec4a 100644 --- a/frontend/services/pluginService.ts +++ b/frontend/services/pluginService.ts @@ -1,4 +1,3 @@ -// src/core/PluginManager.ts import { PluginConfiguration } from 'types/pluginRequirement'; import { Contracts } from 'contracts/capabilityContract'; @@ -8,7 +7,6 @@ export class PluginManager { private extensions: Map = new Map(); async loadPlugins() { - // 扫描插件目录 const pluginDirs = await this.scanPluginDirectory(); for (const dir of pluginDirs) { @@ -16,22 +14,18 @@ export class PluginManager { const config = await import(`@/plugins/${dir}/plugin.config.ts`); const plugin: PluginProps = config.default; - // 注册插件 this.plugins.set(plugin.name, plugin); - // 加载默认配置 if (plugin.settingsSchema) { this.configurations.set(plugin.name, plugin.settingsSchema); } - // 注册扩展 if (plugin.extensions) { Object.entries(plugin.extensions).forEach(([key, value]) => { this.extensions.set(`${plugin.name}.${key}`, value.extension); }); } - // 执行安装钩子 if (plugin.hooks?.onInstall) { await plugin.hooks.onInstall({}); } @@ -41,24 +35,19 @@ export class PluginManager { } } - // 获取插件配置 async getPluginConfig(pluginName: string): Promise { - // 先尝试从数据库获取 const dbConfig = await this.fetchConfigFromDB(pluginName); if (dbConfig) { return dbConfig; } - // 返回默认配置 return this.configurations.get(pluginName); } private async fetchConfigFromDB(pluginName: string) { - // 实现数据库查询逻辑 return null; } private async scanPluginDirectory(): Promise { - // 实现插件目录扫描逻辑 return []; } -} \ No newline at end of file +} diff --git a/frontend/services/routeManager.ts b/frontend/services/routeManager.ts index f205a51..dcc7705 100644 --- a/frontend/services/routeManager.ts +++ b/frontend/services/routeManager.ts @@ -1,88 +1,40 @@ -// File path: services/routeManager.ts +import React from 'react'; // Import React import { useEffect } from 'react'; -import { useRoutes, RouteObject } from 'react-router-dom'; -import { ThemeService } from './themeService'; -import ErrorBoundary from '../components/ErrorBoundary'; +import { LoaderFunction, RouteObject } from 'react-router-dom'; export class RouteManager { private static instance: RouteManager; - private themeService: ThemeService; private routes: RouteObject[] = []; - private constructor(themeService: ThemeService) { - this.themeService = themeService; - } + private constructor() {} - public static getInstance(themeService?: ThemeService): RouteManager { - if (!RouteManager.instance && themeService) { - RouteManager.instance = new RouteManager(themeService); + public static getInstance(): RouteManager { + if (!RouteManager.instance) { + RouteManager.instance = new RouteManager(); } return RouteManager.instance; } - /** - * 初始化路由 - */ - public async initialize(): Promise { - const themeConfig = this.themeService.getThemeConfig(); - if (!themeConfig) { - throw new Error('Theme configuration not loaded'); - } - - this.routes = [ - { - path: '/', - element: this.createRouteElement(themeConfig.routes.index), - errorElement: , - }, - { - path: '/post/:id', - element: this.createRouteElement(themeConfig.routes.post), - }, - { - path: '/tag/:tag', - element: this.createRouteElement(themeConfig.routes.tag), - }, - { - path: '/category/:category', - element: this.createRouteElement(themeConfig.routes.category), - }, - { - path: '*', - element: this.createRouteElement(themeConfig.routes.error), - }, - ]; - - // 添加自定义页面路由 - themeConfig.routes.page.forEach((template, path) => { - this.routes.push({ - path, - element: this.createRouteElement(template), - }); + private register(path:string, element: React.ReactNode) { + this.routes.push({ + path, + element, }); } - /** - * 创建路由元素 - */ - private createRouteElement(templateName: string) { - return (props: any) => { - const template = this.themeService.getTemplate(templateName); - // 这里可以添加模板渲染逻辑 - return
; - }; + + private createRouteElement(path: string,element:React.ReactNode,loader?:LoaderFunction) { + this.routes.push({ + path, + element, + loader?: loader + }) } - /** - * 获取所有路由 - */ public getRoutes(): RouteObject[] { return this.routes; } - /** - * 添加新路由 - */ public addRoute(path: string, templateName: string): void { this.routes.push({ path, diff --git a/frontend/services/themeService.ts b/frontend/services/themeService.ts index 5997ddd..040beb1 100644 --- a/frontend/services/themeService.ts +++ b/frontend/services/themeService.ts @@ -1,4 +1,3 @@ -// File path: services/themeService.ts import { ThemeConfig, ThemeTemplate } from "contracts/themeContract"; import { ApiService } from "./apiService"; @@ -19,18 +18,12 @@ export class ThemeService { return ThemeService.instance; } - /** - * 初始化主题服务 - */ public async initialize(): Promise { try { - // 从API获取当前主题配置 const themeConfig = await this.api.request( '/theme/current', { method: 'GET' } ); - - // 加载主题配置 await this.loadTheme(themeConfig); } catch (error) { console.error('Failed to initialize theme:', error); @@ -38,9 +31,6 @@ export class ThemeService { } } - /** - * 加载主题配置 - */ private async loadTheme(config: ThemeConfig): Promise { try { this.currentTheme = config; @@ -51,9 +41,6 @@ export class ThemeService { } } - /** - * 加载主题模板 - */ private async loadTemplates(): Promise { if (!this.currentTheme) { throw new Error('No theme configuration loaded'); @@ -70,23 +57,16 @@ export class ThemeService { } }; - // 并行加载所有模板 const loadPromises = Array.from(this.currentTheme.templates.values()) .map(template => loadTemplate(template)); await Promise.all(loadPromises); } - /** - * 获取主题配置 - */ public getThemeConfig(): ThemeConfig | undefined { return this.currentTheme; } - /** - * 获取模板内容 - */ public getTemplate(templateName: string): string { const template = this.templates.get(templateName); if (!template) { @@ -95,9 +75,6 @@ export class ThemeService { return template; } - /** - * 根据路由获取对应的模板 - */ public getTemplateByRoute(route: string): string { if (!this.currentTheme) { throw new Error('No theme configuration loaded'); @@ -105,7 +82,6 @@ export class ThemeService { let templateName: string | undefined; - // 检查是否是预定义路由 if (route === '/') { templateName = this.currentTheme.routes.index; } else if (route.startsWith('/post/')) { @@ -115,7 +91,6 @@ export class ThemeService { } else if (route.startsWith('/category/')) { templateName = this.currentTheme.routes.category; } else { - // 检查自定义页面路由 templateName = this.currentTheme.routes.page.get(route); } @@ -126,9 +101,6 @@ export class ThemeService { return this.getTemplate(templateName); } - /** - * 更新主题配置 - */ public async updateThemeConfig(config: Partial): Promise { try { const updatedConfig = await this.api.request(