use std::fmt::Display; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use sqlx::types::{chrono, JsonValue}; use sqlx::{Error, PgPool, Postgres, Transaction}; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub enum Role { Admin, User, } impl Role { pub fn is_admin(&self) -> bool { match self { Role::Admin => true, Role::User => false, } } pub fn is_user(&self) -> bool { match self { Role::Admin => false, Role::User => true, } } } impl Default for Role { fn default() -> Self { Role::User } } impl Display for Role { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Role::Admin => write!(f, "admin"), Role::User => write!(f, "user"), } } } impl Into for String { fn into(self) -> Role { match self.as_str() { "admin" => Role::Admin, "user" => Role::User, _ => Role::User, } } } #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct Account { pub id: String, pub username: String, pub display_name: Option, pub avatar_url: Option, pub lang_tag: String, pub location: Option, pub timezone: Option, pub metadata: JsonValue, pub wallet: JsonValue, pub email: Option, pub password: Option>, pub role: Role, pub facebook_id: Option, pub google_id: Option, pub gamecenter_id: Option, pub steam_id: Option, pub custom_id: Option, pub apple_id: Option, pub facebook_instant_game_id: Option, pub weixin_id: Option, pub douyin_id: Option, pub create_time: DateTime, pub update_time: Option>, pub verify_time: Option>, pub disable_time: Option>, } impl Account { pub async fn find_by_apple_id( apple_id: &str, db_pool: &PgPool, ) -> Result, Error> { match sqlx::query_as!( Account, r#"select * from account where apple_id = $1"#, apple_id ) .fetch_one(db_pool) .await { Ok(account) => Ok(Some(account)), Err(Error::RowNotFound) => Ok(None), Err(e) => { tracing::error!("find_by_apple_id error: {:?}", e); Err(e) } } } pub async fn find_by_custom_id( custom_id: &str, db_pool: &PgPool, ) -> Result, Error> { match sqlx::query_as!( Account, r#"select * from account where custom_id = $1"#, custom_id ) .fetch_one(db_pool) .await { Ok(account) => Ok(Some(account)), Err(Error::RowNotFound) => Ok(None), Err(e) => { tracing::error!("find_by_custom_id error: {:?}", e); Err(e) } } } pub async fn find_by_facebook_id( facebook_id: &str, db_pool: &PgPool, ) -> Result, Error> { match sqlx::query_as!( Account, r#"select * from account where facebook_id = $1"#, facebook_id ) .fetch_one(db_pool) .await { Ok(account) => Ok(Some(account)), Err(Error::RowNotFound) => Ok(None), Err(e) => { tracing::error!("find_by_facebook_id error: {:?}", e); Err(e) } } } pub async fn find_by_google_id( google_id: &str, db_pool: &PgPool, ) -> Result, Error> { match sqlx::query_as!( Account, r#"select * from account where google_id = $1"#, google_id ) .fetch_one(db_pool) .await { Ok(account) => Ok(Some(account)), Err(Error::RowNotFound) => Ok(None), Err(e) => { tracing::error!("find_by_google_id error: {:?}", e); Err(e) } } } pub async fn save_google_account( &self, transaction: &mut Transaction<'_, Postgres>, ) -> Result { sqlx::query_as!( Account, r#" insert into account (username, google_id, email, display_name, avatar_url) values ($1, $2, $3, $4, $5) returning * "#, self.username, self.google_id.as_ref().unwrap(), self.email.as_ref().unwrap(), self.display_name.as_ref().unwrap(), self.avatar_url.as_ref().unwrap() ) .fetch_one(&mut **transaction) .await } pub async fn find_with_password( username: String, password: String, db_pool: &PgPool, ) -> Result, Error> { match sqlx::query_as!( Account, r#" select * from account where username = $1 and password = $2 "#, username, password.as_bytes().to_vec(), ) .fetch_one(db_pool) .await { Ok(account) => Ok(Some(account)), Err(Error::RowNotFound) => Ok(None), Err(e) => { tracing::error!("find_by_google_id error: {:?}", e); Err(e) } } } pub async fn add_account( &self, transaction: &mut Transaction<'_, Postgres>, ) -> Result, Error> { match sqlx::query_as!( Account, r#" insert into account (username, password, lang_tag, role, wallet, metadata) values ($1, $2, $3, $4, $5, $6) returning * "#, self.username, self.password, self.lang_tag, self.role.to_string(), self.wallet, self.metadata ) .fetch_one(&mut **transaction) .await { Ok(account) => Ok(Some(account)), Err(Error::RowNotFound) => Ok(None), Err(e) => { tracing::error!("add_account error: {:?}", e); Err(e) } } } }