- Add /api/users/me/avatar and /api/projects/{name}/avatar multipart upload endpoints
- Fix avatar URL path: missing /avatar prefix (static.gitdata.ai/avatar/{file})
- Fix project avatar: Utc::now() → .timestamp(), missing extension, wrong return type
- Replace broken SkipNoisyPaths middleware with self-contained RequestLogger
(actix-web 4.13 body type incompatibility with newer actix-http)
- Exclude /assets/* requests from main app logger
- Exclude /avatar/*, /blob/*, /media/*, /static/* from static server logger
- Fix TypingEvent missing sender_type field in ws_universal.rs and connection.rs
- Wire real fetch-based upload in user profile settings
- Add project avatar upload UI to project settings page
64 lines
2.0 KiB
Rust
64 lines
2.0 KiB
Rust
use crate::AppService;
|
|
use crate::error::AppError;
|
|
use crate::project::audit::AuditLogParams;
|
|
use chrono::Utc;
|
|
use models::projects::{MemberRole, project};
|
|
use sea_orm::prelude::Expr;
|
|
use sea_orm::*;
|
|
use serde_json::json;
|
|
use session::Session;
|
|
|
|
impl AppService {
|
|
pub async fn project_avatar_upload(
|
|
&self,
|
|
ctx: &Session,
|
|
project_name: String,
|
|
file: Vec<u8>,
|
|
file_ext: String,
|
|
) -> Result<String, AppError> {
|
|
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
|
let project = self
|
|
.utils_find_project_by_name(project_name.clone())
|
|
.await?;
|
|
let role = self
|
|
.utils_project_context_role(&ctx, project_name.clone())
|
|
.await?;
|
|
if role == MemberRole::Member {
|
|
return Err(AppError::NoPower);
|
|
}
|
|
let time = Utc::now().timestamp();
|
|
let file_name = format!("{}-{}", project.id, time);
|
|
self.avatar
|
|
.upload(file, file_name.clone(), &file_ext)
|
|
.await
|
|
.map_err(|e| AppError::AvatarUploadError(e.to_string()))?;
|
|
let static_url = self.config.static_domain().unwrap_or("/static".to_string());
|
|
let file_url = format!(
|
|
"{}/avatar/{}",
|
|
static_url.trim_end_matches('/'),
|
|
format!("{}.{}", file_name, file_ext)
|
|
);
|
|
project::Entity::update_many()
|
|
.filter(project::Column::Id.eq(project.id))
|
|
.col_expr(project::Column::AvatarUrl, Expr::value(file_url.clone()))
|
|
.exec(&self.db)
|
|
.await?;
|
|
self.project_log_audit(
|
|
project_name,
|
|
AuditLogParams {
|
|
action: "update avatar".to_string(),
|
|
details: Some(json!({
|
|
"file_name": file_name,
|
|
"file_url": file_url,
|
|
"project_name": project.name,
|
|
"project_uid": project.id,
|
|
"user_uid": user_uid,
|
|
})),
|
|
},
|
|
ctx,
|
|
)
|
|
.await?;
|
|
Ok(file_url)
|
|
}
|
|
}
|