headless_lms_server/controllers/course_material/
user_details.rs1use 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#[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#[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#[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
114pub 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}