Make media URLs in Mastodon API responses relative to current origin
This commit is contained in:
parent
c796cddff8
commit
e1e9851d5c
18 changed files with 204 additions and 33 deletions
|
@ -26,6 +26,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
|
||||
- Fixed actor object JSON-LD validation errors.
|
||||
- Fixed activity JSON-LD validation errors.
|
||||
- Make media URLs in Mastodon API responses relative to current origin.
|
||||
|
||||
## [1.13.1] - 2023-02-09
|
||||
|
||||
|
|
|
@ -14,6 +14,9 @@ pub fn get_hostname(url: &str) -> Result<String, ParseError> {
|
|||
}
|
||||
|
||||
pub fn guess_protocol(hostname: &str) -> &'static str {
|
||||
if hostname == "localhost" {
|
||||
return "http";
|
||||
};
|
||||
let maybe_ipv4_address = hostname.parse::<Ipv4Addr>();
|
||||
if let Ok(_ipv4_address) = maybe_ipv4_address {
|
||||
return "http";
|
||||
|
|
16
src/http.rs
16
src/http.rs
|
@ -1,6 +1,6 @@
|
|||
use actix_web::{
|
||||
body::{BodySize, BoxBody, MessageBody},
|
||||
dev::ServiceResponse,
|
||||
dev::{ConnectionInfo, ServiceResponse},
|
||||
error::{Error, JsonPayloadError},
|
||||
http::StatusCode,
|
||||
middleware::{ErrorHandlerResponse, ErrorHandlers},
|
||||
|
@ -10,6 +10,8 @@ use actix_web::{
|
|||
};
|
||||
use serde_json::json;
|
||||
|
||||
use mitra_utils::urls::guess_protocol;
|
||||
|
||||
use crate::errors::HttpError;
|
||||
|
||||
pub type FormOrJson<T> = Either<Form<T>, Json<T>>;
|
||||
|
@ -46,3 +48,15 @@ pub fn json_error_handler(
|
|||
other_error => other_error.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_request_base_url(connection_info: ConnectionInfo) -> String {
|
||||
// TODO: HTTP server should set X-Forwarded-Proto header
|
||||
// let scheme = connection_info.scheme();
|
||||
let host = connection_info.host();
|
||||
let scheme = if let Some((hostname, _port)) = host.split_once(':') {
|
||||
guess_protocol(hostname)
|
||||
} else {
|
||||
guess_protocol(host)
|
||||
};
|
||||
format!("{}://{}", scheme, host)
|
||||
}
|
||||
|
|
|
@ -117,14 +117,15 @@ pub struct Account {
|
|||
|
||||
impl Account {
|
||||
pub fn from_profile(
|
||||
base_url: &str,
|
||||
instance_url: &str,
|
||||
profile: DbActorProfile,
|
||||
) -> Self {
|
||||
let profile_url = profile.actor_url(instance_url);
|
||||
let avatar_url = profile.avatar
|
||||
.map(|image| get_file_url(instance_url, &image.file_name));
|
||||
.map(|image| get_file_url(base_url, &image.file_name));
|
||||
let header_url = profile.banner
|
||||
.map(|image| get_file_url(instance_url, &image.file_name));
|
||||
.map(|image| get_file_url(base_url, &image.file_name));
|
||||
let is_locked = profile.actor_json
|
||||
.map(|actor| actor.manually_approves_followers)
|
||||
.unwrap_or(false);
|
||||
|
@ -207,6 +208,7 @@ impl Account {
|
|||
}
|
||||
|
||||
pub fn from_user(
|
||||
base_url: &str,
|
||||
instance_url: &str,
|
||||
user: User,
|
||||
) -> Self {
|
||||
|
@ -224,6 +226,7 @@ impl Account {
|
|||
};
|
||||
let role = ApiRole::from_db(user.role);
|
||||
let mut account = Self::from_profile(
|
||||
base_url,
|
||||
instance_url,
|
||||
user.profile,
|
||||
);
|
||||
|
@ -502,10 +505,12 @@ pub struct ApiSubscription {
|
|||
|
||||
impl ApiSubscription {
|
||||
pub fn from_subscription(
|
||||
base_url: &str,
|
||||
instance_url: &str,
|
||||
subscription: Subscription,
|
||||
) -> Self {
|
||||
let sender = Account::from_profile(
|
||||
base_url,
|
||||
instance_url,
|
||||
subscription.sender,
|
||||
);
|
||||
|
@ -545,6 +550,7 @@ mod tests {
|
|||
..Default::default()
|
||||
};
|
||||
let account = Account::from_profile(
|
||||
INSTANCE_URL,
|
||||
INSTANCE_URL,
|
||||
profile,
|
||||
);
|
||||
|
@ -570,6 +576,7 @@ mod tests {
|
|||
..Default::default()
|
||||
};
|
||||
let account = Account::from_user(
|
||||
INSTANCE_URL,
|
||||
INSTANCE_URL,
|
||||
user,
|
||||
);
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
use actix_web::{
|
||||
get, patch, post, web,
|
||||
HttpRequest, HttpResponse, Scope,
|
||||
dev::ConnectionInfo,
|
||||
get,
|
||||
patch,
|
||||
post,
|
||||
web,
|
||||
HttpRequest,
|
||||
HttpResponse,
|
||||
Scope,
|
||||
};
|
||||
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||
use uuid::Uuid;
|
||||
|
@ -33,6 +39,7 @@ use crate::ethereum::{
|
|||
gate::is_allowed_user,
|
||||
identity::verify_eip191_identity_proof,
|
||||
};
|
||||
use crate::http::get_request_base_url;
|
||||
use crate::identity::{
|
||||
claims::create_identity_claim,
|
||||
did::Did,
|
||||
|
@ -110,6 +117,7 @@ use super::types::{
|
|||
|
||||
#[post("")]
|
||||
pub async fn create_account(
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
maybe_blockchain: web::Data<Option<ContractSet>>,
|
||||
|
@ -195,6 +203,7 @@ pub async fn create_account(
|
|||
};
|
||||
log::warn!("created user {}", user.id);
|
||||
let account = Account::from_user(
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
user,
|
||||
);
|
||||
|
@ -204,12 +213,14 @@ pub async fn create_account(
|
|||
#[get("/verify_credentials")]
|
||||
async fn verify_credentials(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
) -> Result<HttpResponse, HttpError> {
|
||||
let db_client = &**get_database_client(&db_pool).await?;
|
||||
let user = get_current_user(db_client, auth.token()).await?;
|
||||
let account = Account::from_user(
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
user,
|
||||
);
|
||||
|
@ -219,6 +230,7 @@ async fn verify_credentials(
|
|||
#[patch("/update_credentials")]
|
||||
async fn update_credentials(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
account_data: web::Json<AccountUpdateData>,
|
||||
|
@ -246,6 +258,7 @@ async fn update_credentials(
|
|||
).await?.enqueue(db_client).await?;
|
||||
|
||||
let account = Account::from_user(
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
current_user,
|
||||
);
|
||||
|
@ -278,6 +291,7 @@ async fn get_unsigned_update(
|
|||
#[post("/send_activity")]
|
||||
async fn send_signed_activity(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
data: web::Json<SignedActivity>,
|
||||
|
@ -323,6 +337,7 @@ async fn send_signed_activity(
|
|||
outgoing_activity.enqueue(db_client).await?;
|
||||
|
||||
let account = Account::from_user(
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
current_user,
|
||||
);
|
||||
|
@ -363,6 +378,7 @@ async fn get_identity_claim(
|
|||
#[post("/identity_proof")]
|
||||
async fn create_identity_proof(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
proof_data: web::Json<IdentityProofData>,
|
||||
|
@ -441,6 +457,7 @@ async fn create_identity_proof(
|
|||
).await?.enqueue(db_client).await?;
|
||||
|
||||
let account = Account::from_user(
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
current_user,
|
||||
);
|
||||
|
@ -465,6 +482,7 @@ async fn get_relationships_view(
|
|||
|
||||
#[get("/lookup")]
|
||||
async fn lookup_acct(
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
query_params: web::Query<LookupAcctQueryParams>,
|
||||
|
@ -472,6 +490,7 @@ async fn lookup_acct(
|
|||
let db_client = &**get_database_client(&db_pool).await?;
|
||||
let profile = get_profile_by_acct(db_client, &query_params.acct).await?;
|
||||
let account = Account::from_profile(
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
profile,
|
||||
);
|
||||
|
@ -480,6 +499,7 @@ async fn lookup_acct(
|
|||
|
||||
#[get("/search")]
|
||||
async fn search_by_acct(
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
query_params: web::Query<SearchAcctQueryParams>,
|
||||
|
@ -490,9 +510,11 @@ async fn search_by_acct(
|
|||
&query_params.q,
|
||||
query_params.limit.inner(),
|
||||
).await?;
|
||||
let base_url = get_request_base_url(connection_info);
|
||||
let instance_url = config.instance().url();
|
||||
let accounts: Vec<Account> = profiles.into_iter()
|
||||
.map(|profile| Account::from_profile(
|
||||
&base_url,
|
||||
&instance_url,
|
||||
profile,
|
||||
))
|
||||
|
@ -502,6 +524,7 @@ async fn search_by_acct(
|
|||
|
||||
#[get("/search_did")]
|
||||
async fn search_by_did(
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
query_params: web::Query<SearchDidQueryParams>,
|
||||
|
@ -510,9 +533,11 @@ async fn search_by_did(
|
|||
let did: Did = query_params.did.parse()
|
||||
.map_err(|_| ValidationError("invalid DID"))?;
|
||||
let profiles = search_profiles_by_did(db_client, &did, false).await?;
|
||||
let base_url = get_request_base_url(connection_info);
|
||||
let instance_url = config.instance().url();
|
||||
let accounts: Vec<Account> = profiles.into_iter()
|
||||
.map(|profile| Account::from_profile(
|
||||
&base_url,
|
||||
&instance_url,
|
||||
profile,
|
||||
))
|
||||
|
@ -522,6 +547,7 @@ async fn search_by_did(
|
|||
|
||||
#[get("/{account_id}")]
|
||||
async fn get_account(
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
account_id: web::Path<Uuid>,
|
||||
|
@ -529,6 +555,7 @@ async fn get_account(
|
|||
let db_client = &**get_database_client(&db_pool).await?;
|
||||
let profile = get_profile_by_id(db_client, &account_id).await?;
|
||||
let account = Account::from_profile(
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
profile,
|
||||
);
|
||||
|
@ -608,6 +635,7 @@ async fn unfollow_account(
|
|||
#[get("/{account_id}/statuses")]
|
||||
async fn get_account_statuses(
|
||||
auth: Option<BearerAuth>,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
account_id: web::Path<Uuid>,
|
||||
|
@ -636,6 +664,7 @@ async fn get_account_statuses(
|
|||
).await?;
|
||||
let statuses = build_status_list(
|
||||
db_client,
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
maybe_current_user.as_ref(),
|
||||
posts,
|
||||
|
@ -646,6 +675,7 @@ async fn get_account_statuses(
|
|||
#[get("/{account_id}/followers")]
|
||||
async fn get_account_followers(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
account_id: web::Path<Uuid>,
|
||||
|
@ -668,9 +698,11 @@ async fn get_account_followers(
|
|||
).await?;
|
||||
let max_index = usize::from(query_params.limit.inner().saturating_sub(1));
|
||||
let maybe_last_id = followers.get(max_index).map(|item| item.relationship_id);
|
||||
let base_url = get_request_base_url(connection_info);
|
||||
let instance_url = config.instance().url();
|
||||
let accounts: Vec<Account> = followers.into_iter()
|
||||
.map(|item| Account::from_profile(
|
||||
&base_url,
|
||||
&instance_url,
|
||||
item.profile,
|
||||
))
|
||||
|
@ -687,6 +719,7 @@ async fn get_account_followers(
|
|||
#[get("/{account_id}/following")]
|
||||
async fn get_account_following(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
account_id: web::Path<Uuid>,
|
||||
|
@ -709,9 +742,11 @@ async fn get_account_following(
|
|||
).await?;
|
||||
let max_index = usize::from(query_params.limit.inner().saturating_sub(1));
|
||||
let maybe_last_id = following.get(max_index).map(|item| item.relationship_id);
|
||||
let base_url = get_request_base_url(connection_info);
|
||||
let instance_url = config.instance().url();
|
||||
let accounts: Vec<Account> = following.into_iter()
|
||||
.map(|item| Account::from_profile(
|
||||
&base_url,
|
||||
&instance_url,
|
||||
item.profile,
|
||||
))
|
||||
|
@ -728,6 +763,7 @@ async fn get_account_following(
|
|||
#[get("/{account_id}/subscribers")]
|
||||
async fn get_account_subscribers(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
account_id: web::Path<Uuid>,
|
||||
|
@ -741,6 +777,7 @@ async fn get_account_subscribers(
|
|||
let subscriptions: Vec<ApiSubscription> = vec![];
|
||||
return Ok(HttpResponse::Ok().json(subscriptions));
|
||||
};
|
||||
let base_url = get_request_base_url(connection_info);
|
||||
let instance_url = config.instance_url();
|
||||
let subscriptions: Vec<ApiSubscription> = get_incoming_subscriptions(
|
||||
db_client,
|
||||
|
@ -751,6 +788,7 @@ async fn get_account_subscribers(
|
|||
.await?
|
||||
.into_iter()
|
||||
.map(|subscription| ApiSubscription::from_subscription(
|
||||
&base_url,
|
||||
&instance_url,
|
||||
subscription,
|
||||
))
|
||||
|
|
|
@ -13,8 +13,8 @@ pub struct CustomEmoji {
|
|||
}
|
||||
|
||||
impl CustomEmoji {
|
||||
pub fn from_db(instance_url: &str, emoji: DbEmoji) -> Self {
|
||||
let image_url = get_file_url(instance_url, &emoji.image.file_name);
|
||||
pub fn from_db(base_url: &str, emoji: DbEmoji) -> Self {
|
||||
let image_url = get_file_url(base_url, &emoji.image.file_name);
|
||||
Self {
|
||||
shortcode: emoji.emoji_name,
|
||||
url: image_url.clone(),
|
||||
|
|
|
@ -1,23 +1,28 @@
|
|||
use actix_web::{get, web, HttpResponse, Scope};
|
||||
|
||||
use mitra_config::Config;
|
||||
use actix_web::{
|
||||
dev::ConnectionInfo,
|
||||
get,
|
||||
web,
|
||||
HttpResponse,
|
||||
Scope,
|
||||
};
|
||||
|
||||
use crate::database::{get_database_client, DbPool};
|
||||
use crate::errors::HttpError;
|
||||
use crate::http::get_request_base_url;
|
||||
use crate::models::emojis::queries::get_local_emojis;
|
||||
use super::types::CustomEmoji;
|
||||
|
||||
/// https://docs.joinmastodon.org/methods/custom_emojis/
|
||||
#[get("")]
|
||||
async fn custom_emoji_list(
|
||||
config: web::Data<Config>,
|
||||
connection_info: ConnectionInfo,
|
||||
db_pool: web::Data<DbPool>,
|
||||
) -> Result<HttpResponse, HttpError> {
|
||||
let db_client = &**get_database_client(&db_pool).await?;
|
||||
let instance = config.instance();
|
||||
let base_url = get_request_base_url(connection_info);
|
||||
let emojis: Vec<CustomEmoji> = get_local_emojis(db_client).await?
|
||||
.into_iter()
|
||||
.map(|db_emoji| CustomEmoji::from_db(&instance.url(), db_emoji))
|
||||
.map(|db_emoji| CustomEmoji::from_db(&base_url, db_emoji))
|
||||
.collect();
|
||||
Ok(HttpResponse::Ok().json(emojis))
|
||||
}
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
/// https://docs.joinmastodon.org/methods/instance/directory/
|
||||
use actix_web::{get, web, HttpResponse, Scope};
|
||||
use actix_web::{
|
||||
dev::ConnectionInfo,
|
||||
get,
|
||||
web,
|
||||
HttpResponse,
|
||||
Scope,
|
||||
};
|
||||
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||
|
||||
use mitra_config::Config;
|
||||
|
||||
use crate::database::{get_database_client, DbPool};
|
||||
use crate::errors::HttpError;
|
||||
use crate::http::get_request_base_url;
|
||||
use crate::mastodon_api::{
|
||||
accounts::types::Account,
|
||||
oauth::auth::get_current_user,
|
||||
|
@ -16,6 +23,7 @@ use super::types::DirectoryQueryParams;
|
|||
#[get("")]
|
||||
async fn profile_directory(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
query_params: web::Query<DirectoryQueryParams>,
|
||||
|
@ -28,10 +36,12 @@ async fn profile_directory(
|
|||
query_params.offset,
|
||||
query_params.limit.inner(),
|
||||
).await?;
|
||||
let base_url = get_request_base_url(connection_info);
|
||||
let instance_url = config.instance().url();
|
||||
let accounts: Vec<Account> = profiles
|
||||
.into_iter()
|
||||
.map(|profile| Account::from_profile(
|
||||
&base_url,
|
||||
&instance_url,
|
||||
profile,
|
||||
))
|
||||
|
|
|
@ -26,7 +26,7 @@ pub struct Attachment {
|
|||
}
|
||||
|
||||
impl Attachment {
|
||||
pub fn from_db(instance_url: &str, db_attachment: DbMediaAttachment) -> Self {
|
||||
pub fn from_db(base_url: &str, db_attachment: DbMediaAttachment) -> Self {
|
||||
let attachment_type =
|
||||
AttachmentType::from_media_type(db_attachment.media_type);
|
||||
let attachment_type_mastodon = match attachment_type {
|
||||
|
@ -35,7 +35,7 @@ impl Attachment {
|
|||
AttachmentType::Video => "video",
|
||||
};
|
||||
let attachment_url = get_file_url(
|
||||
instance_url,
|
||||
base_url,
|
||||
&db_attachment.file_name,
|
||||
);
|
||||
Self {
|
||||
|
|
|
@ -35,15 +35,17 @@ pub struct ApiNotification {
|
|||
|
||||
impl ApiNotification {
|
||||
pub fn from_db(
|
||||
base_url: &str,
|
||||
instance_url: &str,
|
||||
notification: Notification,
|
||||
) -> Self {
|
||||
let account = Account::from_profile(
|
||||
base_url,
|
||||
instance_url,
|
||||
notification.sender,
|
||||
);
|
||||
let status = notification.post.map(|post| {
|
||||
Status::from_post(instance_url, post)
|
||||
Status::from_post(base_url, instance_url, post)
|
||||
});
|
||||
let event_type_mastodon = match notification.event_type {
|
||||
EventType::Follow => "follow",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
/// https://docs.joinmastodon.org/methods/notifications/
|
||||
use actix_web::{
|
||||
get, web,
|
||||
dev::ConnectionInfo,
|
||||
get,
|
||||
web,
|
||||
HttpRequest, HttpResponse,
|
||||
Scope as ActixScope,
|
||||
};
|
||||
|
@ -10,6 +12,7 @@ use mitra_config::Config;
|
|||
|
||||
use crate::database::{get_database_client, DbPool};
|
||||
use crate::errors::HttpError;
|
||||
use crate::http::get_request_base_url;
|
||||
use crate::mastodon_api::{
|
||||
oauth::auth::get_current_user,
|
||||
pagination::get_paginated_response,
|
||||
|
@ -20,6 +23,7 @@ use super::types::{ApiNotification, NotificationQueryParams};
|
|||
#[get("")]
|
||||
async fn get_notifications_view(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
query_params: web::Query<NotificationQueryParams>,
|
||||
|
@ -27,6 +31,8 @@ async fn get_notifications_view(
|
|||
) -> Result<HttpResponse, HttpError> {
|
||||
let db_client = &**get_database_client(&db_pool).await?;
|
||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||
let base_url = get_request_base_url(connection_info);
|
||||
let instance = config.instance();
|
||||
let notifications: Vec<ApiNotification> = get_notifications(
|
||||
db_client,
|
||||
¤t_user.id,
|
||||
|
@ -34,13 +40,17 @@ async fn get_notifications_view(
|
|||
query_params.limit.inner(),
|
||||
).await?
|
||||
.into_iter()
|
||||
.map(|item| ApiNotification::from_db(&config.instance_url(), item))
|
||||
.map(|item| ApiNotification::from_db(
|
||||
&base_url,
|
||||
&instance.url(),
|
||||
item,
|
||||
))
|
||||
.collect();
|
||||
let max_index = usize::from(query_params.limit.inner().saturating_sub(1));
|
||||
let maybe_last_id = notifications.get(max_index)
|
||||
.map(|item| item.id.clone());
|
||||
let response = get_paginated_response(
|
||||
&config.instance_url(),
|
||||
&instance.url(),
|
||||
request.uri().path(),
|
||||
notifications,
|
||||
maybe_last_id,
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
/// https://docs.joinmastodon.org/methods/search/
|
||||
use actix_web::{get, web, HttpResponse, Scope};
|
||||
use actix_web::{
|
||||
dev::ConnectionInfo,
|
||||
get,
|
||||
web,
|
||||
HttpResponse,
|
||||
Scope,
|
||||
};
|
||||
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||
|
||||
use mitra_config::Config;
|
||||
|
||||
use crate::database::{get_database_client, DbPool};
|
||||
use crate::errors::HttpError;
|
||||
use crate::http::get_request_base_url;
|
||||
use crate::mastodon_api::{
|
||||
accounts::types::Account,
|
||||
oauth::auth::get_current_user,
|
||||
|
@ -18,6 +25,7 @@ use super::types::{SearchQueryParams, SearchResults};
|
|||
#[get("")]
|
||||
async fn search_view(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
query_params: web::Query<SearchQueryParams>,
|
||||
|
@ -31,15 +39,18 @@ async fn search_view(
|
|||
query_params.q.trim(),
|
||||
query_params.limit.inner(),
|
||||
).await?;
|
||||
let base_url = get_request_base_url(connection_info);
|
||||
let instance_url = config.instance().url();
|
||||
let accounts: Vec<Account> = profiles.into_iter()
|
||||
.map(|profile| Account::from_profile(
|
||||
&base_url,
|
||||
&instance_url,
|
||||
profile,
|
||||
))
|
||||
.collect();
|
||||
let statuses = build_status_list(
|
||||
db_client,
|
||||
&base_url,
|
||||
&instance_url,
|
||||
Some(¤t_user),
|
||||
posts,
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
use actix_web::{get, post, web, HttpResponse, Scope};
|
||||
use actix_web::{
|
||||
dev::ConnectionInfo,
|
||||
get,
|
||||
post,
|
||||
web,
|
||||
HttpResponse,
|
||||
Scope,
|
||||
};
|
||||
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||
|
||||
use mitra_config::Config;
|
||||
|
@ -12,6 +19,7 @@ use crate::activitypub::{
|
|||
};
|
||||
use crate::database::{get_database_client, DatabaseError, DbPool};
|
||||
use crate::errors::{HttpError, ValidationError};
|
||||
use crate::http::get_request_base_url;
|
||||
use crate::mastodon_api::{
|
||||
accounts::types::Account,
|
||||
oauth::auth::get_current_user,
|
||||
|
@ -37,6 +45,7 @@ use super::types::{
|
|||
#[post("/change_password")]
|
||||
async fn change_password_view(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
request_data: web::Json<PasswordChangeRequest>,
|
||||
|
@ -47,6 +56,7 @@ async fn change_password_view(
|
|||
.map_err(|_| HttpError::InternalError)?;
|
||||
set_user_password(db_client, ¤t_user.id, password_hash).await?;
|
||||
let account = Account::from_user(
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
current_user,
|
||||
);
|
||||
|
@ -117,6 +127,7 @@ async fn import_follows_view(
|
|||
#[post("/move_followers")]
|
||||
async fn move_followers(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
request_data: web::Json<MoveFollowersRequest>,
|
||||
|
@ -194,6 +205,7 @@ async fn move_followers(
|
|||
).enqueue(db_client).await?;
|
||||
|
||||
let account = Account::from_user(
|
||||
&get_request_base_url(connection_info),
|
||||
&instance.url(),
|
||||
current_user,
|
||||
);
|
||||
|
|
|
@ -75,6 +75,7 @@ pub async fn parse_microsyntaxes(
|
|||
/// Load related objects and build status for API response
|
||||
pub async fn build_status(
|
||||
db_client: &impl DatabaseClient,
|
||||
base_url: &str,
|
||||
instance_url: &str,
|
||||
user: Option<&User>,
|
||||
mut post: Post,
|
||||
|
@ -83,12 +84,13 @@ pub async fn build_status(
|
|||
if let Some(user) = user {
|
||||
add_user_actions(db_client, &user.id, vec![&mut post]).await?;
|
||||
};
|
||||
let status = Status::from_post(instance_url, post);
|
||||
let status = Status::from_post(base_url, instance_url, post);
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
pub async fn build_status_list(
|
||||
db_client: &impl DatabaseClient,
|
||||
base_url: &str,
|
||||
instance_url: &str,
|
||||
user: Option<&User>,
|
||||
mut posts: Vec<Post>,
|
||||
|
@ -99,7 +101,7 @@ pub async fn build_status_list(
|
|||
};
|
||||
let statuses: Vec<Status> = posts
|
||||
.into_iter()
|
||||
.map(|post| Status::from_post(instance_url, post))
|
||||
.map(|post| Status::from_post(base_url, instance_url, post))
|
||||
.collect();
|
||||
Ok(statuses)
|
||||
}
|
||||
|
|
|
@ -85,12 +85,13 @@ pub struct Status {
|
|||
|
||||
impl Status {
|
||||
pub fn from_post(
|
||||
base_url: &str,
|
||||
instance_url: &str,
|
||||
post: Post,
|
||||
) -> Self {
|
||||
let object_id = post.object_id(instance_url);
|
||||
let attachments: Vec<Attachment> = post.attachments.into_iter()
|
||||
.map(|item| Attachment::from_db(instance_url, item))
|
||||
.map(|item| Attachment::from_db(base_url, item))
|
||||
.collect();
|
||||
let mentions: Vec<Mention> = post.mentions.into_iter()
|
||||
.map(|item| Mention::from_profile(instance_url, item))
|
||||
|
@ -99,20 +100,21 @@ impl Status {
|
|||
.map(|tag_name| Tag::from_tag_name(instance_url, tag_name))
|
||||
.collect();
|
||||
let emojis: Vec<CustomEmoji> = post.emojis.into_iter()
|
||||
.map(|emoji| CustomEmoji::from_db(instance_url, emoji))
|
||||
.map(|emoji| CustomEmoji::from_db(base_url, emoji))
|
||||
.collect();
|
||||
let account = Account::from_profile(
|
||||
base_url,
|
||||
instance_url,
|
||||
post.author,
|
||||
);
|
||||
let reblog = if let Some(repost_of) = post.repost_of {
|
||||
let status = Status::from_post(instance_url, *repost_of);
|
||||
let status = Status::from_post(base_url, instance_url, *repost_of);
|
||||
Some(Box::new(status))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let links = post.linked.into_iter().map(|post| {
|
||||
Status::from_post(instance_url, post)
|
||||
Status::from_post(base_url, instance_url, post)
|
||||
}).collect();
|
||||
let visibility = match post.visibility {
|
||||
Visibility::Public => "public",
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
/// https://docs.joinmastodon.org/methods/statuses/
|
||||
use actix_web::{delete, get, post, web, HttpResponse, Scope};
|
||||
use actix_web::{
|
||||
delete,
|
||||
dev::ConnectionInfo,
|
||||
get,
|
||||
post,
|
||||
web,
|
||||
HttpResponse,
|
||||
Scope,
|
||||
};
|
||||
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||
use chrono::Utc;
|
||||
use uuid::Uuid;
|
||||
|
@ -21,7 +29,7 @@ use crate::activitypub::builders::{
|
|||
use crate::database::{get_database_client, DatabaseError, DbPool};
|
||||
use crate::errors::{HttpError, ValidationError};
|
||||
use crate::ethereum::nft::create_mint_signature;
|
||||
use crate::http::FormOrJson;
|
||||
use crate::http::{get_request_base_url, FormOrJson};
|
||||
use crate::ipfs::{
|
||||
store as ipfs_store,
|
||||
posts::PostMetadata,
|
||||
|
@ -69,6 +77,7 @@ use super::types::{
|
|||
#[post("")]
|
||||
async fn create_status(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
status_data: FormOrJson<StatusData>,
|
||||
|
@ -190,7 +199,11 @@ async fn create_status(
|
|||
prepare_create_note(db_client, &instance, ¤t_user, &post)
|
||||
.await?.enqueue(db_client).await?;
|
||||
|
||||
let status = Status::from_post(&instance.url(), post);
|
||||
let status = Status::from_post(
|
||||
&get_request_base_url(connection_info),
|
||||
&instance.url(),
|
||||
post,
|
||||
);
|
||||
Ok(HttpResponse::Ok().json(status))
|
||||
}
|
||||
|
||||
|
@ -232,6 +245,7 @@ async fn preview_status(
|
|||
#[get("/{status_id}")]
|
||||
async fn get_status(
|
||||
auth: Option<BearerAuth>,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
status_id: web::Path<Uuid>,
|
||||
|
@ -247,6 +261,7 @@ async fn get_status(
|
|||
};
|
||||
let status = build_status(
|
||||
db_client,
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
maybe_current_user.as_ref(),
|
||||
post,
|
||||
|
@ -285,6 +300,7 @@ async fn delete_status(
|
|||
#[get("/{status_id}/context")]
|
||||
async fn get_context(
|
||||
auth: Option<BearerAuth>,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
status_id: web::Path<Uuid>,
|
||||
|
@ -301,6 +317,7 @@ async fn get_context(
|
|||
).await?;
|
||||
let statuses = build_status_list(
|
||||
db_client,
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
maybe_current_user.as_ref(),
|
||||
posts,
|
||||
|
@ -327,6 +344,7 @@ async fn get_context(
|
|||
async fn get_thread_view(
|
||||
auth: Option<BearerAuth>,
|
||||
config: web::Data<Config>,
|
||||
connection_info: ConnectionInfo,
|
||||
db_pool: web::Data<DbPool>,
|
||||
status_id: web::Path<Uuid>,
|
||||
) -> Result<HttpResponse, HttpError> {
|
||||
|
@ -342,6 +360,7 @@ async fn get_thread_view(
|
|||
).await?;
|
||||
let statuses = build_status_list(
|
||||
db_client,
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
maybe_current_user.as_ref(),
|
||||
posts,
|
||||
|
@ -352,6 +371,7 @@ async fn get_thread_view(
|
|||
#[post("/{status_id}/favourite")]
|
||||
async fn favourite(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
status_id: web::Path<Uuid>,
|
||||
|
@ -386,6 +406,7 @@ async fn favourite(
|
|||
|
||||
let status = build_status(
|
||||
db_client,
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
Some(¤t_user),
|
||||
post,
|
||||
|
@ -396,6 +417,7 @@ async fn favourite(
|
|||
#[post("/{status_id}/unfavourite")]
|
||||
async fn unfavourite(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
status_id: web::Path<Uuid>,
|
||||
|
@ -427,6 +449,7 @@ async fn unfavourite(
|
|||
|
||||
let status = build_status(
|
||||
db_client,
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
Some(¤t_user),
|
||||
post,
|
||||
|
@ -437,6 +460,7 @@ async fn unfavourite(
|
|||
#[post("/{status_id}/reblog")]
|
||||
async fn reblog(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
status_id: web::Path<Uuid>,
|
||||
|
@ -465,6 +489,7 @@ async fn reblog(
|
|||
|
||||
let status = build_status(
|
||||
db_client,
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
Some(¤t_user),
|
||||
repost,
|
||||
|
@ -475,6 +500,7 @@ async fn reblog(
|
|||
#[post("/{status_id}/unreblog")]
|
||||
async fn unreblog(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
status_id: web::Path<Uuid>,
|
||||
|
@ -502,6 +528,7 @@ async fn unreblog(
|
|||
|
||||
let status = build_status(
|
||||
db_client,
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
Some(¤t_user),
|
||||
post,
|
||||
|
@ -512,6 +539,7 @@ async fn unreblog(
|
|||
#[post("/{status_id}/make_permanent")]
|
||||
async fn make_permanent(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
status_id: web::Path<Uuid>,
|
||||
|
@ -561,6 +589,7 @@ async fn make_permanent(
|
|||
|
||||
let status = build_status(
|
||||
db_client,
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
Some(¤t_user),
|
||||
post,
|
||||
|
@ -605,6 +634,7 @@ async fn get_signature(
|
|||
#[post("/{status_id}/token_minted")]
|
||||
async fn token_minted(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
status_id: web::Path<Uuid>,
|
||||
|
@ -625,6 +655,7 @@ async fn token_minted(
|
|||
|
||||
let status = build_status(
|
||||
db_client,
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
Some(¤t_user),
|
||||
post,
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
use actix_web::{get, post, web, HttpResponse, Scope};
|
||||
use actix_web::{
|
||||
dev::ConnectionInfo,
|
||||
get,
|
||||
post,
|
||||
web,
|
||||
HttpResponse,
|
||||
Scope,
|
||||
};
|
||||
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
@ -15,6 +22,7 @@ use crate::ethereum::{
|
|||
is_registered_recipient,
|
||||
},
|
||||
};
|
||||
use crate::http::get_request_base_url;
|
||||
use crate::mastodon_api::{
|
||||
accounts::types::Account,
|
||||
oauth::auth::get_current_user,
|
||||
|
@ -92,6 +100,7 @@ async fn get_subscription_options(
|
|||
#[post("/options")]
|
||||
pub async fn register_subscription_option(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
maybe_blockchain: web::Data<Option<ContractSet>>,
|
||||
|
@ -166,6 +175,7 @@ pub async fn register_subscription_option(
|
|||
};
|
||||
|
||||
let account = Account::from_user(
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
current_user,
|
||||
);
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
/// https://docs.joinmastodon.org/methods/timelines/
|
||||
use actix_web::{get, web, HttpResponse, Scope};
|
||||
use actix_web::{
|
||||
dev::ConnectionInfo,
|
||||
get,
|
||||
web,
|
||||
HttpResponse,
|
||||
Scope,
|
||||
};
|
||||
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||
|
||||
use mitra_config::Config;
|
||||
|
||||
use crate::database::{get_database_client, DbPool};
|
||||
use crate::errors::HttpError;
|
||||
use crate::http::get_request_base_url;
|
||||
use crate::mastodon_api::{
|
||||
oauth::auth::get_current_user,
|
||||
statuses::helpers::build_status_list,
|
||||
|
@ -20,6 +27,7 @@ use super::types::TimelineQueryParams;
|
|||
#[get("/home")]
|
||||
async fn home_timeline(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
query_params: web::Query<TimelineQueryParams>,
|
||||
|
@ -34,6 +42,7 @@ async fn home_timeline(
|
|||
).await?;
|
||||
let statuses = build_status_list(
|
||||
db_client,
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
Some(¤t_user),
|
||||
posts,
|
||||
|
@ -45,6 +54,7 @@ async fn home_timeline(
|
|||
#[get("/public")]
|
||||
async fn public_timeline(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
query_params: web::Query<TimelineQueryParams>,
|
||||
|
@ -59,6 +69,7 @@ async fn public_timeline(
|
|||
).await?;
|
||||
let statuses = build_status_list(
|
||||
db_client,
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
Some(¤t_user),
|
||||
posts,
|
||||
|
@ -69,6 +80,7 @@ async fn public_timeline(
|
|||
#[get("/tag/{hashtag}")]
|
||||
async fn hashtag_timeline(
|
||||
auth: Option<BearerAuth>,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
hashtag: web::Path<String>,
|
||||
|
@ -88,6 +100,7 @@ async fn hashtag_timeline(
|
|||
).await?;
|
||||
let statuses = build_status_list(
|
||||
db_client,
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
maybe_current_user.as_ref(),
|
||||
posts,
|
||||
|
|
Loading…
Reference in a new issue