Add /api/v1/settings/import_follows API endpoint
This commit is contained in:
parent
7218864563
commit
56df3d82a0
8 changed files with 155 additions and 34 deletions
|
@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
### Added
|
||||
|
||||
- Added `/api/v1/settings/move_followers` API endpoint (replaces `/api/v1/accounts/move_followers`).
|
||||
- Added `/api/v1/settings/import_follows` API endpoint.
|
||||
|
||||
### Removed
|
||||
|
||||
|
|
|
@ -736,6 +736,25 @@ paths:
|
|||
example: |
|
||||
user1@example.org
|
||||
user2@example.org
|
||||
/api/v1/settings/import_follows:
|
||||
post:
|
||||
summary: Import follows from CSV file.
|
||||
security:
|
||||
- tokenAuth: []
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
follows_csv:
|
||||
description: The list of followers in CSV format.
|
||||
type: string
|
||||
responses:
|
||||
204:
|
||||
description: Successful operation
|
||||
400:
|
||||
description: Invalid data.
|
||||
/api/v1/settings/move_followers:
|
||||
post:
|
||||
summary: Move followers from remote alias.
|
||||
|
|
|
@ -1,11 +1,55 @@
|
|||
use tokio_postgres::GenericClient;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::activitypub::builders::follow::prepare_follow;
|
||||
use crate::config::Instance;
|
||||
use crate::database::DatabaseError;
|
||||
use crate::models::relationships::queries::get_relationships;
|
||||
use crate::models::relationships::types::RelationshipType;
|
||||
use crate::models::{
|
||||
profiles::types::DbActorProfile,
|
||||
relationships::queries::{
|
||||
create_follow_request,
|
||||
follow,
|
||||
get_relationships,
|
||||
},
|
||||
relationships::types::RelationshipType,
|
||||
users::types::User,
|
||||
};
|
||||
use super::types::RelationshipMap;
|
||||
|
||||
pub async fn follow_or_create_request(
|
||||
db_client: &mut impl GenericClient,
|
||||
instance: &Instance,
|
||||
current_user: &User,
|
||||
target_profile: &DbActorProfile,
|
||||
) -> Result<(), DatabaseError> {
|
||||
if let Some(ref remote_actor) = target_profile.actor_json {
|
||||
// Create follow request if target is remote
|
||||
match create_follow_request(
|
||||
db_client,
|
||||
¤t_user.id,
|
||||
&target_profile.id,
|
||||
).await {
|
||||
Ok(follow_request) => {
|
||||
prepare_follow(
|
||||
instance,
|
||||
current_user,
|
||||
remote_actor,
|
||||
&follow_request.id,
|
||||
).enqueue(db_client).await?;
|
||||
},
|
||||
Err(DatabaseError::AlreadyExists(_)) => (), // already following
|
||||
Err(other_error) => return Err(other_error),
|
||||
};
|
||||
} else {
|
||||
match follow(db_client, ¤t_user.id, &target_profile.id).await {
|
||||
Ok(_) => (),
|
||||
Err(DatabaseError::AlreadyExists(_)) => (), // already following
|
||||
Err(other_error) => return Err(other_error),
|
||||
};
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_relationship(
|
||||
db_client: &impl GenericClient,
|
||||
source_id: &Uuid,
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
mod helpers;
|
||||
pub mod helpers;
|
||||
pub mod types;
|
||||
pub mod views;
|
||||
|
|
|
@ -6,7 +6,6 @@ use actix_web_httpauth::extractors::bearer::BearerAuth;
|
|||
use uuid::Uuid;
|
||||
|
||||
use crate::activitypub::builders::{
|
||||
follow::prepare_follow,
|
||||
undo_follow::prepare_undo_follow,
|
||||
update_person::{
|
||||
build_update_person,
|
||||
|
@ -55,8 +54,6 @@ use crate::models::profiles::types::{
|
|||
ProofType,
|
||||
};
|
||||
use crate::models::relationships::queries::{
|
||||
create_follow_request,
|
||||
follow,
|
||||
get_followers_paginated,
|
||||
get_following_paginated,
|
||||
hide_replies,
|
||||
|
@ -83,7 +80,7 @@ use crate::utils::{
|
|||
id::new_uuid,
|
||||
passwords::hash_password,
|
||||
};
|
||||
use super::helpers::get_relationship;
|
||||
use super::helpers::{follow_or_create_request, get_relationship};
|
||||
use super::types::{
|
||||
Account,
|
||||
AccountCreateData,
|
||||
|
@ -505,31 +502,12 @@ async fn follow_account(
|
|||
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?;
|
||||
if let Some(remote_actor) = target.actor_json {
|
||||
// Create follow request if target is remote
|
||||
match create_follow_request(
|
||||
db_client,
|
||||
¤t_user.id,
|
||||
&target.id,
|
||||
).await {
|
||||
Ok(follow_request) => {
|
||||
prepare_follow(
|
||||
&config.instance(),
|
||||
¤t_user,
|
||||
&remote_actor,
|
||||
&follow_request.id,
|
||||
).enqueue(db_client).await?;
|
||||
},
|
||||
Err(DatabaseError::AlreadyExists(_)) => (), // already following
|
||||
Err(other_error) => return Err(other_error.into()),
|
||||
};
|
||||
} else {
|
||||
match follow(db_client, ¤t_user.id, &target.id).await {
|
||||
Ok(_) => (),
|
||||
Err(DatabaseError::AlreadyExists(_)) => (), // already following
|
||||
Err(other_error) => return Err(other_error.into()),
|
||||
};
|
||||
};
|
||||
follow_or_create_request(
|
||||
db_client,
|
||||
&config.instance(),
|
||||
¤t_user,
|
||||
&target,
|
||||
).await?;
|
||||
if follow_data.reblogs {
|
||||
show_reposts(db_client, ¤t_user.id, &target.id).await?;
|
||||
} else {
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
use tokio_postgres::GenericClient;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::database::DatabaseError;
|
||||
use crate::activitypub::{
|
||||
fetcher::helpers::get_or_import_profile_by_actor_address,
|
||||
HandlerError,
|
||||
};
|
||||
use crate::config::Config;
|
||||
use crate::database::{get_database_client, DatabaseError, DbPool};
|
||||
use crate::errors::ValidationError;
|
||||
use crate::mastodon_api::accounts::helpers::follow_or_create_request;
|
||||
use crate::models::{
|
||||
profiles::types::DbActorProfile,
|
||||
posts::mentions::mention_to_address,
|
||||
relationships::queries::{get_followers, get_following},
|
||||
users::types::User,
|
||||
};
|
||||
use crate::webfinger::types::ActorAddress;
|
||||
|
||||
|
@ -58,6 +65,44 @@ pub fn parse_address_list(csv: &str)
|
|||
Ok(addresses)
|
||||
}
|
||||
|
||||
pub async fn import_follows_task(
|
||||
config: &Config,
|
||||
current_user: User,
|
||||
db_pool: &DbPool,
|
||||
address_list: Vec<ActorAddress>,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let db_client = &mut **get_database_client(db_pool).await?;
|
||||
for actor_address in address_list {
|
||||
let profile = match get_or_import_profile_by_actor_address(
|
||||
db_client,
|
||||
&config.instance(),
|
||||
&config.media_dir(),
|
||||
&actor_address,
|
||||
).await {
|
||||
Ok(profile) => profile,
|
||||
Err(error @ (
|
||||
HandlerError::FetchError(_) |
|
||||
HandlerError::DatabaseError(DatabaseError::NotFound(_))
|
||||
)) => {
|
||||
log::warn!(
|
||||
"failed to import profile {}: {}",
|
||||
actor_address,
|
||||
error,
|
||||
);
|
||||
continue;
|
||||
},
|
||||
Err(other_error) => return Err(other_error.into()),
|
||||
};
|
||||
follow_or_create_request(
|
||||
db_client,
|
||||
&config.instance(),
|
||||
¤t_user,
|
||||
&profile,
|
||||
).await?;
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::activitypub::actors::types::Actor;
|
||||
|
|
|
@ -5,6 +5,11 @@ pub struct PasswordChangeRequest {
|
|||
pub new_password: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ImportFollowsRequest {
|
||||
pub follows_csv: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct MoveFollowersRequest {
|
||||
pub from_actor_id: String,
|
||||
|
|
|
@ -24,9 +24,14 @@ use crate::utils::passwords::hash_password;
|
|||
use super::helpers::{
|
||||
export_followers,
|
||||
export_follows,
|
||||
import_follows_task,
|
||||
parse_address_list,
|
||||
};
|
||||
use super::types::{MoveFollowersRequest, PasswordChangeRequest};
|
||||
use super::types::{
|
||||
ImportFollowsRequest,
|
||||
MoveFollowersRequest,
|
||||
PasswordChangeRequest,
|
||||
};
|
||||
|
||||
#[post("/change_password")]
|
||||
async fn change_password_view(
|
||||
|
@ -82,6 +87,29 @@ async fn export_follows_view(
|
|||
Ok(response)
|
||||
}
|
||||
|
||||
#[post("/import_follows")]
|
||||
async fn import_follows_view(
|
||||
auth: BearerAuth,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
request_data: web::Json<ImportFollowsRequest>,
|
||||
) -> Result<HttpResponse, HttpError> {
|
||||
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)?;
|
||||
tokio::spawn(async move {
|
||||
import_follows_task(
|
||||
&config,
|
||||
current_user,
|
||||
&db_pool,
|
||||
address_list,
|
||||
).await.unwrap_or_else(|error| {
|
||||
log::error!("import follows: {}", error);
|
||||
});
|
||||
});
|
||||
Ok(HttpResponse::NoContent().finish())
|
||||
}
|
||||
|
||||
#[post("/move_followers")]
|
||||
async fn move_followers(
|
||||
auth: BearerAuth,
|
||||
|
@ -170,5 +198,6 @@ pub fn settings_api_scope() -> Scope {
|
|||
.service(change_password_view)
|
||||
.service(export_followers_view)
|
||||
.service(export_follows_view)
|
||||
.service(import_follows_view)
|
||||
.service(move_followers)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue