1 // This is for db migrations that require code
2 use activitypub_federation::core::signatures::generate_actor_keypair;
4 sql_types::{Nullable, Text},
8 generate_followers_url,
10 generate_local_apub_endpoint,
11 generate_shared_inbox_url,
12 generate_site_inbox_url,
15 use lemmy_db_schema::{
18 community::{Community, CommunityForm},
19 person::{Person, PersonForm},
21 private_message::PrivateMessage,
22 site::{Site, SiteForm},
27 use lemmy_utils::error::LemmyError;
31 pub fn run_advanced_migrations(
33 protocol_and_hostname: &str,
34 ) -> Result<(), LemmyError> {
35 user_updates_2020_04_02(conn, protocol_and_hostname)?;
36 community_updates_2020_04_02(conn, protocol_and_hostname)?;
37 post_updates_2020_04_03(conn, protocol_and_hostname)?;
38 comment_updates_2020_04_03(conn, protocol_and_hostname)?;
39 private_message_updates_2020_05_05(conn, protocol_and_hostname)?;
40 post_thumbnail_url_updates_2020_07_27(conn, protocol_and_hostname)?;
41 apub_columns_2021_02_02(conn)?;
42 instance_actor_2022_01_28(conn, protocol_and_hostname)?;
47 fn user_updates_2020_04_02(
49 protocol_and_hostname: &str,
50 ) -> Result<(), LemmyError> {
51 use lemmy_db_schema::schema::person::dsl::*;
53 info!("Running user_updates_2020_04_02");
55 // Update the actor_id, private_key, and public_key, last_refreshed_at
56 let incorrect_persons = person
57 .filter(actor_id.like("http://changeme%"))
58 .filter(local.eq(true))
59 .load::<Person>(conn)?;
61 for cperson in &incorrect_persons {
62 let keypair = generate_actor_keypair()?;
64 let form = PersonForm {
65 name: cperson.name.to_owned(),
66 actor_id: Some(generate_local_apub_endpoint(
69 protocol_and_hostname,
71 private_key: Some(Some(keypair.private_key)),
72 public_key: keypair.public_key,
73 last_refreshed_at: Some(naive_now()),
74 ..PersonForm::default()
77 Person::update(conn, cperson.id, &form)?;
80 info!("{} person rows updated.", incorrect_persons.len());
85 fn community_updates_2020_04_02(
87 protocol_and_hostname: &str,
88 ) -> Result<(), LemmyError> {
89 use lemmy_db_schema::schema::community::dsl::*;
91 info!("Running community_updates_2020_04_02");
93 // Update the actor_id, private_key, and public_key, last_refreshed_at
94 let incorrect_communities = community
95 .filter(actor_id.like("http://changeme%"))
96 .filter(local.eq(true))
97 .load::<Community>(conn)?;
99 for ccommunity in &incorrect_communities {
100 let keypair = generate_actor_keypair()?;
101 let community_actor_id = generate_local_apub_endpoint(
102 EndpointType::Community,
104 protocol_and_hostname,
107 let form = CommunityForm {
108 name: ccommunity.name.to_owned(),
109 title: ccommunity.title.to_owned(),
110 description: ccommunity.description.to_owned(),
112 actor_id: Some(community_actor_id.to_owned()),
113 local: Some(ccommunity.local),
114 private_key: Some(Some(keypair.private_key)),
115 public_key: keypair.public_key,
116 last_refreshed_at: Some(naive_now()),
117 icon: Some(ccommunity.icon.to_owned()),
118 banner: Some(ccommunity.banner.to_owned()),
122 Community::update(conn, ccommunity.id, &form)?;
125 info!("{} community rows updated.", incorrect_communities.len());
130 fn post_updates_2020_04_03(
132 protocol_and_hostname: &str,
133 ) -> Result<(), LemmyError> {
134 use lemmy_db_schema::schema::post::dsl::*;
136 info!("Running post_updates_2020_04_03");
139 let incorrect_posts = post
140 .filter(ap_id.like("http://changeme%"))
141 .filter(local.eq(true))
142 .load::<Post>(conn)?;
144 for cpost in &incorrect_posts {
145 let apub_id = generate_local_apub_endpoint(
147 &cpost.id.to_string(),
148 protocol_and_hostname,
150 Post::update_ap_id(conn, cpost.id, apub_id)?;
153 info!("{} post rows updated.", incorrect_posts.len());
158 fn comment_updates_2020_04_03(
160 protocol_and_hostname: &str,
161 ) -> Result<(), LemmyError> {
162 use lemmy_db_schema::schema::comment::dsl::*;
164 info!("Running comment_updates_2020_04_03");
167 let incorrect_comments = comment
168 .filter(ap_id.like("http://changeme%"))
169 .filter(local.eq(true))
170 .load::<Comment>(conn)?;
172 for ccomment in &incorrect_comments {
173 let apub_id = generate_local_apub_endpoint(
174 EndpointType::Comment,
175 &ccomment.id.to_string(),
176 protocol_and_hostname,
178 Comment::update_ap_id(conn, ccomment.id, apub_id)?;
181 info!("{} comment rows updated.", incorrect_comments.len());
186 fn private_message_updates_2020_05_05(
188 protocol_and_hostname: &str,
189 ) -> Result<(), LemmyError> {
190 use lemmy_db_schema::schema::private_message::dsl::*;
192 info!("Running private_message_updates_2020_05_05");
195 let incorrect_pms = private_message
196 .filter(ap_id.like("http://changeme%"))
197 .filter(local.eq(true))
198 .load::<PrivateMessage>(conn)?;
200 for cpm in &incorrect_pms {
201 let apub_id = generate_local_apub_endpoint(
202 EndpointType::PrivateMessage,
204 protocol_and_hostname,
206 PrivateMessage::update_ap_id(conn, cpm.id, apub_id)?;
209 info!("{} private message rows updated.", incorrect_pms.len());
214 fn post_thumbnail_url_updates_2020_07_27(
216 protocol_and_hostname: &str,
217 ) -> Result<(), LemmyError> {
218 use lemmy_db_schema::schema::post::dsl::*;
220 info!("Running post_thumbnail_url_updates_2020_07_27");
222 let domain_prefix = format!("{}/pictrs/image/", protocol_and_hostname,);
224 let incorrect_thumbnails = post.filter(thumbnail_url.not_like("http%"));
226 // Prepend the rows with the update
227 let res = diesel::update(incorrect_thumbnails)
231 .into_sql::<Nullable<Text>>()
232 .concat(thumbnail_url),
235 .get_results::<Post>(conn)?;
237 info!("{} Post thumbnail_url rows updated.", res.len());
242 /// We are setting inbox and follower URLs for local and remote actors alike, because for now
243 /// all federated instances are also Lemmy and use the same URL scheme.
244 fn apub_columns_2021_02_02(conn: &PgConnection) -> Result<(), LemmyError> {
245 info!("Running apub_columns_2021_02_02");
247 use lemmy_db_schema::schema::person::dsl::*;
249 .filter(inbox_url.like("http://changeme%"))
250 .load::<Person>(conn)?;
253 let inbox_url_ = generate_inbox_url(&p.actor_id)?;
254 let shared_inbox_url_ = generate_shared_inbox_url(&p.actor_id)?;
255 diesel::update(person.find(p.id))
257 inbox_url.eq(inbox_url_),
258 shared_inbox_url.eq(shared_inbox_url_),
260 .get_result::<Person>(conn)?;
265 use lemmy_db_schema::schema::community::dsl::*;
266 let communities = community
267 .filter(inbox_url.like("http://changeme%"))
268 .load::<Community>(conn)?;
270 for c in &communities {
271 let followers_url_ = generate_followers_url(&c.actor_id)?;
272 let inbox_url_ = generate_inbox_url(&c.actor_id)?;
273 let shared_inbox_url_ = generate_shared_inbox_url(&c.actor_id)?;
274 diesel::update(community.find(c.id))
276 followers_url.eq(followers_url_),
277 inbox_url.eq(inbox_url_),
278 shared_inbox_url.eq(shared_inbox_url_),
280 .get_result::<Community>(conn)?;
287 /// Site object turns into an actor, so that things like instance description can be federated. This
288 /// means we need to add actor columns to the site table, and initialize them with correct values.
289 /// Before this point, there is only a single value in the site table which refers to the local
290 /// Lemmy instance, so thats all we need to update.
291 fn instance_actor_2022_01_28(
293 protocol_and_hostname: &str,
294 ) -> Result<(), LemmyError> {
295 info!("Running instance_actor_2021_09_29");
296 if let Ok(site) = Site::read_local_site(conn) {
297 let key_pair = generate_actor_keypair()?;
298 let actor_id = Url::parse(protocol_and_hostname)?;
299 let site_form = SiteForm {
301 actor_id: Some(actor_id.clone().into()),
302 last_refreshed_at: Some(naive_now()),
303 inbox_url: Some(generate_site_inbox_url(&actor_id.into())?),
304 private_key: Some(Some(key_pair.private_key)),
305 public_key: Some(key_pair.public_key),
308 Site::update(conn, site.id, &site_form)?;