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
92 let page = models::pages::create_for_course_id(
93 &mut conn,
94 course_id,
95 new_page,
96 user.id,
97 models_requests::make_spec_fetcher(
98 app_conf.base_url.clone(),
99 request_id.0,
100 Arc::clone(&jwt_key),
101 ),
102 models_requests::fetch_service_info,
103 )
104 .await?;
105 token.authorized_ok(web::Json(page))
106}
107
108#[instrument(skip(pool))]
117#[utoipa::path(
118 delete,
119 path = "/{page_id}",
120 operation_id = "deletePage",
121 tag = "pages",
122 params(
123 ("page_id" = Uuid, Path, description = "Page id")
124 ),
125 responses(
126 (status = 200, description = "Deleted page", body = Page)
127 )
128)]
129async fn delete_page(
130 page_id: web::Path<Uuid>,
131 pool: web::Data<PgPool>,
132 user: AuthUser,
133) -> ControllerResult<web::Json<Page>> {
134 let mut conn = pool.acquire().await?;
135 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Page(*page_id)).await?;
136 let deleted_page =
137 models::pages::delete_page_and_exercises(&mut conn, *page_id, user.id).await?;
138
139 token.authorized_ok(web::Json(deleted_page))
140}
141
142#[instrument(skip(pool))]
146#[utoipa::path(
147 get,
148 path = "/{page_id}/history",
149 operation_id = "getPageHistory",
150 tag = "pages",
151 params(
152 ("page_id" = Uuid, Path, description = "Page id"),
153 ("page" = Option<i64>, Query, description = "Page number"),
154 ("limit" = Option<i64>, Query, description = "Page size")
155 ),
156 responses(
157 (status = 200, description = "Page history entries", body = Vec<PageHistory>)
158 )
159)]
160async fn history(
161 pool: web::Data<PgPool>,
162 page_id: web::Path<Uuid>,
163 pagination: web::Query<Pagination>,
164 user: AuthUser,
165) -> ControllerResult<web::Json<Vec<PageHistory>>> {
166 let mut conn = pool.acquire().await?;
167 let token = authorize(&mut conn, Act::Teach, Some(user.id), Res::Page(*page_id)).await?;
168
169 let res = models::page_history::history(&mut conn, *page_id, *pagination).await?;
170
171 token.authorized_ok(web::Json(res))
172}
173
174#[instrument(skip(pool))]
178#[utoipa::path(
179 get,
180 path = "/{page_id}/history_count",
181 operation_id = "getPageHistoryCount",
182 tag = "pages",
183 params(
184 ("page_id" = Uuid, Path, description = "Page id")
185 ),
186 responses(
187 (status = 200, description = "Page history count", body = i64)
188 )
189)]
190async fn history_count(
191 pool: web::Data<PgPool>,
192 page_id: web::Path<Uuid>,
193 user: AuthUser,
194) -> ControllerResult<web::Json<i64>> {
195 let mut conn = pool.acquire().await?;
196 let token = authorize(&mut conn, Act::Teach, Some(user.id), Res::Page(*page_id)).await?;
197 let res = models::page_history::history_count(&mut conn, *page_id).await?;
198
199 token.authorized_ok(web::Json(res))
200}
201
202#[instrument(skip(pool, app_conf))]
207#[utoipa::path(
208 post,
209 path = "/{page_id}/restore",
210 operation_id = "restorePageHistory",
211 tag = "pages",
212 params(
213 ("page_id" = Uuid, Path, description = "Page id")
214 ),
215 request_body = HistoryRestoreData,
216 responses(
217 (status = 200, description = "Restored history id", body = Uuid)
218 )
219)]
220async fn restore(
221 request_id: RequestId,
222 pool: web::Data<PgPool>,
223 page_id: web::Path<Uuid>,
224 restore_data: web::Json<HistoryRestoreData>,
225 app_conf: web::Data<ApplicationConfiguration>,
226 user: AuthUser,
227 jwt_key: web::Data<JwtKey>,
228) -> ControllerResult<web::Json<Uuid>> {
229 let mut conn = pool.acquire().await?;
230 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Page(*page_id)).await?;
231 let page = models::pages::get_page(&mut conn, *page_id).await?;
232 let (expected_course_id, expected_exam_id) =
233 match CourseOrExamId::from_course_and_exam_ids(page.course_id, page.exam_id)? {
234 CourseOrExamId::Course(course_id) => (Some(course_id), None),
235 CourseOrExamId::Exam(exam_id) => (None, Some(exam_id)),
236 };
237 let res = models::pages::restore_from_history_for_page_id(
238 &mut conn,
239 *page_id,
240 restore_data.history_id,
241 None,
242 expected_course_id,
243 expected_exam_id,
244 user.id,
245 models_requests::make_spec_fetcher(
246 app_conf.base_url.clone(),
247 request_id.0,
248 Arc::clone(&jwt_key),
249 ),
250 models_requests::fetch_service_info,
251 )
252 .await?;
253
254 token.authorized_ok(web::Json(res))
255}
256
257#[utoipa::path(
263 get,
264 path = "/{page_id}/info",
265 operation_id = "getPageInfo",
266 tag = "pages",
267 params(
268 ("page_id" = Uuid, Path, description = "Page id")
269 ),
270 responses(
271 (status = 200, description = "Page info", body = PageInfo)
272 )
273)]
274async fn get_page_info(
275 page_id: web::Path<Uuid>,
276 pool: web::Data<PgPool>,
277 user: AuthUser,
278) -> ControllerResult<web::Json<PageInfo>> {
279 let mut conn = pool.acquire().await?;
280 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Page(*page_id)).await?;
281
282 let page_info = models::pages::get_page_info(&mut conn, *page_id).await?;
283
284 token.authorized_ok(web::Json(page_info))
285}
286
287#[instrument(skip(pool))]
291#[utoipa::path(
292 put,
293 path = "/{page_id}/page-details",
294 operation_id = "updatePageDetails",
295 tag = "pages",
296 params(
297 ("page_id" = Uuid, Path, description = "Page id")
298 ),
299 request_body = PageDetailsUpdate,
300 responses(
301 (status = 200, description = "Updated page details", body = bool)
302 )
303)]
304async fn update_page_details(
305 page_id: web::Path<Uuid>,
306 payload: web::Json<PageDetailsUpdate>,
307 pool: web::Data<PgPool>,
308 user: AuthUser,
309) -> ControllerResult<web::Json<bool>> {
310 let mut conn = pool.acquire().await?;
311
312 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Page(*page_id)).await?;
313
314 models::pages::update_page_details(&mut conn, *page_id, &payload).await?;
315 token.authorized_ok(web::Json(true))
316}
317
318#[utoipa::path(
322 get,
323 path = "/{course_id}/all-course-pages-for-course",
324 operation_id = "getCoursePages",
325 tag = "pages",
326 params(
327 ("course_id" = Uuid, Path, description = "Course id")
328 ),
329 responses(
330 (status = 200, description = "Course pages", body = Vec<Page>)
331 )
332)]
333async fn get_all_pages_by_course_id(
334 course_id: web::Path<Uuid>,
335 pool: web::Data<PgPool>,
336 user: AuthUser,
337) -> ControllerResult<web::Json<Vec<Page>>> {
338 let mut conn = pool.acquire().await?;
339 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(*course_id)).await?;
340
341 let mut pages = models::pages::get_pages_by_course_id(&mut conn, *course_id).await?;
342
343 pages.sort_by_key(|a| a.order_number);
344
345 token.authorized_ok(web::Json(pages))
346}
347
348pub fn _add_routes(cfg: &mut ServiceConfig) {
356 cfg.route("", web::post().to(post_new_page))
357 .route("/{page_id}", web::delete().to(delete_page))
358 .route("/{page_id}/info", web::get().to(get_page_info))
359 .route(
360 "/{page_id}/page-details",
361 web::put().to(update_page_details),
362 )
363 .route("/{page_id}/history", web::get().to(history))
364 .route("/{page_id}/history_count", web::get().to(history_count))
365 .route("/{page_id}/restore", web::post().to(restore))
366 .route(
367 "/{course_id}/all-course-pages-for-course",
368 web::get().to(get_all_pages_by_course_id),
369 );
370}