Use /api/v1/subscriptions prefix for subscription API methods
This commit is contained in:
parent
742e731b95
commit
daaa0855a6
8 changed files with 142 additions and 117 deletions
|
@ -148,43 +148,6 @@ paths:
|
||||||
$ref: '#/components/schemas/Account'
|
$ref: '#/components/schemas/Account'
|
||||||
400:
|
400:
|
||||||
description: Invalid proof data.
|
description: Invalid proof data.
|
||||||
/api/v1/accounts/authorize_subscription:
|
|
||||||
get:
|
|
||||||
summary: Get authorization for setting up paid subscription.
|
|
||||||
parameters:
|
|
||||||
- name: price
|
|
||||||
in: query
|
|
||||||
description: Subscription price
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: number
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: Signature created
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/Signature'
|
|
||||||
403:
|
|
||||||
description: User's wallet address is not known or not verified
|
|
||||||
418:
|
|
||||||
description: Blockchain integration is not enabled
|
|
||||||
/api/v1/accounts/subscriptions_enabled:
|
|
||||||
post:
|
|
||||||
summary: Notify server about subscription setup
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: Successful operation
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/Account'
|
|
||||||
400:
|
|
||||||
description: User hasn't enabled subscriptions
|
|
||||||
403:
|
|
||||||
description: User's wallet address is not known or not verified
|
|
||||||
418:
|
|
||||||
description: Blockchain integration is not enabled
|
|
||||||
/api/v1/accounts/relationships:
|
/api/v1/accounts/relationships:
|
||||||
get:
|
get:
|
||||||
summary: Find out whether a given actor is followed, blocked, muted, etc.
|
summary: Find out whether a given actor is followed, blocked, muted, etc.
|
||||||
|
@ -660,6 +623,43 @@ paths:
|
||||||
description: Post not found
|
description: Post not found
|
||||||
422:
|
422:
|
||||||
description: Transaction already registered
|
description: Transaction already registered
|
||||||
|
/api/v1/subscriptions/authorize:
|
||||||
|
get:
|
||||||
|
summary: Get authorization for setting up Ethereum subscription.
|
||||||
|
parameters:
|
||||||
|
- name: price
|
||||||
|
in: query
|
||||||
|
description: Subscription price
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: number
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Signature created
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Signature'
|
||||||
|
403:
|
||||||
|
description: User's wallet address is not known or not verified
|
||||||
|
418:
|
||||||
|
description: Blockchain integration is not enabled
|
||||||
|
/api/v1/subscriptions/enable:
|
||||||
|
post:
|
||||||
|
summary: Enable subscriptions
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Successful operation
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Account'
|
||||||
|
400:
|
||||||
|
description: User hasn't enabled subscriptions
|
||||||
|
403:
|
||||||
|
description: User's wallet address is not known or not verified
|
||||||
|
418:
|
||||||
|
description: Blockchain integration is not enabled
|
||||||
/api/v1/timelines/public:
|
/api/v1/timelines/public:
|
||||||
get:
|
get:
|
||||||
summary: View local public posts.
|
summary: View local public posts.
|
||||||
|
|
|
@ -24,6 +24,7 @@ use mitra::mastodon_api::oauth::auth::create_auth_error_handler;
|
||||||
use mitra::mastodon_api::oauth::views::oauth_api_scope;
|
use mitra::mastodon_api::oauth::views::oauth_api_scope;
|
||||||
use mitra::mastodon_api::search::views::search_api_scope;
|
use mitra::mastodon_api::search::views::search_api_scope;
|
||||||
use mitra::mastodon_api::statuses::views::status_api_scope;
|
use mitra::mastodon_api::statuses::views::status_api_scope;
|
||||||
|
use mitra::mastodon_api::subscriptions::views::subscription_api_scope;
|
||||||
use mitra::mastodon_api::timelines::views::timeline_api_scope;
|
use mitra::mastodon_api::timelines::views::timeline_api_scope;
|
||||||
use mitra::mastodon_api::UPLOAD_MAX_SIZE;
|
use mitra::mastodon_api::UPLOAD_MAX_SIZE;
|
||||||
use mitra::nodeinfo::views as nodeinfo;
|
use mitra::nodeinfo::views as nodeinfo;
|
||||||
|
@ -135,8 +136,9 @@ async fn main() -> std::io::Result<()> {
|
||||||
.service(marker_api_scope())
|
.service(marker_api_scope())
|
||||||
.service(media_api_scope())
|
.service(media_api_scope())
|
||||||
.service(notification_api_scope())
|
.service(notification_api_scope())
|
||||||
.service(status_api_scope())
|
|
||||||
.service(search_api_scope())
|
.service(search_api_scope())
|
||||||
|
.service(status_api_scope())
|
||||||
|
.service(subscription_api_scope())
|
||||||
.service(timeline_api_scope())
|
.service(timeline_api_scope())
|
||||||
.service(webfinger::get_descriptor)
|
.service(webfinger::get_descriptor)
|
||||||
.service(activitypub::actor_scope())
|
.service(activitypub::actor_scope())
|
||||||
|
|
|
@ -251,11 +251,6 @@ pub struct IdentityProofData {
|
||||||
pub signature: String,
|
pub signature: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct SubscriptionQueryParams {
|
|
||||||
pub price: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: actix currently doesn't support parameter arrays
|
// TODO: actix currently doesn't support parameter arrays
|
||||||
// https://github.com/actix/actix-web/issues/2044
|
// https://github.com/actix/actix-web/issues/2044
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
|
|
@ -22,14 +22,14 @@ use crate::ethereum::identity::{
|
||||||
create_identity_claim,
|
create_identity_claim,
|
||||||
verify_identity_proof,
|
verify_identity_proof,
|
||||||
};
|
};
|
||||||
use crate::ethereum::subscriptions::{
|
|
||||||
create_subscription_signature,
|
|
||||||
is_registered_recipient,
|
|
||||||
};
|
|
||||||
use crate::mastodon_api::oauth::auth::get_current_user;
|
use crate::mastodon_api::oauth::auth::get_current_user;
|
||||||
use crate::mastodon_api::pagination::get_paginated_response;
|
use crate::mastodon_api::pagination::get_paginated_response;
|
||||||
use crate::mastodon_api::statuses::helpers::build_status_list;
|
use crate::mastodon_api::statuses::helpers::build_status_list;
|
||||||
use crate::mastodon_api::statuses::types::Status;
|
use crate::mastodon_api::statuses::types::Status;
|
||||||
|
use crate::mastodon_api::subscriptions::views::{
|
||||||
|
authorize_subscription,
|
||||||
|
subscriptions_enabled,
|
||||||
|
};
|
||||||
use crate::models::posts::queries::get_posts_by_author;
|
use crate::models::posts::queries::get_posts_by_author;
|
||||||
use crate::models::profiles::queries::{
|
use crate::models::profiles::queries::{
|
||||||
get_profile_by_id,
|
get_profile_by_id,
|
||||||
|
@ -38,7 +38,6 @@ use crate::models::profiles::queries::{
|
||||||
};
|
};
|
||||||
use crate::models::profiles::types::{
|
use crate::models::profiles::types::{
|
||||||
IdentityProof,
|
IdentityProof,
|
||||||
PaymentOption,
|
|
||||||
ProfileUpdateData,
|
ProfileUpdateData,
|
||||||
};
|
};
|
||||||
use crate::models::relationships::queries::{
|
use crate::models::relationships::queries::{
|
||||||
|
@ -77,7 +76,6 @@ use super::types::{
|
||||||
RelationshipQueryParams,
|
RelationshipQueryParams,
|
||||||
SearchDidQueryParams,
|
SearchDidQueryParams,
|
||||||
StatusListQueryParams,
|
StatusListQueryParams,
|
||||||
SubscriptionQueryParams,
|
|
||||||
ApiSubscription,
|
ApiSubscription,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -295,72 +293,6 @@ async fn create_identity_proof(
|
||||||
Ok(HttpResponse::Ok().json(account))
|
Ok(HttpResponse::Ok().json(account))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/authorize_subscription")]
|
|
||||||
async fn authorize_subscription(
|
|
||||||
auth: BearerAuth,
|
|
||||||
config: web::Data<Config>,
|
|
||||||
db_pool: web::Data<Pool>,
|
|
||||||
query_params: web::Query<SubscriptionQueryParams>,
|
|
||||||
) -> Result<HttpResponse, HttpError> {
|
|
||||||
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.as_ref()
|
|
||||||
.ok_or(HttpError::NotSupported)?
|
|
||||||
.ethereum_config()
|
|
||||||
.ok_or(HttpError::NotSupported)?;
|
|
||||||
// The user must have a public wallet address,
|
|
||||||
// because subscribers should be able
|
|
||||||
// to verify that payments are actually sent to the recipient.
|
|
||||||
let wallet_address = current_user
|
|
||||||
.public_wallet_address(&config.default_currency())
|
|
||||||
.ok_or(HttpError::PermissionError)?;
|
|
||||||
let signature = create_subscription_signature(
|
|
||||||
ethereum_config,
|
|
||||||
&wallet_address,
|
|
||||||
query_params.price,
|
|
||||||
).map_err(|_| HttpError::InternalError)?;
|
|
||||||
Ok(HttpResponse::Ok().json(signature))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/subscriptions_enabled")]
|
|
||||||
async fn subscriptions_enabled(
|
|
||||||
auth: BearerAuth,
|
|
||||||
config: web::Data<Config>,
|
|
||||||
db_pool: web::Data<Pool>,
|
|
||||||
maybe_blockchain: web::Data<Option<ContractSet>>,
|
|
||||||
) -> Result<HttpResponse, HttpError> {
|
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
|
||||||
let mut current_user = get_current_user(db_client, auth.token()).await?;
|
|
||||||
let contract_set = maybe_blockchain.as_ref().as_ref()
|
|
||||||
.ok_or(HttpError::NotSupported)?;
|
|
||||||
let wallet_address = current_user
|
|
||||||
.public_wallet_address(&config.default_currency())
|
|
||||||
.ok_or(HttpError::PermissionError)?;
|
|
||||||
let is_registered = is_registered_recipient(contract_set, &wallet_address)
|
|
||||||
.await.map_err(|_| HttpError::InternalError)?;
|
|
||||||
if !is_registered {
|
|
||||||
return Err(ValidationError("recipient is not registered").into());
|
|
||||||
};
|
|
||||||
|
|
||||||
if current_user.profile.payment_options.is_empty() {
|
|
||||||
// Add payment option to profile
|
|
||||||
let mut profile_data = ProfileUpdateData::from(¤t_user.profile);
|
|
||||||
profile_data.payment_options = vec![PaymentOption::EthereumSubscription];
|
|
||||||
current_user.profile = update_profile(
|
|
||||||
db_client,
|
|
||||||
¤t_user.id,
|
|
||||||
profile_data,
|
|
||||||
).await?;
|
|
||||||
|
|
||||||
// Federate
|
|
||||||
prepare_update_person(db_client, config.instance(), ¤t_user)
|
|
||||||
.await?.spawn_deliver();
|
|
||||||
};
|
|
||||||
|
|
||||||
let account = Account::from_user(current_user, &config.instance_url());
|
|
||||||
Ok(HttpResponse::Ok().json(account))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/relationships")]
|
#[get("/relationships")]
|
||||||
async fn get_relationships_view(
|
async fn get_relationships_view(
|
||||||
auth: BearerAuth,
|
auth: BearerAuth,
|
||||||
|
@ -640,10 +572,10 @@ pub fn account_api_scope() -> Scope {
|
||||||
.service(update_credentials)
|
.service(update_credentials)
|
||||||
.service(get_identity_claim)
|
.service(get_identity_claim)
|
||||||
.service(create_identity_proof)
|
.service(create_identity_proof)
|
||||||
.service(authorize_subscription)
|
|
||||||
.service(subscriptions_enabled)
|
|
||||||
.service(get_relationships_view)
|
.service(get_relationships_view)
|
||||||
.service(search_by_did)
|
.service(search_by_did)
|
||||||
|
.route("/authorize_subscription", web::get().to(authorize_subscription))
|
||||||
|
.route("/subscriptions_enabled", web::post().to(subscriptions_enabled))
|
||||||
// Routes with account ID
|
// Routes with account ID
|
||||||
.service(get_account)
|
.service(get_account)
|
||||||
.service(follow_account)
|
.service(follow_account)
|
||||||
|
|
|
@ -8,6 +8,7 @@ pub mod oauth;
|
||||||
mod pagination;
|
mod pagination;
|
||||||
pub mod search;
|
pub mod search;
|
||||||
pub mod statuses;
|
pub mod statuses;
|
||||||
|
pub mod subscriptions;
|
||||||
pub mod timelines;
|
pub mod timelines;
|
||||||
mod uploads;
|
mod uploads;
|
||||||
|
|
||||||
|
|
2
src/mastodon_api/subscriptions/mod.rs
Normal file
2
src/mastodon_api/subscriptions/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
mod types;
|
||||||
|
pub mod views;
|
6
src/mastodon_api/subscriptions/types.rs
Normal file
6
src/mastodon_api/subscriptions/types.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct SubscriptionQueryParams {
|
||||||
|
pub price: u64,
|
||||||
|
}
|
87
src/mastodon_api/subscriptions/views.rs
Normal file
87
src/mastodon_api/subscriptions/views.rs
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
use actix_web::{web, HttpResponse, Scope};
|
||||||
|
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||||
|
|
||||||
|
use crate::activitypub::builders::update_person::prepare_update_person;
|
||||||
|
use crate::config::Config;
|
||||||
|
use crate::database::{Pool, get_database_client};
|
||||||
|
use crate::errors::{HttpError, ValidationError};
|
||||||
|
use crate::ethereum::contracts::ContractSet;
|
||||||
|
use crate::ethereum::subscriptions::{
|
||||||
|
create_subscription_signature,
|
||||||
|
is_registered_recipient,
|
||||||
|
};
|
||||||
|
use crate::mastodon_api::accounts::types::Account;
|
||||||
|
use crate::mastodon_api::oauth::auth::get_current_user;
|
||||||
|
use crate::models::profiles::queries::update_profile;
|
||||||
|
use crate::models::profiles::types::{PaymentOption, ProfileUpdateData};
|
||||||
|
use super::types::SubscriptionQueryParams;
|
||||||
|
|
||||||
|
pub async fn authorize_subscription(
|
||||||
|
auth: BearerAuth,
|
||||||
|
config: web::Data<Config>,
|
||||||
|
db_pool: web::Data<Pool>,
|
||||||
|
query_params: web::Query<SubscriptionQueryParams>,
|
||||||
|
) -> Result<HttpResponse, HttpError> {
|
||||||
|
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.as_ref()
|
||||||
|
.ok_or(HttpError::NotSupported)?
|
||||||
|
.ethereum_config()
|
||||||
|
.ok_or(HttpError::NotSupported)?;
|
||||||
|
// The user must have a public wallet address,
|
||||||
|
// because subscribers should be able
|
||||||
|
// to verify that payments are actually sent to the recipient.
|
||||||
|
let wallet_address = current_user
|
||||||
|
.public_wallet_address(&config.default_currency())
|
||||||
|
.ok_or(HttpError::PermissionError)?;
|
||||||
|
let signature = create_subscription_signature(
|
||||||
|
ethereum_config,
|
||||||
|
&wallet_address,
|
||||||
|
query_params.price,
|
||||||
|
).map_err(|_| HttpError::InternalError)?;
|
||||||
|
Ok(HttpResponse::Ok().json(signature))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn subscriptions_enabled(
|
||||||
|
auth: BearerAuth,
|
||||||
|
config: web::Data<Config>,
|
||||||
|
db_pool: web::Data<Pool>,
|
||||||
|
maybe_blockchain: web::Data<Option<ContractSet>>,
|
||||||
|
) -> Result<HttpResponse, HttpError> {
|
||||||
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
|
let mut current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
|
let contract_set = maybe_blockchain.as_ref().as_ref()
|
||||||
|
.ok_or(HttpError::NotSupported)?;
|
||||||
|
let wallet_address = current_user
|
||||||
|
.public_wallet_address(&config.default_currency())
|
||||||
|
.ok_or(HttpError::PermissionError)?;
|
||||||
|
let is_registered = is_registered_recipient(contract_set, &wallet_address)
|
||||||
|
.await.map_err(|_| HttpError::InternalError)?;
|
||||||
|
if !is_registered {
|
||||||
|
return Err(ValidationError("recipient is not registered").into());
|
||||||
|
};
|
||||||
|
|
||||||
|
if current_user.profile.payment_options.is_empty() {
|
||||||
|
// Add payment option to profile
|
||||||
|
let mut profile_data = ProfileUpdateData::from(¤t_user.profile);
|
||||||
|
profile_data.payment_options = vec![PaymentOption::EthereumSubscription];
|
||||||
|
current_user.profile = update_profile(
|
||||||
|
db_client,
|
||||||
|
¤t_user.id,
|
||||||
|
profile_data,
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
// Federate
|
||||||
|
prepare_update_person(db_client, config.instance(), ¤t_user)
|
||||||
|
.await?.spawn_deliver();
|
||||||
|
};
|
||||||
|
|
||||||
|
let account = Account::from_user(current_user, &config.instance_url());
|
||||||
|
Ok(HttpResponse::Ok().json(account))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subscription_api_scope() -> Scope {
|
||||||
|
web::scope("/api/v1/subscriptions")
|
||||||
|
.route("/authorize", web::get().to(authorize_subscription))
|
||||||
|
.route("/enable", web::post().to(subscriptions_enabled))
|
||||||
|
}
|
Loading…
Reference in a new issue