diff --git a/Cargo.toml b/Cargo.toml index 546be84..6c01aac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ hydrate = [ "leptos_router/hydrate", "katex/wasm-js", ] +diesel-derive-newtype = ["dep:diesel-derive-newtype"] # This profile significantly speeds up build time. If debug info is needed you can comment the line # out temporarily, but make sure to leave this in the main branch. diff --git a/diesel.toml b/diesel.toml index 85fd363..029fb95 100644 --- a/diesel.toml +++ b/diesel.toml @@ -2,7 +2,7 @@ # see https://diesel.rs/guides/configuring-diesel-cli [print_schema] -file = "src/database/schema.rs" +file = "src/backend/database/schema.rs" custom_type_derives = ["diesel::query_builder::QueryId"] [migrations_directory] diff --git a/migrations/2024-11-11-142910_conflict-constraint/down.sql b/migrations/2024-11-11-142910_conflict-constraint/down.sql new file mode 100644 index 0000000..a37e1a7 --- /dev/null +++ b/migrations/2024-11-11-142910_conflict-constraint/down.sql @@ -0,0 +1,2 @@ +ALTER TABLE conflict DROP CONSTRAINT conflict_creator_id_fkey; +ALTER TABLE conflict ADD CONSTRAINT conflict_creator_id_fkey FOREIGN KEY (creator_id) REFERENCES local_user(id) ON UPDATE CASCADE ON DELETE CASCADE; \ No newline at end of file diff --git a/migrations/2024-11-11-142910_conflict-constraint/up.sql b/migrations/2024-11-11-142910_conflict-constraint/up.sql new file mode 100644 index 0000000..33dff81 --- /dev/null +++ b/migrations/2024-11-11-142910_conflict-constraint/up.sql @@ -0,0 +1,2 @@ +ALTER TABLE conflict DROP CONSTRAINT conflict_creator_id_fkey; +ALTER TABLE conflict ADD CONSTRAINT conflict_creator_id_fkey FOREIGN KEY (creator_id) REFERENCES person(id) ON UPDATE CASCADE ON DELETE CASCADE; \ No newline at end of file diff --git a/src/backend/api/article.rs b/src/backend/api/article.rs index 12f5bd4..45265c6 100644 --- a/src/backend/api/article.rs +++ b/src/backend/api/article.rs @@ -116,7 +116,12 @@ pub(in crate::backend::api) async fn edit_article( edit_form.new_text.push('\n'); } + dbg!( + &edit_form.previous_version_id, + &original_article.latest_version + ); if edit_form.previous_version_id == original_article.latest_version { + dbg!("no conflict"); // No intermediate changes, simply submit new version submit_article_update( edit_form.new_text.clone(), @@ -140,7 +145,7 @@ pub(in crate::backend::api) async fn edit_article( hash: EditVersion::new(&patch.to_string()), diff: patch.to_string(), summary: edit_form.summary.clone(), - creator_id: user.local_user.id, + creator_id: user.person.id, article_id: original_article.article.id, previous_version_id: previous_version.hash, }; diff --git a/src/backend/api/mod.rs b/src/backend/api/mod.rs index f3882c2..ac6f364 100644 --- a/src/backend/api/mod.rs +++ b/src/backend/api/mod.rs @@ -92,7 +92,7 @@ async fn edit_conflicts( Extension(user): Extension, data: Data, ) -> MyResult>> { - let conflicts = DbConflict::list(&user.local_user, &data)?; + let conflicts = DbConflict::list(&user.person, &data)?; let conflicts: Vec = try_join_all(conflicts.into_iter().map(|c| { let data = data.reset_request_count(); async move { c.to_api_conflict(&data).await } diff --git a/src/backend/database/conflict.rs b/src/backend/database/conflict.rs index f2d3d19..2badaa8 100644 --- a/src/backend/database/conflict.rs +++ b/src/backend/database/conflict.rs @@ -5,7 +5,7 @@ use crate::{ federation::activities::submit_article_update, utils::generate_article_version, }, - common::{ApiConflict, DbArticle, DbEdit, DbLocalUser, EditVersion}, + common::{ApiConflict, DbArticle, DbEdit, DbPerson, EditVersion, PersonId}, }; use activitypub_federation::config::Data; use diesel::{ @@ -32,7 +32,7 @@ pub struct DbConflict { pub hash: EditVersion, pub diff: String, pub summary: String, - pub creator_id: i32, + pub creator_id: PersonId, pub article_id: i32, pub previous_version_id: EditVersion, } @@ -43,7 +43,7 @@ pub struct DbConflictForm { pub hash: EditVersion, pub diff: String, pub summary: String, - pub creator_id: i32, + pub creator_id: PersonId, pub article_id: i32, pub previous_version_id: EditVersion, } @@ -56,10 +56,10 @@ impl DbConflict { .get_result(conn.deref_mut())?) } - pub fn list(local_user: &DbLocalUser, data: &IbisData) -> MyResult> { + pub fn list(person: &DbPerson, data: &IbisData) -> MyResult> { let mut conn = data.db_pool.get()?; Ok(conflict::table - .filter(conflict::dsl::creator_id.eq(local_user.id)) + .filter(conflict::dsl::creator_id.eq(person.id)) .get_results(conn.deref_mut())?) } diff --git a/src/backend/database/edit.rs b/src/backend/database/edit.rs index 3ada16c..aae3d5c 100644 --- a/src/backend/database/edit.rs +++ b/src/backend/database/edit.rs @@ -4,7 +4,7 @@ use crate::{ error::MyResult, IbisData, }, - common::{DbArticle, DbEdit, EditVersion, EditView}, + common::{DbArticle, DbEdit, EditVersion, EditView, PersonId}, }; use activitypub_federation::fetch::object_id::ObjectId; use chrono::{DateTime, Utc}; @@ -15,7 +15,7 @@ use std::ops::DerefMut; #[derive(Debug, Clone, Insertable, AsChangeset)] #[diesel(table_name = edit, check_for_backend(diesel::pg::Pg))] pub struct DbEditForm { - pub creator_id: i32, + pub creator_id: PersonId, pub hash: EditVersion, pub ap_id: ObjectId, pub diff: String, @@ -28,7 +28,7 @@ pub struct DbEditForm { impl DbEditForm { pub fn new( original_article: &DbArticle, - creator_id: i32, + creator_id: PersonId, updated_text: &str, summary: String, previous_version_id: EditVersion, diff --git a/src/backend/database/schema.rs b/src/backend/database/schema.rs index 81b43ed..727bfd0 100644 --- a/src/backend/database/schema.rs +++ b/src/backend/database/schema.rs @@ -102,7 +102,7 @@ diesel::table! { diesel::joinable!(article -> instance (instance_id)); diesel::joinable!(conflict -> article (article_id)); -diesel::joinable!(conflict -> local_user (creator_id)); +diesel::joinable!(conflict -> person (creator_id)); diesel::joinable!(edit -> article (article_id)); diesel::joinable!(edit -> person (creator_id)); diesel::joinable!(instance_follow -> instance (instance_id)); diff --git a/src/backend/database/user.rs b/src/backend/database/user.rs index 13dd02c..966d09e 100644 --- a/src/backend/database/user.rs +++ b/src/backend/database/user.rs @@ -6,7 +6,14 @@ use crate::{ }, error::MyResult, }, - common::{utils::http_protocol_str, DbInstance, DbLocalUser, DbPerson, LocalUserView}, + common::{ + utils::http_protocol_str, + DbInstance, + DbLocalUser, + DbPerson, + LocalUserView, + PersonId, + }, }; use activitypub_federation::{ config::Data, @@ -31,7 +38,7 @@ use std::ops::DerefMut; #[diesel(table_name = local_user, check_for_backend(diesel::pg::Pg))] pub struct DbLocalUserForm { pub password_encrypted: String, - pub person_id: i32, + pub person_id: PersonId, pub admin: bool, } @@ -58,7 +65,7 @@ impl DbPerson { .get_result::(conn.deref_mut())?) } - pub fn read(id: i32, data: &Data) -> MyResult { + pub fn read(id: PersonId, data: &Data) -> MyResult { let mut conn = data.db_pool.get()?; Ok(person::table.find(id).get_result(conn.deref_mut())?) } @@ -155,7 +162,7 @@ impl DbPerson { }) } - fn read_following(id_: i32, data: &Data) -> MyResult> { + fn read_following(id_: PersonId, data: &Data) -> MyResult> { use instance_follow::dsl::{follower_id, instance_id}; let mut conn = data.db_pool.get()?; Ok(instance_follow::table diff --git a/src/backend/federation/activities/mod.rs b/src/backend/federation/activities/mod.rs index 251d546..c3614b3 100644 --- a/src/backend/federation/activities/mod.rs +++ b/src/backend/federation/activities/mod.rs @@ -7,7 +7,7 @@ use crate::{ update_remote_article::UpdateRemoteArticle, }, }, - common::{DbArticle, DbEdit, DbInstance, EditVersion}, + common::{DbArticle, DbEdit, DbInstance, EditVersion, PersonId}, }; use activitypub_federation::config::Data; use chrono::Utc; @@ -24,7 +24,7 @@ pub async fn submit_article_update( summary: String, previous_version: EditVersion, original_article: &DbArticle, - creator_id: i32, + creator_id: PersonId, data: &Data, ) -> Result<(), Error> { let form = DbEditForm::new( diff --git a/src/backend/utils.rs b/src/backend/utils.rs index 542a2a6..3f3531e 100644 --- a/src/backend/utils.rs +++ b/src/backend/utils.rs @@ -46,7 +46,7 @@ pub fn generate_article_version(edits: &Vec, version: &EditVersion) -> #[cfg(test)] mod test { use super::*; - use crate::common::{DbEdit, DbPerson}; + use crate::common::{DbEdit, DbPerson, PersonId}; use activitypub_federation::fetch::object_id::ObjectId; use chrono::Utc; use diffy::create_patch; @@ -57,7 +57,7 @@ mod test { Ok(EditView { edit: DbEdit { id: 0, - creator_id: 0, + creator_id: PersonId(0), hash: EditVersion::new(&diff), ap_id: ObjectId::parse("http://example.com")?, diff, @@ -67,7 +67,7 @@ mod test { created: Utc::now(), }, creator: DbPerson { - id: 0, + id: PersonId(0), username: "".to_string(), ap_id: ObjectId::parse("http://example.com")?, inbox_url: "".to_string(), diff --git a/src/common/mod.rs b/src/common/mod.rs index 12912ad..4734e2b 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -67,7 +67,7 @@ pub struct DbEdit { // the same edit is used for multiple articles pub id: i32, #[serde(skip)] - pub creator_id: i32, + pub creator_id: PersonId, /// UUID built from sha224 hash of diff pub hash: EditVersion, #[cfg(feature = "ssr")] @@ -150,12 +150,17 @@ pub struct DbLocalUser { pub admin: bool, } +#[derive( + Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize, DieselNewType, +)] +pub struct PersonId(pub i32); + /// Federation related data from a local or remote user. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[cfg_attr(feature = "ssr", derive(Queryable, Selectable, Identifiable))] #[cfg_attr(feature = "ssr", diesel(table_name = person, check_for_backend(diesel::pg::Pg)))] pub struct DbPerson { - pub id: i32, + pub id: PersonId, pub username: String, #[cfg(feature = "ssr")] pub ap_id: ObjectId, diff --git a/src/database/schema.rs b/src/database/schema.rs deleted file mode 100644 index 81b43ed..0000000 --- a/src/database/schema.rs +++ /dev/null @@ -1,121 +0,0 @@ -// @generated automatically by Diesel CLI. - -diesel::table! { - article (id) { - id -> Int4, - title -> Text, - text -> Text, - #[max_length = 255] - ap_id -> Varchar, - instance_id -> Int4, - local -> Bool, - protected -> Bool, - } -} - -diesel::table! { - conflict (id) { - id -> Int4, - hash -> Uuid, - diff -> Text, - summary -> Text, - creator_id -> Int4, - article_id -> Int4, - previous_version_id -> Uuid, - } -} - -diesel::table! { - edit (id) { - id -> Int4, - creator_id -> Int4, - hash -> Uuid, - #[max_length = 255] - ap_id -> Varchar, - diff -> Text, - summary -> Text, - article_id -> Int4, - previous_version_id -> Uuid, - created -> Timestamptz, - } -} - -diesel::table! { - instance (id) { - id -> Int4, - domain -> Text, - #[max_length = 255] - ap_id -> Varchar, - description -> Nullable, - #[max_length = 255] - articles_url -> Nullable, - #[max_length = 255] - inbox_url -> Varchar, - public_key -> Text, - private_key -> Nullable, - last_refreshed_at -> Timestamptz, - local -> Bool, - #[max_length = 255] - instances_url -> Nullable, - } -} - -diesel::table! { - instance_follow (id) { - id -> Int4, - instance_id -> Int4, - follower_id -> Int4, - pending -> Bool, - } -} - -diesel::table! { - jwt_secret (id) { - id -> Int4, - secret -> Varchar, - } -} - -diesel::table! { - local_user (id) { - id -> Int4, - password_encrypted -> Text, - person_id -> Int4, - admin -> Bool, - } -} - -diesel::table! { - person (id) { - id -> Int4, - username -> Text, - #[max_length = 255] - ap_id -> Varchar, - #[max_length = 255] - inbox_url -> Varchar, - public_key -> Text, - private_key -> Nullable, - last_refreshed_at -> Timestamptz, - local -> Bool, - } -} - -diesel::joinable!(article -> instance (instance_id)); -diesel::joinable!(conflict -> article (article_id)); -diesel::joinable!(conflict -> local_user (creator_id)); -diesel::joinable!(edit -> article (article_id)); -diesel::joinable!(edit -> person (creator_id)); -diesel::joinable!(instance_follow -> instance (instance_id)); -diesel::joinable!(instance_follow -> person (follower_id)); -diesel::joinable!(local_user -> person (person_id)); - -diesel::allow_tables_to_appear_in_same_query!( - article, - conflict, - edit, - instance, - instance_follow, - jwt_secret, - local_user, - person, -); diff --git a/src/lib.rs b/src/lib.rs index d24c3dc..0edc1ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,6 @@ +#[macro_use] +extern crate diesel_derive_newtype; + #[cfg(feature = "ssr")] pub mod backend; pub mod common; diff --git a/tests/test.rs b/tests/test.rs index adacdf3..f9f3368 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -358,6 +358,7 @@ async fn test_local_edit_conflict() -> MyResult<()> { previous_version_id: create_res.latest_version.clone(), resolve_conflict_id: None, }; + dbg!("first edit"); let edit_res = data.alpha.edit_article(&edit_form).await?; assert_eq!(edit_res.article.text, edit_form.new_text); assert_eq!(2, edit_res.edits.len()); @@ -370,6 +371,7 @@ async fn test_local_edit_conflict() -> MyResult<()> { previous_version_id: create_res.latest_version, resolve_conflict_id: None, }; + dbg!("second edit"); let edit_res = data .alpha .edit_article_with_conflict(&edit_form)