ruskit/bootstrap/
app.rs

1use crate::framework::{
2    middleware::{
3        Middleware,
4        MiddlewareStack,
5        presets::{Cors, TrimStrings}
6    },
7    views::{Metadata, set_global_metadata},
8    database::{self},
9    inertia::InertiaConfig,
10};
11use std::sync::Arc;
12use tokio::sync::RwLock;
13use once_cell::sync::Lazy;
14use crate::app::models;
15use std::path::Path;
16use std::fs;
17use sqlx::sqlite::SqlitePool;
18
19/// Global application state
20static APP: Lazy<Arc<RwLock<Application>>> = Lazy::new(|| {
21    Arc::new(RwLock::new(Application::new()))
22});
23
24/// Application configuration
25#[derive(Clone)]
26pub struct Application {
27    /// Global middleware stack
28    middleware_stack: MiddlewareStack,
29    /// Middleware groups
30    middleware_groups: Vec<(String, Vec<Middleware>)>,
31    /// Global metadata
32    metadata: Option<Metadata>,
33    /// Inertia configuration
34    inertia_config: Option<InertiaConfig>,
35}
36
37impl Application {
38    pub fn new() -> Self {
39        Self {
40            middleware_stack: MiddlewareStack::new(),
41            middleware_groups: Vec::new(),
42            metadata: None,
43            inertia_config: None,
44        }
45    }
46
47    /// Get the global application instance
48    pub async fn instance() -> Arc<RwLock<Self>> {
49        Arc::clone(&APP)
50    }
51
52    /// Configure global middleware
53    pub async fn middleware<F>(&mut self, configure: F)
54    where
55        F: FnOnce(&mut MiddlewareStack),
56    {
57        configure(&mut self.middleware_stack);
58    }
59
60    /// Configure middleware groups
61    pub async fn middleware_groups<F>(&mut self, configure: F)
62    where
63        F: FnOnce(&mut Vec<(String, Vec<Middleware>)>),
64    {
65        configure(&mut self.middleware_groups);
66    }
67
68    /// Configure global metadata
69    pub async fn metadata<F>(&mut self, configure: F)
70    where
71        F: FnOnce() -> Metadata,
72    {
73        self.metadata = Some(configure());
74        if let Some(metadata) = &self.metadata {
75            set_global_metadata(metadata.clone());
76        }
77    }
78
79    /// Configure Inertia
80    pub async fn inertia<F>(&mut self, configure: F)
81    where
82        F: FnOnce() -> InertiaConfig,
83    {
84        self.inertia_config = Some(configure());
85    }
86
87    /// Get the global middleware stack
88    pub fn middleware_stack(&self) -> MiddlewareStack {
89        self.middleware_stack.clone()
90    }
91
92    /// Get the middleware groups
93    pub fn groups(&self) -> Vec<(String, Vec<Middleware>)> {
94        self.middleware_groups.clone()
95    }
96
97    /// Get the Inertia configuration
98    pub fn inertia_config(&self) -> Option<InertiaConfig> {
99        self.inertia_config.clone()
100    }
101}
102
103/// Initialize the application
104pub async fn bootstrap() -> Result<Application, Box<dyn std::error::Error>> {
105    // Register all models
106    models::register_models();
107
108    // Initialize database
109    println!("Initializing database...");
110    let db_config = database::config::DatabaseConfig::from_env();
111    println!("Initializing database at path: {}", db_config.database_path());
112    
113    // Create database directory if it doesn't exist
114    if let Some(parent) = Path::new(&db_config.database_path()).parent() {
115        println!("Creating database directory: {}", parent.display());
116        fs::create_dir_all(parent)?;
117    }
118    
119    println!("Connecting to database with URL: {}", db_config.connection_url());
120    let pool = SqlitePool::connect(&db_config.connection_url()).await?;
121    println!("Successfully connected to database");
122    
123    // Enable foreign key constraints
124    println!("Enabling foreign key constraints");
125    sqlx::query("PRAGMA foreign_keys = ON")
126        .execute(&pool)
127        .await?;
128    
129    // Store the pool globally
130    *database::POOL.lock().unwrap() = Some(Arc::new(pool));
131    println!("Database pool initialized successfully");
132
133
134    let app = Application::instance().await;
135    let mut app = app.write().await;
136
137    // Configure global middleware
138    app.middleware(|stack| {
139        stack.add(Middleware::Cors(Cors::new("*")));
140        stack.add(Middleware::TrimStrings(TrimStrings::new()));
141    }).await;
142
143    // Configure middleware groups
144    app.middleware_groups(|groups| {
145        groups.push(("api".to_string(), vec![
146            Middleware::Cors(Cors::new("*")),
147            Middleware::TrimStrings(TrimStrings::new()),
148        ]));
149    }).await;
150
151    // Configure global metadata
152    app.metadata(|| {
153        Metadata::new("Ruskit")
154            .with_description("A modern web framework for Rust")
155            .with_keywords("rust, web, framework")
156            .with_author("Your Name")
157            .with_og_title("Ruskit")
158            .with_og_description("A modern web framework for Rust")
159            .with_og_image("https://example.com/og-image.jpg")
160    }).await;
161
162    let app_clone = app.clone();
163    Ok(app_clone)
164}
165
166/// Get the application's middleware stack
167pub async fn middleware_stack() -> MiddlewareStack {
168    let app = Application::instance().await;
169    let app = app.read().await;
170    app.middleware_stack()
171}
172
173/// Get a middleware group by name
174pub async fn middleware_group(name: &str) -> Option<Vec<Middleware>> {
175    let app = Application::instance().await;
176    let app = app.read().await;
177    app.groups()
178        .iter()
179        .find(|(group_name, _)| group_name == name)
180        .map(|(_, middlewares)| middlewares.clone())
181}