headless_lms_server/controllers/main_frontend/
pages.rs

1//! Controllers for requests starting with `/api/v0/main-frontend/pages`.
2
3use std::sync::Arc;
4
5use models::{
6    page_history::PageHistory,
7    pages::{HistoryRestoreData, NewPage, Page, PageDetailsUpdate, PageInfo},
8};
9
10use crate::{
11    domain::{
12        models_requests::{self, JwtKey},
13        request_id::RequestId,
14    },
15    prelude::*,
16};
17
18/**
19POST `/api/v0/main-frontend/pages` - Create a new page.
20
21Please note that this endpoint will change all the exercise and exercise task ids you've created. Make sure the use the updated ids from the response object.
22
23If optional property front_page_of_chapter_id is set, this page will become the front page of the specified course part.
24
25# Example:
26
27Request:
28```http
29POST /api/v0/main-frontend/pages HTTP/1.1
30Content-Type: application/json
31
32{
33  "content": [
34    {
35      "type": "x",
36      "id": "2a4e517d-a7d2-4d82-89fb-a1333d8d01d1"
37    }
38  ],
39  "url_path": "/part-2/best-page",
40  "title": "Hello world!",
41  "course_id": "10363c5b-82b4-4121-8ef1-bae8fb42a5ce",
42  "chapter_id": "2495ffa3-7ea9-4615-baa5-828023688c79"
43}
44```
45*/
46
47#[instrument(skip(pool, app_conf))]
48async fn post_new_page(
49    request_id: RequestId,
50    payload: web::Json<NewPage>,
51    pool: web::Data<PgPool>,
52    app_conf: web::Data<ApplicationConfiguration>,
53    user: AuthUser,
54    jwt_key: web::Data<JwtKey>,
55) -> ControllerResult<web::Json<Page>> {
56    let mut conn = pool.acquire().await?;
57    let new_page = payload.0;
58    let course_id = new_page.course_id.ok_or_else(|| {
59        ControllerError::new(
60            ControllerErrorType::BadRequest,
61            "Cannot create a new page without a course id".to_string(),
62            None,
63        )
64    })?;
65    let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(course_id)).await?;
66
67    let page = models::pages::insert_new_content_page(
68        &mut conn,
69        new_page,
70        user.id,
71        models_requests::make_spec_fetcher(
72            app_conf.base_url.clone(),
73            request_id.0,
74            Arc::clone(&jwt_key),
75        ),
76        models_requests::fetch_service_info,
77    )
78    .await?;
79    token.authorized_ok(web::Json(page))
80}
81
82/**
83DELETE `/api/v0/main-frontend/pages/:page_id` - Delete a page, related exercises, and related exercise tasks by id.
84
85
86# Example
87
88Request: `DELETE /api/v0/main-frontend/pages/40ca9bcf-8eaa-41ba-940e-0fd5dd0c3c02`
89*/
90#[instrument(skip(pool))]
91async fn delete_page(
92    page_id: web::Path<Uuid>,
93    pool: web::Data<PgPool>,
94    user: AuthUser,
95) -> ControllerResult<web::Json<Page>> {
96    let mut conn = pool.acquire().await?;
97    let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Page(*page_id)).await?;
98    let deleted_page = models::pages::delete_page_and_exercises(&mut conn, *page_id).await?;
99
100    token.authorized_ok(web::Json(deleted_page))
101}
102
103/**
104GET /api/v0/main-frontend/pages/:page_id/history
105*/
106#[instrument(skip(pool))]
107async fn history(
108    pool: web::Data<PgPool>,
109    page_id: web::Path<Uuid>,
110    pagination: web::Query<Pagination>,
111    user: AuthUser,
112) -> ControllerResult<web::Json<Vec<PageHistory>>> {
113    let mut conn = pool.acquire().await?;
114    let token = authorize(&mut conn, Act::Teach, Some(user.id), Res::Page(*page_id)).await?;
115
116    let res = models::page_history::history(&mut conn, *page_id, *pagination).await?;
117
118    token.authorized_ok(web::Json(res))
119}
120
121/**
122GET /api/v0/main-frontend/pages/:page_id/history_count
123*/
124#[instrument(skip(pool))]
125async fn history_count(
126    pool: web::Data<PgPool>,
127    page_id: web::Path<Uuid>,
128    user: AuthUser,
129) -> ControllerResult<web::Json<i64>> {
130    let mut conn = pool.acquire().await?;
131    let token = authorize(&mut conn, Act::Teach, Some(user.id), Res::Page(*page_id)).await?;
132    let res = models::page_history::history_count(&mut conn, *page_id).await?;
133
134    token.authorized_ok(web::Json(res))
135}
136
137/**
138POST /api/v0/main-frontend/pages/:page_id/restore
139*/
140
141#[instrument(skip(pool, app_conf))]
142async fn restore(
143    request_id: RequestId,
144    pool: web::Data<PgPool>,
145    page_id: web::Path<Uuid>,
146    restore_data: web::Json<HistoryRestoreData>,
147    app_conf: web::Data<ApplicationConfiguration>,
148    user: AuthUser,
149    jwt_key: web::Data<JwtKey>,
150) -> ControllerResult<web::Json<Uuid>> {
151    let mut conn = pool.acquire().await?;
152    let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Page(*page_id)).await?;
153    let res = models::pages::restore(
154        &mut conn,
155        *page_id,
156        restore_data.history_id,
157        user.id,
158        models_requests::make_spec_fetcher(
159            app_conf.base_url.clone(),
160            request_id.0,
161            Arc::clone(&jwt_key),
162        ),
163        models_requests::fetch_service_info,
164    )
165    .await?;
166
167    token.authorized_ok(web::Json(res))
168}
169
170/**
171GET `/api/v0/main-fronted/pages/:page_id/info` - Get a pages's course id, course name, organization slug
172
173Request: `GET /api/v0/cms/pages/40ca9bcf-8eaa-41ba-940e-0fd5dd0c3c02/info`
174*/
175async fn get_page_info(
176    page_id: web::Path<Uuid>,
177    pool: web::Data<PgPool>,
178    user: AuthUser,
179) -> ControllerResult<web::Json<PageInfo>> {
180    let mut conn = pool.acquire().await?;
181    let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Page(*page_id)).await?;
182
183    let page_info = models::pages::get_page_info(&mut conn, *page_id).await?;
184
185    token.authorized_ok(web::Json(page_info))
186}
187
188/**
189POST `/api/v0/main-frontend/pages/:page_id/page-details` - Update pages title and url_path.
190*/
191#[instrument(skip(pool))]
192async fn update_page_details(
193    page_id: web::Path<Uuid>,
194    payload: web::Json<PageDetailsUpdate>,
195    pool: web::Data<PgPool>,
196    user: AuthUser,
197) -> ControllerResult<web::Json<bool>> {
198    let mut conn = pool.acquire().await?;
199
200    let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Page(*page_id)).await?;
201
202    models::pages::update_page_details(&mut conn, *page_id, &payload).await?;
203    token.authorized_ok(web::Json(true))
204}
205
206/**
207GET `/api/v0/main-frontend/pages/:course_id/all-course-pages-for-course` - Get all pages of a course
208*/
209async fn get_all_pages_by_course_id(
210    course_id: web::Path<Uuid>,
211    pool: web::Data<PgPool>,
212    user: AuthUser,
213) -> ControllerResult<web::Json<Vec<Page>>> {
214    let mut conn = pool.acquire().await?;
215    let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(*course_id)).await?;
216
217    let mut pages = models::pages::get_pages_by_course_id(&mut conn, *course_id).await?;
218
219    pages.sort_by(|a, b| a.order_number.cmp(&b.order_number));
220
221    token.authorized_ok(web::Json(pages))
222}
223
224/**
225Add a route for each controller in this module.
226
227The name starts with an underline in order to appear before other functions in the module documentation.
228
229We add the routes by calling the route method instead of using the route annotations because this method preserves the function signatures for documentation.
230*/
231pub fn _add_routes(cfg: &mut ServiceConfig) {
232    cfg.route("", web::post().to(post_new_page))
233        .route("/{page_id}", web::delete().to(delete_page))
234        .route("/{page_id}/info", web::get().to(get_page_info))
235        .route(
236            "/{page_id}/page-details",
237            web::put().to(update_page_details),
238        )
239        .route("/{page_id}/history", web::get().to(history))
240        .route("/{page_id}/history_count", web::get().to(history_count))
241        .route("/{history_id}/restore", web::post().to(restore))
242        .route(
243            "/{course_id}/all-course-pages-for-course",
244            web::get().to(get_all_pages_by_course_id),
245        );
246}