headless_lms_server/controllers/main_frontend/courses/
chatbots.rs

1//! Controllers for requests starting with `/api/v0/main-frontend/{course_id}/chatbots`.
2use crate::prelude::*;
3
4use headless_lms_models::chatbot_configurations::NewChatbotConf;
5use models::chatbot_configurations::ChatbotConfiguration;
6
7/// GET `/api/v0/main-frontend/courses/{course_id}/chatbots`
8#[instrument(skip(pool))]
9async fn get_chatbots(
10    course_id: web::Path<Uuid>,
11    pool: web::Data<PgPool>,
12    user: AuthUser,
13) -> ControllerResult<web::Json<Vec<ChatbotConfiguration>>> {
14    let mut conn = pool.acquire().await?;
15    let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(*course_id)).await?;
16
17    let configurations =
18        models::chatbot_configurations::get_for_course(&mut conn, *course_id).await?;
19    token.authorized_ok(web::Json(configurations))
20}
21
22/// POST `/api/v0/main-frontend/courses/{course_id}/chatbots`
23#[instrument(skip(pool, payload))]
24async fn create_chatbot(
25    course_id: web::Path<Uuid>,
26    payload: web::Json<String>,
27    pool: web::Data<PgPool>,
28    user: AuthUser,
29) -> ControllerResult<web::Json<ChatbotConfiguration>> {
30    let mut conn = pool.acquire().await?;
31    let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(*course_id)).await?;
32    let mut tx = conn.begin().await?;
33
34    let course = models::courses::get_course(&mut tx, *course_id).await?;
35
36    if !course.can_add_chatbot {
37        return Err(ControllerError::new(
38            ControllerErrorType::BadRequest,
39            "Course doesn't allow creating chatbots.".to_string(),
40            None,
41        ));
42    }
43
44    let model = models::chatbot_configurations_models::get_default(&mut tx)
45        .await
46        .map_err(|e| {
47            ControllerError::new(
48                ControllerErrorType::BadRequest,
49                "No default chatbot model configured. Ask an admin to set one.".to_string(),
50                Some(e.into()),
51            )
52        })?;
53
54    let configuration = models::chatbot_configurations::insert(
55        &mut tx,
56        PKeyPolicy::Generate,
57        NewChatbotConf {
58            chatbot_name: payload.into_inner(),
59            course_id: *course_id,
60            model_id: model.id,
61            thinking_model: model.thinking,
62            ..Default::default()
63        },
64    )
65    .await?;
66    tx.commit().await?;
67
68    token.authorized_ok(web::Json(configuration))
69}
70
71/// POST `/api/v0/main-frontend/courses/{course_id}/chatbots/{chatbot_configuration_id}/set-as-default`
72#[instrument(skip(pool))]
73async fn set_default_chatbot(
74    ids: web::Path<(Uuid, Uuid)>,
75    pool: web::Data<PgPool>,
76    user: AuthUser,
77) -> ControllerResult<web::Json<ChatbotConfiguration>> {
78    let mut conn = pool.acquire().await?;
79    let (course_id, chatbot_configuration_id) = *ids;
80
81    let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(course_id)).await?;
82    let mut tx = conn.begin().await?;
83
84    models::chatbot_configurations::remove_default_chatbot_from_course(&mut tx, course_id).await?;
85
86    let chatbot =
87        models::chatbot_configurations::get_by_id(&mut tx, chatbot_configuration_id).await?;
88
89    if course_id != chatbot.course_id {
90        return Err(ControllerError::new(
91            ControllerErrorType::BadRequest,
92            "Chatbot course id doesn't match the course id provided.".to_string(),
93            None,
94        ));
95    }
96
97    let configuration = models::chatbot_configurations::set_default_chatbot_for_course(
98        &mut tx,
99        chatbot_configuration_id,
100    )
101    .await?;
102    tx.commit().await?;
103
104    token.authorized_ok(web::Json(configuration))
105}
106
107/// POST `/api/v0/main-frontend/courses/{course_id}/chatbots/{chatbot_configuration_id}/set-as-non-default`
108#[instrument(skip(pool))]
109async fn set_non_default_chatbot(
110    ids: web::Path<(Uuid, Uuid)>,
111    pool: web::Data<PgPool>,
112    user: AuthUser,
113) -> ControllerResult<web::Json<ChatbotConfiguration>> {
114    let mut conn = pool.acquire().await?;
115    let (course_id, chatbot_configuration_id) = *ids;
116
117    let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(course_id)).await?;
118    let mut tx = conn.begin().await?;
119
120    models::chatbot_configurations::remove_default_chatbot_from_course(&mut tx, course_id).await?;
121
122    let configuration =
123        models::chatbot_configurations::get_by_id(&mut tx, chatbot_configuration_id).await?;
124
125    if course_id != configuration.course_id {
126        return Err(ControllerError::new(
127            ControllerErrorType::BadRequest,
128            "Chatbot course id doesn't match the course id provided.".to_string(),
129            None,
130        ));
131    }
132    tx.commit().await?;
133
134    token.authorized_ok(web::Json(configuration))
135}
136
137pub fn _add_routes(cfg: &mut web::ServiceConfig) {
138    cfg.route("", web::get().to(get_chatbots))
139        .route("", web::post().to(create_chatbot))
140        .route(
141            "/{chatbot_configuration_id}/set-as-default",
142            web::post().to(set_default_chatbot),
143        )
144        .route(
145            "/{chatbot_configuration_id}/set-as-non-default",
146            web::post().to(set_non_default_chatbot),
147        );
148}