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:
2026-05-11 22:59:52 +02:00
parent 8a254346f4
commit 80f620c840
89 changed files with 2231 additions and 499 deletions

View File

@@ -2,7 +2,7 @@ use application::ports::{
ActivityFeedPageData, FollowersPageData, FollowingPageData, HtmlPageContext, HtmlRenderer,
ImportMappingPageData, ImportPreviewPageData, ImportPreviewRow, ImportProfileView,
ImportRowStatus, ImportUploadPageData, LoginPageData, MovieDetailPageData, NewReviewPageData,
ProfilePageData, RegisterPageData, UsersPageData,
ProfilePageData, ProfileSettingsPageData, RegisterPageData, UsersPageData,
};
use askama::Template;
use chrono::Datelike;
@@ -305,6 +305,15 @@ fn bar_height_px(avg_rating: f64) -> i64 {
(avg_rating / 5.0 * 60.0) as i64
}
#[derive(Template)]
#[template(path = "profile_settings.html")]
struct ProfileSettingsTemplate<'a> {
ctx: &'a HtmlPageContext,
bio: Option<&'a str>,
avatar_url: Option<&'a str>,
saved: bool,
}
#[derive(Template)]
#[template(path = "import_upload.html")]
struct ImportUploadTemplate<'a> {
@@ -649,4 +658,18 @@ impl HtmlRenderer for AskamaHtmlRenderer {
.render()
.map_err(|e| e.to_string())
}
fn render_profile_settings_page(
&self,
data: ProfileSettingsPageData,
) -> Result<String, String> {
ProfileSettingsTemplate {
ctx: &data.ctx,
bio: data.bio.as_deref(),
avatar_url: data.avatar_url.as_deref(),
saved: data.saved,
}
.render()
.map_err(|e| e.to_string())
}
}