systemprompt_users/repository/user/
service_account.rs1use chrono::Utc;
11use systemprompt_identifiers::UserId;
12
13use crate::error::{Result, UserError};
14use crate::models::{AccountKind, User, UserRole, UserRow, UserStatus, normalise_email};
15use crate::repository::UserRepository;
16
17#[derive(Debug, Clone)]
18pub struct NewServiceAccount {
19 pub name: String,
20 pub email: String,
21 pub created_by: UserId,
22}
23
24impl UserRepository {
25 pub async fn insert_service_account(&self, account: NewServiceAccount) -> Result<User> {
26 let id = UserId::generate();
27 let email = normalise_email(&account.email);
28 let user = sqlx::query_as!(
29 UserRow,
30 r#"
31 INSERT INTO users (
32 id, name, email, display_name, status, email_verified, roles, is_bot,
33 account_kind, created_at, updated_at
34 )
35 VALUES ($1, $2, $3, $2, $4, true, ARRAY[$5]::TEXT[], false, $6, $7, $7)
36 RETURNING id, name, email, full_name, display_name, status, email_verified,
37 roles, avatar_url, is_bot, is_scanner, created_at, updated_at, account_kind
38 "#,
39 id.as_str(),
40 account.name,
41 email,
42 UserStatus::Active.as_str(),
43 UserRole::User.as_str(),
44 AccountKind::Service.as_str(),
45 Utc::now()
46 )
47 .fetch_one(&*self.write_pool)
48 .await
49 .map_err(UserError::from)
50 .and_then(User::try_from)?;
51 tracing::info!(
52 user_id = %user.id,
53 created_by = %account.created_by,
54 "service account created"
55 );
56 Ok(user)
57 }
58
59 pub async fn list_service_accounts(&self) -> Result<Vec<User>> {
60 let rows = sqlx::query_as!(
61 UserRow,
62 r#"
63 SELECT id, name, email, full_name, display_name, status, email_verified,
64 roles, avatar_url, is_bot, is_scanner, created_at, updated_at, account_kind
65 FROM users
66 WHERE account_kind = $1 AND status != $2
67 ORDER BY created_at
68 "#,
69 AccountKind::Service.as_str(),
70 UserStatus::Deleted.as_str()
71 )
72 .fetch_all(&*self.pool)
73 .await?;
74 rows.into_iter().map(User::try_from).collect()
75 }
76}