mirror of
https://git.joinplu.me/Plume/Plume.git
synced 2024-11-25 13:01:08 +00:00
Fix follow IDs (#455)
* Generate valid IDs for Follow Fixes #449 * Use the new post-insert hook for all the models * Fix plume-cli build
This commit is contained in:
parent
9b48b8a846
commit
2a188abfa1
12 changed files with 162 additions and 181 deletions
|
@ -88,8 +88,7 @@ fn new<'a>(args: &ArgMatches<'a>, conn: &Connection) {
|
||||||
&bio,
|
&bio,
|
||||||
email,
|
email,
|
||||||
User::hash_pass(&password).expect("Couldn't hash password"),
|
User::hash_pass(&password).expect("Couldn't hash password"),
|
||||||
).expect("Couldn't save new user")
|
).expect("Couldn't save new user");
|
||||||
.update_boxes(conn).expect("Couldn't update ActivityPub informations for new user");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset_password<'a>(args: &ArgMatches<'a>, conn: &Connection) {
|
fn reset_password<'a>(args: &ArgMatches<'a>, conn: &Connection) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use activitypub::{actor::Group, collection::OrderedCollection, Actor, CustomObject, Object};
|
use activitypub::{actor::Group, collection::OrderedCollection, Actor, CustomObject, Object};
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
|
||||||
use openssl::{
|
use openssl::{
|
||||||
hash::MessageDigest,
|
hash::MessageDigest,
|
||||||
pkey::{PKey, Private},
|
pkey::{PKey, Private},
|
||||||
|
@ -30,7 +30,7 @@ use {Connection, BASE_URL, USE_HTTPS, Error, Result};
|
||||||
|
|
||||||
pub type CustomGroup = CustomObject<ApSignature, Group>;
|
pub type CustomGroup = CustomObject<ApSignature, Group>;
|
||||||
|
|
||||||
#[derive(Queryable, Identifiable, Serialize, Deserialize, Clone)]
|
#[derive(Queryable, Identifiable, Serialize, Deserialize, Clone, AsChangeset)]
|
||||||
pub struct Blog {
|
pub struct Blog {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub actor_id: String,
|
pub actor_id: String,
|
||||||
|
@ -45,7 +45,7 @@ pub struct Blog {
|
||||||
pub public_key: String,
|
pub public_key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable)]
|
#[derive(Default, Insertable)]
|
||||||
#[table_name = "blogs"]
|
#[table_name = "blogs"]
|
||||||
pub struct NewBlog {
|
pub struct NewBlog {
|
||||||
pub actor_id: String,
|
pub actor_id: String,
|
||||||
|
@ -62,7 +62,33 @@ pub struct NewBlog {
|
||||||
const BLOG_PREFIX: &str = "~";
|
const BLOG_PREFIX: &str = "~";
|
||||||
|
|
||||||
impl Blog {
|
impl Blog {
|
||||||
insert!(blogs, NewBlog);
|
insert!(blogs, NewBlog, |inserted, conn| {
|
||||||
|
let instance = inserted.get_instance(conn)?;
|
||||||
|
if inserted.outbox_url.is_empty() {
|
||||||
|
inserted.outbox_url = instance.compute_box(
|
||||||
|
BLOG_PREFIX,
|
||||||
|
&inserted.actor_id,
|
||||||
|
"outbox",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if inserted.inbox_url.is_empty() {
|
||||||
|
inserted.inbox_url = instance.compute_box(
|
||||||
|
BLOG_PREFIX,
|
||||||
|
&inserted.actor_id,
|
||||||
|
"inbox",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if inserted.ap_url.is_empty() {
|
||||||
|
inserted.ap_url = instance.compute_box(
|
||||||
|
BLOG_PREFIX,
|
||||||
|
&inserted.actor_id,
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
inserted.save_changes(conn).map_err(Error::from)
|
||||||
|
});
|
||||||
get!(blogs);
|
get!(blogs);
|
||||||
find_by!(blogs, find_by_ap_url, ap_url as &str);
|
find_by!(blogs, find_by_ap_url, ap_url as &str);
|
||||||
find_by!(blogs, find_by_name, actor_id as &str, instance_id as i32);
|
find_by!(blogs, find_by_name, actor_id as &str, instance_id as i32);
|
||||||
|
@ -243,36 +269,6 @@ impl Blog {
|
||||||
Ok(CustomGroup::new(blog, ap_signature))
|
Ok(CustomGroup::new(blog, ap_signature))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_boxes(&self, conn: &Connection) -> Result<()> {
|
|
||||||
let instance = self.get_instance(conn)?;
|
|
||||||
if self.outbox_url.is_empty() {
|
|
||||||
diesel::update(self)
|
|
||||||
.set(blogs::outbox_url.eq(instance.compute_box(
|
|
||||||
BLOG_PREFIX,
|
|
||||||
&self.actor_id,
|
|
||||||
"outbox",
|
|
||||||
)))
|
|
||||||
.execute(conn)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.inbox_url.is_empty() {
|
|
||||||
diesel::update(self)
|
|
||||||
.set(blogs::inbox_url.eq(instance.compute_box(
|
|
||||||
BLOG_PREFIX,
|
|
||||||
&self.actor_id,
|
|
||||||
"inbox",
|
|
||||||
)))
|
|
||||||
.execute(conn)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.ap_url.is_empty() {
|
|
||||||
diesel::update(self)
|
|
||||||
.set(blogs::ap_url.eq(instance.compute_box(BLOG_PREFIX, &self.actor_id, "")))
|
|
||||||
.execute(conn)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn outbox(&self, conn: &Connection) -> Result<ActivityStream<OrderedCollection>> {
|
pub fn outbox(&self, conn: &Connection) -> Result<ActivityStream<OrderedCollection>> {
|
||||||
let mut coll = OrderedCollection::default();
|
let mut coll = OrderedCollection::default();
|
||||||
coll.collection_props.items = serde_json::to_value(self.get_activities(conn)?)?;
|
coll.collection_props.items = serde_json::to_value(self.get_activities(conn)?)?;
|
||||||
|
@ -431,12 +427,10 @@ impl NewBlog {
|
||||||
actor_id,
|
actor_id,
|
||||||
title,
|
title,
|
||||||
summary,
|
summary,
|
||||||
outbox_url: String::from(""),
|
|
||||||
inbox_url: String::from(""),
|
|
||||||
instance_id,
|
instance_id,
|
||||||
ap_url: String::from(""),
|
|
||||||
public_key: String::from_utf8(pub_key).or(Err(Error::Signature))?,
|
public_key: String::from_utf8(pub_key).or(Err(Error::Signature))?,
|
||||||
private_key: Some(String::from_utf8(priv_key).or(Err(Error::Signature))?),
|
private_key: Some(String::from_utf8(priv_key).or(Err(Error::Signature))?),
|
||||||
|
..NewBlog::default()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -461,21 +455,18 @@ pub(crate) mod tests {
|
||||||
"This is a small blog".to_owned(),
|
"This is a small blog".to_owned(),
|
||||||
Instance::get_local(conn).unwrap().id
|
Instance::get_local(conn).unwrap().id
|
||||||
).unwrap()).unwrap();
|
).unwrap()).unwrap();
|
||||||
blog1.update_boxes(conn).unwrap();
|
|
||||||
let blog2 = Blog::insert(conn, NewBlog::new_local(
|
let blog2 = Blog::insert(conn, NewBlog::new_local(
|
||||||
"MyBlog".to_owned(),
|
"MyBlog".to_owned(),
|
||||||
"My blog".to_owned(),
|
"My blog".to_owned(),
|
||||||
"Welcome to my blog".to_owned(),
|
"Welcome to my blog".to_owned(),
|
||||||
Instance::get_local(conn).unwrap().id
|
Instance::get_local(conn).unwrap().id
|
||||||
).unwrap()).unwrap();
|
).unwrap()).unwrap();
|
||||||
blog2.update_boxes(conn).unwrap();
|
|
||||||
let blog3 = Blog::insert(conn, NewBlog::new_local(
|
let blog3 = Blog::insert(conn, NewBlog::new_local(
|
||||||
"WhyILikePlume".to_owned(),
|
"WhyILikePlume".to_owned(),
|
||||||
"Why I like Plume".to_owned(),
|
"Why I like Plume".to_owned(),
|
||||||
"In this blog I will explay you why I like Plume so much".to_owned(),
|
"In this blog I will explay you why I like Plume so much".to_owned(),
|
||||||
Instance::get_local(conn).unwrap().id
|
Instance::get_local(conn).unwrap().id
|
||||||
).unwrap()).unwrap();
|
).unwrap()).unwrap();
|
||||||
blog3.update_boxes(conn).unwrap();
|
|
||||||
|
|
||||||
BlogAuthor::insert(
|
BlogAuthor::insert(
|
||||||
conn,
|
conn,
|
||||||
|
@ -553,7 +544,6 @@ pub(crate) mod tests {
|
||||||
Instance::get_local(conn).unwrap().id,
|
Instance::get_local(conn).unwrap().id,
|
||||||
).unwrap(),
|
).unwrap(),
|
||||||
).unwrap();
|
).unwrap();
|
||||||
b1.update_boxes(conn).unwrap();
|
|
||||||
let b2 = Blog::insert(
|
let b2 = Blog::insert(
|
||||||
conn,
|
conn,
|
||||||
NewBlog::new_local(
|
NewBlog::new_local(
|
||||||
|
@ -563,7 +553,6 @@ pub(crate) mod tests {
|
||||||
Instance::get_local(conn).unwrap().id
|
Instance::get_local(conn).unwrap().id
|
||||||
).unwrap(),
|
).unwrap(),
|
||||||
).unwrap();
|
).unwrap();
|
||||||
b2.update_boxes(conn).unwrap();
|
|
||||||
let blog = vec![ b1, b2 ];
|
let blog = vec![ b1, b2 ];
|
||||||
|
|
||||||
BlogAuthor::insert(
|
BlogAuthor::insert(
|
||||||
|
@ -719,7 +708,6 @@ pub(crate) mod tests {
|
||||||
Instance::get_local(conn).unwrap().id,
|
Instance::get_local(conn).unwrap().id,
|
||||||
).unwrap(),
|
).unwrap(),
|
||||||
).unwrap();
|
).unwrap();
|
||||||
b1.update_boxes(conn).unwrap();
|
|
||||||
let b2 = Blog::insert(
|
let b2 = Blog::insert(
|
||||||
conn,
|
conn,
|
||||||
NewBlog::new_local(
|
NewBlog::new_local(
|
||||||
|
@ -729,7 +717,6 @@ pub(crate) mod tests {
|
||||||
Instance::get_local(conn).unwrap().id,
|
Instance::get_local(conn).unwrap().id,
|
||||||
).unwrap(),
|
).unwrap(),
|
||||||
).unwrap();
|
).unwrap();
|
||||||
b2.update_boxes(conn).unwrap();
|
|
||||||
let blog = vec![ b1, b2 ];
|
let blog = vec![ b1, b2 ];
|
||||||
|
|
||||||
BlogAuthor::insert(
|
BlogAuthor::insert(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use activitypub::{activity::{Create, Delete}, link, object::{Note, Tombstone}};
|
use activitypub::{activity::{Create, Delete}, link, object::{Note, Tombstone}};
|
||||||
use chrono::{self, NaiveDateTime};
|
use chrono::{self, NaiveDateTime};
|
||||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
@ -20,7 +20,7 @@ use schema::comments;
|
||||||
use users::User;
|
use users::User;
|
||||||
use {Connection, Error, Result};
|
use {Connection, Error, Result};
|
||||||
|
|
||||||
#[derive(Queryable, Identifiable, Serialize, Clone)]
|
#[derive(Queryable, Identifiable, Serialize, Clone, AsChangeset)]
|
||||||
pub struct Comment {
|
pub struct Comment {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub content: SafeString,
|
pub content: SafeString,
|
||||||
|
@ -48,7 +48,13 @@ pub struct NewComment {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Comment {
|
impl Comment {
|
||||||
insert!(comments, NewComment);
|
insert!(comments, NewComment, |inserted, conn| {
|
||||||
|
if inserted.ap_url.is_none() {
|
||||||
|
inserted.ap_url = Some(format!("{}comment/{}", inserted.get_post(conn)?.ap_url, inserted.id));
|
||||||
|
let _: Comment = inserted.save_changes(conn)?;
|
||||||
|
}
|
||||||
|
Ok(inserted)
|
||||||
|
});
|
||||||
get!(comments);
|
get!(comments);
|
||||||
list_by!(comments, list_by_post, post_id as i32);
|
list_by!(comments, list_by_post, post_id as i32);
|
||||||
find_by!(comments, find_by_ap_url, ap_url as &str);
|
find_by!(comments, find_by_ap_url, ap_url as &str);
|
||||||
|
@ -79,21 +85,6 @@ impl Comment {
|
||||||
.map_err(Error::from)
|
.map_err(Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_ap_url(&self, conn: &Connection) -> Result<Comment> {
|
|
||||||
if self.ap_url.is_none() {
|
|
||||||
diesel::update(self)
|
|
||||||
.set(comments::ap_url.eq(self.compute_id(conn)?))
|
|
||||||
.execute(conn)?;
|
|
||||||
Comment::get(conn, self.id)
|
|
||||||
} else {
|
|
||||||
Ok(self.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn compute_id(&self, conn: &Connection) -> Result<String> {
|
|
||||||
Ok(format!("{}comment/{}", self.get_post(conn)?.ap_url, self.id))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn can_see(&self, conn: &Connection, user: Option<&User>) -> bool {
|
pub fn can_see(&self, conn: &Connection, user: Option<&User>) -> bool {
|
||||||
self.public_visibility ||
|
self.public_visibility ||
|
||||||
user.as_ref().map(|u| CommentSeers::can_see(conn, self, u).unwrap_or(false))
|
user.as_ref().map(|u| CommentSeers::can_see(conn, self, u).unwrap_or(false))
|
||||||
|
@ -117,7 +108,7 @@ impl Comment {
|
||||||
note.object_props
|
note.object_props
|
||||||
.set_in_reply_to_link(Id::new(self.in_response_to_id.map_or_else(
|
.set_in_reply_to_link(Id::new(self.in_response_to_id.map_or_else(
|
||||||
|| Ok(Post::get(conn, self.post_id)?.ap_url),
|
|| Ok(Post::get(conn, self.post_id)?.ap_url),
|
||||||
|id| Ok(Comment::get(conn, id)?.compute_id(conn)?) as Result<String>,
|
|id| Ok(Comment::get(conn, id)?.ap_url.unwrap_or_default()) as Result<String>,
|
||||||
)?))?;
|
)?))?;
|
||||||
note.object_props
|
note.object_props
|
||||||
.set_published_string(chrono::Utc::now().to_rfc3339())?;
|
.set_published_string(chrono::Utc::now().to_rfc3339())?;
|
||||||
|
|
|
@ -3,7 +3,7 @@ use activitypub::{
|
||||||
actor::Person,
|
actor::Person,
|
||||||
Actor,
|
Actor,
|
||||||
};
|
};
|
||||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
|
||||||
|
|
||||||
use blogs::Blog;
|
use blogs::Blog;
|
||||||
use notifications::*;
|
use notifications::*;
|
||||||
|
@ -17,7 +17,7 @@ use schema::follows;
|
||||||
use users::User;
|
use users::User;
|
||||||
use {ap_url, Connection, BASE_URL, Error, Result};
|
use {ap_url, Connection, BASE_URL, Error, Result};
|
||||||
|
|
||||||
#[derive(Clone, Queryable, Identifiable, Associations)]
|
#[derive(Clone, Queryable, Identifiable, Associations, AsChangeset)]
|
||||||
#[belongs_to(User, foreign_key = "following_id")]
|
#[belongs_to(User, foreign_key = "following_id")]
|
||||||
pub struct Follow {
|
pub struct Follow {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
|
@ -35,7 +35,14 @@ pub struct NewFollow {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Follow {
|
impl Follow {
|
||||||
insert!(follows, NewFollow);
|
insert!(follows, NewFollow, |inserted, conn| {
|
||||||
|
if inserted.ap_url.is_empty() {
|
||||||
|
inserted.ap_url = ap_url(&format!("{}/follows/{}", *BASE_URL, inserted.id));
|
||||||
|
inserted.save_changes(conn).map_err(Error::from)
|
||||||
|
} else {
|
||||||
|
Ok(inserted)
|
||||||
|
}
|
||||||
|
});
|
||||||
get!(follows);
|
get!(follows);
|
||||||
find_by!(follows, find_by_ap_url, ap_url as &str);
|
find_by!(follows, find_by_ap_url, ap_url as &str);
|
||||||
|
|
||||||
|
@ -200,3 +207,33 @@ impl IntoId for Follow {
|
||||||
Id::new(self.ap_url)
|
Id::new(self.ap_url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use diesel::Connection;
|
||||||
|
use super::*;
|
||||||
|
use tests::db;
|
||||||
|
use users::tests as user_tests;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_id() {
|
||||||
|
let conn = db();
|
||||||
|
conn.test_transaction::<_, (), _>(|| {
|
||||||
|
let users = user_tests::fill_database(&conn);
|
||||||
|
let follow = Follow::insert(&conn, NewFollow {
|
||||||
|
follower_id: users[0].id,
|
||||||
|
following_id: users[1].id,
|
||||||
|
ap_url: String::new(),
|
||||||
|
}).expect("Couldn't insert new follow");
|
||||||
|
assert_eq!(follow.ap_url, format!("https://{}/follows/{}", *BASE_URL, follow.id));
|
||||||
|
|
||||||
|
let follow = Follow::insert(&conn, NewFollow {
|
||||||
|
follower_id: users[1].id,
|
||||||
|
following_id: users[0].id,
|
||||||
|
ap_url: String::from("https://some.url/"),
|
||||||
|
}).expect("Couldn't insert new follow");
|
||||||
|
assert_eq!(follow.ap_url, String::from("https://some.url/"));
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -235,13 +235,19 @@ macro_rules! get {
|
||||||
/// ```
|
/// ```
|
||||||
macro_rules! insert {
|
macro_rules! insert {
|
||||||
($table:ident, $from:ident) => {
|
($table:ident, $from:ident) => {
|
||||||
|
insert!($table, $from, |x, _conn| { Ok(x) });
|
||||||
|
};
|
||||||
|
($table:ident, $from:ident, |$val:ident, $conn:ident | $after:block) => {
|
||||||
last!($table);
|
last!($table);
|
||||||
|
|
||||||
pub fn insert(conn: &crate::Connection, new: $from) -> Result<Self> {
|
pub fn insert(conn: &crate::Connection, new: $from) -> Result<Self> {
|
||||||
diesel::insert_into($table::table)
|
diesel::insert_into($table::table)
|
||||||
.values(new)
|
.values(new)
|
||||||
.execute(conn)?;
|
.execute(conn)?;
|
||||||
Self::last(conn)
|
#[allow(unused_mut)]
|
||||||
|
let mut $val = Self::last(conn)?;
|
||||||
|
let $conn = conn;
|
||||||
|
$after
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use activitypub::{
|
||||||
};
|
};
|
||||||
use canapi::{Error as ApiError, Provider};
|
use canapi::{Error as ApiError, Provider};
|
||||||
use chrono::{NaiveDateTime, TimeZone, Utc};
|
use chrono::{NaiveDateTime, TimeZone, Utc};
|
||||||
use diesel::{self, BelongingToDsl, ExpressionMethods, QueryDsl, RunQueryDsl};
|
use diesel::{self, BelongingToDsl, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
|
||||||
use heck::{CamelCase, KebabCase};
|
use heck::{CamelCase, KebabCase};
|
||||||
use scheduled_thread_pool::ScheduledThreadPool as Worker;
|
use scheduled_thread_pool::ScheduledThreadPool as Worker;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
@ -198,7 +198,6 @@ impl<'a> Provider<(&'a Connection, &'a Worker, &'a Searcher, Option<i32>)> for P
|
||||||
source: query.source.expect("Post API::create: no source error"),
|
source: query.source.expect("Post API::create: no source error"),
|
||||||
cover_id: query.cover_id,
|
cover_id: query.cover_id,
|
||||||
}, search).map_err(|_| ApiError::NotFound("Creation error".into()))?;
|
}, search).map_err(|_| ApiError::NotFound("Creation error".into()))?;
|
||||||
post.update_ap_url(conn).map_err(|_| ApiError::NotFound("Error setting ActivityPub URLs".into()))?;;
|
|
||||||
|
|
||||||
PostAuthor::insert(conn, NewPostAuthor {
|
PostAuthor::insert(conn, NewPostAuthor {
|
||||||
author_id: author.id,
|
author_id: author.id,
|
||||||
|
@ -265,7 +264,17 @@ impl Post {
|
||||||
diesel::insert_into(posts::table)
|
diesel::insert_into(posts::table)
|
||||||
.values(new)
|
.values(new)
|
||||||
.execute(conn)?;
|
.execute(conn)?;
|
||||||
let post = Self::last(conn)?;
|
let mut post = Self::last(conn)?;
|
||||||
|
if post.ap_url.is_empty() {
|
||||||
|
post.ap_url = ap_url(&format!(
|
||||||
|
"{}/~/{}/{}/",
|
||||||
|
*BASE_URL,
|
||||||
|
post.get_blog(conn)?.get_fqn(conn),
|
||||||
|
post.slug
|
||||||
|
));
|
||||||
|
let _: Post = post.save_changes(conn)?;
|
||||||
|
}
|
||||||
|
|
||||||
searcher.add_document(conn, &post)?;
|
searcher.add_document(conn, &post)?;
|
||||||
Ok(post)
|
Ok(post)
|
||||||
}
|
}
|
||||||
|
@ -278,7 +287,6 @@ impl Post {
|
||||||
Ok(post)
|
Ok(post)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn list_by_tag(conn: &Connection, tag: String, (min, max): (i32, i32)) -> Result<Vec<Post>> {
|
pub fn list_by_tag(conn: &Connection, tag: String, (min, max): (i32, i32)) -> Result<Vec<Post>> {
|
||||||
use schema::tags;
|
use schema::tags;
|
||||||
|
|
||||||
|
@ -505,17 +513,6 @@ impl Post {
|
||||||
.map_err(Error::from)
|
.map_err(Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_ap_url(&self, conn: &Connection) -> Result<Post> {
|
|
||||||
if self.ap_url.is_empty() {
|
|
||||||
diesel::update(self)
|
|
||||||
.set(posts::ap_url.eq(self.compute_id(conn)?))
|
|
||||||
.execute(conn)?;
|
|
||||||
Post::get(conn, self.id)
|
|
||||||
} else {
|
|
||||||
Ok(self.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_receivers_urls(&self, conn: &Connection) -> Result<Vec<String>> {
|
pub fn get_receivers_urls(&self, conn: &Connection) -> Result<Vec<String>> {
|
||||||
let followers = self
|
let followers = self
|
||||||
.get_authors(conn)?
|
.get_authors(conn)?
|
||||||
|
@ -856,15 +853,6 @@ impl Post {
|
||||||
Ok(format!("/~/{}/{}", blog.get_fqn(conn), self.slug))
|
Ok(format!("/~/{}/{}", blog.get_fqn(conn), self.slug))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_id(&self, conn: &Connection) -> Result<String> {
|
|
||||||
Ok(ap_url(&format!(
|
|
||||||
"{}/~/{}/{}/",
|
|
||||||
BASE_URL.as_str(),
|
|
||||||
self.get_blog(conn)?.get_fqn(conn),
|
|
||||||
self.slug
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cover_url(&self, conn: &Connection) -> Option<String> {
|
pub fn cover_url(&self, conn: &Connection) -> Option<String> {
|
||||||
self.cover_id.and_then(|i| Media::get(conn, i).ok()).and_then(|c| c.url(conn).ok())
|
self.cover_id.and_then(|i| Media::get(conn, i).ok()).and_then(|c| c.url(conn).ok())
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use activitypub::{
|
||||||
};
|
};
|
||||||
use bcrypt;
|
use bcrypt;
|
||||||
use chrono::{NaiveDateTime, Utc};
|
use chrono::{NaiveDateTime, Utc};
|
||||||
use diesel::{self, BelongingToDsl, ExpressionMethods, QueryDsl, RunQueryDsl};
|
use diesel::{self, BelongingToDsl, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
|
||||||
use openssl::{
|
use openssl::{
|
||||||
hash::MessageDigest,
|
hash::MessageDigest,
|
||||||
pkey::{PKey, Private},
|
pkey::{PKey, Private},
|
||||||
|
@ -44,7 +44,7 @@ use {ap_url, Connection, BASE_URL, USE_HTTPS, Error, Result};
|
||||||
|
|
||||||
pub type CustomPerson = CustomObject<ApSignature, Person>;
|
pub type CustomPerson = CustomObject<ApSignature, Person>;
|
||||||
|
|
||||||
#[derive(Queryable, Identifiable, Serialize, Deserialize, Clone, Debug)]
|
#[derive(Queryable, Identifiable, Serialize, Deserialize, Clone, Debug, AsChangeset)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
|
@ -66,7 +66,7 @@ pub struct User {
|
||||||
pub last_fetched_date: NaiveDateTime,
|
pub last_fetched_date: NaiveDateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable)]
|
#[derive(Default, Insertable)]
|
||||||
#[table_name = "users"]
|
#[table_name = "users"]
|
||||||
pub struct NewUser {
|
pub struct NewUser {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
|
@ -90,7 +90,50 @@ pub const AUTH_COOKIE: &str = "user_id";
|
||||||
const USER_PREFIX: &str = "@";
|
const USER_PREFIX: &str = "@";
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
insert!(users, NewUser);
|
insert!(users, NewUser, |inserted, conn| {
|
||||||
|
let instance = inserted.get_instance(conn)?;
|
||||||
|
if inserted.outbox_url.is_empty() {
|
||||||
|
inserted.outbox_url = instance.compute_box(
|
||||||
|
USER_PREFIX,
|
||||||
|
&inserted.username,
|
||||||
|
"outbox",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if inserted.inbox_url.is_empty() {
|
||||||
|
inserted.inbox_url = instance.compute_box(
|
||||||
|
USER_PREFIX,
|
||||||
|
&inserted.username,
|
||||||
|
"inbox",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if inserted.ap_url.is_empty() {
|
||||||
|
inserted.ap_url = instance.compute_box(
|
||||||
|
USER_PREFIX,
|
||||||
|
&inserted.username,
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if inserted.shared_inbox_url.is_none() {
|
||||||
|
inserted.shared_inbox_url = Some(ap_url(&format!(
|
||||||
|
"{}/inbox",
|
||||||
|
Instance::get_local(conn)?
|
||||||
|
.public_domain
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if inserted.followers_endpoint.is_empty() {
|
||||||
|
inserted.followers_endpoint = instance.compute_box(
|
||||||
|
USER_PREFIX,
|
||||||
|
&inserted.username,
|
||||||
|
"followers",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
inserted.save_changes(conn).map_err(Error::from)
|
||||||
|
});
|
||||||
get!(users);
|
get!(users);
|
||||||
find_by!(users, find_by_email, email as &str);
|
find_by!(users, find_by_email, email as &str);
|
||||||
find_by!(users, find_by_name, username as &str, instance_id as i32);
|
find_by!(users, find_by_name, username as &str, instance_id as i32);
|
||||||
|
@ -392,57 +435,6 @@ impl User {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_boxes(&self, conn: &Connection) -> Result<()> {
|
|
||||||
let instance = self.get_instance(conn)?;
|
|
||||||
if self.outbox_url.is_empty() {
|
|
||||||
diesel::update(self)
|
|
||||||
.set(users::outbox_url.eq(instance.compute_box(
|
|
||||||
USER_PREFIX,
|
|
||||||
&self.username,
|
|
||||||
"outbox",
|
|
||||||
)))
|
|
||||||
.execute(conn)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.inbox_url.is_empty() {
|
|
||||||
diesel::update(self)
|
|
||||||
.set(users::inbox_url.eq(instance.compute_box(
|
|
||||||
USER_PREFIX,
|
|
||||||
&self.username,
|
|
||||||
"inbox",
|
|
||||||
)))
|
|
||||||
.execute(conn)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.ap_url.is_empty() {
|
|
||||||
diesel::update(self)
|
|
||||||
.set(users::ap_url.eq(instance.compute_box(USER_PREFIX, &self.username, "")))
|
|
||||||
.execute(conn)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.shared_inbox_url.is_none() {
|
|
||||||
diesel::update(self)
|
|
||||||
.set(users::shared_inbox_url.eq(ap_url(&format!(
|
|
||||||
"{}/inbox",
|
|
||||||
Instance::get_local(conn)?
|
|
||||||
.public_domain
|
|
||||||
))))
|
|
||||||
.execute(conn)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.followers_endpoint.is_empty() {
|
|
||||||
diesel::update(self)
|
|
||||||
.set(users::followers_endpoint.eq(instance.compute_box(
|
|
||||||
USER_PREFIX,
|
|
||||||
&self.username,
|
|
||||||
"followers",
|
|
||||||
)))
|
|
||||||
.execute(conn)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_local_page(conn: &Connection, (min, max): (i32, i32)) -> Result<Vec<User>> {
|
pub fn get_local_page(conn: &Connection, (min, max): (i32, i32)) -> Result<Vec<User>> {
|
||||||
users::table
|
users::table
|
||||||
.filter(users::instance_id.eq(Instance::get_local(conn)?.id))
|
.filter(users::instance_id.eq(Instance::get_local(conn)?.id))
|
||||||
|
@ -917,22 +909,15 @@ impl NewUser {
|
||||||
NewUser {
|
NewUser {
|
||||||
username,
|
username,
|
||||||
display_name,
|
display_name,
|
||||||
outbox_url: String::from(""),
|
|
||||||
inbox_url: String::from(""),
|
|
||||||
is_admin,
|
is_admin,
|
||||||
summary: SafeString::new(summary),
|
summary: SafeString::new(summary),
|
||||||
email: Some(email),
|
email: Some(email),
|
||||||
hashed_password: Some(password),
|
hashed_password: Some(password),
|
||||||
instance_id: Instance::get_local(conn)?.id,
|
instance_id: Instance::get_local(conn)?.id,
|
||||||
ap_url: String::from(""),
|
ap_url: String::new(),
|
||||||
public_key: String::from_utf8(pub_key)
|
public_key: String::from_utf8(pub_key).or(Err(Error::Signature))?,
|
||||||
.expect("NewUser::new_local: public key error"),
|
private_key: Some(String::from_utf8(priv_key).or(Err(Error::Signature))?),
|
||||||
private_key: Some(
|
..NewUser::default()
|
||||||
String::from_utf8(priv_key).expect("NewUser::new_local: private key error"),
|
|
||||||
),
|
|
||||||
shared_inbox_url: None,
|
|
||||||
followers_endpoint: String::from(""),
|
|
||||||
avatar_id: None,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -958,7 +943,6 @@ pub(crate) mod tests {
|
||||||
"admin@example.com".to_owned(),
|
"admin@example.com".to_owned(),
|
||||||
"invalid_admin_password".to_owned(),
|
"invalid_admin_password".to_owned(),
|
||||||
).unwrap();
|
).unwrap();
|
||||||
admin.update_boxes(conn).unwrap();
|
|
||||||
let user = NewUser::new_local(
|
let user = NewUser::new_local(
|
||||||
conn,
|
conn,
|
||||||
"user".to_owned(),
|
"user".to_owned(),
|
||||||
|
@ -968,7 +952,6 @@ pub(crate) mod tests {
|
||||||
"user@example.com".to_owned(),
|
"user@example.com".to_owned(),
|
||||||
"invalid_user_password".to_owned(),
|
"invalid_user_password".to_owned(),
|
||||||
).unwrap();
|
).unwrap();
|
||||||
user.update_boxes(conn).unwrap();
|
|
||||||
let other = NewUser::new_local(
|
let other = NewUser::new_local(
|
||||||
conn,
|
conn,
|
||||||
"other".to_owned(),
|
"other".to_owned(),
|
||||||
|
@ -978,7 +961,6 @@ pub(crate) mod tests {
|
||||||
"other@example.com".to_owned(),
|
"other@example.com".to_owned(),
|
||||||
"invalid_other_password".to_owned(),
|
"invalid_other_password".to_owned(),
|
||||||
).unwrap();
|
).unwrap();
|
||||||
other.update_boxes(conn).unwrap();
|
|
||||||
vec![ admin, user, other ]
|
vec![ admin, user, other ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -996,7 +978,6 @@ pub(crate) mod tests {
|
||||||
"test@example.com".to_owned(),
|
"test@example.com".to_owned(),
|
||||||
User::hash_pass("test_password").unwrap(),
|
User::hash_pass("test_password").unwrap(),
|
||||||
).unwrap();
|
).unwrap();
|
||||||
test_user.update_boxes(conn).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
test_user.id,
|
test_user.id,
|
||||||
|
@ -1097,7 +1078,6 @@ pub(crate) mod tests {
|
||||||
"test@example.com".to_owned(),
|
"test@example.com".to_owned(),
|
||||||
User::hash_pass("test_password").unwrap(),
|
User::hash_pass("test_password").unwrap(),
|
||||||
).unwrap();
|
).unwrap();
|
||||||
test_user.update_boxes(conn).unwrap();
|
|
||||||
|
|
||||||
assert!(test_user.auth("test_password"));
|
assert!(test_user.auth("test_password"));
|
||||||
assert!(!test_user.auth("other_password"));
|
assert!(!test_user.auth("other_password"));
|
||||||
|
|
|
@ -40,7 +40,7 @@ msgstr ""
|
||||||
msgid "You need to be logged in order to create a new blog"
|
msgid "You need to be logged in order to create a new blog"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/blogs.rs:136
|
# src/routes/blogs.rs:135
|
||||||
msgid "You are not allowed to delete this blog."
|
msgid "You are not allowed to delete this blog."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -92,15 +92,15 @@ msgstr ""
|
||||||
msgid "Sorry, but the link expired. Try again"
|
msgid "Sorry, but the link expired. Try again"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/user.rs:131
|
# src/routes/user.rs:126
|
||||||
msgid "You need to be logged in order to access your dashboard"
|
msgid "You need to be logged in order to access your dashboard"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/user.rs:164
|
# src/routes/user.rs:159
|
||||||
msgid "You need to be logged in order to subscribe to someone"
|
msgid "You need to be logged in order to subscribe to someone"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/user.rs:245
|
# src/routes/user.rs:240
|
||||||
msgid "You need to be logged in order to edit your profile"
|
msgid "You need to be logged in order to edit your profile"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,6 @@ pub fn create(conn: DbConn, form: LenientForm<NewBlogForm>, user: User, intl: I1
|
||||||
String::from(""),
|
String::from(""),
|
||||||
Instance::get_local(&*conn).expect("blog::create: instance error").id
|
Instance::get_local(&*conn).expect("blog::create: instance error").id
|
||||||
).expect("blog::create: new local error")).expect("blog::create: error");
|
).expect("blog::create: new local error")).expect("blog::create: error");
|
||||||
blog.update_boxes(&*conn).expect("blog::create: insert error");
|
|
||||||
|
|
||||||
BlogAuthor::insert(&*conn, NewBlogAuthor {
|
BlogAuthor::insert(&*conn, NewBlogAuthor {
|
||||||
blog_id: blog.id,
|
blog_id: blog.id,
|
||||||
|
|
|
@ -53,7 +53,7 @@ pub fn create(blog_name: String, slug: String, form: LenientForm<NewCommentForm>
|
||||||
sensitive: !form.warning.is_empty(),
|
sensitive: !form.warning.is_empty(),
|
||||||
spoiler_text: form.warning.clone(),
|
spoiler_text: form.warning.clone(),
|
||||||
public_visibility: true
|
public_visibility: true
|
||||||
}).expect("comments::create: insert error").update_ap_url(&*conn).expect("comments::create: update ap url error");
|
}).expect("comments::create: insert error");
|
||||||
let new_comment = comm.create_activity(&*conn).expect("comments::create: activity error");
|
let new_comment = comm.create_activity(&*conn).expect("comments::create: activity error");
|
||||||
|
|
||||||
// save mentions
|
// save mentions
|
||||||
|
|
|
@ -227,7 +227,6 @@ pub fn update(blog: String, slug: String, user: User, cl: ContentLen, form: Leni
|
||||||
post.license = form.license.clone();
|
post.license = form.license.clone();
|
||||||
post.cover_id = form.cover;
|
post.cover_id = form.cover;
|
||||||
post.update(&*conn, &searcher).expect("post::update: update error");;
|
post.update(&*conn, &searcher).expect("post::update: update error");;
|
||||||
let post = post.update_ap_url(&*conn).expect("post::update: update ap url error");
|
|
||||||
|
|
||||||
if post.published {
|
if post.published {
|
||||||
post.update_mentions(&conn, mentions.into_iter().filter_map(|m| Mention::build_activity(&conn, &m).ok()).collect())
|
post.update_mentions(&conn, mentions.into_iter().filter_map(|m| Mention::build_activity(&conn, &m).ok()).collect())
|
||||||
|
@ -338,7 +337,7 @@ pub fn create(blog_name: String, form: LenientForm<NewPostForm>, user: User, cl:
|
||||||
},
|
},
|
||||||
&searcher,
|
&searcher,
|
||||||
).expect("post::create: post save error");
|
).expect("post::create: post save error");
|
||||||
let post = post.update_ap_url(&*conn).expect("post::create: update ap url error");
|
|
||||||
PostAuthor::insert(&*conn, NewPostAuthor {
|
PostAuthor::insert(&*conn, NewPostAuthor {
|
||||||
post_id: post.id,
|
post_id: post.id,
|
||||||
author_id: user.id
|
author_id: user.id
|
||||||
|
|
|
@ -78,18 +78,13 @@ pub fn details(
|
||||||
let user_clone = user.clone();
|
let user_clone = user.clone();
|
||||||
worker.execute(move || {
|
worker.execute(move || {
|
||||||
for user_id in user_clone.fetch_followers_ids().expect("Remote user: fetching followers error") {
|
for user_id in user_clone.fetch_followers_ids().expect("Remote user: fetching followers error") {
|
||||||
let follower =
|
let follower = User::from_url(&*fetch_followers_conn, &user_id).expect("user::details: Couldn't fetch follower");
|
||||||
User::find_by_ap_url(&*fetch_followers_conn, &user_id)
|
|
||||||
.unwrap_or_else(|_| {
|
|
||||||
User::fetch_from_url(&*fetch_followers_conn, &user_id)
|
|
||||||
.expect("user::details: Couldn't fetch follower")
|
|
||||||
});
|
|
||||||
follows::Follow::insert(
|
follows::Follow::insert(
|
||||||
&*fetch_followers_conn,
|
&*fetch_followers_conn,
|
||||||
follows::NewFollow {
|
follows::NewFollow {
|
||||||
follower_id: follower.id,
|
follower_id: follower.id,
|
||||||
following_id: user_clone.id,
|
following_id: user_clone.id,
|
||||||
ap_url: format!("{}/follow/{}", follower.ap_url, user_clone.ap_url),
|
ap_url: String::new(),
|
||||||
},
|
},
|
||||||
).expect("Couldn't save follower for remote user");
|
).expect("Couldn't save follower for remote user");
|
||||||
}
|
}
|
||||||
|
@ -147,7 +142,7 @@ pub fn follow(name: String, conn: DbConn, user: User, worker: Worker) -> Result<
|
||||||
follows::NewFollow {
|
follows::NewFollow {
|
||||||
follower_id: user.id,
|
follower_id: user.id,
|
||||||
following_id: target.id,
|
following_id: target.id,
|
||||||
ap_url: format!("{}/follow/{}", user.ap_url, target.ap_url),
|
ap_url: String::new(),
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
f.notify(&*conn)?;
|
f.notify(&*conn)?;
|
||||||
|
@ -359,7 +354,7 @@ pub fn create(conn: DbConn, form: LenientForm<NewUserForm>, intl: I18n) -> Resul
|
||||||
"",
|
"",
|
||||||
form.email.to_string(),
|
form.email.to_string(),
|
||||||
User::hash_pass(&form.password).map_err(to_validation)?,
|
User::hash_pass(&form.password).map_err(to_validation)?,
|
||||||
).and_then(|u| u.update_boxes(&*conn)).map_err(to_validation)?;
|
).map_err(to_validation)?;
|
||||||
Ok(Redirect::to(uri!(super::session::new: m = _)))
|
Ok(Redirect::to(uri!(super::session::new: m = _)))
|
||||||
})
|
})
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
|
|
Loading…
Reference in a new issue