summaryrefslogtreecommitdiff
path: root/src/login
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2024-10-05 20:32:02 -0400
committerOwen Jacobson <owen@grimoire.ca>2024-10-05 22:47:12 -0400
commit1fb26ad31d385ddc628e1b73d6a8764981ca6885 (patch)
treeda226cfc7e054ce93bf37da943a395dee226baa6 /src/login
parent8edd5625ad5dde0ef1637d5c89e9901b3ee65d73 (diff)
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.
Diffstat (limited to 'src/login')
-rw-r--r--src/login/routes.rs65
1 files changed, 65 insertions, 0 deletions
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<App> {
async fn boot(State(app): State<App>, login: Login) -> Result<Boot, Internal> {
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<App>, login: Login) -> Result<Boot, Internal> {
struct Boot {
login: Login,
resume_point: String,
+ channels: Vec<BootChannel>,
+}
+
+#[derive(serde::Serialize)]
+struct BootChannel {
+ #[serde(flatten)]
+ channel: Channel,
+ messages: Vec<BootMessage>,
+}
+
+impl BootChannel {
+ fn new(channel: Channel, messages: impl IntoIterator<Item = Message>) -> 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<Message> 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 {