claw_spawn/server/
state.rs1use crate::application::{BotLifecycleService, ProvisioningService};
2use crate::infrastructure::{
3 AppConfig, DigitalOceanClient, PostgresAccountRepository, PostgresBotRepository,
4 PostgresConfigRepository, PostgresDropletRepository, SecretsEncryption,
5};
6use anyhow::Context;
7use sqlx::PgPool;
8use std::sync::Arc;
9
10pub type ProvisioningServiceType = ProvisioningService<
11 PostgresAccountRepository,
12 PostgresBotRepository,
13 PostgresConfigRepository,
14 PostgresDropletRepository,
15>;
16
17pub type BotLifecycleServiceType =
18 BotLifecycleService<PostgresBotRepository, PostgresConfigRepository>;
19
20#[derive(Clone)]
21pub struct AppState {
22 pub pool: PgPool,
23 pub account_repo: Arc<PostgresAccountRepository>,
24 pub provisioning: Arc<ProvisioningServiceType>,
25 pub lifecycle: Arc<BotLifecycleServiceType>,
26}
27
28pub async fn build_state_with_pool(
32 config: AppConfig,
33 pool: PgPool,
34 run_migrations: bool,
35) -> anyhow::Result<AppState> {
36 if run_migrations {
37 sqlx::migrate!("./migrations")
38 .run(&pool)
39 .await
40 .context("run migrations")?;
41 }
42
43 let encryption =
44 Arc::new(SecretsEncryption::new(&config.encryption_key).context("init encryption")?);
45
46 let do_client = Arc::new(
47 DigitalOceanClient::new(config.digitalocean_token).context("init DigitalOcean client")?,
48 );
49
50 let account_repo = Arc::new(PostgresAccountRepository::new(pool.clone()));
51 let bot_repo = Arc::new(PostgresBotRepository::new(pool.clone()));
52 let config_repo = Arc::new(PostgresConfigRepository::new(pool.clone()));
53 let droplet_repo = Arc::new(PostgresDropletRepository::new(pool.clone()));
54
55 let provisioning = Arc::new(ProvisioningService::new(
56 do_client,
57 account_repo.clone(),
58 bot_repo.clone(),
59 config_repo.clone(),
60 droplet_repo.clone(),
61 encryption,
62 config.openclaw_image,
63 config.control_plane_url,
64 config.customizer_repo_url,
65 config.customizer_ref,
66 config.customizer_workspace_dir,
67 config.customizer_agent_name,
68 config.customizer_owner_name,
69 config.customizer_skip_qmd,
70 config.customizer_skip_cron,
71 config.customizer_skip_git,
72 config.customizer_skip_heartbeat,
73 ));
74
75 let lifecycle = Arc::new(BotLifecycleService::new(
76 bot_repo.clone(),
77 config_repo.clone(),
78 ));
79
80 Ok(AppState {
81 pool,
82 account_repo,
83 provisioning,
84 lifecycle,
85 })
86}
87
88pub async fn build_state_from_env(config: AppConfig) -> anyhow::Result<AppState> {
92 let pool = PgPool::connect(&config.database_url)
93 .await
94 .context("connect database")?;
95 build_state_with_pool(config, pool, true).await
96}