Skip to content

Commit

Permalink
Update dependencies and add Cache-Control header to dynamic routes wi…
Browse files Browse the repository at this point in the history
…th no-store
  • Loading branch information
kellpossible committed Jan 27, 2024
1 parent 3b4f7d7 commit 3ff6159
Show file tree
Hide file tree
Showing 15 changed files with 289 additions and 134 deletions.
224 changes: 172 additions & 52 deletions Cargo.lock

Large diffs are not rendered by default.

15 changes: 8 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ forecast-spreadsheet = { path = "./forecast-spreadsheet" }
utils = { path = "./utils" }
# geo = { path = "./geo" }
average = "0.14.1"
axum = { version = "0.6.20", features = ["headers"] }
axum-extra = { version = "0.7.7", default-features = false, features = ["cookie"] }
axum = { version = "0.7.4" }
headers = "0.4.0"
axum-extra = { version = "0.9.2", default-features = false, features = ["cookie"] }
base64 = "0.21.0"
bcrypt = "0.15.0"
ansi-to-html = "0.1.2"
Expand All @@ -30,7 +31,7 @@ fluent-langneg = "0.13.0"
futures = "0.3.26"
tracing-appender = "0.2"
tracing-subscriber = "0.3"
http = "0.2.8"
http = "1.0.0"
governor = "0.6.0"
tracing = "0.1"
mime_guess = "2.0.4"
Expand All @@ -40,11 +41,11 @@ regex = "1.7.1"
tower = "0.4"
humantime = "2.1.0"
nonzero_ext = "0.3.0"
http-body = "0.4"
http-body = "1.0.0"
time-tz = { workspace = true }
tower-http = { version = "0.4", features = ["trace", "auth"] }
tower-http = { version = "0.5.1", features = ["trace", "auth"] }
tokio = { version = "1.24.1", features = ["macros", "rt-multi-thread"] }
tokio-stream = { version = "0.1.11" }
tokio-stream = { version = "0.1.14" }
deadpool-sqlite = { git = "https://github.com/bikeshedder/deadpool.git" } # Needed for new version of rusqlite
thiserror = "1.0.38"
libsqlite3-sys = { version = "0.26", features = ["bundled"] }
Expand All @@ -60,7 +61,7 @@ once_cell = { workspace = true }
unic-langid = { workspace = true, features = ["serde"] }
time = { workspace = true, features = ["serde", "parsing", "formatting", "macros"] }
resvg = { version = "0.29.0", default-features = false, features = ["filter", "text", "memmap-fonts"] } # required only for svg to png diagram generation
reqwest = { version = "0.11.14", default-features = false, features = ["json", "stream", "rustls-tls"] }
reqwest = { version = "0.11.23", default-features = false, features = ["json", "stream", "rustls-tls"] }
secrecy = { version = "0.8.0", features = ["serde"] }
usvg-text-layout = { version = "0.29.0", default-features = false, features = ["memmap-fonts"]}
buildstructor = "0.5.4"
Expand Down
4 changes: 2 additions & 2 deletions src/admin/analytics/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use sea_query::{Alias, Expr, IntoIden, Order, SimpleExpr, SqliteQueryBuilder};
use sea_query_rusqlite::RusqliteBinder;
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use utils::serde::{duration_seconds_option, rfc3339_option};
use utils::serde::rfc3339_option;

use crate::{
analytics::AnalyticsIden, error::map_eyre_error, state::AppState,
Expand Down Expand Up @@ -171,7 +171,7 @@ pub struct Query {
pub async fn handler(
Extension(templates): Extension<TemplatesWithContext>,
axum::extract::Query(mut query): axum::extract::Query<Query>,
headers: axum::headers::HeaderMap,
headers: headers::HeaderMap,
State(state): State<AppState>,
) -> axum::response::Result<Response> {
let empty_uri_filter: bool = query
Expand Down
12 changes: 8 additions & 4 deletions src/analytics.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
use std::{collections::HashMap, num::NonZeroU32, sync::Arc};

use average::WeightedMean;
use axum::{extract::State, middleware::Next, response::Response};
use axum::{
extract::{Request, State},
middleware::Next,
response::Response,
};
use cronchik::CronSchedule;
use eyre::{Context, ContextCompat};
use futures::{lock::Mutex, StreamExt};
use governor::{state::StreamRateLimitExt, Quota, RateLimiter};
use http::{Request, StatusCode};
use http::StatusCode;
use nonzero_ext::nonzero;
use rusqlite::Row;
use sea_query::{ConditionalStatement, Expr, Func, Order, Query, SimpleExpr, SqliteQueryBuilder};
use sea_query::{ConditionalStatement, Expr, Order, Query, SimpleExpr, SqliteQueryBuilder};
use sea_query_rusqlite::RusqliteBinder;
use serde::Serialize;
use time::{format_description::well_known::Rfc3339, Duration, OffsetDateTime};
Expand Down Expand Up @@ -471,7 +475,7 @@ pub fn channel() -> (mpsc::Sender<Event>, mpsc::Receiver<Event>) {

/// Middleware for performing analytics on incoming requests.
#[tracing::instrument(skip_all)]
pub async fn middleware<B>(state: State<AppState>, request: Request<B>, next: Next<B>) -> Response {
pub async fn middleware(state: State<AppState>, request: Request, next: Next) -> Response {
let uri = Uri::from(request.uri().clone());
let is_bot = request
.extensions()
Expand Down
4 changes: 2 additions & 2 deletions src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use http::{HeaderValue, StatusCode};
use secrecy::{ExposeSecret, SecretString};
use std::{pin::Pin, sync::Arc};
use tokio::sync::OnceCell;
use tower_http::auth::AsyncAuthorizeRequest;
use tower_http::{auth::AsyncAuthorizeRequest, body::UnsyncBoxBody};

/// Basic authentication for accessing logs.
#[derive(Clone)]
Expand All @@ -27,7 +27,7 @@ impl MyBasicAuth {
}

impl<B: Send + 'static> AsyncAuthorizeRequest<B> for MyBasicAuth {
type ResponseBody = http_body::combinators::UnsyncBoxBody<axum::body::Bytes, axum::Error>;
type ResponseBody = axum::body::Body;
type RequestBody = B;
type Future = Pin<
Box<
Expand Down
12 changes: 12 additions & 0 deletions src/cache_control.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use axum::{extract::Request, middleware::Next, response::Response};
use headers::{CacheControl, HeaderMapExt};

/// Middleware to set the [`CacheControl`] header on all reponses to `no-store` to prevent browsers
/// from caching dynamic pages and causing unexpected lag in updates.
pub async fn no_store_middleware(request: Request, next: Next) -> Response {
let mut response = next.run(request).await;
response
.headers_mut()
.typed_insert(CacheControl::new().with_no_store());
response
}
19 changes: 13 additions & 6 deletions src/database/backup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use std::time::Duration;

use base64::Engine;
use eyre::{bail, Context, ContextCompat};
use http::{HeaderValue, StatusCode};
use humansize::format_size;
use md5::{Digest, Md5};
use rusty_s3::{Credentials, S3Action, UrlStyle};
Expand Down Expand Up @@ -51,7 +50,7 @@ async fn perform_backup(config: &Config) -> eyre::Result<BackupInfo> {
.head(head_bucket.sign(Duration::from_secs(60 * 60)))
.send()
.await?;
if response.status() == StatusCode::NOT_FOUND {
if response.status() == reqwest::StatusCode::NOT_FOUND {
bail!("Unable to perform backup, the bucket {s3_bucket_name} does not exist in the region {s3_bucket_region}")
}

Expand Down Expand Up @@ -121,10 +120,18 @@ async fn perform_backup(config: &Config) -> eyre::Result<BackupInfo> {
.to_str()?
.to_owned()
.replace('"', ""),
version_id: Option::transpose(headers.get("x-amz-version-id").map(HeaderValue::to_str))?
.map(ToOwned::to_owned),
expiration: Option::transpose(headers.get("x-amz-expiration").map(HeaderValue::to_str))?
.map(ToOwned::to_owned),
version_id: Option::transpose(
headers
.get("x-amz-version-id")
.map(reqwest::header::HeaderValue::to_str),
)?
.map(ToOwned::to_owned),
expiration: Option::transpose(
headers
.get("x-amz-expiration")
.map(reqwest::header::HeaderValue::to_str),
)?
.map(ToOwned::to_owned),
};

let backup_size = format_size(info.size, humansize::BINARY);
Expand Down
10 changes: 3 additions & 7 deletions src/database/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use axum::extract::State;
use axum::extract::{Request, State};
use axum::middleware::Next;
use axum::response::{IntoResponse, Response};
use deadpool_sqlite::PoolError;
use http::{Request, StatusCode};
use http::StatusCode;
use nonzero_ext::nonzero;
use std::path::Path;
use std::sync::Arc;
Expand Down Expand Up @@ -101,11 +101,7 @@ impl DatabaseInstance {
}

#[tracing::instrument(skip_all)]
pub async fn middleware<B>(
state: State<AppState>,
mut request: Request<B>,
next: Next<B>,
) -> Response {
pub async fn middleware(state: State<AppState>, mut request: Request, next: Next) -> Response {
let database = match state.database.get().await {
Ok(database) => database,
Err(error) => {
Expand Down
5 changes: 3 additions & 2 deletions src/disclaimer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ use crate::{
templates::{render, TemplatesWithContext},
};
use axum::{
extract::Request,
middleware::Next,
response::{IntoResponse, Redirect, Response},
};
use axum_extra::extract::CookieJar;
use eyre::{Context, ContextCompat};
use http::{header::SET_COOKIE, HeaderMap, HeaderValue, Request};
use http::{header::SET_COOKIE, HeaderMap, HeaderValue};

const DISCLAIMER_COOKIE_NAME: &str = "disclaimer";
/// TODO: if this version is updated we need new logic to require the current version of the
Expand Down Expand Up @@ -40,7 +41,7 @@ pub async fn handler(headers: HeaderMap) -> axum::response::Result<impl IntoResp
Ok(response)
}

pub async fn middleware<B>(request: Request<B>, next: Next<B>) -> Response {
pub async fn middleware(request: Request, next: Next) -> Response {
let is_bot = request
.extensions()
.get::<IsBot>()
Expand Down
25 changes: 9 additions & 16 deletions src/i18n.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,24 @@
use axum::{
extract::State,
http::{HeaderMap, HeaderValue, Request},
extract::{Request, State},
http::{HeaderMap, HeaderValue},
middleware::Next,
response::{IntoResponse, Redirect, Response},
response::Response,
};
use axum_extra::extract::CookieJar;
use eyre::{Context, ContextCompat};
use http::{header::SET_COOKIE, StatusCode};
use i18n_embed::{
fluent::{fluent_language_loader, FluentLanguageLoader, NegotiationStrategy},
LanguageLoader,
};
use rust_embed::RustEmbed;
use serde::Deserialize;
use std::{collections::HashMap, str::FromStr, sync::Arc};
use std::{collections::HashMap, sync::Arc};
use time::OffsetDateTime;

use crate::{
error::{map_eyre_error, map_std_error},
state::AppState,
user_preferences::UserPreferences,
};
use crate::{state::AppState, user_preferences::UserPreferences};

#[derive(RustEmbed)]
#[folder = "i18n/"]
struct Localizations;

#[derive(Clone)]
pub struct RequestedLanguages(pub Vec<unic_langid::LanguageIdentifier>);

impl std::fmt::Display for RequestedLanguages {
Expand Down Expand Up @@ -87,11 +80,11 @@ pub fn load_languages(loader: &I18nLoader) -> eyre::Result<()> {
Ok(())
}

pub async fn middleware<B>(
pub async fn middleware(
State(state): State<AppState>,
headers: HeaderMap,
mut request: Request<B>,
next: Next<B>,
mut request: Request,
next: Next,
) -> Response {
let preferences: &UserPreferences = request
.extensions()
Expand Down
9 changes: 8 additions & 1 deletion src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use axum::{extract::State, response::IntoResponse, Extension};
use color_eyre::Help;
use eyre::{bail, eyre, Context, ContextCompat};
use futures::{stream, StreamExt, TryStreamExt};
use headers::{CacheControl, Header, HeaderMapExt};
use i18n_embed::LanguageLoader;
use serde::Serialize;
use unic_langid::LanguageIdentifier;
Expand Down Expand Up @@ -232,5 +233,11 @@ pub async fn handler(
forecasts,
errors,
};
render(&templates.environment, "index.html", &index).map_err(map_eyre_error)
let mut response = render(&templates.environment, "index.html", &index)
.map_err(map_eyre_error)?
.into_response();
response
.headers_mut()
.typed_insert(CacheControl::new().with_no_store());
Ok(response)
}
15 changes: 10 additions & 5 deletions src/isbot.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
use async_trait::async_trait;
use axum::{extract::FromRequestParts, middleware::Next, response::Response};
use http::{header::USER_AGENT, request::Parts, HeaderMap, Request, StatusCode};
use axum::{
extract::{FromRequestParts, Request},
middleware::Next,
response::Response,
};
use http::{header::USER_AGENT, request::Parts, HeaderMap, StatusCode};

#[derive(Copy, Clone)]
pub struct IsBot(bool);

impl IsBot {
Expand All @@ -22,10 +27,10 @@ pub fn is_bot(headers: &HeaderMap) -> bool {
}

/// Middleware to detect whether the request is from a bot based on the [`USER_AGENT`] header.
pub async fn middleware<B>(
pub async fn middleware(
is_bot: IsBot,
mut request: Request<B>,
next: Next<B>,
mut request: Request,
next: Next,
) -> Result<Response, StatusCode> {
request.extensions_mut().insert(is_bot);
Ok(next.run(request).await)
Expand Down
Loading

0 comments on commit 3ff6159

Please sign in to comment.