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'
|
||||
400:
|
||||
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:
|
||||
get:
|
||||
summary: Find out whether a given actor is followed, blocked, muted, etc.
|
||||
|
@ -660,6 +623,43 @@ paths:
|
|||
description: Post not found
|
||||
422:
|
||||
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:
|
||||
get:
|
||||
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::search::views::search_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::UPLOAD_MAX_SIZE;
|
||||
use mitra::nodeinfo::views as nodeinfo;
|
||||
|
@ -135,8 +136,9 @@ async fn main() -> std::io::Result<()> {
|
|||
.service(marker_api_scope())
|
||||
.service(media_api_scope())
|
||||
.service(notification_api_scope())
|
||||
.service(status_api_scope())
|
||||
.service(search_api_scope())
|
||||
.service(status_api_scope())
|
||||
.service(subscription_api_scope())
|
||||
.service(timeline_api_scope())
|
||||
.service(webfinger::get_descriptor)
|
||||
.service(activitypub::actor_scope())
|
||||
|
|
|
@ -251,11 +251,6 @@ pub struct IdentityProofData {
|
|||
pub signature: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SubscriptionQueryParams {
|
||||
pub price: u64,
|
||||
}
|
||||
|
||||
// TODO: actix currently doesn't support parameter arrays
|
||||
// https://github.com/actix/actix-web/issues/2044
|
||||
#[derive(Deserialize)]
|
||||
|
|
|
@ -22,14 +22,14 @@ use crate::ethereum::identity::{
|
|||
create_identity_claim,
|
||||
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::pagination::get_paginated_response;
|
||||
use crate::mastodon_api::statuses::helpers::build_status_list;
|
||||
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::profiles::queries::{
|
||||
get_profile_by_id,
|
||||
|
@ -38,7 +38,6 @@ use crate::models::profiles::queries::{
|
|||
};
|
||||
use crate::models::profiles::types::{
|
||||
IdentityProof,
|
||||
PaymentOption,
|
||||
ProfileUpdateData,
|
||||
};
|
||||
use crate::models::relationships::queries::{
|
||||
|
@ -77,7 +76,6 @@ use super::types::{
|
|||
RelationshipQueryParams,
|
||||
SearchDidQueryParams,
|
||||
StatusListQueryParams,
|
||||
SubscriptionQueryParams,
|
||||
ApiSubscription,
|
||||
};
|
||||
|
||||
|
@ -295,72 +293,6 @@ async fn create_identity_proof(
|
|||
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")]
|
||||
async fn get_relationships_view(
|
||||
auth: BearerAuth,
|
||||
|
@ -640,10 +572,10 @@ pub fn account_api_scope() -> Scope {
|
|||
.service(update_credentials)
|
||||
.service(get_identity_claim)
|
||||
.service(create_identity_proof)
|
||||
.service(authorize_subscription)
|
||||
.service(subscriptions_enabled)
|
||||
.service(get_relationships_view)
|
||||
.service(search_by_did)
|
||||
.route("/authorize_subscription", web::get().to(authorize_subscription))
|
||||
.route("/subscriptions_enabled", web::post().to(subscriptions_enabled))
|
||||
// Routes with account ID
|
||||
.service(get_account)
|
||||
.service(follow_account)
|
||||
|
|
|
@ -8,6 +8,7 @@ pub mod oauth;
|
|||
mod pagination;
|
||||
pub mod search;
|
||||
pub mod statuses;
|
||||
pub mod subscriptions;
|
||||
pub mod timelines;
|
||||
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