-
-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathmod.rs
More file actions
211 lines (182 loc) · 8.99 KB
/
Copy pathmod.rs
File metadata and controls
211 lines (182 loc) · 8.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
//! Application state management.
//!
//! This module contains the global application state that is shared
//! between Tauri commands.
use mcpmux_core::{
AppSettingsRepository, AppSettingsService, CredentialRepository, FeatureSetRepository,
GatewayPortService, InboundMcpClientRepository, InstalledServerRepository, LogConfig,
OutboundOAuthRepository, ServerDiscoveryService,
ServerFeatureRepository as CoreServerFeatureRepository, ServerLogManager,
SpaceBaseDirRepository, SpaceBuiltinConfigRepository, SpaceRepository, SpaceService,
WorkspaceBindingRepository,
};
use mcpmux_storage::{
Database, FieldEncryptor, SqliteAppSettingsRepository, SqliteCredentialRepository,
SqliteFeatureSetRepository, SqliteInboundMcpClientRepository, SqliteInstalledServerRepository,
SqliteOutboundOAuthRepository, SqliteServerFeatureRepository, SqliteSpaceBaseDirRepository,
SqliteSpaceBuiltinConfigRepository, SqliteSpaceRepository, SqliteWorkspaceBindingRepository,
};
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::Mutex;
use tracing::info;
/// Global application state accessible from commands.
pub struct AppState {
/// Base data directory for the app
data_dir: PathBuf,
/// Directory for space configuration files
spaces_dir: PathBuf,
/// App settings repository (for direct access when needed)
pub settings_repository: Arc<dyn AppSettingsRepository>,
/// Gateway port service (handles port resolution with settings)
pub gateway_port_service: Arc<GatewayPortService>,
/// Service for managing spaces
pub space_service: SpaceService,
/// Server discovery service for loading servers from API/bundled/user spaces
pub server_discovery: Arc<ServerDiscoveryService>,
/// Server log manager for file-based logging
pub server_log_manager: Arc<ServerLogManager>,
/// Installed server repository (per-space installations)
pub installed_server_repository: Arc<dyn InstalledServerRepository>,
/// Credential repository (with encryption)
pub credential_repository: Arc<dyn CredentialRepository>,
/// Backend OAuth repository (our DCR with remote MCP servers)
pub backend_oauth_repository: Arc<dyn OutboundOAuthRepository>,
/// FeatureSet repository for permission bundles
pub feature_set_repository: Arc<dyn FeatureSetRepository>,
/// Client repository for AI clients
pub client_repository: Arc<dyn InboundMcpClientRepository>,
/// Workspace-root -> FeatureSet bindings (resolver v2)
pub workspace_binding_repository: Arc<dyn WorkspaceBindingRepository>,
/// Per-Space base directories (scope a workspace root to a Space by prefix)
pub space_base_dir_repository: Arc<dyn SpaceBaseDirRepository>,
/// Per-Space built-in server config (Tool Optimization enablement + tool toggles)
pub space_builtin_config_repository: Arc<dyn SpaceBuiltinConfigRepository>,
/// Server feature repository for discovered MCP features (implements core trait)
pub server_feature_repository: Arc<SqliteServerFeatureRepository>,
/// Server feature repository cast to core trait (for gateway services)
pub server_feature_repository_core: Arc<dyn CoreServerFeatureRepository>,
/// Field encryptor (used for credential repository creation)
#[allow(dead_code)]
pub encryptor: Arc<FieldEncryptor>,
/// Shared database connection (kept alive for the app lifetime)
#[allow(dead_code)]
db: Arc<Mutex<Database>>,
}
impl AppState {
/// Create a new application state with the given data directory.
pub fn new(data_dir: PathBuf) -> anyhow::Result<Self> {
// Ensure data directory exists
std::fs::create_dir_all(&data_dir)?;
// Get or create master key (DPAPI on Windows, OS Keychain elsewhere)
info!("Retrieving master key...");
let key_provider = mcpmux_storage::create_key_provider(&data_dir)?;
let master_key = key_provider.get_or_create_key()?;
info!("Master key retrieved successfully");
// Create field encryptor
let encryptor = Arc::new(FieldEncryptor::new(&master_key)?);
// Open database
let db_path = data_dir.join("mcpmux.db");
info!("Opening database at {:?}", db_path);
let db = Database::open(&db_path)?;
let db = Arc::new(Mutex::new(db));
// Initialize repositories
let space_repository: Arc<dyn SpaceRepository> =
Arc::new(SqliteSpaceRepository::new(db.clone()));
let installed_server_repository: Arc<dyn InstalledServerRepository> = Arc::new(
SqliteInstalledServerRepository::new(db.clone(), encryptor.clone()),
);
let credential_repository: Arc<dyn CredentialRepository> = Arc::new(
SqliteCredentialRepository::new(db.clone(), encryptor.clone()),
);
let backend_oauth_repository: Arc<dyn OutboundOAuthRepository> =
Arc::new(SqliteOutboundOAuthRepository::new(db.clone()));
let feature_set_repository: Arc<dyn FeatureSetRepository> =
Arc::new(SqliteFeatureSetRepository::new(db.clone()));
let client_repository: Arc<dyn InboundMcpClientRepository> =
Arc::new(SqliteInboundMcpClientRepository::new(db.clone()));
let workspace_binding_repository: Arc<dyn WorkspaceBindingRepository> =
Arc::new(SqliteWorkspaceBindingRepository::new(db.clone()));
let space_base_dir_repository: Arc<dyn SpaceBaseDirRepository> =
Arc::new(SqliteSpaceBaseDirRepository::new(db.clone()));
let space_builtin_config_repository: Arc<dyn SpaceBuiltinConfigRepository> =
Arc::new(SqliteSpaceBuiltinConfigRepository::new(db.clone()));
let server_feature_repository = Arc::new(SqliteServerFeatureRepository::new(db.clone()));
let server_feature_repository_core: Arc<dyn CoreServerFeatureRepository> =
server_feature_repository.clone();
// Create app settings repository and services
let settings_repository: Arc<dyn AppSettingsRepository> =
Arc::new(SqliteAppSettingsRepository::new(db.clone()));
let settings_service = Arc::new(AppSettingsService::new(settings_repository.clone()));
let gateway_port_service = Arc::new(GatewayPortService::new(settings_repository.clone()));
// Create services
let space_service = SpaceService::with_feature_set_repository(
space_repository,
feature_set_repository.clone(),
);
// Create server discovery service
// Spaces directory is relative to app data_dir (single source of truth)
let spaces_dir = data_dir.join("spaces");
std::fs::create_dir_all(&spaces_dir)?;
info!("Using spaces directory: {:?}", spaces_dir);
let registry_url = std::env::var("MCPMUX_REGISTRY_URL")
.unwrap_or_else(|_| "https://api.mcpmux.com".to_string());
info!("Using Registry API URL: {}", registry_url);
let server_discovery = Arc::new(
ServerDiscoveryService::new(data_dir.clone(), spaces_dir.clone())
.with_registry_api(registry_url)
.with_settings_service(settings_service),
);
// Create server log manager
let log_config = LogConfig {
base_dir: data_dir.join("logs"),
max_file_size: 10 * 1024 * 1024, // 10MB
max_files: 30, // 30 files
compress: true,
};
let server_log_manager = Arc::new(ServerLogManager::new(log_config));
info!("Application state initialized successfully");
Ok(Self {
data_dir,
spaces_dir,
settings_repository,
gateway_port_service,
space_service,
server_discovery,
server_log_manager,
installed_server_repository,
credential_repository,
backend_oauth_repository,
feature_set_repository,
client_repository,
workspace_binding_repository,
space_base_dir_repository,
space_builtin_config_repository,
server_feature_repository,
server_feature_repository_core,
encryptor,
db,
})
}
/// Get the shared database connection for use by other components (e.g., gateway)
pub fn database(&self) -> Arc<Mutex<Database>> {
self.db.clone()
}
/// Get the base data directory for the app
pub fn data_dir(&self) -> &std::path::Path {
&self.data_dir
}
/// Get the spaces configuration directory
#[allow(dead_code)]
pub fn spaces_dir(&self) -> &std::path::Path {
&self.spaces_dir
}
/// Get the path to a specific space's config file.
///
/// Fails when `space_id` is not a valid UUID — the id arrives over IPC,
/// so this is the path-traversal guard for every space-config command.
pub fn space_config_path(&self, space_id: &str) -> Result<PathBuf, String> {
mcpmux_core::get_space_config_path(&self.spaces_dir, space_id)
.map_err(|e| format!("Invalid space id '{space_id}': {e}"))
}
}