implement cache-control header
This commit is contained in:
parent
901a2227a7
commit
a0c3a13383
|
@ -195,6 +195,7 @@ dependencies = [
|
|||
"bitflags 1.3.2",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"headers",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
|
@ -823,6 +824,31 @@ dependencies = [
|
|||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "headers"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"bitflags 1.3.2",
|
||||
"bytes",
|
||||
"headers-core",
|
||||
"http",
|
||||
"httpdate",
|
||||
"mime",
|
||||
"sha1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "headers-core"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429"
|
||||
dependencies = [
|
||||
"http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
|
@ -1923,6 +1949,17 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.7"
|
||||
|
|
|
@ -6,7 +6,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
aide = { version = "0.11.0", features = ["axum", "redoc"] }
|
||||
anyhow = "1.0.71"
|
||||
axum = "0.6.18"
|
||||
axum = { version = "0.6.18", features = ["headers"] }
|
||||
chrono = { version = "0.4.26", features = ["serde"] }
|
||||
clap = { version = "4.3.4", features = ["derive"] }
|
||||
clickhouse = { version = "0.11.5", default-features = false, features = [
|
||||
|
|
|
@ -1,16 +1,24 @@
|
|||
pub mod message;
|
||||
|
||||
use chrono::{Datelike, NaiveDate, Utc};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Display;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone, Copy)]
|
||||
pub struct ChannelLogDate {
|
||||
pub year: u32,
|
||||
pub month: u32,
|
||||
pub day: u32,
|
||||
}
|
||||
|
||||
impl ChannelLogDate {
|
||||
pub fn is_today(&self) -> bool {
|
||||
Some(Utc::now().date_naive())
|
||||
== NaiveDate::from_ymd_opt(self.year as i32, self.month, self.day)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ChannelLogDate {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}-{:0>2}-{:0>2}", self.year, self.month, self.day)
|
||||
|
@ -23,6 +31,13 @@ pub struct UserLogDate {
|
|||
pub month: u32,
|
||||
}
|
||||
|
||||
impl UserLogDate {
|
||||
pub fn is_current_month(&self) -> bool {
|
||||
let current = Utc::now().date_naive();
|
||||
current.year() as u32 == self.year && current.month() == self.month
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum UserIdentifier<'a> {
|
||||
|
|
|
@ -18,17 +18,19 @@ use crate::{
|
|||
},
|
||||
Result,
|
||||
};
|
||||
use aide::axum::IntoApiResponse;
|
||||
use axum::{
|
||||
extract::{Path, Query, RawQuery, State},
|
||||
headers::CacheControl,
|
||||
response::Redirect,
|
||||
Json,
|
||||
Json, TypedHeader,
|
||||
};
|
||||
use chrono::{Datelike, Utc};
|
||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||
use std::time::Duration;
|
||||
use tracing::debug;
|
||||
|
||||
pub async fn get_channels(app: State<App>) -> Json<ChannelsList> {
|
||||
pub async fn get_channels(app: State<App>) -> impl IntoApiResponse {
|
||||
let channel_ids = app.config.channels.read().unwrap().clone();
|
||||
|
||||
let channels = app
|
||||
|
@ -36,19 +38,20 @@ pub async fn get_channels(app: State<App>) -> Json<ChannelsList> {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
Json(ChannelsList {
|
||||
let json = Json(ChannelsList {
|
||||
channels: channels
|
||||
.into_iter()
|
||||
.map(|(user_id, name)| Channel { name, user_id })
|
||||
.collect(),
|
||||
})
|
||||
});
|
||||
(cache_header(600), json)
|
||||
}
|
||||
|
||||
pub async fn get_channel_logs(
|
||||
app: State<App>,
|
||||
Path(channel_log_params): Path<ChannelLogsPath>,
|
||||
Query(logs_params): Query<LogsParams>,
|
||||
) -> Result<LogsResponse> {
|
||||
) -> Result<impl IntoApiResponse> {
|
||||
debug!("Params: {logs_params:?}");
|
||||
|
||||
let channel_id = match channel_log_params.channel_info.channel_id_type {
|
||||
|
@ -71,17 +74,25 @@ pub async fn get_channel_logs(
|
|||
|
||||
let stream = read_channel(&app.db, &channel_id, log_date, logs_params.reverse).await?;
|
||||
|
||||
Ok(LogsResponse {
|
||||
let logs = LogsResponse {
|
||||
response_type: logs_params.response_type(),
|
||||
stream,
|
||||
})
|
||||
};
|
||||
|
||||
let cache = if log_date.is_today() {
|
||||
no_cache_header()
|
||||
} else {
|
||||
cache_header(36000)
|
||||
};
|
||||
|
||||
Ok((cache, logs))
|
||||
}
|
||||
|
||||
pub async fn get_user_logs_by_name(
|
||||
app: State<App>,
|
||||
path: Path<UserLogsPath>,
|
||||
params: Query<LogsParams>,
|
||||
) -> Result<LogsResponse> {
|
||||
) -> Result<impl IntoApiResponse> {
|
||||
let user_id = app
|
||||
.get_users(vec![], vec![path.user.clone()])
|
||||
.await?
|
||||
|
@ -97,7 +108,7 @@ pub async fn get_user_logs_by_id(
|
|||
app: State<App>,
|
||||
path: Path<UserLogsPath>,
|
||||
params: Query<LogsParams>,
|
||||
) -> Result<LogsResponse> {
|
||||
) -> Result<impl IntoApiResponse> {
|
||||
let user_id = path.user.clone();
|
||||
get_user_logs(app, path, params, user_id).await
|
||||
}
|
||||
|
@ -107,7 +118,7 @@ async fn get_user_logs(
|
|||
Path(user_logs_path): Path<UserLogsPath>,
|
||||
Query(logs_params): Query<LogsParams>,
|
||||
user_id: String,
|
||||
) -> Result<LogsResponse> {
|
||||
) -> Result<impl IntoApiResponse> {
|
||||
let log_date = UserLogDate::try_from(&user_logs_path)?;
|
||||
|
||||
let channel_id = match user_logs_path.channel_info.channel_id_type {
|
||||
|
@ -135,16 +146,24 @@ async fn get_user_logs(
|
|||
)
|
||||
.await?;
|
||||
|
||||
Ok(LogsResponse {
|
||||
let logs = LogsResponse {
|
||||
stream,
|
||||
response_type: logs_params.response_type(),
|
||||
})
|
||||
};
|
||||
|
||||
let cache = if log_date.is_current_month() {
|
||||
no_cache_header()
|
||||
} else {
|
||||
cache_header(36000)
|
||||
};
|
||||
|
||||
Ok((cache, logs))
|
||||
}
|
||||
|
||||
pub async fn list_available_logs(
|
||||
Query(AvailableLogsParams { user, channel }): Query<AvailableLogsParams>,
|
||||
app: State<App>,
|
||||
) -> Result<Json<AvailableLogs>> {
|
||||
) -> Result<impl IntoApiResponse> {
|
||||
let channel_id = match channel {
|
||||
ChannelParam::ChannelId(id) => id,
|
||||
ChannelParam::Channel(name) => app.get_user_id_by_name(&name).await?,
|
||||
|
@ -163,7 +182,7 @@ pub async fn list_available_logs(
|
|||
};
|
||||
|
||||
if !available_logs.is_empty() {
|
||||
Ok(Json(AvailableLogs { available_logs }))
|
||||
Ok((cache_header(600), Json(AvailableLogs { available_logs })))
|
||||
} else {
|
||||
Err(Error::NotFound)
|
||||
}
|
||||
|
@ -232,7 +251,7 @@ pub async fn random_channel_line(
|
|||
channel,
|
||||
}): Path<LogsPathChannel>,
|
||||
Query(logs_params): Query<LogsParams>,
|
||||
) -> Result<LogsResponse> {
|
||||
) -> Result<impl IntoApiResponse> {
|
||||
let channel_id = match channel_id_type {
|
||||
ChannelIdType::Name => app.get_user_id_by_name(&channel).await?,
|
||||
ChannelIdType::Id => channel,
|
||||
|
@ -241,10 +260,11 @@ pub async fn random_channel_line(
|
|||
let random_line = read_random_channel_line(&app.db, &channel_id).await?;
|
||||
let stream = LogsStream::new_provided(vec![random_line])?;
|
||||
|
||||
Ok(LogsResponse {
|
||||
let logs = LogsResponse {
|
||||
stream,
|
||||
response_type: logs_params.response_type(),
|
||||
})
|
||||
};
|
||||
Ok((no_cache_header(), logs))
|
||||
}
|
||||
|
||||
pub async fn random_user_line_by_name(
|
||||
|
@ -255,7 +275,7 @@ pub async fn random_user_line_by_name(
|
|||
user,
|
||||
}): Path<UserLogPathParams>,
|
||||
query: Query<LogsParams>,
|
||||
) -> Result<LogsResponse> {
|
||||
) -> Result<impl IntoApiResponse> {
|
||||
let user_id = app.get_user_id_by_name(&user).await?;
|
||||
random_user_line(app, channel_id_type, channel, user_id, query).await
|
||||
}
|
||||
|
@ -268,7 +288,7 @@ pub async fn random_user_line_by_id(
|
|||
user,
|
||||
}): Path<UserLogPathParams>,
|
||||
query: Query<LogsParams>,
|
||||
) -> Result<LogsResponse> {
|
||||
) -> Result<impl IntoApiResponse> {
|
||||
random_user_line(app, channel_id_type, channel, user, query).await
|
||||
}
|
||||
|
||||
|
@ -278,7 +298,7 @@ async fn random_user_line(
|
|||
channel: String,
|
||||
user_id: String,
|
||||
Query(logs_params): Query<LogsParams>,
|
||||
) -> Result<LogsResponse> {
|
||||
) -> Result<impl IntoApiResponse> {
|
||||
let channel_id = match channel_id_type {
|
||||
ChannelIdType::Name => app.get_user_id_by_name(&channel).await?,
|
||||
ChannelIdType::Id => channel,
|
||||
|
@ -287,10 +307,11 @@ async fn random_user_line(
|
|||
let random_line = read_random_user_line(&app.db, &channel_id, &user_id).await?;
|
||||
let stream = LogsStream::new_provided(vec![random_line])?;
|
||||
|
||||
Ok(LogsResponse {
|
||||
let logs = LogsResponse {
|
||||
stream,
|
||||
response_type: logs_params.response_type(),
|
||||
})
|
||||
};
|
||||
Ok((no_cache_header(), logs))
|
||||
}
|
||||
|
||||
pub async fn optout(app: State<App>) -> Json<String> {
|
||||
|
@ -312,3 +333,15 @@ pub async fn optout(app: State<App>) -> Json<String> {
|
|||
|
||||
Json(optout_code)
|
||||
}
|
||||
|
||||
fn cache_header(secs: u64) -> TypedHeader<CacheControl> {
|
||||
TypedHeader(
|
||||
CacheControl::new()
|
||||
.with_public()
|
||||
.with_max_age(Duration::from_secs(secs)),
|
||||
)
|
||||
}
|
||||
|
||||
fn no_cache_header() -> TypedHeader<CacheControl> {
|
||||
TypedHeader(CacheControl::new().with_no_cache())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue