数据库:删除了资源库依赖表

后端:修复配置文件字段写错,暴露初始化接口
This commit is contained in:
lsy 2024-11-18 19:14:37 +08:00
parent 9d88e9a159
commit c19d21ce7d
6 changed files with 181 additions and 119 deletions

View File

@ -6,11 +6,11 @@
install = false # 是否为第一次安装 install = false # 是否为第一次安装
non_relational = true # 是否使用非关系型数据库 non_relational = true # 是否使用非关系型数据库
# 数据库 # 关系型数据库
[db_config] [sql_config]
db_type = "postgresql" # 数据库类型 db_type = "postgresql" # 数据库类型
address = "localhost" # 地址 address = "localhost" # 地址
prot = 5432 # 端口 port = 5432 # 端口
user = "postgres" # 用户名 user = "postgres" # 用户名
password = "postgres" # 密码 password = "postgres" # 密码
db_name = "echoes" # 数据库 db_name = "echoes" # 数据库

View File

@ -6,34 +6,34 @@
use serde::Deserialize; use serde::Deserialize;
use std::{ env, fs}; use std::{ env, fs};
#[derive(Deserialize)] #[derive(Deserialize,Debug,Clone)]
pub struct Config { pub struct Config {
pub info: Info, // 配置信息 pub info: Info, // 配置信息
pub sql_config: SqlConfig, // 关系型数据库配置 pub sql_config: SqlConfig, // 关系型数据库配置
// pub no_sql_config:NoSqlConfig, 非关系型数据库配置 // pub no_sql_config:NoSqlConfig, 非关系型数据库配置
} }
#[derive(Deserialize)] #[derive(Deserialize,Debug,Clone)]
pub struct Info { pub struct Info {
pub install: bool, // 是否安装 pub install: bool, // 是否安装
pub non_relational: bool, // 是否非关系型 pub non_relational: bool, // 是否非关系型
} }
#[derive(Deserialize)] #[derive(Deserialize,Debug,Clone)]
pub struct SqlConfig { pub struct SqlConfig {
pub db_type: String, // 数据库类型 pub db_type: String, // 数据库类型
pub address: String, // 地址 pub address: String, // 地址
pub prot: u32, // 端口 pub port: u32, // 端口
pub user: String, // 用户名 pub user: String, // 用户名
pub password: String, // 密码 pub password: String, // 密码
pub db_name: String, // 数据库名称 pub db_name: String, // 数据库名称
} }
#[derive(Deserialize)] #[derive(Deserialize,Debug,Clone)]
pub struct NoSqlConfig { pub struct NoSqlConfig {
pub db_type: String, // 数据库类型 pub db_type: String, // 数据库类型
pub address: String, // 地址 pub address: String, // 地址
pub prot: u32, // 端口 pub port: u32, // 端口
pub user: String, // 用户名 pub user: String, // 用户名
pub password: String, // 密码 pub password: String, // 密码
pub db_name: String, // 数据库名称 pub db_name: String, // 数据库名称

View File

@ -2,6 +2,7 @@
/** /**
PostgreSQL
*/ */
mod postgresql; mod postgresql;
@ -48,6 +49,13 @@ pub trait DatabaseTrait: Send + Sync {
&'a self, &'a self,
builder: &QueryBuilder, builder: &QueryBuilder,
) -> Result<Vec<HashMap<String, String>>, Box<dyn Error + 'a>>; ) -> Result<Vec<HashMap<String, String>>, Box<dyn Error + 'a>>;
/**
@param database
@return Result<(), Box<dyn Error>>
*/
async fn initialization(database: config::SqlConfig) -> Result<(), Box<dyn Error>> where Self: Sized;
} }
#[derive(Clone)] #[derive(Clone)]
@ -70,7 +78,7 @@ impl Database {
@param database @param database
@return Result<Self, Box<dyn Error>> @return Result<Self, Box<dyn Error>>
*/ */
pub async fn init(database: config::SqlConfig) -> Result<Self, Box<dyn Error>> { pub async fn link(database: config::SqlConfig) -> Result<Self, Box<dyn Error>> {
let db = match database.db_type.as_str() { let db = match database.db_type.as_str() {
"postgresql" => postgresql::Postgresql::connect(database).await?, "postgresql" => postgresql::Postgresql::connect(database).await?,
_ => return Err("unknown database type".into()), _ => return Err("unknown database type".into()),
@ -78,6 +86,19 @@ impl Database {
Ok(Self { db: Arc::new(Box::new(db)) }) Ok(Self { db: Arc::new(Box::new(db)) })
} }
/**
@param database
@return Result<(), Box<dyn Error>>
*/
pub async fn initial_setup(database: config::SqlConfig) -> Result<(), Box<dyn Error>> {
match database.db_type.as_str() {
"postgresql" => postgresql::Postgresql::initialization(database).await?,
_ => return Err("unknown database type".into()),
};
Ok(())
}
} }
impl QueryBuilder { impl QueryBuilder {

View File

@ -1,7 +1,3 @@
--- 创建数据库
CREATE DATABASE echoes;
--- 切换数据库
\c echoes;
--- 安装自动生成uuid插件 --- 安装自动生成uuid插件
CREATE EXTENSION IF NOT EXISTS pgcrypto; CREATE EXTENSION IF NOT EXISTS pgcrypto;
--- 用户权限枚举 --- 用户权限枚举
@ -18,7 +14,7 @@ CREATE TABLE persons
person_avatar VARCHAR(255), --- 用户头像URL person_avatar VARCHAR(255), --- 用户头像URL
person_role VARCHAR(50), --- 用户角色 person_role VARCHAR(50), --- 用户角色
person_last_login TIMESTAMP DEFAULT CURRENT_TIMESTAMP, --- 最后登录时间 person_last_login TIMESTAMP DEFAULT CURRENT_TIMESTAMP, --- 最后登录时间
person_level privilege_level NOT NULL DEFULT 'contributor' --- 用户权限 person_level privilege_level NOT NULL DEFAULT 'contributor' --- 用户权限
); );
--- 页面状态枚举 --- 页面状态枚举
CREATE TYPE publication_status AS ENUM ('draft', 'published', 'private','hide'); CREATE TYPE publication_status AS ENUM ('draft', 'published', 'private','hide');
@ -37,7 +33,7 @@ CREATE TABLE pages
--- 文章表 --- 文章表
CREATE TABLE posts CREATE TABLE posts
( (
post_author VARCHAR(100) NOT NULL REFERENCES person (person_name) ON DELETE CASCADE, --- 文章作者 post_author VARCHAR(100) NOT NULL REFERENCES persons (person_name) ON DELETE CASCADE, --- 文章作者
post_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), --- 文章主键 post_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), --- 文章主键
post_picture VARCHAR(255), --- 文章头图 post_picture VARCHAR(255), --- 文章头图
post_title VARCHAR(255) NOT NULL, --- 文章标题 post_title VARCHAR(255) NOT NULL, --- 文章标题
@ -79,26 +75,22 @@ CREATE TABLE post_categories
category_id VARCHAR(50) REFERENCES categories (category_name) ON DELETE CASCADE, --- 外键约束引用categories的主键 category_id VARCHAR(50) REFERENCES categories (category_name) ON DELETE CASCADE, --- 外键约束引用categories的主键
PRIMARY KEY (post_id, category_id) --- 复合主键 PRIMARY KEY (post_id, category_id) --- 复合主键
); );
--- 资源库 --- 资源库
CREATE TABLE library CREATE TABLE library
( (
library_author VARCHAR(100) NOT NULL REFERENCES person (person_name) ON DELETE CASCADE, --- 资源作者 library_author VARCHAR(100) NOT NULL REFERENCES persons (person_name) ON DELETE CASCADE, --- 资源作者
library_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), --- 资源ID library_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), --- 资源ID
library_name VARCHAR(255) NOT NULL, --- 资源名称 library_name VARCHAR(255) NOT NULL, --- 资源名称
library_size BIGINT NOT NULL, --- 大小 library_size BIGINT NOT NULL, --- 大小
library_storage_path VARCHAR(255) NOT NULL UNIQUE, --- 储存路径 library_storage_path VARCHAR(255) NOT NULL UNIQUE, --- 储存路径
library_type VARCHAR(50) NOT NULL REFERENCES library_type (library_type) ON DELETE CASCADE, --- 资源类别 library_type VARCHAR(50) NOT NULL, --- 资源类别
library_class VARCHAR(50), --- 资源类 library_class VARCHAR(50), --- 资源类
library_description VARCHAR(255), --- 资源描述 library_description VARCHAR(255), --- 资源描述
library_created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP --- 创建时间 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 CREATE TABLE config
( (

View File

@ -1,13 +1,15 @@
// src/database/relational/postgresql/mod.rs // File path: src/database/relational/postgresql/mod.rs
/* /**
* PostgreSQL数据库的交互功能 * PostgreSQL数据库的交互功能
* *
*/ */
use super::{DatabaseTrait, QueryBuilder}; use super::{DatabaseTrait, QueryBuilder};
use crate::config; use crate::config;
use async_trait::async_trait; use async_trait::async_trait;
use sqlx::{Column, PgPool, Row}; use sqlx::{Column, PgPool, Row, Executor};
use std::{collections::HashMap, error::Error}; use std::{collections::HashMap, error::Error};
use std::{env, fs};
#[derive(Clone)] #[derive(Clone)]
/// PostgreSQL数据库连接池结构体 /// PostgreSQL数据库连接池结构体
@ -18,6 +20,41 @@ pub struct Postgresql {
#[async_trait] #[async_trait]
impl DatabaseTrait for Postgresql { impl DatabaseTrait for Postgresql {
/**
*
*
* #
* - `db_config`:
*
* #
* - `Result<(), Box<dyn Error>>`:
*/
async fn initialization(db_config: config::SqlConfig) -> Result<(), Box<dyn Error>> {
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实例 * PostgreSQL数据库并返回Postgresql实例
* *
@ -27,11 +64,10 @@ impl DatabaseTrait for Postgresql {
* # * #
* - `Result<Self, Box<dyn Error>>`: * - `Result<Self, Box<dyn Error>>`:
*/ */
async fn connect(db_config: config::SqlConfig) -> Result<Self, Box<dyn Error>> { async fn connect(db_config: config::SqlConfig) -> Result<Self, Box<dyn Error>> {
let connection_str = format!( let connection_str = format!(
"postgres://{}:{}@{}:{}/{}", "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
); );
// 连接到数据库池 // 连接到数据库池
@ -85,5 +121,4 @@ impl DatabaseTrait for Postgresql {
Ok(results) Ok(results)
} }
} }

View File

@ -1,4 +1,4 @@
// main.rs // File path: /d:/data/echoes/backend/src/main.rs
/** /**
* API接口 * API接口
@ -9,100 +9,114 @@
*/ */
mod config; // 配置模块 mod config; // 配置模块
mod database; mod database; // 数据库模块
mod secret; mod secret; // 密钥模块
use chrono::Duration;
// 数据库模块 use chrono::Duration; // 引入时间持续时间
use database::relational; // 引入关系型数据库 use database::relational; // 引入关系型数据库
use once_cell::sync::Lazy; // 用于延迟初始化 use once_cell::sync::Lazy; // 用于延迟初始化
use rocket::{get, http::Status, launch, response::status, routes, serde::json::Json}; // 引入Rocket框架相关功能 use rocket::{get, post, http::Status, launch, response::status, routes, serde::json::Json}; // 引入Rocket框架相关功能
use std::sync::Arc; use std::sync::Arc; // 引入Arc用于线程安全的引用计数
// 引入Arc用于线程安全的引用计数
use tokio::sync::Mutex; // 引入Mutex用于异步锁 use tokio::sync::Mutex; // 引入Mutex用于异步锁
// 全局数据库连接变量 // 全局数据库连接变量
static DB: Lazy<Arc<Mutex<Option<relational::Database>>>> = Lazy::new(|| Arc::new(Mutex::new(None))); static SQL: Lazy<Arc<Mutex<Option<relational::Database>>>> =
Lazy::new(|| Arc::new(Mutex::new(None)));
/** /**
* *
*
* #
* - `database`:
*
* #
* - `Result<(), Box<dyn std::error::Error>>`:
*/ */
async fn init_db(database: config::SqlConfig) -> Result<(), Box<dyn std::error::Error>> { async fn init_sql(database: config::SqlConfig) -> Result<(), Box<dyn std::error::Error>> {
let database = relational::Database::init(database).await?; // 初始化数据库 let database = relational::Database::link(database).await?; // 初始化数据库
*DB.lock().await = Some(database); // 保存数据库实例 *SQL.lock().await = Some(database); // 保存数据库实例
Ok(()) Ok(())
} }
/** /**
* *
*
* #
* - `Result<relational::Database, Box<dyn std::error::Error>>`:
*/ */
async fn get_db() -> Result<relational::Database, Box<dyn std::error::Error>> { async fn get_sql() -> Result<relational::Database, Box<dyn std::error::Error>> {
DB.lock() SQL.lock()
.await .await
.clone() .clone()
.ok_or_else(|| "Database not initialized".into()) // 返回错误信息 .ok_or_else(|| "Database not initialized".into()) // 返回错误信息
} }
/** /**
* *
*
* #
* - `data`:
*
* #
* - `Result<status::Custom<String>, status::Custom<String>>`:
*/ */
#[get("/sql")] #[post("/install", format = "json", data = "<data>")]
async fn ssql() -> Result<Json<Vec<std::collections::HashMap<String, String>>>, status::Custom<String>> { async fn install(data: Json<config::SqlConfig>) -> Result<status::Custom<String>, status::Custom<String>> {
let db = get_db().await.map_err(|e| { relational::Database::initial_setup(data.into_inner()).await.map_err(|e| {
status::Custom( status::Custom(
Status::InternalServerError, Status::InternalServerError,
format!("Database error: {}", e), // 数据库错误信息 format!("Database initialization failed: {}", e), // 连接失败信息
) )
})?; })?;
let query_result = db Ok(status::Custom(
.get_db() Status::Ok,
.query("SELECT * FROM info".to_string()) format!("Initialization successful"),
.await ))
.map_err(|e| status::Custom(Status::InternalServerError, format!("Query error: {}", e)))?;
Ok(Json(query_result)) // 返回查询结果
} }
/** /**
* * JWT接口
*
* #
* - `Result<status::Custom<String>, status::Custom<String>>`: JWT令牌或错误信息
*/ */
#[get("/install")]
async fn install() -> status::Custom<String> {
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")] #[get("/system")]
async fn token_system() -> Result<status::Custom<String>, status::Custom<String>> { async fn token_system() -> Result<status::Custom<String>, status::Custom<String>> {
// 创建 Claims // 创建 Claims
let claims = secret::CustomClaims { let claims = secret::CustomClaims {
user_id: String::from("system"), user_id: String::from("system"), // 用户ID
device_ua: String::from("system"), device_ua: String::from("system"), // 设备用户代理
}; };
// 生成JWT // 生成JWT
let token = secret::generate_jwt(claims,Duration::seconds(1)) let token = secret::generate_jwt(claims, Duration::seconds(1)).map_err(|e| {
.map_err(|e| status::Custom(Status::InternalServerError, format!("JWT generation failed: {}", e)))?; status::Custom(
Status::InternalServerError,
format!("JWT generation failed: {}", e), // JWT生成失败信息
)
})?;
Ok(status::Custom(Status::Ok, token)) Ok(status::Custom(Status::Ok, token)) // 返回JWT令牌
} }
/** /**
* Rocket应用 * Rocket应用
*
* #
* - `rocket::Rocket`: Rocket应用实例
*/ */
#[launch] #[launch]
async fn rocket() -> _ { async fn rocket() -> _ {
let config = config::Config::read().expect("Failed to read config"); // 读取配置 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 .await
.expect("Failed to connect to database"); // 初始化数据库连接 .expect("Failed to connect to database"); // 初始化数据库连接
rocket::build() rocket::build()
.mount("/", routes![install, ssql]) // 挂载API路由
.mount("/auth/token", routes![token_system]) .mount("/auth/token", routes![token_system])
} else {
rocket::build()
.mount("/", routes![install]) // 挂载API路由
}
} }