cloudillo_profile/
update.rs1use axum::{
7 extract::{Path, State},
8 http::StatusCode,
9 Json,
10};
11
12use serde::Serialize;
13
14use crate::prelude::*;
15use cloudillo_core::extract::Auth;
16use cloudillo_types::meta_adapter::UpdateProfileData;
17use cloudillo_types::types::{AdminProfilePatch, ProfileInfo, ProfilePatch};
18
19#[derive(Serialize)]
20pub struct UpdateProfileResponse {
21 profile: ProfileInfo,
22}
23
24fn profile_type_label(db_type: &str) -> &str {
25 match db_type {
26 "P" => "person",
27 "C" => "community",
28 other => other,
29 }
30}
31
32pub async fn patch_own_profile(
34 State(app): State<App>,
35 Auth(auth): Auth,
36 Json(patch): Json<ProfilePatch>,
37) -> ClResult<(StatusCode, Json<UpdateProfileResponse>)> {
38 let tn_id = auth.tn_id;
39
40 let profile_update =
42 UpdateProfileData { name: patch.name.map(Into::into), ..Default::default() };
43
44 app.meta_adapter.update_profile(tn_id, &auth.id_tag, &profile_update).await?;
46
47 let profile_data = app.meta_adapter.get_profile_info(tn_id, &auth.id_tag).await?;
49
50 let profile = ProfileInfo {
51 id_tag: profile_data.id_tag.to_string(),
52 name: profile_data.name.to_string(),
53 r#type: Some(profile_type_label(&profile_data.r#type).to_string()),
54 profile_pic: profile_data.profile_pic.map(|s| s.to_string()),
55 status: None,
56 connected: None,
57 following: None,
58 roles: None,
59 created_at: Some(profile_data.created_at),
60 };
61
62 info!("User {} updated their profile", auth.id_tag);
63 Ok((StatusCode::OK, Json(UpdateProfileResponse { profile })))
64}
65
66pub async fn patch_profile_admin(
68 State(app): State<App>,
69 Auth(auth): Auth,
70 Path(id_tag): Path<String>,
71 Json(patch): Json<AdminProfilePatch>,
72) -> ClResult<(StatusCode, Json<UpdateProfileResponse>)> {
73 let has_admin_role = auth.roles.iter().any(|role| role.as_ref() == "leader");
75 if !has_admin_role {
76 warn!("Non-admin user {} attempted to modify profile {}", auth.id_tag, id_tag);
77 return Err(Error::PermissionDenied);
78 }
79
80 let tn_id = auth.tn_id;
81
82 let response_roles = match &patch.roles {
84 Patch::Value(Some(roles)) => Some(roles.clone()),
85 Patch::Value(None) | Patch::Null => Some(vec![]),
86 Patch::Undefined => None,
87 };
88
89 let profile_update = UpdateProfileData {
90 name: patch.name.map(Into::into),
91 roles: patch
92 .roles
93 .map(|opt_roles| opt_roles.map(|roles| roles.into_iter().map(Into::into).collect())),
94 status: patch.status,
95 ..Default::default()
96 };
97
98 app.meta_adapter.update_profile(tn_id, &id_tag, &profile_update).await?;
100
101 let profile_data = app.meta_adapter.get_profile_info(tn_id, &id_tag).await?;
103
104 let profile = ProfileInfo {
105 id_tag: profile_data.id_tag.to_string(),
106 name: profile_data.name.to_string(),
107 r#type: Some(profile_type_label(&profile_data.r#type).to_string()),
108 profile_pic: profile_data.profile_pic.map(|s| s.to_string()),
109 status: None,
110 connected: None,
111 following: None,
112 roles: response_roles,
113 created_at: Some(profile_data.created_at),
114 };
115
116 info!("Admin {} updated profile {}", auth.id_tag, id_tag);
117 Ok((StatusCode::OK, Json(UpdateProfileResponse { profile })))
118}
119
120pub async fn patch_profile_relationship(
122 State(app): State<App>,
123 Auth(auth): Auth,
124 Path(id_tag): Path<String>,
125 Json(patch): Json<cloudillo_types::meta_adapter::UpdateProfileData>,
126) -> ClResult<StatusCode> {
127 let tn_id = auth.tn_id;
128
129 app.meta_adapter.update_profile(tn_id, &id_tag, &patch).await?;
131
132 info!("User {} updated relationship with {}", auth.id_tag, id_tag);
133 Ok(StatusCode::OK)
134}
135
136