数据库:修改config字段
后端:去除全局配置文件,安装时增加创建系统的配置文件,新增或者系统配置文件路由 前端:为系统安装状态添加了环境变量。
This commit is contained in:
parent
d2eac057ca
commit
b1854b4fb8
@ -5,16 +5,39 @@ use std::{env, fs};
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct Config {
|
||||
pub address: String,
|
||||
pub port: u32,
|
||||
pub info: Info,
|
||||
pub sql_config: SqlConfig,
|
||||
}
|
||||
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
address: "0.0.0.0".to_string(),
|
||||
port: 22000,
|
||||
info: Info::default(),
|
||||
sql_config: SqlConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct Info {
|
||||
pub install: bool,
|
||||
pub non_relational: bool,
|
||||
}
|
||||
|
||||
impl Default for Info {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
install: false,
|
||||
non_relational: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct SqlConfig {
|
||||
pub db_type: String,
|
||||
@ -25,6 +48,19 @@ pub struct SqlConfig {
|
||||
pub db_name: String,
|
||||
}
|
||||
|
||||
impl Default for SqlConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
db_type: "postgresql".to_string(),
|
||||
address: "localhost".to_string(),
|
||||
port: 5432,
|
||||
user: "postgres".to_string(),
|
||||
password: "postgres".to_string(),
|
||||
db_name: "echoes".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct NoSqlConfig {
|
||||
pub db_type: String,
|
||||
@ -35,6 +71,19 @@ pub struct NoSqlConfig {
|
||||
pub db_name: String,
|
||||
}
|
||||
|
||||
impl Default for NoSqlConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
db_type: "postgresql".to_string(),
|
||||
address: "localhost".to_string(),
|
||||
port: 5432,
|
||||
user: "postgres".to_string(),
|
||||
password: "postgres".to_string(),
|
||||
db_name: "echoes".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn read() -> CustomResult<Self> {
|
||||
let path = Self::get_path()?;
|
||||
|
@ -77,13 +77,12 @@ impl TextValidator {
|
||||
let max_length = self
|
||||
.level_max_lengths
|
||||
.get(&level)
|
||||
.ok_or_else(|| "Invalid validation level".into_custom_error())?;
|
||||
.ok_or( "Invalid validation level".into_custom_error())?;
|
||||
|
||||
if text.len() > *max_length {
|
||||
return Err("Text exceeds maximum length".into_custom_error());
|
||||
}
|
||||
|
||||
// 简化验证逻辑
|
||||
if level == ValidationLevel::Relaxed {
|
||||
return self.validate_sql_patterns(text);
|
||||
}
|
||||
@ -514,7 +513,6 @@ impl QueryBuilder {
|
||||
Ok((sql, params))
|
||||
}
|
||||
|
||||
// 添加新的辅助方法
|
||||
fn build_where_clause_with_index(
|
||||
&self,
|
||||
clause: &WhereClause,
|
||||
|
@ -80,5 +80,5 @@ CREATE TABLE library
|
||||
CREATE TABLE config
|
||||
(
|
||||
config_name VARCHAR(50) PRIMARY KEY CHECK (LOWER(config_name) = config_name),
|
||||
config_config JSON
|
||||
config_data JSON
|
||||
);
|
||||
|
@ -12,16 +12,14 @@ use error::{CustomErrorInto, CustomResult};
|
||||
|
||||
pub struct AppState {
|
||||
db: Arc<Mutex<Option<relational::Database>>>,
|
||||
configure: Arc<Mutex<config::Config>>,
|
||||
shutdown: Arc<Mutex<Option<Shutdown>>>,
|
||||
restart_progress: Arc<Mutex<bool>>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn new(config: config::Config) -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
db: Arc::new(Mutex::new(None)),
|
||||
configure: Arc::new(Mutex::new(config)),
|
||||
shutdown: Arc::new(Mutex::new(None)),
|
||||
restart_progress: Arc::new(Mutex::new(false)),
|
||||
}
|
||||
@ -61,22 +59,24 @@ impl AppState {
|
||||
|
||||
#[rocket::main]
|
||||
async fn main() -> CustomResult<()> {
|
||||
let config = config::Config::read()?;
|
||||
let config = config::Config::read().unwrap_or_default();
|
||||
|
||||
let state = AppState::new(config.clone());
|
||||
|
||||
if config.info.install {
|
||||
state.sql_link(&config.sql_config).await?;
|
||||
}
|
||||
let rocket_config = rocket::Config::figment()
|
||||
.merge(("address", config.address.clone()))
|
||||
.merge(("port", config.port));
|
||||
let state = AppState::new();
|
||||
|
||||
let state = Arc::new(state);
|
||||
|
||||
let rocket_builder = rocket::build().manage(state.clone());
|
||||
let rocket_builder = rocket::build().configure(rocket_config).manage(state.clone());
|
||||
|
||||
let rocket_builder = if !config.info.install {
|
||||
rocket_builder.mount("/", rocket::routes![routes::install::install])
|
||||
} else {
|
||||
rocket_builder.mount("/auth/token", routes::jwt_routes())
|
||||
state.sql_link(&config.sql_config).await?;
|
||||
rocket_builder
|
||||
.mount("/auth/token", routes::jwt_routes())
|
||||
.mount("/config", routes::configure_routes())
|
||||
};
|
||||
|
||||
let rocket = rocket_builder.ignite().await?;
|
||||
|
@ -3,7 +3,6 @@ use crate::database::relational::builder;
|
||||
use crate::error::{AppResult, AppResultInto};
|
||||
use crate::AppState;
|
||||
use chrono::Duration;
|
||||
use jwt_compact::Token;
|
||||
use rocket::{
|
||||
http::Status,
|
||||
post,
|
||||
@ -91,12 +90,14 @@ pub async fn token_system(
|
||||
String::from("该用户密码丢失"),
|
||||
))?;
|
||||
|
||||
auth::bcrypt::verify_hash(&data.password, password).into_app_result()?;
|
||||
auth::bcrypt::verify_hash(&data.password, password).map_err(|_| {
|
||||
status::Custom(Status::Forbidden, String::from("密码错误"))
|
||||
})?;
|
||||
|
||||
let claims = auth::jwt::CustomClaims {
|
||||
name: "system".into(),
|
||||
};
|
||||
let token = auth::jwt::generate_jwt(claims, Duration::seconds(1)).into_app_result()?;
|
||||
let token = auth::jwt::generate_jwt(claims, Duration::minutes(1)).into_app_result()?;
|
||||
|
||||
Ok(token)
|
||||
}
|
||||
|
@ -1,10 +1,85 @@
|
||||
use super::SystemToken;
|
||||
use crate::error::AppResult;
|
||||
use crate::database::{relational, relational::builder};
|
||||
use crate::error::{AppResult, AppResultInto, CustomResult};
|
||||
use crate::AppState;
|
||||
use rocket::{
|
||||
get,
|
||||
http::Status,
|
||||
post,
|
||||
response::status,
|
||||
serde::json::{Json, Value},
|
||||
Request,
|
||||
State,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct SystemConfigure {
|
||||
pub author_name: String,
|
||||
pub current_theme: String,
|
||||
pub site_keyword: Vec<String>,
|
||||
pub site_description: String,
|
||||
pub admin_path: String,
|
||||
}
|
||||
|
||||
impl Default for SystemConfigure {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
author_name: "lsy".to_string(),
|
||||
current_theme: "default".to_string(),
|
||||
site_keyword: vec!["echoes".to_string()],
|
||||
site_description: "echoes是一个高效、可扩展的博客平台".to_string(),
|
||||
admin_path: "admin".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_configure(
|
||||
sql: &relational::Database,
|
||||
comfig_type: String,
|
||||
name: String,
|
||||
) -> CustomResult<Json<Value>> {
|
||||
let mut sql_builder =
|
||||
builder::QueryBuilder::new(builder::SqlOperation::Select, "config".to_string())?;
|
||||
sql_builder.set_value(
|
||||
"config_name".to_string(),
|
||||
builder::SafeValue::Text(
|
||||
format!("{}_{}", comfig_type, name).to_string(),
|
||||
builder::ValidationLevel::Strict,
|
||||
),
|
||||
)?;
|
||||
let result = sql.get_db().execute_query(&sql_builder).await?;
|
||||
Ok(Json(json!(result)))
|
||||
}
|
||||
|
||||
pub async fn insert_configure(
|
||||
sql: &relational::Database,
|
||||
comfig_type: String,
|
||||
name: String,
|
||||
data: Json<Value>,
|
||||
) -> CustomResult<()> {
|
||||
let mut builder =
|
||||
builder::QueryBuilder::new(builder::SqlOperation::Insert, "config".to_string())?;
|
||||
builder.set_value(
|
||||
"config_name".to_string(),
|
||||
builder::SafeValue::Text(
|
||||
format!("{}_{}", comfig_type, name).to_string(),
|
||||
builder::ValidationLevel::Strict,
|
||||
),
|
||||
)?;
|
||||
builder.set_value(
|
||||
"config_data".to_string(),
|
||||
builder::SafeValue::Json(data.into_inner()),
|
||||
)?;
|
||||
sql.get_db().execute_query(&builder).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[get("/system")]
|
||||
pub async fn system_config_get(state: &State<Arc<AppState>>,token: SystemToken) -> AppResult<Json<Value>> {
|
||||
let sql = state.sql_get().await.into_app_result()?;
|
||||
let configure = get_configure(&sql, "system".to_string(), "configure".to_string())
|
||||
.await
|
||||
.into_app_result()?;
|
||||
Ok(configure)
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
use crate::auth;
|
||||
use crate::database::relational;
|
||||
use crate::error::{AppResult, AppResultInto};
|
||||
use crate::routes::person;
|
||||
use super::{person, configure};
|
||||
use crate::AppState;
|
||||
use crate::{config, utils};
|
||||
use chrono::Duration;
|
||||
use rocket::{http::Status, post, response::status, serde::json::Json, State};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
use serde_json::json;
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct InstallData {
|
||||
@ -28,7 +29,7 @@ pub async fn install(
|
||||
data: Json<InstallData>,
|
||||
state: &State<Arc<AppState>>,
|
||||
) -> AppResult<status::Custom<Json<InstallReplyData>>> {
|
||||
let mut config = state.configure.lock().await;
|
||||
let mut config = config::Config::read().unwrap_or_default();
|
||||
if config.info.install {
|
||||
return Err(status::Custom(
|
||||
Status::BadRequest,
|
||||
@ -36,6 +37,9 @@ pub async fn install(
|
||||
));
|
||||
}
|
||||
|
||||
config.info.install = true;
|
||||
config.sql_config = data.sql_config.clone();
|
||||
|
||||
let data = data.into_inner();
|
||||
|
||||
relational::Database::initial_setup(data.sql_config.clone())
|
||||
@ -44,7 +48,7 @@ pub async fn install(
|
||||
|
||||
let _ = auth::jwt::generate_key();
|
||||
|
||||
config.info.install = true;
|
||||
|
||||
|
||||
state.sql_link(&data.sql_config).await.into_app_result()?;
|
||||
let sql = state.sql_get().await.into_app_result()?;
|
||||
@ -76,6 +80,12 @@ pub async fn install(
|
||||
.await
|
||||
.into_app_result()?;
|
||||
|
||||
let mut system_configure = configure::SystemConfigure::default();
|
||||
system_configure.author_name = data.name.clone();
|
||||
|
||||
configure::insert_configure(&sql, "system".to_string(), "configure".to_string(), Json(json!(system_configure))).await.into_app_result()?;
|
||||
|
||||
|
||||
let token = auth::jwt::generate_jwt(
|
||||
auth::jwt::CustomClaims {
|
||||
name: data.name.clone(),
|
||||
|
@ -2,6 +2,7 @@ pub mod auth;
|
||||
pub mod configure;
|
||||
pub mod install;
|
||||
pub mod person;
|
||||
use crate::auth::jwt;
|
||||
use rocket::http::Status;
|
||||
use rocket::request::{FromRequest, Outcome, Request};
|
||||
use rocket::routes;
|
||||
@ -30,22 +31,14 @@ pub struct SystemToken(String);
|
||||
#[rocket::async_trait]
|
||||
impl<'r> FromRequest<'r> for SystemToken {
|
||||
type Error = ();
|
||||
|
||||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||
let token = request
|
||||
.headers()
|
||||
.get_one("Authorization")
|
||||
.map(|value| value.replace("Bearer ", ""));
|
||||
|
||||
match token {
|
||||
Some(token) => {
|
||||
if token == "system" {
|
||||
Outcome::Success(SystemToken(token))
|
||||
} else {
|
||||
Outcome::Error((Status::Unauthorized, ()))
|
||||
}
|
||||
}
|
||||
None => Outcome::Error((Status::Unauthorized, ())),
|
||||
match token.and_then(|t| jwt::validate_jwt(&t).ok()) {
|
||||
Some(claims) if claims.name == "system" => Outcome::Success(SystemToken(claims.name)),
|
||||
_ => Outcome::Error((Status::Unauthorized, ())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -53,3 +46,7 @@ impl<'r> FromRequest<'r> for SystemToken {
|
||||
pub fn jwt_routes() -> Vec<rocket::Route> {
|
||||
routes![auth::token::token_system]
|
||||
}
|
||||
|
||||
pub fn configure_routes() -> Vec<rocket::Route> {
|
||||
routes![configure::system_config_get]
|
||||
}
|
||||
|
1
frontend/app/env.d.ts
vendored
1
frontend/app/env.d.ts
vendored
@ -15,6 +15,7 @@ interface ImportMetaEnv {
|
||||
readonly VITE_ASSETS_PATH: string; // 存储静态资源的目录路径
|
||||
VITE_SYSTEM_USERNAME: string; // 前端账号名称
|
||||
VITE_SYSTEM_PASSWORD: string; // 前端账号密码
|
||||
VITE_SYSTEM_STATUS: boolean; // 系统是否进行安装
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
|
Loading…
Reference in New Issue
Block a user