diff --git a/server/Cargo.lock b/server/Cargo.lock index ceb09b755..025d40a54 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -1545,6 +1545,7 @@ dependencies = [ "lettre", "lettre_email", "log", + "openssl", "percent-encoding", "rand 0.7.3", "regex 1.3.6", diff --git a/server/Cargo.toml b/server/Cargo.toml index 1455428d7..d94d56401 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -38,3 +38,4 @@ url = "2.1.1" percent-encoding = "2.1.0" isahc = "0.9" comrak = "0.7" +openssl = "0.10" diff --git a/server/migrations/2020-03-26-192410_add_activitypub_tables/down.sql b/server/migrations/2020-03-26-192410_add_activitypub_tables/down.sql new file mode 100644 index 000000000..b1710623e --- /dev/null +++ b/server/migrations/2020-03-26-192410_add_activitypub_tables/down.sql @@ -0,0 +1,16 @@ +drop table activity; + +alter table user_ +drop column actor_id, +drop column private_key, +drop column public_key, +drop column bio, +drop column local, +drop column last_refreshed_at; + +alter table community +drop column actor_id, +drop column private_key, +drop column public_key, +drop column local, +drop column last_refreshed_at; diff --git a/server/migrations/2020-03-26-192410_add_activitypub_tables/up.sql b/server/migrations/2020-03-26-192410_add_activitypub_tables/up.sql new file mode 100644 index 000000000..8fe3b8ed1 --- /dev/null +++ b/server/migrations/2020-03-26-192410_add_activitypub_tables/up.sql @@ -0,0 +1,36 @@ +-- The Activitypub activity table +-- All user actions must create a row here. +create table activity ( + id serial primary key, + user_id int references user_ on update cascade on delete cascade not null, -- Ensures that the user is set up here. + data jsonb not null, + local boolean not null default true, + published timestamp not null default now(), + updated timestamp +); + +-- Making sure that id is unique +create unique index idx_activity_unique_apid on activity ((data ->> 'id'::text)); + +-- Add federation columns to the two actor tables +alter table user_ +-- TODO uniqueness constraints should be added on these 3 columns later +add column actor_id character varying(255) not null default 'changeme', -- This needs to be checked and updated in code, building from the site url if local +add column bio text, -- not on community, already has description +add column local boolean not null default true, +add column private_key text, -- These need to be generated from code +add column public_key text, +add column last_refreshed_at timestamp not null default now() -- Used to re-fetch federated actor periodically +; + +-- Community +alter table community +add column actor_id character varying(255) not null default 'changeme', -- This needs to be checked and updated in code, building from the site url if local +add column local boolean not null default true, +add column private_key text, -- These need to be generated from code +add column public_key text, +add column last_refreshed_at timestamp not null default now() -- Used to re-fetch federated actor periodically +; + +-- Don't worry about rebuilding the views right now. + diff --git a/server/src/api/community.rs b/server/src/api/community.rs index dac8733b2..e81f3be0d 100644 --- a/server/src/api/community.rs +++ b/server/src/api/community.rs @@ -1,5 +1,6 @@ use super::*; use crate::apub::puller::{get_all_communities, get_remote_community}; +use crate::apub::{gen_keypair_str, make_apub_endpoint, EndpointType}; use crate::settings::Settings; use diesel::PgConnection; use std::str::FromStr; @@ -208,6 +209,8 @@ impl Perform for Oper { } // When you create a community, make sure the user becomes a moderator and a follower + let (community_public_key, community_private_key) = gen_keypair_str(); + let community_form = CommunityForm { name: data.name.to_owned(), title: data.title.to_owned(), @@ -218,6 +221,11 @@ impl Perform for Oper { deleted: None, nsfw: data.nsfw, updated: None, + actor_id: make_apub_endpoint(EndpointType::Community, &data.name).to_string(), + local: true, + private_key: Some(community_private_key), + public_key: Some(community_public_key), + last_refreshed_at: None, }; let inserted_community = match Community::create(&conn, &community_form) { @@ -298,6 +306,8 @@ impl Perform for Oper { return Err(APIError::err("no_community_edit_allowed").into()); } + let read_community = Community::read(&conn, data.edit_id)?; + let community_form = CommunityForm { name: data.name.to_owned(), title: data.title.to_owned(), @@ -308,6 +318,11 @@ impl Perform for Oper { deleted: data.deleted.to_owned(), nsfw: data.nsfw, updated: Some(naive_now()), + actor_id: read_community.actor_id, + local: read_community.local, + private_key: read_community.private_key, + public_key: read_community.public_key, + last_refreshed_at: None, }; let _updated_community = match Community::update(&conn, data.edit_id, &community_form) { @@ -571,6 +586,11 @@ impl Perform for Oper { deleted: None, nsfw: read_community.nsfw, updated: Some(naive_now()), + actor_id: read_community.actor_id, + local: read_community.local, + private_key: read_community.private_key, + public_key: read_community.public_key, + last_refreshed_at: None, }; let _updated_community = match Community::update(&conn, data.community_id, &community_form) { diff --git a/server/src/api/user.rs b/server/src/api/user.rs index 056a2a846..59a3d623a 100644 --- a/server/src/api/user.rs +++ b/server/src/api/user.rs @@ -1,4 +1,5 @@ use super::*; +use crate::apub::{gen_keypair_str, make_apub_endpoint, EndpointType}; use crate::settings::Settings; use crate::{generate_random_string, send_email}; use bcrypt::verify; @@ -250,6 +251,8 @@ impl Perform for Oper { return Err(APIError::err("admin_already_created").into()); } + let (user_public_key, user_private_key) = gen_keypair_str(); + // Register the new user let user_form = UserForm { name: data.username.to_owned(), @@ -269,6 +272,12 @@ impl Perform for Oper { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: make_apub_endpoint(EndpointType::User, &data.username).to_string(), + bio: None, + local: true, + private_key: Some(user_private_key), + public_key: Some(user_public_key), + last_refreshed_at: None, }; // Create the user @@ -287,12 +296,15 @@ impl Perform for Oper { } }; + let (community_public_key, community_private_key) = gen_keypair_str(); + // Create the main community if it doesn't exist let main_community: Community = match Community::read(&conn, 2) { Ok(c) => c, Err(_e) => { + let default_community_name = "main"; let community_form = CommunityForm { - name: "main".to_string(), + name: default_community_name.to_string(), title: "The Default Community".to_string(), description: Some("The Default Community".to_string()), category_id: 1, @@ -301,6 +313,11 @@ impl Perform for Oper { removed: None, deleted: None, updated: None, + actor_id: make_apub_endpoint(EndpointType::Community, default_community_name).to_string(), + local: true, + private_key: Some(community_private_key), + public_key: Some(community_public_key), + last_refreshed_at: None, }; Community::create(&conn, &community_form).unwrap() } @@ -403,6 +420,12 @@ impl Perform for Oper { lang: data.lang.to_owned(), show_avatars: data.show_avatars, send_notifications_to_email: data.send_notifications_to_email, + actor_id: read_user.actor_id, + bio: read_user.bio, + local: read_user.local, + private_key: read_user.private_key, + public_key: read_user.public_key, + last_refreshed_at: None, }; let updated_user = match User_::update(&conn, user_id, &user_form) { @@ -561,6 +584,12 @@ impl Perform for Oper { lang: read_user.lang, show_avatars: read_user.show_avatars, send_notifications_to_email: read_user.send_notifications_to_email, + actor_id: read_user.actor_id, + bio: read_user.bio, + local: read_user.local, + private_key: read_user.private_key, + public_key: read_user.public_key, + last_refreshed_at: None, }; match User_::update(&conn, data.user_id, &user_form) { @@ -624,6 +653,12 @@ impl Perform for Oper { lang: read_user.lang, show_avatars: read_user.show_avatars, send_notifications_to_email: read_user.send_notifications_to_email, + actor_id: read_user.actor_id, + bio: read_user.bio, + local: read_user.local, + private_key: read_user.private_key, + public_key: read_user.public_key, + last_refreshed_at: None, }; match User_::update(&conn, data.user_id, &user_form) { diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs index ed6ac6562..661c20269 100644 --- a/server/src/apub/mod.rs +++ b/server/src/apub/mod.rs @@ -3,6 +3,7 @@ pub mod post; pub mod puller; pub mod user; use crate::Settings; +use openssl::{pkey::PKey, rsa::Rsa}; use actix_web::body::Body; use actix_web::HttpResponse; @@ -17,13 +18,13 @@ where .json(json) } -enum EndpointType { +pub enum EndpointType { Community, User, Post, } -fn make_apub_endpoint(endpoint_type: EndpointType, name: &str) -> Url { +pub fn make_apub_endpoint(endpoint_type: EndpointType, name: &str) -> Url { let point = match endpoint_type { EndpointType::Community => "c", EndpointType::User => "u", @@ -47,3 +48,25 @@ pub fn get_apub_protocol_string() -> &'static str { "http" } } + +pub fn gen_keypair() -> (Vec, Vec) { + let rsa = Rsa::generate(2048).expect("sign::gen_keypair: key generation error"); + let pkey = PKey::from_rsa(rsa).expect("sign::gen_keypair: parsing error"); + ( + pkey + .public_key_to_pem() + .expect("sign::gen_keypair: public key encoding error"), + pkey + .private_key_to_pem_pkcs8() + .expect("sign::gen_keypair: private key encoding error"), + ) +} + +pub fn gen_keypair_str() -> (String, String) { + let (public_key, private_key) = gen_keypair(); + (vec_bytes_to_str(public_key), vec_bytes_to_str(private_key)) +} + +fn vec_bytes_to_str(bytes: Vec) -> String { + String::from_utf8_lossy(&bytes).into_owned() +} diff --git a/server/src/db/code_migrations.rs b/server/src/db/code_migrations.rs new file mode 100644 index 000000000..e0c736e13 --- /dev/null +++ b/server/src/db/code_migrations.rs @@ -0,0 +1,101 @@ +// This is for db migrations that require code +use super::community::{Community, CommunityForm}; +use super::user::{UserForm, User_}; +use super::*; +use crate::apub::{gen_keypair_str, make_apub_endpoint, EndpointType}; +use crate::naive_now; +use log::info; + +pub fn run_advanced_migrations(conn: &PgConnection) -> Result<(), Error> { + user_updates_2020_04_02(conn)?; + community_updates_2020_04_02(conn)?; + + Ok(()) +} + +fn user_updates_2020_04_02(conn: &PgConnection) -> Result<(), Error> { + use crate::schema::user_::dsl::*; + + info!("Running user_updates_2020_04_02"); + + // Update the actor_id, private_key, and public_key, last_refreshed_at + let incorrect_users = user_ + .filter(actor_id.eq("changeme")) + .filter(local.eq(true)) + .load::(conn)?; + + for cuser in &incorrect_users { + let (user_public_key, user_private_key) = gen_keypair_str(); + + let form = UserForm { + name: cuser.name.to_owned(), + fedi_name: cuser.fedi_name.to_owned(), + email: cuser.email.to_owned(), + matrix_user_id: cuser.matrix_user_id.to_owned(), + avatar: cuser.avatar.to_owned(), + password_encrypted: cuser.password_encrypted.to_owned(), + preferred_username: cuser.preferred_username.to_owned(), + updated: None, + admin: cuser.admin, + banned: cuser.banned, + show_nsfw: cuser.show_nsfw, + theme: cuser.theme.to_owned(), + default_sort_type: cuser.default_sort_type, + default_listing_type: cuser.default_listing_type, + lang: cuser.lang.to_owned(), + show_avatars: cuser.show_avatars, + send_notifications_to_email: cuser.send_notifications_to_email, + actor_id: make_apub_endpoint(EndpointType::User, &cuser.name).to_string(), + bio: cuser.bio.to_owned(), + local: cuser.local, + private_key: Some(user_private_key), + public_key: Some(user_public_key), + last_refreshed_at: Some(naive_now()), + }; + + User_::update(&conn, cuser.id, &form)?; + } + + info!("{} user rows updated.", incorrect_users.len()); + + Ok(()) +} + +fn community_updates_2020_04_02(conn: &PgConnection) -> Result<(), Error> { + use crate::schema::community::dsl::*; + + info!("Running community_updates_2020_04_02"); + + // Update the actor_id, private_key, and public_key, last_refreshed_at + let incorrect_communities = community + .filter(actor_id.eq("changeme")) + .filter(local.eq(true)) + .load::(conn)?; + + for ccommunity in &incorrect_communities { + let (community_public_key, community_private_key) = gen_keypair_str(); + + let form = CommunityForm { + name: ccommunity.name.to_owned(), + title: ccommunity.title.to_owned(), + description: ccommunity.description.to_owned(), + category_id: ccommunity.category_id, + creator_id: ccommunity.creator_id, + removed: None, + deleted: None, + nsfw: ccommunity.nsfw, + updated: None, + actor_id: make_apub_endpoint(EndpointType::Community, &ccommunity.name).to_string(), + local: ccommunity.local, + private_key: Some(community_private_key), + public_key: Some(community_public_key), + last_refreshed_at: Some(naive_now()), + }; + + Community::update(&conn, ccommunity.id, &form)?; + } + + info!("{} community rows updated.", incorrect_communities.len()); + + Ok(()) +} diff --git a/server/src/db/comment.rs b/server/src/db/comment.rs index c9bfbac6c..8110fc5ba 100644 --- a/server/src/db/comment.rs +++ b/server/src/db/comment.rs @@ -186,6 +186,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -200,6 +206,11 @@ mod tests { deleted: None, updated: None, nsfw: false, + actor_id: "changeme".into(), + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_community = Community::create(&conn, &new_community).unwrap(); diff --git a/server/src/db/comment_view.rs b/server/src/db/comment_view.rs index 85b41d826..97c03c536 100644 --- a/server/src/db/comment_view.rs +++ b/server/src/db/comment_view.rs @@ -450,6 +450,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -464,6 +470,11 @@ mod tests { deleted: None, updated: None, nsfw: false, + actor_id: "changeme".into(), + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_community = Community::create(&conn, &new_community).unwrap(); diff --git a/server/src/db/community.rs b/server/src/db/community.rs index 635009635..ff5782243 100644 --- a/server/src/db/community.rs +++ b/server/src/db/community.rs @@ -15,6 +15,11 @@ pub struct Community { pub updated: Option, pub deleted: bool, pub nsfw: bool, + pub actor_id: String, + pub local: bool, + pub private_key: Option, + pub public_key: Option, + pub last_refreshed_at: chrono::NaiveDateTime, } #[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)] @@ -29,6 +34,11 @@ pub struct CommunityForm { pub updated: Option, pub deleted: Option, pub nsfw: bool, + pub actor_id: String, + pub local: bool, + pub private_key: Option, + pub public_key: Option, + pub last_refreshed_at: Option, } impl Crud for Community { @@ -232,6 +242,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -246,6 +262,11 @@ mod tests { removed: None, deleted: None, updated: None, + actor_id: "changeme".into(), + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_community = Community::create(&conn, &new_community).unwrap(); @@ -262,6 +283,11 @@ mod tests { deleted: false, published: inserted_community.published, updated: None, + actor_id: "changeme".into(), + local: true, + private_key: None, + public_key: None, + last_refreshed_at: inserted_community.published, }; let community_follower_form = CommunityFollowerForm { diff --git a/server/src/db/mod.rs b/server/src/db/mod.rs index e0d358ffe..f434f7288 100644 --- a/server/src/db/mod.rs +++ b/server/src/db/mod.rs @@ -5,6 +5,7 @@ use diesel::*; use serde::{Deserialize, Serialize}; pub mod category; +pub mod code_migrations; pub mod comment; pub mod comment_view; pub mod community; diff --git a/server/src/db/moderator.rs b/server/src/db/moderator.rs index a8c3df4f1..b01dc5cc1 100644 --- a/server/src/db/moderator.rs +++ b/server/src/db/moderator.rs @@ -454,6 +454,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_mod = User_::create(&conn, &new_mod).unwrap(); @@ -476,6 +482,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -490,6 +502,11 @@ mod tests { deleted: None, updated: None, nsfw: false, + actor_id: "changeme".into(), + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_community = Community::create(&conn, &new_community).unwrap(); diff --git a/server/src/db/password_reset_request.rs b/server/src/db/password_reset_request.rs index 6951fd399..c9d18e1cf 100644 --- a/server/src/db/password_reset_request.rs +++ b/server/src/db/password_reset_request.rs @@ -104,6 +104,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); diff --git a/server/src/db/post.rs b/server/src/db/post.rs index ffde14d3d..bf9a9ad73 100644 --- a/server/src/db/post.rs +++ b/server/src/db/post.rs @@ -207,6 +207,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -221,6 +227,11 @@ mod tests { deleted: None, updated: None, nsfw: false, + actor_id: "changeme".into(), + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_community = Community::create(&conn, &new_community).unwrap(); diff --git a/server/src/db/post_view.rs b/server/src/db/post_view.rs index f48f4f680..587ae52b0 100644 --- a/server/src/db/post_view.rs +++ b/server/src/db/post_view.rs @@ -375,6 +375,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -389,6 +395,11 @@ mod tests { deleted: None, updated: None, nsfw: false, + actor_id: "changeme".into(), + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_community = Community::create(&conn, &new_community).unwrap(); diff --git a/server/src/db/private_message.rs b/server/src/db/private_message.rs index cc073b594..18ec4963b 100644 --- a/server/src/db/private_message.rs +++ b/server/src/db/private_message.rs @@ -81,6 +81,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_creator = User_::create(&conn, &creator_form).unwrap(); @@ -103,6 +109,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_recipient = User_::create(&conn, &recipient_form).unwrap(); diff --git a/server/src/db/user.rs b/server/src/db/user.rs index e7c769650..32596c270 100644 --- a/server/src/db/user.rs +++ b/server/src/db/user.rs @@ -27,6 +27,12 @@ pub struct User_ { pub show_avatars: bool, pub send_notifications_to_email: bool, pub matrix_user_id: Option, + pub actor_id: String, + pub bio: Option, + pub local: bool, + pub private_key: Option, + pub public_key: Option, + pub last_refreshed_at: chrono::NaiveDateTime, } #[derive(Insertable, AsChangeset, Clone)] @@ -49,6 +55,12 @@ pub struct UserForm { pub show_avatars: bool, pub send_notifications_to_email: bool, pub matrix_user_id: Option, + pub actor_id: String, + pub bio: Option, + pub local: bool, + pub private_key: Option, + pub public_key: Option, + pub last_refreshed_at: Option, } impl Crud for User_ { @@ -78,6 +90,7 @@ impl User_ { Self::create(&conn, &edited_user) } + // TODO do more individual updates like these pub fn update_password( conn: &PgConnection, user_id: i32, @@ -202,6 +215,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -226,6 +245,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: inserted_user.published, }; let read_user = User_::read(&conn, inserted_user.id).unwrap(); diff --git a/server/src/db/user_mention.rs b/server/src/db/user_mention.rs index 0cf257955..48814c89a 100644 --- a/server/src/db/user_mention.rs +++ b/server/src/db/user_mention.rs @@ -80,6 +80,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -102,6 +108,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_recipient = User_::create(&conn, &recipient_form).unwrap(); @@ -116,6 +128,11 @@ mod tests { deleted: None, updated: None, nsfw: false, + actor_id: "changeme".into(), + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_community = Community::create(&conn, &new_community).unwrap(); diff --git a/server/src/lib.rs b/server/src/lib.rs index 9f882b7f8..e45311eec 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -17,6 +17,7 @@ pub extern crate jsonwebtoken; pub extern crate lettre; pub extern crate lettre_email; extern crate log; +pub extern crate openssl; pub extern crate rand; pub extern crate regex; pub extern crate rss; diff --git a/server/src/main.rs b/server/src/main.rs index 601c2e0dc..f2a19d30b 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -6,6 +6,7 @@ use actix::prelude::*; use actix_web::*; use diesel::r2d2::{ConnectionManager, Pool}; use diesel::PgConnection; +use lemmy_server::db::code_migrations::run_advanced_migrations; use lemmy_server::routes::{api, federation, feeds, index, nodeinfo, webfinger, websocket}; use lemmy_server::settings::Settings; use lemmy_server::websocket::server::*; @@ -28,6 +29,7 @@ async fn main() -> io::Result<()> { // Run the migrations from code let conn = pool.get().unwrap(); embedded_migrations::run(&conn).unwrap(); + run_advanced_migrations(&conn).unwrap(); // Set up websocket server let server = ChatServer::startup(pool.clone()).start(); diff --git a/server/src/schema.rs b/server/src/schema.rs index d9449fef9..41769ded6 100644 --- a/server/src/schema.rs +++ b/server/src/schema.rs @@ -1,316 +1,339 @@ table! { - category (id) { - id -> Int4, - name -> Varchar, - } + activity (id) { + id -> Int4, + user_id -> Int4, + data -> Jsonb, + local -> Bool, + published -> Timestamp, + updated -> Nullable, + } } table! { - comment (id) { - id -> Int4, - creator_id -> Int4, - post_id -> Int4, - parent_id -> Nullable, - content -> Text, - removed -> Bool, - read -> Bool, - published -> Timestamp, - updated -> Nullable, - deleted -> Bool, - } + category (id) { + id -> Int4, + name -> Varchar, + } } table! { - comment_like (id) { - id -> Int4, - user_id -> Int4, - comment_id -> Int4, - post_id -> Int4, - score -> Int2, - published -> Timestamp, - } + comment (id) { + id -> Int4, + creator_id -> Int4, + post_id -> Int4, + parent_id -> Nullable, + content -> Text, + removed -> Bool, + read -> Bool, + published -> Timestamp, + updated -> Nullable, + deleted -> Bool, + } } table! { - comment_saved (id) { - id -> Int4, - comment_id -> Int4, - user_id -> Int4, - published -> Timestamp, - } + comment_like (id) { + id -> Int4, + user_id -> Int4, + comment_id -> Int4, + post_id -> Int4, + score -> Int2, + published -> Timestamp, + } } table! { - community (id) { - id -> Int4, - name -> Varchar, - title -> Varchar, - description -> Nullable, - category_id -> Int4, - creator_id -> Int4, - removed -> Bool, - published -> Timestamp, - updated -> Nullable, - deleted -> Bool, - nsfw -> Bool, - } + comment_saved (id) { + id -> Int4, + comment_id -> Int4, + user_id -> Int4, + published -> Timestamp, + } } table! { - community_follower (id) { - id -> Int4, - community_id -> Int4, - user_id -> Int4, - published -> Timestamp, - } + community (id) { + id -> Int4, + name -> Varchar, + title -> Varchar, + description -> Nullable, + category_id -> Int4, + creator_id -> Int4, + removed -> Bool, + published -> Timestamp, + updated -> Nullable, + deleted -> Bool, + nsfw -> Bool, + actor_id -> Varchar, + local -> Bool, + private_key -> Nullable, + public_key -> Nullable, + last_refreshed_at -> Timestamp, + } } table! { - community_moderator (id) { - id -> Int4, - community_id -> Int4, - user_id -> Int4, - published -> Timestamp, - } + community_follower (id) { + id -> Int4, + community_id -> Int4, + user_id -> Int4, + published -> Timestamp, + } } table! { - community_user_ban (id) { - id -> Int4, - community_id -> Int4, - user_id -> Int4, - published -> Timestamp, - } + community_moderator (id) { + id -> Int4, + community_id -> Int4, + user_id -> Int4, + published -> Timestamp, + } } table! { - mod_add (id) { - id -> Int4, - mod_user_id -> Int4, - other_user_id -> Int4, - removed -> Nullable, - when_ -> Timestamp, - } + community_user_ban (id) { + id -> Int4, + community_id -> Int4, + user_id -> Int4, + published -> Timestamp, + } } table! { - mod_add_community (id) { - id -> Int4, - mod_user_id -> Int4, - other_user_id -> Int4, - community_id -> Int4, - removed -> Nullable, - when_ -> Timestamp, - } + mod_add (id) { + id -> Int4, + mod_user_id -> Int4, + other_user_id -> Int4, + removed -> Nullable, + when_ -> Timestamp, + } } table! { - mod_ban (id) { - id -> Int4, - mod_user_id -> Int4, - other_user_id -> Int4, - reason -> Nullable, - banned -> Nullable, - expires -> Nullable, - when_ -> Timestamp, - } + mod_add_community (id) { + id -> Int4, + mod_user_id -> Int4, + other_user_id -> Int4, + community_id -> Int4, + removed -> Nullable, + when_ -> Timestamp, + } } table! { - mod_ban_from_community (id) { - id -> Int4, - mod_user_id -> Int4, - other_user_id -> Int4, - community_id -> Int4, - reason -> Nullable, - banned -> Nullable, - expires -> Nullable, - when_ -> Timestamp, - } + mod_ban (id) { + id -> Int4, + mod_user_id -> Int4, + other_user_id -> Int4, + reason -> Nullable, + banned -> Nullable, + expires -> Nullable, + when_ -> Timestamp, + } } table! { - mod_lock_post (id) { - id -> Int4, - mod_user_id -> Int4, - post_id -> Int4, - locked -> Nullable, - when_ -> Timestamp, - } + mod_ban_from_community (id) { + id -> Int4, + mod_user_id -> Int4, + other_user_id -> Int4, + community_id -> Int4, + reason -> Nullable, + banned -> Nullable, + expires -> Nullable, + when_ -> Timestamp, + } } table! { - mod_remove_comment (id) { - id -> Int4, - mod_user_id -> Int4, - comment_id -> Int4, - reason -> Nullable, - removed -> Nullable, - when_ -> Timestamp, - } + mod_lock_post (id) { + id -> Int4, + mod_user_id -> Int4, + post_id -> Int4, + locked -> Nullable, + when_ -> Timestamp, + } } table! { - mod_remove_community (id) { - id -> Int4, - mod_user_id -> Int4, - community_id -> Int4, - reason -> Nullable, - removed -> Nullable, - expires -> Nullable, - when_ -> Timestamp, - } + mod_remove_comment (id) { + id -> Int4, + mod_user_id -> Int4, + comment_id -> Int4, + reason -> Nullable, + removed -> Nullable, + when_ -> Timestamp, + } } table! { - mod_remove_post (id) { - id -> Int4, - mod_user_id -> Int4, - post_id -> Int4, - reason -> Nullable, - removed -> Nullable, - when_ -> Timestamp, - } + mod_remove_community (id) { + id -> Int4, + mod_user_id -> Int4, + community_id -> Int4, + reason -> Nullable, + removed -> Nullable, + expires -> Nullable, + when_ -> Timestamp, + } } table! { - mod_sticky_post (id) { - id -> Int4, - mod_user_id -> Int4, - post_id -> Int4, - stickied -> Nullable, - when_ -> Timestamp, - } + mod_remove_post (id) { + id -> Int4, + mod_user_id -> Int4, + post_id -> Int4, + reason -> Nullable, + removed -> Nullable, + when_ -> Timestamp, + } } table! { - password_reset_request (id) { - id -> Int4, - user_id -> Int4, - token_encrypted -> Text, - published -> Timestamp, - } + mod_sticky_post (id) { + id -> Int4, + mod_user_id -> Int4, + post_id -> Int4, + stickied -> Nullable, + when_ -> Timestamp, + } } table! { - post (id) { - id -> Int4, - name -> Varchar, - url -> Nullable, - body -> Nullable, - creator_id -> Int4, - community_id -> Int4, - removed -> Bool, - locked -> Bool, - published -> Timestamp, - updated -> Nullable, - deleted -> Bool, - nsfw -> Bool, - stickied -> Bool, - embed_title -> Nullable, - embed_description -> Nullable, - embed_html -> Nullable, - thumbnail_url -> Nullable, - } + password_reset_request (id) { + id -> Int4, + user_id -> Int4, + token_encrypted -> Text, + published -> Timestamp, + } } table! { - post_like (id) { - id -> Int4, - post_id -> Int4, - user_id -> Int4, - score -> Int2, - published -> Timestamp, - } + post (id) { + id -> Int4, + name -> Varchar, + url -> Nullable, + body -> Nullable, + creator_id -> Int4, + community_id -> Int4, + removed -> Bool, + locked -> Bool, + published -> Timestamp, + updated -> Nullable, + deleted -> Bool, + nsfw -> Bool, + stickied -> Bool, + embed_title -> Nullable, + embed_description -> Nullable, + embed_html -> Nullable, + thumbnail_url -> Nullable, + } } table! { - post_read (id) { - id -> Int4, - post_id -> Int4, - user_id -> Int4, - published -> Timestamp, - } + post_like (id) { + id -> Int4, + post_id -> Int4, + user_id -> Int4, + score -> Int2, + published -> Timestamp, + } } table! { - post_saved (id) { - id -> Int4, - post_id -> Int4, - user_id -> Int4, - published -> Timestamp, - } + post_read (id) { + id -> Int4, + post_id -> Int4, + user_id -> Int4, + published -> Timestamp, + } } table! { - private_message (id) { - id -> Int4, - creator_id -> Int4, - recipient_id -> Int4, - content -> Text, - deleted -> Bool, - read -> Bool, - published -> Timestamp, - updated -> Nullable, - } + post_saved (id) { + id -> Int4, + post_id -> Int4, + user_id -> Int4, + published -> Timestamp, + } } table! { - site (id) { - id -> Int4, - name -> Varchar, - description -> Nullable, - creator_id -> Int4, - published -> Timestamp, - updated -> Nullable, - enable_downvotes -> Bool, - open_registration -> Bool, - enable_nsfw -> Bool, - } + private_message (id) { + id -> Int4, + creator_id -> Int4, + recipient_id -> Int4, + content -> Text, + deleted -> Bool, + read -> Bool, + published -> Timestamp, + updated -> Nullable, + } } table! { - user_ (id) { - id -> Int4, - name -> Varchar, - fedi_name -> Varchar, - preferred_username -> Nullable, - password_encrypted -> Text, - email -> Nullable, - avatar -> Nullable, - admin -> Bool, - banned -> Bool, - published -> Timestamp, - updated -> Nullable, - show_nsfw -> Bool, - theme -> Varchar, - default_sort_type -> Int2, - default_listing_type -> Int2, - lang -> Varchar, - show_avatars -> Bool, - send_notifications_to_email -> Bool, - matrix_user_id -> Nullable, - } + site (id) { + id -> Int4, + name -> Varchar, + description -> Nullable, + creator_id -> Int4, + published -> Timestamp, + updated -> Nullable, + enable_downvotes -> Bool, + open_registration -> Bool, + enable_nsfw -> Bool, + } } table! { - user_ban (id) { - id -> Int4, - user_id -> Int4, - published -> Timestamp, - } + user_ (id) { + id -> Int4, + name -> Varchar, + fedi_name -> Varchar, + preferred_username -> Nullable, + password_encrypted -> Text, + email -> Nullable, + avatar -> Nullable, + admin -> Bool, + banned -> Bool, + published -> Timestamp, + updated -> Nullable, + show_nsfw -> Bool, + theme -> Varchar, + default_sort_type -> Int2, + default_listing_type -> Int2, + lang -> Varchar, + show_avatars -> Bool, + send_notifications_to_email -> Bool, + matrix_user_id -> Nullable, + actor_id -> Varchar, + bio -> Nullable, + local -> Bool, + private_key -> Nullable, + public_key -> Nullable, + last_refreshed_at -> Timestamp, + } } table! { - user_mention (id) { - id -> Int4, - recipient_id -> Int4, - comment_id -> Int4, - read -> Bool, - published -> Timestamp, - } + user_ban (id) { + id -> Int4, + user_id -> Int4, + published -> Timestamp, + } } +table! { + user_mention (id) { + id -> Int4, + recipient_id -> Int4, + comment_id -> Int4, + read -> Bool, + published -> Timestamp, + } +} + +joinable!(activity -> user_ (user_id)); joinable!(comment -> post (post_id)); joinable!(comment -> user_ (creator_id)); joinable!(comment_like -> comment (comment_id)); @@ -353,6 +376,7 @@ joinable!(user_mention -> comment (comment_id)); joinable!(user_mention -> user_ (recipient_id)); allow_tables_to_appear_in_same_query!( + activity, category, comment, comment_like,