use sofie::core::Instant;
use sofie::http::{Area, Routes, Shipped};
use sofie::prelude::*;
use super::{AccountSession, Impersonations, ending_all, impersonations_in};
use crate::account_of;
use crate::signing_in::OwnSession;
pub fn routes<A: Area>(routes: &mut Routes<A>) {
routes
.shipped(
"/sessions",
"sessions",
(include_str!("screen.html"), None),
own::<A>,
)
.name("list_sessions");
routes
.post("/sessions/{account_session}/end", end_one::<A>)
.name("end_session")
.then_screen("sessions", "sessions");
routes
.post("/sessions/others", end_others::<A>)
.name("end_other_sessions")
.then_screen("sessions", "sessions");
}
#[derive(ReadModel)]
#[read(from = AccountSession)]
struct Live {
id: Id<AccountSession>,
area: String,
device: Option<String>,
last_seen_at: Instant,
created_at: Instant,
}
async fn own<A: Area>(principal: Principal<A>, read: Read) -> Result<Vec<Live>> {
Live::query()
.where_(AccountSession::ACCOUNT.eq(account_of(&principal)))
.where_(AccountSession::ENDED_AT.is_null())
.latest()
.all(&read)
.await
}
async fn end_one<A: Area>(
principal: Principal<A>,
_own: OwnSession,
mut record: AccountSession,
read: Read,
clock: Clock,
) -> Result<(AccountSession, Impersonations)> {
if record.account != account_of(&principal) {
return Err(Error::NotFound);
}
let now = clock.now();
record.end(now);
let impersonations = impersonations_in(&read, std::slice::from_ref(&record), now).await?;
Ok((record, impersonations))
}
async fn end_others<A: Area>(
principal: Principal<A>,
_own: OwnSession,
session: Session,
read: Read,
clock: Clock,
) -> Result<(Vec<AccountSession>, Impersonations)> {
let now = clock.now();
let ended = ending_all(&read, account_of(&principal), session.current(), now).await?;
let impersonations = impersonations_in(&read, &ended, now).await?;
Ok((ended, impersonations))
}