mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-19 19:04:23 +08:00
237 lines
6.5 KiB
Rust
237 lines
6.5 KiB
Rust
use sea_orm::{ConnectOptions, Database, DatabaseConnection, DbErr};
|
|
use std::str::FromStr;
|
|
use std::sync::Arc;
|
|
use std::time::Duration;
|
|
use tokio::sync::RwLock;
|
|
use tracing::{info, warn};
|
|
|
|
const DB_MAX_CONNECTIONS: u32 = 20;
|
|
const DB_MIN_CONNECTIONS: u32 = 1;
|
|
const DB_CONNECT_TIMEOUT_SECS: u64 = 12;
|
|
const DB_ACQUIRE_TIMEOUT_SECS: u64 = 12;
|
|
const DB_IDLE_TIMEOUT_SECS: u64 = 30;
|
|
|
|
fn apply_default_connect_options(opt: &mut ConnectOptions) {
|
|
opt.max_connections(DB_MAX_CONNECTIONS)
|
|
.min_connections(DB_MIN_CONNECTIONS)
|
|
.connect_timeout(Duration::from_secs(DB_CONNECT_TIMEOUT_SECS))
|
|
.acquire_timeout(Duration::from_secs(DB_ACQUIRE_TIMEOUT_SECS))
|
|
.idle_timeout(Duration::from_secs(DB_IDLE_TIMEOUT_SECS))
|
|
.sqlx_logging(true)
|
|
.sqlx_logging_level(tracing::log::LevelFilter::Info);
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub enum DatabaseType {
|
|
SQLite,
|
|
PostgreSQL,
|
|
}
|
|
|
|
impl FromStr for DatabaseType {
|
|
type Err = String;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s.to_lowercase().as_str() {
|
|
"sqlite" => Ok(Self::SQLite),
|
|
"postgres" | "postgresql" => Ok(Self::PostgreSQL),
|
|
_ => Err(format!("Unknown database type: {}", s)),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct DatabaseConfig {
|
|
pub db_type: DatabaseType,
|
|
pub sqlite_path: String,
|
|
pub postgres_url: Option<String>,
|
|
}
|
|
|
|
impl Default for DatabaseConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
db_type: DatabaseType::SQLite,
|
|
sqlite_path: "secscore.db".to_string(),
|
|
postgres_url: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl DatabaseConfig {
|
|
pub fn sqlite(path: String) -> Self {
|
|
Self {
|
|
db_type: DatabaseType::SQLite,
|
|
sqlite_path: path,
|
|
postgres_url: None,
|
|
}
|
|
}
|
|
|
|
pub fn postgres(url: String) -> Self {
|
|
Self {
|
|
db_type: DatabaseType::PostgreSQL,
|
|
sqlite_path: String::new(),
|
|
postgres_url: Some(url),
|
|
}
|
|
}
|
|
|
|
pub fn connection_url(&self) -> String {
|
|
match self.db_type {
|
|
DatabaseType::SQLite => format!("sqlite://{}?mode=rwc", self.sqlite_path),
|
|
DatabaseType::PostgreSQL => self.postgres_url.clone().unwrap_or_default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ConnectionManager {
|
|
connection: Arc<RwLock<Option<DatabaseConnection>>>,
|
|
config: Arc<RwLock<DatabaseConfig>>,
|
|
}
|
|
|
|
impl Clone for ConnectionManager {
|
|
fn clone(&self) -> Self {
|
|
Self {
|
|
connection: self.connection.clone(),
|
|
config: self.config.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for ConnectionManager {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl ConnectionManager {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
connection: Arc::new(RwLock::new(None)),
|
|
config: Arc::new(RwLock::new(DatabaseConfig::default())),
|
|
}
|
|
}
|
|
|
|
pub async fn connect(&self, config: DatabaseConfig) -> Result<(), DbErr> {
|
|
let url = config.connection_url();
|
|
info!(
|
|
"Connecting to database: {} (type: {:?})",
|
|
if config.db_type == DatabaseType::PostgreSQL {
|
|
"postgresql://***"
|
|
} else {
|
|
&url
|
|
},
|
|
config.db_type
|
|
);
|
|
|
|
let mut opt = ConnectOptions::new(&url);
|
|
apply_default_connect_options(&mut opt);
|
|
|
|
let conn = Database::connect(opt).await?;
|
|
|
|
let mut connection_guard = self.connection.write().await;
|
|
*connection_guard = Some(conn);
|
|
|
|
let mut config_guard = self.config.write().await;
|
|
*config_guard = config;
|
|
|
|
info!("Database connection established successfully");
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn connect_sqlite(&self, path: &str) -> Result<(), DbErr> {
|
|
let config = DatabaseConfig::sqlite(path.to_string());
|
|
self.connect(config).await
|
|
}
|
|
|
|
pub async fn connect_postgres(&self, url: &str) -> Result<(), DbErr> {
|
|
let config = DatabaseConfig::postgres(url.to_string());
|
|
self.connect(config).await
|
|
}
|
|
|
|
pub async fn disconnect(&self) -> Result<(), DbErr> {
|
|
let mut connection_guard = self.connection.write().await;
|
|
if let Some(conn) = connection_guard.take() {
|
|
conn.close().await?;
|
|
info!("Database connection closed");
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_connection(&self) -> Option<DatabaseConnection> {
|
|
let guard = self.connection.read().await;
|
|
guard.clone()
|
|
}
|
|
|
|
pub async fn is_connected(&self) -> bool {
|
|
let guard = self.connection.read().await;
|
|
guard.is_some()
|
|
}
|
|
|
|
pub async fn get_config(&self) -> DatabaseConfig {
|
|
let guard = self.config.read().await;
|
|
guard.clone()
|
|
}
|
|
|
|
pub async fn get_database_type(&self) -> DatabaseType {
|
|
let guard = self.config.read().await;
|
|
guard.db_type.clone()
|
|
}
|
|
|
|
pub async fn test_connection(&self) -> Result<bool, DbErr> {
|
|
let conn = self.get_connection().await;
|
|
if let Some(conn) = conn {
|
|
match conn.ping().await {
|
|
Ok(_) => {
|
|
info!("Database connection test successful");
|
|
Ok(true)
|
|
}
|
|
Err(e) => {
|
|
warn!("Database connection test failed: {}", e);
|
|
Err(e)
|
|
}
|
|
}
|
|
} else {
|
|
warn!("No database connection available");
|
|
Ok(false)
|
|
}
|
|
}
|
|
|
|
pub async fn switch_database(&self, config: DatabaseConfig) -> Result<(), DbErr> {
|
|
info!("Switching database to type: {:?}", config.db_type);
|
|
|
|
self.disconnect().await?;
|
|
|
|
self.connect(config).await?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub async fn create_sqlite_connection(path: &str) -> Result<DatabaseConnection, DbErr> {
|
|
let url = format!("sqlite://{}?mode=rwc", path);
|
|
let mut opt = ConnectOptions::new(&url);
|
|
apply_default_connect_options(&mut opt);
|
|
|
|
Database::connect(opt).await
|
|
}
|
|
|
|
pub async fn create_postgres_connection(url: &str) -> Result<DatabaseConnection, DbErr> {
|
|
let mut opt = ConnectOptions::new(url);
|
|
apply_default_connect_options(&mut opt);
|
|
|
|
Database::connect(opt).await
|
|
}
|
|
|
|
pub async fn test_sqlite_connection(path: &str) -> Result<bool, DbErr> {
|
|
let conn = create_sqlite_connection(path).await?;
|
|
conn.ping().await?;
|
|
conn.close().await?;
|
|
Ok(true)
|
|
}
|
|
|
|
pub async fn test_postgres_connection(url: &str) -> Result<bool, DbErr> {
|
|
let conn = create_postgres_connection(url).await?;
|
|
conn.ping().await?;
|
|
conn.close().await?;
|
|
Ok(true)
|
|
}
|