headless_lms_server/controllers/main_frontend/
page_audio_files.rs

1//! Controllers for requests starting with `/api/v0/main-frontend/page_audio`.
2
3use std::path::Path;
4
5use futures::StreamExt;
6use models::page_audio_files::PageAudioFile;
7
8use crate::prelude::*;
9
10/**
11POST `/api/v0/main-frontend/page_audio/:page_id` - Sets or updates the page audio.
12
13# Example
14
15Request:
16```http
17POST /api/v0/main-frontend/page_audio/d332f3d9-39a5-4a18-80f4-251727693c37 HTTP/1.1
18Content-Type: multipart/form-data
19
20BINARY_DATA
21```
22*/
23
24#[instrument(skip(request, payload, pool, file_store))]
25async fn set_page_audio(
26    request: HttpRequest,
27    mut payload: Multipart,
28    page_id: web::Path<Uuid>,
29    pool: web::Data<PgPool>,
30    user: AuthUser,
31    file_store: web::Data<dyn FileStore>,
32) -> ControllerResult<web::Json<bool>> {
33    let mut conn = pool.acquire().await?;
34    let page = models::pages::get_page(&mut conn, *page_id).await?;
35    if let Some(course_id) = page.course_id {
36        let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(course_id)).await?;
37
38        let field = match payload.next().await {
39            Some(Ok(field)) => field,
40            Some(Err(error)) => {
41                return Err(ControllerError::new(
42                    ControllerErrorType::InternalServerError,
43                    error.to_string(),
44                    None,
45                ));
46            }
47            None => {
48                return Err(ControllerError::new(
49                    ControllerErrorType::BadRequest,
50                    "Didn't upload any files",
51                    None,
52                ));
53            }
54        };
55
56        let mime_type = field
57            .content_type()
58            .map(|ct| ct.to_string())
59            .unwrap_or_else(|| "".to_string());
60
61        match mime_type.as_str() {
62            "audio/mpeg" | "audio/ogg" => {}
63            unsupported => {
64                return Err(ControllerError::new(
65                    ControllerErrorType::BadRequest,
66                    format!("Unsupported audio Mime type: {}", unsupported),
67                    None,
68                ));
69            }
70        };
71
72        let course = models::courses::get_course(&mut conn, page.course_id.unwrap()).await?;
73        let media_path = upload_field_from_cms(
74            request.headers(),
75            field,
76            StoreKind::Course(course.id),
77            file_store.as_ref(),
78            &mut conn,
79            user,
80        )
81        .await?;
82
83        models::page_audio_files::insert_page_audio(
84            &mut conn,
85            page.id,
86            &media_path.as_path().to_string_lossy(),
87            &mime_type,
88        )
89        .await?;
90
91        token.authorized_ok(web::Json(true))
92    } else {
93        Err(ControllerError::new(
94            ControllerErrorType::BadRequest,
95            "The page needs to be related to a course.".to_string(),
96            None,
97        ))
98    }
99}
100
101/**
102DELETE `/api/v0/main-frontend/page_audio/:file_id` - Removes the chapter image.
103
104# Example
105
106Request:
107```http
108DELETE /api/v0/main-frontend/page_audio/d332f3d9-39a5-4a18-80f4-251727693c37 HTTP/1.1
109```
110*/
111
112#[instrument(skip(pool, file_store))]
113async fn remove_page_audio(
114    page_audio_id: web::Path<Uuid>,
115    page_id: web::Path<Uuid>,
116    pool: web::Data<PgPool>,
117    user: AuthUser,
118    file_store: web::Data<dyn FileStore>,
119) -> ControllerResult<web::Json<()>> {
120    let mut conn = pool.acquire().await?;
121    let audio =
122        models::page_audio_files::get_page_audio_files_by_id(&mut conn, *page_audio_id).await?;
123    let page = models::pages::get_page(&mut conn, audio.page_id).await?;
124    if let Some(course_id) = page.course_id {
125        let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(course_id)).await?;
126
127        let path = models::page_audio_files::delete_page_audio(&mut conn, *page_audio_id).await?;
128        file_store.delete(Path::new(&path)).await.map_err(|_| {
129            ControllerError::new(
130                ControllerErrorType::BadRequest,
131                "Could not delete the file from the file store".to_string(),
132                None,
133            )
134        })?;
135        token.authorized_ok(web::Json(()))
136    } else {
137        Err(ControllerError::new(
138            ControllerErrorType::BadRequest,
139            "The page needs to be related to a course.".to_string(),
140            None,
141        ))
142    }
143}
144
145/**
146GET `/api/v0/main-fronted/page_audio/:page_id/files` - Get a page audio files
147
148Request: `GET /api/v0/cms/page_audio/40ca9bcf-8eaa-41ba-940e-0fd5dd0c3c02/files`
149*/
150#[instrument(skip(app_conf))]
151
152async fn get_page_audio(
153    page_id: web::Path<Uuid>,
154    pool: web::Data<PgPool>,
155    user: AuthUser,
156    app_conf: web::Data<ApplicationConfiguration>,
157) -> ControllerResult<web::Json<Vec<PageAudioFile>>> {
158    let mut conn = pool.acquire().await?;
159    let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Page(*page_id)).await?;
160
161    let mut page_audio_files =
162        models::page_audio_files::get_page_audio_files(&mut conn, *page_id).await?;
163
164    let base_url = &app_conf.base_url;
165    for audio in page_audio_files.iter_mut() {
166        audio.path = format!("{base_url}/api/v0/files/{}", audio.path);
167    }
168
169    token.authorized_ok(web::Json(page_audio_files))
170}
171
172/**
173Add a route for each controller in this module.
174
175The name starts with an underline in order to appear before other functions in the module documentation.
176
177We add the routes by calling the route method instead of using the route annotations because this method preserves the function signatures for documentation.
178*/
179pub fn _add_routes(cfg: &mut ServiceConfig) {
180    cfg.route("/{page_id}", web::post().to(set_page_audio))
181        .route("/{file_id}", web::delete().to(remove_page_audio))
182        .route("/{page_id}/files", web::get().to(get_page_audio));
183}