use crate::prelude::*;
use tokio::sync::{broadcast, RwLock};
use super::home_dir;
use super::github::GitHubClient;
use super::handlers::fleet::RepoDetailedStatus;
use super::ops::OpsRegistry;
use super::sync::SyncHandle;
use crate::persist::global::{DatabaseService, WsbRepos};
use crate::logging::{LogLine, LogRingBuffer};
#[derive(Clone, Debug, serde::Serialize)]
pub struct FileChangedEvent {
pub path: String,
pub mtime_secs: Option<u64>,
}
#[derive(Clone)]
pub struct WebState {
pub db: Arc<WsbRepos>,
pub db_service: Arc<DatabaseService>,
pub sync: SyncHandle,
pub github: Arc<RwLock<Option<GitHubClient>>>,
pub sync_progress_tx: broadcast::Sender<SyncProgress>,
pub log_tx: broadcast::Sender<LogLine>,
pub log_ring: LogRingBuffer,
pub workspace_root: PathBuf,
pub debug: bool,
pub port: u16,
pub git_status_tx: broadcast::Sender<RepoDetailedStatus>,
pub git_status_snapshot: Arc<RwLock<HashMap<String, RepoDetailedStatus>>>,
pub git_focus_tx: broadcast::Sender<String>,
pub git_nudge_tx: broadcast::Sender<Option<String>>,
pub ops: OpsRegistry,
pub file_changed_tx: broadcast::Sender<FileChangedEvent>,
pub workspace_changed_tx: broadcast::Sender<Vec<String>>,
}
impl WebState {
pub async fn new(
db: Arc<WsbRepos>,
db_service: Arc<DatabaseService>,
workspace_root: PathBuf,
debug: bool,
port: u16,
log_tx: broadcast::Sender<LogLine>,
log_ring: LogRingBuffer,
) -> Result<Self> {
let sync = super::sync::spawn_sync_actor(db.clone());
let (sync_progress_tx, _) = broadcast::channel(256);
let github = match load_github_token() {
Some(token) => Some(GitHubClient::new(&token)),
None => None,
};
let (git_status_tx, _) = broadcast::channel(64);
let (git_focus_tx, _) = broadcast::channel(16);
let (git_nudge_tx, _) = broadcast::channel::<Option<String>>(64);
let (file_changed_tx, _) = broadcast::channel(256);
let (workspace_changed_tx, _) = broadcast::channel::<Vec<String>>(64);
Ok(Self {
db,
db_service,
sync,
github: Arc::new(RwLock::new(github)),
sync_progress_tx,
log_tx,
log_ring,
workspace_root,
debug,
port,
git_status_tx,
git_status_snapshot: Arc::new(RwLock::new(HashMap::new())),
git_focus_tx,
git_nudge_tx,
ops: OpsRegistry::new(),
file_changed_tx,
workspace_changed_tx,
})
}
}
#[derive(Clone, Debug, serde::Serialize)]
pub struct SyncProgress {
pub phase: SyncPhase,
#[serde(skip_serializing_if = "Option::is_none")]
pub current_project: Option<String>,
pub sessions_total: usize,
pub sessions_done: usize,
pub messages_indexed: usize,
}
#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum SyncPhase {
Idle,
Discovering,
Syncing,
Done,
}
pub fn load_github_token_exists() -> bool {
load_github_token().is_some()
}
pub fn load_github_token() -> Option<String> {
let config_path = home_dir().ok()?.join(".wsb").join("config.toml");
let content = std::fs::read_to_string(&config_path).ok()?;
let table: toml::Table = content.parse().ok()?;
let github = table.get("github")?.as_table()?;
let token = github.get("token")?.as_str()?;
if token.is_empty() {
None
} else {
Some(token.to_string())
}
}