420dedbc1e
- Add IM service modules: audit, channel roles, custom emojis, forum tags, integrations, invitations, repo links, slash commands, stages, voice, webhooks - Add PR service modules: review requests, templates - Add repo service modules: contributors, release assets, git extras (archive, branch rename, commit extras, diff/merge, tag, tree) - Add user service: social (follow/block) - Add internal auth service - Update existing service modules with expanded functionality - Remove deleted IM modules: articles, delivery trace, drafts, follows, messages, polls, presence, reactions, threads
99 lines
3.8 KiB
Rust
99 lines
3.8 KiB
Rust
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)?;
|
|
// Read from writer to avoid replication lag
|
|
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.writer())
|
|
.await
|
|
.map_err(AppError::Database)?
|
|
.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)
|
|
}
|
|
}
|