feat: add PATCH /federation/me/also-known-as endpoint
Adds alsoKnownAs column to users table (migration 013), reads it in the AP actor JSON, and exposes PATCH /federation/me/also-known-as to set or clear it. Required pre-condition for broadcast_move.
This commit is contained in:
@@ -546,6 +546,7 @@ impl PostgresApUserRepository {
|
|||||||
bio: Option<String>,
|
bio: Option<String>,
|
||||||
avatar_url: Option<String>,
|
avatar_url: Option<String>,
|
||||||
header_url: Option<String>,
|
header_url: Option<String>,
|
||||||
|
also_known_as: Option<String>,
|
||||||
) -> ApUser {
|
) -> ApUser {
|
||||||
let profile_url = url::Url::parse(&format!("{}/users/{}", self.base_url, username)).ok();
|
let profile_url = url::Url::parse(&format!("{}/users/{}", self.base_url, username)).ok();
|
||||||
let avatar_url = avatar_url.and_then(|u| url::Url::parse(&u).ok());
|
let avatar_url = avatar_url.and_then(|u| url::Url::parse(&u).ok());
|
||||||
@@ -556,7 +557,7 @@ impl PostgresApUserRepository {
|
|||||||
bio,
|
bio,
|
||||||
avatar_url,
|
avatar_url,
|
||||||
banner_url,
|
banner_url,
|
||||||
also_known_as: None,
|
also_known_as,
|
||||||
profile_url,
|
profile_url,
|
||||||
attachment: vec![],
|
attachment: vec![],
|
||||||
}
|
}
|
||||||
@@ -573,15 +574,25 @@ impl ApUserRepository for PostgresApUserRepository {
|
|||||||
bio: Option<String>,
|
bio: Option<String>,
|
||||||
avatar_url: Option<String>,
|
avatar_url: Option<String>,
|
||||||
header_url: Option<String>,
|
header_url: Option<String>,
|
||||||
|
also_known_as: Option<String>,
|
||||||
}
|
}
|
||||||
let row = sqlx::query_as::<_, Row>(
|
let row = sqlx::query_as::<_, Row>(
|
||||||
"SELECT id,username,bio,avatar_url,header_url FROM users WHERE id=$1 AND local=true",
|
"SELECT id,username,bio,avatar_url,header_url,also_known_as FROM users WHERE id=$1 AND local=true",
|
||||||
)
|
)
|
||||||
.bind(id)
|
.bind(id)
|
||||||
.fetch_optional(&self.pool)
|
.fetch_optional(&self.pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| anyhow!(e))?;
|
.map_err(|e| anyhow!(e))?;
|
||||||
Ok(row.map(|r| self.row_to_ap_user(r.id, r.username, r.bio, r.avatar_url, r.header_url)))
|
Ok(row.map(|r| {
|
||||||
|
self.row_to_ap_user(
|
||||||
|
r.id,
|
||||||
|
r.username,
|
||||||
|
r.bio,
|
||||||
|
r.avatar_url,
|
||||||
|
r.header_url,
|
||||||
|
r.also_known_as,
|
||||||
|
)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn find_by_username(&self, username: &str) -> Result<Option<ApUser>> {
|
async fn find_by_username(&self, username: &str) -> Result<Option<ApUser>> {
|
||||||
@@ -592,15 +603,25 @@ impl ApUserRepository for PostgresApUserRepository {
|
|||||||
bio: Option<String>,
|
bio: Option<String>,
|
||||||
avatar_url: Option<String>,
|
avatar_url: Option<String>,
|
||||||
header_url: Option<String>,
|
header_url: Option<String>,
|
||||||
|
also_known_as: Option<String>,
|
||||||
}
|
}
|
||||||
let row = sqlx::query_as::<_, Row>(
|
let row = sqlx::query_as::<_, Row>(
|
||||||
"SELECT id,username,bio,avatar_url,header_url FROM users WHERE username=$1 AND local=true",
|
"SELECT id,username,bio,avatar_url,header_url,also_known_as FROM users WHERE username=$1 AND local=true",
|
||||||
)
|
)
|
||||||
.bind(username)
|
.bind(username)
|
||||||
.fetch_optional(&self.pool)
|
.fetch_optional(&self.pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| anyhow!(e))?;
|
.map_err(|e| anyhow!(e))?;
|
||||||
Ok(row.map(|r| self.row_to_ap_user(r.id, r.username, r.bio, r.avatar_url, r.header_url)))
|
Ok(row.map(|r| {
|
||||||
|
self.row_to_ap_user(
|
||||||
|
r.id,
|
||||||
|
r.username,
|
||||||
|
r.bio,
|
||||||
|
r.avatar_url,
|
||||||
|
r.header_url,
|
||||||
|
r.also_known_as,
|
||||||
|
)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn count_users(&self) -> Result<usize> {
|
async fn count_users(&self) -> Result<usize> {
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE users ADD COLUMN IF NOT EXISTS also_known_as TEXT;
|
||||||
@@ -288,6 +288,20 @@ impl UserWriter for PgUserRepository {
|
|||||||
.into_domain()
|
.into_domain()
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn set_also_known_as(
|
||||||
|
&self,
|
||||||
|
user_id: &UserId,
|
||||||
|
value: Option<String>,
|
||||||
|
) -> Result<(), DomainError> {
|
||||||
|
sqlx::query("UPDATE users SET also_known_as = $2, updated_at = NOW() WHERE id = $1")
|
||||||
|
.bind(user_id.as_uuid())
|
||||||
|
.bind(value)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await
|
||||||
|
.into_domain()
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -60,6 +60,13 @@ impl UserWriter for ConflictOnSaveStore {
|
|||||||
) -> Result<(), DomainError> {
|
) -> Result<(), DomainError> {
|
||||||
self.0.update_profile(user_id, input).await
|
self.0.update_profile(user_id, input).await
|
||||||
}
|
}
|
||||||
|
async fn set_also_known_as(
|
||||||
|
&self,
|
||||||
|
user_id: &UserId,
|
||||||
|
value: Option<String>,
|
||||||
|
) -> Result<(), DomainError> {
|
||||||
|
self.0.set_also_known_as(user_id, value).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -105,6 +112,13 @@ impl UserWriter for EmailConflictOnSaveStore {
|
|||||||
) -> Result<(), DomainError> {
|
) -> Result<(), DomainError> {
|
||||||
self.0.update_profile(user_id, input).await
|
self.0.update_profile(user_id, input).await
|
||||||
}
|
}
|
||||||
|
async fn set_also_known_as(
|
||||||
|
&self,
|
||||||
|
user_id: &UserId,
|
||||||
|
value: Option<String>,
|
||||||
|
) -> Result<(), DomainError> {
|
||||||
|
self.0.set_also_known_as(user_id, value).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FakeHasher;
|
struct FakeHasher;
|
||||||
|
|||||||
@@ -83,6 +83,11 @@ pub trait UserWriter: Send + Sync {
|
|||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
input: UpdateProfileInput,
|
input: UpdateProfileInput,
|
||||||
) -> Result<(), DomainError>;
|
) -> Result<(), DomainError>;
|
||||||
|
async fn set_also_known_as(
|
||||||
|
&self,
|
||||||
|
user_id: &UserId,
|
||||||
|
value: Option<String>,
|
||||||
|
) -> Result<(), DomainError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Combined supertrait — `AppState.users` stays `Arc<dyn UserRepository>`.
|
/// Combined supertrait — `AppState.users` stays `Arc<dyn UserRepository>`.
|
||||||
|
|||||||
@@ -152,6 +152,14 @@ impl UserWriter for TestStore {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn set_also_known_as(
|
||||||
|
&self,
|
||||||
|
_user_id: &UserId,
|
||||||
|
_value: Option<String>,
|
||||||
|
) -> Result<(), DomainError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -126,3 +126,17 @@ pub async fn post_move_account(
|
|||||||
.map_err(ApiError::from)?;
|
.map_err(ApiError::from)?;
|
||||||
Ok(StatusCode::NO_CONTENT)
|
Ok(StatusCode::NO_CONTENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct AlsoKnownAsBody {
|
||||||
|
pub also_known_as: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn patch_also_known_as(
|
||||||
|
Deps(d): Deps<FederationManagementDeps>,
|
||||||
|
AuthUser(uid): AuthUser,
|
||||||
|
Json(body): Json<AlsoKnownAsBody>,
|
||||||
|
) -> Result<StatusCode, ApiError> {
|
||||||
|
d.users.set_also_known_as(&uid, body.also_known_as).await?;
|
||||||
|
Ok(StatusCode::NO_CONTENT)
|
||||||
|
}
|
||||||
|
|||||||
@@ -108,6 +108,10 @@ pub fn router() -> Router<AppState> {
|
|||||||
"/federation/me/move",
|
"/federation/me/move",
|
||||||
post(federation_management::post_move_account),
|
post(federation_management::post_move_account),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/federation/me/also-known-as",
|
||||||
|
patch(federation_management::patch_also_known_as),
|
||||||
|
)
|
||||||
.route("/tags/popular", get(feed::get_popular_tags))
|
.route("/tags/popular", get(feed::get_popular_tags))
|
||||||
.route("/tags/{name}", get(feed::tag_thoughts_handler))
|
.route("/tags/{name}", get(feed::tag_thoughts_handler))
|
||||||
// notifications
|
// notifications
|
||||||
|
|||||||
Reference in New Issue
Block a user