feat(web): don't show remove button for current session

main
Jef Roosens 2025-06-17 13:45:16 +02:00
parent 21b3450aeb
commit 7887477ed1
No known key found for this signature in database
GPG Key ID: 21FD3D77D56BAF49
6 changed files with 23 additions and 14 deletions

View File

@ -10,7 +10,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
* Web UI
* Started development based on HTMX and PicoCSS
* Very simple homepage
* Login/logout button
* Page for managing logged-in sessions
## [0.1.0](https://git.rustybever.be/Chewing_Bever/otter/src/tag/0.1.0)

View File

@ -42,7 +42,7 @@ impl Default for Pagination {
fn default() -> Self {
Self {
page: 0,
per_page: 1,
per_page: 25,
}
}
}
@ -198,7 +198,7 @@ pub async fn auth_web_middleware(
match extract_session(ctx, &jar).await {
Ok(Some(session)) => {
req.extensions_mut().insert(session.user);
req.extensions_mut().insert(session);
next.run(req).await
}

View File

@ -26,19 +26,20 @@ pub fn router(ctx: Context) -> Router<Context> {
pub async fn get_sessions(
State(ctx): State<Context>,
headers: HeaderMap,
Extension(user): Extension<gpodder::User>,
Extension(session): Extension<gpodder::Session>,
Query(page): Query<super::Pagination>,
) -> AppResult<TemplateResponse<Page<View>>> {
let next_page = page.next_page();
let sessions =
tokio::task::spawn_blocking(move || ctx.store.paginated_sessions(&user, page.into()))
.await
.unwrap()?;
let sessions = tokio::task::spawn_blocking(move || {
ctx.store.paginated_sessions(&session.user, page.into())
})
.await
.unwrap()?;
let next_page_query =
(sessions.len() == next_page.per_page as usize).then_some(next_page.to_query());
Ok(View::Sessions(sessions, next_page_query)
Ok(View::Sessions(sessions, session.id, next_page_query)
.page(&headers)
.headers(&headers)
.authenticated(true)
@ -47,14 +48,14 @@ pub async fn get_sessions(
pub async fn delete_session(
State(ctx): State<Context>,
Extension(user): Extension<gpodder::User>,
Extension(session): Extension<gpodder::Session>,
Path(id): Path<i64>,
) -> AppResult<()> {
tokio::task::spawn_blocking(move || {
let session = ctx.store.get_session(id)?;
let other_session = ctx.store.get_session(id)?;
// Check to ensure a user can't remove a session that's not theirs
if session.user.id != user.id {
if session.user.id != other_session.user.id {
return Err(AppError::Unauthorized);
}

View File

@ -82,7 +82,7 @@ pub fn initialize_tera() -> tera::Result<tera::Tera> {
include_str!("templates/views/login.html"),
),
(
View::Sessions(Vec::new(), None).template(),
View::Sessions(Vec::new(), 0, None).template(),
include_str!("templates/views/sessions.html"),
),
])?;

View File

@ -14,9 +14,13 @@
<th>{{ session.user_agent }}</th>
<th>{{ session.last_seen }}</th>
<th>
{%- if session.id != current_session_id -%}
<a hx-delete="/sessions/{{ session.id }}"
hx-target="closest tr"
>Remove</a>
{%- else -%}
Current session
{%- endif -%}
</th>
</tr>
{% endfor %}

View File

@ -6,7 +6,7 @@ use super::{Query, Template};
pub enum View {
Index,
Login,
Sessions(Vec<gpodder::Session>, Option<Query>),
Sessions(Vec<gpodder::Session>, i64, Option<Query>),
}
#[derive(Serialize)]
@ -30,11 +30,12 @@ impl Template for View {
let template = self.template();
match self {
Self::Sessions(sessions, query) => {
Self::Sessions(sessions, current_session_id, query) => {
ctx.insert(
"sessions",
&sessions.into_iter().map(Session::from).collect::<Vec<_>>(),
);
ctx.insert("current_session_id", &current_session_id);
if let Some(query) = query {
ctx.insert("next_page_query", &query.encode());