headless_lms_server/controllers/course_material/
user_details.rs

1use std::net::IpAddr;
2
3use headless_lms_utils::ip_to_country::IpToCountryMapper;
4use models::user_details::UserDetail;
5use utoipa::{OpenApi, ToSchema};
6
7use crate::prelude::*;
8
9#[derive(OpenApi)]
10#[openapi(paths(get_user_details, update_user_info, get_user_country_by_ip))]
11pub(crate) struct CourseMaterialUserDetailsApiDoc;
12
13/**
14GET `/api/v0/course-material/user-details/user` - Find user details by user id
15*/
16#[utoipa::path(
17    get,
18    path = "/user",
19    operation_id = "getCourseMaterialAuthenticatedUserDetails",
20    tag = "course-material-user-details",
21    responses(
22        (status = 200, description = "Authenticated user details", body = UserDetail)
23    )
24)]
25#[instrument(skip(pool))]
26pub async fn get_user_details(
27    user: AuthUser,
28    pool: web::Data<PgPool>,
29) -> ControllerResult<web::Json<UserDetail>> {
30    let mut conn = pool.acquire().await?;
31
32    let token = skip_authorize();
33
34    let res = models::user_details::get_user_details_by_user_id(&mut conn, user.id).await?;
35    token.authorized_ok(web::Json(res))
36}
37
38#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
39
40pub struct UserInfoPayload {
41    pub email: String,
42    pub first_name: String,
43    pub last_name: String,
44    pub country: String,
45    pub email_communication_consent: bool,
46}
47
48/**
49POST `/api/v0/course-material/user-details/update-user-info` - Updates the users information such as email, name, country and email communication consent
50*/
51#[utoipa::path(
52    post,
53    path = "/update-user-info",
54    operation_id = "updateCourseMaterialUserInfo",
55    tag = "course-material-user-details",
56    request_body = UserInfoPayload,
57    responses(
58        (status = 200, description = "Updated user details", body = UserDetail)
59    )
60)]
61#[instrument(skip(pool))]
62pub async fn update_user_info(
63    user: AuthUser,
64    pool: web::Data<PgPool>,
65    payload: web::Json<UserInfoPayload>,
66) -> ControllerResult<web::Json<UserDetail>> {
67    let mut conn = pool.acquire().await?;
68    let res = models::user_details::update_user_info(
69        &mut conn,
70        user.id,
71        &payload.email,
72        &payload.first_name,
73        &payload.last_name,
74        &payload.country,
75        payload.email_communication_consent,
76    )
77    .await?;
78
79    let token = skip_authorize();
80    token.authorized_ok(web::Json(res))
81}
82
83/**
84GET `/api/v0/course-material/user-details/users-ip-country` - Find users country by their IP  address
85*/
86#[utoipa::path(
87    get,
88    path = "/users-ip-country",
89    operation_id = "getCourseMaterialCountryFromIp",
90    tag = "course-material-user-details",
91    responses(
92        (status = 200, description = "Detected country code", body = String)
93    )
94)]
95pub async fn get_user_country_by_ip(
96    req: HttpRequest,
97    ip_to_country_mapper: web::Data<IpToCountryMapper>,
98) -> ControllerResult<String> {
99    let connection_info = req.connection_info();
100
101    let ip: Option<IpAddr> = connection_info
102        .realip_remote_addr()
103        .and_then(|ip| ip.parse::<IpAddr>().ok());
104
105    let country = ip
106        .and_then(|ip| ip_to_country_mapper.map_ip_to_country(&ip))
107        .map(|c| c.to_string())
108        .unwrap_or_default();
109
110    let token = skip_authorize();
111    token.authorized_ok(country.to_string())
112}
113
114/**
115Add a route for each controller in this module.
116
117The name starts with an underline in order to appear before other functions in the module documentation.
118
119We add the routes by calling the route method instead of using the route annotations because this method preserves the function signatures for documentation.
120*/
121pub fn _add_routes(cfg: &mut ServiceConfig) {
122    cfg.route("/user", web::get().to(get_user_details))
123        .route("/update-user-info", web::post().to(update_user_info))
124        .route("/users-ip-country", web::get().to(get_user_country_by_ip));
125}