feat: init
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::AppError;
|
||||
use crate::models::users::UserProfile;
|
||||
use crate::service::UserService;
|
||||
use crate::session::Session;
|
||||
|
||||
use super::util::merge_optional_text;
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)]
|
||||
pub struct UpdateUserProfileParams {
|
||||
pub full_name: Option<String>,
|
||||
pub company: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub website_url: Option<String>,
|
||||
pub twitter_username: Option<String>,
|
||||
pub timezone: Option<String>,
|
||||
pub language: Option<String>,
|
||||
pub profile_readme: Option<String>,
|
||||
}
|
||||
|
||||
impl UserService {
|
||||
pub async fn user_profile(&self, ctx: &Session) -> Result<UserProfile, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
self.ensure_user_profile(user_uid).await
|
||||
}
|
||||
|
||||
pub async fn user_update_profile(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
params: UpdateUserProfileParams,
|
||||
) -> Result<UserProfile, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let current = self.ensure_user_profile(user_uid).await?;
|
||||
let now = chrono::Utc::now();
|
||||
|
||||
sqlx::query_as::<_, UserProfile>(
|
||||
"UPDATE user_profile SET full_name = $1, company = $2, location = $3, website_url = $4, \
|
||||
twitter_username = $5, timezone = $6, language = $7, profile_readme = $8, updated_at = $9 \
|
||||
WHERE user_id = $10 RETURNING user_id, full_name, company, location, website_url, \
|
||||
twitter_username, timezone, language, profile_readme, created_at, updated_at",
|
||||
)
|
||||
.bind(merge_optional_text(params.full_name, current.full_name))
|
||||
.bind(merge_optional_text(params.company, current.company))
|
||||
.bind(merge_optional_text(params.location, current.location))
|
||||
.bind(merge_optional_text(params.website_url, current.website_url))
|
||||
.bind(merge_optional_text(params.twitter_username, current.twitter_username))
|
||||
.bind(merge_optional_text(params.timezone, current.timezone))
|
||||
.bind(merge_optional_text(params.language, current.language))
|
||||
.bind(merge_optional_text(params.profile_readme, current.profile_readme))
|
||||
.bind(now)
|
||||
.bind(user_uid)
|
||||
.fetch_one(self.ctx.db.writer())
|
||||
.await
|
||||
.map_err(AppError::Database)
|
||||
}
|
||||
|
||||
async fn ensure_user_profile(&self, user_uid: uuid::Uuid) -> Result<UserProfile, AppError> {
|
||||
if let Some(profile) = self.find_user_profile(user_uid).await? {
|
||||
return Ok(profile);
|
||||
}
|
||||
let now = chrono::Utc::now();
|
||||
sqlx::query(
|
||||
"INSERT INTO user_profile (user_id, created_at, updated_at) VALUES ($1, $2, $2) ON CONFLICT (user_id) DO NOTHING",
|
||||
)
|
||||
.bind(user_uid)
|
||||
.bind(now)
|
||||
.execute(self.ctx.db.writer())
|
||||
.await
|
||||
.map_err(AppError::Database)?;
|
||||
self.find_user_profile(user_uid)
|
||||
.await?
|
||||
.ok_or(AppError::UserNotFound)
|
||||
}
|
||||
|
||||
async fn find_user_profile(
|
||||
&self,
|
||||
user_uid: uuid::Uuid,
|
||||
) -> Result<Option<UserProfile>, AppError> {
|
||||
sqlx::query_as::<_, UserProfile>(
|
||||
"SELECT user_id, full_name, company, location, website_url, twitter_username, \
|
||||
timezone, language, profile_readme, created_at, updated_at \
|
||||
FROM user_profile WHERE user_id = $1",
|
||||
)
|
||||
.bind(user_uid)
|
||||
.fetch_optional(self.ctx.db.reader())
|
||||
.await
|
||||
.map_err(AppError::Database)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user