后端:重构postgre数据结构
前端:定义好主题,插件,上下文参数的接口
This commit is contained in:
parent
ddbb770923
commit
7f2a02dc61
@ -1,36 +1,64 @@
|
|||||||
// main.rs
|
// /d:/data/echoes/backend/src/main.rs
|
||||||
mod config;
|
|
||||||
mod sql;
|
|
||||||
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;
|
|
||||||
|
|
||||||
/* 修改全局变量的类型定义 */
|
/**
|
||||||
|
* 主程序入口,提供数据库连接和相关API接口。
|
||||||
|
*
|
||||||
|
* 接口:
|
||||||
|
* - GET /api/install: 测试数据库连接
|
||||||
|
* - GET /api/sql: 执行SQL查询并返回结果
|
||||||
|
*/
|
||||||
|
|
||||||
|
mod config; // 配置模块
|
||||||
|
mod sql; // SQL模块
|
||||||
|
use crate::sql::Database; // 引入数据库结构
|
||||||
|
use once_cell::sync::Lazy; // 用于延迟初始化
|
||||||
|
use rocket::{get, http::Status, launch, response::status, routes, serde::json::Json}; // 引入Rocket框架相关功能
|
||||||
|
use std::sync::Arc; // 引入Arc用于线程安全的引用计数
|
||||||
|
use tokio::sync::Mutex; // 引入Mutex用于异步锁
|
||||||
|
|
||||||
|
// 全局数据库连接变量
|
||||||
static DB: Lazy<Arc<Mutex<Option<Database>>>> = Lazy::new(|| Arc::new(Mutex::new(None)));
|
static DB: Lazy<Arc<Mutex<Option<Database>>>> = Lazy::new(|| Arc::new(Mutex::new(None)));
|
||||||
|
|
||||||
/* 数据库连接函数 */
|
/**
|
||||||
|
* 初始化数据库连接
|
||||||
|
*
|
||||||
|
* # 参数
|
||||||
|
* - `database`: 数据库配置
|
||||||
|
*
|
||||||
|
* # 返回
|
||||||
|
* - `Result<(), Box<dyn std::error::Error>>`: 初始化结果
|
||||||
|
*/
|
||||||
async fn init_db(database: config::Database) -> Result<(), Box<dyn std::error::Error>> {
|
async fn init_db(database: config::Database) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let database = Database::init(database).await?;
|
let database = Database::init(database).await?; // 初始化数据库
|
||||||
*DB.lock().await = Some(database);
|
*DB.lock().await = Some(database); // 保存数据库实例
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
/* 获取数据库的引用 */
|
|
||||||
|
/**
|
||||||
|
* 获取数据库的引用
|
||||||
|
*
|
||||||
|
* # 返回
|
||||||
|
* - `Result<Database, Box<dyn std::error::Error>>`: 数据库实例或错误
|
||||||
|
*/
|
||||||
async fn get_db() -> Result<Database, Box<dyn std::error::Error>> {
|
async fn get_db() -> Result<Database, Box<dyn std::error::Error>> {
|
||||||
DB.lock()
|
DB.lock()
|
||||||
.await
|
.await
|
||||||
.clone()
|
.clone()
|
||||||
.ok_or_else(|| "Database not initialized".into())
|
.ok_or_else(|| "Database not initialized".into()) // 返回错误信息
|
||||||
}
|
}
|
||||||
/* 用于测试数据库 */
|
|
||||||
|
/**
|
||||||
|
* 测试数据库接口
|
||||||
|
*
|
||||||
|
* # 返回
|
||||||
|
* - `Result<Json<Vec<std::collections::HashMap<String, String>>>, status::Custom<String>>`: 查询结果或错误
|
||||||
|
*/
|
||||||
#[get("/sql")]
|
#[get("/sql")]
|
||||||
async fn ssql(
|
async fn ssql() -> Result<Json<Vec<std::collections::HashMap<String, String>>>, status::Custom<String>> {
|
||||||
) -> 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| {
|
||||||
status::Custom(
|
status::Custom(
|
||||||
Status::InternalServerError,
|
Status::InternalServerError,
|
||||||
format!("Database error: {}", e),
|
format!("Database error: {}", e), // 数据库错误信息
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@ -40,27 +68,39 @@ async fn ssql(
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| status::Custom(Status::InternalServerError, format!("Query error: {}", e)))?;
|
.map_err(|e| status::Custom(Status::InternalServerError, format!("Query error: {}", e)))?;
|
||||||
|
|
||||||
Ok(Json(query_result))
|
Ok(Json(query_result)) // 返回查询结果
|
||||||
}
|
}
|
||||||
/* 安装接口 */
|
|
||||||
|
/**
|
||||||
|
* 数据库安装接口
|
||||||
|
*
|
||||||
|
* # 返回
|
||||||
|
* - `status::Custom<String>`: 连接成功或失败的信息
|
||||||
|
*/
|
||||||
#[get("/install")]
|
#[get("/install")]
|
||||||
async fn install() -> status::Custom<String> {
|
async fn install() -> status::Custom<String> {
|
||||||
get_db()
|
get_db()
|
||||||
.await
|
.await
|
||||||
.map(|_| status::Custom(Status::Ok, "Database connected successfully".into()))
|
.map(|_| status::Custom(Status::Ok, "Database connected successfully".into())) // 连接成功
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
status::Custom(
|
status::Custom(
|
||||||
Status::InternalServerError,
|
Status::InternalServerError,
|
||||||
format!("Failed to connect: {}", e),
|
format!("Failed to connect: {}", e), // 连接失败信息
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/* 启动函数 */
|
|
||||||
|
/**
|
||||||
|
* 启动Rocket应用
|
||||||
|
*
|
||||||
|
* # 返回
|
||||||
|
* - `rocket::Rocket`: Rocket实例
|
||||||
|
*/
|
||||||
#[launch]
|
#[launch]
|
||||||
async fn rocket() -> _ {
|
async fn rocket() -> _ {
|
||||||
let config = config::Config::read("./src/config/config.toml").expect("Failed to read config");
|
let config = config::Config::read("./src/config/config.toml").expect("Failed to read config"); // 读取配置
|
||||||
init_db(config.database)
|
init_db(config.database)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to connect to database");
|
.expect("Failed to connect to database"); // 初始化数据库连接
|
||||||
rocket::build().mount("/api", routes![install, ssql])
|
rocket::build().mount("/api", routes![install, ssql]) // 挂载API路由
|
||||||
}
|
}
|
||||||
|
90
backend/src/sql/postgresql/init.sql
Normal file
90
backend/src/sql/postgresql/init.sql
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
--- 安装自动生成uuid插件
|
||||||
|
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||||
|
--- 网站信息表
|
||||||
|
CREATE TABLE settings
|
||||||
|
(
|
||||||
|
settings_keys VARCHAR(255) PRIMARY KEY,
|
||||||
|
settings_values TEXT NOT NULL
|
||||||
|
);
|
||||||
|
--- 页面状态枚举
|
||||||
|
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描述
|
||||||
|
post_title VARCHAR(255) NOT NULL, --- 文章标题
|
||||||
|
page_content TEXT NOT NULL, --- 独立页面内容
|
||||||
|
page_mould VARCHAR(50), --- 独立页面模板名称
|
||||||
|
page_fields JSON, --- 自定义字段
|
||||||
|
page_path VARCHAR(255), --- 文章路径
|
||||||
|
page_status publication_status DEFAULT 'draft' --- 文章状态
|
||||||
|
);
|
||||||
|
--- 文章表
|
||||||
|
CREATE TABLE posts
|
||||||
|
(
|
||||||
|
post_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), --- 文章主键
|
||||||
|
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, --- 文章是否编辑未保存
|
||||||
|
posts_unsaved_content TEXT, --- 未保存的文章
|
||||||
|
posts_path VARCHAR(255), --- 文章路径
|
||||||
|
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) --- 标签名称主键
|
||||||
|
);
|
||||||
|
--- 文章与标签的关系表
|
||||||
|
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) --- 复合主键
|
||||||
|
);
|
||||||
|
--- 分类表
|
||||||
|
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
|
||||||
|
);
|
||||||
|
--- 文章与分类的关系表
|
||||||
|
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) --- 复合主键
|
||||||
|
);
|
||||||
|
--- 资源库
|
||||||
|
CREATE TABLE library
|
||||||
|
(
|
||||||
|
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 REFERENCES library_type (library_type) ON DELETE CASCADE, --- 资源类别
|
||||||
|
library_description VARCHAR(255), --- 资源描述
|
||||||
|
library_created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP --- 创建时间
|
||||||
|
|
||||||
|
);
|
||||||
|
--- 资源库类别
|
||||||
|
CREATE TABLE library_type
|
||||||
|
(
|
||||||
|
library_type VARCHAR(50) PRIMARY KEY, --- 资源类别
|
||||||
|
library_description VARCHAR(200) --- 资源类别描述
|
||||||
|
);
|
||||||
|
--- 配置文件库
|
||||||
|
CREATE TABLE config
|
||||||
|
(
|
||||||
|
config_name VARCHAR(50) PRIMARY KEY, --- 配置文件名称
|
||||||
|
config_config JSON --- 配置文件
|
||||||
|
)
|
@ -1,5 +1,5 @@
|
|||||||
// sql/psotgresql.rs
|
// sql/psotgresql.rs
|
||||||
/*
|
/*
|
||||||
为postgresql数据库实现具体的方法
|
为postgresql数据库实现具体的方法
|
||||||
*/
|
*/
|
||||||
use super::DatabaseTrait;
|
use super::DatabaseTrait;
|
||||||
@ -18,39 +18,46 @@ impl DatabaseTrait for Postgresql {
|
|||||||
async fn connect(database: config::Database) -> Result<Self, Box<dyn Error>> {
|
async fn connect(database: config::Database) -> Result<Self, Box<dyn Error>> {
|
||||||
let connection_str = format!(
|
let connection_str = format!(
|
||||||
"postgres://{}:{}@{}:{}/{}",
|
"postgres://{}:{}@{}:{}/{}",
|
||||||
database.user,
|
database.user, database.password, database.address, database.prot, database.db_name
|
||||||
database.password,
|
|
||||||
database.address,
|
|
||||||
database.prot,
|
|
||||||
database.db_name
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 连接到数据库池
|
||||||
let pool = PgPool::connect(&connection_str)
|
let pool = PgPool::connect(&connection_str)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Box::new(e) as Box<dyn Error>)?;
|
.map_err(|e| Box::new(e) as Box<dyn Error>)?;
|
||||||
|
|
||||||
|
// 返回Postgresql实例
|
||||||
Ok(Postgresql { pool })
|
Ok(Postgresql { pool })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步执行查询并返回结果。
|
||||||
|
*/
|
||||||
async fn query<'a>(
|
async fn query<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
query: String,
|
query: String,
|
||||||
) -> Result<Vec<HashMap<String, String>>, Box<dyn Error + 'a>> {
|
) -> 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
|
||||||
.map_err(|e| Box::new(e) as Box<dyn Error>)?;
|
.map_err(|e| Box::new(e) as Box<dyn Error>)?;
|
||||||
|
|
||||||
|
// 存储查询结果
|
||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
|
|
||||||
|
// 遍历每一行并构建结果映射
|
||||||
for row in rows {
|
for row in rows {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
for column in row.columns() {
|
for column in row.columns() {
|
||||||
|
// 获取列的值,若失败则使用默认值
|
||||||
let value: String = row.try_get(column.name()).unwrap_or_default();
|
let value: String = row.try_get(column.name()).unwrap_or_default();
|
||||||
map.insert(column.name().to_string(), value);
|
map.insert(column.name().to_string(), value);
|
||||||
}
|
}
|
||||||
results.push(map);
|
results.push(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 返回查询结果
|
||||||
Ok(results)
|
Ok(results)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,61 +0,0 @@
|
|||||||
// 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 };
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
// 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>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,27 +0,0 @@
|
|||||||
// 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>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,66 +0,0 @@
|
|||||||
// 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 };
|
|
||||||
}
|
|
13
frontend/index.html
Normal file
13
frontend/index.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vite + React + TS</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,41 +0,0 @@
|
|||||||
// 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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
// 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,18 +0,0 @@
|
|||||||
// types/common.ts
|
|
||||||
/**
|
|
||||||
* 版本信息接口
|
|
||||||
*/
|
|
||||||
export interface VersionInfo {
|
|
||||||
major: number; // 主版本号 - 不兼容的API修改
|
|
||||||
minor: number; // 次版本号 - 向下兼容的功能性新增
|
|
||||||
patch: number; // 修订号 - 向下兼容的问题修正
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 依赖项配置接口
|
|
||||||
*/
|
|
||||||
export interface Dependency {
|
|
||||||
id: string; // 依赖项的唯一标识符
|
|
||||||
version: string; // 依赖项的版本要求
|
|
||||||
type: 'plugin' | 'theme'; // 依赖项类型:插件或主题
|
|
||||||
}
|
|
12
frontend/types/context.ts
Normal file
12
frontend/types/context.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// File path: /d:/data/echoes/frontend/types/context.ts
|
||||||
|
/**
|
||||||
|
* 应用程序上下文配置接口
|
||||||
|
*
|
||||||
|
* 此接口定义了应用程序的上下文配置,包括API基础URL、主题、插件和资源目录的路径。
|
||||||
|
*/
|
||||||
|
export interface AppContext {
|
||||||
|
apiBaseUrl: string; // 用于访问API的基础URL
|
||||||
|
themesPath: string; // 存储主题文件的目录路径
|
||||||
|
pluginsPath: string; // 存储插件文件的目录路径
|
||||||
|
assetsPath: string; // 存储静态资源的目录路径
|
||||||
|
}
|
@ -1,23 +1,41 @@
|
|||||||
// types/plugin.ts
|
// File path: /d:/data/echoes/frontend/types/plugin.ts
|
||||||
import {Dependency} from "./common";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 插件配置接口
|
* 插件配置接口
|
||||||
|
*
|
||||||
|
* 该接口定义了插件的基本配置,包括插件的名称、版本、描述、作者等信息。
|
||||||
|
* 还包括插件的生命周期钩子和依赖项的配置。
|
||||||
*/
|
*/
|
||||||
export interface PluginConfig {
|
export interface PluginConfig {
|
||||||
id: string; // 插件唯一标识符
|
|
||||||
name: string; // 插件名称
|
name: string; // 插件名称
|
||||||
version: string; // 插件版本
|
version: string; // 插件版本
|
||||||
displayName: string; // 插件显示名称
|
displayName: string; // 插件显示名称
|
||||||
description?: string; // 插件描述
|
description?: string; // 插件描述(可选)
|
||||||
author?: string; // 插件作者
|
author?: string; // 插件作者(可选)
|
||||||
enabled: boolean; // 插件是否启用
|
enabled: boolean; // 插件是否启用
|
||||||
icon?: string; // 插件图标URL
|
icon?: string; // 插件图标URL(可选)
|
||||||
adminPath?: string; // 插件管理页面路径
|
managePath?: string; // 插件管理页面路径(可选)
|
||||||
entry: string; // 插件入口组件路径
|
entry: string; // 插件入口组件路径
|
||||||
dependencies?: Dependency[]; // 插件依赖项
|
// 主题配置
|
||||||
performance?: {
|
settingsSchema?: {
|
||||||
maxLoadTime?: number; // 最大加载时间(毫秒)
|
type: string; // 配置类型
|
||||||
maxMemoryUsage?: number; // 最大内存使用量(bytes)
|
properties: Record<string, {
|
||||||
|
type: string; // 属性类型
|
||||||
|
title: string; // 属性标题
|
||||||
|
description?: string; // 属性描述(可选)
|
||||||
|
data?: any; // 额外数据(可选)
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
// 依赖
|
||||||
|
dependencies?: {
|
||||||
|
plugins?: string[]; // 依赖的插件列表(可选)
|
||||||
|
themes?: string[]; // 依赖的主题列表(可选)
|
||||||
|
};
|
||||||
|
// 插件生命周期钩子
|
||||||
|
hooks?: {
|
||||||
|
onInstall?: string; // 安装时调用的钩子(可选)
|
||||||
|
onUninstall?: string; // 卸载时调用的钩子(可选)
|
||||||
|
onEnable?: string; // 启用时调用的钩子(可选)
|
||||||
|
onDisable?: string; // 禁用时调用的钩子(可选)
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -1,20 +1,55 @@
|
|||||||
// types/theme.ts
|
// /d:/data/echoes/frontend/types/theme.ts
|
||||||
import {Dependency} from "./common.ts";
|
/**
|
||||||
|
* 主题配置和模板接口定义文件
|
||||||
|
* 该文件包含主题配置接口和主题模板接口的定义,用于主题管理和渲染。
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主题配置接口
|
* 主题配置接口
|
||||||
*/
|
*/
|
||||||
export interface ThemeConfig {
|
export interface ThemeConfig {
|
||||||
name: string; // 主题的唯一名称标识符
|
name: string; // 主题的唯一标识符
|
||||||
version: string; // 主题版本
|
displayName: string; // 主题的显示名称
|
||||||
displayName: string; // 主题显示名称
|
version: string; // 主题的版本号
|
||||||
description?: string; // 主题描述
|
description?: string; // 主题的描述信息
|
||||||
author?: string; // 主题作者
|
author?: string; // 主题的作者信息
|
||||||
preview?: string; // 主题预览图URL
|
entry: string; // 主题的入口组件路径
|
||||||
globalStyles?: string; // 主题全局样式
|
templates: Map<string, ThemeTemplate>; // 主题模板的映射表
|
||||||
dependencies?: Dependency[]; // 主题依赖项
|
/** 主题全局配置 */
|
||||||
performance?: {
|
globalSettings?: {
|
||||||
maxLoadTime?: number; // 最大加载时间(毫秒)
|
layout?: string; // 主题的布局配置
|
||||||
maxMemoryUsage?: number; // 最大内存使用量(bytes)
|
css?: string; // 主题的CSS配置
|
||||||
};
|
};
|
||||||
}
|
/** 主题配置文件 */
|
||||||
|
settingsSchema?: {
|
||||||
|
type: string; // 配置文件的类型
|
||||||
|
/** 属性定义 */
|
||||||
|
properties: Record<string, {
|
||||||
|
type: string; // 属性的数据类型
|
||||||
|
title: string; // 属性的标题
|
||||||
|
description?: string; // 属性的描述信息
|
||||||
|
data?: any; // 属性的默认数据
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
/** 依赖 */
|
||||||
|
dependencies?: {
|
||||||
|
plugins?: string[]; // 主题所依赖的插件列表
|
||||||
|
assets?: string[]; // 主题所依赖的资源列表
|
||||||
|
};
|
||||||
|
/** 钩子 */
|
||||||
|
hooks?: {
|
||||||
|
beforeRender?: string; // 渲染前执行的钩子
|
||||||
|
afterRender?: string; // 渲染后执行的钩子
|
||||||
|
onActivate?: string; // 主题激活时执行的钩子
|
||||||
|
onDeactivate?: string; // 主题停用时执行的钩子
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主题模板接口
|
||||||
|
*/
|
||||||
|
export interface ThemeTemplate {
|
||||||
|
path: string; // 模板文件的路径
|
||||||
|
name: string; // 模板的名称
|
||||||
|
description?: string; // 模板的描述信息
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user