The smokesignal.events web application
at main 243 lines 7.0 kB view raw
1use anyhow::Result; 2use axum::{ 3 Form, 4 extract::Query, 5 response::{IntoResponse, Redirect}, 6}; 7use axum_htmx::{HxRedirect, HxRequest}; 8use axum_template::RenderHtml; 9use http::StatusCode; 10use minijinja::context as template_context; 11use serde::Deserialize; 12use std::borrow::Cow; 13 14use crate::{ 15 contextual_error, 16 http::{ 17 context::{AdminRequestContext, admin_template_context}, 18 errors::WebError, 19 }, 20 select_template, 21 storage::{ 22 denylist::denylist_add_or_update, 23 identity_profile::{handle_nuke, identity_profile_get}, 24 notification::notification_get, 25 }, 26}; 27 28#[derive(Deserialize)] 29pub(crate) struct IdentityProfileQuery { 30 pub(crate) did: String, 31} 32 33pub(crate) async fn handle_admin_identity_profile( 34 admin_ctx: AdminRequestContext, 35 Query(query): Query<IdentityProfileQuery>, 36) -> Result<impl IntoResponse, WebError> { 37 let canonical_url = format!( 38 "https://{}/admin/identity_profile", 39 admin_ctx.web_context.config.external_base 40 ); 41 let default_context = admin_template_context(&admin_ctx, &canonical_url, "identity_profiles"); 42 43 let language = admin_ctx.language; 44 let web_context = admin_ctx.web_context; 45 46 let render_template = select_template!("admin_identity_profile", false, false, language); 47 let error_template = select_template!(false, false, language); 48 49 // Get the identity profile 50 let profile = identity_profile_get(&web_context.pool, &query.did).await; 51 if let Err(err) = profile { 52 return contextual_error!( 53 web_context, 54 language.0, 55 error_template, 56 default_context, 57 err 58 ); 59 } 60 let profile = profile.unwrap(); 61 62 if profile.is_none() { 63 return contextual_error!( 64 web_context, 65 language.0, 66 error_template, 67 default_context, 68 "Identity profile not found" 69 ); 70 } 71 let profile = profile.unwrap(); 72 73 // Get notification settings (may not exist for all profiles) 74 let notifications = notification_get(&web_context.pool, &query.did).await; 75 let notifications = notifications.ok().flatten(); 76 77 // Convert profile to JSON for display 78 let profile_json = serde_json::to_string_pretty(&profile) 79 .unwrap_or_else(|_| "Error formatting JSON".to_string()); 80 81 // Convert notifications to JSON if available 82 let notifications_json = notifications.as_ref().map(|n| { 83 serde_json::to_string_pretty(n).unwrap_or_else(|_| "Error formatting JSON".to_string()) 84 }); 85 86 Ok(RenderHtml( 87 &render_template, 88 web_context.engine.clone(), 89 template_context! { ..default_context, ..template_context! { 90 did => query.did.clone(), 91 profile => profile, 92 profile_json => profile_json, 93 notifications => notifications, 94 notifications_json => notifications_json, 95 }}, 96 ) 97 .into_response()) 98} 99 100#[derive(Deserialize)] 101pub(crate) struct BanDidForm { 102 did: String, 103} 104 105#[derive(Deserialize)] 106pub(crate) struct BanPdsForm { 107 did: String, 108 pds: String, 109} 110 111#[derive(Deserialize)] 112pub(crate) struct NukeIdentityForm { 113 did: String, 114} 115 116pub(crate) async fn handle_admin_identity_profile_ban_did( 117 admin_ctx: AdminRequestContext, 118 HxRequest(hx_request): HxRequest, 119 Form(form): Form<BanDidForm>, 120) -> Result<impl IntoResponse, WebError> { 121 let error_template = select_template!(false, false, admin_ctx.language); 122 123 if form.did == admin_ctx.admin_handle.did { 124 return contextual_error!( 125 admin_ctx.web_context, 126 admin_ctx.language, 127 error_template, 128 template_context! { 129 message => "You cannot ban your own identity." 130 }, 131 "You cannot ban your own identity." 132 ); 133 } 134 135 let reason = format!( 136 "DID banned by admin {}", 137 admin_ctx.admin_handle.did.replace('\'', "") 138 ); 139 140 if let Err(err) = denylist_add_or_update( 141 &admin_ctx.web_context.pool, 142 Cow::Borrowed(&form.did), 143 Cow::Owned(reason), 144 ) 145 .await 146 { 147 return contextual_error!( 148 admin_ctx.web_context, 149 admin_ctx.language, 150 error_template, 151 template_context! {}, 152 err 153 ); 154 } 155 156 let redirect_url = format!("/admin/identity_profile?did={}", form.did); 157 if hx_request { 158 let hx_redirect = HxRedirect::from(redirect_url.as_str()); 159 Ok((StatusCode::OK, hx_redirect, "").into_response()) 160 } else { 161 Ok(Redirect::to(&redirect_url).into_response()) 162 } 163} 164 165pub(crate) async fn handle_admin_identity_profile_ban_pds( 166 admin_ctx: AdminRequestContext, 167 HxRequest(hx_request): HxRequest, 168 Form(form): Form<BanPdsForm>, 169) -> Result<impl IntoResponse, WebError> { 170 let error_template = select_template!(false, false, admin_ctx.language); 171 172 let reason = format!( 173 "PDS banned by admin {}", 174 admin_ctx.admin_handle.did.replace('\'', "") 175 ); 176 177 if let Err(err) = denylist_add_or_update( 178 &admin_ctx.web_context.pool, 179 Cow::Borrowed(&form.pds), 180 Cow::Owned(reason), 181 ) 182 .await 183 { 184 return contextual_error!( 185 admin_ctx.web_context, 186 admin_ctx.language, 187 error_template, 188 template_context! {}, 189 err 190 ); 191 } 192 193 let redirect_url = format!("/admin/identity_profile?did={}", form.did); 194 if hx_request { 195 let hx_redirect = HxRedirect::from(redirect_url.as_str()); 196 Ok((StatusCode::OK, hx_redirect, "").into_response()) 197 } else { 198 Ok(Redirect::to(&redirect_url).into_response()) 199 } 200} 201 202pub(crate) async fn handle_admin_identity_profile_nuke( 203 admin_ctx: AdminRequestContext, 204 HxRequest(hx_request): HxRequest, 205 Form(form): Form<NukeIdentityForm>, 206) -> Result<impl IntoResponse, WebError> { 207 let error_template = select_template!(false, false, admin_ctx.language); 208 209 if form.did == admin_ctx.admin_handle.did { 210 return contextual_error!( 211 admin_ctx.web_context, 212 admin_ctx.language, 213 error_template, 214 template_context! { 215 message => "You cannot nuke your own identity." 216 }, 217 "You cannot nuke your own identity." 218 ); 219 } 220 221 if let Err(err) = handle_nuke( 222 &admin_ctx.web_context.pool, 223 &form.did, 224 &admin_ctx.admin_handle.did, 225 ) 226 .await 227 { 228 return contextual_error!( 229 admin_ctx.web_context, 230 admin_ctx.language, 231 error_template, 232 template_context! {}, 233 err 234 ); 235 } 236 237 if hx_request { 238 let hx_redirect = HxRedirect::from("/admin/identity_profiles"); 239 Ok((StatusCode::OK, hx_redirect, "").into_response()) 240 } else { 241 Ok(Redirect::to("/admin/identity_profiles").into_response()) 242 } 243}