dedupe HTML escaping into a shared html module
web_escape (main.rs) and escape_html (auth.rs, web.rs) were three byte-identical copies of the same & < > " ' replacement chain. Moved into src/html.rs as html::escape and used from all three call sites.
This commit is contained in:
parent
1a0ee71e6b
commit
84f5f24b6a
4 changed files with 48 additions and 64 deletions
61
src/web.rs
61
src/web.rs
|
|
@ -13,7 +13,7 @@ use serde_json::Value;
|
|||
use sqlx::Row;
|
||||
|
||||
use crate::{
|
||||
AppState, db, leaderboard,
|
||||
AppState, db, html, leaderboard,
|
||||
model::{ActivityCrossing, LeaderboardGroup},
|
||||
};
|
||||
|
||||
|
|
@ -379,7 +379,7 @@ fn render_index(
|
|||
r#"<div>Angemeldet als <strong>{}</strong><br>
|
||||
<a class="button secondary" href="/groups">Gruppen</a>
|
||||
<a class="button" href="/logout">Logout</a></div>"#,
|
||||
escape_html(name)
|
||||
html::escape(name)
|
||||
));
|
||||
} else {
|
||||
html.push_str(
|
||||
|
|
@ -410,7 +410,7 @@ fn render_index(
|
|||
"<option value=\"{}\"{}>{}</option>",
|
||||
id,
|
||||
selected,
|
||||
escape_html(name)
|
||||
html::escape(name)
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -427,8 +427,8 @@ fn render_index(
|
|||
html.push_str("<section class=\"card\">");
|
||||
html.push_str(&format!(
|
||||
"<h2>{} → {}</h2>",
|
||||
escape_html(&group.from_county),
|
||||
escape_html(&group.to_county)
|
||||
html::escape(&group.from_county),
|
||||
html::escape(&group.to_county)
|
||||
));
|
||||
html.push_str(&format!(
|
||||
"<div class=\"small\">10-Minuten-Fenster · {} · Standort ±10 m</div>",
|
||||
|
|
@ -454,7 +454,7 @@ fn render_index(
|
|||
.map(|url| {
|
||||
format!(
|
||||
"<a href=\"{}\" target=\"_blank\" rel=\"noreferrer\">Intervall</a>",
|
||||
escape_html(url)
|
||||
html::escape(url)
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| "<span class=\"small\">—</span>".into());
|
||||
|
|
@ -478,11 +478,11 @@ fn render_index(
|
|||
<td><div class="mini-map" data-lat="{}" data-lon="{}"></div></td>
|
||||
</tr>"#,
|
||||
row.rank,
|
||||
escape_html(&row.athlete),
|
||||
escape_html(&row.crossing_time.to_rfc3339()),
|
||||
html::escape(&row.athlete),
|
||||
html::escape(&row.crossing_time.to_rfc3339()),
|
||||
row.crossing_time.format("%H:%M:%S%.3f UTC"),
|
||||
escape_html(&row.activity_url),
|
||||
escape_html(&row.activity_id),
|
||||
html::escape(&row.activity_url),
|
||||
html::escape(&row.activity_id),
|
||||
interval,
|
||||
row.lat,
|
||||
row.lon,
|
||||
|
|
@ -522,18 +522,18 @@ fn render_activity(
|
|||
<section class="card">
|
||||
<h2>{}</h2>
|
||||
<div class="small">Athlet: {} · Aktivität: {}</div>"#,
|
||||
escape_html(title),
|
||||
escape_html(&athlete_name),
|
||||
escape_html(sport),
|
||||
escape_html(title),
|
||||
escape_html(&athlete_name),
|
||||
escape_html(activity_id),
|
||||
html::escape(title),
|
||||
html::escape(&athlete_name),
|
||||
html::escape(sport),
|
||||
html::escape(title),
|
||||
html::escape(&athlete_name),
|
||||
html::escape(activity_id),
|
||||
));
|
||||
|
||||
if let Some(start) = start_time {
|
||||
html.push_str(&format!(
|
||||
r#"<div class="small activity-start" data-time="{}">Start: {}</div>"#,
|
||||
escape_html(&start.to_rfc3339()),
|
||||
html::escape(&start.to_rfc3339()),
|
||||
start.format("%Y-%m-%d %H:%M:%S UTC")
|
||||
));
|
||||
}
|
||||
|
|
@ -552,7 +552,7 @@ fn render_activity(
|
|||
})
|
||||
})
|
||||
.collect();
|
||||
let points_json = escape_html(&serde_json::to_string(&points).unwrap_or_else(|_| "[]".into()));
|
||||
let points_json = html::escape(&serde_json::to_string(&points).unwrap_or_else(|_| "[]".into()));
|
||||
|
||||
html.push_str(&format!(
|
||||
r#"<div id="activity-map" class="activity-map" data-points="{}"></div>"#,
|
||||
|
|
@ -573,7 +573,7 @@ fn render_activity(
|
|||
.map(|url| {
|
||||
format!(
|
||||
"<a href=\"{}\" target=\"_blank\" rel=\"noreferrer\">Intervall</a>",
|
||||
escape_html(url)
|
||||
html::escape(url)
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| "—".into());
|
||||
|
|
@ -592,10 +592,10 @@ fn render_activity(
|
|||
<td>{} → {}</td><td class="rank">{}</td>
|
||||
<td>{}</td><td>{}</td><td>{}</td></tr>"#,
|
||||
index + 1,
|
||||
escape_html(&crossing.crossing_time.to_rfc3339()),
|
||||
html::escape(&crossing.crossing_time.to_rfc3339()),
|
||||
crossing.crossing_time.format("%H:%M:%S%.3f UTC"),
|
||||
escape_html(&crossing.from_county),
|
||||
escape_html(&crossing.to_county),
|
||||
html::escape(&crossing.from_county),
|
||||
html::escape(&crossing.to_county),
|
||||
crossing.rank,
|
||||
power,
|
||||
hr,
|
||||
|
|
@ -626,7 +626,7 @@ fn render_groups(
|
|||
html.push_str(&format!(
|
||||
r#"<header><div><a href="/">← Leaderboard</a><h1>Leaderboard-Gruppen</h1>
|
||||
<div class="small">Gruppen werden für {} verwaltet.</div></div></header>"#,
|
||||
escape_html(owner_name)
|
||||
html::escape(owner_name)
|
||||
));
|
||||
|
||||
html.push_str(
|
||||
|
|
@ -639,7 +639,7 @@ fn render_groups(
|
|||
html.push_str(&format!(
|
||||
r#"<label><input type="checkbox" name="members" value="{}"> {}</label>"#,
|
||||
id,
|
||||
escape_html(name)
|
||||
html::escape(name)
|
||||
));
|
||||
}
|
||||
html.push_str(r#"</div><button type="submit">Gruppe anlegen</button></form></section>"#);
|
||||
|
|
@ -651,7 +651,7 @@ fn render_groups(
|
|||
<input type="text" name="name" value="{}" required>
|
||||
<div class="members">"#,
|
||||
group_id,
|
||||
escape_html(name)
|
||||
html::escape(name)
|
||||
));
|
||||
|
||||
for (id, athlete_name) in athletes {
|
||||
|
|
@ -660,7 +660,7 @@ fn render_groups(
|
|||
r#"<label><input type="checkbox" name="members" value="{}"{}> {}</label>"#,
|
||||
id,
|
||||
checked,
|
||||
escape_html(athlete_name)
|
||||
html::escape(athlete_name)
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -746,12 +746,3 @@ if(activityMapEl){
|
|||
</script>
|
||||
</body></html>
|
||||
"#;
|
||||
|
||||
fn escape_html(value: &str) -> String {
|
||||
value
|
||||
.replace('&', "&")
|
||||
.replace('<', "<")
|
||||
.replace('>', ">")
|
||||
.replace('"', """)
|
||||
.replace('\'', "'")
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue