1use countable::{self, Counter};
2use leptos::*;
3
4use super::*;
5
6#[cfg(feature = "ssr")]
7use actix_web::web::Data;
8#[cfg(feature = "ssr")]
9use leptos_actix::extract;
10
11#[cfg(feature = "ssr")]
12pub async fn extract_pool() -> Result<Data<backend::PgPool>, AppError> {
13 extract::<Data<backend::PgPool>>()
14 .await
15 .map_err(|err| AppError::Extraction(err.to_string()))
16}
17
18#[server(CheckUser, "/api")]
19pub async fn check_user(session: UserSession) -> Result<(), ServerFnError> {
20 use backend::auth::SessionState;
21 let pool = extract_pool().await?;
22 match backend::auth::check_user(&pool, &session.username, session.token).await {
23 Ok(SessionState::Valid) => Ok(()),
24 Ok(SessionState::Expired) => Err(AppError::ExpiredToken)?,
25 Err(err) => {
26 leptos_actix::redirect("/login");
27 Err(err.into())
28 }
29 }
30}
31
32#[server(LoginUser, "/api", "Url", "login_user")]
33pub async fn login_user(
34 username: String,
35 password: String,
36 remember: Option<String>,
37) -> Result<UserSession, ServerFnError> {
38 let pool = extract_pool().await?;
39
40 let dur = if remember.is_some() {
41 chrono::Duration::days(30)
42 } else {
43 chrono::Duration::days(1)
44 };
45
46 let user = backend::auth::login_user(&pool, username, password, dur).await?;
47
48 let session = UserSession {
49 user_uuid: user.uuid,
50 username: user.username,
51 token: user.token.unwrap(),
52 };
53
54 set_session_cookie(session.clone()).await?;
55 leptos_actix::redirect("/");
56
57 Ok(session)
58}
59
60#[server(CreateAccount, "/api", "Url", "create_account")]
61pub async fn create_account(
62 username: String,
63 password: String,
64 password_repeat: String,
65) -> Result<UserSession, ServerFnError> {
66 if password != password_repeat {
67 Err(backend::LoginError::InvalidPassword)?;
68 }
69
70 let pool = extract_pool().await?;
71 let user = backend::auth::insert_user(&pool, &username, &password).await?;
72
73 let session_user = UserSession {
74 user_uuid: user.uuid,
75 username: user.username,
76 token: user.token.unwrap(),
77 };
78
79 login_user(username, password, None).await?;
80
81 Ok(session_user)
82}
83
84#[server(ChangePassword, "/api")]
85pub async fn change_password(
86 username: String,
87 old_pass: String,
88 new_pass: String,
89 new_pass_repeat: String,
90) -> Result<(), ServerFnError> {
91 if new_pass != new_pass_repeat {
92 Err(backend::LoginError::InvalidPassword)?;
93 };
94
95 let pool = extract_pool().await?;
96 let _ = backend::auth::change_password(&pool, username, old_pass, new_pass).await?;
97
98 Ok(())
99}
100
101#[allow(clippy::too_many_arguments)]
102#[server(EditCountableForm)]
103pub async fn edit_countable_form(
104 session_user_uuid: uuid::Uuid,
105 session_username: String,
106 session_token: uuid::Uuid,
107
108 countable_key: uuid::Uuid,
109 countable_kind: CountableKind,
110 countable_name: String,
111 countable_count: i32,
112 countable_hours: i64,
113 countable_mins: i64,
114 countable_secs: i64,
115 countable_millis: i64,
116 countable_hunttype: String,
117 countable_charm: Option<String>,
118) -> Result<(), ServerFnError> {
119 let session = UserSession {
120 user_uuid: session_user_uuid,
121 username: session_username,
122 token: session_token,
123 };
124 check_user(session).await?;
125
126 let countable_time =
127 ((countable_hours * 60 + countable_mins) * 60 + countable_secs) * 1000 + countable_millis;
128
129 let mut conn = extract_pool().await?.begin().await?;
130 match countable_kind {
131 CountableKind::Counter => {
132 backend::counter::set_name(&mut conn, countable_key, &countable_name).await?;
133 backend::counter::set_count(&mut conn, countable_key, countable_count).await?;
134 backend::counter::set_time(&mut conn, countable_key, countable_time).await?;
135 backend::counter::set_hunttype(&mut conn, countable_key, countable_hunttype.into())
136 .await?;
137 backend::counter::set_charm(&mut conn, countable_key, countable_charm.is_some())
138 .await?;
139 }
140 CountableKind::Phase => {
141 backend::phase::set_name(&mut conn, countable_key, &countable_name).await?;
142 backend::phase::set_count(&mut conn, countable_key, countable_count).await?;
143 backend::phase::set_time(&mut conn, countable_key, countable_time).await?;
144 backend::phase::set_hunttype(&mut conn, countable_key, countable_hunttype.into())
145 .await?;
146 backend::phase::set_charm(&mut conn, countable_key, countable_charm.is_some()).await?;
147 }
148 _ => (),
149 }
150
151 conn.commit().await?;
152
153 return Ok(());
154}
155
156#[server(UpdateCountable, "/api/session")]
157pub async fn update_countable_many(list: Vec<countable::Countable>) -> Result<(), ServerFnError> {
158 let pool = extract_pool().await?;
159 let session = session::actix_extract_user().await?;
160
161 let mut tx = pool.begin().await?;
162
163 for countable in list {
164 match countable {
165 countable::Countable::Counter(c) => {
166 if c.lock()?.owner_uuid != session.user_uuid {
167 Err(AppError::Unauthorized)?
168 }
169 backend::counter::update(&mut tx, c.lock()?.clone().into()).await?
170 }
171 countable::Countable::Phase(p) => {
172 if p.lock()?.owner_uuid != session.user_uuid {
173 Err(AppError::Unauthorized)?
174 }
175 backend::phase::update(&mut tx, p.lock()?.clone().into()).await?
176 }
177 countable::Countable::Chain(_) => todo!(),
178 }
179 }
180
181 tx.commit().await?;
182
183 return Ok(());
184}
185
186#[server(UpdateCounter, "/api")]
187pub async fn update_counter(session: UserSession, counter: Counter) -> Result<(), ServerFnError> {
188 let pool = extract_pool().await?;
189
190 let _ =
191 backend::update_counter(&pool, &session.username, session.token, counter.into()).await?;
192
193 Ok(())
194}
195
196#[server(ArchiveCountable, "/api/session")]
197pub async fn archive_countable(countable: Countable) -> Result<(), ServerFnError> {
198 let pool = extract_pool().await?;
199 let mut tx = pool.begin().await?;
200
201 let uuid = countable.uuid();
202
203 if let Err(err) = match countable {
204 Countable::Counter(_) => backend::counter::archive(&mut tx, uuid).await,
205 Countable::Phase(_) => backend::phase::archive(&mut tx, uuid).await,
206 Countable::Chain(_) => todo!(),
207 } {
208 tx.rollback().await?;
209 return Err(err.into());
210 }
211
212 tx.commit().await?;
213
214 Ok(())
215}
216
217#[server(RemoveCountable, "/api/session")]
218pub async fn remove_countable(
219 session: UserSession,
220 countable: Countable,
221) -> Result<(), ServerFnError> {
222 match countable {
223 Countable::Counter(_) => remove_counter(session, countable.uuid()).await?,
224 Countable::Phase(_) => remove_phase(session, countable.uuid()).await?,
225 Countable::Chain(_) => todo!(),
226 }
227
228 return Ok(());
229}
230
231#[server(RemoveCounter, "/api")]
232pub async fn remove_counter(
233 session: UserSession,
234 counter_id: uuid::Uuid,
235) -> Result<(), ServerFnError> {
236 let pool = extract_pool().await?;
237
238 backend::remove_counter(&pool, &session.username, session.token, counter_id).await?;
239
240 Ok(())
241}
242
243#[server(RemovePhase, "/api")]
244pub async fn remove_phase(session: UserSession, phase_id: uuid::Uuid) -> Result<(), ServerFnError> {
245 let pool = extract_pool().await?;
246
247 let _ = backend::auth::get_user(&pool, &session.username, session.token).await?;
248 backend::remove_phase(&pool, phase_id).await?;
249
250 Ok(())
251}
252
253#[server(GetUserPreferences, "/api")]
254pub async fn get_user_preferences(session: UserSession) -> Result<Preferences, ServerFnError> {
255 let pool = extract_pool().await?;
256
257 let user = match backend::auth::get_user(&pool, &session.username, session.token).await {
258 Ok(user) => user,
259 Err(_) => {
260 return Ok(Preferences::default());
261 }
262 };
263 let session_user = UserSession {
264 user_uuid: user.uuid,
265 username: user.username,
266 token: user.token.unwrap_or_default(),
267 };
268 let prefs = match backend::DbPreferences::db_get(&pool, user.uuid).await {
269 Ok(data) => Preferences::from_db(&session_user, data),
270 Err(backend::BackendError::DataNotFound(_)) => {
271 let new_prefs = Preferences::new(&session_user);
272 save_preferences(
273 session.user_uuid,
274 session.username,
275 session.token,
276 new_prefs.clone(),
277 )
278 .await?;
279 new_prefs
280 }
281 Err(err) => return Err(err)?,
282 };
283
284 Ok(Preferences::from(prefs))
285}
286
287#[server(SavePreferences, "/api/session")]
288pub async fn save_preferences(
289 session_user_uuid: uuid::Uuid,
290 session_username: String,
291 session_token: uuid::Uuid,
292 preferences: Preferences,
293) -> Result<(), ServerFnError> {
294 let session = UserSession {
295 user_uuid: session_user_uuid,
296 username: session_username,
297 token: session_token,
298 };
299 let pool = extract_pool().await?;
300
301 let user = backend::auth::get_user(&pool, &session.username, session.token).await?;
302
303 let accent_color = if preferences.use_default_accent_color {
304 None
305 } else {
306 Some(preferences.accent_color.0)
307 };
308
309 let db_prefs = backend::DbPreferences {
310 user_uuid: user.uuid,
311 use_default_accent_color: preferences.use_default_accent_color,
312 accent_color,
313 show_separator: preferences.show_separator,
314 multi_select: preferences.multi_select,
315 save_on_pause: preferences.save_on_pause,
316 };
317 db_prefs
318 .db_set(&pool, &session.username, session.token)
319 .await?;
320
321 Ok(())
322}
323
324#[server]
325pub async fn set_session_cookie(session: UserSession) -> Result<(), ServerFnError> {
326 use actix_web::cookie::{self, time::Duration};
327 use actix_web::http::header;
328
329 let resp = expect_context::<leptos_actix::ResponseOptions>();
330
331 let mut cookie = cookie::Cookie::new("session", serde_json::to_string(&session)?);
332 cookie.set_max_age(Duration::days(30));
333 cookie.set_path("/");
334
335 resp.append_header(
336 header::SET_COOKIE,
337 header::HeaderValue::from_str(&cookie.to_string())?,
338 );
339
340 return Ok(());
341}
342
343#[server(ServerChangeAccountInfo, "/api")]
344async fn change_username(
345 old_username: String,
346 password: String,
347 new_username: String,
348) -> Result<UserSession, ServerFnError> {
349 let pool = api::extract_pool().await?;
350 let user =
351 backend::auth::change_username(&pool, &old_username, &new_username, &password).await?;
352
353 let session_user = UserSession {
354 user_uuid: user.uuid,
355 username: user.username.clone(),
356 token: user.token.unwrap(),
357 };
358
359 api::login_user(user.username, password, Some(String::new())).await?;
360 leptos_actix::redirect("/preferences");
361
362 return Ok(session_user);
363}