2022-11-25 23:37:04 +00:00
|
|
|
use actix_web::{get, post, web, HttpResponse, Scope};
|
2022-11-24 01:30:23 +00:00
|
|
|
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
|
|
|
|
2023-01-08 21:40:41 +00:00
|
|
|
use crate::activitypub::{
|
|
|
|
builders::{
|
2023-01-08 23:21:31 +00:00
|
|
|
move_person::prepare_move_person,
|
2023-01-08 21:40:41 +00:00
|
|
|
undo_follow::prepare_undo_follow,
|
|
|
|
},
|
|
|
|
};
|
2022-11-24 01:30:23 +00:00
|
|
|
use crate::config::Config;
|
2023-01-08 21:40:41 +00:00
|
|
|
use crate::database::{get_database_client, DatabaseError, DbPool};
|
|
|
|
use crate::errors::{HttpError, ValidationError};
|
2022-11-25 23:37:04 +00:00
|
|
|
use crate::mastodon_api::{
|
2023-01-08 23:21:31 +00:00
|
|
|
accounts::types::Account,
|
2022-11-25 23:37:04 +00:00
|
|
|
oauth::auth::get_current_user,
|
|
|
|
};
|
2023-01-08 21:40:41 +00:00
|
|
|
use crate::models::{
|
|
|
|
profiles::helpers::find_aliases,
|
|
|
|
profiles::queries::{get_profile_by_acct, get_profile_by_remote_actor_id},
|
|
|
|
relationships::queries::{follow, unfollow},
|
|
|
|
users::queries::set_user_password,
|
|
|
|
};
|
2023-01-08 23:21:31 +00:00
|
|
|
use crate::utils::passwords::hash_password;
|
2023-01-10 01:36:06 +00:00
|
|
|
use super::helpers::{
|
|
|
|
export_followers,
|
|
|
|
export_follows,
|
|
|
|
parse_address_list,
|
|
|
|
};
|
2023-01-08 21:40:41 +00:00
|
|
|
use super::types::{MoveFollowersRequest, PasswordChangeRequest};
|
2022-11-25 23:37:04 +00:00
|
|
|
|
|
|
|
#[post("/change_password")]
|
|
|
|
async fn change_password_view(
|
|
|
|
auth: BearerAuth,
|
|
|
|
config: web::Data<Config>,
|
2022-12-03 21:23:52 +00:00
|
|
|
db_pool: web::Data<DbPool>,
|
2022-11-25 23:37:04 +00:00
|
|
|
request_data: web::Json<PasswordChangeRequest>,
|
|
|
|
) -> Result<HttpResponse, HttpError> {
|
|
|
|
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)?;
|
|
|
|
set_user_password(db_client, ¤t_user.id, password_hash).await?;
|
|
|
|
let account = Account::from_user(current_user, &config.instance_url());
|
|
|
|
Ok(HttpResponse::Ok().json(account))
|
|
|
|
}
|
2022-11-24 01:30:23 +00:00
|
|
|
|
|
|
|
#[get("/export_followers")]
|
|
|
|
async fn export_followers_view(
|
|
|
|
auth: BearerAuth,
|
|
|
|
config: web::Data<Config>,
|
2022-12-03 21:23:52 +00:00
|
|
|
db_pool: web::Data<DbPool>,
|
2022-11-24 01:30:23 +00:00
|
|
|
) -> Result<HttpResponse, HttpError> {
|
|
|
|
let db_client = &**get_database_client(&db_pool).await?;
|
|
|
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
|
|
|
let csv = export_followers(
|
|
|
|
db_client,
|
|
|
|
&config.instance().hostname(),
|
|
|
|
¤t_user.id,
|
|
|
|
).await?;
|
|
|
|
let response = HttpResponse::Ok()
|
|
|
|
.content_type("text/csv")
|
|
|
|
.body(csv);
|
|
|
|
Ok(response)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/export_follows")]
|
|
|
|
async fn export_follows_view(
|
|
|
|
auth: BearerAuth,
|
|
|
|
config: web::Data<Config>,
|
2022-12-03 21:23:52 +00:00
|
|
|
db_pool: web::Data<DbPool>,
|
2022-11-24 01:30:23 +00:00
|
|
|
) -> Result<HttpResponse, HttpError> {
|
|
|
|
let db_client = &**get_database_client(&db_pool).await?;
|
|
|
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
|
|
|
let csv = export_follows(
|
|
|
|
db_client,
|
|
|
|
&config.instance().hostname(),
|
|
|
|
¤t_user.id,
|
|
|
|
).await?;
|
|
|
|
let response = HttpResponse::Ok()
|
|
|
|
.content_type("text/csv")
|
|
|
|
.body(csv);
|
|
|
|
Ok(response)
|
|
|
|
}
|
|
|
|
|
2023-01-08 21:40:41 +00:00
|
|
|
#[post("/move_followers")]
|
|
|
|
async fn move_followers(
|
|
|
|
auth: BearerAuth,
|
|
|
|
config: web::Data<Config>,
|
|
|
|
db_pool: web::Data<DbPool>,
|
|
|
|
request_data: web::Json<MoveFollowersRequest>,
|
|
|
|
) -> Result<HttpResponse, HttpError> {
|
|
|
|
let db_client = &mut **get_database_client(&db_pool).await?;
|
|
|
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
2023-01-10 00:26:09 +00:00
|
|
|
let instance = config.instance();
|
|
|
|
if request_data.from_actor_id.starts_with(&instance.url()) {
|
|
|
|
return Err(ValidationError("can't move from local actor").into());
|
|
|
|
};
|
2023-01-08 21:40:41 +00:00
|
|
|
// Existence of actor is not verified because
|
|
|
|
// the old profile could have been deleted
|
|
|
|
let maybe_from_profile = match get_profile_by_remote_actor_id(
|
|
|
|
db_client,
|
|
|
|
&request_data.from_actor_id,
|
|
|
|
).await {
|
|
|
|
Ok(profile) => Some(profile),
|
|
|
|
Err(DatabaseError::NotFound(_)) => None,
|
|
|
|
Err(other_error) => return Err(other_error.into()),
|
|
|
|
};
|
|
|
|
if maybe_from_profile.is_some() {
|
|
|
|
// Find known aliases of the current user
|
|
|
|
let mut aliases = find_aliases(db_client, ¤t_user.profile).await?
|
|
|
|
.into_iter()
|
2023-01-10 00:26:09 +00:00
|
|
|
.map(|profile| profile.actor_id(&instance.url()));
|
2023-01-08 21:40:41 +00:00
|
|
|
if !aliases.any(|actor_id| actor_id == request_data.from_actor_id) {
|
|
|
|
return Err(ValidationError("old profile is not an alias").into());
|
|
|
|
};
|
|
|
|
};
|
|
|
|
let mut followers = vec![];
|
2023-01-10 01:36:06 +00:00
|
|
|
let address_list = parse_address_list(&request_data.followers_csv)?;
|
|
|
|
for follower_address in address_list {
|
|
|
|
let follower_acct = follower_address.acct(&instance.hostname());
|
2023-01-08 21:40:41 +00:00
|
|
|
// TODO: fetch unknown profiles
|
|
|
|
let follower = get_profile_by_acct(db_client, &follower_acct).await?;
|
|
|
|
if let Some(remote_actor) = follower.actor_json {
|
|
|
|
// Add remote actor to activity recipients list
|
2023-01-08 23:21:31 +00:00
|
|
|
followers.push(remote_actor);
|
2023-01-08 21:40:41 +00:00
|
|
|
} else {
|
|
|
|
// Immediately move local followers (only if alias can be verified)
|
|
|
|
if let Some(ref from_profile) = maybe_from_profile {
|
|
|
|
match unfollow(db_client, &follower.id, &from_profile.id).await {
|
|
|
|
Ok(maybe_follow_request_id) => {
|
|
|
|
// Send Undo(Follow) to a remote actor
|
|
|
|
let remote_actor = from_profile.actor_json.as_ref()
|
|
|
|
.expect("actor data must be present");
|
|
|
|
let follow_request_id = maybe_follow_request_id
|
|
|
|
.expect("follow request must exist");
|
|
|
|
prepare_undo_follow(
|
2023-01-10 00:26:09 +00:00
|
|
|
&instance,
|
2023-01-08 21:40:41 +00:00
|
|
|
¤t_user,
|
|
|
|
remote_actor,
|
|
|
|
&follow_request_id,
|
|
|
|
).enqueue(db_client).await?;
|
|
|
|
},
|
|
|
|
// Not a follower, ignore
|
|
|
|
Err(DatabaseError::NotFound(_)) => continue,
|
|
|
|
Err(other_error) => return Err(other_error.into()),
|
|
|
|
};
|
|
|
|
match follow(db_client, &follower.id, ¤t_user.id).await {
|
|
|
|
Ok(_) => (),
|
|
|
|
// Ignore if already following
|
|
|
|
Err(DatabaseError::AlreadyExists(_)) => (),
|
|
|
|
Err(other_error) => return Err(other_error.into()),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
2023-01-08 23:21:31 +00:00
|
|
|
prepare_move_person(
|
2023-01-10 00:26:09 +00:00
|
|
|
&instance,
|
2023-01-08 21:40:41 +00:00
|
|
|
¤t_user,
|
|
|
|
&request_data.from_actor_id,
|
2023-01-08 23:21:31 +00:00
|
|
|
followers,
|
|
|
|
None,
|
|
|
|
).enqueue(db_client).await?;
|
|
|
|
|
2023-01-10 00:26:09 +00:00
|
|
|
let account = Account::from_user(current_user, &instance.url());
|
2023-01-08 23:21:31 +00:00
|
|
|
Ok(HttpResponse::Ok().json(account))
|
2023-01-08 21:40:41 +00:00
|
|
|
}
|
|
|
|
|
2022-11-24 01:30:23 +00:00
|
|
|
pub fn settings_api_scope() -> Scope {
|
|
|
|
web::scope("/api/v1/settings")
|
2022-11-25 23:37:04 +00:00
|
|
|
.service(change_password_view)
|
2022-11-24 01:30:23 +00:00
|
|
|
.service(export_followers_view)
|
|
|
|
.service(export_follows_view)
|
2023-01-08 21:40:41 +00:00
|
|
|
.service(move_followers)
|
2022-11-24 01:30:23 +00:00
|
|
|
}
|