From c19d21ce7d382cf893719089be7d651edb432ee4 Mon Sep 17 00:00:00 2001 From: lsy Date: Mon, 18 Nov 2024 19:14:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=EF=BC=9A=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E4=BA=86=E8=B5=84=E6=BA=90=E5=BA=93=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=E8=A1=A8=20=E5=90=8E=E7=AB=AF=EF=BC=9A=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E5=AD=97=E6=AE=B5=E5=86=99?= =?UTF-8?q?=E9=94=99=EF=BC=8C=E6=9A=B4=E9=9C=B2=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/assets/config.toml | 6 +- backend/src/config.rs | 14 +- backend/src/database/relational/mod.rs | 25 +++- .../database/relational/postgresql/init.sql | 82 ++++++------ .../src/database/relational/postgresql/mod.rs | 53 ++++++-- backend/src/main.rs | 120 ++++++++++-------- 6 files changed, 181 insertions(+), 119 deletions(-) diff --git a/backend/assets/config.toml b/backend/assets/config.toml index 573ee36..5b1d688 100644 --- a/backend/assets/config.toml +++ b/backend/assets/config.toml @@ -6,11 +6,11 @@ install = false # 是否为第一次安装 non_relational = true # 是否使用非关系型数据库 -# 数据库 -[db_config] +# 关系型数据库 +[sql_config] db_type = "postgresql" # 数据库类型 address = "localhost" # 地址 -prot = 5432 # 端口 +port = 5432 # 端口 user = "postgres" # 用户名 password = "postgres" # 密码 db_name = "echoes" # 数据库 diff --git a/backend/src/config.rs b/backend/src/config.rs index 7cb1a98..f91dd8a 100644 --- a/backend/src/config.rs +++ b/backend/src/config.rs @@ -4,36 +4,36 @@ */ use serde::Deserialize; -use std::{env, fs}; +use std::{ env, fs}; -#[derive(Deserialize)] +#[derive(Deserialize,Debug,Clone)] pub struct Config { pub info: Info, // 配置信息 pub sql_config: SqlConfig, // 关系型数据库配置 // pub no_sql_config:NoSqlConfig, 非关系型数据库配置 } -#[derive(Deserialize)] +#[derive(Deserialize,Debug,Clone)] pub struct Info { pub install: bool, // 是否安装 pub non_relational: bool, // 是否非关系型 } -#[derive(Deserialize)] +#[derive(Deserialize,Debug,Clone)] pub struct SqlConfig { pub db_type: String, // 数据库类型 pub address: String, // 地址 - pub prot: u32, // 端口 + pub port: u32, // 端口 pub user: String, // 用户名 pub password: String, // 密码 pub db_name: String, // 数据库名称 } -#[derive(Deserialize)] +#[derive(Deserialize,Debug,Clone)] pub struct NoSqlConfig { pub db_type: String, // 数据库类型 pub address: String, // 地址 - pub prot: u32, // 端口 + pub port: u32, // 端口 pub user: String, // 用户名 pub password: String, // 密码 pub db_name: String, // 数据库名称 diff --git a/backend/src/database/relational/mod.rs b/backend/src/database/relational/mod.rs index 5338df4..3580e93 100644 --- a/backend/src/database/relational/mod.rs +++ b/backend/src/database/relational/mod.rs @@ -2,6 +2,7 @@ /** 本模块定义了数据库的特征和方法,包括查询构建器和数据库连接。 + 提供了对不同类型数据库的支持,如PostgreSQL。 */ mod postgresql; @@ -47,7 +48,14 @@ pub trait DatabaseTrait: Send + Sync { async fn execute_query<'a>( &'a self, builder: &QueryBuilder, - ) -> Result>, Box> ; + ) -> Result>, Box>; + + /** + 初始化数据库 + @param database 数据库配置 + @return Result<(), Box> 返回初始化结果或错误 + */ + async fn initialization(database: config::SqlConfig) -> Result<(), Box> where Self: Sized; } #[derive(Clone)] @@ -70,7 +78,7 @@ impl Database { @param database 数据库配置 @return Result> 返回数据库实例或错误 */ - pub async fn init(database: config::SqlConfig) -> Result> { + pub async fn link(database: config::SqlConfig) -> Result> { let db = match database.db_type.as_str() { "postgresql" => postgresql::Postgresql::connect(database).await?, _ => return Err("unknown database type".into()), @@ -78,6 +86,19 @@ 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?, + _ => return Err("unknown database type".into()), + }; + Ok(()) + } } impl QueryBuilder { diff --git a/backend/src/database/relational/postgresql/init.sql b/backend/src/database/relational/postgresql/init.sql index d3fd96b..1bf2ae4 100644 --- a/backend/src/database/relational/postgresql/init.sql +++ b/backend/src/database/relational/postgresql/init.sql @@ -1,7 +1,3 @@ ---- 创建数据库 -CREATE DATABASE echoes; ---- 切换数据库 -\c echoes; --- 安装自动生成uuid插件 CREATE EXTENSION IF NOT EXISTS pgcrypto; --- 用户权限枚举 @@ -9,16 +5,16 @@ 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 DEFULT '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), --- 用户头像URL + 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'); @@ -37,26 +33,26 @@ CREATE TABLE pages --- 文章表 CREATE TABLE posts ( - post_author VARCHAR(100) NOT NULL REFERENCES person (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, ---文章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) --- 文章时间约束 ); --- 标签表 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 @@ -79,26 +75,22 @@ CREATE TABLE post_categories category_id VARCHAR(50) REFERENCES categories (category_name) ON DELETE CASCADE, --- 外键约束,引用categories的主键 PRIMARY KEY (post_id, category_id) --- 复合主键 ); + --- 资源库 CREATE TABLE library ( - library_author VARCHAR(100) NOT NULL REFERENCES person (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 REFERENCES library_type (library_type) ON DELETE CASCADE, --- 资源类别 - 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(), --- 资源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 --- 创建时间 ); ---- 资源库类别 -CREATE TABLE library_type -( - library_type VARCHAR(50) PRIMARY KEY CHECK (LOWER(library_type) = library_type), --- 资源类别 - library_description VARCHAR(200) --- 资源类别描述 -); + --- 配置文件库 CREATE TABLE config ( diff --git a/backend/src/database/relational/postgresql/mod.rs b/backend/src/database/relational/postgresql/mod.rs index 6e041be..869ce27 100644 --- a/backend/src/database/relational/postgresql/mod.rs +++ b/backend/src/database/relational/postgresql/mod.rs @@ -1,13 +1,15 @@ -// src/database/relational/postgresql/mod.rs -/* +// File path: src/database/relational/postgresql/mod.rs +/** * 该模块实现了与PostgreSQL数据库的交互功能。 * 包含连接池的结构体定义,提供数据库操作的基础。 -*/ + */ + use super::{DatabaseTrait, QueryBuilder}; use crate::config; use async_trait::async_trait; -use sqlx::{Column, PgPool, Row}; +use sqlx::{Column, PgPool, Row, Executor}; use std::{collections::HashMap, error::Error}; +use std::{env, fs}; #[derive(Clone)] /// PostgreSQL数据库连接池结构体 @@ -18,7 +20,42 @@ pub struct Postgresql { #[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") + .join("database") + .join("relational") + .join("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实例。 * * # 参数 @@ -27,11 +64,10 @@ impl DatabaseTrait for Postgresql { * # 返回 * - `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.prot, db_config.db_name + db_config.user, db_config.password, db_config.address, db_config.port, db_config.db_name ); // 连接到数据库池 @@ -43,7 +79,7 @@ impl DatabaseTrait for Postgresql { Ok(Postgresql { pool }) } - /** + /** * 异步执行查询并返回结果。 * * # 参数 @@ -85,5 +121,4 @@ impl DatabaseTrait for Postgresql { Ok(results) } - } diff --git a/backend/src/main.rs b/backend/src/main.rs index 35fb04a..254eb1d 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -1,108 +1,122 @@ -// main.rs +// File path: /d:/data/echoes/backend/src/main.rs /** * 主程序入口,提供数据库连接和相关API接口。 - * + * * 接口: * - GET /api/install: 测试数据库连接 * - GET /api/sql: 执行SQL查询并返回结果 */ mod config; // 配置模块 -mod database; -mod secret; -use chrono::Duration; -// 数据库模块 +mod database; // 数据库模块 +mod secret; // 密钥模块 + +use chrono::Duration; // 引入时间持续时间 use database::relational; // 引入关系型数据库 use once_cell::sync::Lazy; // 用于延迟初始化 -use rocket::{get, http::Status, launch, response::status, routes, serde::json::Json}; // 引入Rocket框架相关功能 -use std::sync::Arc; -// 引入Arc用于线程安全的引用计数 +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 DB: Lazy>>> = Lazy::new(|| Arc::new(Mutex::new(None))); +static SQL: Lazy>>> = + Lazy::new(|| Arc::new(Mutex::new(None))); /** * 初始化数据库连接 + * + * # 参数 + * - `database`: 数据库配置 + * + * # 返回 + * - `Result<(), Box>`: 初始化结果 */ -async fn init_db(database: config::SqlConfig) -> Result<(), Box> { - let database = relational::Database::init(database).await?; // 初始化数据库 - *DB.lock().await = Some(database); // 保存数据库实例 +async fn init_sql(database: config::SqlConfig) -> Result<(), Box> { + let database = relational::Database::link(database).await?; // 初始化数据库 + *SQL.lock().await = Some(database); // 保存数据库实例 Ok(()) } /** * 获取数据库的引用 + * + * # 返回 + * - `Result>`: 数据库引用或错误信息 */ -async fn get_db() -> Result> { - DB.lock() +async fn get_sql() -> Result> { + SQL.lock() .await .clone() .ok_or_else(|| "Database not initialized".into()) // 返回错误信息 } /** - * 测试数据库接口 -*/ -#[get("/sql")] -async fn ssql() -> Result>>, status::Custom> { - let db = get_db().await.map_err(|e| { + * 数据库安装接口 + * + * # 参数 + * - `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 error: {}", e), // 数据库错误信息 + format!("Database initialization failed: {}", e), // 连接失败信息 ) })?; - - let query_result = db - .get_db() - .query("SELECT * FROM info".to_string()) - .await - .map_err(|e| status::Custom(Status::InternalServerError, format!("Query error: {}", e)))?; - - Ok(Json(query_result)) // 返回查询结果 + + Ok(status::Custom( + Status::Ok, + format!("Initialization successful"), + )) } /** - * 数据库安装接口 + * 生成系统JWT接口 + * + * # 返回 + * - `Result, status::Custom>`: JWT令牌或错误信息 */ -#[get("/install")] -async fn install() -> status::Custom { - get_db() - .await - .map(|_| status::Custom(Status::Ok, "Database connected successfully".into())) // 连接成功 - .unwrap_or_else(|e| { - status::Custom( - Status::InternalServerError, - format!("Failed to connect: {}", e), // 连接失败信息 - ) - }) -} - #[get("/system")] async fn token_system() -> Result, status::Custom> { // 创建 Claims let claims = secret::CustomClaims { - user_id: String::from("system"), - device_ua: String::from("system"), + user_id: String::from("system"), // 用户ID + 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)))?; - - Ok(status::Custom(Status::Ok, token)) + let token = secret::generate_jwt(claims, Duration::seconds(1)).map_err(|e| { + status::Custom( + Status::InternalServerError, + format!("JWT generation failed: {}", e), // JWT生成失败信息 + ) + })?; + + Ok(status::Custom(Status::Ok, token)) // 返回JWT令牌 } /** * 启动Rocket应用 + * + * # 返回 + * - `rocket::Rocket`: Rocket应用实例 */ #[launch] async fn rocket() -> _ { let config = config::Config::read().expect("Failed to read config"); // 读取配置 - init_db(config.sql_config) + + if config.info.install { + init_sql(config.sql_config) .await .expect("Failed to connect to database"); // 初始化数据库连接 - rocket::build() - .mount("/", routes![install, ssql]) // 挂载API路由 - .mount("/auth/token", routes![token_system]) + rocket::build() + .mount("/auth/token", routes![token_system]) + } else { + rocket::build() + .mount("/", routes![install]) // 挂载API路由 + } }