Compare commits
	
		
			2 Commits 
		
	
	
		
			82ccad196c
			...
			823133c034
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
									
								
								 | 
						823133c034 | |
| 
							
							
								
									
								
								 | 
						bf132f93dc | 
| 
						 | 
				
			
			@ -65,3 +65,9 @@ pub struct Subscription {
 | 
			
		|||
    pub url: String,
 | 
			
		||||
    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> {
 | 
			
		||||
        self.store.get_user(username)?.ok_or(AuthErr::UnknownUser)
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,6 +38,10 @@ pub trait AuthStore {
 | 
			
		|||
    /// Retrieve the session with the given session ID
 | 
			
		||||
    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
 | 
			
		||||
    fn get_user(&self, username: &str) -> Result<Option<User>, AuthErr>;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -114,4 +114,31 @@ impl gpodder::AuthStore for SqliteRepository {
 | 
			
		|||
                .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,
 | 
			
		||||
    http::StatusCode,
 | 
			
		||||
    middleware::Next,
 | 
			
		||||
    response::{IntoResponse, Redirect, Response},
 | 
			
		||||
    routing::get,
 | 
			
		||||
    response::{IntoResponse, Response},
 | 
			
		||||
    Router,
 | 
			
		||||
};
 | 
			
		||||
use http_body_util::BodyExt;
 | 
			
		||||
| 
						 | 
				
			
			@ -26,11 +25,10 @@ pub struct Context {
 | 
			
		|||
pub fn app(ctx: Context) -> Router {
 | 
			
		||||
    Router::new()
 | 
			
		||||
        .merge(gpodder::router(ctx.clone()))
 | 
			
		||||
        .merge(web::router(ctx.clone()))
 | 
			
		||||
        .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(body_logger))
 | 
			
		||||
        // .layer(axum::middleware::from_fn(body_logger))
 | 
			
		||||
        .layer(TraceLayer::new_for_http())
 | 
			
		||||
        .with_state(ctx)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +43,7 @@ async fn header_logger(request: Request, next: Next) -> Response {
 | 
			
		|||
    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 bytes = match body
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,7 +43,7 @@ async fn get_login(State(ctx): State<Context>, headers: HeaderMap, jar: CookieJa
 | 
			
		|||
        .flatten()
 | 
			
		||||
        .is_some()
 | 
			
		||||
    {
 | 
			
		||||
        Redirect::to("/_").into_response()
 | 
			
		||||
        Redirect::to("/").into_response()
 | 
			
		||||
    } else {
 | 
			
		||||
        View::Login
 | 
			
		||||
            .page(&headers)
 | 
			
		||||
| 
						 | 
				
			
			@ -87,7 +87,7 @@ async fn post_login(
 | 
			
		|||
                    .path("/")
 | 
			
		||||
                    .max_age(Duration::days(365)),
 | 
			
		||||
            ),
 | 
			
		||||
            Redirect::to("/_"),
 | 
			
		||||
            Redirect::to("/"),
 | 
			
		||||
        )
 | 
			
		||||
            .into_response()),
 | 
			
		||||
        Err(AuthErr::UnknownUser | AuthErr::InvalidPassword) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -129,7 +129,7 @@ pub async fn auth_web_middleware(
 | 
			
		|||
) -> Response {
 | 
			
		||||
    // SAFETY: this extractor's error type is Infallible
 | 
			
		||||
    let jar: CookieJar = req.extract_parts().await.unwrap();
 | 
			
		||||
    let redirect = Redirect::to("/_/login");
 | 
			
		||||
    let redirect = Redirect::to("/login");
 | 
			
		||||
 | 
			
		||||
    match extract_session(ctx, &jar).await {
 | 
			
		||||
        Ok(Some(session)) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
<article>
 | 
			
		||||
    <form hx-post="/_/login" hx-target="#inner">
 | 
			
		||||
    <form hx-post="/login" hx-target="#inner">
 | 
			
		||||
        <label for="username">Username:</label>
 | 
			
		||||
        <input type="text" id="username" name="username">
 | 
			
		||||
        <label for="password">Password:</label>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue