ocre-cli 0.2.0

Command-line tool for Ocre: create, generate, migrate, run and deploy apps.
//! The devices signed in to an account, and signing them out (HTML).
//! Generated by `ocre g auth --db-sessions`.

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 })
}

/// Signs one device out (this one included: the next page asks to log in).
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"))
}

/// "Sign out everywhere else", e.g. after a lost phone or a password change.
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"))
}