1use crate::application::ports::UserRepository;
2use crate::domain::entities::{User, UserRole};
3use crate::infrastructure::pool::DbPool;
4use async_trait::async_trait;
5use uuid::Uuid;
6
7pub struct PostgresUserRepository {
8 pool: DbPool,
9}
10
11impl PostgresUserRepository {
12 pub fn new(pool: DbPool) -> Self {
13 Self { pool }
14 }
15}
16
17#[async_trait]
18impl UserRepository for PostgresUserRepository {
19 async fn create(&self, user: &User) -> Result<User, String> {
20 sqlx::query!(
21 r#"
22 INSERT INTO users (id, email, password_hash, first_name, last_name, role, organization_id, is_active, created_at, updated_at)
23 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
24 "#,
25 user.id,
26 user.email,
27 user.password_hash,
28 user.first_name,
29 user.last_name,
30 user.role.to_string(),
31 user.organization_id,
32 user.is_active,
33 user.created_at,
34 user.updated_at,
35 )
36 .execute(&self.pool)
37 .await
38 .map_err(|e| format!("Failed to create user: {}", e))?;
39
40 Ok(user.clone())
41 }
42
43 async fn find_by_id(&self, id: Uuid) -> Result<Option<User>, String> {
44 let result = sqlx::query!(
45 r#"
46 SELECT id, email, password_hash, first_name, last_name, role, organization_id, is_active, created_at, updated_at
47 FROM users
48 WHERE id = $1
49 "#,
50 id
51 )
52 .fetch_optional(&self.pool)
53 .await
54 .map_err(|e| format!("Failed to find user: {}", e))?;
55
56 match result {
57 Some(row) => {
58 let role = row
59 .role
60 .parse::<UserRole>()
61 .map_err(|e| format!("Invalid role: {}", e))?;
62
63 Ok(Some(User {
64 id: row.id,
65 email: row.email,
66 password_hash: row.password_hash,
67 first_name: row.first_name,
68 last_name: row.last_name,
69 role,
70 organization_id: row.organization_id,
71 is_active: row.is_active,
72 processing_restricted: false,
73 processing_restricted_at: None,
74 marketing_opt_out: false,
75 marketing_opt_out_at: None,
76 created_at: row.created_at,
77 updated_at: row.updated_at,
78 }))
79 }
80 None => Ok(None),
81 }
82 }
83
84 async fn find_by_email(&self, email: &str) -> Result<Option<User>, String> {
85 let result = sqlx::query!(
86 r#"
87 SELECT id, email, password_hash, first_name, last_name, role, organization_id, is_active, created_at, updated_at
88 FROM users
89 WHERE email = $1
90 "#,
91 email
92 )
93 .fetch_optional(&self.pool)
94 .await
95 .map_err(|e| format!("Failed to find user by email: {}", e))?;
96
97 match result {
98 Some(row) => {
99 let role = row
100 .role
101 .parse::<UserRole>()
102 .map_err(|e| format!("Invalid role: {}", e))?;
103
104 Ok(Some(User {
105 id: row.id,
106 email: row.email,
107 password_hash: row.password_hash,
108 first_name: row.first_name,
109 last_name: row.last_name,
110 role,
111 organization_id: row.organization_id,
112 is_active: row.is_active,
113 processing_restricted: false,
114 processing_restricted_at: None,
115 marketing_opt_out: false,
116 marketing_opt_out_at: None,
117 created_at: row.created_at,
118 updated_at: row.updated_at,
119 }))
120 }
121 None => Ok(None),
122 }
123 }
124
125 async fn find_all(&self) -> Result<Vec<User>, String> {
126 let rows = sqlx::query!(
127 r#"
128 SELECT id, email, password_hash, first_name, last_name, role, organization_id, is_active, created_at, updated_at
129 FROM users
130 ORDER BY created_at DESC
131 "#
132 )
133 .fetch_all(&self.pool)
134 .await
135 .map_err(|e| format!("Failed to fetch users: {}", e))?;
136
137 let users = rows
138 .into_iter()
139 .filter_map(|row| {
140 let role = row.role.parse::<UserRole>().ok()?;
141 Some(User {
142 id: row.id,
143 email: row.email,
144 password_hash: row.password_hash,
145 first_name: row.first_name,
146 last_name: row.last_name,
147 role,
148 organization_id: row.organization_id,
149 is_active: row.is_active,
150 processing_restricted: false,
151 processing_restricted_at: None,
152 marketing_opt_out: false,
153 marketing_opt_out_at: None,
154 created_at: row.created_at,
155 updated_at: row.updated_at,
156 })
157 })
158 .collect();
159
160 Ok(users)
161 }
162
163 async fn find_by_organization(&self, org_id: Uuid) -> Result<Vec<User>, String> {
164 let rows = sqlx::query!(
165 r#"
166 SELECT id, email, password_hash, first_name, last_name, role, organization_id, is_active, created_at, updated_at
167 FROM users
168 WHERE organization_id = $1
169 ORDER BY created_at DESC
170 "#,
171 org_id
172 )
173 .fetch_all(&self.pool)
174 .await
175 .map_err(|e| format!("Failed to fetch users by organization: {}", e))?;
176
177 let users = rows
178 .into_iter()
179 .filter_map(|row| {
180 let role = row.role.parse::<UserRole>().ok()?;
181 Some(User {
182 id: row.id,
183 email: row.email,
184 password_hash: row.password_hash,
185 first_name: row.first_name,
186 last_name: row.last_name,
187 role,
188 organization_id: row.organization_id,
189 is_active: row.is_active,
190 processing_restricted: false,
191 processing_restricted_at: None,
192 marketing_opt_out: false,
193 marketing_opt_out_at: None,
194 created_at: row.created_at,
195 updated_at: row.updated_at,
196 })
197 })
198 .collect();
199
200 Ok(users)
201 }
202
203 async fn update(&self, user: &User) -> Result<User, String> {
204 sqlx::query!(
205 r#"
206 UPDATE users
207 SET email = $2, first_name = $3, last_name = $4, role = $5,
208 organization_id = $6, is_active = $7, updated_at = $8
209 WHERE id = $1
210 "#,
211 user.id,
212 user.email,
213 user.first_name,
214 user.last_name,
215 user.role.to_string(),
216 user.organization_id,
217 user.is_active,
218 user.updated_at,
219 )
220 .execute(&self.pool)
221 .await
222 .map_err(|e| format!("Failed to update user: {}", e))?;
223
224 Ok(user.clone())
225 }
226
227 async fn delete(&self, id: Uuid) -> Result<bool, String> {
228 let result = sqlx::query!(
229 r#"
230 DELETE FROM users
231 WHERE id = $1
232 "#,
233 id
234 )
235 .execute(&self.pool)
236 .await
237 .map_err(|e| format!("Failed to delete user: {}", e))?;
238
239 Ok(result.rows_affected() > 0)
240 }
241
242 async fn count_by_organization(&self, org_id: Uuid) -> Result<i64, String> {
243 let result = sqlx::query!(
244 r#"
245 SELECT COUNT(*) as count
246 FROM users
247 WHERE organization_id = $1
248 "#,
249 org_id
250 )
251 .fetch_one(&self.pool)
252 .await
253 .map_err(|e| format!("Failed to count users: {}", e))?;
254
255 Ok(result.count.unwrap_or(0))
256 }
257}