From 1fb26ad31d385ddc628e1b73d6a8764981ca6885 Mon Sep 17 00:00:00 2001 From: Owen Jacobson Date: Sat, 5 Oct 2024 20:32:02 -0400 Subject: Use `/api/boot` to bootstrap the client. The client now takes an initial snapshot from the response to `/api/boot`, then picks up the event stream at the immediately-successive event to the moment the snapshot was taken. This commit removes the following unused endpoints: * `/api/channels` (GET) * `/api/channels/:channel/messages` (GET) The information therein is now part of the boot response. We can always add 'em back, but I wanted to clear the deck for designing something more capable, for dealing with client needs. --- src/login/routes.rs | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) (limited to 'src/login') diff --git a/src/login/routes.rs b/src/login/routes.rs index 0874cc3..b0e3fee 100644 --- a/src/login/routes.rs +++ b/src/login/routes.rs @@ -5,12 +5,16 @@ use axum::{ routing::{get, post}, Router, }; +use futures::stream::{self, StreamExt as _, TryStreamExt as _}; use crate::{ app::App, + channel::Channel, clock::RequestedAt, error::{Internal, Unauthorized}, + event::Instant, login::{Login, Password}, + message::{self, Message}, token::{app, extract::IdentityToken}, }; @@ -26,9 +30,21 @@ pub fn router() -> Router { async fn boot(State(app): State, login: Login) -> Result { let resume_point = app.logins().boot_point().await?; + let channels = app.channels().all(resume_point.into()).await?; + let channels = stream::iter(channels) + .then(|channel| async { + app.messages() + .in_channel(&channel.id, resume_point.into()) + .await + .map(|messages| BootChannel::new(channel, messages)) + }) + .try_collect() + .await?; + Ok(Boot { login, resume_point: resume_point.to_string(), + channels, }) } @@ -36,6 +52,55 @@ async fn boot(State(app): State, login: Login) -> Result { struct Boot { login: Login, resume_point: String, + channels: Vec, +} + +#[derive(serde::Serialize)] +struct BootChannel { + #[serde(flatten)] + channel: Channel, + messages: Vec, +} + +impl BootChannel { + fn new(channel: Channel, messages: impl IntoIterator) -> Self { + Self { + channel, + messages: messages.into_iter().map(BootMessage::from).collect(), + } + } +} + +#[derive(serde::Serialize)] +struct BootMessage { + #[serde(flatten)] + sent: Instant, + sender: Login, + message: BootMessageBody, +} + +impl From for BootMessage { + fn from(message: Message) -> Self { + let Message { + sent, + channel: _, + sender, + id, + body, + } = message; + + Self { + sent, + sender, + message: BootMessageBody { id, body }, + } + } +} + +#[derive(serde::Serialize)] +struct BootMessageBody { + id: message::Id, + body: String, } impl IntoResponse for Boot { -- cgit v1.2.3