Add CLI command that deletes old remote posts
This commit is contained in:
parent
76106e4214
commit
8b4325ad96
2 changed files with 89 additions and 4 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
use chrono::{Duration, Utc};
|
||||||
use clap::Clap;
|
use clap::Clap;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
@ -7,7 +8,7 @@ use mitra::database::migrate::apply_migrations;
|
||||||
use mitra::ethereum::signatures::generate_ecdsa_key;
|
use mitra::ethereum::signatures::generate_ecdsa_key;
|
||||||
use mitra::ethereum::utils::key_to_ethereum_address;
|
use mitra::ethereum::utils::key_to_ethereum_address;
|
||||||
use mitra::logger::configure_logger;
|
use mitra::logger::configure_logger;
|
||||||
use mitra::models::posts::queries::delete_post;
|
use mitra::models::posts::queries::{delete_post, find_extraneous_posts};
|
||||||
use mitra::models::profiles::queries::delete_profile;
|
use mitra::models::profiles::queries::delete_profile;
|
||||||
use mitra::models::users::queries::{
|
use mitra::models::users::queries::{
|
||||||
create_invite_code,
|
create_invite_code,
|
||||||
|
@ -31,6 +32,7 @@ enum SubCommand {
|
||||||
ListInviteCodes(ListInviteCodes),
|
ListInviteCodes(ListInviteCodes),
|
||||||
DeleteProfile(DeleteProfile),
|
DeleteProfile(DeleteProfile),
|
||||||
DeletePost(DeletePost),
|
DeletePost(DeletePost),
|
||||||
|
DeleteExtraneousPosts(DeleteExtraneousPosts),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate RSA private key
|
/// Generate RSA private key
|
||||||
|
@ -71,6 +73,16 @@ struct DeletePost {
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Delete old remote posts
|
||||||
|
#[derive(Clap)]
|
||||||
|
struct DeleteExtraneousPosts {
|
||||||
|
#[clap(short)]
|
||||||
|
days: i64,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
dry_run: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let opts: Opts = Opts::parse();
|
let opts: Opts = Opts::parse();
|
||||||
|
@ -103,10 +115,10 @@ async fn main() {
|
||||||
if invite_codes.is_empty() {
|
if invite_codes.is_empty() {
|
||||||
println!("no invite codes found");
|
println!("no invite codes found");
|
||||||
return;
|
return;
|
||||||
}
|
};
|
||||||
for code in invite_codes {
|
for code in invite_codes {
|
||||||
println!("{}", code);
|
println!("{}", code);
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
SubCommand::DeleteProfile(subopts) => {
|
SubCommand::DeleteProfile(subopts) => {
|
||||||
let deletion_queue = delete_profile(db_client, &subopts.id).await.unwrap();
|
let deletion_queue = delete_profile(db_client, &subopts.id).await.unwrap();
|
||||||
|
@ -118,6 +130,17 @@ async fn main() {
|
||||||
deletion_queue.process(&config).await;
|
deletion_queue.process(&config).await;
|
||||||
println!("post deleted");
|
println!("post deleted");
|
||||||
},
|
},
|
||||||
|
SubCommand::DeleteExtraneousPosts(subopts) => {
|
||||||
|
let created_before = Utc::now() - Duration::days(subopts.days);
|
||||||
|
let posts = find_extraneous_posts(db_client, &created_before).await.unwrap();
|
||||||
|
for post_id in posts {
|
||||||
|
if !subopts.dry_run {
|
||||||
|
let deletion_queue = delete_post(db_client, &post_id).await.unwrap();
|
||||||
|
deletion_queue.process(&config).await;
|
||||||
|
};
|
||||||
|
println!("post {} deleted", post_id);
|
||||||
|
};
|
||||||
|
},
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::{DateTime, Utc};
|
||||||
use tokio_postgres::GenericClient;
|
use tokio_postgres::GenericClient;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
@ -727,6 +727,68 @@ pub async fn get_token_waitlist(
|
||||||
Ok(waitlist)
|
Ok(waitlist)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finds all contexts (identified by top-level post)
|
||||||
|
/// created before the specified date
|
||||||
|
/// that do not contain local posts, reposts, mentions or reactions.
|
||||||
|
pub async fn find_extraneous_posts(
|
||||||
|
db_client: &impl GenericClient,
|
||||||
|
created_before: &DateTime<Utc>,
|
||||||
|
) -> Result<Vec<Uuid>, DatabaseError> {
|
||||||
|
let rows = db_client.query(
|
||||||
|
"
|
||||||
|
WITH RECURSIVE context (id, post_id) AS (
|
||||||
|
SELECT post.id, post.id FROM post
|
||||||
|
WHERE
|
||||||
|
post.in_reply_to_id IS NULL
|
||||||
|
AND post.repost_of_id IS NULL
|
||||||
|
AND post.created_at < $1
|
||||||
|
UNION
|
||||||
|
SELECT context.id, post.id FROM post
|
||||||
|
JOIN context ON (
|
||||||
|
post.in_reply_to_id = context.post_id
|
||||||
|
OR post.repost_of_id = context.post_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
SELECT context_agg.id
|
||||||
|
FROM (
|
||||||
|
SELECT context.id, array_agg(context.post_id) AS posts
|
||||||
|
FROM context
|
||||||
|
GROUP BY context.id
|
||||||
|
) AS context_agg
|
||||||
|
WHERE
|
||||||
|
NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM post
|
||||||
|
JOIN actor_profile ON post.author_id = actor_profile.id
|
||||||
|
WHERE
|
||||||
|
post.id = ANY(context_agg.posts)
|
||||||
|
AND actor_profile.actor_json IS NULL
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM mention
|
||||||
|
JOIN actor_profile ON mention.profile_id = actor_profile.id
|
||||||
|
WHERE
|
||||||
|
mention.post_id = ANY(context_agg.posts)
|
||||||
|
AND actor_profile.actor_json IS NULL
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM post_reaction
|
||||||
|
JOIN actor_profile ON post_reaction.author_id = actor_profile.id
|
||||||
|
WHERE
|
||||||
|
post_reaction.post_id = ANY(context_agg.posts)
|
||||||
|
AND actor_profile.actor_json IS NULL
|
||||||
|
)
|
||||||
|
",
|
||||||
|
&[&created_before],
|
||||||
|
).await?;
|
||||||
|
let ids: Vec<Uuid> = rows.iter()
|
||||||
|
.map(|row| row.try_get("id"))
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
Ok(ids)
|
||||||
|
}
|
||||||
|
|
||||||
/// Deletes post from database and returns collection of orphaned objects.
|
/// Deletes post from database and returns collection of orphaned objects.
|
||||||
pub async fn delete_post(
|
pub async fn delete_post(
|
||||||
db_client: &mut impl GenericClient,
|
db_client: &mut impl GenericClient,
|
||||||
|
|
Loading…
Reference in a new issue