1use std::collections::HashMap;
4use std::net::IpAddr;
5use std::path::PathBuf;
6use std::sync::Arc;
7use std::time::{Instant, SystemTime};
8
9use tokio::sync::{broadcast, Mutex, RwLock};
10use tokio_util::sync::CancellationToken;
11use tuitbot_core::automation::circuit_breaker::CircuitBreaker;
12use tuitbot_core::automation::Runtime;
13use tuitbot_core::config::{ConnectorConfig, ContentSourcesConfig, DeploymentMode};
14use tuitbot_core::content::ContentGenerator;
15use tuitbot_core::storage::DbPool;
16use tuitbot_core::x_api::auth::TokenManager;
17
18use tuitbot_core::error::XApiError;
19use tuitbot_core::x_api::auth;
20
21use crate::ws::WsEvent;
22
23pub struct PendingOAuth {
25 pub code_verifier: String,
27 pub created_at: Instant,
29}
30
31pub struct AppState {
33 pub db: DbPool,
35 pub config_path: PathBuf,
37 pub data_dir: PathBuf,
39 pub event_tx: broadcast::Sender<WsEvent>,
41 pub api_token: String,
43 pub passphrase_hash: RwLock<Option<String>>,
45 pub passphrase_hash_mtime: RwLock<Option<SystemTime>>,
47 pub bind_host: String,
49 pub bind_port: u16,
51 pub login_attempts: Mutex<HashMap<IpAddr, (u32, Instant)>>,
53 pub runtimes: Mutex<HashMap<String, Runtime>>,
55 pub content_generators: Mutex<HashMap<String, Arc<ContentGenerator>>>,
57 pub circuit_breaker: Option<Arc<CircuitBreaker>>,
59 pub watchtower_cancel: Option<CancellationToken>,
61 pub content_sources: ContentSourcesConfig,
63 pub connector_config: ConnectorConfig,
65 pub deployment_mode: DeploymentMode,
67 pub provider_backend: String,
69 pub pending_oauth: Mutex<HashMap<String, PendingOAuth>>,
71 pub token_managers: Mutex<HashMap<String, Arc<TokenManager>>>,
73 pub x_client_id: String,
75}
76
77impl AppState {
78 pub async fn get_x_access_token(
83 &self,
84 token_path: &std::path::Path,
85 account_id: &str,
86 ) -> Result<String, XApiError> {
87 {
89 let managers = self.token_managers.lock().await;
90 if let Some(tm) = managers.get(account_id) {
91 return tm.get_access_token().await;
92 }
93 }
94
95 let tokens = auth::load_tokens(token_path)?.ok_or(XApiError::AuthExpired)?;
97
98 let tm = Arc::new(TokenManager::new(
99 tokens,
100 self.x_client_id.clone(),
101 token_path.to_path_buf(),
102 ));
103
104 let access_token = tm.get_access_token().await?;
105
106 self.token_managers
107 .lock()
108 .await
109 .insert(account_id.to_string(), tm);
110
111 Ok(access_token)
112 }
113}