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