use crate::cli::args::{
DaemonCommand, DaemonInstallArgs, DaemonLogsArgs, DaemonStartArgs, GlobalArgs, ServiceType,
};
use crate::cli::output::OutputWriter;
use crate::daemon::process::{
generate_launchd_plist, generate_systemd_service, get_daemon_status, read_daemon_logs,
restart_daemon, start_daemon, stop_daemon,
};
use crate::daemon::state::DaemonState;
use crate::error::{ExitStatus, Result};
pub async fn handle_daemon(cmd: DaemonCommand, global: &GlobalArgs) -> Result<ExitStatus> {
match cmd {
DaemonCommand::Start(args) => handle_start(args, global).await,
DaemonCommand::Stop => handle_stop(global).await,
DaemonCommand::Restart => handle_restart(global).await,
DaemonCommand::Status => handle_status(global).await,
DaemonCommand::Logs(args) => handle_logs(args, global).await,
DaemonCommand::Install(args) => handle_install(args, global).await,
DaemonCommand::Uninstall => handle_uninstall(global).await,
DaemonCommand::Run => handle_run(global).await,
}
}
async fn handle_start(args: DaemonStartArgs, global: &GlobalArgs) -> Result<ExitStatus> {
let info = start_daemon(args.mcp, args.sync, args.port)?;
let writer = OutputWriter::new(global);
writer.write_value(&info)?;
if !global.quiet {
eprintln!("Daemon started with PID {}", info.pid.unwrap_or(0));
eprintln!("Log file: {}", DaemonState::log_file_path()?.display());
}
Ok(ExitStatus::Success)
}
async fn handle_stop(global: &GlobalArgs) -> Result<ExitStatus> {
let info = stop_daemon()?;
let writer = OutputWriter::new(global);
writer.write_value(&info)?;
if !global.quiet {
eprintln!("Daemon stopped");
}
Ok(ExitStatus::Success)
}
async fn handle_restart(global: &GlobalArgs) -> Result<ExitStatus> {
let info = restart_daemon()?;
let writer = OutputWriter::new(global);
writer.write_value(&info)?;
if !global.quiet {
eprintln!("Daemon restarted with PID {}", info.pid.unwrap_or(0));
}
Ok(ExitStatus::Success)
}
async fn handle_status(global: &GlobalArgs) -> Result<ExitStatus> {
let info = get_daemon_status()?;
let writer = OutputWriter::new(global);
writer.write_value(&info)?;
if !global.quiet {
if info.running {
eprintln!("Daemon is running");
eprintln!(" PID: {}", info.pid.unwrap_or(0));
if let Some(uptime) = info.uptime_secs {
eprintln!(" Uptime: {}s", uptime);
}
if let Some(ref last) = info.last_sync {
eprintln!(" Last sync: {}", last);
}
if let Some(ref next) = info.next_sync {
eprintln!(" Next sync: {}", next);
}
eprintln!(" Total syncs: {}", info.sync_status.total_syncs);
if info.mcp_enabled {
eprintln!(" MCP server: enabled (port {})", info.mcp_port);
}
} else {
eprintln!("Daemon is not running");
}
}
Ok(ExitStatus::Success)
}
async fn handle_logs(args: DaemonLogsArgs, global: &GlobalArgs) -> Result<ExitStatus> {
let logs = read_daemon_logs(args.tail, args.follow)?;
if global.effective_output_format() == crate::cli::args::OutputFormat::Json {
let writer = OutputWriter::new(global);
writer.write_value(&serde_json::json!({ "logs": logs }))?;
} else {
println!("{}", logs);
}
Ok(ExitStatus::Success)
}
async fn handle_install(args: DaemonInstallArgs, global: &GlobalArgs) -> Result<ExitStatus> {
let service_type = args.service_type.unwrap_or_else(|| {
#[cfg(target_os = "macos")]
{
ServiceType::Launchd
}
#[cfg(not(target_os = "macos"))]
{
ServiceType::Systemd
}
});
let (content, path, instructions) = match service_type {
ServiceType::Systemd => {
let content = generate_systemd_service();
let path = "/etc/systemd/system/gdelt.service";
let instructions = format!(
r#"To install the systemd service:
1. Save the service file:
sudo tee {} > /dev/null << 'EOF'
{}EOF
2. Enable and start the service:
sudo systemctl daemon-reload
sudo systemctl enable gdelt
sudo systemctl start gdelt
3. Check status:
sudo systemctl status gdelt
"#,
path, content
);
(content, path, instructions)
}
ServiceType::Launchd => {
let content = generate_launchd_plist();
let path = "~/Library/LaunchAgents/com.gdelt.daemon.plist";
let instructions = format!(
r#"To install the launchd service:
1. Save the plist file:
cat > {} << 'EOF'
{}EOF
2. Load the service:
launchctl load {}
3. Check status:
launchctl list | grep gdelt
"#,
path, content, path
);
(content, path, instructions)
}
};
if global.effective_output_format() == crate::cli::args::OutputFormat::Json {
let writer = OutputWriter::new(global);
writer.write_value(&serde_json::json!({
"service_type": format!("{:?}", service_type),
"path": path,
"content": content,
}))?;
} else {
println!("{}", instructions);
}
Ok(ExitStatus::Success)
}
async fn handle_uninstall(global: &GlobalArgs) -> Result<ExitStatus> {
let instructions = r#"To uninstall the daemon service:
For systemd:
sudo systemctl stop gdelt
sudo systemctl disable gdelt
sudo rm /etc/systemd/system/gdelt.service
sudo systemctl daemon-reload
For launchd:
launchctl unload ~/Library/LaunchAgents/com.gdelt.daemon.plist
rm ~/Library/LaunchAgents/com.gdelt.daemon.plist
"#;
if global.effective_output_format() == crate::cli::args::OutputFormat::Json {
let writer = OutputWriter::new(global);
writer.write_value(&serde_json::json!({
"instructions": instructions,
}))?;
} else {
println!("{}", instructions);
}
Ok(ExitStatus::Success)
}
async fn handle_run(global: &GlobalArgs) -> Result<ExitStatus> {
use crate::daemon::scheduler::{ScheduledTask, Scheduler};
use crate::daemon::state::DaemonState;
use crate::daemon::sync::run_sync;
use tokio::time::{interval, Duration};
if !global.quiet {
eprintln!("Starting GDELT daemon in foreground mode");
}
let mut state = DaemonState::load()?;
state.mark_started(std::process::id());
state.save()?;
let mut scheduler = Scheduler::new();
scheduler.add_task(ScheduledTask::interval("sync", state.sync_interval_secs));
let mut check_interval = interval(Duration::from_secs(60));
loop {
check_interval.tick().await;
let due_task_names: Vec<String> = scheduler
.due_tasks()
.iter()
.map(|t| t.name.clone())
.collect();
for task_name in due_task_names {
match task_name.as_str() {
"sync" => {
eprintln!("Running scheduled sync...");
match run_sync().await {
Ok(result) => {
let mut state = DaemonState::load()?;
state.record_sync_success(
result.files_downloaded,
result.events_imported,
result.gkg_imported,
);
state.save()?;
eprintln!(
"Sync complete: {} files, {} events",
result.files_downloaded, result.events_imported
);
}
Err(e) => {
let mut state = DaemonState::load()?;
state.record_sync_failure(&e.to_string());
state.save()?;
eprintln!("Sync failed: {}", e);
}
}
scheduler.mark_task_run("sync");
}
_ => {}
}
}
let state = DaemonState::load()?;
if !state.running {
eprintln!("Daemon stop requested, shutting down...");
break;
}
}
let mut state = DaemonState::load()?;
state.mark_stopped();
state.save()?;
Ok(ExitStatus::Success)
}