headless_lms_server/controllers/main_frontend/
chapters.rsuse std::{path::PathBuf, str::FromStr, sync::Arc};
use headless_lms_models::chapters::DatabaseChapter;
use models::chapters::{Chapter, ChapterUpdate, NewChapter};
use crate::{
domain::{
models_requests::{self, JwtKey},
request_id::RequestId,
},
prelude::*,
};
#[instrument(skip(pool, file_store, app_conf))]
async fn post_new_chapter(
request_id: RequestId,
pool: web::Data<PgPool>,
payload: web::Json<NewChapter>,
user: AuthUser,
file_store: web::Data<dyn FileStore>,
app_conf: web::Data<ApplicationConfiguration>,
jwt_key: web::Data<JwtKey>,
) -> ControllerResult<web::Json<Chapter>> {
let mut conn = pool.acquire().await?;
let token = authorize(
&mut conn,
Act::Edit,
Some(user.id),
Res::Course(payload.course_id),
)
.await?;
let new_chapter = payload.0;
let (database_chapter, ..) = models::library::content_management::create_new_chapter(
&mut conn,
PKeyPolicy::Generate,
&new_chapter,
user.id,
models_requests::make_spec_fetcher(
app_conf.base_url.clone(),
request_id.0,
Arc::clone(&jwt_key),
),
models_requests::fetch_service_info,
)
.await?;
return token.authorized_ok(web::Json(Chapter::from_database_chapter(
&database_chapter,
file_store.as_ref(),
app_conf.as_ref(),
)));
}
#[instrument(skip(pool, file_store, app_conf))]
async fn delete_chapter(
chapter_id: web::Path<String>,
pool: web::Data<PgPool>,
user: AuthUser,
file_store: web::Data<dyn FileStore>,
app_conf: web::Data<ApplicationConfiguration>,
) -> ControllerResult<web::Json<Chapter>> {
let mut conn = pool.acquire().await?;
let chapter_id = Uuid::from_str(&chapter_id)?;
let token = authorize(
&mut conn,
Act::Edit,
Some(user.id),
Res::Chapter(chapter_id),
)
.await?;
let deleted_chapter = models::chapters::delete_chapter(&mut conn, chapter_id).await?;
return token.authorized_ok(web::Json(Chapter::from_database_chapter(
&deleted_chapter,
file_store.as_ref(),
app_conf.as_ref(),
)));
}
#[instrument(skip(payload, pool, file_store, app_conf))]
async fn update_chapter(
payload: web::Json<ChapterUpdate>,
chapter_id: web::Path<String>,
pool: web::Data<PgPool>,
user: AuthUser,
file_store: web::Data<dyn FileStore>,
app_conf: web::Data<ApplicationConfiguration>,
) -> ControllerResult<web::Json<Chapter>> {
let mut conn = pool.acquire().await?;
let chapter_id = Uuid::from_str(&chapter_id)?;
let course_id = models::chapters::get_course_id(&mut conn, chapter_id).await?;
let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(course_id)).await?;
let course_update = payload.0;
let chapter = models::chapters::update_chapter(&mut conn, chapter_id, course_update).await?;
let response = Chapter::from_database_chapter(&chapter, file_store.as_ref(), app_conf.as_ref());
token.authorized_ok(web::Json(response))
}
#[instrument(skip(request, payload, pool, file_store, app_conf))]
async fn set_chapter_image(
request: HttpRequest,
payload: Multipart,
chapter_id: web::Path<Uuid>,
pool: web::Data<PgPool>,
user: AuthUser,
file_store: web::Data<dyn FileStore>,
app_conf: web::Data<ApplicationConfiguration>,
) -> ControllerResult<web::Json<Chapter>> {
let mut conn = pool.acquire().await?;
let chapter = models::chapters::get_chapter(&mut conn, *chapter_id).await?;
let token = authorize(
&mut conn,
Act::Edit,
Some(user.id),
Res::Course(chapter.course_id),
)
.await?;
let course = models::courses::get_course(&mut conn, chapter.course_id).await?;
let chapter_image = upload_file_from_cms(
request.headers(),
payload,
StoreKind::Course(course.id),
file_store.as_ref(),
&mut conn,
user,
)
.await?
.to_string_lossy()
.to_string();
let updated_chapter =
models::chapters::update_chapter_image_path(&mut conn, chapter.id, Some(chapter_image))
.await?;
if let Some(old_image_path) = chapter.chapter_image_path {
let file = PathBuf::from_str(&old_image_path).map_err(|original_error| {
ControllerError::new(
ControllerErrorType::InternalServerError,
original_error.to_string(),
Some(original_error.into()),
)
})?;
file_store.delete(&file).await.map_err(|original_error| {
ControllerError::new(
ControllerErrorType::InternalServerError,
original_error.to_string(),
Some(original_error.into()),
)
})?;
}
let response =
Chapter::from_database_chapter(&updated_chapter, file_store.as_ref(), app_conf.as_ref());
token.authorized_ok(web::Json(response))
}
#[instrument(skip(pool, file_store))]
async fn remove_chapter_image(
chapter_id: web::Path<Uuid>,
pool: web::Data<PgPool>,
user: AuthUser,
file_store: web::Data<dyn FileStore>,
) -> ControllerResult<web::Json<()>> {
let mut conn = pool.acquire().await?;
let chapter = models::chapters::get_chapter(&mut conn, *chapter_id).await?;
let token = authorize(
&mut conn,
Act::Edit,
Some(user.id),
Res::Course(chapter.course_id),
)
.await?;
if let Some(chapter_image_path) = chapter.chapter_image_path {
let file = PathBuf::from_str(&chapter_image_path).map_err(|original_error| {
ControllerError::new(
ControllerErrorType::InternalServerError,
original_error.to_string(),
Some(original_error.into()),
)
})?;
let _res = models::chapters::update_chapter_image_path(&mut conn, chapter.id, None).await?;
file_store.delete(&file).await.map_err(|original_error| {
ControllerError::new(
ControllerErrorType::InternalServerError,
original_error.to_string(),
Some(original_error.into()),
)
})?;
}
token.authorized_ok(web::Json(()))
}
async fn get_all_chapters_by_course_id(
course_id: web::Path<Uuid>,
pool: web::Data<PgPool>,
user: AuthUser,
) -> ControllerResult<web::Json<Vec<DatabaseChapter>>> {
let mut conn = pool.acquire().await?;
let token = authorize(&mut conn, Act::View, Some(user.id), Res::Course(*course_id)).await?;
let mut chapters = models::chapters::course_chapters(&mut conn, *course_id).await?;
chapters.sort_by(|a, b| a.chapter_number.cmp(&b.chapter_number));
token.authorized_ok(web::Json(chapters))
}
pub fn _add_routes(cfg: &mut ServiceConfig) {
cfg.route("", web::post().to(post_new_chapter))
.route("/{chapter_id}", web::delete().to(delete_chapter))
.route("/{chapter_id}", web::put().to(update_chapter))
.route("/{chapter_id}/image", web::put().to(set_chapter_image))
.route(
"/{chapter_id}/image",
web::delete().to(remove_chapter_image),
)
.route(
"/{course_id}/all-chapters-for-course",
web::get().to(get_all_chapters_by_course_id),
);
}