Add missing event thumbnail upload endpoint
- Fixed 502 error when uploading thumbnails via admin panel - Added /api/upload/events/:id/thumbnail route and handler - Thumbnails saved with thumb_ prefix for organization - Includes proper file cleanup when replacing existing thumbnails - Resolves CORS issues by ensuring endpoint exists for proper middleware handling
This commit is contained in:
parent
17aeb7d55e
commit
916a54caa2
|
@ -17,6 +17,7 @@ pub fn routes() -> Router<crate::AppState> {
|
|||
.route("/bulletins/:id/pdf", post(upload_bulletin_pdf))
|
||||
.route("/bulletins/:id/cover", post(upload_bulletin_cover))
|
||||
.route("/events/:id/image", post(upload_event_image))
|
||||
.route("/events/:id/thumbnail", post(upload_event_thumbnail))
|
||||
.route("/pending_events/:id/image", post(upload_pending_event_image))
|
||||
.nest_service("/files", tower_http::services::ServeDir::new("uploads"))
|
||||
.layer(DefaultBodyLimit::max(50 * 1024 * 1024))
|
||||
|
@ -245,3 +246,82 @@ async fn upload_pending_event_image(
|
|||
|
||||
Err(ApiError::ValidationError("No file found in request".to_string()))
|
||||
}
|
||||
|
||||
async fn upload_event_thumbnail(
|
||||
Path(id): Path<Uuid>,
|
||||
State(state): State<crate::AppState>,
|
||||
mut multipart: Multipart,
|
||||
) -> Result<Json<Value>> {
|
||||
while let Some(field) = multipart.next_field().await.map_err(|_| ApiError::ValidationError("Invalid multipart".to_string()))? {
|
||||
if field.name() == Some("file") {
|
||||
let filename = field.file_name()
|
||||
.ok_or_else(|| ApiError::ValidationError("No filename provided".to_string()))?
|
||||
.to_string();
|
||||
|
||||
let ext = filename.split('.').last().unwrap_or("jpg").to_lowercase();
|
||||
if !["jpg", "jpeg", "png", "webp", "gif"].contains(&ext.as_str()) {
|
||||
return Err(ApiError::ValidationError("Only image files allowed".to_string()));
|
||||
}
|
||||
|
||||
// Get current event to check for existing thumbnail
|
||||
let current_event = sqlx::query!(
|
||||
"SELECT thumbnail FROM events WHERE id = $1",
|
||||
id
|
||||
)
|
||||
.fetch_optional(&state.pool)
|
||||
.await
|
||||
.map_err(|_| ApiError::ValidationError("Failed to fetch event".to_string()))?
|
||||
.ok_or_else(|| ApiError::NotFound("Event not found".to_string()))?;
|
||||
|
||||
let file_id = Uuid::new_v4();
|
||||
let file_path = format!("uploads/events/thumb_{}.{}", file_id, ext);
|
||||
let full_path = PathBuf::from(&file_path);
|
||||
|
||||
if let Some(parent) = full_path.parent() {
|
||||
tokio::fs::create_dir_all(parent).await.map_err(|_| ApiError::ValidationError("Failed to create directory".to_string()))?;
|
||||
}
|
||||
|
||||
let data = field.bytes().await.map_err(|_| ApiError::ValidationError("Failed to read file".to_string()))?;
|
||||
let mut file = File::create(&full_path).await.map_err(|_| ApiError::ValidationError("Failed to create file".to_string()))?;
|
||||
file.write_all(&data).await.map_err(|_| ApiError::ValidationError("Failed to write file".to_string()))?;
|
||||
|
||||
// Update event record with full URL
|
||||
let url_builder = UrlBuilder::new();
|
||||
let thumbnail_url = url_builder.build_upload_url(&format!("events/thumb_{}.{}", file_id, ext));
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE events SET thumbnail = $1, updated_at = NOW() WHERE id = $2",
|
||||
thumbnail_url,
|
||||
id
|
||||
)
|
||||
.execute(&state.pool)
|
||||
.await
|
||||
.map_err(|_| ApiError::ValidationError("Failed to update event record".to_string()))?;
|
||||
|
||||
// Delete old thumbnail file if it exists
|
||||
if let Some(old_thumbnail_url) = current_event.thumbnail {
|
||||
// Extract file path from URL (remove base URL if present)
|
||||
if let Some(relative_path) = old_thumbnail_url.strip_prefix("https://").and_then(|s| s.split_once('/')).map(|(_, path)| path)
|
||||
.or_else(|| old_thumbnail_url.strip_prefix("http://").and_then(|s| s.split_once('/')).map(|(_, path)| path))
|
||||
.or_else(|| Some(old_thumbnail_url.as_str())) {
|
||||
|
||||
let old_file_path = PathBuf::from(relative_path);
|
||||
if old_file_path.exists() {
|
||||
if let Err(_) = tokio::fs::remove_file(&old_file_path).await {
|
||||
tracing::warn!("Failed to delete old event thumbnail: {:?}", old_file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(Json(json!({
|
||||
"success": true,
|
||||
"file_path": file_path,
|
||||
"thumbnail_url": thumbnail_url,
|
||||
"message": "Event thumbnail uploaded successfully"
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
Err(ApiError::ValidationError("No file found in request".to_string()))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue