use sqlx::sqlite::SqlitePool; use super::repo::Provider as _; use crate::{ clock::DateTime, db, error::failed::{ErrorExt, Failed, ResultExt as _}, event::Broadcaster, name::Name, password::Password, token::{Secret, Token, repo::Provider as _}, user::{create, create::Create}, }; #[derive(Clone)] pub struct Setup { db: SqlitePool, events: Broadcaster, } impl Setup { pub const fn new(db: SqlitePool, events: Broadcaster) -> Self { Self { db, events } } pub async fn initial( &self, name: &Name, password: &Password, created_at: &DateTime, ) -> Result { let create = Create::begin(name, password, created_at); let validated = create.validate().map_err(|err| match err { create::Error::InvalidName(name) => Error::InvalidName(name), create::Error::Failed(_) => err.fail("Failed to validate new user"), })?; let mut tx = self.db.begin().await.fail(db::failed::BEGIN)?; if tx .setup() .completed() .await .fail("Failed to check for prior setup completion")? { Err(Error::SetupCompleted) } else { let stored = validated .store(&mut tx) .await .fail("Failed to create initial user")?; let login = stored.login(); let (token, secret) = Token::generate(login, created_at); tx.tokens() .create(&token, &secret) .await .fail("Failed to create token")?; tx.commit().await.fail(db::failed::COMMIT)?; stored.publish(&self.events); Ok(secret) } } pub async fn completed(&self) -> Result { 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("invalid user name: {0}")] InvalidName(Name), #[error(transparent)] Failed(#[from] Failed), }