summaryrefslogtreecommitdiff
path: root/src/repo
diff options
context:
space:
mode:
Diffstat (limited to 'src/repo')
-rw-r--r--src/repo/channel.rs179
-rw-r--r--src/repo/error.rs23
-rw-r--r--src/repo/login/extract.rs15
-rw-r--r--src/repo/login/mod.rs4
-rw-r--r--src/repo/login/store.rs86
-rw-r--r--src/repo/message.rs33
-rw-r--r--src/repo/mod.rs6
-rw-r--r--src/repo/pool.rs18
-rw-r--r--src/repo/token.rs159
9 files changed, 0 insertions, 523 deletions
diff --git a/src/repo/channel.rs b/src/repo/channel.rs
deleted file mode 100644
index 3c7468f..0000000
--- a/src/repo/channel.rs
+++ /dev/null
@@ -1,179 +0,0 @@
-use std::fmt;
-
-use sqlx::{sqlite::Sqlite, SqliteConnection, Transaction};
-
-use crate::{
- clock::DateTime,
- events::types::{self, Sequence},
- id::Id as BaseId,
-};
-
-pub trait Provider {
- fn channels(&mut self) -> Channels;
-}
-
-impl<'c> Provider for Transaction<'c, Sqlite> {
- fn channels(&mut self) -> Channels {
- Channels(self)
- }
-}
-
-pub struct Channels<'t>(&'t mut SqliteConnection);
-
-#[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,
- 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, 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?;
-
- Ok(channel)
- }
-
- pub async fn by_id(&mut self, channel: &Id) -> Result<Channel, sqlx::Error> {
- let channel = sqlx::query_as!(
- Channel,
- r#"
- select
- id as "id: Id",
- name,
- created_at as "created_at: DateTime"
- from channel
- where id = $1
- "#,
- channel,
- )
- .fetch_one(&mut *self.0)
- .await?;
-
- Ok(channel)
- }
-
- pub async fn all(&mut self) -> Result<Vec<Channel>, sqlx::Error> {
- let channels = sqlx::query_as!(
- Channel,
- r#"
- select
- id as "id: Id",
- name,
- created_at as "created_at: DateTime"
- from channel
- order by channel.name
- "#,
- )
- .fetch_all(&mut *self.0)
- .await?;
-
- 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`.
-#[derive(
- Clone,
- Debug,
- Eq,
- Hash,
- Ord,
- PartialEq,
- PartialOrd,
- sqlx::Type,
- serde::Deserialize,
- serde::Serialize,
-)]
-#[sqlx(transparent)]
-#[serde(transparent)]
-pub struct Id(BaseId);
-
-impl From<BaseId> for Id {
- fn from(id: BaseId) -> Self {
- Self(id)
- }
-}
-
-impl Id {
- pub fn generate() -> Self {
- BaseId::generate("C")
- }
-}
-
-impl fmt::Display for Id {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
- }
-}
diff --git a/src/repo/error.rs b/src/repo/error.rs
deleted file mode 100644
index a5961e2..0000000
--- a/src/repo/error.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-pub trait NotFound {
- type Ok;
- fn not_found<E, F>(self, map: F) -> Result<Self::Ok, E>
- where
- E: From<sqlx::Error>,
- F: FnOnce() -> E;
-}
-
-impl<T> NotFound for Result<T, sqlx::Error> {
- type Ok = T;
-
- fn not_found<E, F>(self, map: F) -> Result<T, E>
- where
- E: From<sqlx::Error>,
- F: FnOnce() -> E,
- {
- match self {
- Err(sqlx::Error::RowNotFound) => Err(map()),
- Err(other) => Err(other.into()),
- Ok(value) => Ok(value),
- }
- }
-}
diff --git a/src/repo/login/extract.rs b/src/repo/login/extract.rs
deleted file mode 100644
index ab61106..0000000
--- a/src/repo/login/extract.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-use axum::{extract::FromRequestParts, http::request::Parts};
-
-use super::Login;
-use crate::{app::App, login::extract::Identity};
-
-#[async_trait::async_trait]
-impl FromRequestParts<App> for Login {
- type Rejection = <Identity as FromRequestParts<App>>::Rejection;
-
- async fn from_request_parts(parts: &mut Parts, state: &App) -> Result<Self, Self::Rejection> {
- let identity = Identity::from_request_parts(parts, state).await?;
-
- Ok(identity.login)
- }
-}
diff --git a/src/repo/login/mod.rs b/src/repo/login/mod.rs
deleted file mode 100644
index a1b4c6f..0000000
--- a/src/repo/login/mod.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-mod extract;
-mod store;
-
-pub use self::store::{Id, Login, Provider};
diff --git a/src/repo/login/store.rs b/src/repo/login/store.rs
deleted file mode 100644
index b485941..0000000
--- a/src/repo/login/store.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-use sqlx::{sqlite::Sqlite, SqliteConnection, Transaction};
-
-use crate::{id::Id as BaseId, password::StoredHash};
-
-pub trait Provider {
- fn logins(&mut self) -> Logins;
-}
-
-impl<'c> Provider for Transaction<'c, Sqlite> {
- fn logins(&mut self) -> Logins {
- Logins(self)
- }
-}
-
-pub struct Logins<'t>(&'t mut SqliteConnection);
-
-// This also implements FromRequestParts (see `./extract.rs`). As a result, it
-// can be used as an extractor for endpoints that want to require login, or for
-// endpoints that need to behave differently depending on whether the client is
-// or is not logged in.
-#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
-pub struct Login {
- pub id: Id,
- pub name: String,
- // The omission of the hashed password is deliberate, to minimize the
- // chance that it ends up tangled up in debug output or in some other chunk
- // of logic elsewhere.
-}
-
-impl<'c> Logins<'c> {
- pub async fn create(
- &mut self,
- name: &str,
- password_hash: &StoredHash,
- ) -> Result<Login, sqlx::Error> {
- let id = Id::generate();
-
- let login = sqlx::query_as!(
- Login,
- r#"
- insert or fail
- into login (id, name, password_hash)
- values ($1, $2, $3)
- returning
- id as "id: Id",
- name
- "#,
- id,
- name,
- password_hash,
- )
- .fetch_one(&mut *self.0)
- .await?;
-
- Ok(login)
- }
-}
-
-impl<'t> From<&'t mut SqliteConnection> for Logins<'t> {
- fn from(tx: &'t mut SqliteConnection) -> Self {
- Self(tx)
- }
-}
-
-// Stable identifier for a [Login]. Prefixed with `L`.
-#[derive(Clone, Debug, Eq, PartialEq, sqlx::Type, serde::Serialize)]
-#[sqlx(transparent)]
-pub struct Id(BaseId);
-
-impl From<BaseId> for Id {
- fn from(id: BaseId) -> Self {
- Self(id)
- }
-}
-
-impl Id {
- pub fn generate() -> Self {
- BaseId::generate("L")
- }
-}
-
-impl std::fmt::Display for Id {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- self.0.fmt(f)
- }
-}
diff --git a/src/repo/message.rs b/src/repo/message.rs
deleted file mode 100644
index a1f73d5..0000000
--- a/src/repo/message.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-use std::fmt;
-
-use crate::id::Id as BaseId;
-
-// Stable identifier for a [Message]. Prefixed with `M`.
-#[derive(Clone, Debug, Eq, Hash, PartialEq, sqlx::Type, serde::Deserialize, serde::Serialize)]
-#[sqlx(transparent)]
-#[serde(transparent)]
-pub struct Id(BaseId);
-
-impl From<BaseId> for Id {
- fn from(id: BaseId) -> Self {
- Self(id)
- }
-}
-
-impl Id {
- pub fn generate() -> Self {
- BaseId::generate("M")
- }
-}
-
-impl fmt::Display for Id {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- 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/mod.rs b/src/repo/mod.rs
deleted file mode 100644
index cb9d7c8..0000000
--- a/src/repo/mod.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-pub mod channel;
-pub mod error;
-pub mod login;
-pub mod message;
-pub mod pool;
-pub mod token;
diff --git a/src/repo/pool.rs b/src/repo/pool.rs
deleted file mode 100644
index b4aa6fc..0000000
--- a/src/repo/pool.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use std::str::FromStr;
-
-use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions};
-
-pub async fn prepare(url: &str) -> sqlx::Result<SqlitePool> {
- let pool = create(url).await?;
- sqlx::migrate!().run(&pool).await?;
- Ok(pool)
-}
-
-async fn create(database_url: &str) -> sqlx::Result<SqlitePool> {
- let options = SqliteConnectOptions::from_str(database_url)?
- .create_if_missing(true)
- .optimize_on_close(true, /* analysis_limit */ None);
-
- let pool = SqlitePoolOptions::new().connect_with(options).await?;
- Ok(pool)
-}
diff --git a/src/repo/token.rs b/src/repo/token.rs
deleted file mode 100644
index d96c094..0000000
--- a/src/repo/token.rs
+++ /dev/null
@@ -1,159 +0,0 @@
-use std::fmt;
-
-use sqlx::{sqlite::Sqlite, SqliteConnection, Transaction};
-use uuid::Uuid;
-
-use super::login::{self, Login};
-use crate::{clock::DateTime, id::Id as BaseId, login::extract::IdentitySecret};
-
-pub trait Provider {
- fn tokens(&mut self) -> Tokens;
-}
-
-impl<'c> Provider for Transaction<'c, Sqlite> {
- fn tokens(&mut self) -> Tokens {
- Tokens(self)
- }
-}
-
-pub struct Tokens<'t>(&'t mut SqliteConnection);
-
-impl<'c> Tokens<'c> {
- // Issue a new token for an existing login. The issued_at timestamp will
- // be used to control expiry, until the token is actually used.
- pub async fn issue(
- &mut self,
- login: &Login,
- issued_at: &DateTime,
- ) -> Result<IdentitySecret, sqlx::Error> {
- let id = Id::generate();
- let secret = Uuid::new_v4().to_string();
-
- let secret = sqlx::query_scalar!(
- r#"
- insert
- into token (id, secret, login, issued_at, last_used_at)
- values ($1, $2, $3, $4, $4)
- returning secret as "secret!: IdentitySecret"
- "#,
- id,
- secret,
- login.id,
- issued_at,
- )
- .fetch_one(&mut *self.0)
- .await?;
-
- Ok(secret)
- }
-
- // Revoke a token by its secret.
- pub async fn revoke(&mut self, token: &Id) -> Result<(), sqlx::Error> {
- sqlx::query_scalar!(
- r#"
- delete
- from token
- where id = $1
- returning id as "id: Id"
- "#,
- token,
- )
- .fetch_one(&mut *self.0)
- .await?;
-
- Ok(())
- }
-
- // Expire and delete all tokens that haven't been used more recently than
- // `expire_at`.
- pub async fn expire(&mut self, expire_at: &DateTime) -> Result<Vec<Id>, sqlx::Error> {
- let tokens = sqlx::query_scalar!(
- r#"
- delete
- from token
- where last_used_at < $1
- returning id as "id: Id"
- "#,
- expire_at,
- )
- .fetch_all(&mut *self.0)
- .await?;
-
- Ok(tokens)
- }
-
- // Validate a token by its secret, retrieving the associated Login record.
- // Will return [None] if the token is not valid. The token's last-used
- // timestamp will be set to `used_at`.
- pub async fn validate(
- &mut self,
- secret: &IdentitySecret,
- used_at: &DateTime,
- ) -> Result<(Id, Login), sqlx::Error> {
- // I would use `update … returning` to do this in one query, but
- // sqlite3, as of this writing, does not allow an update's `returning`
- // clause to reference columns from tables joined into the update. Two
- // queries is fine, but it feels untidy.
- sqlx::query!(
- r#"
- update token
- set last_used_at = $1
- where secret = $2
- "#,
- used_at,
- secret,
- )
- .execute(&mut *self.0)
- .await?;
-
- let login = sqlx::query!(
- r#"
- select
- token.id as "token_id: Id",
- login.id as "login_id: login::Id",
- name as "login_name"
- from login
- join token on login.id = token.login
- where token.secret = $1
- "#,
- secret,
- )
- .map(|row| {
- (
- row.token_id,
- Login {
- id: row.login_id,
- name: row.login_name,
- },
- )
- })
- .fetch_one(&mut *self.0)
- .await?;
-
- Ok(login)
- }
-}
-
-// Stable identifier for a token. Prefixed with `T`.
-#[derive(Clone, Debug, Eq, Hash, PartialEq, sqlx::Type, serde::Deserialize, serde::Serialize)]
-#[sqlx(transparent)]
-#[serde(transparent)]
-pub struct Id(BaseId);
-
-impl From<BaseId> for Id {
- fn from(id: BaseId) -> Self {
- Self(id)
- }
-}
-
-impl Id {
- pub fn generate() -> Self {
- BaseId::generate("T")
- }
-}
-
-impl fmt::Display for Id {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
- }
-}