后端数据库接口统一使用数据库配置文件来传输配置信息,给后端代码加上注释,前端正在实现动态加载主题和插件
This commit is contained in:
parent
c2bb2d21d9
commit
ddbb770923
@ -1,22 +0,0 @@
|
|||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct Config {
|
|
||||||
pub info: Info,
|
|
||||||
pub database: Database,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct Info {
|
|
||||||
pub install: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct Database {
|
|
||||||
pub db_type : String,
|
|
||||||
pub address : String,
|
|
||||||
pub prot : u32,
|
|
||||||
pub user : String,
|
|
||||||
pub password : String,
|
|
||||||
pub db_name : String,
|
|
||||||
}
|
|
@ -1,3 +1,6 @@
|
|||||||
|
# config/config.toml
|
||||||
|
# 配置文件
|
||||||
|
|
||||||
[info]
|
[info]
|
||||||
install = false
|
install = false
|
||||||
|
|
34
backend/src/config/mod.rs
Normal file
34
backend/src/config/mod.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// config/mod.rs
|
||||||
|
/*
|
||||||
|
配置文件结构和操作
|
||||||
|
*/
|
||||||
|
use std::fs;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
pub info: Info,
|
||||||
|
pub database: Database,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Info {
|
||||||
|
pub install: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Database {
|
||||||
|
pub db_type: String,
|
||||||
|
pub address: String,
|
||||||
|
pub prot: u32,
|
||||||
|
pub user: String,
|
||||||
|
pub password: String,
|
||||||
|
pub db_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
/// 读取配置文件
|
||||||
|
pub fn read(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
|
Ok(toml::from_str(&fs::read_to_string(path)?)?)
|
||||||
|
}
|
||||||
|
}
|
@ -1,68 +1,66 @@
|
|||||||
// main.rs
|
// main.rs
|
||||||
mod sql;
|
|
||||||
mod config;
|
mod config;
|
||||||
use rocket::{ get, launch, routes};
|
mod sql;
|
||||||
use rocket::serde::json::Json; // Added import for Json
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use rocket::http::Status;
|
|
||||||
use rocket::response::status;
|
|
||||||
use std::sync::Arc; // Added import for Arc and Mutex
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
use crate::sql::Database;
|
use crate::sql::Database;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use rocket::{get, http::Status, launch, response::status, routes, serde::json::Json};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
// 修改全局变量的类型定义
|
/* 修改全局变量的类型定义 */
|
||||||
static GLOBAL_SQL: Lazy<Arc<Mutex<Option<Database>>>> = Lazy::new(|| {
|
static DB: Lazy<Arc<Mutex<Option<Database>>>> = Lazy::new(|| Arc::new(Mutex::new(None)));
|
||||||
Arc::new(Mutex::new(None))
|
|
||||||
});
|
|
||||||
|
|
||||||
// 修改数据库连接函数
|
/* 数据库连接函数 */
|
||||||
async fn connect_database() -> Result<(), Box<dyn std::error::Error>> {
|
async fn init_db(database: config::Database) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let database = sql::Database::init().await?;
|
let database = Database::init(database).await?;
|
||||||
let mut lock = GLOBAL_SQL.lock().await;
|
*DB.lock().await = Some(database);
|
||||||
*lock = Some(database);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
/* 获取数据库的引用 */
|
||||||
async fn get_db() -> Result<Database, Box<dyn std::error::Error>> {
|
async fn get_db() -> Result<Database, Box<dyn std::error::Error>> {
|
||||||
let lock = GLOBAL_SQL.lock().await;
|
DB.lock()
|
||||||
match &*lock {
|
.await
|
||||||
Some(db) => Ok(db.clone()),
|
.clone()
|
||||||
None => Err("Database not initialized".into())
|
.ok_or_else(|| "Database not initialized".into())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
/* 用于测试数据库 */
|
||||||
|
|
||||||
#[get("/sql")]
|
#[get("/sql")]
|
||||||
async fn ssql() -> Result<Json<Vec<std::collections::HashMap<String, String>>>, status::Custom<String>> {
|
async fn ssql(
|
||||||
|
) -> Result<Json<Vec<std::collections::HashMap<String, String>>>, status::Custom<String>> {
|
||||||
let db = get_db().await.map_err(|e| {
|
let db = get_db().await.map_err(|e| {
|
||||||
eprintln!("Database error: {}", e);
|
status::Custom(
|
||||||
status::Custom(Status::InternalServerError, format!("Database error: {}", e))
|
Status::InternalServerError,
|
||||||
|
format!("Database error: {}", e),
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let query_result = db.get_db()
|
let query_result = db
|
||||||
.query("SELECT * FROM info".to_string()) // 确保这里是正确的表名
|
.get_db()
|
||||||
|
.query("SELECT * FROM info".to_string())
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| status::Custom(Status::InternalServerError, format!("Query error: {}", e)))?;
|
||||||
eprintln!("Query error: {}", e);
|
|
||||||
status::Custom(Status::InternalServerError, format!("Query error: {}", e))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(Json(query_result))
|
Ok(Json(query_result))
|
||||||
}
|
}
|
||||||
|
/* 安装接口 */
|
||||||
|
|
||||||
#[get("/install")]
|
#[get("/install")]
|
||||||
async fn install() -> status::Custom<String> {
|
async fn install() -> status::Custom<String> {
|
||||||
match connect_database().await {
|
get_db()
|
||||||
Ok(_) => status::Custom(Status::Ok, "Database connected successfully".to_string()),
|
.await
|
||||||
Err(e) => status::Custom(Status::InternalServerError, format!("Failed to connect: {}", e))
|
.map(|_| status::Custom(Status::Ok, "Database connected successfully".into()))
|
||||||
}
|
.unwrap_or_else(|e| {
|
||||||
|
status::Custom(
|
||||||
|
Status::InternalServerError,
|
||||||
|
format!("Failed to connect: {}", e),
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
/* 启动函数 */
|
||||||
|
|
||||||
#[launch]
|
#[launch]
|
||||||
async fn rocket() -> _ {
|
async fn rocket() -> _ {
|
||||||
connect_database().await.expect("Failed to connect to database");
|
let config = config::Config::read("./src/config/config.toml").expect("Failed to read config");
|
||||||
rocket::build()
|
init_db(config.database)
|
||||||
.mount("/api", routes![install,ssql])
|
.await
|
||||||
}
|
.expect("Failed to connect to database");
|
||||||
|
rocket::build().mount("/api", routes![install, ssql])
|
||||||
|
}
|
||||||
|
@ -1,57 +1,42 @@
|
|||||||
// mod.rs
|
// sql/mod.rs
|
||||||
|
/*
|
||||||
|
定义了数据库具有的特征和方法
|
||||||
|
*/
|
||||||
mod postgresql;
|
mod postgresql;
|
||||||
use std::{collections::HashMap, fs};
|
use std::collections::HashMap;
|
||||||
use toml;
|
use super::config;
|
||||||
use crate::config::Config;
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Databasetrait: Send + Sync {
|
pub trait DatabaseTrait: Send + Sync {
|
||||||
async fn connect(
|
// 连接数据库
|
||||||
address: String,
|
async fn connect(database: config::Database) -> Result<Self, Box<dyn Error>> where Self: Sized;
|
||||||
port: u32,
|
// 执行查询
|
||||||
user: String,
|
|
||||||
password: String,
|
|
||||||
dbname: String,
|
|
||||||
) -> Result<Self, Box<dyn Error>> where Self: Sized;
|
|
||||||
async fn query<'a>(&'a self, query: String) -> Result<Vec<HashMap<String, String>>, Box<dyn Error + 'a>>;
|
async fn query<'a>(&'a self, query: String) -> Result<Vec<HashMap<String, String>>, Box<dyn Error + 'a>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Database {
|
pub struct Database {
|
||||||
pub db: Arc<Box<dyn Databasetrait>>,
|
// 数据库实例
|
||||||
|
pub db: Arc<Box<dyn DatabaseTrait>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Database {
|
impl Database {
|
||||||
pub fn get_db(&self) -> &Box<dyn Databasetrait> {
|
// 获取当前数据库实例
|
||||||
|
pub fn get_db(&self) -> &Box<dyn DatabaseTrait> {
|
||||||
&self.db
|
&self.db
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// 初始化数据库
|
||||||
|
pub async fn init(database: config::Database) -> Result<Self, Box<dyn Error>> {
|
||||||
|
let db = match database.db_type.as_str() {
|
||||||
|
"postgresql" => postgresql::Postgresql::connect(database).await?,
|
||||||
|
_ => return Err("unknown database type".into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self { db: Arc::new(Box::new(db)) })
|
||||||
|
|
||||||
impl Database {
|
|
||||||
pub async fn init() -> Result<Database, Box<dyn Error>> {
|
|
||||||
let config_string = fs::read_to_string("./src/config.toml")
|
|
||||||
.map_err(|e| Box::new(e) as Box<dyn Error>)?;
|
|
||||||
let config: Config = toml::from_str(&config_string)
|
|
||||||
.map_err(|e| Box::new(e) as Box<dyn Error>)?;
|
|
||||||
|
|
||||||
match config.database.db_type.as_str() {
|
|
||||||
"postgresql" => {
|
|
||||||
let db = postgresql::Postgresql::connect(
|
|
||||||
config.database.address,
|
|
||||||
config.database.prot,
|
|
||||||
config.database.user,
|
|
||||||
config.database.password,
|
|
||||||
config.database.db_name,
|
|
||||||
).await?;
|
|
||||||
Ok(Database {
|
|
||||||
db: Arc::new(Box::new(db))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_ => Err(anyhow::anyhow!("unknown database type").into()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,28 @@
|
|||||||
|
// sql/psotgresql.rs
|
||||||
|
/*
|
||||||
|
为postgresql数据库实现具体的方法
|
||||||
|
*/
|
||||||
|
use super::DatabaseTrait;
|
||||||
|
use crate::config;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use sqlx::{PgPool, Row,Column};
|
use sqlx::{Column, PgPool, Row};
|
||||||
use std::{collections::HashMap, error::Error};
|
use std::{collections::HashMap, error::Error};
|
||||||
use super::Databasetrait;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Postgresql {
|
pub struct Postgresql {
|
||||||
pool: PgPool,
|
pool: PgPool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Databasetrait for Postgresql {
|
impl DatabaseTrait for Postgresql {
|
||||||
async fn connect(
|
async fn connect(database: config::Database) -> Result<Self, Box<dyn Error>> {
|
||||||
address: String,
|
|
||||||
port: u32,
|
|
||||||
user: String,
|
|
||||||
password: String,
|
|
||||||
dbname: String,
|
|
||||||
) -> Result<Self, Box<dyn Error>> {
|
|
||||||
let connection_str = format!(
|
let connection_str = format!(
|
||||||
"postgres://{}:{}@{}:{}/{}",
|
"postgres://{}:{}@{}:{}/{}",
|
||||||
user, password, address, port, dbname
|
database.user,
|
||||||
|
database.password,
|
||||||
|
database.address,
|
||||||
|
database.prot,
|
||||||
|
database.db_name
|
||||||
);
|
);
|
||||||
|
|
||||||
let pool = PgPool::connect(&connection_str)
|
let pool = PgPool::connect(&connection_str)
|
||||||
@ -30,7 +31,10 @@ impl Databasetrait for Postgresql {
|
|||||||
|
|
||||||
Ok(Postgresql { pool })
|
Ok(Postgresql { pool })
|
||||||
}
|
}
|
||||||
async fn query<'a>(&'a self, query: String) -> Result<Vec<HashMap<String, String>>, Box<dyn Error + 'a>> {
|
async fn query<'a>(
|
||||||
|
&'a self,
|
||||||
|
query: String,
|
||||||
|
) -> Result<Vec<HashMap<String, String>>, Box<dyn Error + 'a>> {
|
||||||
let rows = sqlx::query(&query)
|
let rows = sqlx::query(&query)
|
||||||
.fetch_all(&self.pool)
|
.fetch_all(&self.pool)
|
||||||
.await
|
.await
|
||||||
@ -49,4 +53,4 @@ impl Databasetrait for Postgresql {
|
|||||||
|
|
||||||
Ok(results)
|
Ok(results)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
61
frontend/hooks/plugin/usePluginLoader.ts
Normal file
61
frontend/hooks/plugin/usePluginLoader.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// hooks/usePluginLoader.ts
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { DependencyChecker } from '../../services/dependency-checker';
|
||||||
|
import { PerformanceMonitor } from '../../services/performance-monitor';
|
||||||
|
import { CacheManager } from '../../services/cache-manager';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件加载Hook
|
||||||
|
* @param pluginId 插件ID
|
||||||
|
* @returns 加载状态和错误信息
|
||||||
|
*/
|
||||||
|
export function usePluginLoader(pluginId: string) {
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadPlugin = async () => {
|
||||||
|
try {
|
||||||
|
// 检查缓存
|
||||||
|
const cached = CacheManager.get(`plugin:${pluginId}`);
|
||||||
|
if (cached) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始性能监控
|
||||||
|
const monitor = PerformanceMonitor.startMonitoring(pluginId);
|
||||||
|
|
||||||
|
// 获取插件配置
|
||||||
|
const plugin = await fetch(`/api/admin/plugins/${pluginId}`).then(r => r.json());
|
||||||
|
|
||||||
|
// 检查依赖
|
||||||
|
if (plugin.dependencies) {
|
||||||
|
const dependenciesOk = await DependencyChecker.checkDependencies(plugin.dependencies);
|
||||||
|
if (!dependenciesOk) {
|
||||||
|
throw new Error('依赖检查失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载插件
|
||||||
|
const component = await import(/* @vite-ignore */plugin.entry);
|
||||||
|
|
||||||
|
// 缓存结果
|
||||||
|
CacheManager.set(`plugin:${pluginId}`, component);
|
||||||
|
|
||||||
|
// 结束监控
|
||||||
|
monitor.end();
|
||||||
|
|
||||||
|
return component;
|
||||||
|
} catch (err) {
|
||||||
|
setError(err instanceof Error ? err.message : '插件加载失败');
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadPlugin();
|
||||||
|
}, [pluginId]);
|
||||||
|
|
||||||
|
return { loading, error };
|
||||||
|
}
|
33
frontend/hooks/theme/themeManager.tsx
Normal file
33
frontend/hooks/theme/themeManager.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// components/theme/themeManager.tsx
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useThemeLoader } from './useThemeLoader';
|
||||||
|
|
||||||
|
interface ThemeManagerProps {
|
||||||
|
themeName: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ThemeManager: React.FC<ThemeManagerProps> = ({
|
||||||
|
themeName,
|
||||||
|
children
|
||||||
|
}) => {
|
||||||
|
const { theme, loading, error } = useThemeLoader(themeName);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <div>加载主题中...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return <div>主题加载失败: {error}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!theme) {
|
||||||
|
return <div>主题未找到</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`theme-${themeName}`}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
27
frontend/hooks/theme/themeSwitch.tsx
Normal file
27
frontend/hooks/theme/themeSwitch.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// components/theme/themeSwitch.tsx
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface ThemeSwitchProps {
|
||||||
|
currentTheme: string;
|
||||||
|
availableThemes: string[];
|
||||||
|
onThemeChange: (themeName: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ThemeSwitch: React.FC<ThemeSwitchProps> = ({
|
||||||
|
currentTheme,
|
||||||
|
availableThemes,
|
||||||
|
onThemeChange
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<select
|
||||||
|
value={currentTheme}
|
||||||
|
onChange={(e) => onThemeChange(e.target.value)}
|
||||||
|
>
|
||||||
|
{availableThemes.map(theme => (
|
||||||
|
<option key={theme} value={theme}>
|
||||||
|
{theme}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
);
|
||||||
|
};
|
66
frontend/hooks/theme/useThemeLoader.ts
Normal file
66
frontend/hooks/theme/useThemeLoader.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// hooks/theme/useThemeLoader.ts
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { ThemeConfig } from '../../types/theme';
|
||||||
|
import { DependencyChecker } from '../../services/dependency-checker';
|
||||||
|
import { PerformanceMonitor } from '../../services/performance-monitor';
|
||||||
|
import { CacheManager } from '../../services/cache-manager';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主题加载Hook
|
||||||
|
*/
|
||||||
|
export function useThemeLoader(themeName: string) {
|
||||||
|
const [theme, setTheme] = useState<ThemeConfig | null>(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadTheme = async () => {
|
||||||
|
const monitor = PerformanceMonitor.startMonitoring(`theme:${themeName}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 检查缓存
|
||||||
|
const cached = CacheManager.get(`theme:${themeName}`);
|
||||||
|
if (cached) {
|
||||||
|
setTheme(cached);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取主题配置
|
||||||
|
const response = await fetch(`/api/themes/${themeName}`);
|
||||||
|
const themeConfig: ThemeConfig = await response.json();
|
||||||
|
|
||||||
|
// 3. 检查依赖
|
||||||
|
if (themeConfig.dependencies) {
|
||||||
|
const dependenciesOk = await DependencyChecker.checkDependencies(
|
||||||
|
themeConfig.dependencies
|
||||||
|
);
|
||||||
|
if (!dependenciesOk) {
|
||||||
|
throw new Error('主题依赖检查失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 加载主题样式
|
||||||
|
if (themeConfig.globalStyles) {
|
||||||
|
const styleElement = document.createElement('style');
|
||||||
|
styleElement.innerHTML = themeConfig.globalStyles;
|
||||||
|
document.head.appendChild(styleElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 缓存主题配置
|
||||||
|
CacheManager.set(`theme:${themeName}`, themeConfig, 3600000); // 1小时缓存
|
||||||
|
|
||||||
|
setTheme(themeConfig);
|
||||||
|
} catch (err) {
|
||||||
|
setError(err instanceof Error ? err.message : '加载主题失败');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
monitor.end();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadTheme();
|
||||||
|
}, [themeName]);
|
||||||
|
|
||||||
|
return { theme, loading, error };
|
||||||
|
}
|
41
frontend/services/cache-manager.ts
Normal file
41
frontend/services/cache-manager.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// services/cache-manager.ts
|
||||||
|
/**
|
||||||
|
* 缓存管理服务
|
||||||
|
*/
|
||||||
|
export class CacheManager {
|
||||||
|
private static cache = new Map<string, any>();
|
||||||
|
private static ttl = new Map<string, number>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置缓存
|
||||||
|
* @param key 缓存键
|
||||||
|
* @param value 缓存值
|
||||||
|
* @param expiresIn 过期时间(毫秒)
|
||||||
|
*/
|
||||||
|
static set(key: string, value: any, expiresIn: number = 3600000) {
|
||||||
|
this.cache.set(key, value);
|
||||||
|
this.ttl.set(key, Date.now() + expiresIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取缓存
|
||||||
|
* @param key 缓存键
|
||||||
|
* @returns 缓存值或null(如果已过期或不存在)
|
||||||
|
*/
|
||||||
|
static get(key: string): any {
|
||||||
|
if (this.ttl.has(key) && Date.now() > (this.ttl.get(key) || 0)) {
|
||||||
|
this.cache.delete(key);
|
||||||
|
this.ttl.delete(key);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.cache.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除所有缓存
|
||||||
|
*/
|
||||||
|
static clear() {
|
||||||
|
this.cache.clear();
|
||||||
|
this.ttl.clear();
|
||||||
|
}
|
||||||
|
}
|
29
frontend/services/dependency-checker.ts
Normal file
29
frontend/services/dependency-checker.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// services/dependency-checker.ts
|
||||||
|
|
||||||
|
import { Dependency } from "../types/common";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 依赖检查器服务
|
||||||
|
*/
|
||||||
|
export class DependencyChecker {
|
||||||
|
/**
|
||||||
|
* 检查依赖项是否满足要求
|
||||||
|
* @param dependencies 需要检查的依赖项列表
|
||||||
|
* @returns 是否所有依赖都满足要求
|
||||||
|
*/
|
||||||
|
static async checkDependencies(dependencies: Dependency[]): Promise<boolean> {
|
||||||
|
for (const dep of dependencies) {
|
||||||
|
const response = await fetch(`/api/dependencies/check`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(dep)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error(`依赖检查失败: ${dep.id}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
54
frontend/services/performance-monitor.ts
Normal file
54
frontend/services/performance-monitor.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// services/performance-monitor.ts
|
||||||
|
/**
|
||||||
|
* 性能指标接口
|
||||||
|
*/
|
||||||
|
interface PerformanceMetrics {
|
||||||
|
loadTime: number; // 加载时间(毫秒)
|
||||||
|
memoryUsage: number; // 内存使用量(bytes)
|
||||||
|
errors: string[]; // 错误信息列表
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 性能监控服务
|
||||||
|
*/
|
||||||
|
export class PerformanceMonitor {
|
||||||
|
private static metrics: Map<string, PerformanceMetrics> = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始监控性能
|
||||||
|
* @param id 监控对象的唯一标识符
|
||||||
|
*/
|
||||||
|
static startMonitoring(id: string) {
|
||||||
|
const startTime = performance.now();
|
||||||
|
const startMemory = (performance as any).memory?.usedJSHeapSize || 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
// 结束监控并记录指标
|
||||||
|
end: () => {
|
||||||
|
const loadTime = performance.now() - startTime;
|
||||||
|
const memoryUsage = ((performance as any).memory?.usedJSHeapSize || 0) - startMemory;
|
||||||
|
|
||||||
|
this.metrics.set(id, {
|
||||||
|
loadTime,
|
||||||
|
memoryUsage,
|
||||||
|
errors: []
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 记录错误信息
|
||||||
|
error: (err: string) => {
|
||||||
|
const metrics = this.metrics.get(id) || { loadTime: 0, memoryUsage: 0, errors: [] };
|
||||||
|
metrics.errors.push(err);
|
||||||
|
this.metrics.set(id, metrics);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取性能指标
|
||||||
|
* @param id 监控对象的唯一标识符
|
||||||
|
*/
|
||||||
|
static getMetrics(id: string): PerformanceMetrics | undefined {
|
||||||
|
return this.metrics.get(id);
|
||||||
|
}
|
||||||
|
}
|
39
frontend/services/plugin-communication.ts
Normal file
39
frontend/services/plugin-communication.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// services/plugin-communication.ts
|
||||||
|
/**
|
||||||
|
* 消息处理器类型定义
|
||||||
|
*/
|
||||||
|
type MessageHandler = (data: any) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件通信服务
|
||||||
|
*/
|
||||||
|
export class PluginMessenger {
|
||||||
|
// 存储所有消息处理器
|
||||||
|
private static handlers: Map<string, Set<MessageHandler>> = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订阅消息通道
|
||||||
|
* @param channel 通道名称
|
||||||
|
* @param handler 消息处理函数
|
||||||
|
* @returns 取消订阅函数
|
||||||
|
*/
|
||||||
|
static subscribe(channel: string, handler: MessageHandler) {
|
||||||
|
if (!this.handlers.has(channel)) {
|
||||||
|
this.handlers.set(channel, new Set());
|
||||||
|
}
|
||||||
|
this.handlers.get(channel)?.add(handler);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
this.handlers.get(channel)?.delete(handler);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布消息到指定通道
|
||||||
|
* @param channel 通道名称
|
||||||
|
* @param data 消息数据
|
||||||
|
*/
|
||||||
|
static publish(channel: string, data: any) {
|
||||||
|
this.handlers.get(channel)?.forEach(handler => handler(data));
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
//main.tsx
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import "./main.css"
|
import "./main.css"
|
||||||
import DynamicPage from "./page/page.tsx"
|
import DynamicPage from "./page/page.tsx"
|
||||||
@ -7,7 +8,7 @@ import {createContext} from "react";
|
|||||||
export const serverAddressContext=createContext("localhost:8080")
|
export const serverAddressContext=createContext("localhost:8080")
|
||||||
// 动态路由
|
// 动态路由
|
||||||
const RouterListener: React.FC = () => {
|
const RouterListener: React.FC = () => {
|
||||||
let pathname = location.pathname.split("/");
|
const pathname = location.pathname.split("/");
|
||||||
console.log(pathname)
|
console.log(pathname)
|
||||||
return (
|
return (
|
||||||
<serverAddressContext.Provider value={"localhost:8080"}>
|
<serverAddressContext.Provider value={"localhost:8080"}>
|
||||||
@ -17,7 +18,7 @@ const RouterListener: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root")).render(
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<RouterListener/>
|
<RouterListener/>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// page/page.tsx
|
||||||
import React from "react";
|
import React from "react";
|
||||||
const THEMEPATH= "../../themes"
|
const THEMEPATH= "../../themes"
|
||||||
import {serverAddressContext} from "../main.tsx";
|
import {serverAddressContext} from "../main.tsx";
|
||||||
|
18
frontend/types/common.ts
Normal file
18
frontend/types/common.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// types/common.ts
|
||||||
|
/**
|
||||||
|
* 版本信息接口
|
||||||
|
*/
|
||||||
|
export interface VersionInfo {
|
||||||
|
major: number; // 主版本号 - 不兼容的API修改
|
||||||
|
minor: number; // 次版本号 - 向下兼容的功能性新增
|
||||||
|
patch: number; // 修订号 - 向下兼容的问题修正
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 依赖项配置接口
|
||||||
|
*/
|
||||||
|
export interface Dependency {
|
||||||
|
id: string; // 依赖项的唯一标识符
|
||||||
|
version: string; // 依赖项的版本要求
|
||||||
|
type: 'plugin' | 'theme'; // 依赖项类型:插件或主题
|
||||||
|
}
|
23
frontend/types/plugin.ts
Normal file
23
frontend/types/plugin.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// types/plugin.ts
|
||||||
|
import {Dependency} from "./common";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件配置接口
|
||||||
|
*/
|
||||||
|
export interface PluginConfig {
|
||||||
|
id: string; // 插件唯一标识符
|
||||||
|
name: string; // 插件名称
|
||||||
|
version: string; // 插件版本
|
||||||
|
displayName: string; // 插件显示名称
|
||||||
|
description?: string; // 插件描述
|
||||||
|
author?: string; // 插件作者
|
||||||
|
enabled: boolean; // 插件是否启用
|
||||||
|
icon?: string; // 插件图标URL
|
||||||
|
adminPath?: string; // 插件管理页面路径
|
||||||
|
entry: string; // 插件入口组件路径
|
||||||
|
dependencies?: Dependency[]; // 插件依赖项
|
||||||
|
performance?: {
|
||||||
|
maxLoadTime?: number; // 最大加载时间(毫秒)
|
||||||
|
maxMemoryUsage?: number; // 最大内存使用量(bytes)
|
||||||
|
};
|
||||||
|
}
|
20
frontend/types/theme.ts
Normal file
20
frontend/types/theme.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// types/theme.ts
|
||||||
|
import {Dependency} from "./common.ts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主题配置接口
|
||||||
|
*/
|
||||||
|
export interface ThemeConfig {
|
||||||
|
name: string; // 主题的唯一名称标识符
|
||||||
|
version: string; // 主题版本
|
||||||
|
displayName: string; // 主题显示名称
|
||||||
|
description?: string; // 主题描述
|
||||||
|
author?: string; // 主题作者
|
||||||
|
preview?: string; // 主题预览图URL
|
||||||
|
globalStyles?: string; // 主题全局样式
|
||||||
|
dependencies?: Dependency[]; // 主题依赖项
|
||||||
|
performance?: {
|
||||||
|
maxLoadTime?: number; // 最大加载时间(毫秒)
|
||||||
|
maxMemoryUsage?: number; // 最大内存使用量(bytes)
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user