Allow calling follow/unfollow API methods multiple times

For compatibility with Mastodon.
This commit is contained in:
silverpill 2022-01-03 18:13:16 +00:00
parent d46165f397
commit 6d331f7669
3 changed files with 97 additions and 27 deletions

View file

@ -204,6 +204,38 @@ paths:
$ref: '#/components/schemas/Account'
404:
description: Profile not found
/api/v1/accounts/{account_id}/follow:
post:
summary: Follow the given actor.
security:
- tokenAuth: []
parameters:
- $ref: '#/components/parameters/account_id'
responses:
200:
description: Successfully followed, or actor was already followed
content:
application/json:
schema:
$ref: '#/components/schemas/Relationship'
404:
description: Profile not found
/api/v1/accounts/{account_id}/unfollow:
post:
summary: Unfollow the given actor.
security:
- tokenAuth: []
parameters:
- $ref: '#/components/parameters/account_id'
responses:
200:
description: Successfully unfollowed, or actor was already not followed
content:
application/json:
schema:
$ref: '#/components/schemas/Relationship'
404:
description: Profile not found
/api/v1/statuses/{status_id}:
delete:
summary: Delete post
@ -330,6 +362,10 @@ paths:
$ref: '#/components/schemas/Status'
components:
securitySchemes:
tokenAuth:
type: http
scheme: bearer
parameters:
account_id:
name: account_id
@ -367,6 +403,22 @@ components:
description: Ethereum wallet address.
type: string
example: '0xd8da6bf...'
Relationship:
type: object
properties:
id:
description: The account id.
type: string
format: uuid
following:
description: Are you following this user?
type: boolean
followed_by:
description: Are you followed by this user?
type: boolean
requested:
description: Do you have a pending follow request for this user?
type: boolean
Status:
type: object
properties:

View file

@ -12,7 +12,7 @@ use crate::activitypub::actor::Actor;
use crate::activitypub::deliverer::deliver_activity;
use crate::config::Config;
use crate::database::{Pool, get_database_client};
use crate::errors::{HttpError, ValidationError};
use crate::errors::{DatabaseError, HttpError, ValidationError};
use crate::ethereum::gate::is_allowed_user;
use crate::mastodon_api::oauth::auth::get_current_user;
use crate::models::posts::helpers::{
@ -209,16 +209,25 @@ async fn follow_account(
let target = get_profile_by_id(db_client, &account_id).await?;
if let Some(remote_actor) = target.actor_json {
// Remote follow
let request = create_follow_request(db_client, &current_user.id, &target.id).await?;
let activity = create_activity_follow(
&config.instance_url(),
&current_user.profile,
&request.id,
&remote_actor.id,
);
deliver_activity(&config, &current_user, activity, vec![remote_actor]);
match create_follow_request(db_client, &current_user.id, &target.id).await {
Ok(request) => {
let activity = create_activity_follow(
&config.instance_url(),
&current_user.profile,
&request.id,
&remote_actor.id,
);
deliver_activity(&config, &current_user, activity, vec![remote_actor]);
},
Err(DatabaseError::AlreadyExists(_)) => (), // already following
Err(other_error) => return Err(other_error.into()),
};
} else {
follow(db_client, &current_user.id, &target.id).await?;
match follow(db_client, &current_user.id, &target.id).await {
Ok(_) => (),
Err(DatabaseError::AlreadyExists(_)) => (), // already following
Err(other_error) => return Err(other_error.into()),
};
};
let relationship = get_relationship(
db_client,
@ -240,26 +249,35 @@ async fn unfollow_account(
let target = get_profile_by_id(db_client, &account_id).await?;
if let Some(remote_actor) = target.actor_json {
// Remote follow
let follow_request = get_follow_request_by_path(
match get_follow_request_by_path(
db_client,
&current_user.id,
&target.id,
).await?;
unfollow(
db_client,
&current_user.id,
&target.id,
).await?;
// Federate
let activity = create_activity_undo_follow(
&config.instance_url(),
&current_user.profile,
&follow_request.id,
&remote_actor.id,
);
deliver_activity(&config, &current_user, activity, vec![remote_actor]);
).await {
Ok(follow_request) => {
unfollow(
db_client,
&current_user.id,
&target.id,
).await?;
// Federate
let activity = create_activity_undo_follow(
&config.instance_url(),
&current_user.profile,
&follow_request.id,
&remote_actor.id,
);
deliver_activity(&config, &current_user, activity, vec![remote_actor]);
},
Err(DatabaseError::NotFound(_)) => (), // not following
Err(other_error) => return Err(other_error.into()),
};
} else {
unfollow(db_client, &current_user.id, &target.id).await?;
match unfollow(db_client, &current_user.id, &target.id).await {
Ok(_) => (),
Err(DatabaseError::NotFound(_)) => (), // not following
Err(other_error) => return Err(other_error.into()),
};
};
let relationship = get_relationship(
db_client,

View file

@ -161,7 +161,7 @@ pub async fn create_follow_request(
&request.target_id,
&request.request_status,
],
).await?;
).await.map_err(catch_unique_violation("follow request"))?;
Ok(request)
}