diff --git a/src/atproto/lexicon/events_smokesignal_calendar_event.rs b/src/atproto/lexicon/events_smokesignal_calendar_event.rs index 17cc404..4d670ed 100644 --- a/src/atproto/lexicon/events_smokesignal_calendar_event.rs +++ b/src/atproto/lexicon/events_smokesignal_calendar_event.rs @@ -143,9 +143,10 @@ mod tests { } = &event_response.value; assert_eq!(name, "Pigeons Playing Ping Pong @ Neptune Theatre"); - assert!(text - .as_ref() - .is_some_and(|value| value == "Pigeons Playing Ping Pong @ Neptune Theatre")); + assert!( + text.as_ref() + .is_some_and(|value| value == "Pigeons Playing Ping Pong @ Neptune Theatre") + ); // Verify datetime fields are present and correctly parsed assert!(starts_at.is_some(), "Expected starts_at to be present"); diff --git a/src/bin/crypto.rs b/src/bin/crypto.rs index f1853f9..1dee7de 100644 --- a/src/bin/crypto.rs +++ b/src/bin/crypto.rs @@ -1,15 +1,17 @@ use std::env; -use base64::{engine::general_purpose, Engine as _}; +use base64::{Engine as _, engine::general_purpose}; use rand::RngCore; fn main() { let mut rng = rand::thread_rng(); - env::args().for_each(|arg| if arg.as_str() == "key" { - let mut key: [u8; 64] = [0; 64]; - rng.fill_bytes(&mut key); - let encoded: String = general_purpose::STANDARD_NO_PAD.encode(key); - println!("{encoded}"); + env::args().for_each(|arg| { + if arg.as_str() == "key" { + let mut key: [u8; 64] = [0; 64]; + rng.fill_bytes(&mut key); + let encoded: String = general_purpose::STANDARD_NO_PAD.encode(key); + println!("{encoded}"); + } }); } diff --git a/src/bin/smokesignal.rs b/src/bin/smokesignal.rs index 963a5eb..1edaa26 100644 --- a/src/bin/smokesignal.rs +++ b/src/bin/smokesignal.rs @@ -1,6 +1,6 @@ use anyhow::Result; use atproto_identity::key::identify_key; -use atproto_identity::resolve::{create_resolver, IdentityResolver, InnerIdentityResolver}; +use atproto_identity::resolve::{IdentityResolver, InnerIdentityResolver, create_resolver}; use atproto_oauth_axum::state::OAuthClientConfig; use smokesignal::{ http::{ diff --git a/src/config.rs b/src/config.rs index 7d48aa1..54d590b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,7 @@ use anyhow::{Context, Result}; -use atproto_identity::key::{identify_key, to_public, KeyData, KeyType}; +use atproto_identity::key::{KeyData, KeyType, identify_key, to_public}; use axum_extra::extract::cookie::Key; -use base64::{engine::general_purpose, Engine as _}; +use base64::{Engine as _, engine::general_purpose}; use ordermap::OrderMap; use crate::config_errors::ConfigError; diff --git a/src/config_errors.rs b/src/config_errors.rs index c39f7a8..9d44ac0 100644 --- a/src/config_errors.rs +++ b/src/config_errors.rs @@ -130,7 +130,9 @@ pub enum ConfigError { /// /// This error occurs when oauth_backend is set to "aip" but /// required AIP configuration values are missing. - #[error("error-config-18 When oauth_backend is 'aip', AIP_HOSTNAME, AIP_CLIENT_ID, and AIP_CLIENT_SECRET must all be set")] + #[error( + "error-config-18 When oauth_backend is 'aip', AIP_HOSTNAME, AIP_CLIENT_ID, and AIP_CLIENT_SECRET must all be set" + )] AipConfigurationIncomplete, /// Error when oauth_backend has an invalid value. diff --git a/src/http/cache_countries.rs b/src/http/cache_countries.rs index aae46af..93bf788 100644 --- a/src/http/cache_countries.rs +++ b/src/http/cache_countries.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Result}; +use anyhow::{Result, anyhow}; use once_cell::sync::OnceCell; use std::{collections::BTreeMap, sync::Arc}; diff --git a/src/http/errors/middleware_errors.rs b/src/http/errors/middleware_errors.rs index 38701c3..fed02c2 100644 --- a/src/http/errors/middleware_errors.rs +++ b/src/http/errors/middleware_errors.rs @@ -25,20 +25,6 @@ pub(crate) enum WebSessionError { SerializeFailed(serde_json::Error), } -/// Represents errors that can occur during authentication middleware operations. -/// -/// These errors typically happen in the authentication middleware layer when -/// processing requests, including cryptographic operations and session validation. -#[derive(Debug, Error)] -pub(crate) enum AuthMiddlewareError { - /// Error when content signing fails. - /// - /// This error occurs when the authentication middleware attempts to - /// cryptographically sign content but the operation fails. - #[error("error-authmiddleware-1 Unable to sign content: {0:?}")] - SigningFailed(anyhow::Error), -} - #[derive(Debug, Error)] pub(crate) enum MiddlewareAuthError { #[error("error-middleware-auth-1 Access Denied: {0}")] @@ -49,9 +35,6 @@ pub(crate) enum MiddlewareAuthError { #[error("error-middleware-auth-3 Unhandled Auth Error: {0:?}")] Anyhow(#[from] anyhow::Error), - - #[error(transparent)] - AuthError(#[from] AuthMiddlewareError), } impl IntoResponse for MiddlewareAuthError { diff --git a/src/http/errors/migrate_rsvp_error.rs b/src/http/errors/migrate_rsvp_error.rs index 95139ba..29fd9be 100644 --- a/src/http/errors/migrate_rsvp_error.rs +++ b/src/http/errors/migrate_rsvp_error.rs @@ -11,7 +11,9 @@ pub(crate) enum MigrateRsvpError { /// This error occurs when attempting to migrate an RSVP with a status /// that doesn't match one of the expected values ('going', 'interested', /// or 'notgoing'). - #[error("error-migrate-rsvp-1 Invalid RSVP status: {0}. Expected 'going', 'interested', or 'notgoing'.")] + #[error( + "error-migrate-rsvp-1 Invalid RSVP status: {0}. Expected 'going', 'interested', or 'notgoing'." + )] InvalidRsvpStatus(String), /// Error when a user is not authorized to migrate an RSVP. diff --git a/src/http/errors/mod.rs b/src/http/errors/mod.rs index 8a6f5d2..5cc01a5 100644 --- a/src/http/errors/mod.rs +++ b/src/http/errors/mod.rs @@ -21,7 +21,7 @@ pub(crate) use edit_event_error::EditEventError; pub(crate) use event_view_errors::EventViewError; pub(crate) use import_error::ImportError; pub(crate) use login_error::LoginError; -pub(crate) use middleware_errors::{AuthMiddlewareError, WebSessionError}; +pub(crate) use middleware_errors::WebSessionError; pub(crate) use migrate_event_error::MigrateEventError; pub(crate) use migrate_rsvp_error::MigrateRsvpError; pub(crate) use rsvp_error::RSVPError; diff --git a/src/http/event_view.rs b/src/http/event_view.rs index 60db761..892155b 100644 --- a/src/http/event_view.rs +++ b/src/http/event_view.rs @@ -17,13 +17,13 @@ use crate::{ }, http::utils::truncate_text, storage::{ + StoragePool, errors::StorageError, event::{ count_event_rsvps, extract_event_details, get_event_rsvp_counts, model::{Event, EventWithRole}, }, identity_profile::{handles_by_did, model::IdentityProfile}, - StoragePool, }, }; diff --git a/src/http/handle_admin_denylist.rs b/src/http/handle_admin_denylist.rs index 2ca6ad2..bc6fa39 100644 --- a/src/http/handle_admin_denylist.rs +++ b/src/http/handle_admin_denylist.rs @@ -1,8 +1,8 @@ use anyhow::Result; use axum::{ + Form, extract::Query, response::{IntoResponse, Redirect}, - Form, }; use axum_template::RenderHtml; use minijinja::context as template_context; @@ -12,7 +12,7 @@ use std::borrow::Cow; use crate::{ contextual_error, http::{ - context::{admin_template_context, AdminRequestContext}, + context::{AdminRequestContext, admin_template_context}, errors::WebError, pagination::{Pagination, PaginationView}, }, diff --git a/src/http/handle_admin_handles.rs b/src/http/handle_admin_handles.rs index 24797c4..d0e0c8a 100644 --- a/src/http/handle_admin_handles.rs +++ b/src/http/handle_admin_handles.rs @@ -11,7 +11,7 @@ use minijinja::context as template_context; use crate::{ contextual_error, http::{ - context::{admin_template_context, AdminRequestContext}, + context::{AdminRequestContext, admin_template_context}, errors::WebError, pagination::{Pagination, PaginationView}, }, diff --git a/src/http/handle_admin_import_event.rs b/src/http/handle_admin_import_event.rs index 5e6efbf..e776b7e 100644 --- a/src/http/handle_admin_import_event.rs +++ b/src/http/handle_admin_import_event.rs @@ -16,7 +16,7 @@ use crate::{ }, contextual_error, http::{ - context::{admin_template_context, AdminRequestContext}, + context::{AdminRequestContext, admin_template_context}, errors::{AdminImportEventError, CommonError, LoginError, WebError}, }, select_template, diff --git a/src/http/handle_admin_import_rsvp.rs b/src/http/handle_admin_import_rsvp.rs index 160e2c4..b4284ae 100644 --- a/src/http/handle_admin_import_rsvp.rs +++ b/src/http/handle_admin_import_rsvp.rs @@ -13,16 +13,16 @@ use urlencoding; use crate::{ atproto::lexicon::{ community::lexicon::calendar::rsvp::{ - Rsvp as CommunityRsvpLexicon, RsvpStatus as CommunityRsvpStatusLexicon, - NSID as COMMUNITY_RSVP_NSID, + NSID as COMMUNITY_RSVP_NSID, Rsvp as CommunityRsvpLexicon, + RsvpStatus as CommunityRsvpStatusLexicon, }, events::smokesignal::calendar::rsvp::{ - Rsvp as SmokesignalRsvpLexicon, NSID as SMOKESIGNAL_RSVP_NSID, + NSID as SMOKESIGNAL_RSVP_NSID, Rsvp as SmokesignalRsvpLexicon, }, }, contextual_error, http::{ - context::{admin_template_context, AdminRequestContext}, + context::{AdminRequestContext, admin_template_context}, errors::{AdminImportRsvpError, CommonError, LoginError, WebError}, }, select_template, diff --git a/src/http/handle_admin_index.rs b/src/http/handle_admin_index.rs index 153c4bb..ef901eb 100644 --- a/src/http/handle_admin_index.rs +++ b/src/http/handle_admin_index.rs @@ -3,7 +3,7 @@ use axum::response::IntoResponse; use axum_template::RenderHtml; use minijinja::context as template_context; -use crate::http::context::{admin_template_context, AdminRequestContext}; +use crate::http::context::{AdminRequestContext, admin_template_context}; use super::errors::WebError; diff --git a/src/http/handle_create_event.rs b/src/http/handle_create_event.rs index 0ba0fa3..e4074d3 100644 --- a/src/http/handle_create_event.rs +++ b/src/http/handle_create_event.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use anyhow::Result; use axum::extract::State; use axum::response::IntoResponse; @@ -11,6 +10,7 @@ use chrono::Utc; use http::Method; use http::StatusCode; use minijinja::context as template_context; +use std::collections::HashMap; use crate::atproto::auth::{ create_dpop_auth_from_aip_session, create_dpop_auth_from_oauth_session, @@ -54,7 +54,7 @@ pub(crate) async fn handle_create_event( HxBoosted(hx_boosted): HxBoosted, Form(mut build_event_form): Form, ) -> Result { - let current_handle = auth.require(&web_context.config, "/event")?; + let current_handle = auth.require("/event")?; let is_development = cfg!(debug_assertions); diff --git a/src/http/handle_create_rsvp.rs b/src/http/handle_create_rsvp.rs index 9ee1005..64fb38b 100644 --- a/src/http/handle_create_rsvp.rs +++ b/src/http/handle_create_rsvp.rs @@ -16,7 +16,7 @@ use crate::config::OAuthBackendConfig; use crate::{ atproto::lexicon::{ com::atproto::repo::StrongRef, - community::lexicon::calendar::rsvp::{Rsvp, RsvpStatus, NSID}, + community::lexicon::calendar::rsvp::{NSID, Rsvp, RsvpStatus}, }, contextual_error, http::{ @@ -28,11 +28,9 @@ use crate::{ utils::url_from_aturi, }, select_template, - storage::{ - event::{rsvp_insert_with_metadata, RsvpInsertParams} - }, + storage::event::{RsvpInsertParams, rsvp_insert_with_metadata}, }; -use atproto_client::com::atproto::repo::{put_record, PutRecordRequest, PutRecordResponse}; +use atproto_client::com::atproto::repo::{PutRecordRequest, PutRecordResponse, put_record}; pub(crate) async fn handle_create_rsvp( method: Method, @@ -43,10 +41,13 @@ pub(crate) async fn handle_create_rsvp( HxBoosted(hx_boosted): HxBoosted, Form(mut build_rsvp_form): Form, ) -> Result { - let current_handle = auth.require(&web_context.config, "/rsvp")?; + let current_handle = auth.require("/rsvp")?; // Check if user has email address set - let identity_has_email = current_handle.email.as_ref().is_some_and(|value| !value.is_empty()); + let identity_has_email = current_handle + .email + .as_ref() + .is_some_and(|value| !value.is_empty()); let default_context = template_context! { current_handle, diff --git a/src/http/handle_delete_event.rs b/src/http/handle_delete_event.rs index ba974ff..40594f8 100644 --- a/src/http/handle_delete_event.rs +++ b/src/http/handle_delete_event.rs @@ -6,18 +6,18 @@ use http::StatusCode; use minijinja::context as template_context; use serde::{Deserialize, Serialize}; -use atproto_client::com::atproto::repo::{delete_record, DeleteRecordRequest}; +use atproto_client::com::atproto::repo::{DeleteRecordRequest, delete_record}; use crate::{ atproto::{ - auth::{create_dpop_auth_from_oauth_session, create_dpop_auth_from_aip_session}, + auth::{create_dpop_auth_from_aip_session, create_dpop_auth_from_oauth_session}, lexicon::community::lexicon::calendar::event::NSID as LexiconCommunityEventNSID, }, + config::OAuthBackendConfig, contextual_error, http::{context::UserRequestContext, errors::WebError, middleware_auth::Auth}, select_template, - storage::{event::{event_delete, event_exists}}, - config::OAuthBackendConfig, + storage::event::{event_delete, event_exists}, }; #[derive(Debug, Deserialize, Serialize)] @@ -80,62 +80,60 @@ pub(crate) async fn handle_delete_event( if form.confirm.as_deref() == Some("true") { // Create DPoP authentication based on auth type let dpop_auth = match &ctx.auth { - Auth::Pds { session, .. } => { - match create_dpop_auth_from_oauth_session(session) { - Ok(auth) => auth, - Err(err) => { - tracing::error!("Failed to create DPoP auth from OAuth session: {}", err); - return contextual_error!( - ctx.web_context, - ctx.language, - error_template, - default_context, - err, - StatusCode::INTERNAL_SERVER_ERROR - ); - } + Auth::Pds { session, .. } => match create_dpop_auth_from_oauth_session(session) { + Ok(auth) => auth, + Err(err) => { + tracing::error!("Failed to create DPoP auth from OAuth session: {}", err); + return contextual_error!( + ctx.web_context, + ctx.language, + error_template, + default_context, + err, + StatusCode::INTERNAL_SERVER_ERROR + ); } - } - Auth::Aip { .. } => { - match &ctx.web_context.config.oauth_backend { - OAuthBackendConfig::AIP { hostname, .. } => { - let access_token = match &ctx.auth { - Auth::Aip { access_token, .. } => access_token, - _ => unreachable!("We already matched on Auth::Aip"), - }; - - match create_dpop_auth_from_aip_session( - &ctx.web_context.http_client, - hostname, - access_token, - ).await { - Ok(auth) => auth, - Err(err) => { - tracing::error!("Failed to create DPoP auth from AIP session: {}", err); - return contextual_error!( - ctx.web_context, - ctx.language, - error_template, - default_context, - err, - StatusCode::INTERNAL_SERVER_ERROR - ); - } + }, + Auth::Aip { .. } => match &ctx.web_context.config.oauth_backend { + OAuthBackendConfig::AIP { hostname, .. } => { + let access_token = match &ctx.auth { + Auth::Aip { access_token, .. } => access_token, + _ => unreachable!("We already matched on Auth::Aip"), + }; + + match create_dpop_auth_from_aip_session( + &ctx.web_context.http_client, + hostname, + access_token, + ) + .await + { + Ok(auth) => auth, + Err(err) => { + tracing::error!("Failed to create DPoP auth from AIP session: {}", err); + return contextual_error!( + ctx.web_context, + ctx.language, + error_template, + default_context, + err, + StatusCode::INTERNAL_SERVER_ERROR + ); } } - _ => { - tracing::error!("AIP auth found but OAuth backend is not AIP"); - return contextual_error!( - ctx.web_context, - ctx.language, - error_template, - default_context, - anyhow!("Authentication configuration mismatch"), - StatusCode::INTERNAL_SERVER_ERROR - ); - } } - } + _ => { + tracing::error!("AIP auth found but OAuth backend is not AIP"); + return contextual_error!( + ctx.web_context, + ctx.language, + error_template, + default_context, + anyhow!("Authentication configuration mismatch"), + StatusCode::INTERNAL_SERVER_ERROR + ); + } + }, Auth::Unauthenticated => { // This should not happen due to the check above return Ok(StatusCode::FORBIDDEN.into_response()); @@ -156,7 +154,9 @@ pub(crate) async fn handle_delete_event( &dpop_auth, ¤t_handle.pds, delete_record_request, - ).await { + ) + .await + { Ok(_) => { tracing::info!("Successfully deleted event from PDS: {}", lookup_aturi); } diff --git a/src/http/handle_edit_event.rs b/src/http/handle_edit_event.rs index 59f5be2..1ba6c26 100644 --- a/src/http/handle_edit_event.rs +++ b/src/http/handle_edit_event.rs @@ -15,8 +15,8 @@ use crate::http::middleware_auth::Auth; use crate::{ atproto::{ lexicon::community::lexicon::calendar::event::{ - Event as LexiconCommunityEvent, EventLink, EventLocation, Mode, NamedUri, Status, - NSID as LexiconCommunityEventNSID, + Event as LexiconCommunityEvent, EventLink, EventLocation, Mode, + NSID as LexiconCommunityEventNSID, NamedUri, Status, }, lexicon::community::lexicon::location::Address, }, @@ -26,7 +26,7 @@ use crate::{ http::errors::{CommonError, WebError}, http::event_form::BuildLocationForm, http::event_form::{BuildEventContentState, BuildEventForm, BuildLinkForm, BuildStartsForm}, - http::location_edit_status::{check_location_edit_status, LocationEditStatus}, + http::location_edit_status::{LocationEditStatus, check_location_edit_status}, http::timezones::supported_timezones, http::utils::url_from_aturi, select_template, @@ -35,7 +35,7 @@ use crate::{ identity_profile::{handle_for_did, handle_for_handle}, }, }; -use atproto_client::com::atproto::repo::{put_record, PutRecordRequest, PutRecordResponse}; +use atproto_client::com::atproto::repo::{PutRecordRequest, PutRecordResponse, put_record}; pub(crate) async fn handle_edit_event( ctx: UserRequestContext, @@ -45,7 +45,7 @@ pub(crate) async fn handle_edit_event( Path((handle_slug, event_rkey)): Path<(String, String)>, Form(mut build_event_form): Form, ) -> Result { - let current_handle = ctx.auth.require(&ctx.web_context.config, "/")?; + let current_handle = ctx.auth.require("/")?; let default_context = template_context! { current_handle, diff --git a/src/http/handle_export_rsvps.rs b/src/http/handle_export_rsvps.rs index 51814d9..35db355 100644 --- a/src/http/handle_export_rsvps.rs +++ b/src/http/handle_export_rsvps.rs @@ -2,11 +2,8 @@ use std::str::FromStr; use anyhow::Result; use atproto_record::aturi::ATURI; -use axum::{ - extract::Path, - response::IntoResponse, -}; -use http::{header::CONTENT_DISPOSITION, HeaderValue}; +use axum::{extract::Path, response::IntoResponse}; +use http::{HeaderValue, header::CONTENT_DISPOSITION}; use crate::atproto::lexicon::community::lexicon::calendar::event::NSID as LexiconCommunityEventNSID; use crate::http::context::UserRequestContext; @@ -16,20 +13,20 @@ use crate::storage::event::{event_exists, get_event_rsvps_for_export}; /// Generate a CSV string from RSVP export data fn generate_csv(rsvps: Vec) -> String { let mut csv = String::new(); - + // Add CSV header csv.push_str("event,rsvp,did,handle,status,created_at,email\n"); - + // Add data rows for rsvp in rsvps { let created_at_str = match rsvp.created_at { Some(dt) => dt.to_rfc3339(), None => String::new(), }; - + let handle_str = rsvp.handle.unwrap_or_default(); let email_str = rsvp.email.unwrap_or_default(); - + // Escape CSV fields that might contain commas or quotes let event = escape_csv_field(&rsvp.event_aturi); let rsvp_aturi = escape_csv_field(&rsvp.rsvp_aturi); @@ -38,13 +35,13 @@ fn generate_csv(rsvps: Vec) -> String { let status = escape_csv_field(&rsvp.status); let created_at = escape_csv_field(&created_at_str); let email = escape_csv_field(&email_str); - + csv.push_str(&format!( "{},{},{},{},{},{},{}\n", event, rsvp_aturi, did, handle, status, created_at, email )); } - + csv } @@ -63,9 +60,12 @@ fn generate_filename(event_aturi: &str) -> String { // Extract the DID, collection, and record key from the AT-URI // Format: at://did:plc:example/collection.nsid/recordkey if let Ok(aturi) = ATURI::from_str(event_aturi) { - return format!("{}-{}-{}.csv", aturi.authority, aturi.collection, aturi.record_key); + return format!( + "{}-{}-{}.csv", + aturi.authority, aturi.collection, aturi.record_key + ); } - + // Fallback if parsing fails "rsvp-export.csv".to_string() } @@ -75,11 +75,13 @@ pub(crate) async fn handle_export_rsvps( Path((handle_slug, event_rkey)): Path<(String, String)>, ) -> Result { // Require authentication - let current_handle = ctx.auth.require(&ctx.web_context.config, "/")?; + let current_handle = ctx.auth.require("/")?; // Check if the current user is the event organizer if handle_slug != current_handle.did { - return Err(WebError::from(crate::http::errors::CommonError::NotAuthorized)); + return Err(WebError::from( + crate::http::errors::CommonError::NotAuthorized, + )); } let lookup_aturi = format!( @@ -90,11 +92,15 @@ pub(crate) async fn handle_export_rsvps( // Get the event let event_exists = event_exists(&ctx.web_context.pool, &lookup_aturi).await; if let Err(_) = event_exists { - return Err(WebError::from(crate::http::errors::CommonError::NotAuthorized)); + return Err(WebError::from( + crate::http::errors::CommonError::NotAuthorized, + )); } if !event_exists.unwrap() { - return Err(WebError::from(crate::http::errors::CommonError::NotAuthorized)); + return Err(WebError::from( + crate::http::errors::CommonError::NotAuthorized, + )); } // Get all RSVPs for the event with detailed information @@ -113,8 +119,14 @@ pub(crate) async fn handle_export_rsvps( let response = ( [ - (http::header::CONTENT_TYPE, HeaderValue::from_static("text/csv; charset=utf-8")), - (CONTENT_DISPOSITION, HeaderValue::from_str(&content_disposition).unwrap()), + ( + http::header::CONTENT_TYPE, + HeaderValue::from_static("text/csv; charset=utf-8"), + ), + ( + CONTENT_DISPOSITION, + HeaderValue::from_str(&content_disposition).unwrap(), + ), ], csv_content, ); @@ -138,8 +150,10 @@ mod tests { #[test] fn test_generate_filename() { - let aturi = "at://did:plc:cbkjy5n7bk3ax2wplmtjofq2/community.lexicon.calendar.event/3ltbbcuygrc2c"; - let expected = "did:plc:cbkjy5n7bk3ax2wplmtjofq2-community.lexicon.calendar.event-3ltbbcuygrc2c.csv"; + let aturi = + "at://did:plc:cbkjy5n7bk3ax2wplmtjofq2/community.lexicon.calendar.event/3ltbbcuygrc2c"; + let expected = + "did:plc:cbkjy5n7bk3ax2wplmtjofq2-community.lexicon.calendar.event-3ltbbcuygrc2c.csv"; assert_eq!(generate_filename(aturi), expected); } @@ -169,10 +183,10 @@ mod tests { let csv = generate_csv(rsvps); let lines: Vec<&str> = csv.lines().collect(); - + assert_eq!(lines.len(), 3); // Header + 2 data rows assert_eq!(lines[0], "event,rsvp,did,handle,status,created_at,email"); assert!(lines[1].contains("user1@example.com")); assert!(lines[2].contains("interested")); } -} \ No newline at end of file +} diff --git a/src/http/handle_import.rs b/src/http/handle_import.rs index f49f027..f821500 100644 --- a/src/http/handle_import.rs +++ b/src/http/handle_import.rs @@ -18,15 +18,15 @@ use crate::{ community::lexicon::calendar::{ event::{Event as LexiconCommunityEvent, NSID as LEXICON_COMMUNITY_EVENT_NSID}, rsvp::{ - Rsvp as LexiconCommunityRsvp, RsvpStatus as LexiconCommunityRsvpStatus, - NSID as LEXICON_COMMUNITY_RSVP_NSID, + NSID as LEXICON_COMMUNITY_RSVP_NSID, Rsvp as LexiconCommunityRsvp, + RsvpStatus as LexiconCommunityRsvpStatus, }, }, events::smokesignal::calendar::{ event::{Event as SmokeSignalEvent, NSID as SMOKESIGNAL_EVENT_NSID}, rsvp::{ - Rsvp as SmokeSignalRsvp, RsvpStatus as SmokeSignalRsvpStatus, - NSID as SMOKESIGNAL_RSVP_NSID, + NSID as SMOKESIGNAL_RSVP_NSID, Rsvp as SmokeSignalRsvp, + RsvpStatus as SmokeSignalRsvpStatus, }, }, }, @@ -40,7 +40,7 @@ use crate::{ select_template, storage::event::{event_insert_with_metadata, rsvp_insert_with_metadata}, }; -use atproto_client::com::atproto::repo::{list_records, ListRecordsParams}; +use atproto_client::com::atproto::repo::{ListRecordsParams, list_records}; pub(crate) async fn handle_import( State(web_context): State, @@ -49,7 +49,7 @@ pub(crate) async fn handle_import( HxRequest(hx_request): HxRequest, HxBoosted(hx_boosted): HxBoosted, ) -> Result { - let current_handle = auth.require(&web_context.config, "/import")?; + let current_handle = auth.require("/import")?; let default_context = template_context! { current_handle, @@ -185,7 +185,7 @@ pub(crate) async fn handle_import_submit( error_template, template_context! {}, ImportError::FailedToListCommunityEvents(err.to_string()) - ) + ); } } } @@ -268,7 +268,7 @@ pub(crate) async fn handle_import_submit( error_template, template_context! {}, ImportError::FailedToListCommunityRSVPs(err.to_string()) - ) + ); } } } @@ -336,7 +336,7 @@ pub(crate) async fn handle_import_submit( error_template, template_context! {}, ImportError::FailedToListSmokesignalEvents(err.to_string()) - ) + ); } } } @@ -415,7 +415,7 @@ pub(crate) async fn handle_import_submit( error_template, template_context! {}, ImportError::FailedToListSmokesignalRSVPs(err.to_string()) - ) + ); } } } @@ -426,7 +426,7 @@ pub(crate) async fn handle_import_submit( error_template, template_context! {}, ImportError::UnsupportedCollectionType(collection.clone()) - ) + ); } }; diff --git a/src/http/handle_index.rs b/src/http/handle_index.rs index 3194b58..d082170 100644 --- a/src/http/handle_index.rs +++ b/src/http/handle_index.rs @@ -24,10 +24,7 @@ use crate::{ utils::url_from_aturi, }, select_template, - storage::{ - event::activity_list_recent, - identity_profile::handles_by_did, - }, + storage::{event::activity_list_recent, identity_profile::handles_by_did}, }; #[derive(Deserialize, Serialize, PartialEq)] @@ -132,15 +129,15 @@ pub(crate) async fn handle_index( #[derive(Debug)] enum GroupedActivity<'a> { - Individual { - did: String, - activity: &'a crate::storage::event::model::ActivityItem + Individual { + did: String, + activity: &'a crate::storage::event::model::ActivityItem, }, - GroupedRsvp { - dids: Vec, + GroupedRsvp { + dids: Vec, first_activity: &'a crate::storage::event::model::ActivityItem, rsvp_statuses: Vec, - count: usize + count: usize, }, } @@ -155,7 +152,7 @@ pub(crate) async fn handle_index( let mut group_dids = vec![activity.did.clone()]; let mut group_statuses = vec![activity.rsvp_status.clone().unwrap_or_default()]; let mut j = i + 1; - + while j < activity_list.len() { let next_activity = activity_list[j]; if next_activity.activity_type == "rsvp" @@ -207,7 +204,11 @@ pub(crate) async fn handle_index( let organizer_did = extract_did_from_aturi(&activity.event_aturi); needed_dids.insert(organizer_did); } - GroupedActivity::GroupedRsvp { dids, first_activity, .. } => { + GroupedActivity::GroupedRsvp { + dids, + first_activity, + .. + } => { for did in dids { needed_dids.insert(did.clone()); } @@ -232,8 +233,9 @@ pub(crate) async fn handle_index( .map(|profile| profile.handle.clone()) .unwrap_or_else(|| did.clone()); - let event_url = url_from_aturi(&web_context.config.external_base, &activity.event_aturi) - .unwrap_or_else(|_| format!("/event/{}", activity.event_aturi)); + let event_url = + url_from_aturi(&web_context.config.external_base, &activity.event_aturi) + .unwrap_or_else(|_| format!("/event/{}", activity.event_aturi)); let event_organizer_did = extract_did_from_aturi(&activity.event_aturi); let event_organizer_handle = handles @@ -254,7 +256,12 @@ pub(crate) async fn handle_index( updated_at: activity.updated_at.to_rfc3339(), })); } - GroupedActivity::GroupedRsvp { dids, first_activity, rsvp_statuses, count } => { + GroupedActivity::GroupedRsvp { + dids, + first_activity, + rsvp_statuses, + count, + } => { let group_handles: Vec = dids .iter() .map(|did| { @@ -265,8 +272,11 @@ pub(crate) async fn handle_index( }) .collect(); - let event_url = url_from_aturi(&web_context.config.external_base, &first_activity.event_aturi) - .unwrap_or_else(|_| format!("/event/{}", first_activity.event_aturi)); + let event_url = url_from_aturi( + &web_context.config.external_base, + &first_activity.event_aturi, + ) + .unwrap_or_else(|_| format!("/event/{}", first_activity.event_aturi)); let event_organizer_did = extract_did_from_aturi(&first_activity.event_aturi); let event_organizer_handle = handles @@ -301,7 +311,8 @@ pub(crate) async fn handle_index( let params: Vec<(&str, &str)> = vec![("tab", &tab_name)]; - let pagination_view = PaginationView::new(page_size, activity_displays.len() as i64, page, params); + let pagination_view = + PaginationView::new(page_size, activity_displays.len() as i64, page, params); if activity_displays.len() > page_size as usize { activity_displays.truncate(page_size as usize); diff --git a/src/http/handle_migrate_event.rs b/src/http/handle_migrate_event.rs index 843cec8..1ab4a50 100644 --- a/src/http/handle_migrate_event.rs +++ b/src/http/handle_migrate_event.rs @@ -17,13 +17,13 @@ use crate::config::OAuthBackendConfig; use crate::{ atproto::lexicon::{ community::lexicon::calendar::event::{ - Event as CommunityEvent, EventLink, EventLocation as CommunityLocation, Mode, Status, - NSID as COMMUNITY_NSID, + Event as CommunityEvent, EventLink, EventLocation as CommunityLocation, Mode, + NSID as COMMUNITY_NSID, Status, }, community::lexicon::location, events::smokesignal::calendar::event::{ - Event as SmokeSignalEvent, Location as SmokeSignalLocation, PlaceLocation, - NSID as SMOKESIGNAL_NSID, + Event as SmokeSignalEvent, Location as SmokeSignalLocation, NSID as SMOKESIGNAL_NSID, + PlaceLocation, }, }, contextual_error, @@ -37,7 +37,7 @@ use crate::{ identity_profile::{handle_for_did, handle_for_handle, model::IdentityProfile}, }, }; -use atproto_client::com::atproto::repo::{put_record, PutRecordRequest, PutRecordResponse}; +use atproto_client::com::atproto::repo::{PutRecordRequest, PutRecordResponse, put_record}; pub(crate) async fn handle_migrate_event( State(web_context): State, @@ -47,7 +47,7 @@ pub(crate) async fn handle_migrate_event( HxRequest(hx_request): HxRequest, Path((handle_slug, event_rkey)): Path<(String, String)>, ) -> Result { - let current_handle = auth.require(&web_context.config, "/")?; + let current_handle = auth.require("/")?; // Configure templates let default_context = template_context! { @@ -262,7 +262,7 @@ pub(crate) async fn handle_migrate_event( error_template, default_context, MigrateEventError::DestinationExists, - StatusCode::CONFLICT + StatusCode::OK ); } diff --git a/src/http/handle_migrate_rsvp.rs b/src/http/handle_migrate_rsvp.rs index 161d958..6b32ddb 100644 --- a/src/http/handle_migrate_rsvp.rs +++ b/src/http/handle_migrate_rsvp.rs @@ -17,7 +17,7 @@ use crate::config::OAuthBackendConfig; use crate::{ atproto::lexicon::{ com::atproto::repo::StrongRef, - community::lexicon::calendar::rsvp::{Rsvp, RsvpStatus, NSID as RSVP_COLLECTION}, + community::lexicon::calendar::rsvp::{NSID as RSVP_COLLECTION, Rsvp, RsvpStatus}, events::smokesignal::calendar::event::NSID as EVENT_COLLECTION, }, contextual_error, @@ -33,7 +33,7 @@ use crate::{ identity_profile::{handle_for_did, handle_for_handle, model::IdentityProfile}, }, }; -use atproto_client::com::atproto::repo::{put_record, PutRecordRequest, PutRecordResponse}; +use atproto_client::com::atproto::repo::{PutRecordRequest, PutRecordResponse, put_record}; /// Migrates a user's RSVP from a legacy event to a standard event format. /// @@ -55,10 +55,7 @@ pub(crate) async fn handle_migrate_rsvp( Path((handle_slug, event_rkey)): Path<(String, String)>, ) -> Result { // Require user to be logged in - let current_handle = auth.require( - &web_context.config, - "/{handle_slug}/{event_rkey}/migrate-rsvp", - )?; + let current_handle = auth.require("/{handle_slug}/{event_rkey}/migrate-rsvp")?; let default_context = template_context! { language => language.to_string(), diff --git a/src/http/handle_oauth_aip_callback.rs b/src/http/handle_oauth_aip_callback.rs index 27b1780..b56f5f4 100644 --- a/src/http/handle_oauth_aip_callback.rs +++ b/src/http/handle_oauth_aip_callback.rs @@ -1,6 +1,10 @@ use std::collections::HashMap; -use crate::{config::OAuthBackendConfig, contextual_error, select_template, storage::identity_profile::{handle_for_did, identity_profile_set_email}}; +use crate::{ + config::OAuthBackendConfig, + contextual_error, select_template, + storage::identity_profile::{handle_for_did, identity_profile_set_email}, +}; use anyhow::{Context, Result, anyhow}; use axum::{ extract::State, @@ -126,7 +130,13 @@ pub(crate) async fn handle_oauth_callback( let identity_profile = handle_for_did(&web_context.pool, &oauth_request.did).await?; - let maybe_email = get_email_from_userinfo(&web_context.http_client, hostname, &identity_profile.did, &token_response.access_token).await; + let maybe_email = get_email_from_userinfo( + &web_context.http_client, + hostname, + &identity_profile.did, + &token_response.access_token, + ) + .await; let maybe_email = match maybe_email { Ok(value) => value, Err(err) => { @@ -138,7 +148,10 @@ pub(crate) async fn handle_oauth_callback( // Write the email address to the database if it already isn't in the database. // Only set if the identity_profile's email field is None (not even an empty string) if identity_profile.email.is_none() { - if let Err(err) = identity_profile_set_email(&web_context.pool, &oauth_request.did, Some(&email)).await { + if let Err(err) = + identity_profile_set_email(&web_context.pool, &oauth_request.did, Some(&email)) + .await + { tracing::error!(error = ?err, "Failed to set email from OAuth userinfo"); } } @@ -163,9 +176,8 @@ pub(crate) async fn handle_oauth_callback( let updated_jar = jar.add(cookie); // Retrieve destination from OAuth request before deleting it - let postgres_storage = crate::storage::atproto::PostgresOAuthRequestStorage::new( - web_context.pool.clone() - ); + let postgres_storage = + crate::storage::atproto::PostgresOAuthRequestStorage::new(web_context.pool.clone()); let destination = match postgres_storage.get_destination(&callback_state).await { Ok(Some(dest)) => dest, Ok(None) => "/".to_string(), diff --git a/src/http/handle_oauth_aip_login.rs b/src/http/handle_oauth_aip_login.rs index e751a51..a4cc005 100644 --- a/src/http/handle_oauth_aip_login.rs +++ b/src/http/handle_oauth_aip_login.rs @@ -1,10 +1,10 @@ -use anyhow::{anyhow, Result}; +use anyhow::{Result, anyhow}; use atproto_identity::resolve::IdentityResolver; use atproto_oauth::pkce::generate; use atproto_oauth::workflow::OAuthRequestState as AipOAuthRequestState; use atproto_oauth_aip::{ resources::oauth_authorization_server, - workflow::{oauth_init, OAuthClient}, + workflow::{OAuthClient, oauth_init}, }; use axum::response::Redirect; use axum::{extract::State, response::IntoResponse}; @@ -13,7 +13,7 @@ use axum_htmx::{HxBoosted, HxRedirect, HxRequest}; use axum_template::RenderHtml; use http::StatusCode; use minijinja::context as template_context; -use rand::{distributions::Alphanumeric, Rng}; +use rand::{Rng, distributions::Alphanumeric}; use serde::Deserialize; use crate::{ @@ -248,7 +248,7 @@ pub(crate) async fn handle_oauth_aip_login( if dest != "/" { // Create a direct instance to access the set_destination method let postgres_storage = crate::storage::atproto::PostgresOAuthRequestStorage::new( - web_context.pool.clone() + web_context.pool.clone(), ); if let Err(err) = postgres_storage.set_destination(&state, dest).await { tracing::error!(?err, "set_destination"); diff --git a/src/http/handle_oauth_callback.rs b/src/http/handle_oauth_callback.rs index a82b3d4..7ab1038 100644 --- a/src/http/handle_oauth_callback.rs +++ b/src/http/handle_oauth_callback.rs @@ -1,13 +1,13 @@ -use anyhow::{anyhow, Result}; +use anyhow::{Result, anyhow}; use atproto_identity::{axum::state::KeyProviderExtractor, key::identify_key}; -use atproto_oauth::workflow::{oauth_complete, OAuthClient}; +use atproto_oauth::workflow::{OAuthClient, oauth_complete}; use axum::{ extract::State, response::{IntoResponse, Redirect}, }; use axum_extra::extract::{ - cookie::{Cookie, SameSite}, Form, PrivateCookieJar, + cookie::{Cookie, SameSite}, }; use minijinja::context as template_context; use serde::{Deserialize, Serialize}; @@ -17,7 +17,7 @@ use crate::{contextual_error, select_template}; use super::{ context::WebContext, errors::{LoginError, WebError}, - middleware_auth::{WebSession, AUTH_COOKIE_NAME}, + middleware_auth::{AUTH_COOKIE_NAME, WebSession}, middleware_i18n::Language, }; @@ -164,9 +164,8 @@ pub(crate) async fn handle_oauth_callback( let token_response = token_response.unwrap(); // Retrieve destination from OAuth request before deleting it - let postgres_storage = crate::storage::atproto::PostgresOAuthRequestStorage::new( - web_context.pool.clone() - ); + let postgres_storage = + crate::storage::atproto::PostgresOAuthRequestStorage::new(web_context.pool.clone()); let destination = match postgres_storage.get_destination(&callback_state).await { Ok(Some(dest)) => dest, Ok(None) => "/".to_string(), diff --git a/src/http/handle_oauth_login.rs b/src/http/handle_oauth_login.rs index 184ae65..fd152d8 100644 --- a/src/http/handle_oauth_login.rs +++ b/src/http/handle_oauth_login.rs @@ -1,12 +1,12 @@ use anyhow::Result; use atproto_identity::{ - key::{generate_key, identify_key, KeyType}, + key::{KeyType, generate_key, identify_key}, resolve::IdentityResolver, }; use atproto_oauth::{ pkce::generate, resources::pds_resources, - workflow::{oauth_init, OAuthClient, OAuthRequest, OAuthRequestState}, + workflow::{OAuthClient, OAuthRequest, OAuthRequestState, oauth_init}, }; use axum::{extract::State, response::IntoResponse}; use axum_extra::extract::{Cached, Form, Query}; @@ -14,7 +14,7 @@ use axum_htmx::{HxBoosted, HxRedirect, HxRequest}; use axum_template::RenderHtml; use http::StatusCode; use minijinja::context as template_context; -use rand::{distributions::Alphanumeric, Rng}; +use rand::{Rng, distributions::Alphanumeric}; use serde::Deserialize; use crate::{ @@ -277,9 +277,12 @@ pub(crate) async fn handle_oauth_login( if dest != "/" { // Create a direct instance to access the set_destination method let postgres_storage = crate::storage::atproto::PostgresOAuthRequestStorage::new( - web_context.pool.clone() + web_context.pool.clone(), ); - if let Err(err) = postgres_storage.set_destination(&oauth_request_state.state, dest).await { + if let Err(err) = postgres_storage + .set_destination(&oauth_request_state.state, dest) + .await + { tracing::error!(?err, "set_destination"); // Don't fail the login flow if we can't store the destination } diff --git a/src/http/handle_profile.rs b/src/http/handle_profile.rs index a54a1de..959910c 100644 --- a/src/http/handle_profile.rs +++ b/src/http/handle_profile.rs @@ -200,7 +200,8 @@ pub(crate) async fn handle_profile_view( && next_activity.event_aturi == activity.event_aturi { group_dids.push(next_activity.did.clone()); - group_statuses.push(next_activity.rsvp_status.clone().unwrap_or_default()); + group_statuses + .push(next_activity.rsvp_status.clone().unwrap_or_default()); j += 1; } else { break; @@ -241,7 +242,11 @@ pub(crate) async fn handle_profile_view( let organizer_did = extract_did_from_aturi(&activity.event_aturi); needed_dids.insert(organizer_did); } - GroupedActivity::GroupedRsvp { dids, first_activity, .. } => { + GroupedActivity::GroupedRsvp { + dids, + first_activity, + .. + } => { for did in dids { needed_dids.insert(did.clone()); } @@ -252,7 +257,8 @@ pub(crate) async fn handle_profile_view( } // Get handles for needed DIDs - let handles = handles_by_did(&ctx.web_context.pool, needed_dids.into_iter().collect()).await?; + let handles = + handles_by_did(&ctx.web_context.pool, needed_dids.into_iter().collect()).await?; // Create ActivityDisplay objects let mut activity_displays: Vec = Vec::new(); @@ -265,8 +271,11 @@ pub(crate) async fn handle_profile_view( .map(|profile| profile.handle.clone()) .unwrap_or_else(|| did.clone()); - let event_url = url_from_aturi(&ctx.web_context.config.external_base, &activity.event_aturi) - .unwrap_or_else(|_| format!("/event/{}", activity.event_aturi)); + let event_url = url_from_aturi( + &ctx.web_context.config.external_base, + &activity.event_aturi, + ) + .unwrap_or_else(|_| format!("/event/{}", activity.event_aturi)); let event_organizer_did = extract_did_from_aturi(&activity.event_aturi); let event_organizer_handle = handles @@ -287,7 +296,12 @@ pub(crate) async fn handle_profile_view( updated_at: activity.updated_at.to_rfc3339(), })); } - GroupedActivity::GroupedRsvp { dids, first_activity, rsvp_statuses, count } => { + GroupedActivity::GroupedRsvp { + dids, + first_activity, + rsvp_statuses, + count, + } => { let group_handles: Vec = dids .iter() .map(|did| { @@ -298,17 +312,22 @@ pub(crate) async fn handle_profile_view( }) .collect(); - let event_url = url_from_aturi(&ctx.web_context.config.external_base, &first_activity.event_aturi) - .unwrap_or_else(|_| format!("/event/{}", first_activity.event_aturi)); + let event_url = url_from_aturi( + &ctx.web_context.config.external_base, + &first_activity.event_aturi, + ) + .unwrap_or_else(|_| format!("/event/{}", first_activity.event_aturi)); - let event_organizer_did = extract_did_from_aturi(&first_activity.event_aturi); + let event_organizer_did = + extract_did_from_aturi(&first_activity.event_aturi); let event_organizer_handle = handles .get(&event_organizer_did) .map(|profile| profile.handle.clone()) .unwrap_or_else(|| event_organizer_did.clone()); let first_status = &rsvp_statuses[0]; - let all_same_status = rsvp_statuses.iter().all(|status| status == first_status); + let all_same_status = + rsvp_statuses.iter().all(|status| status == first_status); let display_status = if all_same_status { Some(first_status.clone()) } else { @@ -394,7 +413,8 @@ pub(crate) async fn handle_profile_view( } }; - let organizer_handlers = hydrate_event_organizers(&ctx.web_context.pool, &events).await?; + let organizer_handlers = + hydrate_event_organizers(&ctx.web_context.pool, &events).await?; let mut events = events .iter() @@ -410,7 +430,8 @@ pub(crate) async fn handle_profile_view( .collect::>(); if let Err(err) = - super::event_view::hydrate_event_rsvp_counts(&ctx.web_context.pool, &mut events).await + super::event_view::hydrate_event_rsvp_counts(&ctx.web_context.pool, &mut events) + .await { tracing::warn!("Failed to hydrate event counts: {}", err); } diff --git a/src/http/handle_set_language.rs b/src/http/handle_set_language.rs index b2e9a22..d711911 100644 --- a/src/http/handle_set_language.rs +++ b/src/http/handle_set_language.rs @@ -4,15 +4,15 @@ use axum::{ response::{IntoResponse, Redirect}, }; use axum_extra::extract::{ - cookie::{Cookie, CookieJar, SameSite}, Cached, Form, + cookie::{Cookie, CookieJar, SameSite}, }; use minijinja::context as template_context; use serde::Deserialize; use std::{borrow::Cow, str::FromStr}; use unic_langid::LanguageIdentifier; -use crate::storage::identity_profile::{handle_update_field, HandleField}; +use crate::storage::identity_profile::{HandleField, handle_update_field}; use super::{ context::WebContext, errors::WebError, middleware_auth::Auth, middleware_i18n::COOKIE_LANG, diff --git a/src/http/handle_settings.rs b/src/http/handle_settings.rs index 891d4eb..15683f8 100644 --- a/src/http/handle_settings.rs +++ b/src/http/handle_settings.rs @@ -16,7 +16,9 @@ use crate::{ timezones::supported_timezones, }, select_template, - storage::identity_profile::{handle_for_did, handle_update_field, identity_profile_set_email, HandleField}, + storage::identity_profile::{ + HandleField, handle_for_did, handle_update_field, identity_profile_set_email, + }, }; #[derive(Deserialize, Clone, Debug)] @@ -41,7 +43,7 @@ pub(crate) async fn handle_settings( HxBoosted(hx_boosted): HxBoosted, ) -> Result { // Require authentication - let current_handle = auth.require(&web_context.config, "/settings")?; + let current_handle = auth.require("/settings")?; let default_context = template_context! { current_handle => current_handle.clone(), @@ -251,7 +253,10 @@ pub(crate) async fn handle_email_update( }; let error_template = select_template!(false, true, language); - let render_template = format!("settings.{}.email.html", language.to_string().to_lowercase()); + let render_template = format!( + "settings.{}.email.html", + language.to_string().to_lowercase() + ); // Update the email in the database let update_result = match email_form.email { @@ -261,9 +266,7 @@ pub(crate) async fn handle_email_update( Some(email) => { identity_profile_set_email(&web_context.pool, ¤t_handle.did, Some(&email)).await } - None => { - identity_profile_set_email(&web_context.pool, ¤t_handle.did, Some("")).await - } + None => identity_profile_set_email(&web_context.pool, ¤t_handle.did, Some("")).await, }; if let Err(err) = update_result { diff --git a/src/http/handle_view_event.rs b/src/http/handle_view_event.rs index da282dd..c7b5c07 100644 --- a/src/http/handle_view_event.rs +++ b/src/http/handle_view_event.rs @@ -17,12 +17,13 @@ use crate::contextual_error; use crate::http::context::UserRequestContext; use crate::http::errors::ViewEventError; use crate::http::errors::WebError; -use crate::http::event_view::hydrate_event_rsvp_counts; use crate::http::event_view::EventView; +use crate::http::event_view::hydrate_event_rsvp_counts; use crate::http::pagination::Pagination; use crate::http::tab_selector::TabSelector; use crate::http::utils::url_from_aturi; use crate::select_template; +use crate::storage::StoragePool; use crate::storage::event::count_event_rsvps; use crate::storage::event::event_exists; use crate::storage::event::event_get; @@ -32,7 +33,6 @@ use crate::storage::event::get_user_rsvp_with_email_shared; use crate::storage::identity_profile::handle_for_did; use crate::storage::identity_profile::handle_for_handle; use crate::storage::identity_profile::model::IdentityProfile; -use crate::storage::StoragePool; #[derive(Debug, Deserialize, Serialize, PartialEq)] pub enum RSVPTab { @@ -127,7 +127,10 @@ pub(crate) async fn handle_view_event( let profile = profile.unwrap(); - let identity_has_email = ctx.current_handle.as_ref().is_some_and(|handle| handle.email.as_ref().is_some_and(|value| !value.is_empty())); + let identity_has_email = ctx + .current_handle + .as_ref() + .is_some_and(|handle| handle.email.as_ref().is_some_and(|value| !value.is_empty())); // We'll use TimeZoneSelector to implement the time zone selection logic // The timezone selection will happen after we fetch the event @@ -284,7 +287,10 @@ pub(crate) async fn handle_view_event( let event_url = url_from_aturi(&ctx.web_context.config.external_base, &event.aturi)?; // Create login URL with destination parameter for this event - let login_url = format!("/oauth/login?destination={}", urlencoding::encode(&format!("/{}/{}", handle_slug, event_rkey))); + let login_url = format!( + "/oauth/login?destination={}", + urlencoding::encode(&format!("/{}/{}", handle_slug, event_rkey)) + ); // Add Edit button link if the user is the event creator let can_edit = ctx @@ -307,7 +313,13 @@ pub(crate) async fn handle_view_event( // Only fetch RSVP data for standard (non-legacy) events // Get user's RSVP status and email sharing preference if logged in let (user_rsvp, user_email_shared) = if let Some(current_entity) = &ctx.current_handle { - match get_user_rsvp_with_email_shared(&ctx.web_context.pool, &lookup_aturi, ¤t_entity.did).await { + match get_user_rsvp_with_email_shared( + &ctx.web_context.pool, + &lookup_aturi, + ¤t_entity.did, + ) + .await + { Ok(Some((status, email_shared))) => (Some(status), email_shared), Ok(None) => (None, false), Err(err) => { diff --git a/src/http/middleware_auth.rs b/src/http/middleware_auth.rs index 55f2c75..e6e1e96 100644 --- a/src/http/middleware_auth.rs +++ b/src/http/middleware_auth.rs @@ -1,5 +1,4 @@ use anyhow::Result; -use atproto_oauth::jwt::{mint, Claims, Header, JoseClaims}; use axum::{ extract::{FromRef, FromRequestParts}, http::request::Parts, @@ -10,9 +9,7 @@ use serde::{Deserialize, Serialize}; use tracing::{debug, instrument, trace}; use crate::{ - config::Config, - http::context::WebContext, - http::errors::{AuthMiddlewareError, WebSessionError}, + config::Config, http::context::WebContext, http::errors::WebSessionError, storage::identity_profile::model::IdentityProfile, }; @@ -73,10 +70,9 @@ impl Auth { /// /// This creates a redirect URL with a signed token containing the destination, /// which the login handler can verify and redirect back to after successful authentication. - #[instrument(level = "debug", skip(self, config), err)] + #[instrument(level = "debug", skip(self), err)] pub(crate) fn require( &self, - config: &crate::config::Config, location: &str, ) -> Result { match self { diff --git a/src/http/middleware_i18n.rs b/src/http/middleware_i18n.rs index 4d54ed5..45779f5 100644 --- a/src/http/middleware_i18n.rs +++ b/src/http/middleware_i18n.rs @@ -4,7 +4,7 @@ use axum::{ http::request::Parts, response::Response, }; -use axum_extra::extract::{cookie::CookieJar, Cached}; +use axum_extra::extract::{Cached, cookie::CookieJar}; use std::{cmp::Ordering, str::FromStr}; use tracing::{debug, instrument, trace}; use unic_langid::LanguageIdentifier; diff --git a/src/http/pagination.rs b/src/http/pagination.rs index b6d4bf6..0a44e06 100644 --- a/src/http/pagination.rs +++ b/src/http/pagination.rs @@ -38,7 +38,15 @@ impl Pagination { (page, page_size) } - pub(crate) fn clamped_with_options(&self, page_default: i64, page_min: i64, page_max: i64, page_size_default: i64, page_size_min: i64, page_size_max: i64) -> (i64, i64) { + pub(crate) fn clamped_with_options( + &self, + page_default: i64, + page_min: i64, + page_max: i64, + page_size_default: i64, + page_size_min: i64, + page_size_max: i64, + ) -> (i64, i64) { let page = self.page.unwrap_or(page_default).clamp(page_min, page_max); let page_size = self .page_size diff --git a/src/http/rsvp_form.rs b/src/http/rsvp_form.rs index 02eb9fc..f7c49af 100644 --- a/src/http/rsvp_form.rs +++ b/src/http/rsvp_form.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Deserializer, Serialize}; use crate::{ errors::expand_error, i18n::Locales, - storage::{event::event_get_cid, StoragePool}, + storage::{StoragePool, event::event_get_cid}, }; #[allow(dead_code)] diff --git a/src/http/server.rs b/src/http/server.rs index 5e6abb4..32b04fa 100644 --- a/src/http/server.rs +++ b/src/http/server.rs @@ -1,14 +1,14 @@ use std::time::Duration; use axum::{ + Router, http::HeaderValue, routing::{get, post}, - Router, }; use axum_htmx::AutoVaryLayer; use http::{ - header::{ACCEPT, ACCEPT_LANGUAGE}, Method, + header::{ACCEPT, ACCEPT_LANGUAGE}, }; use tower_http::trace::TraceLayer; use tower_http::{classify::ServerErrorsFailureClass, timeout::TimeoutLayer}; @@ -48,7 +48,9 @@ use crate::http::{ }, handle_profile::handle_profile_view, handle_set_language::handle_set_language, - handle_settings::{handle_email_update, handle_language_update, handle_settings, handle_timezone_update}, + handle_settings::{ + handle_email_update, handle_language_update, handle_settings, handle_timezone_update, + }, handle_view_event::handle_view_event, handle_view_feed::handle_view_feed, handle_view_rsvp::handle_view_rsvp, @@ -128,8 +130,14 @@ pub fn build_router(web_context: WebContext) -> Router { .route("/event/links", post(handle_link_at_builder)) .route("/{handle_slug}/{event_rkey}/edit", get(handle_edit_event)) .route("/{handle_slug}/{event_rkey}/edit", post(handle_edit_event)) - .route("/{handle_slug}/{event_rkey}/export-rsvps", get(handle_export_rsvps)) - .route("/{handle_slug}/{event_rkey}/delete", post(handle_delete_event)) + .route( + "/{handle_slug}/{event_rkey}/export-rsvps", + get(handle_export_rsvps), + ) + .route( + "/{handle_slug}/{event_rkey}/delete", + post(handle_delete_event), + ) .route( "/{handle_slug}/{event_rkey}/migrate", get(handle_migrate_event), diff --git a/src/http/templates.rs b/src/http/templates.rs index 285cb5d..5be32b0 100644 --- a/src/http/templates.rs +++ b/src/http/templates.rs @@ -21,7 +21,7 @@ pub(crate) fn render_alert>( pub mod reload_env { use std::path::PathBuf; - use minijinja::{path_loader, Environment}; + use minijinja::{Environment, path_loader}; use minijinja_autoreload::AutoReloader; pub fn build_env(http_external: &str, version: &str) -> AutoReloader { diff --git a/src/http/timezones.rs b/src/http/timezones.rs index e6ac868..b2e895c 100644 --- a/src/http/timezones.rs +++ b/src/http/timezones.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Result}; +use anyhow::{Result, anyhow}; use chrono::{DateTime, NaiveDateTime, Utc}; use itertools::Itertools; diff --git a/src/i18n.rs b/src/i18n.rs index 51efebb..e313cfc 100644 --- a/src/i18n.rs +++ b/src/i18n.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use fluent::{bundle::FluentBundle, FluentArgs, FluentResource}; +use fluent::{FluentArgs, FluentResource, bundle::FluentBundle}; use std::collections::HashMap; use unic_langid::LanguageIdentifier; @@ -105,7 +105,7 @@ pub mod embed { use rust_embed::Embed; use unic_langid::LanguageIdentifier; - use crate::i18n::{errors::I18nError, Locales}; + use crate::i18n::{Locales, errors::I18nError}; #[derive(Embed)] #[folder = "i18n/"] @@ -135,7 +135,7 @@ pub mod reload { use std::path::PathBuf; use unic_langid::LanguageIdentifier; - use crate::i18n::{errors::I18nError, Locales}; + use crate::i18n::{Locales, errors::I18nError}; pub fn populate_locale( supported_locales: &Vec, diff --git a/src/storage/atproto.rs b/src/storage/atproto.rs index 6b487e0..6096331 100644 --- a/src/storage/atproto.rs +++ b/src/storage/atproto.rs @@ -6,7 +6,7 @@ use serde_json::Value as JsonValue; use sqlx::FromRow; use std::sync::Arc; -use crate::storage::{errors::StorageError, StoragePool}; +use crate::storage::{StoragePool, errors::StorageError}; /// Database row representation of OAuthRequest #[derive(FromRow)] @@ -131,9 +131,9 @@ impl PostgresOAuthRequestStorage { destination: &str, ) -> Result<(), StorageError> { if oauth_state.trim().is_empty() { - return Err(StorageError::UnableToExecuteQuery( - sqlx::Error::Protocol("OAuth state cannot be empty".to_string()), - )); + return Err(StorageError::UnableToExecuteQuery(sqlx::Error::Protocol( + "OAuth state cannot be empty".to_string(), + ))); } let mut tx = self @@ -161,14 +161,11 @@ impl PostgresOAuthRequestStorage { } /// Get the destination for an OAuth request - pub async fn get_destination( - &self, - oauth_state: &str, - ) -> Result, StorageError> { + pub async fn get_destination(&self, oauth_state: &str) -> Result, StorageError> { if oauth_state.trim().is_empty() { - return Err(StorageError::UnableToExecuteQuery( - sqlx::Error::Protocol("OAuth state cannot be empty".to_string()), - )); + return Err(StorageError::UnableToExecuteQuery(sqlx::Error::Protocol( + "OAuth state cannot be empty".to_string(), + ))); } let mut tx = self diff --git a/src/storage/denylist.rs b/src/storage/denylist.rs index 096c026..e4a4210 100644 --- a/src/storage/denylist.rs +++ b/src/storage/denylist.rs @@ -6,7 +6,7 @@ use std::hash::Hasher; use self::model::DenylistEntry; -use crate::storage::{errors::StorageError, StoragePool}; +use crate::storage::{StoragePool, errors::StorageError}; pub(crate) mod model { use chrono::{DateTime, Utc}; diff --git a/src/storage/event.rs b/src/storage/event.rs index 348ac0d..2a71753 100644 --- a/src/storage/event.rs +++ b/src/storage/event.rs @@ -11,8 +11,8 @@ use crate::atproto::lexicon::community::lexicon::calendar::rsvp::{ Rsvp as RsvpLexicon, RsvpStatus as RsvpStatusLexicon, }; -use super::errors::StorageError; use super::StoragePool; +use super::errors::StorageError; use model::{ActivityItem, Event, EventWithRole, Rsvp}; pub mod model { @@ -1341,11 +1341,22 @@ pub async fn get_event_rsvps_for_export( ORDER BY r.updated_at ASC "#; - let rsvps = sqlx::query_as::<_, (String, String, String, Option, String, Option>, Option)>(query) - .bind(event_aturi) - .fetch_all(tx.as_mut()) - .await - .map_err(StorageError::UnableToExecuteQuery)?; + let rsvps = sqlx::query_as::< + _, + ( + String, + String, + String, + Option, + String, + Option>, + Option, + ), + >(query) + .bind(event_aturi) + .fetch_all(tx.as_mut()) + .await + .map_err(StorageError::UnableToExecuteQuery)?; tx.commit() .await @@ -1353,8 +1364,8 @@ pub async fn get_event_rsvps_for_export( let export_data: Vec = rsvps .into_iter() - .map(|(event_aturi, rsvp_aturi, did, handle, status, created_at, email)| { - RsvpExportData { + .map( + |(event_aturi, rsvp_aturi, did, handle, status, created_at, email)| RsvpExportData { event_aturi, rsvp_aturi, did, @@ -1362,8 +1373,8 @@ pub async fn get_event_rsvps_for_export( status, created_at, email, - } - }) + }, + ) .collect(); Ok(export_data) diff --git a/src/storage/identity_profile.rs b/src/storage/identity_profile.rs index a530322..ac3a541 100644 --- a/src/storage/identity_profile.rs +++ b/src/storage/identity_profile.rs @@ -4,9 +4,9 @@ use chrono::Utc; use cityhasher::HashMap; use sqlx::{Postgres, QueryBuilder}; +use crate::storage::StoragePool; use crate::storage::denylist::denylist_add_or_update; use crate::storage::errors::StorageError; -use crate::storage::StoragePool; use model::IdentityProfile; pub mod model { diff --git a/src/storage/oauth.rs b/src/storage/oauth.rs index fb76a5e..45bdb7d 100644 --- a/src/storage/oauth.rs +++ b/src/storage/oauth.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use chrono::{DateTime, Utc}; -use crate::storage::{errors::StorageError, identity_profile::model::IdentityProfile, StoragePool}; +use crate::storage::{StoragePool, errors::StorageError, identity_profile::model::IdentityProfile}; use model::OAuthSession; pub async fn oauth_session_update( diff --git a/src/task_identity_refresh.rs b/src/task_identity_refresh.rs index 06c039a..5b7c3e9 100644 --- a/src/task_identity_refresh.rs +++ b/src/task_identity_refresh.rs @@ -2,7 +2,7 @@ use anyhow::Result; use atproto_identity::{resolve::IdentityResolver, storage::DidDocumentStorage}; use chrono::Duration; use sqlx::FromRow; -use tokio::time::{sleep, Instant}; +use tokio::time::{Instant, sleep}; use tokio_util::sync::CancellationToken; use crate::storage::StoragePool; diff --git a/src/task_oauth_requests_cleanup.rs b/src/task_oauth_requests_cleanup.rs index c933ba8..6979b54 100644 --- a/src/task_oauth_requests_cleanup.rs +++ b/src/task_oauth_requests_cleanup.rs @@ -1,6 +1,6 @@ use anyhow::Result; use chrono::Duration; -use tokio::time::{sleep, Instant}; +use tokio::time::{Instant, sleep}; use tokio_util::sync::CancellationToken; use crate::storage::StoragePool; diff --git a/src/task_refresh_tokens.rs b/src/task_refresh_tokens.rs index f085981..fcf3031 100644 --- a/src/task_refresh_tokens.rs +++ b/src/task_refresh_tokens.rs @@ -1,19 +1,19 @@ use anyhow::Result; use atproto_identity::key::identify_key; -use atproto_oauth::workflow::{oauth_refresh, OAuthClient}; +use atproto_oauth::workflow::{OAuthClient, oauth_refresh}; use chrono::{Duration, Utc}; -use deadpool_redis::redis::{pipe, AsyncCommands}; +use deadpool_redis::redis::{AsyncCommands, pipe}; use std::borrow::Cow; -use tokio::time::{sleep, Instant}; +use tokio::time::{Instant, sleep}; use tokio_util::sync::CancellationToken; use crate::{ config::SigningKeys, refresh_tokens_errors::RefreshError, storage::{ - cache::{build_worker_queue, OAUTH_REFRESH_HEARTBEATS, OAUTH_REFRESH_QUEUE}, - oauth::{oauth_session_delete, oauth_session_update, web_session_lookup}, CachePool, StoragePool, + cache::{OAUTH_REFRESH_HEARTBEATS, OAUTH_REFRESH_QUEUE, build_worker_queue}, + oauth::{oauth_session_delete, oauth_session_update, web_session_lookup}, }, };