gbserver/src/services/bot/bot.md

6.7 KiB

        // .service(create_bot)
        // .service(get_bot)
        // .service(list_bots)
        // .service(update_bot)
        // .service(delete_bot)
        // .service(update_bot_status)
        // .service(execute_bot_command)

use crate::services::{config::BotConfig, state::AppState}; use actix_web::{ delete, get, post, put, web::{self, Data, Json, Path}, HttpResponse, Responder, Result, }; use chrono::Utc; use serde::{Deserialize, Serialize}; use sqlx::{postgres::PgQueryResult, FromRow, PgPool}; use uuid::Uuid;

// 1. Core Data Structures

// 2. Request/Response DTOs #[derive(Debug, Serialize, Deserialize)] pub struct CreateBotRequest { pub name: String, pub initial_config: Option<serde_json::Value>, }

#[derive(Debug, Serialize, Deserialize)] pub struct UpdateBotRequest { pub name: Option, pub status: Option, pub config: Option<serde_json::Value>, }

#[derive(Debug, Serialize, Deserialize)] pub struct BotResponse { pub bot_id: Uuid, pub name: String, pub status: BotStatus, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, }

// 3. Helper Functions impl From for BotResponse { fn from(bot: Bot) -> Self { BotResponse { bot_id: bot.bot_id, name: bot.name, status: bot.status, created_at: bot.created_at, updated_at: bot.updated_at, } } }

async fn find_bot(bot_id: Uuid, pool: &PgPool) -> Result<Bot, sqlx::Error> { sqlx::query_as::<_, Bot>("SELECT * FROM bots WHERE bot_id = $1") .bind(bot_id) .fetch_one(pool) .await }

// 4. API Endpoints #[post("/bots/create")] pub async fn create_bot( payload: Json, state: Data, ) -> Result<impl Responder, actix_web::Error> { let new_bot = sqlx::query_as::<_, Bot>( r#" INSERT INTO bots (name, status, config) VALUES ($1, 'active', $2) RETURNING * "#, ) .bind(&payload.name) .bind(&payload.initial_config) .fetch_one(&state.db) .await .map_err(|e| { log::error!("Failed to create bot: {}", e); actix_web::error::ErrorInternalServerError("Failed to create bot") })?;

Ok(HttpResponse::Created().json(BotResponse::from(new_bot)))

}

#[get("/bots/{bot_id}")] pub async fn get_bot( path: Path, state: Data, ) -> Result<impl Responder, actix_web::Error> { let bot_id = path.into_inner(); let bot = find_bot(bot_id, &state.db).await.map_err(|e| match e { sqlx::Error::RowNotFound => actix_web::error::ErrorNotFound("Bot not found"), _ => { log::error!("Failed to fetch bot: {}", e); actix_web::error::ErrorInternalServerError("Failed to fetch bot") } })?;

Ok(HttpResponse::Ok().json(BotResponse::from(bot)))

}

#[get("/bots")] pub async fn list_bots(state: Data) -> Result<impl Responder, actix_web::Error> { let bots = sqlx::query_as::<_, Bot>("SELECT * FROM bots ORDER BY created_at DESC") .fetch_all(&state.db) .await .map_err(|e| { log::error!("Failed to list bots: {}", e); actix_web::error::ErrorInternalServerError("Failed to list bots") })?;

let responses: Vec<BotResponse> = bots.into_iter().map(BotResponse::from).collect();
Ok(HttpResponse::Ok().json(responses))

}

#[put("/bots/{bot_id}")] pub async fn update_bot( path: Path, payload: Json, state: Data, ) -> Result<impl Responder, actix_web::Error> { let bot_id = path.into_inner();

let updated_bot = sqlx::query_as::<_, Bot>(
    r#"
    UPDATE bots
    SET
        name = COALESCE($1, name),
        status = COALESCE($2, status),
        config = COALESCE($3, config),
        updated_at = NOW()
    WHERE bot_id = $4
    RETURNING *
    "#,
)
.bind(&payload.name)
.bind(&payload.status)
.bind(&payload.config)
.bind(bot_id)
.fetch_one(&state.db)
.await
.map_err(|e| match e {
    sqlx::Error::RowNotFound => actix_web::error::ErrorNotFound("Bot not found"),
    _ => {
        log::error!("Failed to update bot: {}", e);
        actix_web::error::ErrorInternalServerError("Failed to update bot")
    }
})?;

Ok(HttpResponse::Ok().json(BotResponse::from(updated_bot)))

}

#[delete("/bots/{bot_id}")] pub async fn delete_bot( path: Path, state: Data, ) -> Result<impl Responder, actix_web::Error> { let bot_id = path.into_inner();

let result = sqlx::query("DELETE FROM bots WHERE bot_id = $1")
    .bind(bot_id)
    .execute(&state.db)
    .await
    .map_err(|e| {
        log::error!("Failed to delete bot: {}", e);
        actix_web::error::ErrorInternalServerError("Failed to delete bot")
    })?;

if result.rows_affected() == 0 {
    return Err(actix_web::error::ErrorNotFound("Bot not found"));
}

Ok(HttpResponse::NoContent().finish())

}

#[put("/bots/{bot_id}/status")] pub async fn update_bot_status( path: Path, new_status: Json, state: Data, ) -> Result<impl Responder, actix_web::Error> { let bot_id = path.into_inner();

let updated_bot = sqlx::query_as::<_, Bot>(
    "UPDATE bots SET status = $1, updated_at = NOW() WHERE bot_id = $2 RETURNING *",
)
.bind(new_status.into_inner())
.bind(bot_id)
.fetch_one(&state.db)
.await
.map_err(|e| match e {
    sqlx::Error::RowNotFound => actix_web::error::ErrorNotFound("Bot not found"),
    _ => {
        log::error!("Failed to update bot status: {}", e);
        actix_web::error::ErrorInternalServerError("Failed to update bot status")
    }
})?;

Ok(HttpResponse::Ok().json(BotResponse::from(updated_bot)))

}

#[post("/bots/{bot_id}/execute")] pub async fn execute_bot_command( path: Path, command: Json<serde_json::Value>, state: Data, ) -> Result<impl Responder, actix_web::Error> { let bot_id = path.into_inner();

// Verify bot exists
let _ = find_bot(bot_id, &state.db).await.map_err(|e| match e {
    sqlx::Error::RowNotFound => actix_web::error::ErrorNotFound("Bot not found"),
    _ => {
        log::error!("Failed to fetch bot: {}", e);
        actix_web::error::ErrorInternalServerError("Failed to fetch bot")
    }
})?;

// Here you would implement your bot execution logic
// For now, we'll just echo back the command
Ok(HttpResponse::Ok().json(json!({
    "bot_id": bot_id,
    "command": command,
    "result": "Command executed successfully (simulated)"
})))

}