headless_lms_server/controllers/main_frontend/courses/
chatbots.rs1use crate::prelude::*;
3
4use headless_lms_models::chatbot_configurations::NewChatbotConf;
5use models::chatbot_configurations::ChatbotConfiguration;
6use utoipa::OpenApi;
7
8#[derive(OpenApi)]
9#[openapi(paths(
10 get_chatbots,
11 create_chatbot,
12 set_default_chatbot,
13 set_non_default_chatbot
14))]
15pub(crate) struct MainFrontendCourseChatbotsApiDoc;
16
17#[utoipa::path(
19 get,
20 path = "",
21 operation_id = "getCourseChatbots",
22 tag = "courses",
23 params(
24 ("course_id" = String, Path, description = "Course id")
25 ),
26 responses(
27 (status = 200, description = "Course chatbots", body = Vec<ChatbotConfiguration>)
28 )
29)]
30#[instrument(skip(pool))]
31async fn get_chatbots(
32 course_id: web::Path<Uuid>,
33 pool: web::Data<PgPool>,
34 user: AuthUser,
35) -> ControllerResult<web::Json<Vec<ChatbotConfiguration>>> {
36 let mut conn = pool.acquire().await?;
37 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(*course_id)).await?;
38
39 let configurations =
40 models::chatbot_configurations::get_for_course(&mut conn, *course_id).await?;
41 token.authorized_ok(web::Json(configurations))
42}
43
44#[utoipa::path(
46 post,
47 path = "",
48 operation_id = "createCourseChatbot",
49 tag = "courses",
50 params(
51 ("course_id" = String, Path, description = "Course id")
52 ),
53 request_body(
54 content = String,
55 description = "JSON string literal chatbot name, e.g. \"Chatbot 1\".",
56 content_type = "application/json"
57 ),
58 responses(
59 (status = 200, description = "Created course chatbot", body = ChatbotConfiguration)
60 )
61)]
62#[instrument(skip(pool, payload))]
63async fn create_chatbot(
64 course_id: web::Path<Uuid>,
65 payload: web::Json<String>,
66 pool: web::Data<PgPool>,
67 user: AuthUser,
68) -> ControllerResult<web::Json<ChatbotConfiguration>> {
69 let mut conn = pool.acquire().await?;
70 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(*course_id)).await?;
71 let mut tx = conn.begin().await?;
72
73 let course = models::courses::get_course(&mut tx, *course_id).await?;
74
75 if !course.can_add_chatbot {
76 return Err(ControllerError::new(
77 ControllerErrorType::BadRequest,
78 "Course doesn't allow creating chatbots.".to_string(),
79 None,
80 ));
81 }
82
83 let model = models::chatbot_configurations_models::get_default(&mut tx)
84 .await
85 .map_err(|e| {
86 ControllerError::new(
87 ControllerErrorType::BadRequest,
88 "No default chatbot model configured. Ask an admin to set one.".to_string(),
89 Some(e.into()),
90 )
91 })?;
92
93 let configuration = models::chatbot_configurations::insert(
94 &mut tx,
95 PKeyPolicy::Generate,
96 NewChatbotConf {
97 chatbot_name: payload.into_inner(),
98 course_id: *course_id,
99 model_id: model.id,
100 thinking_model: model.thinking,
101 ..Default::default()
102 },
103 )
104 .await?;
105 tx.commit().await?;
106
107 token.authorized_ok(web::Json(configuration))
108}
109
110#[utoipa::path(
112 post,
113 path = "/{chatbot_configuration_id}/set-as-default",
114 operation_id = "setCourseChatbotAsDefault",
115 tag = "courses",
116 params(
117 ("course_id" = String, Path, description = "Course id"),
118 ("chatbot_configuration_id" = String, Path, description = "Chatbot configuration id")
119 ),
120 responses(
121 (status = 200, description = "Updated course chatbot", body = ChatbotConfiguration)
122 )
123)]
124#[instrument(skip(pool))]
125async fn set_default_chatbot(
126 ids: web::Path<(Uuid, Uuid)>,
127 pool: web::Data<PgPool>,
128 user: AuthUser,
129) -> ControllerResult<web::Json<ChatbotConfiguration>> {
130 let mut conn = pool.acquire().await?;
131 let (course_id, chatbot_configuration_id) = *ids;
132
133 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(course_id)).await?;
134 let mut tx = conn.begin().await?;
135
136 models::chatbot_configurations::remove_default_chatbot_from_course(&mut tx, course_id).await?;
137
138 let chatbot =
139 models::chatbot_configurations::get_by_id(&mut tx, chatbot_configuration_id).await?;
140
141 if course_id != chatbot.course_id {
142 return Err(ControllerError::new(
143 ControllerErrorType::BadRequest,
144 "Chatbot course id doesn't match the course id provided.".to_string(),
145 None,
146 ));
147 }
148
149 let configuration = models::chatbot_configurations::set_default_chatbot_for_course(
150 &mut tx,
151 chatbot_configuration_id,
152 )
153 .await?;
154 tx.commit().await?;
155
156 token.authorized_ok(web::Json(configuration))
157}
158
159#[utoipa::path(
161 post,
162 path = "/{chatbot_configuration_id}/set-as-non-default",
163 operation_id = "setCourseChatbotAsNonDefault",
164 tag = "courses",
165 params(
166 ("course_id" = String, Path, description = "Course id"),
167 ("chatbot_configuration_id" = String, Path, description = "Chatbot configuration id")
168 ),
169 responses(
170 (status = 200, description = "Updated course chatbot", body = ChatbotConfiguration)
171 )
172)]
173#[instrument(skip(pool))]
174async fn set_non_default_chatbot(
175 ids: web::Path<(Uuid, Uuid)>,
176 pool: web::Data<PgPool>,
177 user: AuthUser,
178) -> ControllerResult<web::Json<ChatbotConfiguration>> {
179 let mut conn = pool.acquire().await?;
180 let (course_id, chatbot_configuration_id) = *ids;
181
182 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(course_id)).await?;
183 let mut tx = conn.begin().await?;
184
185 models::chatbot_configurations::remove_default_chatbot_from_course(&mut tx, course_id).await?;
186
187 let configuration =
188 models::chatbot_configurations::get_by_id(&mut tx, chatbot_configuration_id).await?;
189
190 if course_id != configuration.course_id {
191 return Err(ControllerError::new(
192 ControllerErrorType::BadRequest,
193 "Chatbot course id doesn't match the course id provided.".to_string(),
194 None,
195 ));
196 }
197 tx.commit().await?;
198
199 token.authorized_ok(web::Json(configuration))
200}
201
202pub fn _add_routes(cfg: &mut web::ServiceConfig) {
203 cfg.route("", web::get().to(get_chatbots))
204 .route("", web::post().to(create_chatbot))
205 .route(
206 "/{chatbot_configuration_id}/set-as-default",
207 web::post().to(set_default_chatbot),
208 )
209 .route(
210 "/{chatbot_configuration_id}/set-as-non-default",
211 web::post().to(set_non_default_chatbot),
212 );
213}