Compare commits
2 Commits
82ccad196c
...
823133c034
Author | SHA1 | Date |
---|---|---|
|
823133c034 | |
|
bf132f93dc |
|
@ -65,3 +65,9 @@ pub struct Subscription {
|
||||||
pub url: String,
|
pub url: String,
|
||||||
pub time_changed: DateTime<Utc>,
|
pub time_changed: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Page {
|
||||||
|
pub page: u32,
|
||||||
|
pub per_page: u32,
|
||||||
|
}
|
||||||
|
|
|
@ -37,6 +37,14 @@ impl GpodderRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn paginated_sessions(
|
||||||
|
&self,
|
||||||
|
user: &models::User,
|
||||||
|
page: models::Page,
|
||||||
|
) -> Result<Vec<models::Session>, AuthErr> {
|
||||||
|
self.store.paginated_sessions(user, page)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_user(&self, username: &str) -> Result<models::User, AuthErr> {
|
pub fn get_user(&self, username: &str) -> Result<models::User, AuthErr> {
|
||||||
self.store.get_user(username)?.ok_or(AuthErr::UnknownUser)
|
self.store.get_user(username)?.ok_or(AuthErr::UnknownUser)
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,10 @@ pub trait AuthStore {
|
||||||
/// Retrieve the session with the given session ID
|
/// Retrieve the session with the given session ID
|
||||||
fn get_session(&self, session_id: i64) -> Result<Option<Session>, AuthErr>;
|
fn get_session(&self, session_id: i64) -> Result<Option<Session>, AuthErr>;
|
||||||
|
|
||||||
|
/// Retrieve a paginated list of the given user's sessions, ordered descending by the last seen
|
||||||
|
/// value.
|
||||||
|
fn paginated_sessions(&self, user: &User, page: Page) -> Result<Vec<Session>, AuthErr>;
|
||||||
|
|
||||||
/// Retrieve the user with the given username
|
/// Retrieve the user with the given username
|
||||||
fn get_user(&self, username: &str) -> Result<Option<User>, AuthErr>;
|
fn get_user(&self, username: &str) -> Result<Option<User>, AuthErr>;
|
||||||
|
|
||||||
|
|
|
@ -114,4 +114,31 @@ impl gpodder::AuthStore for SqliteRepository {
|
||||||
.map_err(DbError::from)?,
|
.map_err(DbError::from)?,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn paginated_sessions(
|
||||||
|
&self,
|
||||||
|
user: &gpodder::User,
|
||||||
|
page: gpodder::Page,
|
||||||
|
) -> Result<Vec<gpodder::Session>, AuthErr> {
|
||||||
|
(|| {
|
||||||
|
let sessions = sessions::table
|
||||||
|
.filter(sessions::user_id.eq(user.id))
|
||||||
|
.order(sessions::last_seen.desc())
|
||||||
|
.offset((page.page * page.per_page) as i64)
|
||||||
|
.limit(page.per_page as i64)
|
||||||
|
.select(Session::as_select())
|
||||||
|
.get_results(&mut self.pool.get()?)?
|
||||||
|
.into_iter()
|
||||||
|
.map(|session| gpodder::Session {
|
||||||
|
id: session.id,
|
||||||
|
last_seen: DateTime::from_timestamp(session.last_seen, 0).unwrap(),
|
||||||
|
user_agent: session.user_agent,
|
||||||
|
user: user.clone(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok::<_, DbError>(sessions)
|
||||||
|
})()
|
||||||
|
.map_err(AuthErr::from)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,7 @@ use axum::{
|
||||||
extract::Request,
|
extract::Request,
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
middleware::Next,
|
middleware::Next,
|
||||||
response::{IntoResponse, Redirect, Response},
|
response::{IntoResponse, Response},
|
||||||
routing::get,
|
|
||||||
Router,
|
Router,
|
||||||
};
|
};
|
||||||
use http_body_util::BodyExt;
|
use http_body_util::BodyExt;
|
||||||
|
@ -26,11 +25,10 @@ pub struct Context {
|
||||||
pub fn app(ctx: Context) -> Router {
|
pub fn app(ctx: Context) -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
.merge(gpodder::router(ctx.clone()))
|
.merge(gpodder::router(ctx.clone()))
|
||||||
|
.merge(web::router(ctx.clone()))
|
||||||
.nest("/static", r#static::router())
|
.nest("/static", r#static::router())
|
||||||
.nest("/_", web::router(ctx.clone()))
|
|
||||||
.route("/", get(|| async { Redirect::to("/_") }))
|
|
||||||
.layer(axum::middleware::from_fn(header_logger))
|
.layer(axum::middleware::from_fn(header_logger))
|
||||||
.layer(axum::middleware::from_fn(body_logger))
|
// .layer(axum::middleware::from_fn(body_logger))
|
||||||
.layer(TraceLayer::new_for_http())
|
.layer(TraceLayer::new_for_http())
|
||||||
.with_state(ctx)
|
.with_state(ctx)
|
||||||
}
|
}
|
||||||
|
@ -45,7 +43,7 @@ async fn header_logger(request: Request, next: Next) -> Response {
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn body_logger(request: Request, next: Next) -> Response {
|
async fn _body_logger(request: Request, next: Next) -> Response {
|
||||||
let (parts, body) = request.into_parts();
|
let (parts, body) = request.into_parts();
|
||||||
|
|
||||||
let bytes = match body
|
let bytes = match body
|
||||||
|
|
|
@ -43,7 +43,7 @@ async fn get_login(State(ctx): State<Context>, headers: HeaderMap, jar: CookieJa
|
||||||
.flatten()
|
.flatten()
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
Redirect::to("/_").into_response()
|
Redirect::to("/").into_response()
|
||||||
} else {
|
} else {
|
||||||
View::Login
|
View::Login
|
||||||
.page(&headers)
|
.page(&headers)
|
||||||
|
@ -87,7 +87,7 @@ async fn post_login(
|
||||||
.path("/")
|
.path("/")
|
||||||
.max_age(Duration::days(365)),
|
.max_age(Duration::days(365)),
|
||||||
),
|
),
|
||||||
Redirect::to("/_"),
|
Redirect::to("/"),
|
||||||
)
|
)
|
||||||
.into_response()),
|
.into_response()),
|
||||||
Err(AuthErr::UnknownUser | AuthErr::InvalidPassword) => {
|
Err(AuthErr::UnknownUser | AuthErr::InvalidPassword) => {
|
||||||
|
@ -129,7 +129,7 @@ pub async fn auth_web_middleware(
|
||||||
) -> Response {
|
) -> Response {
|
||||||
// SAFETY: this extractor's error type is Infallible
|
// SAFETY: this extractor's error type is Infallible
|
||||||
let jar: CookieJar = req.extract_parts().await.unwrap();
|
let jar: CookieJar = req.extract_parts().await.unwrap();
|
||||||
let redirect = Redirect::to("/_/login");
|
let redirect = Redirect::to("/login");
|
||||||
|
|
||||||
match extract_session(ctx, &jar).await {
|
match extract_session(ctx, &jar).await {
|
||||||
Ok(Some(session)) => {
|
Ok(Some(session)) => {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<article>
|
<article>
|
||||||
<form hx-post="/_/login" hx-target="#inner">
|
<form hx-post="/login" hx-target="#inner">
|
||||||
<label for="username">Username:</label>
|
<label for="username">Username:</label>
|
||||||
<input type="text" id="username" name="username">
|
<input type="text" id="username" name="username">
|
||||||
<label for="password">Password:</label>
|
<label for="password">Password:</label>
|
||||||
|
|
Loading…
Reference in New Issue