feat(web): add users page
parent
4854c84601
commit
30609b1cef
|
@ -77,6 +77,7 @@ pub fn router(ctx: Context) -> Router<Context> {
|
||||||
.route("/login", get(get_login).post(post_login))
|
.route("/login", get(get_login).post(post_login))
|
||||||
.route("/logout", post(post_logout))
|
.route("/logout", post(post_logout))
|
||||||
.merge(sessions::router(ctx.clone()))
|
.merge(sessions::router(ctx.clone()))
|
||||||
|
.merge(users::router(ctx.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_index(
|
async fn get_index(
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
use axum::{
|
||||||
|
Extension, Router,
|
||||||
|
extract::{Path, Query, State},
|
||||||
|
http::HeaderMap,
|
||||||
|
routing::{delete, get},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
server::{
|
||||||
|
Context,
|
||||||
|
error::{AppError, AppResult},
|
||||||
|
},
|
||||||
|
web::{Page, TemplateExt, TemplateResponse, ToQuery, View},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn router(ctx: Context) -> Router<Context> {
|
||||||
|
Router::new()
|
||||||
|
.route("/users", get(get_users))
|
||||||
|
.route_layer(axum::middleware::from_fn_with_state(
|
||||||
|
ctx.clone(),
|
||||||
|
super::auth_web_middleware,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_users(
|
||||||
|
State(ctx): State<Context>,
|
||||||
|
headers: HeaderMap,
|
||||||
|
Extension(session): Extension<gpodder::Session>,
|
||||||
|
Query(page): Query<super::Pagination>,
|
||||||
|
) -> AppResult<TemplateResponse<Page<View>>> {
|
||||||
|
let next_page = page.next_page();
|
||||||
|
let user_id = session.user.id;
|
||||||
|
let users = tokio::task::spawn_blocking(move || {
|
||||||
|
ctx.store.admin(&session.user)?.paginated_users(page.into())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap()?;
|
||||||
|
|
||||||
|
let next_page_query =
|
||||||
|
(users.len() == next_page.per_page as usize).then_some(next_page.to_query());
|
||||||
|
|
||||||
|
Ok(View::Users(users, user_id, next_page_query)
|
||||||
|
.page(&headers)
|
||||||
|
.headers(&headers)
|
||||||
|
.authenticated(true)
|
||||||
|
.response(&ctx.tera))
|
||||||
|
}
|
|
@ -85,6 +85,10 @@ pub fn initialize_tera() -> tera::Result<tera::Tera> {
|
||||||
View::Sessions(Vec::new(), 0, None).template(),
|
View::Sessions(Vec::new(), 0, None).template(),
|
||||||
include_str!("templates/views/sessions.html"),
|
include_str!("templates/views/sessions.html"),
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
View::Users(Vec::new(), 0, None).template(),
|
||||||
|
include_str!("templates/views/users.html"),
|
||||||
|
),
|
||||||
])?;
|
])?;
|
||||||
|
|
||||||
Ok(tera)
|
Ok(tera)
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
<h1>Users</h1>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<th>Username</th>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for user in users %}
|
||||||
|
<th>{{ user.username }}</th>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{%- if next_page_query %}
|
||||||
|
<tr
|
||||||
|
hx-get="/users?{{ next_page_query }}"
|
||||||
|
hx-trigger="revealed"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
hx-select="table > tbody > tr"
|
||||||
|
></tr>
|
||||||
|
{% endif %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
|
@ -7,6 +7,7 @@ pub enum View {
|
||||||
Index,
|
Index,
|
||||||
Login,
|
Login,
|
||||||
Sessions(Vec<gpodder::Session>, i64, Option<Query>),
|
Sessions(Vec<gpodder::Session>, i64, Option<Query>),
|
||||||
|
Users(Vec<gpodder::User>, i64, Option<Query>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
@ -16,12 +17,19 @@ struct Session {
|
||||||
last_seen: DateTime<Utc>,
|
last_seen: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct User {
|
||||||
|
id: i64,
|
||||||
|
username: String,
|
||||||
|
}
|
||||||
|
|
||||||
impl Template for View {
|
impl Template for View {
|
||||||
fn template(&self) -> &'static str {
|
fn template(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::Index => "views/index.html",
|
Self::Index => "views/index.html",
|
||||||
Self::Login => "views/login.html",
|
Self::Login => "views/login.html",
|
||||||
Self::Sessions(..) => "views/sessions.html",
|
Self::Sessions(..) => "views/sessions.html",
|
||||||
|
Self::Users(..) => "views/users.html",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +49,18 @@ impl Template for View {
|
||||||
ctx.insert("next_page_query", &query.encode());
|
ctx.insert("next_page_query", &query.encode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Self::Users(users, current_user_id, query) => {
|
||||||
|
ctx.insert(
|
||||||
|
"users",
|
||||||
|
&users.into_iter().map(User::from).collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.insert("current_user_id", ¤t_user_id);
|
||||||
|
|
||||||
|
if let Some(query) = query {
|
||||||
|
ctx.insert("next_page_query", &query.encode());
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -57,3 +77,12 @@ impl From<gpodder::Session> for Session {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<gpodder::User> for User {
|
||||||
|
fn from(value: gpodder::User) -> Self {
|
||||||
|
Self {
|
||||||
|
id: value.id,
|
||||||
|
username: value.username,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue