mirror of
https://github.com/house-of-vanity/yggman.git
synced 2025-12-16 19:17:56 +00:00
first commit
This commit is contained in:
64
src/database/connection.rs
Normal file
64
src/database/connection.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
use sea_orm::{Database, DatabaseConnection, DbErr, ConnectionTrait};
|
||||
use sea_orm::{Schema, DbBackend, Statement};
|
||||
use migration::prelude::{SqliteQueryBuilder, PostgresQueryBuilder, MysqlQueryBuilder};
|
||||
use std::time::Duration;
|
||||
use std::path::Path;
|
||||
use crate::config::DatabaseConfig;
|
||||
|
||||
pub async fn create_connection(config: &DatabaseConfig) -> Result<DatabaseConnection, DbErr> {
|
||||
// Create SQLite database file if it doesn't exist
|
||||
if config.url.starts_with("sqlite://") {
|
||||
let db_path = config.url.strip_prefix("sqlite://").unwrap_or(&config.url);
|
||||
|
||||
// Create parent directories if they don't exist
|
||||
if let Some(parent) = Path::new(db_path).parent() {
|
||||
if !parent.exists() {
|
||||
std::fs::create_dir_all(parent)
|
||||
.map_err(|e| DbErr::Custom(format!("Failed to create database directory: {}", e)))?;
|
||||
}
|
||||
}
|
||||
|
||||
// Create empty database file if it doesn't exist
|
||||
if !Path::new(db_path).exists() {
|
||||
std::fs::File::create(db_path)
|
||||
.map_err(|e| DbErr::Custom(format!("Failed to create database file: {}", e)))?;
|
||||
tracing::info!("Created SQLite database file: {}", db_path);
|
||||
}
|
||||
}
|
||||
|
||||
let mut options = sea_orm::ConnectOptions::new(&config.url);
|
||||
|
||||
options
|
||||
.max_connections(config.max_connections)
|
||||
.min_connections(1)
|
||||
.connect_timeout(Duration::from_secs(config.connect_timeout))
|
||||
.acquire_timeout(Duration::from_secs(config.acquire_timeout))
|
||||
.idle_timeout(Duration::from_secs(config.idle_timeout))
|
||||
.max_lifetime(Duration::from_secs(config.max_lifetime))
|
||||
.sqlx_logging(true)
|
||||
.sqlx_logging_level(tracing::log::LevelFilter::Debug);
|
||||
|
||||
Database::connect(options).await
|
||||
}
|
||||
|
||||
pub async fn migrate_database(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
// Get the database backend
|
||||
let backend = db.get_database_backend();
|
||||
let schema = Schema::new(backend);
|
||||
|
||||
// Create nodes table if it doesn't exist
|
||||
let mut create_table_stmt = schema.create_table_from_entity(crate::database::entities::node::Entity);
|
||||
|
||||
// Convert to SQL
|
||||
let sql = match backend {
|
||||
DbBackend::Sqlite => create_table_stmt.if_not_exists().to_string(SqliteQueryBuilder),
|
||||
DbBackend::Postgres => create_table_stmt.if_not_exists().to_string(PostgresQueryBuilder),
|
||||
DbBackend::MySql => create_table_stmt.if_not_exists().to_string(MysqlQueryBuilder),
|
||||
};
|
||||
|
||||
// Execute the statement
|
||||
db.execute(Statement::from_string(backend, sql)).await?;
|
||||
|
||||
tracing::info!("Database migration completed");
|
||||
Ok(())
|
||||
}
|
||||
1
src/database/entities/mod.rs
Normal file
1
src/database/entities/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod node;
|
||||
82
src/database/entities/node.rs
Normal file
82
src/database/entities/node.rs
Normal file
@@ -0,0 +1,82 @@
|
||||
use sea_orm::entity::prelude::*;
|
||||
use sea_orm::Set;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "nodes")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub public_key: String,
|
||||
pub private_key: String,
|
||||
pub listen: String, // JSON array stored as string
|
||||
pub addresses: String, // JSON array stored as string
|
||||
pub created_at: DateTimeUtc,
|
||||
pub updated_at: DateTimeUtc,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
id: Set(uuid::Uuid::new_v4().to_string()),
|
||||
created_at: Set(chrono::Utc::now()),
|
||||
updated_at: Set(chrono::Utc::now()),
|
||||
..ActiveModelTrait::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn before_save<'life0, 'async_trait, C>(
|
||||
mut self,
|
||||
_db: &'life0 C,
|
||||
_insert: bool,
|
||||
) -> core::pin::Pin<Box<dyn core::future::Future<Output = Result<Self, DbErr>> + core::marker::Send + 'async_trait>>
|
||||
where
|
||||
'life0: 'async_trait,
|
||||
C: 'async_trait + ConnectionTrait,
|
||||
Self: 'async_trait,
|
||||
{
|
||||
Box::pin(async move {
|
||||
self.updated_at = Set(chrono::Utc::now());
|
||||
Ok(self)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Conversion functions between database model and domain model
|
||||
impl From<Model> for crate::yggdrasil::Node {
|
||||
fn from(model: Model) -> Self {
|
||||
let listen: Vec<String> = serde_json::from_str(&model.listen).unwrap_or_default();
|
||||
let addresses: Vec<String> = serde_json::from_str(&model.addresses).unwrap_or_default();
|
||||
|
||||
crate::yggdrasil::Node {
|
||||
id: model.id,
|
||||
name: model.name,
|
||||
public_key: model.public_key,
|
||||
private_key: model.private_key,
|
||||
listen,
|
||||
addresses,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&crate::yggdrasil::Node> for ActiveModel {
|
||||
fn from(node: &crate::yggdrasil::Node) -> Self {
|
||||
let listen = serde_json::to_string(&node.listen).unwrap_or_default();
|
||||
let addresses = serde_json::to_string(&node.addresses).unwrap_or_default();
|
||||
|
||||
ActiveModel {
|
||||
id: Set(node.id.clone()),
|
||||
name: Set(node.name.clone()),
|
||||
public_key: Set(node.public_key.clone()),
|
||||
private_key: Set(node.private_key.clone()),
|
||||
listen: Set(listen),
|
||||
addresses: Set(addresses),
|
||||
created_at: Set(chrono::Utc::now()),
|
||||
updated_at: Set(chrono::Utc::now()),
|
||||
}
|
||||
}
|
||||
}
|
||||
4
src/database/mod.rs
Normal file
4
src/database/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod entities;
|
||||
pub mod connection;
|
||||
|
||||
pub use connection::*;
|
||||
Reference in New Issue
Block a user