summaryrefslogtreecommitdiff
path: root/src/repo
diff options
context:
space:
mode:
authorKit La Touche <kit@transneptune.net>2024-09-28 21:55:50 -0400
committerKit La Touche <kit@transneptune.net>2024-09-28 21:55:50 -0400
commit897eef0306917baf3662e691b29f182d35805296 (patch)
tree024e2a3fa13ac96e0b4339a6d62ae533efe7db07 /src/repo
parentc524b333befc8cc97aa49f73b3ed28bc3b82420c (diff)
parent4d0bb0709b168a24ab6a8dbc86da45d7503596ee (diff)
Merge branch 'main' into feature-frontend
Diffstat (limited to 'src/repo')
-rw-r--r--src/repo/channel.rs84
-rw-r--r--src/repo/login/extract.rs2
-rw-r--r--src/repo/message.rs6
-rw-r--r--src/repo/token.rs10
4 files changed, 87 insertions, 15 deletions
diff --git a/src/repo/channel.rs b/src/repo/channel.rs
index 0186413..3c7468f 100644
--- a/src/repo/channel.rs
+++ b/src/repo/channel.rs
@@ -2,7 +2,11 @@ use std::fmt;
use sqlx::{sqlite::Sqlite, SqliteConnection, Transaction};
-use crate::id::Id as BaseId;
+use crate::{
+ clock::DateTime,
+ events::types::{self, Sequence},
+ id::Id as BaseId,
+};
pub trait Provider {
fn channels(&mut self) -> Channels;
@@ -16,26 +20,38 @@ impl<'c> Provider for Transaction<'c, Sqlite> {
pub struct Channels<'t>(&'t mut SqliteConnection);
-#[derive(Debug, Eq, PartialEq, serde::Serialize)]
+#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
pub struct Channel {
pub id: Id,
pub name: String,
+ #[serde(skip)]
+ pub created_at: DateTime,
}
impl<'c> Channels<'c> {
- pub async fn create(&mut self, name: &str) -> Result<Channel, sqlx::Error> {
+ pub async fn create(
+ &mut self,
+ name: &str,
+ created_at: &DateTime,
+ ) -> Result<Channel, sqlx::Error> {
let id = Id::generate();
+ let sequence = Sequence::default();
let channel = sqlx::query_as!(
Channel,
r#"
insert
- into channel (id, name)
- values ($1, $2)
- returning id as "id: Id", name
+ into channel (id, name, created_at, last_sequence)
+ values ($1, $2, $3, $4)
+ returning
+ id as "id: Id",
+ name,
+ created_at as "created_at: DateTime"
"#,
id,
name,
+ created_at,
+ sequence,
)
.fetch_one(&mut *self.0)
.await?;
@@ -47,7 +63,10 @@ impl<'c> Channels<'c> {
let channel = sqlx::query_as!(
Channel,
r#"
- select id as "id: Id", name
+ select
+ id as "id: Id",
+ name,
+ created_at as "created_at: DateTime"
from channel
where id = $1
"#,
@@ -64,8 +83,9 @@ impl<'c> Channels<'c> {
Channel,
r#"
select
- channel.id as "id: Id",
- channel.name
+ id as "id: Id",
+ name,
+ created_at as "created_at: DateTime"
from channel
order by channel.name
"#,
@@ -75,6 +95,52 @@ impl<'c> Channels<'c> {
Ok(channels)
}
+
+ pub async fn delete_expired(
+ &mut self,
+ channel: &Channel,
+ sequence: Sequence,
+ deleted_at: &DateTime,
+ ) -> Result<types::ChannelEvent, sqlx::Error> {
+ let channel = channel.id.clone();
+ sqlx::query_scalar!(
+ r#"
+ delete from channel
+ where id = $1
+ returning 1 as "row: i64"
+ "#,
+ channel,
+ )
+ .fetch_one(&mut *self.0)
+ .await?;
+
+ Ok(types::ChannelEvent {
+ sequence,
+ at: *deleted_at,
+ data: types::DeletedEvent { channel }.into(),
+ })
+ }
+
+ pub async fn expired(&mut self, expired_at: &DateTime) -> Result<Vec<Channel>, sqlx::Error> {
+ let channels = sqlx::query_as!(
+ Channel,
+ r#"
+ select
+ channel.id as "id: Id",
+ channel.name,
+ channel.created_at as "created_at: DateTime"
+ from channel
+ left join message
+ where created_at < $1
+ and message.id is null
+ "#,
+ expired_at,
+ )
+ .fetch_all(&mut *self.0)
+ .await?;
+
+ Ok(channels)
+ }
}
// Stable identifier for a [Channel]. Prefixed with `C`.
diff --git a/src/repo/login/extract.rs b/src/repo/login/extract.rs
index e5f96d0..c127078 100644
--- a/src/repo/login/extract.rs
+++ b/src/repo/login/extract.rs
@@ -32,7 +32,7 @@ impl FromRequestParts<App> for Login {
let secret = identity_token.secret().ok_or(LoginError::Unauthorized)?;
let app = State::<App>::from_request_parts(parts, state).await?;
- match app.logins().validate(secret, &used_at).await {
+ match app.logins().validate(&secret, &used_at).await {
Ok(login) => Ok(login),
Err(ValidateError::InvalidToken) => Err(LoginError::Unauthorized),
Err(other) => Err(other.into()),
diff --git a/src/repo/message.rs b/src/repo/message.rs
index 385b103..a1f73d5 100644
--- a/src/repo/message.rs
+++ b/src/repo/message.rs
@@ -25,3 +25,9 @@ impl fmt::Display for Id {
self.0.fmt(f)
}
}
+
+#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
+pub struct Message {
+ pub id: Id,
+ pub body: String,
+}
diff --git a/src/repo/token.rs b/src/repo/token.rs
index 8276bea..15eef48 100644
--- a/src/repo/token.rs
+++ b/src/repo/token.rs
@@ -2,7 +2,7 @@ use sqlx::{sqlite::Sqlite, SqliteConnection, Transaction};
use uuid::Uuid;
use super::login::{self, Login};
-use crate::clock::DateTime;
+use crate::{clock::DateTime, login::extract::IdentitySecret};
pub trait Provider {
fn tokens(&mut self) -> Tokens;
@@ -23,7 +23,7 @@ impl<'c> Tokens<'c> {
&mut self,
login: &Login,
issued_at: &DateTime,
- ) -> Result<String, sqlx::Error> {
+ ) -> Result<IdentitySecret, sqlx::Error> {
let secret = Uuid::new_v4().to_string();
let secret = sqlx::query_scalar!(
@@ -31,7 +31,7 @@ impl<'c> Tokens<'c> {
insert
into token (secret, login, issued_at, last_used_at)
values ($1, $2, $3, $3)
- returning secret as "secret!"
+ returning secret as "secret!: IdentitySecret"
"#,
secret,
login.id,
@@ -44,7 +44,7 @@ impl<'c> Tokens<'c> {
}
// Revoke a token by its secret.
- pub async fn revoke(&mut self, secret: &str) -> Result<(), sqlx::Error> {
+ pub async fn revoke(&mut self, secret: &IdentitySecret) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
delete
@@ -82,7 +82,7 @@ impl<'c> Tokens<'c> {
// timestamp will be set to `used_at`.
pub async fn validate(
&mut self,
- secret: &str,
+ secret: &IdentitySecret,
used_at: &DateTime,
) -> Result<Login, sqlx::Error> {
// I would use `update … returning` to do this in one query, but