feat: image storage generalization, user profile, and federation polish
- Replace PosterStorage with generic ImageStorage port (IMAGE_STORAGE_BACKEND/PATH env vars)
- Rename poster-storage crate to image-storage; serve at /images/{*key}
- Add bio and avatar_path to User model (migration 0009_user_profile)
- update_profile use case with avatar upload, mime validation, old avatar cleanup
- GET/PUT /api/v1/profile and GET/POST /settings/profile HTML page
- Enrich AP Person actor with summary, icon, url, discoverable fields
- Store remote actor avatar_url (migration 0010_ap_remote_actor_avatar)
- Shared inbox delivery via collect_inboxes deduplication
- Broadcast Update(Person) to followers on UserUpdated event
- Paginated outbox: OrderedCollection + OrderedCollectionPage with cursor
- Announce/boost tracking in ap_announces table (migration 0011_ap_announces)
This commit is contained in:
25
crates/presentation/src/handlers/images.rs
Normal file
25
crates/presentation/src/handlers/images.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
http::{StatusCode, header},
|
||||
response::IntoResponse,
|
||||
};
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
pub async fn get_image(
|
||||
State(state): State<AppState>,
|
||||
Path(key): Path<String>,
|
||||
) -> impl IntoResponse {
|
||||
if key.starts_with("http://") || key.starts_with("https://") {
|
||||
return axum::response::Redirect::temporary(&key).into_response();
|
||||
}
|
||||
match state.app_ctx.image_storage.get(&key).await {
|
||||
Ok(bytes) => {
|
||||
let mime = infer::get(&bytes)
|
||||
.map(|t| t.mime_type())
|
||||
.unwrap_or("application/octet-stream");
|
||||
([(header::CONTENT_TYPE, mime)], bytes).into_response()
|
||||
}
|
||||
Err(_) => StatusCode::NOT_FOUND.into_response(),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user