Add "error" and "error_description" fields to Mastodon API error responses
This commit is contained in:
parent
971b541826
commit
e4254e7a3d
21 changed files with 240 additions and 143 deletions
|
@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Set deliverer timeout to 30 seconds.
|
- Set deliverer timeout to 30 seconds.
|
||||||
- Added `federation` parameter group to configuration.
|
- Added `federation` parameter group to configuration.
|
||||||
- Add empty `spoiler_text` property to Mastodon API Status object.
|
- Add empty `spoiler_text` property to Mastodon API Status object.
|
||||||
|
- Added `error` and `error_description` fields to Mastodon API error responses.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -22,6 +23,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
- Deprecated `proxy_url` configuration parameter (replaced by `federation.proxy_url`).
|
- Deprecated `proxy_url` configuration parameter (replaced by `federation.proxy_url`).
|
||||||
- Deprecated Atom feeds at `/feeds/{username}`.
|
- Deprecated Atom feeds at `/feeds/{username}`.
|
||||||
|
- Deprecated `message` field in Mastodon API error response.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|
|
@ -39,12 +39,6 @@ pub enum HttpError {
|
||||||
#[error("{0} not found")]
|
#[error("{0} not found")]
|
||||||
NotFoundError(&'static str),
|
NotFoundError(&'static str),
|
||||||
|
|
||||||
#[error("operation not supported")]
|
|
||||||
NotSupported,
|
|
||||||
|
|
||||||
#[error("{0}")]
|
|
||||||
OperationError(&'static str),
|
|
||||||
|
|
||||||
#[error("internal error")]
|
#[error("internal error")]
|
||||||
InternalError,
|
InternalError,
|
||||||
}
|
}
|
||||||
|
@ -80,8 +74,6 @@ impl ResponseError for HttpError {
|
||||||
HttpError::AuthError(_) => StatusCode::UNAUTHORIZED,
|
HttpError::AuthError(_) => StatusCode::UNAUTHORIZED,
|
||||||
HttpError::PermissionError => StatusCode::FORBIDDEN,
|
HttpError::PermissionError => StatusCode::FORBIDDEN,
|
||||||
HttpError::NotFoundError(_) => StatusCode::NOT_FOUND,
|
HttpError::NotFoundError(_) => StatusCode::NOT_FOUND,
|
||||||
HttpError::NotSupported => StatusCode::IM_A_TEAPOT,
|
|
||||||
HttpError::OperationError(_) => StatusCode::UNPROCESSABLE_ENTITY,
|
|
||||||
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,10 @@ use uuid::Uuid;
|
||||||
|
|
||||||
use mitra_utils::markdown::markdown_basic_to_html;
|
use mitra_utils::markdown::markdown_basic_to_html;
|
||||||
|
|
||||||
use crate::errors::{HttpError, ValidationError};
|
use crate::errors::ValidationError;
|
||||||
use crate::identity::did::Did;
|
use crate::identity::did::Did;
|
||||||
use crate::mastodon_api::{
|
use crate::mastodon_api::{
|
||||||
|
errors::MastodonError,
|
||||||
pagination::PageSize,
|
pagination::PageSize,
|
||||||
uploads::{save_b64_file, UploadError},
|
uploads::{save_b64_file, UploadError},
|
||||||
};
|
};
|
||||||
|
@ -315,7 +316,7 @@ impl AccountUpdateData {
|
||||||
self,
|
self,
|
||||||
profile: &DbActorProfile,
|
profile: &DbActorProfile,
|
||||||
media_dir: &Path,
|
media_dir: &Path,
|
||||||
) -> Result<ProfileUpdateData, HttpError> {
|
) -> Result<ProfileUpdateData, MastodonError> {
|
||||||
let maybe_bio = if let Some(ref bio_source) = self.note {
|
let maybe_bio = if let Some(ref bio_source) = self.note {
|
||||||
let bio = markdown_basic_to_html(bio_source)
|
let bio = markdown_basic_to_html(bio_source)
|
||||||
.map_err(|_| ValidationError("invalid markdown"))?;
|
.map_err(|_| ValidationError("invalid markdown"))?;
|
||||||
|
|
|
@ -32,7 +32,7 @@ use crate::activitypub::builders::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use crate::database::{get_database_client, DatabaseError, DbPool};
|
use crate::database::{get_database_client, DatabaseError, DbPool};
|
||||||
use crate::errors::{HttpError, ValidationError};
|
use crate::errors::ValidationError;
|
||||||
use crate::ethereum::{
|
use crate::ethereum::{
|
||||||
contracts::ContractSet,
|
contracts::ContractSet,
|
||||||
eip4361::verify_eip4361_signature,
|
eip4361::verify_eip4361_signature,
|
||||||
|
@ -58,6 +58,7 @@ use crate::json_signatures::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use crate::mastodon_api::{
|
use crate::mastodon_api::{
|
||||||
|
errors::MastodonError,
|
||||||
oauth::auth::get_current_user,
|
oauth::auth::get_current_user,
|
||||||
pagination::get_paginated_response,
|
pagination::get_paginated_response,
|
||||||
search::helpers::search_profiles_only,
|
search::helpers::search_profiles_only,
|
||||||
|
@ -122,7 +123,7 @@ pub async fn create_account(
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
maybe_blockchain: web::Data<Option<ContractSet>>,
|
maybe_blockchain: web::Data<Option<ContractSet>>,
|
||||||
account_data: web::Json<AccountCreateData>,
|
account_data: web::Json<AccountCreateData>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &mut **get_database_client(&db_pool).await?;
|
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||||
// Validate
|
// Validate
|
||||||
account_data.clean()?;
|
account_data.clean()?;
|
||||||
|
@ -136,7 +137,7 @@ pub async fn create_account(
|
||||||
|
|
||||||
let maybe_password_hash = if let Some(password) = account_data.password.as_ref() {
|
let maybe_password_hash = if let Some(password) = account_data.password.as_ref() {
|
||||||
let password_hash = hash_password(password)
|
let password_hash = hash_password(password)
|
||||||
.map_err(|_| HttpError::InternalError)?;
|
.map_err(|_| MastodonError::InternalError)?;
|
||||||
Some(password_hash)
|
Some(password_hash)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -165,7 +166,7 @@ pub async fn create_account(
|
||||||
let wallet_address = maybe_wallet_address.as_ref()
|
let wallet_address = maybe_wallet_address.as_ref()
|
||||||
.ok_or(ValidationError("wallet address is required"))?;
|
.ok_or(ValidationError("wallet address is required"))?;
|
||||||
let is_allowed = is_allowed_user(gate, wallet_address).await
|
let is_allowed = is_allowed_user(gate, wallet_address).await
|
||||||
.map_err(|_| HttpError::InternalError)?;
|
.map_err(|_| MastodonError::InternalError)?;
|
||||||
if !is_allowed {
|
if !is_allowed {
|
||||||
return Err(ValidationError("not allowed to sign up").into());
|
return Err(ValidationError("not allowed to sign up").into());
|
||||||
};
|
};
|
||||||
|
@ -175,10 +176,10 @@ pub async fn create_account(
|
||||||
// Generate RSA private key for actor
|
// Generate RSA private key for actor
|
||||||
let private_key = match web::block(generate_rsa_key).await {
|
let private_key = match web::block(generate_rsa_key).await {
|
||||||
Ok(Ok(private_key)) => private_key,
|
Ok(Ok(private_key)) => private_key,
|
||||||
_ => return Err(HttpError::InternalError),
|
_ => return Err(MastodonError::InternalError),
|
||||||
};
|
};
|
||||||
let private_key_pem = serialize_private_key(&private_key)
|
let private_key_pem = serialize_private_key(&private_key)
|
||||||
.map_err(|_| HttpError::InternalError)?;
|
.map_err(|_| MastodonError::InternalError)?;
|
||||||
|
|
||||||
let AccountCreateData { username, invite_code, .. } =
|
let AccountCreateData { username, invite_code, .. } =
|
||||||
account_data.into_inner();
|
account_data.into_inner();
|
||||||
|
@ -216,7 +217,7 @@ async fn verify_credentials(
|
||||||
connection_info: ConnectionInfo,
|
connection_info: ConnectionInfo,
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let user = get_current_user(db_client, auth.token()).await?;
|
let user = get_current_user(db_client, auth.token()).await?;
|
||||||
let account = Account::from_user(
|
let account = Account::from_user(
|
||||||
|
@ -234,7 +235,7 @@ async fn update_credentials(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
account_data: web::Json<AccountUpdateData>,
|
account_data: web::Json<AccountUpdateData>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let mut current_user = get_current_user(db_client, auth.token()).await?;
|
let mut current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let mut profile_data = account_data.into_inner()
|
let mut profile_data = account_data.into_inner()
|
||||||
|
@ -270,7 +271,7 @@ async fn get_unsigned_update(
|
||||||
auth: BearerAuth,
|
auth: BearerAuth,
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let internal_activity_id = generate_ulid();
|
let internal_activity_id = generate_ulid();
|
||||||
|
@ -278,9 +279,9 @@ async fn get_unsigned_update(
|
||||||
&config.instance_url(),
|
&config.instance_url(),
|
||||||
¤t_user,
|
¤t_user,
|
||||||
Some(internal_activity_id),
|
Some(internal_activity_id),
|
||||||
).map_err(|_| HttpError::InternalError)?;
|
).map_err(|_| MastodonError::InternalError)?;
|
||||||
let canonical_json = canonicalize_object(&activity)
|
let canonical_json = canonicalize_object(&activity)
|
||||||
.map_err(|_| HttpError::InternalError)?;
|
.map_err(|_| MastodonError::InternalError)?;
|
||||||
let data = UnsignedActivity {
|
let data = UnsignedActivity {
|
||||||
params: ActivityParams::Update { internal_activity_id },
|
params: ActivityParams::Update { internal_activity_id },
|
||||||
message: canonical_json,
|
message: canonical_json,
|
||||||
|
@ -295,7 +296,7 @@ async fn send_signed_activity(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
data: web::Json<SignedActivity>,
|
data: web::Json<SignedActivity>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &mut **get_database_client(&db_pool).await?;
|
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let signer = data.signer.parse::<Did>()
|
let signer = data.signer.parse::<Did>()
|
||||||
|
@ -310,11 +311,11 @@ async fn send_signed_activity(
|
||||||
&config.instance(),
|
&config.instance(),
|
||||||
¤t_user,
|
¤t_user,
|
||||||
Some(*internal_activity_id),
|
Some(*internal_activity_id),
|
||||||
).await.map_err(|_| HttpError::InternalError)?
|
).await.map_err(|_| MastodonError::InternalError)?
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let canonical_json = canonicalize_object(&outgoing_activity.activity)
|
let canonical_json = canonicalize_object(&outgoing_activity.activity)
|
||||||
.map_err(|_| HttpError::InternalError)?;
|
.map_err(|_| MastodonError::InternalError)?;
|
||||||
let proof = match signer {
|
let proof = match signer {
|
||||||
Did::Key(signer) => {
|
Did::Key(signer) => {
|
||||||
let signature_bin = parse_minisign_signature(&data.signature)
|
let signature_bin = parse_minisign_signature(&data.signature)
|
||||||
|
@ -332,7 +333,7 @@ async fn send_signed_activity(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
add_integrity_proof(&mut outgoing_activity.activity, proof)
|
add_integrity_proof(&mut outgoing_activity.activity, proof)
|
||||||
.map_err(|_| HttpError::InternalError)?;
|
.map_err(|_| MastodonError::InternalError)?;
|
||||||
|
|
||||||
outgoing_activity.enqueue(db_client).await?;
|
outgoing_activity.enqueue(db_client).await?;
|
||||||
|
|
||||||
|
@ -350,7 +351,7 @@ async fn get_identity_claim(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
query_params: web::Query<IdentityClaimQueryParams>,
|
query_params: web::Query<IdentityClaimQueryParams>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let did = match query_params.proof_type.as_str() {
|
let did = match query_params.proof_type.as_str() {
|
||||||
|
@ -370,7 +371,7 @@ async fn get_identity_claim(
|
||||||
};
|
};
|
||||||
let actor_id = current_user.profile.actor_id(&config.instance_url());
|
let actor_id = current_user.profile.actor_id(&config.instance_url());
|
||||||
let claim = create_identity_claim(&actor_id, &did)
|
let claim = create_identity_claim(&actor_id, &did)
|
||||||
.map_err(|_| HttpError::InternalError)?;
|
.map_err(|_| MastodonError::InternalError)?;
|
||||||
let response = IdentityClaim { did, claim };
|
let response = IdentityClaim { did, claim };
|
||||||
Ok(HttpResponse::Ok().json(response))
|
Ok(HttpResponse::Ok().json(response))
|
||||||
}
|
}
|
||||||
|
@ -382,7 +383,7 @@ async fn create_identity_proof(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
proof_data: web::Json<IdentityProofData>,
|
proof_data: web::Json<IdentityProofData>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let mut current_user = get_current_user(db_client, auth.token()).await?;
|
let mut current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let did = proof_data.did.parse::<Did>()
|
let did = proof_data.did.parse::<Did>()
|
||||||
|
@ -469,7 +470,7 @@ async fn get_relationships_view(
|
||||||
auth: BearerAuth,
|
auth: BearerAuth,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
query_params: web::Query<RelationshipQueryParams>,
|
query_params: web::Query<RelationshipQueryParams>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let relationship = get_relationship(
|
let relationship = get_relationship(
|
||||||
|
@ -486,7 +487,7 @@ async fn lookup_acct(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
query_params: web::Query<LookupAcctQueryParams>,
|
query_params: web::Query<LookupAcctQueryParams>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let profile = get_profile_by_acct(db_client, &query_params.acct).await?;
|
let profile = get_profile_by_acct(db_client, &query_params.acct).await?;
|
||||||
let account = Account::from_profile(
|
let account = Account::from_profile(
|
||||||
|
@ -503,7 +504,7 @@ async fn search_by_acct(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
query_params: web::Query<SearchAcctQueryParams>,
|
query_params: web::Query<SearchAcctQueryParams>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let profiles = search_profiles_only(
|
let profiles = search_profiles_only(
|
||||||
db_client,
|
db_client,
|
||||||
|
@ -528,7 +529,7 @@ async fn search_by_did(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
query_params: web::Query<SearchDidQueryParams>,
|
query_params: web::Query<SearchDidQueryParams>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let did: Did = query_params.did.parse()
|
let did: Did = query_params.did.parse()
|
||||||
.map_err(|_| ValidationError("invalid DID"))?;
|
.map_err(|_| ValidationError("invalid DID"))?;
|
||||||
|
@ -551,7 +552,7 @@ async fn get_account(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
account_id: web::Path<Uuid>,
|
account_id: web::Path<Uuid>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let profile = get_profile_by_id(db_client, &account_id).await?;
|
let profile = get_profile_by_id(db_client, &account_id).await?;
|
||||||
let account = Account::from_profile(
|
let account = Account::from_profile(
|
||||||
|
@ -569,7 +570,7 @@ async fn follow_account(
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
account_id: web::Path<Uuid>,
|
account_id: web::Path<Uuid>,
|
||||||
follow_data: web::Json<FollowData>,
|
follow_data: web::Json<FollowData>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &mut **get_database_client(&db_pool).await?;
|
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let target = get_profile_by_id(db_client, &account_id).await?;
|
let target = get_profile_by_id(db_client, &account_id).await?;
|
||||||
|
@ -603,7 +604,7 @@ async fn unfollow_account(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
account_id: web::Path<Uuid>,
|
account_id: web::Path<Uuid>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &mut **get_database_client(&db_pool).await?;
|
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let target = get_profile_by_id(db_client, &account_id).await?;
|
let target = get_profile_by_id(db_client, &account_id).await?;
|
||||||
|
@ -611,7 +612,7 @@ async fn unfollow_account(
|
||||||
Ok(Some(follow_request_id)) => {
|
Ok(Some(follow_request_id)) => {
|
||||||
// Remote follow
|
// Remote follow
|
||||||
let remote_actor = target.actor_json
|
let remote_actor = target.actor_json
|
||||||
.ok_or(HttpError::InternalError)?;
|
.ok_or(MastodonError::InternalError)?;
|
||||||
prepare_undo_follow(
|
prepare_undo_follow(
|
||||||
&config.instance(),
|
&config.instance(),
|
||||||
¤t_user,
|
¤t_user,
|
||||||
|
@ -640,7 +641,7 @@ async fn get_account_statuses(
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
account_id: web::Path<Uuid>,
|
account_id: web::Path<Uuid>,
|
||||||
query_params: web::Query<StatusListQueryParams>,
|
query_params: web::Query<StatusListQueryParams>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let maybe_current_user = match auth {
|
let maybe_current_user = match auth {
|
||||||
Some(auth) => Some(get_current_user(db_client, auth.token()).await?),
|
Some(auth) => Some(get_current_user(db_client, auth.token()).await?),
|
||||||
|
@ -681,7 +682,7 @@ async fn get_account_followers(
|
||||||
account_id: web::Path<Uuid>,
|
account_id: web::Path<Uuid>,
|
||||||
query_params: web::Query<FollowListQueryParams>,
|
query_params: web::Query<FollowListQueryParams>,
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let profile = get_profile_by_id(db_client, &account_id).await?;
|
let profile = get_profile_by_id(db_client, &account_id).await?;
|
||||||
|
@ -725,7 +726,7 @@ async fn get_account_following(
|
||||||
account_id: web::Path<Uuid>,
|
account_id: web::Path<Uuid>,
|
||||||
query_params: web::Query<FollowListQueryParams>,
|
query_params: web::Query<FollowListQueryParams>,
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let profile = get_profile_by_id(db_client, &account_id).await?;
|
let profile = get_profile_by_id(db_client, &account_id).await?;
|
||||||
|
@ -768,7 +769,7 @@ async fn get_account_subscribers(
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
account_id: web::Path<Uuid>,
|
account_id: web::Path<Uuid>,
|
||||||
query_params: web::Query<FollowListQueryParams>,
|
query_params: web::Query<FollowListQueryParams>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let profile = get_profile_by_id(db_client, &account_id).await?;
|
let profile = get_profile_by_id(db_client, &account_id).await?;
|
||||||
|
|
|
@ -8,8 +8,10 @@ use actix_web::{
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::database::{get_database_client, DbPool};
|
use crate::database::{get_database_client, DbPool};
|
||||||
use crate::errors::HttpError;
|
use crate::mastodon_api::{
|
||||||
use crate::mastodon_api::oauth::utils::generate_access_token;
|
errors::MastodonError,
|
||||||
|
oauth::utils::generate_access_token,
|
||||||
|
};
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
oauth::queries::create_oauth_app,
|
oauth::queries::create_oauth_app,
|
||||||
oauth::types::DbOauthAppData,
|
oauth::types::DbOauthAppData,
|
||||||
|
@ -24,7 +26,7 @@ async fn create_app_view(
|
||||||
web::Json<CreateAppRequest>,
|
web::Json<CreateAppRequest>,
|
||||||
web::Form<CreateAppRequest>,
|
web::Form<CreateAppRequest>,
|
||||||
>,
|
>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let request_data = match request_data {
|
let request_data = match request_data {
|
||||||
Either::Left(json) => json.into_inner(),
|
Either::Left(json) => json.into_inner(),
|
||||||
Either::Right(form) => form.into_inner(),
|
Either::Right(form) => form.into_inner(),
|
||||||
|
|
|
@ -7,8 +7,8 @@ use actix_web::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::database::{get_database_client, DbPool};
|
use crate::database::{get_database_client, DbPool};
|
||||||
use crate::errors::HttpError;
|
|
||||||
use crate::http::get_request_base_url;
|
use crate::http::get_request_base_url;
|
||||||
|
use crate::mastodon_api::errors::MastodonError;
|
||||||
use crate::models::emojis::queries::get_local_emojis;
|
use crate::models::emojis::queries::get_local_emojis;
|
||||||
use super::types::CustomEmoji;
|
use super::types::CustomEmoji;
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ use super::types::CustomEmoji;
|
||||||
async fn custom_emoji_list(
|
async fn custom_emoji_list(
|
||||||
connection_info: ConnectionInfo,
|
connection_info: ConnectionInfo,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let base_url = get_request_base_url(connection_info);
|
let base_url = get_request_base_url(connection_info);
|
||||||
let emojis: Vec<CustomEmoji> = get_local_emojis(db_client).await?
|
let emojis: Vec<CustomEmoji> = get_local_emojis(db_client).await?
|
||||||
|
|
|
@ -11,10 +11,10 @@ use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||||
use mitra_config::Config;
|
use mitra_config::Config;
|
||||||
|
|
||||||
use crate::database::{get_database_client, DbPool};
|
use crate::database::{get_database_client, DbPool};
|
||||||
use crate::errors::HttpError;
|
|
||||||
use crate::http::get_request_base_url;
|
use crate::http::get_request_base_url;
|
||||||
use crate::mastodon_api::{
|
use crate::mastodon_api::{
|
||||||
accounts::types::Account,
|
accounts::types::Account,
|
||||||
|
errors::MastodonError,
|
||||||
oauth::auth::get_current_user,
|
oauth::auth::get_current_user,
|
||||||
};
|
};
|
||||||
use crate::models::profiles::queries::get_profiles;
|
use crate::models::profiles::queries::get_profiles;
|
||||||
|
@ -27,7 +27,7 @@ async fn profile_directory(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
query_params: web::Query<DirectoryQueryParams>,
|
query_params: web::Query<DirectoryQueryParams>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
get_current_user(db_client, auth.token()).await?;
|
get_current_user(db_client, auth.token()).await?;
|
||||||
let profiles = get_profiles(
|
let profiles = get_profiles(
|
||||||
|
|
89
src/mastodon_api/errors.rs
Normal file
89
src/mastodon_api/errors.rs
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
use actix_web::{
|
||||||
|
error::ResponseError,
|
||||||
|
http::StatusCode,
|
||||||
|
HttpResponse,
|
||||||
|
HttpResponseBuilder,
|
||||||
|
};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::database::DatabaseError;
|
||||||
|
use crate::errors::ValidationError;
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum MastodonError {
|
||||||
|
#[error(transparent)]
|
||||||
|
ActixError(#[from] actix_web::Error),
|
||||||
|
|
||||||
|
#[error("database error")]
|
||||||
|
DatabaseError(#[source] DatabaseError),
|
||||||
|
|
||||||
|
#[error("{0}")]
|
||||||
|
ValidationError(String),
|
||||||
|
|
||||||
|
#[error("{0}")]
|
||||||
|
ValidationErrorAuto(#[from] ValidationError),
|
||||||
|
|
||||||
|
#[error("{0}")]
|
||||||
|
AuthError(&'static str),
|
||||||
|
|
||||||
|
#[error("permission error")]
|
||||||
|
PermissionError,
|
||||||
|
|
||||||
|
#[error("{0} not found")]
|
||||||
|
NotFoundError(&'static str),
|
||||||
|
|
||||||
|
#[error("operation not supported")]
|
||||||
|
NotSupported,
|
||||||
|
|
||||||
|
#[error("{0}")]
|
||||||
|
OperationError(&'static str),
|
||||||
|
|
||||||
|
#[error("internal error")]
|
||||||
|
InternalError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DatabaseError> for MastodonError {
|
||||||
|
fn from(error: DatabaseError) -> Self {
|
||||||
|
match error {
|
||||||
|
DatabaseError::NotFound(name) => Self::NotFoundError(name),
|
||||||
|
DatabaseError::AlreadyExists(name) => Self::ValidationError(
|
||||||
|
format!("{} already exists", name),
|
||||||
|
),
|
||||||
|
_ => Self::DatabaseError(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://docs.joinmastodon.org/entities/Error/
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct MastodonErrorData {
|
||||||
|
message: String, // deprecated
|
||||||
|
error: String,
|
||||||
|
error_description: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResponseError for MastodonError {
|
||||||
|
fn error_response(&self) -> HttpResponse {
|
||||||
|
let error_data = MastodonErrorData {
|
||||||
|
message: self.to_string(),
|
||||||
|
error: self.to_string(),
|
||||||
|
error_description: Some(self.to_string()),
|
||||||
|
};
|
||||||
|
HttpResponseBuilder::new(self.status_code()).json(error_data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn status_code(&self) -> StatusCode {
|
||||||
|
match self {
|
||||||
|
Self::ActixError(error) =>
|
||||||
|
error.as_response_error().status_code(),
|
||||||
|
Self::ValidationError(_) => StatusCode::BAD_REQUEST,
|
||||||
|
Self::ValidationErrorAuto(_) => StatusCode::BAD_REQUEST,
|
||||||
|
Self::AuthError(_) => StatusCode::UNAUTHORIZED,
|
||||||
|
Self::PermissionError => StatusCode::FORBIDDEN,
|
||||||
|
Self::NotFoundError(_) => StatusCode::NOT_FOUND,
|
||||||
|
Self::NotSupported => StatusCode::IM_A_TEAPOT,
|
||||||
|
Self::OperationError(_) => StatusCode::UNPROCESSABLE_ENTITY,
|
||||||
|
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,8 +3,8 @@ use actix_web::{get, web, HttpResponse, Scope};
|
||||||
use mitra_config::Config;
|
use mitra_config::Config;
|
||||||
|
|
||||||
use crate::database::{get_database_client, DbPool};
|
use crate::database::{get_database_client, DbPool};
|
||||||
use crate::errors::HttpError;
|
|
||||||
use crate::ethereum::contracts::ContractSet;
|
use crate::ethereum::contracts::ContractSet;
|
||||||
|
use crate::mastodon_api::errors::MastodonError;
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
instances::queries::get_peer_count,
|
instances::queries::get_peer_count,
|
||||||
posts::queries::get_local_post_count,
|
posts::queries::get_local_post_count,
|
||||||
|
@ -18,7 +18,7 @@ async fn instance_view(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
maybe_blockchain: web::Data<Option<ContractSet>>,
|
maybe_blockchain: web::Data<Option<ContractSet>>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let user_count = get_user_count(db_client).await?;
|
let user_count = get_user_count(db_client).await?;
|
||||||
let post_count = get_local_post_count(db_client).await?;
|
let post_count = get_local_post_count(db_client).await?;
|
||||||
|
|
|
@ -2,8 +2,10 @@ use actix_web::{get, post, web, HttpResponse, Scope};
|
||||||
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||||
|
|
||||||
use crate::database::{get_database_client, DbPool};
|
use crate::database::{get_database_client, DbPool};
|
||||||
use crate::errors::HttpError;
|
use crate::mastodon_api::{
|
||||||
use crate::mastodon_api::oauth::auth::get_current_user;
|
errors::MastodonError,
|
||||||
|
oauth::auth::get_current_user,
|
||||||
|
};
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
markers::queries::{
|
markers::queries::{
|
||||||
create_or_update_marker,
|
create_or_update_marker,
|
||||||
|
@ -19,7 +21,7 @@ async fn get_marker_view(
|
||||||
auth: BearerAuth,
|
auth: BearerAuth,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
query_params: web::Query<MarkerQueryParams>,
|
query_params: web::Query<MarkerQueryParams>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let timeline = query_params.to_timeline()?;
|
let timeline = query_params.to_timeline()?;
|
||||||
|
@ -35,7 +37,7 @@ async fn update_marker_view(
|
||||||
auth: BearerAuth,
|
auth: BearerAuth,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
marker_data: web::Json<MarkerCreateData>,
|
marker_data: web::Json<MarkerCreateData>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let db_marker = create_or_update_marker(
|
let db_marker = create_or_update_marker(
|
||||||
|
|
|
@ -5,8 +5,8 @@ use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||||
use mitra_config::Config;
|
use mitra_config::Config;
|
||||||
|
|
||||||
use crate::database::{get_database_client, DbPool};
|
use crate::database::{get_database_client, DbPool};
|
||||||
use crate::errors::HttpError;
|
|
||||||
use crate::mastodon_api::{
|
use crate::mastodon_api::{
|
||||||
|
errors::MastodonError,
|
||||||
oauth::auth::get_current_user,
|
oauth::auth::get_current_user,
|
||||||
uploads::save_b64_file,
|
uploads::save_b64_file,
|
||||||
};
|
};
|
||||||
|
@ -19,7 +19,7 @@ async fn create_attachment_view(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
attachment_data: web::Json<AttachmentCreateData>,
|
attachment_data: web::Json<AttachmentCreateData>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let (file_name, file_size, media_type) = save_b64_file(
|
let (file_name, file_size, media_type) = save_b64_file(
|
||||||
|
|
|
@ -7,12 +7,14 @@ pub mod markers;
|
||||||
pub mod media;
|
pub mod media;
|
||||||
pub mod notifications;
|
pub mod notifications;
|
||||||
pub mod oauth;
|
pub mod oauth;
|
||||||
mod pagination;
|
|
||||||
pub mod search;
|
pub mod search;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
pub mod statuses;
|
pub mod statuses;
|
||||||
pub mod subscriptions;
|
pub mod subscriptions;
|
||||||
pub mod timelines;
|
pub mod timelines;
|
||||||
|
|
||||||
|
mod errors;
|
||||||
|
mod pagination;
|
||||||
mod uploads;
|
mod uploads;
|
||||||
|
|
||||||
const MASTODON_API_VERSION: &str = "4.0.0";
|
const MASTODON_API_VERSION: &str = "4.0.0";
|
||||||
|
|
|
@ -11,9 +11,9 @@ use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||||
use mitra_config::Config;
|
use mitra_config::Config;
|
||||||
|
|
||||||
use crate::database::{get_database_client, DbPool};
|
use crate::database::{get_database_client, DbPool};
|
||||||
use crate::errors::HttpError;
|
|
||||||
use crate::http::get_request_base_url;
|
use crate::http::get_request_base_url;
|
||||||
use crate::mastodon_api::{
|
use crate::mastodon_api::{
|
||||||
|
errors::MastodonError,
|
||||||
oauth::auth::get_current_user,
|
oauth::auth::get_current_user,
|
||||||
pagination::get_paginated_response,
|
pagination::get_paginated_response,
|
||||||
};
|
};
|
||||||
|
@ -28,7 +28,7 @@ async fn get_notifications_view(
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
query_params: web::Query<NotificationQueryParams>,
|
query_params: web::Query<NotificationQueryParams>,
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let base_url = get_request_base_url(connection_info);
|
let base_url = get_request_base_url(connection_info);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::database::{DatabaseClient, DatabaseError};
|
use crate::database::{DatabaseClient, DatabaseError};
|
||||||
use crate::errors::HttpError;
|
use crate::mastodon_api::errors::MastodonError;
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
oauth::queries::get_user_by_oauth_token,
|
oauth::queries::get_user_by_oauth_token,
|
||||||
users::types::User,
|
users::types::User,
|
||||||
|
@ -8,13 +8,13 @@ use crate::models::{
|
||||||
pub async fn get_current_user(
|
pub async fn get_current_user(
|
||||||
db_client: &impl DatabaseClient,
|
db_client: &impl DatabaseClient,
|
||||||
token: &str,
|
token: &str,
|
||||||
) -> Result<User, HttpError> {
|
) -> Result<User, MastodonError> {
|
||||||
let user = get_user_by_oauth_token(db_client, token).await.map_err(|err| {
|
let user = get_user_by_oauth_token(db_client, token).await.map_err(|err| {
|
||||||
match err {
|
match err {
|
||||||
DatabaseError::NotFound(_) => {
|
DatabaseError::NotFound(_) => {
|
||||||
HttpError::AuthError("access token is invalid")
|
MastodonError::AuthError("access token is invalid")
|
||||||
},
|
},
|
||||||
_ => HttpError::InternalError,
|
_ => MastodonError::InternalError,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
Ok(user)
|
Ok(user)
|
||||||
|
|
|
@ -13,12 +13,13 @@ use mitra_config::Config;
|
||||||
use mitra_utils::passwords::verify_password;
|
use mitra_utils::passwords::verify_password;
|
||||||
|
|
||||||
use crate::database::{get_database_client, DatabaseError, DbPool};
|
use crate::database::{get_database_client, DatabaseError, DbPool};
|
||||||
use crate::errors::{HttpError, ValidationError};
|
use crate::errors::ValidationError;
|
||||||
use crate::ethereum::{
|
use crate::ethereum::{
|
||||||
eip4361::verify_eip4361_signature,
|
eip4361::verify_eip4361_signature,
|
||||||
utils::validate_ethereum_address,
|
utils::validate_ethereum_address,
|
||||||
};
|
};
|
||||||
use crate::http::FormOrJson;
|
use crate::http::FormOrJson;
|
||||||
|
use crate::mastodon_api::errors::MastodonError;
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
oauth::queries::{
|
oauth::queries::{
|
||||||
create_oauth_authorization,
|
create_oauth_authorization,
|
||||||
|
@ -60,7 +61,7 @@ async fn authorize_view(
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
form_data: web::Form<AuthorizationRequest>,
|
form_data: web::Form<AuthorizationRequest>,
|
||||||
query_params: web::Query<AuthorizationQueryParams>,
|
query_params: web::Query<AuthorizationQueryParams>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let user = get_user_by_name(db_client, &form_data.username).await?;
|
let user = get_user_by_name(db_client, &form_data.username).await?;
|
||||||
let password_hash = user.password_hash.as_ref()
|
let password_hash = user.password_hash.as_ref()
|
||||||
|
@ -68,7 +69,7 @@ async fn authorize_view(
|
||||||
let password_correct = verify_password(
|
let password_correct = verify_password(
|
||||||
password_hash,
|
password_hash,
|
||||||
&form_data.password,
|
&form_data.password,
|
||||||
).map_err(|_| HttpError::InternalError)?;
|
).map_err(|_| MastodonError::InternalError)?;
|
||||||
if !password_correct {
|
if !password_correct {
|
||||||
return Err(ValidationError("incorrect password").into());
|
return Err(ValidationError("incorrect password").into());
|
||||||
};
|
};
|
||||||
|
@ -116,7 +117,7 @@ async fn token_view(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
request_data: FormOrJson<TokenRequest>,
|
request_data: FormOrJson<TokenRequest>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let request_data = request_data.into_inner();
|
let request_data = request_data.into_inner();
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let user = match request_data.grant_type.as_str() {
|
let user = match request_data.grant_type.as_str() {
|
||||||
|
@ -165,7 +166,7 @@ async fn token_view(
|
||||||
let password_correct = verify_password(
|
let password_correct = verify_password(
|
||||||
password_hash,
|
password_hash,
|
||||||
password,
|
password,
|
||||||
).map_err(|_| HttpError::InternalError)?;
|
).map_err(|_| MastodonError::InternalError)?;
|
||||||
if !password_correct {
|
if !password_correct {
|
||||||
return Err(ValidationError("incorrect password").into());
|
return Err(ValidationError("incorrect password").into());
|
||||||
};
|
};
|
||||||
|
@ -193,7 +194,7 @@ async fn revoke_token_view(
|
||||||
auth: BearerAuth,
|
auth: BearerAuth,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
request_data: web::Json<RevocationRequest>,
|
request_data: web::Json<RevocationRequest>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &mut **get_database_client(&db_pool).await?;
|
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
match delete_oauth_token(
|
match delete_oauth_token(
|
||||||
|
@ -202,7 +203,7 @@ async fn revoke_token_view(
|
||||||
&request_data.token,
|
&request_data.token,
|
||||||
).await {
|
).await {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(DatabaseError::NotFound(_)) => return Err(HttpError::PermissionError),
|
Err(DatabaseError::NotFound(_)) => return Err(MastodonError::PermissionError),
|
||||||
Err(other_error) => return Err(other_error.into()),
|
Err(other_error) => return Err(other_error.into()),
|
||||||
};
|
};
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
|
|
|
@ -11,10 +11,10 @@ use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||||
use mitra_config::Config;
|
use mitra_config::Config;
|
||||||
|
|
||||||
use crate::database::{get_database_client, DbPool};
|
use crate::database::{get_database_client, DbPool};
|
||||||
use crate::errors::HttpError;
|
|
||||||
use crate::http::get_request_base_url;
|
use crate::http::get_request_base_url;
|
||||||
use crate::mastodon_api::{
|
use crate::mastodon_api::{
|
||||||
accounts::types::Account,
|
accounts::types::Account,
|
||||||
|
errors::MastodonError,
|
||||||
oauth::auth::get_current_user,
|
oauth::auth::get_current_user,
|
||||||
statuses::helpers::build_status_list,
|
statuses::helpers::build_status_list,
|
||||||
statuses::types::Tag,
|
statuses::types::Tag,
|
||||||
|
@ -29,7 +29,7 @@ async fn search_view(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
query_params: web::Query<SearchQueryParams>,
|
query_params: web::Query<SearchQueryParams>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &mut **get_database_client(&db_pool).await?;
|
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let (profiles, posts, tags) = search(
|
let (profiles, posts, tags) = search(
|
||||||
|
|
|
@ -18,10 +18,11 @@ use crate::activitypub::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use crate::database::{get_database_client, DatabaseError, DbPool};
|
use crate::database::{get_database_client, DatabaseError, DbPool};
|
||||||
use crate::errors::{HttpError, ValidationError};
|
use crate::errors::ValidationError;
|
||||||
use crate::http::get_request_base_url;
|
use crate::http::get_request_base_url;
|
||||||
use crate::mastodon_api::{
|
use crate::mastodon_api::{
|
||||||
accounts::types::Account,
|
accounts::types::Account,
|
||||||
|
errors::MastodonError,
|
||||||
oauth::auth::get_current_user,
|
oauth::auth::get_current_user,
|
||||||
};
|
};
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
|
@ -49,11 +50,11 @@ async fn change_password_view(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
request_data: web::Json<PasswordChangeRequest>,
|
request_data: web::Json<PasswordChangeRequest>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let password_hash = hash_password(&request_data.new_password)
|
let password_hash = hash_password(&request_data.new_password)
|
||||||
.map_err(|_| HttpError::InternalError)?;
|
.map_err(|_| MastodonError::InternalError)?;
|
||||||
set_user_password(db_client, ¤t_user.id, password_hash).await?;
|
set_user_password(db_client, ¤t_user.id, password_hash).await?;
|
||||||
let account = Account::from_user(
|
let account = Account::from_user(
|
||||||
&get_request_base_url(connection_info),
|
&get_request_base_url(connection_info),
|
||||||
|
@ -68,7 +69,7 @@ async fn export_followers_view(
|
||||||
auth: BearerAuth,
|
auth: BearerAuth,
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let csv = export_followers(
|
let csv = export_followers(
|
||||||
|
@ -87,7 +88,7 @@ async fn export_follows_view(
|
||||||
auth: BearerAuth,
|
auth: BearerAuth,
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let csv = export_follows(
|
let csv = export_follows(
|
||||||
|
@ -107,7 +108,7 @@ async fn import_follows_view(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
request_data: web::Json<ImportFollowsRequest>,
|
request_data: web::Json<ImportFollowsRequest>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let address_list = parse_address_list(&request_data.follows_csv)?;
|
let address_list = parse_address_list(&request_data.follows_csv)?;
|
||||||
|
@ -131,7 +132,7 @@ async fn move_followers(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
request_data: web::Json<MoveFollowersRequest>,
|
request_data: web::Json<MoveFollowersRequest>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &mut **get_database_client(&db_pool).await?;
|
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let instance = config.instance();
|
let instance = config.instance();
|
||||||
|
|
|
@ -27,7 +27,7 @@ use crate::activitypub::builders::{
|
||||||
undo_like::prepare_undo_like,
|
undo_like::prepare_undo_like,
|
||||||
};
|
};
|
||||||
use crate::database::{get_database_client, DatabaseError, DbPool};
|
use crate::database::{get_database_client, DatabaseError, DbPool};
|
||||||
use crate::errors::{HttpError, ValidationError};
|
use crate::errors::ValidationError;
|
||||||
use crate::ethereum::nft::create_mint_signature;
|
use crate::ethereum::nft::create_mint_signature;
|
||||||
use crate::http::{get_request_base_url, FormOrJson};
|
use crate::http::{get_request_base_url, FormOrJson};
|
||||||
use crate::ipfs::{
|
use crate::ipfs::{
|
||||||
|
@ -35,7 +35,10 @@ use crate::ipfs::{
|
||||||
posts::PostMetadata,
|
posts::PostMetadata,
|
||||||
utils::get_ipfs_url,
|
utils::get_ipfs_url,
|
||||||
};
|
};
|
||||||
use crate::mastodon_api::oauth::auth::get_current_user;
|
use crate::mastodon_api::{
|
||||||
|
errors::MastodonError,
|
||||||
|
oauth::auth::get_current_user,
|
||||||
|
};
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
posts::helpers::{can_create_post, can_view_post},
|
posts::helpers::{can_create_post, can_view_post},
|
||||||
posts::queries::{
|
posts::queries::{
|
||||||
|
@ -81,11 +84,11 @@ async fn create_status(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
status_data: FormOrJson<StatusData>,
|
status_data: FormOrJson<StatusData>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &mut **get_database_client(&db_pool).await?;
|
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
if !can_create_post(¤t_user) {
|
if !can_create_post(¤t_user) {
|
||||||
return Err(HttpError::PermissionError);
|
return Err(MastodonError::PermissionError);
|
||||||
};
|
};
|
||||||
let instance = config.instance();
|
let instance = config.instance();
|
||||||
let status_data = status_data.into_inner();
|
let status_data = status_data.into_inner();
|
||||||
|
@ -213,7 +216,7 @@ async fn preview_status(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
status_data: web::Json<StatusPreviewData>,
|
status_data: web::Json<StatusPreviewData>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
get_current_user(db_client, auth.token()).await?;
|
get_current_user(db_client, auth.token()).await?;
|
||||||
let instance = config.instance();
|
let instance = config.instance();
|
||||||
|
@ -249,7 +252,7 @@ async fn get_status(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
status_id: web::Path<Uuid>,
|
status_id: web::Path<Uuid>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let maybe_current_user = match auth {
|
let maybe_current_user = match auth {
|
||||||
Some(auth) => Some(get_current_user(db_client, auth.token()).await?),
|
Some(auth) => Some(get_current_user(db_client, auth.token()).await?),
|
||||||
|
@ -257,7 +260,7 @@ async fn get_status(
|
||||||
};
|
};
|
||||||
let post = get_post_by_id(db_client, &status_id).await?;
|
let post = get_post_by_id(db_client, &status_id).await?;
|
||||||
if !can_view_post(db_client, maybe_current_user.as_ref(), &post).await? {
|
if !can_view_post(db_client, maybe_current_user.as_ref(), &post).await? {
|
||||||
return Err(HttpError::NotFoundError("post"));
|
return Err(MastodonError::NotFoundError("post"));
|
||||||
};
|
};
|
||||||
let status = build_status(
|
let status = build_status(
|
||||||
db_client,
|
db_client,
|
||||||
|
@ -275,12 +278,12 @@ async fn delete_status(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
status_id: web::Path<Uuid>,
|
status_id: web::Path<Uuid>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &mut **get_database_client(&db_pool).await?;
|
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let post = get_post_by_id(db_client, &status_id).await?;
|
let post = get_post_by_id(db_client, &status_id).await?;
|
||||||
if post.author.id != current_user.id {
|
if post.author.id != current_user.id {
|
||||||
return Err(HttpError::PermissionError);
|
return Err(MastodonError::PermissionError);
|
||||||
};
|
};
|
||||||
let delete_note = prepare_delete_note(
|
let delete_note = prepare_delete_note(
|
||||||
db_client,
|
db_client,
|
||||||
|
@ -304,7 +307,7 @@ async fn get_context(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
status_id: web::Path<Uuid>,
|
status_id: web::Path<Uuid>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let maybe_current_user = match auth {
|
let maybe_current_user = match auth {
|
||||||
Some(auth) => Some(get_current_user(db_client, auth.token()).await?),
|
Some(auth) => Some(get_current_user(db_client, auth.token()).await?),
|
||||||
|
@ -347,7 +350,7 @@ async fn get_thread_view(
|
||||||
connection_info: ConnectionInfo,
|
connection_info: ConnectionInfo,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
status_id: web::Path<Uuid>,
|
status_id: web::Path<Uuid>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let maybe_current_user = match auth {
|
let maybe_current_user = match auth {
|
||||||
Some(auth) => Some(get_current_user(db_client, auth.token()).await?),
|
Some(auth) => Some(get_current_user(db_client, auth.token()).await?),
|
||||||
|
@ -375,12 +378,12 @@ async fn favourite(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
status_id: web::Path<Uuid>,
|
status_id: web::Path<Uuid>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &mut **get_database_client(&db_pool).await?;
|
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let mut post = get_post_by_id(db_client, &status_id).await?;
|
let mut post = get_post_by_id(db_client, &status_id).await?;
|
||||||
if post.repost_of_id.is_some() {
|
if post.repost_of_id.is_some() {
|
||||||
return Err(HttpError::NotFoundError("post"));
|
return Err(MastodonError::NotFoundError("post"));
|
||||||
};
|
};
|
||||||
let maybe_reaction_created = match create_reaction(
|
let maybe_reaction_created = match create_reaction(
|
||||||
db_client, ¤t_user.id, &status_id, None,
|
db_client, ¤t_user.id, &status_id, None,
|
||||||
|
@ -421,7 +424,7 @@ async fn unfavourite(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
status_id: web::Path<Uuid>,
|
status_id: web::Path<Uuid>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &mut **get_database_client(&db_pool).await?;
|
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let mut post = get_post_by_id(db_client, &status_id).await?;
|
let mut post = get_post_by_id(db_client, &status_id).await?;
|
||||||
|
@ -464,15 +467,15 @@ async fn reblog(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
status_id: web::Path<Uuid>,
|
status_id: web::Path<Uuid>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &mut **get_database_client(&db_pool).await?;
|
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
if !can_create_post(¤t_user) {
|
if !can_create_post(¤t_user) {
|
||||||
return Err(HttpError::PermissionError);
|
return Err(MastodonError::PermissionError);
|
||||||
};
|
};
|
||||||
let mut post = get_post_by_id(db_client, &status_id).await?;
|
let mut post = get_post_by_id(db_client, &status_id).await?;
|
||||||
if !post.is_public() || post.repost_of_id.is_some() {
|
if !post.is_public() || post.repost_of_id.is_some() {
|
||||||
return Err(HttpError::NotFoundError("post"));
|
return Err(MastodonError::NotFoundError("post"));
|
||||||
};
|
};
|
||||||
let repost_data = PostCreateData::repost(status_id.into_inner(), None);
|
let repost_data = PostCreateData::repost(status_id.into_inner(), None);
|
||||||
let mut repost = create_post(db_client, ¤t_user.id, repost_data).await?;
|
let mut repost = create_post(db_client, ¤t_user.id, repost_data).await?;
|
||||||
|
@ -504,7 +507,7 @@ async fn unreblog(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
status_id: web::Path<Uuid>,
|
status_id: web::Path<Uuid>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &mut **get_database_client(&db_pool).await?;
|
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let reposts = find_reposts_by_user(
|
let reposts = find_reposts_by_user(
|
||||||
|
@ -512,7 +515,7 @@ async fn unreblog(
|
||||||
¤t_user.id,
|
¤t_user.id,
|
||||||
&[*status_id],
|
&[*status_id],
|
||||||
).await?;
|
).await?;
|
||||||
let repost_id = reposts.first().ok_or(HttpError::NotFoundError("post"))?;
|
let repost_id = reposts.first().ok_or(MastodonError::NotFoundError("post"))?;
|
||||||
// Ignore returned data because reposts don't have attached files
|
// Ignore returned data because reposts don't have attached files
|
||||||
delete_post(db_client, repost_id).await?;
|
delete_post(db_client, repost_id).await?;
|
||||||
let post = get_post_by_id(db_client, &status_id).await?;
|
let post = get_post_by_id(db_client, &status_id).await?;
|
||||||
|
@ -543,28 +546,28 @@ async fn make_permanent(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
status_id: web::Path<Uuid>,
|
status_id: web::Path<Uuid>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &mut **get_database_client(&db_pool).await?;
|
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let mut post = get_post_by_id(db_client, &status_id).await?;
|
let mut post = get_post_by_id(db_client, &status_id).await?;
|
||||||
if post.ipfs_cid.is_some() {
|
if post.ipfs_cid.is_some() {
|
||||||
return Err(HttpError::OperationError("post already saved to IPFS"));
|
return Err(MastodonError::OperationError("post already saved to IPFS"));
|
||||||
};
|
};
|
||||||
if post.author.id != current_user.id || !post.is_public() || post.repost_of_id.is_some() {
|
if post.author.id != current_user.id || !post.is_public() || post.repost_of_id.is_some() {
|
||||||
// Users can only archive their own public posts
|
// Users can only archive their own public posts
|
||||||
return Err(HttpError::PermissionError);
|
return Err(MastodonError::PermissionError);
|
||||||
};
|
};
|
||||||
let ipfs_api_url = config.ipfs_api_url.as_ref()
|
let ipfs_api_url = config.ipfs_api_url.as_ref()
|
||||||
.ok_or(HttpError::NotSupported)?;
|
.ok_or(MastodonError::NotSupported)?;
|
||||||
|
|
||||||
let mut attachments = vec![];
|
let mut attachments = vec![];
|
||||||
for attachment in post.attachments.iter_mut() {
|
for attachment in post.attachments.iter_mut() {
|
||||||
// Add attachment to IPFS
|
// Add attachment to IPFS
|
||||||
let image_path = config.media_dir().join(&attachment.file_name);
|
let image_path = config.media_dir().join(&attachment.file_name);
|
||||||
let image_data = std::fs::read(image_path)
|
let image_data = std::fs::read(image_path)
|
||||||
.map_err(|_| HttpError::InternalError)?;
|
.map_err(|_| MastodonError::InternalError)?;
|
||||||
let image_cid = ipfs_store::add(ipfs_api_url, image_data).await
|
let image_cid = ipfs_store::add(ipfs_api_url, image_data).await
|
||||||
.map_err(|_| HttpError::InternalError)?;
|
.map_err(|_| MastodonError::InternalError)?;
|
||||||
attachment.ipfs_cid = Some(image_cid.clone());
|
attachment.ipfs_cid = Some(image_cid.clone());
|
||||||
attachments.push((attachment.id, image_cid));
|
attachments.push((attachment.id, image_cid));
|
||||||
};
|
};
|
||||||
|
@ -579,10 +582,10 @@ async fn make_permanent(
|
||||||
maybe_post_image_cid,
|
maybe_post_image_cid,
|
||||||
);
|
);
|
||||||
let post_metadata_json = serde_json::to_string(&post_metadata)
|
let post_metadata_json = serde_json::to_string(&post_metadata)
|
||||||
.map_err(|_| HttpError::InternalError)?
|
.map_err(|_| MastodonError::InternalError)?
|
||||||
.as_bytes().to_vec();
|
.as_bytes().to_vec();
|
||||||
let post_metadata_cid = ipfs_store::add(ipfs_api_url, post_metadata_json).await
|
let post_metadata_cid = ipfs_store::add(ipfs_api_url, post_metadata_json).await
|
||||||
.map_err(|_| HttpError::InternalError)?;
|
.map_err(|_| MastodonError::InternalError)?;
|
||||||
|
|
||||||
set_post_ipfs_cid(db_client, &post.id, &post_metadata_cid, attachments).await?;
|
set_post_ipfs_cid(db_client, &post.id, &post_metadata_cid, attachments).await?;
|
||||||
post.ipfs_cid = Some(post_metadata_cid);
|
post.ipfs_cid = Some(post_metadata_cid);
|
||||||
|
@ -603,31 +606,31 @@ async fn get_signature(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
status_id: web::Path<Uuid>,
|
status_id: web::Path<Uuid>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let ethereum_config = config.blockchain()
|
let ethereum_config = config.blockchain()
|
||||||
.ok_or(HttpError::NotSupported)?
|
.ok_or(MastodonError::NotSupported)?
|
||||||
.ethereum_config()
|
.ethereum_config()
|
||||||
.ok_or(HttpError::NotSupported)?;
|
.ok_or(MastodonError::NotSupported)?;
|
||||||
// User must have a public ethereum address
|
// User must have a public ethereum address
|
||||||
let wallet_address = current_user
|
let wallet_address = current_user
|
||||||
.public_wallet_address(&Currency::Ethereum)
|
.public_wallet_address(&Currency::Ethereum)
|
||||||
.ok_or(HttpError::PermissionError)?;
|
.ok_or(MastodonError::PermissionError)?;
|
||||||
let post = get_post_by_id(db_client, &status_id).await?;
|
let post = get_post_by_id(db_client, &status_id).await?;
|
||||||
if post.author.id != current_user.id || !post.is_public() || post.repost_of_id.is_some() {
|
if post.author.id != current_user.id || !post.is_public() || post.repost_of_id.is_some() {
|
||||||
// Users can only tokenize their own public posts
|
// Users can only tokenize their own public posts
|
||||||
return Err(HttpError::PermissionError);
|
return Err(MastodonError::PermissionError);
|
||||||
};
|
};
|
||||||
let ipfs_cid = post.ipfs_cid
|
let ipfs_cid = post.ipfs_cid
|
||||||
// Post metadata is not immutable
|
// Post metadata is not immutable
|
||||||
.ok_or(HttpError::PermissionError)?;
|
.ok_or(MastodonError::PermissionError)?;
|
||||||
let token_uri = get_ipfs_url(&ipfs_cid);
|
let token_uri = get_ipfs_url(&ipfs_cid);
|
||||||
let signature = create_mint_signature(
|
let signature = create_mint_signature(
|
||||||
ethereum_config,
|
ethereum_config,
|
||||||
&wallet_address,
|
&wallet_address,
|
||||||
&token_uri,
|
&token_uri,
|
||||||
).map_err(|_| HttpError::InternalError)?;
|
).map_err(|_| MastodonError::InternalError)?;
|
||||||
Ok(HttpResponse::Ok().json(signature))
|
Ok(HttpResponse::Ok().json(signature))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,15 +642,15 @@ async fn token_minted(
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
status_id: web::Path<Uuid>,
|
status_id: web::Path<Uuid>,
|
||||||
transaction_data: web::Json<TransactionData>,
|
transaction_data: web::Json<TransactionData>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let mut post = get_post_by_id(db_client, &status_id).await?;
|
let mut post = get_post_by_id(db_client, &status_id).await?;
|
||||||
if post.token_tx_id.is_some() {
|
if post.token_tx_id.is_some() {
|
||||||
return Err(HttpError::OperationError("transaction is already registered"));
|
return Err(MastodonError::OperationError("transaction is already registered"));
|
||||||
};
|
};
|
||||||
if post.author.id != current_user.id || !post.is_public() || post.repost_of_id.is_some() {
|
if post.author.id != current_user.id || !post.is_public() || post.repost_of_id.is_some() {
|
||||||
return Err(HttpError::PermissionError);
|
return Err(MastodonError::PermissionError);
|
||||||
};
|
};
|
||||||
let token_tx_id = transaction_data.into_inner().transaction_id;
|
let token_tx_id = transaction_data.into_inner().transaction_id;
|
||||||
set_post_token_tx_id(db_client, &post.id, &token_tx_id).await?;
|
set_post_token_tx_id(db_client, &post.id, &token_tx_id).await?;
|
||||||
|
|
|
@ -14,7 +14,7 @@ use mitra_utils::currencies::Currency;
|
||||||
|
|
||||||
use crate::activitypub::builders::update_person::prepare_update_person;
|
use crate::activitypub::builders::update_person::prepare_update_person;
|
||||||
use crate::database::{get_database_client, DbPool};
|
use crate::database::{get_database_client, DbPool};
|
||||||
use crate::errors::{HttpError, ValidationError};
|
use crate::errors::ValidationError;
|
||||||
use crate::ethereum::{
|
use crate::ethereum::{
|
||||||
contracts::ContractSet,
|
contracts::ContractSet,
|
||||||
subscriptions::{
|
subscriptions::{
|
||||||
|
@ -25,6 +25,7 @@ use crate::ethereum::{
|
||||||
use crate::http::get_request_base_url;
|
use crate::http::get_request_base_url;
|
||||||
use crate::mastodon_api::{
|
use crate::mastodon_api::{
|
||||||
accounts::types::Account,
|
accounts::types::Account,
|
||||||
|
errors::MastodonError,
|
||||||
oauth::auth::get_current_user,
|
oauth::auth::get_current_user,
|
||||||
};
|
};
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
|
@ -62,24 +63,24 @@ pub async fn authorize_subscription(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
query_params: web::Query<SubscriptionAuthorizationQueryParams>,
|
query_params: web::Query<SubscriptionAuthorizationQueryParams>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let ethereum_config = config.blockchain()
|
let ethereum_config = config.blockchain()
|
||||||
.ok_or(HttpError::NotSupported)?
|
.ok_or(MastodonError::NotSupported)?
|
||||||
.ethereum_config()
|
.ethereum_config()
|
||||||
.ok_or(HttpError::NotSupported)?;
|
.ok_or(MastodonError::NotSupported)?;
|
||||||
// The user must have a public ethereum address,
|
// The user must have a public ethereum address,
|
||||||
// because subscribers should be able
|
// because subscribers should be able
|
||||||
// to verify that payments are actually sent to the recipient.
|
// to verify that payments are actually sent to the recipient.
|
||||||
let wallet_address = current_user
|
let wallet_address = current_user
|
||||||
.public_wallet_address(&Currency::Ethereum)
|
.public_wallet_address(&Currency::Ethereum)
|
||||||
.ok_or(HttpError::PermissionError)?;
|
.ok_or(MastodonError::PermissionError)?;
|
||||||
let signature = create_subscription_signature(
|
let signature = create_subscription_signature(
|
||||||
ethereum_config,
|
ethereum_config,
|
||||||
&wallet_address,
|
&wallet_address,
|
||||||
query_params.price,
|
query_params.price,
|
||||||
).map_err(|_| HttpError::InternalError)?;
|
).map_err(|_| MastodonError::InternalError)?;
|
||||||
Ok(HttpResponse::Ok().json(signature))
|
Ok(HttpResponse::Ok().json(signature))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +88,7 @@ pub async fn authorize_subscription(
|
||||||
async fn get_subscription_options(
|
async fn get_subscription_options(
|
||||||
auth: BearerAuth,
|
auth: BearerAuth,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let options: Vec<SubscriptionOption> = current_user.profile
|
let options: Vec<SubscriptionOption> = current_user.profile
|
||||||
|
@ -105,23 +106,23 @@ pub async fn register_subscription_option(
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
maybe_blockchain: web::Data<Option<ContractSet>>,
|
maybe_blockchain: web::Data<Option<ContractSet>>,
|
||||||
subscription_option: web::Json<SubscriptionOption>,
|
subscription_option: web::Json<SubscriptionOption>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let mut current_user = get_current_user(db_client, auth.token()).await?;
|
let mut current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
if !current_user.role.has_permission(Permission::ManageSubscriptionOptions) {
|
if !current_user.role.has_permission(Permission::ManageSubscriptionOptions) {
|
||||||
return Err(HttpError::PermissionError);
|
return Err(MastodonError::PermissionError);
|
||||||
};
|
};
|
||||||
|
|
||||||
let maybe_payment_option = match subscription_option.into_inner() {
|
let maybe_payment_option = match subscription_option.into_inner() {
|
||||||
SubscriptionOption::Ethereum => {
|
SubscriptionOption::Ethereum => {
|
||||||
let ethereum_config = config.blockchain()
|
let ethereum_config = config.blockchain()
|
||||||
.and_then(|conf| conf.ethereum_config())
|
.and_then(|conf| conf.ethereum_config())
|
||||||
.ok_or(HttpError::NotSupported)?;
|
.ok_or(MastodonError::NotSupported)?;
|
||||||
let contract_set = maybe_blockchain.as_ref().as_ref()
|
let contract_set = maybe_blockchain.as_ref().as_ref()
|
||||||
.ok_or(HttpError::NotSupported)?;
|
.ok_or(MastodonError::NotSupported)?;
|
||||||
let wallet_address = current_user
|
let wallet_address = current_user
|
||||||
.public_wallet_address(&Currency::Ethereum)
|
.public_wallet_address(&Currency::Ethereum)
|
||||||
.ok_or(HttpError::PermissionError)?;
|
.ok_or(MastodonError::PermissionError)?;
|
||||||
if current_user.profile.payment_options
|
if current_user.profile.payment_options
|
||||||
.any(PaymentType::EthereumSubscription)
|
.any(PaymentType::EthereumSubscription)
|
||||||
{
|
{
|
||||||
|
@ -131,7 +132,7 @@ pub async fn register_subscription_option(
|
||||||
let is_registered = is_registered_recipient(
|
let is_registered = is_registered_recipient(
|
||||||
contract_set,
|
contract_set,
|
||||||
&wallet_address,
|
&wallet_address,
|
||||||
).await.map_err(|_| HttpError::InternalError)?;
|
).await.map_err(|_| MastodonError::InternalError)?;
|
||||||
if !is_registered {
|
if !is_registered {
|
||||||
return Err(ValidationError("recipient is not registered").into());
|
return Err(ValidationError("recipient is not registered").into());
|
||||||
};
|
};
|
||||||
|
@ -143,7 +144,7 @@ pub async fn register_subscription_option(
|
||||||
SubscriptionOption::Monero { price, payout_address } => {
|
SubscriptionOption::Monero { price, payout_address } => {
|
||||||
let monero_config = config.blockchain()
|
let monero_config = config.blockchain()
|
||||||
.and_then(|conf| conf.monero_config())
|
.and_then(|conf| conf.monero_config())
|
||||||
.ok_or(HttpError::NotSupported)?;
|
.ok_or(MastodonError::NotSupported)?;
|
||||||
if price == 0 {
|
if price == 0 {
|
||||||
return Err(ValidationError("price must be greater than 0").into());
|
return Err(ValidationError("price must be greater than 0").into());
|
||||||
};
|
};
|
||||||
|
@ -186,7 +187,7 @@ pub async fn register_subscription_option(
|
||||||
async fn find_subscription(
|
async fn find_subscription(
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
query_params: web::Query<SubscriptionQueryParams>,
|
query_params: web::Query<SubscriptionQueryParams>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let subscription = get_subscription_by_participants(
|
let subscription = get_subscription_by_participants(
|
||||||
db_client,
|
db_client,
|
||||||
|
@ -205,11 +206,11 @@ async fn create_invoice_view(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
invoice_data: web::Json<InvoiceData>,
|
invoice_data: web::Json<InvoiceData>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let monero_config = config.blockchain()
|
let monero_config = config.blockchain()
|
||||||
.ok_or(HttpError::NotSupported)?
|
.ok_or(MastodonError::NotSupported)?
|
||||||
.monero_config()
|
.monero_config()
|
||||||
.ok_or(HttpError::NotSupported)?;
|
.ok_or(MastodonError::NotSupported)?;
|
||||||
if invoice_data.sender_id == invoice_data.recipient_id {
|
if invoice_data.sender_id == invoice_data.recipient_id {
|
||||||
return Err(ValidationError("sender must be different from recipient").into());
|
return Err(ValidationError("sender must be different from recipient").into());
|
||||||
};
|
};
|
||||||
|
@ -225,7 +226,7 @@ async fn create_invoice_view(
|
||||||
};
|
};
|
||||||
|
|
||||||
let payment_address = create_monero_address(monero_config).await
|
let payment_address = create_monero_address(monero_config).await
|
||||||
.map_err(|_| HttpError::InternalError)?
|
.map_err(|_| MastodonError::InternalError)?
|
||||||
.to_string();
|
.to_string();
|
||||||
let db_invoice = create_invoice(
|
let db_invoice = create_invoice(
|
||||||
db_client,
|
db_client,
|
||||||
|
@ -243,7 +244,7 @@ async fn create_invoice_view(
|
||||||
async fn get_invoice(
|
async fn get_invoice(
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
invoice_id: web::Path<Uuid>,
|
invoice_id: web::Path<Uuid>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let db_invoice = get_invoice_by_id(db_client, &invoice_id).await?;
|
let db_invoice = get_invoice_by_id(db_client, &invoice_id).await?;
|
||||||
let invoice = Invoice::from(db_invoice);
|
let invoice = Invoice::from(db_invoice);
|
||||||
|
|
|
@ -11,9 +11,9 @@ use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||||
use mitra_config::Config;
|
use mitra_config::Config;
|
||||||
|
|
||||||
use crate::database::{get_database_client, DbPool};
|
use crate::database::{get_database_client, DbPool};
|
||||||
use crate::errors::HttpError;
|
|
||||||
use crate::http::get_request_base_url;
|
use crate::http::get_request_base_url;
|
||||||
use crate::mastodon_api::{
|
use crate::mastodon_api::{
|
||||||
|
errors::MastodonError,
|
||||||
oauth::auth::get_current_user,
|
oauth::auth::get_current_user,
|
||||||
statuses::helpers::build_status_list,
|
statuses::helpers::build_status_list,
|
||||||
};
|
};
|
||||||
|
@ -31,7 +31,7 @@ async fn home_timeline(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
query_params: web::Query<TimelineQueryParams>,
|
query_params: web::Query<TimelineQueryParams>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let posts = get_home_timeline(
|
let posts = get_home_timeline(
|
||||||
|
@ -58,7 +58,7 @@ async fn public_timeline(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
query_params: web::Query<TimelineQueryParams>,
|
query_params: web::Query<TimelineQueryParams>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let posts = get_local_timeline(
|
let posts = get_local_timeline(
|
||||||
|
@ -85,7 +85,7 @@ async fn hashtag_timeline(
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
hashtag: web::Path<String>,
|
hashtag: web::Path<String>,
|
||||||
query_params: web::Query<TimelineQueryParams>,
|
query_params: web::Query<TimelineQueryParams>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, MastodonError> {
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let maybe_current_user = match auth {
|
let maybe_current_user = match auth {
|
||||||
Some(auth) => Some(get_current_user(db_client, auth.token()).await?),
|
Some(auth) => Some(get_current_user(db_client, auth.token()).await?),
|
||||||
|
|
|
@ -2,8 +2,8 @@ use std::path::Path;
|
||||||
|
|
||||||
use mitra_utils::files::sniff_media_type;
|
use mitra_utils::files::sniff_media_type;
|
||||||
|
|
||||||
use crate::errors::HttpError;
|
|
||||||
use crate::media::{save_file, SUPPORTED_MEDIA_TYPES};
|
use crate::media::{save_file, SUPPORTED_MEDIA_TYPES};
|
||||||
|
use super::errors::MastodonError;
|
||||||
|
|
||||||
pub const UPLOAD_MAX_SIZE: usize = 1024 * 1024 * 5;
|
pub const UPLOAD_MAX_SIZE: usize = 1024 * 1024 * 5;
|
||||||
|
|
||||||
|
@ -22,12 +22,12 @@ pub enum UploadError {
|
||||||
InvalidMediaType,
|
InvalidMediaType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<UploadError> for HttpError {
|
impl From<UploadError> for MastodonError {
|
||||||
fn from(error: UploadError) -> Self {
|
fn from(error: UploadError) -> Self {
|
||||||
match error {
|
match error {
|
||||||
UploadError::WriteError(_) => HttpError::InternalError,
|
UploadError::WriteError(_) => MastodonError::InternalError,
|
||||||
other_error => {
|
other_error => {
|
||||||
HttpError::ValidationError(other_error.to_string())
|
MastodonError::ValidationError(other_error.to_string())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue