1use anyhow::{Context, Result, bail};
9use gize_core::Dialect;
10
11use crate::migrate::{connect, runtime};
12
13pub fn create(
17 database_url: &str,
18 dialect: Dialect,
19 email: &str,
20 name: &str,
21 password_hash: &str,
22) -> Result<()> {
23 runtime()?.block_on(async {
24 let pool = connect(database_url).await?;
25
26 let existing = sqlx::query(&format!(
28 "SELECT id FROM users WHERE email = {} LIMIT 1",
29 dialect.placeholder(1)
30 ))
31 .bind(email.to_string())
32 .fetch_optional(&pool)
33 .await
34 .context("querying the `users` table — does it exist? run `gize migrate` first")?;
35 if existing.is_some() {
36 bail!("an account with email `{email}` already exists");
37 }
38
39 let app_id = dialect.app_generates_id();
42 let columns = if app_id {
43 "id, name, email, password, is_admin"
44 } else {
45 "name, email, password, is_admin"
46 };
47 let count = if app_id { 5 } else { 4 };
48 let placeholders = (1..=count)
49 .map(|i| dialect.placeholder(i))
50 .collect::<Vec<_>>()
51 .join(", ");
52 let sql = format!("INSERT INTO users ({columns}) VALUES ({placeholders})");
53
54 let mut query = sqlx::query(&sql);
55 if app_id {
56 query = query.bind(uuid::Uuid::new_v4().as_bytes().to_vec());
57 }
58 query
59 .bind(name.to_string())
60 .bind(email.to_string())
61 .bind(password_hash.to_string())
62 .bind(true)
63 .execute(&pool)
64 .await
65 .context("inserting the admin user")?;
66
67 Ok(())
68 })
69}