use askama::Template;
use axum::{
Router,
extract::{Path, State},
response::{Html, Redirect},
routing::{get, post},
};
use ocre::{Ctx, Error, Flash, Result, Session, render};
use crate::{
auth::{CurrentUser, session_token},
models::user_session::{self, UserSession},
};
pub fn routes() -> Router<Ctx> {
Router::new()
.route("/account/sessions", get(index))
.route("/account/sessions/others/delete", post(destroy_others))
.route("/account/sessions/{id}/delete", post(destroy))
}
#[derive(Template)]
#[template(path = "auth/user_sessions.html")]
struct IndexView {
flash: Flash,
sessions: Vec<UserSession>,
}
async fn index(
State(ctx): State<Ctx>,
CurrentUser(user): CurrentUser,
session: Session,
flash: Flash,
) -> Result<Html<String>> {
let token = session_token(&session)?.ok_or(Error::Unauthorized)?;
let sessions = user_session::for_user(&ctx, user.id, &token).await?;
render(&IndexView { flash, sessions })
}
async fn destroy(
State(ctx): State<Ctx>,
CurrentUser(user): CurrentUser,
session: Session,
Path(id): Path<i64>,
) -> Result<Redirect> {
if !user_session::revoke(&ctx, user.id, id).await? {
return Err(Error::NotFound);
}
session.flash("notice", "That device is signed out.")?;
Ok(Redirect::to("/account/sessions"))
}
async fn destroy_others(State(ctx): State<Ctx>, CurrentUser(user): CurrentUser, session: Session) -> Result<Redirect> {
let token = session_token(&session)?.ok_or(Error::Unauthorized)?;
let count = user_session::revoke_others(&ctx, user.id, &token).await?;
session.flash("notice", format!("Signed out of {count} other session(s)."))?;
Ok(Redirect::to("/account/sessions"))
}