-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Moved language preference cookie into general purpose preference cookie
- Loading branch information
1 parent
8c45591
commit 7b6d7a8
Showing
6 changed files
with
131 additions
and
92 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
{% macro weather(weather_maps) %} | ||
{% if "Windy" in weather_maps %} | ||
{% with weather_map = weather_maps.Windy %} | ||
<h3 class="text-3xl text-center py-1">windy.com</h3> | ||
<iframe width="100%" | ||
height="450" | ||
src="https://embed.windy.com/embed2.html?lat={{ weather_map.latitude }}&lon={{ weather_map.longitude }}&detailLat={{ weather_map.latitude }}&detailLon={{ weather_map.longitude }}&width=650&height=450&zoom=11&level=surface&overlay=wind&product=ecmwf&menu=&message=&marker=&calendar=now&pressure=&type=map&location=coordinates&detail=true&metricWind=m%2Fs&metricTemp=%C2%B0C&radarRange=-1" | ||
frameborder="0"></iframe> | ||
{% endwith %} | ||
{% endif %} | ||
{% if "Meteoblue" in weather_maps %} | ||
{% with weather_map = weather_maps.Meteoblue %} | ||
<h3 class="text-3xl text-center py-1 pt-2"> | ||
<!-- DO NOT REMOVE THIS LINK --><a class="text-blue-600 hover:text-blue-800" | ||
href="https://www.meteoblue.com/en/weather/week/{{ weather_map.location_id }}?utm_source=weather_widget&utm_medium=linkus&utm_content=daily&utm_campaign=Weather%2BWidget" | ||
target="_blank" | ||
rel="noopener">meteoblue</a> | ||
</h3> | ||
<div class="flex items-center justify-center"> | ||
<div class="w-full"> | ||
<iframe src="https://www.meteoblue.com/en/weather/widget/daily/{{ weather_map.location_id }}?geoloc=fixed&days=7&tempunit=CELSIUS&windunit=METER_PER_SECOND&precipunit=MILLIMETER&coloured=coloured&pictoicon=0&pictoicon=1&maxtemperature=0&maxtemperature=1&mintemperature=0&mintemperature=1&windspeed=0&windspeed=1&windgust=0&winddirection=0&winddirection=1&uv=0&humidity=0&precipitation=0&precipitation=1&precipitationprobability=0&precipitationprobability=1&spot=0&spot=1&pressure=0&layout=light" | ||
frameborder="0" | ||
scrolling="yes" | ||
allowtransparency="true" | ||
sandbox="allow-same-origin allow-scripts allow-popups allow-popups-to-escape-sandbox" | ||
class="w-full md:aspect-[17/10] aspect-[10/10]"></iframe> | ||
<div></div> | ||
</div> | ||
</div> | ||
{% endwith %} | ||
{% endif %} | ||
{% endmacro %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
use axum::{ | ||
extract::Query, | ||
middleware::Next, | ||
response::{IntoResponse, Redirect, Response}, | ||
Extension, | ||
}; | ||
use axum_extra::extract::CookieJar; | ||
use base64::Engine; | ||
use eyre::{Context, ContextCompat}; | ||
use http::{header::SET_COOKIE, HeaderMap, HeaderValue, Request, StatusCode}; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
use crate::error::{map_eyre_error, map_std_error}; | ||
|
||
#[derive(Serialize, Deserialize, Default, Clone)] | ||
#[serde(default)] | ||
pub struct UserPreferences { | ||
pub lang: Option<unic_langid::LanguageIdentifier>, | ||
} | ||
|
||
impl UserPreferences { | ||
/// Merge right into left, skipping any fields that are `None` on right. | ||
fn merge(mut left: Self, right: Self) -> Self { | ||
if right.lang.is_some() { | ||
left.lang = right.lang; | ||
} | ||
|
||
left | ||
} | ||
} | ||
|
||
const COOKIE_NAME: &str = "preferences"; | ||
/// The Max-Age property for the cookie (in seconds). | ||
const COOKIE_MAX_AGE_SECONDS: u64 = 365 * 24 * 60 * 60; | ||
|
||
/// Handler for setting user preferences using a query, and redirecting to the referrer URL | ||
/// provided in the request. This merges with what has currently been set. | ||
pub async fn handler( | ||
Query(set_preferences): Query<UserPreferences>, | ||
Extension(current_preferences): Extension<UserPreferences>, | ||
headers: HeaderMap, | ||
) -> axum::response::Result<impl IntoResponse> { | ||
let referer_str = headers | ||
.get("Referer") | ||
.wrap_err("No referer headers") | ||
.map_err(map_eyre_error)? | ||
.to_str() | ||
.wrap_err("Referer is not a valid string") | ||
.map_err(map_eyre_error)?; | ||
let mut response = Redirect::to(referer_str).into_response(); | ||
|
||
let preferences = UserPreferences::merge(current_preferences, set_preferences); | ||
let preferences_data = serde_urlencoded::to_string(preferences) | ||
.context("Error serializing preferences") | ||
.map_err(map_eyre_error)?; | ||
let value = HeaderValue::from_str(&format!( | ||
"{COOKIE_NAME}={preferences_data}; Max-Age={COOKIE_MAX_AGE_SECONDS}" | ||
)) | ||
.map_err(map_std_error)?; | ||
response.headers_mut().insert(SET_COOKIE, value); | ||
Ok(response) | ||
} | ||
|
||
/// Middleware for extracting user preferences from cookie that was set using [`set_handler`]. | ||
pub async fn middleware<B>(mut request: Request<B>, next: Next<B>) -> Response { | ||
let cookies = CookieJar::from_headers(request.headers()); | ||
let preferences: UserPreferences = | ||
match Option::transpose(cookies.get(COOKIE_NAME).map(|cookie| { | ||
serde_urlencoded::from_str(cookie.value()).context("Error deserializing preferences") | ||
})) { | ||
Ok(preferences) => preferences.unwrap_or_default(), | ||
Err(error) => { | ||
return ( | ||
StatusCode::INTERNAL_SERVER_ERROR, | ||
format!("Unable to parse preferences cookie: {error}"), | ||
) | ||
.into_response() | ||
} | ||
}; | ||
|
||
request.extensions_mut().insert(preferences); | ||
next.run(request).await | ||
} |