1use std::{path::PathBuf, str::FromStr};
4
5use models::{
6 courses::{Course, CourseCount},
7 exams::{CourseExam, NewExam, OrgExam},
8 organizations::Organization,
9 pages::{self, NewPage},
10};
11
12use crate::{
13 controllers::helpers::file_uploading::upload_image_for_organization,
14 domain::authorization::{
15 Action, Resource, authorize_with_fetched_list_of_roles, skip_authorize,
16 },
17 prelude::*,
18};
19use actix_web::web::{self, Json};
20
21#[instrument(skip(pool, file_store, app_conf))]
26async fn get_all_organizations(
27 pool: web::Data<PgPool>,
28 file_store: web::Data<dyn FileStore>,
29 app_conf: web::Data<ApplicationConfiguration>,
30) -> ControllerResult<web::Json<Vec<Organization>>> {
31 let mut conn = pool.acquire().await?;
32 let organizations = models::organizations::all_organizations(&mut conn)
33 .await?
34 .into_iter()
35 .map(|org| Organization::from_database_organization(org, file_store.as_ref(), &app_conf))
36 .collect();
37
38 let token = skip_authorize();
39 token.authorized_ok(web::Json(organizations))
40}
41
42#[instrument(skip(pool))]
46async fn get_organization_courses(
47 organization_id: web::Path<Uuid>,
48 pool: web::Data<PgPool>,
49 user: Option<AuthUser>,
50 pagination: web::Query<Pagination>,
51) -> ControllerResult<web::Json<Vec<Course>>> {
52 let mut conn = pool.acquire().await?;
53
54 let user = user.map(|u| u.id);
55 let courses = models::courses::organization_courses_visible_to_user_paginated(
56 &mut conn,
57 *organization_id,
58 user,
59 *pagination,
60 )
61 .await?;
62
63 let token = skip_authorize();
64 token.authorized_ok(web::Json(courses))
65}
66
67#[instrument(skip(pool))]
72async fn get_organization_duplicatable_courses(
73 organization_id: web::Path<Uuid>,
74 pool: web::Data<PgPool>,
75 user: AuthUser,
76) -> ControllerResult<web::Json<Vec<Course>>> {
77 let mut conn = pool.acquire().await?;
78 let courses = models::courses::get_by_organization_id(&mut conn, *organization_id).await?;
79
80 let user_roles = models::roles::get_roles(&mut conn, user.id).await?;
83
84 let mut duplicatable_courses = Vec::new();
85 for course in courses {
86 if authorize_with_fetched_list_of_roles(
87 &mut conn,
88 Action::Duplicate,
89 Some(user.id),
90 Resource::Course(course.id),
91 &user_roles,
92 )
93 .await
94 .is_ok()
95 {
96 duplicatable_courses.push(course);
97 }
98 }
99
100 let token = skip_authorize();
101 token.authorized_ok(web::Json(duplicatable_courses))
102}
103
104#[instrument(skip(pool))]
105async fn get_organization_course_count(
106 request_organization_id: web::Path<Uuid>,
107 pool: web::Data<PgPool>,
108) -> ControllerResult<Json<CourseCount>> {
109 let mut conn = pool.acquire().await?;
110 let result =
111 models::courses::organization_course_count(&mut conn, *request_organization_id).await?;
112
113 let token = skip_authorize();
114 token.authorized_ok(Json(result))
115}
116
117#[instrument(skip(pool))]
118async fn get_organization_active_courses(
119 request_organization_id: web::Path<Uuid>,
120 pool: web::Data<PgPool>,
121 pagination: web::Query<Pagination>,
122) -> ControllerResult<Json<Vec<Course>>> {
123 let mut conn = pool.acquire().await?;
124 let courses = models::courses::get_active_courses_for_organization(
125 &mut conn,
126 *request_organization_id,
127 *pagination,
128 )
129 .await?;
130
131 let token = skip_authorize();
132 token.authorized_ok(Json(courses))
133}
134
135#[instrument(skip(pool))]
136async fn get_organization_active_courses_count(
137 request_organization_id: web::Path<Uuid>,
138 pool: web::Data<PgPool>,
139) -> ControllerResult<Json<CourseCount>> {
140 let mut conn = pool.acquire().await?;
141 let result = models::courses::get_active_courses_for_organization_count(
142 &mut conn,
143 *request_organization_id,
144 )
145 .await?;
146
147 let token = skip_authorize();
148 token.authorized_ok(Json(result))
149}
150
151#[instrument(skip(request, payload, pool, file_store, app_conf))]
166async fn set_organization_image(
167 request: HttpRequest,
168 payload: Multipart,
169 organization_id: web::Path<Uuid>,
170 pool: web::Data<PgPool>,
171 user: AuthUser,
172 file_store: web::Data<dyn FileStore>,
173 app_conf: web::Data<ApplicationConfiguration>,
174) -> ControllerResult<web::Json<Organization>> {
175 let mut conn = pool.acquire().await?;
176 let organization = models::organizations::get_organization(&mut conn, *organization_id).await?;
177 let token = authorize(
178 &mut conn,
179 Act::Edit,
180 Some(user.id),
181 Res::Organization(organization.id),
182 )
183 .await?;
184 let organization_image = upload_image_for_organization(
185 request.headers(),
186 payload,
187 &organization,
188 &file_store,
189 user,
190 &mut conn,
191 )
192 .await?
193 .to_string_lossy()
194 .to_string();
195 let updated_organization = models::organizations::update_organization_image_path(
196 &mut conn,
197 organization.id,
198 Some(organization_image),
199 )
200 .await?;
201
202 if let Some(old_image_path) = organization.organization_image_path {
204 let file = PathBuf::from_str(&old_image_path).map_err(|original_error| {
205 ControllerError::new(
206 ControllerErrorType::InternalServerError,
207 original_error.to_string(),
208 Some(original_error.into()),
209 )
210 })?;
211 file_store.delete(&file).await.map_err(|original_error| {
212 ControllerError::new(
213 ControllerErrorType::InternalServerError,
214 original_error.to_string(),
215 Some(original_error.into()),
216 )
217 })?;
218 }
219
220 let response = Organization::from_database_organization(
221 updated_organization,
222 file_store.as_ref(),
223 app_conf.as_ref(),
224 );
225 token.authorized_ok(web::Json(response))
226}
227
228#[instrument(skip(pool, file_store))]
240async fn remove_organization_image(
241 organization_id: web::Path<Uuid>,
242 pool: web::Data<PgPool>,
243 user: AuthUser,
244 file_store: web::Data<dyn FileStore>,
245) -> ControllerResult<web::Json<()>> {
246 let mut conn = pool.acquire().await?;
247 let organization = models::organizations::get_organization(&mut conn, *organization_id).await?;
248 let token = authorize(
249 &mut conn,
250 Act::Edit,
251 Some(user.id),
252 Res::Organization(organization.id),
253 )
254 .await?;
255 if let Some(organization_image_path) = organization.organization_image_path {
256 let file = PathBuf::from_str(&organization_image_path).map_err(|original_error| {
257 ControllerError::new(
258 ControllerErrorType::InternalServerError,
259 original_error.to_string(),
260 Some(original_error.into()),
261 )
262 })?;
263 let _res =
264 models::organizations::update_organization_image_path(&mut conn, organization.id, None)
265 .await?;
266 file_store.delete(&file).await.map_err(|original_error| {
267 ControllerError::new(
268 ControllerErrorType::InternalServerError,
269 original_error.to_string(),
270 Some(original_error.into()),
271 )
272 })?;
273 }
274 token.authorized_ok(web::Json(()))
275}
276
277#[instrument(skip(pool, file_store, app_conf))]
282async fn get_organization(
283 organization_id: web::Path<Uuid>,
284 pool: web::Data<PgPool>,
285 file_store: web::Data<dyn FileStore>,
286 app_conf: web::Data<ApplicationConfiguration>,
287) -> ControllerResult<web::Json<Organization>> {
288 let mut conn = pool.acquire().await?;
289 let db_organization =
290 models::organizations::get_organization(&mut conn, *organization_id).await?;
291 let organization =
292 Organization::from_database_organization(db_organization, file_store.as_ref(), &app_conf);
293
294 let token = skip_authorize();
295 token.authorized_ok(web::Json(organization))
296}
297
298#[instrument(skip(pool))]
302async fn get_course_exams(
303 pool: web::Data<PgPool>,
304 organization: web::Path<Uuid>,
305) -> ControllerResult<web::Json<Vec<CourseExam>>> {
306 let mut conn = pool.acquire().await?;
307 let exams = models::exams::get_course_exams_for_organization(&mut conn, *organization).await?;
308
309 let token = skip_authorize();
310 token.authorized_ok(web::Json(exams))
311}
312
313#[instrument(skip(pool))]
317async fn get_org_exams(
318 pool: web::Data<PgPool>,
319 organization: web::Path<Uuid>,
320) -> ControllerResult<web::Json<Vec<OrgExam>>> {
321 let mut conn = pool.acquire().await?;
322 let exams = models::exams::get_exams_for_organization(&mut conn, *organization).await?;
323
324 let token = skip_authorize();
325 token.authorized_ok(web::Json(exams))
326}
327
328#[instrument(skip(pool))]
332pub async fn get_org_exam_with_exam_id(
333 pool: web::Data<PgPool>,
334 exam_id: web::Path<Uuid>,
335 user: AuthUser,
336) -> ControllerResult<web::Json<OrgExam>> {
337 let mut conn = pool.acquire().await?;
338 let token = authorize(&mut conn, Act::Teach, Some(user.id), Res::Exam(*exam_id)).await?;
339
340 let exam = models::exams::get_organization_exam_with_exam_id(&mut conn, *exam_id).await?;
341
342 token.authorized_ok(web::Json(exam))
343}
344
345#[instrument(skip(pool))]
349async fn create_exam(
350 pool: web::Data<PgPool>,
351 payload: web::Json<NewExam>,
352 user: AuthUser,
353) -> ControllerResult<web::Json<()>> {
354 let mut conn = pool.acquire().await?;
355 let mut tx = conn.begin().await?;
356
357 let new_exam = payload.0;
358 let token = authorize(
359 &mut tx,
360 Act::CreateCoursesOrExams,
361 Some(user.id),
362 Res::Organization(new_exam.organization_id),
363 )
364 .await?;
365
366 let new_exam_id = models::exams::insert(&mut tx, PKeyPolicy::Generate, &new_exam).await?;
367 pages::insert_exam_page(
368 &mut tx,
369 new_exam_id,
370 NewPage {
371 chapter_id: None,
372 course_id: None,
373 exam_id: Some(new_exam_id),
374 front_page_of_chapter_id: None,
375 content: serde_json::Value::Array(vec![]),
376 content_search_language: Some("simple".to_string()),
377 exercise_slides: vec![],
378 exercise_tasks: vec![],
379 exercises: vec![],
380 title: "exam page".to_string(),
381 url_path: "/".to_string(),
382 },
383 user.id,
384 )
385 .await?;
386
387 models::roles::insert(
388 &mut tx,
389 user.id,
390 models::roles::UserRole::Teacher,
391 models::roles::RoleDomain::Exam(new_exam_id),
392 )
393 .await?;
394
395 tx.commit().await?;
396
397 token.authorized_ok(web::Json(()))
398}
399
400pub fn _add_routes(cfg: &mut ServiceConfig) {
408 cfg.route("", web::get().to(get_all_organizations))
409 .route("/{organization_id}", web::get().to(get_organization))
410 .route(
411 "/{organization_id}/courses",
412 web::get().to(get_organization_courses),
413 )
414 .route(
415 "/{organization_id}/courses/duplicatable",
416 web::get().to(get_organization_duplicatable_courses),
417 )
418 .route(
419 "/{organization_id}/courses/count",
420 web::get().to(get_organization_course_count),
421 )
422 .route(
423 "/{organization_id}/courses/active",
424 web::get().to(get_organization_active_courses),
425 )
426 .route(
427 "/{organization_id}/courses/active/count",
428 web::get().to(get_organization_active_courses_count),
429 )
430 .route(
431 "/{organization_id}/image",
432 web::put().to(set_organization_image),
433 )
434 .route(
435 "/{organization_id}/image",
436 web::delete().to(remove_organization_image),
437 )
438 .route(
439 "/{organization_id}/course_exams",
440 web::get().to(get_course_exams),
441 )
442 .route("/{organization_id}/org_exams", web::get().to(get_org_exams))
443 .route(
444 "/{organization_id}/fetch_org_exam",
445 web::get().to(get_org_exam_with_exam_id),
446 )
447 .route("/{organization_id}/exams", web::post().to(create_exam));
448}