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 ..Default::default()
101 },
102 )
103 .await?;
104 tx.commit().await?;
105
106 token.authorized_ok(web::Json(configuration))
107}
108
109#[utoipa::path(
111 post,
112 path = "/{chatbot_configuration_id}/set-as-default",
113 operation_id = "setCourseChatbotAsDefault",
114 tag = "courses",
115 params(
116 ("course_id" = String, Path, description = "Course id"),
117 ("chatbot_configuration_id" = String, Path, description = "Chatbot configuration id")
118 ),
119 responses(
120 (status = 200, description = "Updated course chatbot", body = ChatbotConfiguration)
121 )
122)]
123#[instrument(skip(pool))]
124async fn set_default_chatbot(
125 ids: web::Path<(Uuid, Uuid)>,
126 pool: web::Data<PgPool>,
127 user: AuthUser,
128) -> ControllerResult<web::Json<ChatbotConfiguration>> {
129 let mut conn = pool.acquire().await?;
130 let (course_id, chatbot_configuration_id) = *ids;
131
132 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(course_id)).await?;
133 let mut tx = conn.begin().await?;
134
135 models::chatbot_configurations::remove_default_chatbot_from_course(&mut tx, course_id).await?;
136
137 let chatbot =
138 models::chatbot_configurations::get_by_id(&mut tx, chatbot_configuration_id).await?;
139
140 if course_id != chatbot.course_id {
141 return Err(ControllerError::new(
142 ControllerErrorType::BadRequest,
143 "Chatbot course id doesn't match the course id provided.".to_string(),
144 None,
145 ));
146 }
147
148 let configuration = models::chatbot_configurations::set_default_chatbot_for_course(
149 &mut tx,
150 chatbot_configuration_id,
151 )
152 .await?;
153 tx.commit().await?;
154
155 token.authorized_ok(web::Json(configuration))
156}
157
158#[utoipa::path(
160 post,
161 path = "/{chatbot_configuration_id}/set-as-non-default",
162 operation_id = "setCourseChatbotAsNonDefault",
163 tag = "courses",
164 params(
165 ("course_id" = String, Path, description = "Course id"),
166 ("chatbot_configuration_id" = String, Path, description = "Chatbot configuration id")
167 ),
168 responses(
169 (status = 200, description = "Updated course chatbot", body = ChatbotConfiguration)
170 )
171)]
172#[instrument(skip(pool))]
173async fn set_non_default_chatbot(
174 ids: web::Path<(Uuid, Uuid)>,
175 pool: web::Data<PgPool>,
176 user: AuthUser,
177) -> ControllerResult<web::Json<ChatbotConfiguration>> {
178 let mut conn = pool.acquire().await?;
179 let (course_id, chatbot_configuration_id) = *ids;
180
181 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(course_id)).await?;
182 let mut tx = conn.begin().await?;
183
184 models::chatbot_configurations::remove_default_chatbot_from_course(&mut tx, course_id).await?;
185
186 let configuration =
187 models::chatbot_configurations::get_by_id(&mut tx, chatbot_configuration_id).await?;
188
189 if course_id != configuration.course_id {
190 return Err(ControllerError::new(
191 ControllerErrorType::BadRequest,
192 "Chatbot course id doesn't match the course id provided.".to_string(),
193 None,
194 ));
195 }
196 tx.commit().await?;
197
198 token.authorized_ok(web::Json(configuration))
199}
200
201pub fn _add_routes(cfg: &mut web::ServiceConfig) {
202 cfg.route("", web::get().to(get_chatbots))
203 .route("", web::post().to(create_chatbot))
204 .route(
205 "/{chatbot_configuration_id}/set-as-default",
206 web::post().to(set_default_chatbot),
207 )
208 .route(
209 "/{chatbot_configuration_id}/set-as-non-default",
210 web::post().to(set_non_default_chatbot),
211 );
212}