mirror of
https://github.com/Blah-IM/blahrs.git
synced 2025-06-30 19:45:34 +00:00
feat(blahd): impl ETag for feed
This commit is contained in:
parent
fac146e859
commit
70481e6c74
4 changed files with 114 additions and 23 deletions
|
@ -23,7 +23,7 @@ use blah_types::{get_timestamp, Id, Signed, UserKey};
|
|||
use database::{Transaction, TransactionOps};
|
||||
use feed::FeedData;
|
||||
use id::IdExt;
|
||||
use middleware::{Auth, MaybeAuth, ResultExt as _, SignedJson};
|
||||
use middleware::{Auth, ETag, MaybeAuth, ResultExt as _, SignedJson};
|
||||
use parking_lot::Mutex;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use serde_inline_default::serde_inline_default;
|
||||
|
@ -434,11 +434,11 @@ async fn room_get_metadata(
|
|||
|
||||
async fn room_get_feed<FT: feed::FeedType>(
|
||||
st: ArcState,
|
||||
ETag(etag): ETag<Id>,
|
||||
R(OriginalUri(req_uri), _): RE<OriginalUri>,
|
||||
R(Path(rid), _): RE<Path<Id>>,
|
||||
R(Query(mut pagination), _): RE<Query<Pagination>>,
|
||||
) -> Result<Response, ApiError> {
|
||||
// TODO: If-None-Match.
|
||||
let self_url = st
|
||||
.config
|
||||
.base_url
|
||||
|
@ -460,6 +460,12 @@ async fn room_get_feed<FT: feed::FeedType>(
|
|||
Ok((title, msgs, skip_token))
|
||||
})?;
|
||||
|
||||
// Use `Id(0)` as the tag for an empty list.
|
||||
let ret_etag = msgs.first().map_or(Id(0), |msg| msg.cid);
|
||||
if etag == Some(ret_etag) {
|
||||
return Ok(StatusCode::NOT_MODIFIED.into_response());
|
||||
}
|
||||
|
||||
let next_url = skip_token.map(|skip_token| {
|
||||
let next_params = Pagination {
|
||||
skip_token: Some(skip_token),
|
||||
|
@ -478,13 +484,14 @@ async fn room_get_feed<FT: feed::FeedType>(
|
|||
next_url
|
||||
});
|
||||
|
||||
Ok(FT::to_feed_response(FeedData {
|
||||
let resp = FT::to_feed_response(FeedData {
|
||||
rid,
|
||||
title,
|
||||
msgs,
|
||||
self_url,
|
||||
next_url,
|
||||
}))
|
||||
});
|
||||
Ok((ETag(Some(ret_etag)), resp).into_response())
|
||||
}
|
||||
|
||||
/// Get room messages with pagination parameters,
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
use std::backtrace::Backtrace;
|
||||
use std::convert::Infallible;
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::extract::rejection::{JsonRejection, PathRejection, QueryRejection};
|
||||
use axum::extract::{FromRef, FromRequest, FromRequestParts, Request};
|
||||
use axum::http::{header, request, StatusCode};
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use axum::http::{header, request, HeaderValue, StatusCode};
|
||||
use axum::response::{IntoResponse, IntoResponseParts, Response, ResponseParts};
|
||||
use axum::{async_trait, Json};
|
||||
use blah_types::msg::AuthPayload;
|
||||
use blah_types::{Signed, UserKey};
|
||||
|
@ -244,3 +245,42 @@ where
|
|||
Ok(Self(data.signee.user))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ETag<T>(pub Option<T>);
|
||||
|
||||
#[async_trait]
|
||||
impl<S, T: FromStr> FromRequestParts<S> for ETag<T>
|
||||
where
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request_parts(
|
||||
parts: &mut request::Parts,
|
||||
_state: &S,
|
||||
) -> Result<Self, Self::Rejection> {
|
||||
let tag = parts
|
||||
.headers
|
||||
.get(header::IF_NONE_MATCH)
|
||||
.and_then(|v| v.to_str().ok()?.strip_prefix('"')?.strip_suffix('"'))
|
||||
.filter(|s| !s.is_empty())
|
||||
.and_then(|s| s.parse::<T>().ok());
|
||||
Ok(Self(tag))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> IntoResponseParts for ETag<T> {
|
||||
type Error = Infallible;
|
||||
|
||||
fn into_response_parts(self, mut res: ResponseParts) -> Result<ResponseParts, Self::Error> {
|
||||
if let Some(tag) = &self.0 {
|
||||
res.headers_mut().insert(
|
||||
header::ETAG,
|
||||
HeaderValue::from_str(&format!("\"{tag}\""))
|
||||
.expect("ETag must be a valid header value"),
|
||||
);
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue