1use std::sync::Arc;
4
5use models::{
6 CourseOrExamId,
7 page_history::PageHistory,
8 pages::{HistoryRestoreData, NewPage, Page, PageDetailsUpdate, PageInfo},
9};
10use utoipa::OpenApi;
11
12use crate::{
13 domain::{
14 models_requests::{self, JwtKey},
15 request_id::RequestId,
16 },
17 prelude::*,
18};
19
20#[derive(OpenApi)]
21#[openapi(paths(
22 post_new_page,
23 delete_page,
24 get_page_info,
25 update_page_details,
26 history,
27 history_count,
28 restore,
29 get_all_pages_by_course_id
30))]
31pub(crate) struct MainFrontendPagesApiDoc;
32
33#[instrument(skip(pool, app_conf))]
63#[utoipa::path(
64 post,
65 path = "",
66 operation_id = "createPage",
67 tag = "pages",
68 request_body = NewPage,
69 responses(
70 (status = 200, description = "Created page", body = Page)
71 )
72)]
73async fn post_new_page(
74 request_id: RequestId,
75 payload: web::Json<NewPage>,
76 pool: web::Data<PgPool>,
77 app_conf: web::Data<ApplicationConfiguration>,
78 user: AuthUser,
79 jwt_key: web::Data<JwtKey>,
80) -> ControllerResult<web::Json<Page>> {
81 let mut conn = pool.acquire().await?;
82 let new_page = payload.0;
83 let course_id = new_page.course_id.ok_or_else(|| {
84 ControllerError::new(
85 ControllerErrorType::BadRequest,
86 "Cannot create a new page without a course id".to_string(),
87 None,
88 )
89 })?;
90 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(course_id)).await?;
91 domain::authorization::ensure_user_can_add_exercise_tasks(
92 &mut conn,
93 user.id,
94 None,
95 &new_page.exercise_tasks,
96 )
97 .await?;
98
99 let page = models::pages::create_for_course_id(
100 &mut conn,
101 course_id,
102 new_page,
103 user.id,
104 models_requests::make_spec_fetcher(
105 app_conf.base_url.clone(),
106 request_id.0,
107 Arc::clone(&jwt_key),
108 ),
109 models_requests::fetch_service_info,
110 )
111 .await?;
112 token.authorized_ok(web::Json(page))
113}
114
115#[instrument(skip(pool))]
124#[utoipa::path(
125 delete,
126 path = "/{page_id}",
127 operation_id = "deletePage",
128 tag = "pages",
129 params(
130 ("page_id" = Uuid, Path, description = "Page id")
131 ),
132 responses(
133 (status = 200, description = "Deleted page", body = Page)
134 )
135)]
136async fn delete_page(
137 page_id: web::Path<Uuid>,
138 pool: web::Data<PgPool>,
139 user: AuthUser,
140) -> ControllerResult<web::Json<Page>> {
141 let mut conn = pool.acquire().await?;
142 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Page(*page_id)).await?;
143 let deleted_page = models::pages::delete_page_and_exercises(
144 &mut conn,
145 *page_id,
146 models::entity_history::HistoryAuthor::user(user.id),
147 )
148 .await?;
149
150 token.authorized_ok(web::Json(deleted_page))
151}
152
153#[instrument(skip(pool))]
157#[utoipa::path(
158 get,
159 path = "/{page_id}/history",
160 operation_id = "getPageHistory",
161 tag = "pages",
162 params(
163 ("page_id" = Uuid, Path, description = "Page id"),
164 ("page" = Option<i64>, Query, description = "Page number"),
165 ("limit" = Option<i64>, Query, description = "Page size")
166 ),
167 responses(
168 (status = 200, description = "Page history entries", body = Vec<PageHistory>)
169 )
170)]
171async fn history(
172 pool: web::Data<PgPool>,
173 page_id: web::Path<Uuid>,
174 pagination: web::Query<Pagination>,
175 user: AuthUser,
176) -> ControllerResult<web::Json<Vec<PageHistory>>> {
177 let mut conn = pool.acquire().await?;
178 let token = authorize(&mut conn, Act::Teach, Some(user.id), Res::Page(*page_id)).await?;
179
180 let res = models::page_history::history(&mut conn, *page_id, *pagination).await?;
181
182 token.authorized_ok(web::Json(res))
183}
184
185#[instrument(skip(pool))]
189#[utoipa::path(
190 get,
191 path = "/{page_id}/history_count",
192 operation_id = "getPageHistoryCount",
193 tag = "pages",
194 params(
195 ("page_id" = Uuid, Path, description = "Page id")
196 ),
197 responses(
198 (status = 200, description = "Page history count", body = i64)
199 )
200)]
201async fn history_count(
202 pool: web::Data<PgPool>,
203 page_id: web::Path<Uuid>,
204 user: AuthUser,
205) -> ControllerResult<web::Json<i64>> {
206 let mut conn = pool.acquire().await?;
207 let token = authorize(&mut conn, Act::Teach, Some(user.id), Res::Page(*page_id)).await?;
208 let res = models::page_history::history_count(&mut conn, *page_id).await?;
209
210 token.authorized_ok(web::Json(res))
211}
212
213#[instrument(skip(pool, app_conf))]
218#[utoipa::path(
219 post,
220 path = "/{page_id}/restore",
221 operation_id = "restorePageHistory",
222 tag = "pages",
223 params(
224 ("page_id" = Uuid, Path, description = "Page id")
225 ),
226 request_body = HistoryRestoreData,
227 responses(
228 (status = 200, description = "Restored history id", body = Uuid)
229 )
230)]
231async fn restore(
232 request_id: RequestId,
233 pool: web::Data<PgPool>,
234 page_id: web::Path<Uuid>,
235 restore_data: web::Json<HistoryRestoreData>,
236 app_conf: web::Data<ApplicationConfiguration>,
237 user: AuthUser,
238 jwt_key: web::Data<JwtKey>,
239) -> ControllerResult<web::Json<Uuid>> {
240 let mut conn = pool.acquire().await?;
241 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Page(*page_id)).await?;
242 let page = models::pages::get_page(&mut conn, *page_id).await?;
243 let (expected_course_id, expected_exam_id) =
244 match CourseOrExamId::from_course_and_exam_ids(page.course_id, page.exam_id)? {
245 CourseOrExamId::Course(course_id) => (Some(course_id), None),
246 CourseOrExamId::Exam(exam_id) => (None, Some(exam_id)),
247 };
248 let res = models::pages::restore_from_history_for_page_id(
249 &mut conn,
250 *page_id,
251 restore_data.history_id,
252 None,
253 expected_course_id,
254 expected_exam_id,
255 user.id,
256 models_requests::make_spec_fetcher(
257 app_conf.base_url.clone(),
258 request_id.0,
259 Arc::clone(&jwt_key),
260 ),
261 models_requests::fetch_service_info,
262 )
263 .await?;
264
265 token.authorized_ok(web::Json(res))
266}
267
268#[utoipa::path(
274 get,
275 path = "/{page_id}/info",
276 operation_id = "getPageInfo",
277 tag = "pages",
278 params(
279 ("page_id" = Uuid, Path, description = "Page id")
280 ),
281 responses(
282 (status = 200, description = "Page info", body = PageInfo)
283 )
284)]
285async fn get_page_info(
286 page_id: web::Path<Uuid>,
287 pool: web::Data<PgPool>,
288 user: AuthUser,
289) -> ControllerResult<web::Json<PageInfo>> {
290 let mut conn = pool.acquire().await?;
291 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Page(*page_id)).await?;
292
293 let page_info = models::pages::get_page_info(&mut conn, *page_id).await?;
294
295 token.authorized_ok(web::Json(page_info))
296}
297
298#[instrument(skip(pool))]
302#[utoipa::path(
303 put,
304 path = "/{page_id}/page-details",
305 operation_id = "updatePageDetails",
306 tag = "pages",
307 params(
308 ("page_id" = Uuid, Path, description = "Page id")
309 ),
310 request_body = PageDetailsUpdate,
311 responses(
312 (status = 200, description = "Updated page details", body = bool)
313 )
314)]
315async fn update_page_details(
316 page_id: web::Path<Uuid>,
317 payload: web::Json<PageDetailsUpdate>,
318 pool: web::Data<PgPool>,
319 user: AuthUser,
320) -> ControllerResult<web::Json<bool>> {
321 let mut conn = pool.acquire().await?;
322
323 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Page(*page_id)).await?;
324
325 models::pages::update_page_details(&mut conn, *page_id, &payload).await?;
326 token.authorized_ok(web::Json(true))
327}
328
329#[utoipa::path(
333 get,
334 path = "/{course_id}/all-course-pages-for-course",
335 operation_id = "getCoursePages",
336 tag = "pages",
337 params(
338 ("course_id" = Uuid, Path, description = "Course id")
339 ),
340 responses(
341 (status = 200, description = "Course pages", body = Vec<Page>)
342 )
343)]
344async fn get_all_pages_by_course_id(
345 course_id: web::Path<Uuid>,
346 pool: web::Data<PgPool>,
347 user: AuthUser,
348) -> ControllerResult<web::Json<Vec<Page>>> {
349 let mut conn = pool.acquire().await?;
350 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(*course_id)).await?;
351
352 let mut pages = models::pages::get_pages_by_course_id(&mut conn, *course_id).await?;
353
354 pages.sort_by_key(|a| a.order_number);
355
356 token.authorized_ok(web::Json(pages))
357}
358
359pub fn _add_routes(cfg: &mut ServiceConfig) {
367 cfg.route("", web::post().to(post_new_page))
368 .route("/{page_id}", web::delete().to(delete_page))
369 .route("/{page_id}/info", web::get().to(get_page_info))
370 .route(
371 "/{page_id}/page-details",
372 web::put().to(update_page_details),
373 )
374 .route("/{page_id}/history", web::get().to(history))
375 .route("/{page_id}/history_count", web::get().to(history_count))
376 .route("/{page_id}/restore", web::post().to(restore))
377 .route(
378 "/{course_id}/all-course-pages-for-course",
379 web::get().to(get_all_pages_by_course_id),
380 );
381}