diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2024-10-11 20:55:36 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2024-10-11 20:55:36 -0400 |
| commit | 5ff106e910544788bc916626ae7665cb26e5af30 (patch) | |
| tree | f03f98677293a9d892e2d21d1a9a80aeedab60a3 /src/setup/app.rs | |
| parent | d33c8af14c4adc1c15ab048299e06f9f35ae4de6 (diff) | |
Provide a separate "initial setup" endpoint that creates a user.
Diffstat (limited to 'src/setup/app.rs')
| -rw-r--r-- | src/setup/app.rs | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/src/setup/app.rs b/src/setup/app.rs new file mode 100644 index 0000000..24e0010 --- /dev/null +++ b/src/setup/app.rs @@ -0,0 +1,62 @@ +use sqlx::sqlite::SqlitePool; + +use super::repo::Provider as _; +use crate::{ + clock::DateTime, + event::{repo::Provider as _, Broadcaster, Event}, + login::{repo::Provider as _, Password}, + token::{repo::Provider as _, Secret}, +}; + +pub struct Setup<'a> { + db: &'a SqlitePool, + events: &'a Broadcaster, +} + +impl<'a> Setup<'a> { + pub const fn new(db: &'a SqlitePool, events: &'a Broadcaster) -> Self { + Self { db, events } + } + + pub async fn initial( + &self, + name: &str, + password: &Password, + created_at: &DateTime, + ) -> Result<Secret, Error> { + let password_hash = password.hash()?; + + let mut tx = self.db.begin().await?; + let login = if tx.setup().completed().await? { + Err(Error::SetupCompleted)? + } else { + let created = tx.sequence().next(created_at).await?; + tx.logins().create(name, &password_hash, &created).await? + }; + let secret = tx.tokens().issue(&login, created_at).await?; + tx.commit().await?; + + self.events + .broadcast(login.events().map(Event::from).collect::<Vec<_>>()); + + Ok(secret) + } + + pub async fn completed(&self) -> Result<bool, sqlx::Error> { + let mut tx = self.db.begin().await?; + let completed = tx.setup().completed().await?; + tx.commit().await?; + + Ok(completed) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("initial setup previously completed")] + SetupCompleted, + #[error(transparent)] + Database(#[from] sqlx::Error), + #[error(transparent)] + PasswordHash(#[from] password_hash::Error), +} |
