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