1use parking_lot::Mutex;
2use std::collections::{HashMap, HashSet};
4use std::sync::Arc;
5use std::time::Instant;
6
7use serde_json::Value;
8
9use imessage_core::config::AppConfig;
10use imessage_db::imessage::repository::MessageRepository;
11use imessage_private_api::service::PrivateApiService;
12use imessage_webhooks::service::WebhookService;
13
14use crate::middleware::error::AppError;
15
16pub type FindMyFriendsCache = (HashMap<String, Value>, Option<Instant>);
18
19#[derive(Clone)]
23pub struct AppState {
24 pub config: Arc<AppConfig>,
25 pub imessage_repo: Arc<Mutex<MessageRepository>>,
26 pub private_api: Option<Arc<PrivateApiService>>,
27 pub webhook_service: Option<Arc<WebhookService>>,
28 pub findmy_friends_cache: Arc<Mutex<FindMyFriendsCache>>,
30 pub findmy_key: Arc<Mutex<Option<[u8; 32]>>>,
32 pub send_cache: Arc<Mutex<HashMap<String, Instant>>>,
34 pub typing_cache: Arc<Mutex<HashSet<String>>>,
36 pub findmy_refresh_lock: Arc<tokio::sync::Mutex<()>>,
38}
39
40impl AppState {
41 pub fn new(
42 config: AppConfig,
43 imessage_repo: MessageRepository,
44 private_api: Option<Arc<PrivateApiService>>,
45 webhook_service: Option<Arc<WebhookService>>,
46 ) -> Self {
47 Self {
48 config: Arc::new(config),
49 imessage_repo: Arc::new(Mutex::new(imessage_repo)),
50 private_api,
51 webhook_service,
52 findmy_friends_cache: Arc::new(Mutex::new((HashMap::new(), None))),
53 findmy_key: Arc::new(Mutex::new(None)),
54 send_cache: Arc::new(Mutex::new(HashMap::new())),
55 typing_cache: Arc::new(Mutex::new(HashSet::new())),
56 findmy_refresh_lock: Arc::new(tokio::sync::Mutex::new(())),
57 }
58 }
59
60 pub fn require_private_api(&self) -> Result<Arc<PrivateApiService>, AppError> {
63 let api = self
64 .private_api
65 .as_ref()
66 .ok_or_else(|| AppError::imessage_error("Private API is not enabled"))?;
67 if !api.is_messages_ready() {
68 return Err(AppError::imessage_error(
69 "Private API helper is not connected",
70 ));
71 }
72 Ok(api.clone())
73 }
74
75 pub fn require_findmy_private_api(&self) -> Result<Arc<PrivateApiService>, AppError> {
78 if !self.config.enable_findmy_private_api {
79 return Err(AppError::imessage_error(
80 "FindMy Private API is not enabled",
81 ));
82 }
83 let api = self
84 .private_api
85 .as_ref()
86 .ok_or_else(|| AppError::imessage_error("FindMy Private API is not enabled"))?;
87 if !api.is_findmy_ready() {
88 return Err(AppError::imessage_error(
89 "FindMy Private API helper is not connected",
90 ));
91 }
92 Ok(api.clone())
93 }
94
95 pub fn require_facetime_private_api(&self) -> Result<Arc<PrivateApiService>, AppError> {
98 if !self.config.enable_facetime_private_api {
99 return Err(AppError::imessage_error(
100 "FaceTime Private API is not enabled",
101 ));
102 }
103 let api = self
104 .private_api
105 .as_ref()
106 .ok_or_else(|| AppError::imessage_error("FaceTime Private API is not enabled"))?;
107 if !api.is_facetime_ready() {
108 return Err(AppError::imessage_error(
109 "FaceTime Private API helper is not connected",
110 ));
111 }
112 Ok(api.clone())
113 }
114
115 pub fn is_send_cached(&self, temp_guid: &str) -> bool {
117 self.send_cache.lock().contains_key(temp_guid)
118 }
119
120 pub fn cache_send(&self, temp_guid: String) {
122 self.send_cache.lock().insert(temp_guid, Instant::now());
123 }
124
125 pub fn uncache_send(&self, temp_guid: &str) {
127 self.send_cache.lock().remove(temp_guid);
128 }
129}