Skip to content
Merged

misc #136

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions frontend/templates/events/edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ <h1>Edit Event</h1>
<form
id="form"
class="ext/form"
action="/events/{{ event.id }}/update"
method="post"
enctype="multipart/form-data"
>
<div class="field">
<label for="title">Title</label>
Expand Down Expand Up @@ -351,12 +348,6 @@ <h2>Available Spots</h2>
return;
}

const slug = $("slug").value;
if (!slug || !/^[a-zA-Z0-9\-]+$/.test(slug)) {
alert("Slug can only contain letters, numbers, and dashes.");
return;
}

// Retrieve the value of an input, with adjustments for HTML input type weirdness
let value = (name, root = ui.form) => {
const el = root.querySelector(`[name="${name}"]`);
Expand Down Expand Up @@ -399,10 +390,16 @@ <h2>Available Spots</h2>
return [start, end];
})();

const slug = value("slug");
if (!slug || !/^[a-zA-Z0-9\-]+$/.test(slug)) {
alert("Slug can only contain letters, numbers, and dashes.");
return;
}

const body = {
id: parseInt("{{ event.id }}") || undefined,
id: parseInt(`{{ event.id }}`),
title: value("title"),
slug: value("slug"),
slug,
start,
end,
capacity: value("capacity"),
Expand Down Expand Up @@ -436,7 +433,6 @@ <h2>Available Spots</h2>
}),
};

const slug = value("slug");
try {
const formData = new FormData();
formData.append("data", JSON.stringify(body));
Expand All @@ -452,7 +448,12 @@ <h2>Available Spots</h2>
});

if (resp.ok) {
window.location.href = `/e/${slug}`;
const payload = await resp.json();
if (payload?.error) {
alert(`Error: ${payload.error}`);
} else {
window.location.href = `/e/${slug}`;
}
} else {
alert(`Error: ${await resp.text()}`);
}
Expand Down
47 changes: 24 additions & 23 deletions src/app/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,14 +213,14 @@ mod edit {
// Handle edit submission.
#[derive(Debug, serde::Deserialize)]
pub struct EditForm {
id: Option<i64>,
id: i64,
#[serde(flatten)]
event: UpdateEvent,
spots: Vec<UpdateSpot>,
}
pub async fn edit_form(
State(state): State<SharedAppState>, mut multipart: axum::extract::Multipart,
) -> HtmlResult {
) -> JsonResult<()> {
let mut form: Option<EditForm> = None;
let mut flyer: Option<DynamicImage> = None;

Expand All @@ -245,12 +245,24 @@ mod edit {
if form.event.slug.is_empty()
|| !form.event.slug.chars().all(|c| c.is_ascii_alphanumeric() || c == '-')
{
return Ok((StatusCode::BAD_REQUEST, "Slug can only contain letters, numbers, and dashes.")
.into_response());
bail!("Slug can only contain letters, numbers, and dashes.");
}

match form.id {
Some(id) => {
0 => {
tracing::info!("create: {:?}", &form.event);
let event_id = Event::create(&state.db, &form.event, &flyer).await?;

let mut spot_ids = vec![];
for spot in form.spots {
let id = Spot::create(&state.db, &spot).await?;
spot_ids.push(id);
}

Spot::add_to_event(&state.db, event_id, spot_ids).await?;
}
id => {
tracing::info!("edit: {:?}", &form.event);
Event::update(&state.db, id, &form.event, &flyer).await?;

let rsvp_counts = Spot::rsvp_counts_for_event(&state.db, id).await?;
Expand All @@ -277,20 +289,9 @@ mod edit {
Spot::add_to_event(&state.db, id, to_add).await?;
Spot::remove_from_event(&state.db, id, to_delete).await?;
}
None => {
let event_id = Event::create(&state.db, &form.event, &flyer).await?;

let mut spot_ids = vec![];
for spot in form.spots {
let id = Spot::create(&state.db, &spot).await?;
spot_ids.push(id);
}

Spot::add_to_event(&state.db, event_id, spot_ids).await?;
}
}

Ok(Redirect::to("/events").into_response())
Ok(Json(()))
}

// Edit invite page.
Expand Down Expand Up @@ -1790,8 +1791,8 @@ mod rsvp {

#[derive(thiserror::Error, Debug)]
pub enum ParseSelectionError {
#[error("failed to parse request JSON")]
Parse,
#[error("failed to parse request: {0}")]
Parse(#[from] serde_json::Error),
#[error("unknown spot_id={spot_id}")]
UnknownSpot { spot_id: i64 },

Expand All @@ -1810,7 +1811,7 @@ mod rsvp {
contribution: Option<i64>,
}

let rsvps: Vec<RsvpForm> = serde_json::from_str(selection).map_err(|_| Error::Parse)?;
let rsvps: Vec<RsvpForm> = serde_json::from_str(selection)?;
let mut parsed = vec![];

for rsvp in rsvps {
Expand Down Expand Up @@ -1849,8 +1850,8 @@ mod rsvp {
}
#[derive(thiserror::Error, Debug)]
pub enum ParseAttendeesError {
#[error("failed to parse request JSON")]
Parse,
#[error("failed to parse request: {0}")]
Parse(#[from] serde_json::Error),

#[error("unknown or duplicate rsvp_id={rsvp_id}")]
UnknownOrDuplicateRsvp { rsvp_id: i64 },
Expand Down Expand Up @@ -1887,7 +1888,7 @@ mod rsvp {

is_me: bool,
}
let attendees: Vec<AttendeeForm> = serde_json::from_str(attendees).map_err(|_| Error::Parse)?;
let attendees: Vec<AttendeeForm> = serde_json::from_str(attendees)?;

// Track available rsvp_ids, seen email/phones for duplicate detection
let mut remaining_rsvps: HashSet<i64> = HashSet::from_iter(rsvps.iter().map(|r| r.rsvp_id));
Expand Down
2 changes: 1 addition & 1 deletion src/app/webhooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ pub mod stripe {
object: T,
}
let event: Type = serde_json::from_str(&body).map_err(|_| invalid())?;
fn parse<T: serde::de::DeserializeOwned>(body: &str) -> AppResult<T> {
fn parse<T: serde::de::DeserializeOwned>(body: &str) -> Result<T, AppError> {
let event: Event<T> = serde_json::from_str(body).map_err(|_| invalid())?;
Ok(event.data.object)
}
Expand Down
20 changes: 14 additions & 6 deletions src/utils/cloudflare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ impl Cloudflare {
#[derive(serde::Deserialize, serde::Serialize, Debug)]
struct Response {
success: bool,
hostname: String,
challenge_ts: DateTime<Utc>,
#[serde(rename = "error-codes")]
hostname: Option<String>,
challenge_ts: Option<DateTime<Utc>>,
#[serde(default, rename = "error-codes")]
error_codes: Vec<String>,
}
let res: Response = req.json().await?;
Expand All @@ -54,10 +54,18 @@ impl Cloudflare {
let challenge_ok = res.success;
let domain_ok = match self.app_domain.as_str() {
"localhost" => true,
domain => res.hostname == domain,
domain => res.hostname.is_some_and(|host| host == domain),
};
let age_ok = Utc::now().signed_duration_since(res.challenge_ts).num_minutes() < 5;
let age_ok = res
.challenge_ts
.is_some_and(|ts| Utc::now().signed_duration_since(ts).num_minutes() < 5);
let ok = challenge_ok && domain_ok && age_ok;

Ok(challenge_ok && domain_ok && age_ok)
tracing::info!(
"Turnstile token={token} client_ip={client_ip} -> ok={ok} challenge_ok={challenge_ok} domain_ok={domain_ok} age_ok={age_ok} errors={:?}",
res.error_codes
);

Ok(ok)
}
}
5 changes: 2 additions & 3 deletions src/utils/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ impl<E: Error + Send + Sync + 'static> From<E> for AnyError {
#[macro_export]
macro_rules! bail {
( $fmt:expr ) => {
return Err(AnyError::new($fmt));
return Err(AnyError::new($fmt).into());
};
( $fmt:expr, $($arg:expr),* $(,)?) => {
return Err(AnyError::new(format!("{}", format_args!($fmt, $($arg),*))));
return Err(AnyError::new(format!("{}", format_args!($fmt, $($arg),*))).into());
}
}
pub use bail;
Expand All @@ -84,7 +84,6 @@ pub enum AppError {
Unauthorized(Backtrace),
Invalid(Backtrace),
}
pub type AppResult<T> = Result<T, AppError>;

impl AppError {
pub fn message(&self) -> &'static str {
Expand Down
Loading