The smokesignal.events web application
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 273 lines 8.2 kB view raw
1use anyhow::Result; 2use axum::{ 3 Form, 4 extract::Query, 5 response::{IntoResponse, Redirect}, 6}; 7use axum_template::RenderHtml; 8use minijinja::context as template_context; 9use serde::Deserialize; 10 11use crate::{ 12 contextual_error, 13 http::{ 14 context::{AdminRequestContext, admin_template_context}, 15 errors::WebError, 16 pagination::{Pagination, PaginationView}, 17 }, 18 profile_index::ProfileIndexManager, 19 select_template, 20}; 21 22#[derive(Debug, Deserialize)] 23pub(crate) struct SearchIndexQuery { 24 #[serde(default)] 25 query: Option<String>, 26} 27 28#[derive(Debug, Deserialize)] 29pub(crate) struct DeleteIndexedProfileForm { 30 aturi: String, 31} 32 33pub(crate) async fn handle_admin_profile_index( 34 admin_ctx: AdminRequestContext, 35 pagination: Query<Pagination>, 36 search_query: Query<SearchIndexQuery>, 37) -> Result<impl IntoResponse, WebError> { 38 let canonical_url = format!( 39 "https://{}/admin/search-index/profiles", 40 admin_ctx.web_context.config.external_base 41 ); 42 let default_context = 43 admin_template_context(&admin_ctx, &canonical_url, "search-index-profiles"); 44 45 let render_template = select_template!("admin_profile_index", false, false, admin_ctx.language); 46 let error_template = select_template!(false, false, admin_ctx.language); 47 48 // Check if OpenSearch is enabled 49 let opensearch_endpoint = match admin_ctx.web_context.config.opensearch_endpoint.as_ref() { 50 Some(endpoint) => endpoint, 51 None => { 52 return contextual_error!( 53 admin_ctx.web_context, 54 admin_ctx.language, 55 error_template, 56 default_context, 57 anyhow::anyhow!("OpenSearch is not enabled") 58 ); 59 } 60 }; 61 62 // Create profile index manager 63 let manager = match ProfileIndexManager::new(opensearch_endpoint) { 64 Ok(m) => m, 65 Err(err) => { 66 return contextual_error!( 67 admin_ctx.web_context, 68 admin_ctx.language, 69 error_template, 70 default_context, 71 err 72 ); 73 } 74 }; 75 76 // Get index stats 77 let stats = match manager.get_stats().await { 78 Ok(s) => s, 79 Err(err) => { 80 return contextual_error!( 81 admin_ctx.web_context, 82 admin_ctx.language, 83 error_template, 84 default_context, 85 err 86 ); 87 } 88 }; 89 90 let (page, page_size) = pagination.admin_clamped(); 91 92 // Get profiles (either search or list) 93 let (total_count, mut profiles) = if let Some(ref q) = search_query.query { 94 if !q.is_empty() { 95 match manager.search_indexed_profiles(q, page, page_size).await { 96 Ok(result) => result, 97 Err(err) => { 98 return contextual_error!( 99 admin_ctx.web_context, 100 admin_ctx.language, 101 error_template, 102 default_context, 103 err 104 ); 105 } 106 } 107 } else { 108 match manager.list_indexed_profiles(page, page_size).await { 109 Ok(result) => result, 110 Err(err) => { 111 return contextual_error!( 112 admin_ctx.web_context, 113 admin_ctx.language, 114 error_template, 115 default_context, 116 err 117 ); 118 } 119 } 120 } 121 } else { 122 match manager.list_indexed_profiles(page, page_size).await { 123 Ok(result) => result, 124 Err(err) => { 125 return contextual_error!( 126 admin_ctx.web_context, 127 admin_ctx.language, 128 error_template, 129 default_context, 130 err 131 ); 132 } 133 } 134 }; 135 136 // Build query params for pagination 137 let mut params: Vec<(&str, String)> = vec![]; 138 if let Some(ref q) = search_query.query 139 && !q.is_empty() 140 { 141 params.push(("query", q.clone())); 142 } 143 144 let params_refs: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect(); 145 146 let pagination_view = PaginationView::new(page_size, profiles.len() as i64, page, params_refs); 147 148 if profiles.len() > page_size as usize { 149 profiles.truncate(page_size as usize); 150 } 151 152 Ok(RenderHtml( 153 &render_template, 154 admin_ctx.web_context.engine.clone(), 155 template_context! { ..default_context, ..template_context! { 156 profiles, 157 total_count, 158 index_stats => stats, 159 search_query => search_query.query.clone().unwrap_or_default(), 160 pagination => pagination_view, 161 }}, 162 ) 163 .into_response()) 164} 165 166pub(crate) async fn handle_admin_profile_index_delete( 167 admin_ctx: AdminRequestContext, 168 Form(form): Form<DeleteIndexedProfileForm>, 169) -> Result<impl IntoResponse, WebError> { 170 let error_template = select_template!(false, false, admin_ctx.language); 171 172 // Check if OpenSearch is enabled 173 let opensearch_endpoint = match admin_ctx.web_context.config.opensearch_endpoint.as_ref() { 174 Some(endpoint) => endpoint, 175 None => { 176 return contextual_error!( 177 admin_ctx.web_context, 178 admin_ctx.language, 179 error_template, 180 template_context! {}, 181 anyhow::anyhow!("OpenSearch is not enabled") 182 ); 183 } 184 }; 185 186 // Create profile index manager 187 let manager = match ProfileIndexManager::new(opensearch_endpoint) { 188 Ok(m) => m, 189 Err(err) => { 190 return contextual_error!( 191 admin_ctx.web_context, 192 admin_ctx.language, 193 error_template, 194 template_context! {}, 195 err 196 ); 197 } 198 }; 199 200 // Delete the indexed profile 201 if let Err(err) = manager.delete_indexed_profile(&form.aturi).await { 202 return contextual_error!( 203 admin_ctx.web_context, 204 admin_ctx.language, 205 error_template, 206 template_context! {}, 207 err 208 ); 209 } 210 211 Ok(Redirect::to("/admin/search-index/profiles").into_response()) 212} 213 214pub(crate) async fn handle_admin_profile_index_rebuild( 215 admin_ctx: AdminRequestContext, 216) -> Result<impl IntoResponse, WebError> { 217 let error_template = select_template!(false, false, admin_ctx.language); 218 219 // Check if OpenSearch is enabled 220 let opensearch_endpoint = match admin_ctx.web_context.config.opensearch_endpoint.as_ref() { 221 Some(endpoint) => endpoint, 222 None => { 223 return contextual_error!( 224 admin_ctx.web_context, 225 admin_ctx.language, 226 error_template, 227 template_context! {}, 228 anyhow::anyhow!("OpenSearch is not enabled") 229 ); 230 } 231 }; 232 233 // Create profile index manager 234 let manager = match ProfileIndexManager::new(opensearch_endpoint) { 235 Ok(m) => m, 236 Err(err) => { 237 return contextual_error!( 238 admin_ctx.web_context, 239 admin_ctx.language, 240 error_template, 241 template_context! {}, 242 err 243 ); 244 } 245 }; 246 247 // Rebuild the index 248 let indexed_count = match manager 249 .rebuild_index( 250 &admin_ctx.web_context.pool, 251 admin_ctx.web_context.identity_resolver.clone(), 252 ) 253 .await 254 { 255 Ok(count) => count, 256 Err(err) => { 257 return contextual_error!( 258 admin_ctx.web_context, 259 admin_ctx.language, 260 error_template, 261 template_context! {}, 262 err 263 ); 264 } 265 }; 266 267 tracing::info!( 268 "Profile search index rebuilt: {} profiles indexed", 269 indexed_count 270 ); 271 272 Ok(Redirect::to("/admin/search-index/profiles").into_response()) 273}