user.rs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. use uuid::Uuid;
  2. use chrono::{DateTime, Utc};
  3. use sqlx::{PgPool, FromRow};
  4. use serde::Serialize;
  5. use crate::http_error::HttpError;
  6. #[derive(FromRow)]
  7. pub struct User {
  8. pub id: Uuid,
  9. pub session_token: Uuid,
  10. pub name: String,
  11. pub email: String,
  12. pub pass_hash: String,
  13. pub tokens: i64,
  14. pub created_at: DateTime<Utc>
  15. }
  16. #[derive(Serialize)]
  17. pub struct ResponseUser {
  18. id: String,
  19. name: String,
  20. email: String,
  21. tokens: i64
  22. }
  23. impl User {
  24. pub fn new(name: String, email: String, pass_hash: String) -> Self {
  25. Self {
  26. id: Uuid::now_v7(),
  27. session_token: Uuid::new_v4(),
  28. name: name,
  29. email: email.to_lowercase(),
  30. pass_hash: pass_hash,
  31. tokens: 0,
  32. created_at: Utc::now()
  33. }
  34. }
  35. pub async fn insert_one(&self, db: &PgPool) -> Result<(), HttpError> {
  36. let result = sqlx::query(
  37. "INSERT into users(id, session_token, name, email, pass_hash, tokens, created_at)
  38. VALUES($1, $2, $3, $4, $5, $6, $7)"
  39. )
  40. .bind(&self.id)
  41. .bind(&self.session_token)
  42. .bind(&self.name)
  43. .bind(&self.email)
  44. .bind(&self.pass_hash)
  45. .bind(&self.tokens)
  46. .bind(&self.created_at)
  47. .execute(db)
  48. .await;
  49. match result {
  50. Ok(_) => Ok(()),
  51. Err(sqlx::Error::Database(db_err)) if db_err.code().as_deref() == Some("23505") => {
  52. Err(HttpError::InvalidInput("User with this email already exists".into()))
  53. },
  54. Err(e) => Err(HttpError::Database(e.into()))
  55. }
  56. }
  57. pub async fn find_one_by_session(
  58. db: &PgPool,
  59. user_id: Uuid,
  60. session_token: Uuid
  61. ) -> Result<User, HttpError> {
  62. let user = sqlx::query_as::<_, User>(
  63. "SELECT *
  64. FROM users
  65. WHERE id = $1
  66. and session_token = $2"
  67. )
  68. .bind(user_id)
  69. .bind(session_token)
  70. .fetch_optional(db)
  71. .await?;
  72. match user {
  73. Some(u) => Ok(u),
  74. None => Err(HttpError::Auth)
  75. }
  76. }
  77. pub async fn find_one_by_email(db: &PgPool, user_email: String) -> Result<User, HttpError> {
  78. let email = user_email.to_lowercase();
  79. let user = sqlx::query_as::<_, User>(
  80. "SELECT *
  81. FROM users
  82. WHERE email = $1"
  83. )
  84. .bind(email)
  85. .fetch_optional(db)
  86. .await?;
  87. match user {
  88. Some(u) => Ok(u),
  89. _ => Err(HttpError::NotFound("Invalid email/password combination".into()))
  90. }
  91. }
  92. pub async fn add_tokens(db: &PgPool, user: Uuid, new_tokens: i32) -> Result<(), HttpError> {
  93. sqlx::query(
  94. "UPDATE users
  95. SET tokens = tokens + $1
  96. WHERE id = $2"
  97. )
  98. .bind(new_tokens)
  99. .bind(user)
  100. .execute(db)
  101. .await?;
  102. Ok(())
  103. }
  104. pub fn response(self) -> ResponseUser {
  105. ResponseUser {
  106. id: self.id.to_string(),
  107. name: self.name,
  108. email: self.email,
  109. tokens: self.tokens
  110. }
  111. }
  112. }