Skip to main content

minifly_api/
state.rs

1use crate::config::Config;
2use crate::docker::DockerClient;
3use anyhow::Result;
4use minifly_core::models::{App, Machine, Lease};
5use minifly_litefs::manager::LiteFSManager;
6use minifly_network::InternalDnsResolver;
7use sqlx::sqlite::{SqlitePool, SqlitePoolOptions};
8use std::collections::HashMap;
9use std::sync::{Arc, RwLock};
10use std::path::PathBuf;
11use std::time::Instant;
12
13#[derive(Clone)]
14pub struct AppState {
15    pub config: Config,
16    pub db: SqlitePool,
17    pub docker: DockerClient,
18    pub litefs: Arc<LiteFSManager>,
19    pub dns_resolver: Arc<InternalDnsResolver>,
20    pub leases: Arc<RwLock<HashMap<String, Lease>>>,
21    pub machines: Arc<RwLock<HashMap<String, Machine>>>,
22    pub apps: Arc<RwLock<HashMap<String, App>>>,
23    pub start_time: Instant,
24}
25
26impl AppState {
27    pub async fn new(config: Config) -> Result<Self> {
28        // Initialize database connection
29        let db = SqlitePoolOptions::new()
30            .max_connections(5)
31            .connect(&config.database_url)
32            .await?;
33        
34        // Run migrations
35        sqlx::migrate!("./migrations").run(&db).await?;
36        
37        // Initialize Docker client
38        let docker = DockerClient::new(config.docker_host.as_deref())?;
39        
40        // Initialize LiteFS manager
41        let litefs_base_dir = PathBuf::from(&config.data_dir).join("litefs");
42        let litefs = Arc::new(LiteFSManager::new(litefs_base_dir).await?);
43        
44        // Initialize DNS resolver
45        let dns_resolver = Arc::new(InternalDnsResolver::new());
46        
47        Ok(Self {
48            config,
49            db,
50            docker,
51            litefs,
52            dns_resolver,
53            leases: Arc::new(RwLock::new(HashMap::new())),
54            machines: Arc::new(RwLock::new(HashMap::new())),
55            apps: Arc::new(RwLock::new(HashMap::new())),
56            start_time: Instant::now(),
57        })
58    }
59    
60    pub fn generate_machine_id(&self) -> String {
61        // Generate a 15-character hex ID similar to Fly.io
62        use rand::Rng;
63        let mut rng = rand::thread_rng();
64        let bytes: Vec<u8> = (0..8).map(|_| rng.gen()).collect();
65        hex::encode(bytes)[..15].to_string()
66    }
67    
68    pub fn generate_instance_id(&self) -> String {
69        // Generate instance ID in Fly.io format
70        use rand::Rng;
71        let mut rng = rand::thread_rng();
72        let chars: String = (0..26)
73            .map(|_| {
74                let idx = rng.gen_range(0..36);
75                if idx < 10 {
76                    (b'0' + idx) as char
77                } else {
78                    (b'A' + idx - 10) as char
79                }
80            })
81            .collect();
82        chars
83    }
84    
85    pub fn generate_private_ip(&self, app_id: &str, machine_index: u32) -> String {
86        // Generate IPv6 address in Fly.io format: fdaa:0:app_id:a7b:machine_index::2
87        let app_hash = {
88            use sha2::{Sha256, Digest};
89            let mut hasher = Sha256::new();
90            hasher.update(app_id);
91            let result = hasher.finalize();
92            format!("{:x}", u16::from_be_bytes([result[0], result[1]]))
93        };
94        
95        format!("{}{}:a7b:{}::2", self.config.internal_network_prefix, app_hash, machine_index)
96    }
97}