use crate::api::{create_router as create_api_router, ApiState};
use crate::auth::AuthService;
use crate::backup::BackupService;
use crate::config::CollabConfig;
use crate::core_bridge::CoreBridge;
use crate::error::Result;
use crate::events::EventBus;
use crate::history::{History, VersionControl};
use crate::merge::MergeService;
use crate::sync::SyncEngine;
use crate::user::UserService;
use crate::websocket::{ws_handler, WsState};
use crate::workspace::WorkspaceService;
use axum::routing::get;
use axum::Router;
use sqlx::{Pool, Sqlite};
use std::sync::Arc;
pub struct CollabServer {
_config: CollabConfig,
db: Pool<Sqlite>,
auth: Arc<AuthService>,
user: Arc<UserService>,
workspace: Arc<WorkspaceService>,
event_bus: Arc<EventBus>,
sync: Arc<SyncEngine>,
history: Arc<History>,
merge: Arc<MergeService>,
backup: Arc<BackupService>,
}
impl CollabServer {
pub async fn new(config: CollabConfig) -> Result<Self> {
let db = sqlx::SqlitePool::connect(&config.database_url).await?;
Self::run_migrations(&db).await?;
let workspace_dir = config.workspace_dir.as_deref().unwrap_or("./workspaces");
let core_bridge = Arc::new(CoreBridge::new(workspace_dir));
let auth = Arc::new(AuthService::new(config.jwt_secret.clone()));
let user = Arc::new(UserService::new(db.clone(), auth.clone()));
let workspace =
Arc::new(WorkspaceService::with_core_bridge(db.clone(), core_bridge.clone()));
let event_bus = Arc::new(EventBus::new(config.event_bus_capacity));
let sync = Arc::new(SyncEngine::with_integration(
event_bus.clone(),
db.clone(),
core_bridge.clone(),
workspace.clone(),
));
let mut history = History::new(db.clone());
history.set_auto_commit(config.auto_commit);
let history = Arc::new(history);
let merge = Arc::new(MergeService::new(db.clone()));
let backup = Arc::new(BackupService::new(
db.clone(),
config.backup_dir.clone(),
core_bridge,
workspace.clone(),
));
Ok(Self {
_config: config,
db,
auth,
user,
workspace,
event_bus,
sync,
history,
merge,
backup,
})
}
pub async fn run_migrations(db: &sqlx::SqlitePool) -> Result<()> {
tracing::info!("Running database migrations");
sqlx::migrate!("./migrations").run(db).await.map_err(|e| {
tracing::error!("Migration failed: {}", e);
crate::error::CollabError::DatabaseError(format!("Migration failed: {e}"))
})?;
tracing::info!("Database migrations completed successfully");
Ok(())
}
pub async fn run(self, addr: &str) -> Result<()> {
tracing::info!("Starting MockForge Collaboration Server on {}", addr);
let version_control = Arc::new(VersionControl::new(self.db.clone()));
let api_state = ApiState {
auth: self.auth.clone(),
user: self.user.clone(),
workspace: self.workspace.clone(),
history: version_control,
merge: self.merge.clone(),
backup: self.backup.clone(),
sync: self.sync.clone(),
};
let api_router = create_api_router(api_state);
let ws_state = WsState {
auth: self.auth.clone(),
sync: self.sync.clone(),
event_bus: self.event_bus.clone(),
workspace: self.workspace.clone(),
};
let app = Router::new()
.route("/ws", get(ws_handler))
.with_state(ws_state)
.merge(api_router);
let listener = tokio::net::TcpListener::bind(addr)
.await
.map_err(|e| crate::error::CollabError::Internal(format!("Failed to bind: {e}")))?;
tracing::info!("Server listening on {}", addr);
axum::serve(listener, app)
.await
.map_err(|e| crate::error::CollabError::Internal(format!("Server error: {e}")))?;
Ok(())
}
#[must_use]
pub fn auth(&self) -> Arc<AuthService> {
self.auth.clone()
}
#[must_use]
pub fn workspace(&self) -> Arc<WorkspaceService> {
self.workspace.clone()
}
#[must_use]
pub fn sync(&self) -> Arc<SyncEngine> {
self.sync.clone()
}
#[must_use]
pub fn history(&self) -> Arc<History> {
self.history.clone()
}
}