mirror of
https://git.joinplu.me/Plume/Plume.git
synced 2024-11-22 19:41:03 +00:00
Merge pull request 'Close #888 Extract DbConn from PlumeRocket' (#889) from extract-dbconn into main
Reviewed-on: https://git.joinplu.me/Plume/Plume/pulls/889
This commit is contained in:
commit
e68ca1151d
36 changed files with 1174 additions and 1063 deletions
|
@ -87,7 +87,7 @@ impl BlocklistedEmail {
|
|||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::{instance::tests as instance_tests, tests::rockets, Connection as Conn};
|
||||
use crate::{instance::tests as instance_tests, tests::db, Connection as Conn};
|
||||
use diesel::Connection;
|
||||
|
||||
pub(crate) fn fill_database(conn: &Conn) -> Vec<BlocklistedEmail> {
|
||||
|
@ -106,29 +106,28 @@ pub(crate) mod tests {
|
|||
}
|
||||
#[test]
|
||||
fn test_match() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let various = fill_database(conn);
|
||||
let various = fill_database(&conn);
|
||||
let match1 = "user1@bad-actor.com";
|
||||
let match2 = "spammer@lax-administration.com";
|
||||
let no_match = "happy-user@lax-administration.com";
|
||||
assert_eq!(
|
||||
BlocklistedEmail::matches_blocklist(conn, match1)
|
||||
BlocklistedEmail::matches_blocklist(&conn, match1)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.id,
|
||||
various[0].id
|
||||
);
|
||||
assert_eq!(
|
||||
BlocklistedEmail::matches_blocklist(conn, match2)
|
||||
BlocklistedEmail::matches_blocklist(&conn, match2)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.id,
|
||||
various[1].id
|
||||
);
|
||||
assert_eq!(
|
||||
BlocklistedEmail::matches_blocklist(conn, no_match)
|
||||
BlocklistedEmail::matches_blocklist(&conn, no_match)
|
||||
.unwrap()
|
||||
.is_none(),
|
||||
true
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
ap_url, instance::*, medias::Media, posts::Post, safe_string::SafeString, schema::blogs,
|
||||
users::User, Connection, Error, PlumeRocket, Result, CONFIG, ITEMS_PER_PAGE,
|
||||
ap_url, db_conn::DbConn, instance::*, medias::Media, posts::Post, safe_string::SafeString,
|
||||
schema::blogs, users::User, Connection, Error, PlumeRocket, Result, CONFIG, ITEMS_PER_PAGE,
|
||||
};
|
||||
use activitypub::{
|
||||
actor::Group,
|
||||
|
@ -131,25 +131,25 @@ impl Blog {
|
|||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
pub fn find_by_fqn(c: &PlumeRocket, fqn: &str) -> Result<Blog> {
|
||||
pub fn find_by_fqn(conn: &DbConn, fqn: &str) -> Result<Blog> {
|
||||
let from_db = blogs::table
|
||||
.filter(blogs::fqn.eq(fqn))
|
||||
.first(&*c.conn)
|
||||
.first(&**conn)
|
||||
.optional()?;
|
||||
if let Some(from_db) = from_db {
|
||||
Ok(from_db)
|
||||
} else {
|
||||
Blog::fetch_from_webfinger(c, fqn)
|
||||
Blog::fetch_from_webfinger(conn, fqn)
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_from_webfinger(c: &PlumeRocket, acct: &str) -> Result<Blog> {
|
||||
fn fetch_from_webfinger(conn: &DbConn, acct: &str) -> Result<Blog> {
|
||||
resolve_with_prefix(Prefix::Group, acct.to_owned(), true)?
|
||||
.links
|
||||
.into_iter()
|
||||
.find(|l| l.mime_type == Some(String::from("application/activity+json")))
|
||||
.ok_or(Error::Webfinger)
|
||||
.and_then(|l| Blog::from_id(c, &l.href?, None, CONFIG.proxy()).map_err(|(_, e)| e))
|
||||
.and_then(|l| Blog::from_id(conn, &l.href?, None, CONFIG.proxy()).map_err(|(_, e)| e))
|
||||
}
|
||||
|
||||
pub fn to_activity(&self, conn: &Connection) -> Result<CustomGroup> {
|
||||
|
@ -334,20 +334,20 @@ impl IntoId for Blog {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromId<PlumeRocket> for Blog {
|
||||
impl FromId<DbConn> for Blog {
|
||||
type Error = Error;
|
||||
type Object = CustomGroup;
|
||||
|
||||
fn from_db(c: &PlumeRocket, id: &str) -> Result<Self> {
|
||||
Self::find_by_ap_url(&c.conn, id)
|
||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
||||
Self::find_by_ap_url(&conn, id)
|
||||
}
|
||||
|
||||
fn from_activity(c: &PlumeRocket, acct: CustomGroup) -> Result<Self> {
|
||||
fn from_activity(conn: &DbConn, acct: CustomGroup) -> Result<Self> {
|
||||
let url = Url::parse(&acct.object.object_props.id_string()?)?;
|
||||
let inst = url.host_str()?;
|
||||
let instance = Instance::find_by_domain(&c.conn, inst).or_else(|_| {
|
||||
let instance = Instance::find_by_domain(conn, inst).or_else(|_| {
|
||||
Instance::insert(
|
||||
&c.conn,
|
||||
conn,
|
||||
NewInstance {
|
||||
public_domain: inst.to_owned(),
|
||||
name: inst.to_owned(),
|
||||
|
@ -370,9 +370,9 @@ impl FromId<PlumeRocket> for Blog {
|
|||
.and_then(|icon| {
|
||||
let owner = icon.object_props.attributed_to_link::<Id>().ok()?;
|
||||
Media::save_remote(
|
||||
&c.conn,
|
||||
conn,
|
||||
icon.object_props.url_string().ok()?,
|
||||
&User::from_id(c, &owner, None, CONFIG.proxy()).ok()?,
|
||||
&User::from_id(conn, &owner, None, CONFIG.proxy()).ok()?,
|
||||
)
|
||||
.ok()
|
||||
})
|
||||
|
@ -386,9 +386,9 @@ impl FromId<PlumeRocket> for Blog {
|
|||
.and_then(|banner| {
|
||||
let owner = banner.object_props.attributed_to_link::<Id>().ok()?;
|
||||
Media::save_remote(
|
||||
&c.conn,
|
||||
conn,
|
||||
banner.object_props.url_string().ok()?,
|
||||
&User::from_id(c, &owner, None, CONFIG.proxy()).ok()?,
|
||||
&User::from_id(conn, &owner, None, CONFIG.proxy()).ok()?,
|
||||
)
|
||||
.ok()
|
||||
})
|
||||
|
@ -400,7 +400,7 @@ impl FromId<PlumeRocket> for Blog {
|
|||
}
|
||||
|
||||
Blog::insert(
|
||||
&c.conn,
|
||||
conn,
|
||||
NewBlog {
|
||||
actor_id: name.clone(),
|
||||
title: acct.object.object_props.name_string().unwrap_or(name),
|
||||
|
@ -496,12 +496,8 @@ impl NewBlog {
|
|||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
blog_authors::*,
|
||||
instance::tests as instance_tests,
|
||||
medias::NewMedia,
|
||||
tests::{db, rockets},
|
||||
users::tests as usersTests,
|
||||
Connection as Conn,
|
||||
blog_authors::*, instance::tests as instance_tests, medias::NewMedia, tests::db,
|
||||
users::tests as usersTests, Connection as Conn,
|
||||
};
|
||||
use diesel::Connection;
|
||||
|
||||
|
@ -588,10 +584,10 @@ pub(crate) mod tests {
|
|||
fn get_instance() {
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
fill_database(conn);
|
||||
fill_database(&conn);
|
||||
|
||||
let blog = Blog::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewBlog::new_local(
|
||||
"SomeName".to_owned(),
|
||||
"Some name".to_owned(),
|
||||
|
@ -603,7 +599,7 @@ pub(crate) mod tests {
|
|||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
blog.get_instance(conn).unwrap().id,
|
||||
blog.get_instance(&conn).unwrap().id,
|
||||
Instance::get_local().unwrap().id
|
||||
);
|
||||
// TODO add tests for remote instance
|
||||
|
@ -615,10 +611,10 @@ pub(crate) mod tests {
|
|||
fn authors() {
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (user, _) = fill_database(conn);
|
||||
let (user, _) = fill_database(&conn);
|
||||
|
||||
let b1 = Blog::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewBlog::new_local(
|
||||
"SomeName".to_owned(),
|
||||
"Some name".to_owned(),
|
||||
|
@ -629,7 +625,7 @@ pub(crate) mod tests {
|
|||
)
|
||||
.unwrap();
|
||||
let b2 = Blog::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewBlog::new_local(
|
||||
"Blog".to_owned(),
|
||||
"Blog".to_owned(),
|
||||
|
@ -642,7 +638,7 @@ pub(crate) mod tests {
|
|||
let blog = vec![b1, b2];
|
||||
|
||||
BlogAuthor::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewBlogAuthor {
|
||||
blog_id: blog[0].id,
|
||||
author_id: user[0].id,
|
||||
|
@ -652,7 +648,7 @@ pub(crate) mod tests {
|
|||
.unwrap();
|
||||
|
||||
BlogAuthor::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewBlogAuthor {
|
||||
blog_id: blog[0].id,
|
||||
author_id: user[1].id,
|
||||
|
@ -662,7 +658,7 @@ pub(crate) mod tests {
|
|||
.unwrap();
|
||||
|
||||
BlogAuthor::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewBlogAuthor {
|
||||
blog_id: blog[1].id,
|
||||
author_id: user[0].id,
|
||||
|
@ -672,39 +668,39 @@ pub(crate) mod tests {
|
|||
.unwrap();
|
||||
|
||||
assert!(blog[0]
|
||||
.list_authors(conn)
|
||||
.list_authors(&conn)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|a| a.id == user[0].id));
|
||||
assert!(blog[0]
|
||||
.list_authors(conn)
|
||||
.list_authors(&conn)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|a| a.id == user[1].id));
|
||||
assert!(blog[1]
|
||||
.list_authors(conn)
|
||||
.list_authors(&conn)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|a| a.id == user[0].id));
|
||||
assert!(!blog[1]
|
||||
.list_authors(conn)
|
||||
.list_authors(&conn)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|a| a.id == user[1].id));
|
||||
|
||||
assert!(Blog::find_for_author(conn, &user[0])
|
||||
assert!(Blog::find_for_author(&conn, &user[0])
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|b| b.id == blog[0].id));
|
||||
assert!(Blog::find_for_author(conn, &user[1])
|
||||
assert!(Blog::find_for_author(&conn, &user[1])
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|b| b.id == blog[0].id));
|
||||
assert!(Blog::find_for_author(conn, &user[0])
|
||||
assert!(Blog::find_for_author(&conn, &user[0])
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|b| b.id == blog[1].id));
|
||||
assert!(!Blog::find_for_author(conn, &user[1])
|
||||
assert!(!Blog::find_for_author(&conn, &user[1])
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|b| b.id == blog[1].id));
|
||||
|
@ -714,13 +710,12 @@ pub(crate) mod tests {
|
|||
|
||||
#[test]
|
||||
fn find_local() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
fill_database(conn);
|
||||
fill_database(&conn);
|
||||
|
||||
let blog = Blog::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewBlog::new_local(
|
||||
"SomeName".to_owned(),
|
||||
"Some name".to_owned(),
|
||||
|
@ -731,7 +726,7 @@ pub(crate) mod tests {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(Blog::find_by_fqn(&r, "SomeName").unwrap().id, blog.id);
|
||||
assert_eq!(Blog::find_by_fqn(&conn, "SomeName").unwrap().id, blog.id);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
@ -740,10 +735,10 @@ pub(crate) mod tests {
|
|||
fn get_fqn() {
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
fill_database(conn);
|
||||
fill_database(&conn);
|
||||
|
||||
let blog = Blog::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewBlog::new_local(
|
||||
"SomeName".to_owned(),
|
||||
"Some name".to_owned(),
|
||||
|
@ -763,10 +758,10 @@ pub(crate) mod tests {
|
|||
fn delete() {
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (_, blogs) = fill_database(conn);
|
||||
let (_, blogs) = fill_database(&conn);
|
||||
|
||||
blogs[0].delete(conn).unwrap();
|
||||
assert!(Blog::get(conn, blogs[0].id).is_err());
|
||||
blogs[0].delete(&conn).unwrap();
|
||||
assert!(Blog::get(&conn, blogs[0].id).is_err());
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
@ -775,10 +770,10 @@ pub(crate) mod tests {
|
|||
fn delete_via_user() {
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (user, _) = fill_database(conn);
|
||||
let (user, _) = fill_database(&conn);
|
||||
|
||||
let b1 = Blog::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewBlog::new_local(
|
||||
"SomeName".to_owned(),
|
||||
"Some name".to_owned(),
|
||||
|
@ -789,7 +784,7 @@ pub(crate) mod tests {
|
|||
)
|
||||
.unwrap();
|
||||
let b2 = Blog::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewBlog::new_local(
|
||||
"Blog".to_owned(),
|
||||
"Blog".to_owned(),
|
||||
|
@ -802,7 +797,7 @@ pub(crate) mod tests {
|
|||
let blog = vec![b1, b2];
|
||||
|
||||
BlogAuthor::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewBlogAuthor {
|
||||
blog_id: blog[0].id,
|
||||
author_id: user[0].id,
|
||||
|
@ -812,7 +807,7 @@ pub(crate) mod tests {
|
|||
.unwrap();
|
||||
|
||||
BlogAuthor::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewBlogAuthor {
|
||||
blog_id: blog[0].id,
|
||||
author_id: user[1].id,
|
||||
|
@ -822,7 +817,7 @@ pub(crate) mod tests {
|
|||
.unwrap();
|
||||
|
||||
BlogAuthor::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewBlogAuthor {
|
||||
blog_id: blog[1].id,
|
||||
author_id: user[0].id,
|
||||
|
@ -831,24 +826,23 @@ pub(crate) mod tests {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
user[0].delete(conn).unwrap();
|
||||
assert!(Blog::get(conn, blog[0].id).is_ok());
|
||||
assert!(Blog::get(conn, blog[1].id).is_err());
|
||||
user[1].delete(conn).unwrap();
|
||||
assert!(Blog::get(conn, blog[0].id).is_err());
|
||||
user[0].delete(&conn).unwrap();
|
||||
assert!(Blog::get(&conn, blog[0].id).is_ok());
|
||||
assert!(Blog::get(&conn, blog[1].id).is_err());
|
||||
user[1].delete(&conn).unwrap();
|
||||
assert!(Blog::get(&conn, blog[0].id).is_err());
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn self_federation() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (users, mut blogs) = fill_database(conn);
|
||||
let (users, mut blogs) = fill_database(&conn);
|
||||
blogs[0].icon_id = Some(
|
||||
Media::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewMedia {
|
||||
file_path: "aaa.png".into(),
|
||||
alt_text: String::new(),
|
||||
|
@ -864,7 +858,7 @@ pub(crate) mod tests {
|
|||
);
|
||||
blogs[0].banner_id = Some(
|
||||
Media::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewMedia {
|
||||
file_path: "bbb.png".into(),
|
||||
alt_text: String::new(),
|
||||
|
@ -878,11 +872,11 @@ pub(crate) mod tests {
|
|||
.unwrap()
|
||||
.id,
|
||||
);
|
||||
let _: Blog = blogs[0].save_changes(conn).unwrap();
|
||||
let _: Blog = blogs[0].save_changes(&**conn).unwrap();
|
||||
|
||||
let ap_repr = blogs[0].to_activity(conn).unwrap();
|
||||
blogs[0].delete(conn).unwrap();
|
||||
let blog = Blog::from_activity(&r, ap_repr).unwrap();
|
||||
let ap_repr = blogs[0].to_activity(&conn).unwrap();
|
||||
blogs[0].delete(&conn).unwrap();
|
||||
let blog = Blog::from_activity(&conn, ap_repr).unwrap();
|
||||
|
||||
assert_eq!(blog.actor_id, blogs[0].actor_id);
|
||||
assert_eq!(blog.title, blogs[0].title);
|
||||
|
@ -894,8 +888,8 @@ pub(crate) mod tests {
|
|||
assert_eq!(blog.public_key, blogs[0].public_key);
|
||||
assert_eq!(blog.fqn, blogs[0].fqn);
|
||||
assert_eq!(blog.summary_html, blogs[0].summary_html);
|
||||
assert_eq!(blog.icon_url(conn), blogs[0].icon_url(conn));
|
||||
assert_eq!(blog.banner_url(conn), blogs[0].banner_url(conn));
|
||||
assert_eq!(blog.icon_url(&conn), blogs[0].icon_url(&conn));
|
||||
assert_eq!(blog.banner_url(&conn), blogs[0].banner_url(&conn));
|
||||
|
||||
Ok(())
|
||||
})
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{
|
||||
comment_seers::{CommentSeers, NewCommentSeers},
|
||||
db_conn::DbConn,
|
||||
instance::Instance,
|
||||
medias::Media,
|
||||
mentions::Mention,
|
||||
|
@ -8,7 +9,7 @@ use crate::{
|
|||
safe_string::SafeString,
|
||||
schema::comments,
|
||||
users::User,
|
||||
Connection, Error, PlumeRocket, Result, CONFIG,
|
||||
Connection, Error, Result, CONFIG,
|
||||
};
|
||||
use activitypub::{
|
||||
activity::{Create, Delete},
|
||||
|
@ -104,13 +105,13 @@ impl Comment {
|
|||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn to_activity(&self, c: &PlumeRocket) -> Result<Note> {
|
||||
let author = User::get(&c.conn, self.author_id)?;
|
||||
pub fn to_activity(&self, conn: &DbConn) -> Result<Note> {
|
||||
let author = User::get(conn, self.author_id)?;
|
||||
let (html, mentions, _hashtags) = utils::md_to_html(
|
||||
self.content.get().as_ref(),
|
||||
Some(&Instance::get_local()?.public_domain),
|
||||
true,
|
||||
Some(Media::get_media_processor(&c.conn, vec![&author])),
|
||||
Some(Media::get_media_processor(conn, vec![&author])),
|
||||
);
|
||||
|
||||
let mut note = Note::default();
|
||||
|
@ -123,8 +124,8 @@ impl Comment {
|
|||
note.object_props.set_content_string(html)?;
|
||||
note.object_props
|
||||
.set_in_reply_to_link(Id::new(self.in_response_to_id.map_or_else(
|
||||
|| Ok(Post::get(&c.conn, self.post_id)?.ap_url),
|
||||
|id| Ok(Comment::get(&c.conn, id)?.ap_url.unwrap_or_default()) as Result<String>,
|
||||
|| Ok(Post::get(conn, self.post_id)?.ap_url),
|
||||
|id| Ok(Comment::get(conn, id)?.ap_url.unwrap_or_default()) as Result<String>,
|
||||
)?))?;
|
||||
note.object_props
|
||||
.set_published_string(chrono::Utc::now().to_rfc3339())?;
|
||||
|
@ -133,16 +134,16 @@ impl Comment {
|
|||
note.object_props.set_tag_link_vec(
|
||||
mentions
|
||||
.into_iter()
|
||||
.filter_map(|m| Mention::build_activity(c, &m).ok())
|
||||
.filter_map(|m| Mention::build_activity(conn, &m).ok())
|
||||
.collect::<Vec<link::Mention>>(),
|
||||
)?;
|
||||
Ok(note)
|
||||
}
|
||||
|
||||
pub fn create_activity(&self, c: &PlumeRocket) -> Result<Create> {
|
||||
let author = User::get(&c.conn, self.author_id)?;
|
||||
pub fn create_activity(&self, conn: &DbConn) -> Result<Create> {
|
||||
let author = User::get(&conn, self.author_id)?;
|
||||
|
||||
let note = self.to_activity(c)?;
|
||||
let note = self.to_activity(conn)?;
|
||||
let mut act = Create::default();
|
||||
act.create_props.set_actor_link(author.into_id())?;
|
||||
act.create_props.set_object_object(note.clone())?;
|
||||
|
@ -151,7 +152,7 @@ impl Comment {
|
|||
act.object_props
|
||||
.set_to_link_vec(note.object_props.to_link_vec::<Id>()?)?;
|
||||
act.object_props
|
||||
.set_cc_link_vec(vec![Id::new(self.get_author(&c.conn)?.followers_endpoint)])?;
|
||||
.set_cc_link_vec(vec![Id::new(self.get_author(&conn)?.followers_endpoint)])?;
|
||||
Ok(act)
|
||||
}
|
||||
|
||||
|
@ -193,16 +194,15 @@ impl Comment {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromId<PlumeRocket> for Comment {
|
||||
impl FromId<DbConn> for Comment {
|
||||
type Error = Error;
|
||||
type Object = Note;
|
||||
|
||||
fn from_db(c: &PlumeRocket, id: &str) -> Result<Self> {
|
||||
Self::find_by_ap_url(&c.conn, id)
|
||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
||||
Self::find_by_ap_url(conn, id)
|
||||
}
|
||||
|
||||
fn from_activity(c: &PlumeRocket, note: Note) -> Result<Self> {
|
||||
let conn = &*c.conn;
|
||||
fn from_activity(conn: &DbConn, note: Note) -> Result<Self> {
|
||||
let comm = {
|
||||
let previous_url = note.object_props.in_reply_to.as_ref()?.as_str()?;
|
||||
let previous_comment = Comment::find_by_ap_url(conn, previous_url);
|
||||
|
@ -235,7 +235,7 @@ impl FromId<PlumeRocket> for Comment {
|
|||
Ok(Post::find_by_ap_url(conn, previous_url)?.id) as Result<i32>
|
||||
})?,
|
||||
author_id: User::from_id(
|
||||
c,
|
||||
conn,
|
||||
¬e.object_props.attributed_to_link::<Id>()?,
|
||||
None,
|
||||
CONFIG.proxy(),
|
||||
|
@ -296,7 +296,7 @@ impl FromId<PlumeRocket> for Comment {
|
|||
.collect::<HashSet<_>>() // remove duplicates (don't do a query more than once)
|
||||
.into_iter()
|
||||
.map(|v| {
|
||||
if let Ok(user) = User::from_id(c, &v, None, CONFIG.proxy()) {
|
||||
if let Ok(user) = User::from_id(conn, &v, None, CONFIG.proxy()) {
|
||||
vec![user]
|
||||
} else {
|
||||
vec![] // TODO try to fetch collection
|
||||
|
@ -322,41 +322,41 @@ impl FromId<PlumeRocket> for Comment {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsObject<User, Create, &PlumeRocket> for Comment {
|
||||
impl AsObject<User, Create, &DbConn> for Comment {
|
||||
type Error = Error;
|
||||
type Output = Self;
|
||||
|
||||
fn activity(self, _c: &PlumeRocket, _actor: User, _id: &str) -> Result<Self> {
|
||||
fn activity(self, _conn: &DbConn, _actor: User, _id: &str) -> Result<Self> {
|
||||
// The actual creation takes place in the FromId impl
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsObject<User, Delete, &PlumeRocket> for Comment {
|
||||
impl AsObject<User, Delete, &DbConn> for Comment {
|
||||
type Error = Error;
|
||||
type Output = ();
|
||||
|
||||
fn activity(self, c: &PlumeRocket, actor: User, _id: &str) -> Result<()> {
|
||||
fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> {
|
||||
if self.author_id != actor.id {
|
||||
return Err(Error::Unauthorized);
|
||||
}
|
||||
|
||||
for m in Mention::list_for_comment(&c.conn, self.id)? {
|
||||
for n in Notification::find_for_mention(&c.conn, &m)? {
|
||||
n.delete(&c.conn)?;
|
||||
for m in Mention::list_for_comment(conn, self.id)? {
|
||||
for n in Notification::find_for_mention(conn, &m)? {
|
||||
n.delete(conn)?;
|
||||
}
|
||||
m.delete(&c.conn)?;
|
||||
m.delete(conn)?;
|
||||
}
|
||||
|
||||
for n in Notification::find_for_comment(&c.conn, &self)? {
|
||||
n.delete(&c.conn)?;
|
||||
for n in Notification::find_for_comment(&conn, &self)? {
|
||||
n.delete(&**conn)?;
|
||||
}
|
||||
|
||||
diesel::update(comments::table)
|
||||
.filter(comments::in_response_to_id.eq(self.id))
|
||||
.set(comments::in_response_to_id.eq(self.in_response_to_id))
|
||||
.execute(&*c.conn)?;
|
||||
diesel::delete(&self).execute(&*c.conn)?;
|
||||
.execute(&**conn)?;
|
||||
diesel::delete(&self).execute(&**conn)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -392,17 +392,16 @@ mod tests {
|
|||
use super::*;
|
||||
use crate::inbox::{inbox, tests::fill_database, InboxResult};
|
||||
use crate::safe_string::SafeString;
|
||||
use crate::tests::rockets;
|
||||
use crate::tests::db;
|
||||
use diesel::Connection;
|
||||
|
||||
// creates a post, get it's Create activity, delete the post,
|
||||
// "send" the Create to the inbox, and check it works
|
||||
#[test]
|
||||
fn self_federation() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (posts, users, _) = fill_database(&r);
|
||||
let (posts, users, _) = fill_database(&conn);
|
||||
|
||||
let original_comm = Comment::insert(
|
||||
conn,
|
||||
|
@ -418,14 +417,14 @@ mod tests {
|
|||
},
|
||||
)
|
||||
.unwrap();
|
||||
let act = original_comm.create_activity(&r).unwrap();
|
||||
let act = original_comm.create_activity(&conn).unwrap();
|
||||
inbox(
|
||||
&r,
|
||||
serde_json::to_value(original_comm.build_delete(conn).unwrap()).unwrap(),
|
||||
&conn,
|
||||
serde_json::to_value(original_comm.build_delete(&conn).unwrap()).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
match inbox(&r, serde_json::to_value(act).unwrap()).unwrap() {
|
||||
match inbox(&conn, serde_json::to_value(act).unwrap()).unwrap() {
|
||||
InboxResult::Commented(c) => {
|
||||
// TODO: one is HTML, the other markdown: assert_eq!(c.content, original_comm.content);
|
||||
assert_eq!(c.in_response_to_id, original_comm.in_response_to_id);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
ap_url, notifications::*, schema::follows, users::User, Connection, Error, PlumeRocket, Result,
|
||||
CONFIG,
|
||||
ap_url, db_conn::DbConn, notifications::*, schema::follows, users::User, Connection, Error,
|
||||
Result, CONFIG,
|
||||
};
|
||||
use activitypub::activity::{Accept, Follow as FollowAct, Undo};
|
||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
|
||||
|
@ -141,11 +141,11 @@ impl Follow {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsObject<User, FollowAct, &PlumeRocket> for User {
|
||||
impl AsObject<User, FollowAct, &DbConn> for User {
|
||||
type Error = Error;
|
||||
type Output = Follow;
|
||||
|
||||
fn activity(self, c: &PlumeRocket, actor: User, id: &str) -> Result<Follow> {
|
||||
fn activity(self, conn: &DbConn, actor: User, id: &str) -> Result<Follow> {
|
||||
// Mastodon (at least) requires the full Follow object when accepting it,
|
||||
// so we rebuilt it here
|
||||
let mut follow = FollowAct::default();
|
||||
|
@ -153,21 +153,21 @@ impl AsObject<User, FollowAct, &PlumeRocket> for User {
|
|||
follow
|
||||
.follow_props
|
||||
.set_actor_link::<Id>(actor.clone().into_id())?;
|
||||
Follow::accept_follow(&c.conn, &actor, &self, follow, actor.id, self.id)
|
||||
Follow::accept_follow(conn, &actor, &self, follow, actor.id, self.id)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromId<PlumeRocket> for Follow {
|
||||
impl FromId<DbConn> for Follow {
|
||||
type Error = Error;
|
||||
type Object = FollowAct;
|
||||
|
||||
fn from_db(c: &PlumeRocket, id: &str) -> Result<Self> {
|
||||
Follow::find_by_ap_url(&c.conn, id)
|
||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
||||
Follow::find_by_ap_url(conn, id)
|
||||
}
|
||||
|
||||
fn from_activity(c: &PlumeRocket, follow: FollowAct) -> Result<Self> {
|
||||
fn from_activity(conn: &DbConn, follow: FollowAct) -> Result<Self> {
|
||||
let actor = User::from_id(
|
||||
c,
|
||||
conn,
|
||||
&follow.follow_props.actor_link::<Id>()?,
|
||||
None,
|
||||
CONFIG.proxy(),
|
||||
|
@ -175,28 +175,28 @@ impl FromId<PlumeRocket> for Follow {
|
|||
.map_err(|(_, e)| e)?;
|
||||
|
||||
let target = User::from_id(
|
||||
c,
|
||||
conn,
|
||||
&follow.follow_props.object_link::<Id>()?,
|
||||
None,
|
||||
CONFIG.proxy(),
|
||||
)
|
||||
.map_err(|(_, e)| e)?;
|
||||
Follow::accept_follow(&c.conn, &actor, &target, follow, actor.id, target.id)
|
||||
Follow::accept_follow(conn, &actor, &target, follow, actor.id, target.id)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsObject<User, Undo, &PlumeRocket> for Follow {
|
||||
impl AsObject<User, Undo, &DbConn> for Follow {
|
||||
type Error = Error;
|
||||
type Output = ();
|
||||
|
||||
fn activity(self, c: &PlumeRocket, actor: User, _id: &str) -> Result<()> {
|
||||
let conn = &*c.conn;
|
||||
fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> {
|
||||
let conn = conn;
|
||||
if self.follower_id == actor.id {
|
||||
diesel::delete(&self).execute(conn)?;
|
||||
diesel::delete(&self).execute(&**conn)?;
|
||||
|
||||
// delete associated notification if any
|
||||
if let Ok(notif) = Notification::find(conn, notification_kind::FOLLOW, self.id) {
|
||||
diesel::delete(¬if).execute(conn)?;
|
||||
if let Ok(notif) = Notification::find(&conn, notification_kind::FOLLOW, self.id) {
|
||||
diesel::delete(¬if).execute(&**conn)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -2,11 +2,12 @@ use activitypub::activity::*;
|
|||
|
||||
use crate::{
|
||||
comments::Comment,
|
||||
db_conn::DbConn,
|
||||
follows, likes,
|
||||
posts::{Post, PostUpdate},
|
||||
reshares::Reshare,
|
||||
users::User,
|
||||
Error, PlumeRocket, CONFIG,
|
||||
Error, CONFIG,
|
||||
};
|
||||
use plume_common::activity_pub::inbox::Inbox;
|
||||
|
||||
|
@ -45,8 +46,8 @@ impl_into_inbox_result! {
|
|||
Reshare => Reshared
|
||||
}
|
||||
|
||||
pub fn inbox(ctx: &PlumeRocket, act: serde_json::Value) -> Result<InboxResult, Error> {
|
||||
Inbox::handle(ctx, act)
|
||||
pub fn inbox(conn: &DbConn, act: serde_json::Value) -> Result<InboxResult, Error> {
|
||||
Inbox::handle(conn, act)
|
||||
.with::<User, Announce, Post>(CONFIG.proxy())
|
||||
.with::<User, Create, Comment>(CONFIG.proxy())
|
||||
.with::<User, Create, Post>(CONFIG.proxy())
|
||||
|
@ -66,13 +67,13 @@ pub fn inbox(ctx: &PlumeRocket, act: serde_json::Value) -> Result<InboxResult, E
|
|||
pub(crate) mod tests {
|
||||
use super::InboxResult;
|
||||
use crate::blogs::tests::fill_database as blog_fill_db;
|
||||
use crate::db_conn::DbConn;
|
||||
use crate::safe_string::SafeString;
|
||||
use crate::tests::rockets;
|
||||
use crate::PlumeRocket;
|
||||
use crate::tests::db;
|
||||
use diesel::Connection;
|
||||
|
||||
pub fn fill_database(
|
||||
rockets: &PlumeRocket,
|
||||
conn: &DbConn,
|
||||
) -> (
|
||||
Vec<crate::posts::Post>,
|
||||
Vec<crate::users::User>,
|
||||
|
@ -81,9 +82,9 @@ pub(crate) mod tests {
|
|||
use crate::post_authors::*;
|
||||
use crate::posts::*;
|
||||
|
||||
let (users, blogs) = blog_fill_db(&rockets.conn);
|
||||
let (users, blogs) = blog_fill_db(&conn);
|
||||
let post = Post::insert(
|
||||
&rockets.conn,
|
||||
&conn,
|
||||
NewPost {
|
||||
blog_id: blogs[0].id,
|
||||
slug: "testing".to_owned(),
|
||||
|
@ -101,7 +102,7 @@ pub(crate) mod tests {
|
|||
.unwrap();
|
||||
|
||||
PostAuthor::insert(
|
||||
&rockets.conn,
|
||||
&conn,
|
||||
NewPostAuthor {
|
||||
post_id: post.id,
|
||||
author_id: users[0].id,
|
||||
|
@ -114,10 +115,9 @@ pub(crate) mod tests {
|
|||
|
||||
#[test]
|
||||
fn announce_post() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (posts, users, _) = fill_database(&r);
|
||||
let (posts, users, _) = fill_database(&conn);
|
||||
let act = json!({
|
||||
"id": "https://plu.me/announce/1",
|
||||
"actor": users[0].ap_url,
|
||||
|
@ -125,7 +125,7 @@ pub(crate) mod tests {
|
|||
"type": "Announce",
|
||||
});
|
||||
|
||||
match super::inbox(&r, act).unwrap() {
|
||||
match super::inbox(&conn, act).unwrap() {
|
||||
super::InboxResult::Reshared(r) => {
|
||||
assert_eq!(r.post_id, posts[0].id);
|
||||
assert_eq!(r.user_id, users[0].id);
|
||||
|
@ -139,10 +139,9 @@ pub(crate) mod tests {
|
|||
|
||||
#[test]
|
||||
fn create_comment() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (posts, users, _) = fill_database(&r);
|
||||
let (posts, users, _) = fill_database(&conn);
|
||||
let act = json!({
|
||||
"id": "https://plu.me/comment/1/activity",
|
||||
"actor": users[0].ap_url,
|
||||
|
@ -157,7 +156,7 @@ pub(crate) mod tests {
|
|||
"type": "Create",
|
||||
});
|
||||
|
||||
match super::inbox(&r, act).unwrap() {
|
||||
match super::inbox(&conn, act).unwrap() {
|
||||
super::InboxResult::Commented(c) => {
|
||||
assert_eq!(c.author_id, users[0].id);
|
||||
assert_eq!(c.post_id, posts[0].id);
|
||||
|
@ -173,10 +172,9 @@ pub(crate) mod tests {
|
|||
|
||||
#[test]
|
||||
fn spoof_comment() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (posts, users, _) = fill_database(&r);
|
||||
let (posts, users, _) = fill_database(&conn);
|
||||
let act = json!({
|
||||
"id": "https://plu.me/comment/1/activity",
|
||||
"actor": users[0].ap_url,
|
||||
|
@ -192,7 +190,7 @@ pub(crate) mod tests {
|
|||
});
|
||||
|
||||
assert!(matches!(
|
||||
super::inbox(&r, act.clone()),
|
||||
super::inbox(&conn, act.clone()),
|
||||
Err(super::Error::Inbox(
|
||||
box plume_common::activity_pub::inbox::InboxError::InvalidObject(_),
|
||||
))
|
||||
|
@ -203,10 +201,9 @@ pub(crate) mod tests {
|
|||
|
||||
#[test]
|
||||
fn spoof_comment_by_object_with_id() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (posts, users, _) = fill_database(&r);
|
||||
let (posts, users, _) = fill_database(&conn);
|
||||
let act = json!({
|
||||
"id": "https://plu.me/comment/1/activity",
|
||||
"actor": users[0].ap_url,
|
||||
|
@ -224,7 +221,7 @@ pub(crate) mod tests {
|
|||
});
|
||||
|
||||
assert!(matches!(
|
||||
super::inbox(&r, act.clone()),
|
||||
super::inbox(&conn, act.clone()),
|
||||
Err(super::Error::Inbox(
|
||||
box plume_common::activity_pub::inbox::InboxError::InvalidObject(_),
|
||||
))
|
||||
|
@ -234,10 +231,9 @@ pub(crate) mod tests {
|
|||
}
|
||||
#[test]
|
||||
fn spoof_comment_by_object_without_id() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (posts, users, _) = fill_database(&r);
|
||||
let (posts, users, _) = fill_database(&conn);
|
||||
let act = json!({
|
||||
"id": "https://plu.me/comment/1/activity",
|
||||
"actor": users[0].ap_url,
|
||||
|
@ -253,7 +249,7 @@ pub(crate) mod tests {
|
|||
});
|
||||
|
||||
assert!(matches!(
|
||||
super::inbox(&r, act.clone()),
|
||||
super::inbox(&conn, act.clone()),
|
||||
Err(super::Error::Inbox(
|
||||
box plume_common::activity_pub::inbox::InboxError::InvalidObject(_),
|
||||
))
|
||||
|
@ -264,10 +260,9 @@ pub(crate) mod tests {
|
|||
|
||||
#[test]
|
||||
fn create_post() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (_, users, blogs) = fill_database(&r);
|
||||
let (_, users, blogs) = fill_database(&conn);
|
||||
let act = json!({
|
||||
"id": "https://plu.me/comment/1/activity",
|
||||
"actor": users[0].ap_url,
|
||||
|
@ -288,9 +283,9 @@ pub(crate) mod tests {
|
|||
"type": "Create",
|
||||
});
|
||||
|
||||
match super::inbox(&r, act).unwrap() {
|
||||
match super::inbox(&conn, act).unwrap() {
|
||||
super::InboxResult::Post(p) => {
|
||||
assert!(p.is_author(conn, users[0].id).unwrap());
|
||||
assert!(p.is_author(&conn, users[0].id).unwrap());
|
||||
assert_eq!(p.source, "Hello.".to_owned());
|
||||
assert_eq!(p.blog_id, blogs[0].id);
|
||||
assert_eq!(p.content, SafeString::new("Hello."));
|
||||
|
@ -305,10 +300,9 @@ pub(crate) mod tests {
|
|||
|
||||
#[test]
|
||||
fn spoof_post() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (_, users, blogs) = fill_database(&r);
|
||||
let (_, users, blogs) = fill_database(&conn);
|
||||
let act = json!({
|
||||
"id": "https://plu.me/comment/1/activity",
|
||||
"actor": users[0].ap_url,
|
||||
|
@ -330,7 +324,7 @@ pub(crate) mod tests {
|
|||
});
|
||||
|
||||
assert!(matches!(
|
||||
super::inbox(&r, act.clone()),
|
||||
super::inbox(&conn, act.clone()),
|
||||
Err(super::Error::Inbox(
|
||||
box plume_common::activity_pub::inbox::InboxError::InvalidObject(_),
|
||||
))
|
||||
|
@ -341,10 +335,9 @@ pub(crate) mod tests {
|
|||
|
||||
#[test]
|
||||
fn spoof_post_by_object_with_id() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (_, users, blogs) = fill_database(&r);
|
||||
let (_, users, blogs) = fill_database(&conn);
|
||||
let act = json!({
|
||||
"id": "https://plu.me/comment/1/activity",
|
||||
"actor": users[0].ap_url,
|
||||
|
@ -369,7 +362,7 @@ pub(crate) mod tests {
|
|||
});
|
||||
|
||||
assert!(matches!(
|
||||
super::inbox(&r, act.clone()),
|
||||
super::inbox(&conn, act.clone()),
|
||||
Err(super::Error::Inbox(
|
||||
box plume_common::activity_pub::inbox::InboxError::InvalidObject(_),
|
||||
))
|
||||
|
@ -380,10 +373,9 @@ pub(crate) mod tests {
|
|||
|
||||
#[test]
|
||||
fn spoof_post_by_object_without_id() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (_, users, blogs) = fill_database(&r);
|
||||
let (_, users, blogs) = fill_database(&conn);
|
||||
let act = json!({
|
||||
"id": "https://plu.me/comment/1/activity",
|
||||
"actor": users[0].ap_url,
|
||||
|
@ -405,7 +397,7 @@ pub(crate) mod tests {
|
|||
});
|
||||
|
||||
assert!(matches!(
|
||||
super::inbox(&r, act.clone()),
|
||||
super::inbox(&conn, act.clone()),
|
||||
Err(super::Error::Inbox(
|
||||
box plume_common::activity_pub::inbox::InboxError::InvalidObject(_),
|
||||
))
|
||||
|
@ -418,12 +410,11 @@ pub(crate) mod tests {
|
|||
fn delete_comment() {
|
||||
use crate::comments::*;
|
||||
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (posts, users, _) = fill_database(&r);
|
||||
let (posts, users, _) = fill_database(&conn);
|
||||
Comment::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewComment {
|
||||
content: SafeString::new("My comment"),
|
||||
in_response_to_id: None,
|
||||
|
@ -443,7 +434,7 @@ pub(crate) mod tests {
|
|||
"object": "https://plu.me/comment/1",
|
||||
"type": "Delete",
|
||||
});
|
||||
assert!(super::inbox(&r, fail_act).is_err());
|
||||
assert!(super::inbox(&conn, fail_act).is_err());
|
||||
|
||||
let ok_act = json!({
|
||||
"id": "https://plu.me/comment/1/delete",
|
||||
|
@ -451,17 +442,16 @@ pub(crate) mod tests {
|
|||
"object": "https://plu.me/comment/1",
|
||||
"type": "Delete",
|
||||
});
|
||||
assert!(super::inbox(&r, ok_act).is_ok());
|
||||
assert!(super::inbox(&conn, ok_act).is_ok());
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_post() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (posts, users, _) = fill_database(&r);
|
||||
let (posts, users, _) = fill_database(&conn);
|
||||
|
||||
let fail_act = json!({
|
||||
"id": "https://plu.me/comment/1/delete",
|
||||
|
@ -469,7 +459,7 @@ pub(crate) mod tests {
|
|||
"object": posts[0].ap_url,
|
||||
"type": "Delete",
|
||||
});
|
||||
assert!(super::inbox(&r, fail_act).is_err());
|
||||
assert!(super::inbox(&conn, fail_act).is_err());
|
||||
|
||||
let ok_act = json!({
|
||||
"id": "https://plu.me/comment/1/delete",
|
||||
|
@ -477,17 +467,16 @@ pub(crate) mod tests {
|
|||
"object": posts[0].ap_url,
|
||||
"type": "Delete",
|
||||
});
|
||||
assert!(super::inbox(&r, ok_act).is_ok());
|
||||
assert!(super::inbox(&conn, ok_act).is_ok());
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_user() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (_, users, _) = fill_database(&r);
|
||||
let (_, users, _) = fill_database(&conn);
|
||||
|
||||
let fail_act = json!({
|
||||
"id": "https://plu.me/@/Admin#delete",
|
||||
|
@ -495,7 +484,7 @@ pub(crate) mod tests {
|
|||
"object": users[0].ap_url,
|
||||
"type": "Delete",
|
||||
});
|
||||
assert!(super::inbox(&r, fail_act).is_err());
|
||||
assert!(super::inbox(&conn, fail_act).is_err());
|
||||
|
||||
let ok_act = json!({
|
||||
"id": "https://plu.me/@/Admin#delete",
|
||||
|
@ -503,8 +492,8 @@ pub(crate) mod tests {
|
|||
"object": users[0].ap_url,
|
||||
"type": "Delete",
|
||||
});
|
||||
assert!(super::inbox(&r, ok_act).is_ok());
|
||||
assert!(crate::users::User::get(conn, users[0].id).is_err());
|
||||
assert!(super::inbox(&conn, ok_act).is_ok());
|
||||
assert!(crate::users::User::get(&conn, users[0].id).is_err());
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
@ -512,10 +501,9 @@ pub(crate) mod tests {
|
|||
|
||||
#[test]
|
||||
fn follow() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (_, users, _) = fill_database(&r);
|
||||
let (_, users, _) = fill_database(&conn);
|
||||
|
||||
let act = json!({
|
||||
"id": "https://plu.me/follow/1",
|
||||
|
@ -523,7 +511,7 @@ pub(crate) mod tests {
|
|||
"object": users[1].ap_url,
|
||||
"type": "Follow",
|
||||
});
|
||||
match super::inbox(&r, act).unwrap() {
|
||||
match super::inbox(&conn, act).unwrap() {
|
||||
InboxResult::Followed(f) => {
|
||||
assert_eq!(f.follower_id, users[0].id);
|
||||
assert_eq!(f.following_id, users[1].id);
|
||||
|
@ -537,10 +525,9 @@ pub(crate) mod tests {
|
|||
|
||||
#[test]
|
||||
fn like() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (posts, users, _) = fill_database(&r);
|
||||
let (posts, users, _) = fill_database(&conn);
|
||||
|
||||
let act = json!({
|
||||
"id": "https://plu.me/like/1",
|
||||
|
@ -548,7 +535,7 @@ pub(crate) mod tests {
|
|||
"object": posts[0].ap_url,
|
||||
"type": "Like",
|
||||
});
|
||||
match super::inbox(&r, act).unwrap() {
|
||||
match super::inbox(&conn, act).unwrap() {
|
||||
InboxResult::Liked(l) => {
|
||||
assert_eq!(l.user_id, users[1].id);
|
||||
assert_eq!(l.post_id, posts[0].id);
|
||||
|
@ -564,13 +551,12 @@ pub(crate) mod tests {
|
|||
fn undo_reshare() {
|
||||
use crate::reshares::*;
|
||||
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (posts, users, _) = fill_database(&r);
|
||||
let (posts, users, _) = fill_database(&conn);
|
||||
|
||||
let announce = Reshare::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewReshare {
|
||||
post_id: posts[0].id,
|
||||
user_id: users[1].id,
|
||||
|
@ -585,7 +571,7 @@ pub(crate) mod tests {
|
|||
"object": announce.ap_url,
|
||||
"type": "Undo",
|
||||
});
|
||||
assert!(super::inbox(&r, fail_act).is_err());
|
||||
assert!(super::inbox(&conn, fail_act).is_err());
|
||||
|
||||
let ok_act = json!({
|
||||
"id": "https://plu.me/undo/1",
|
||||
|
@ -593,7 +579,7 @@ pub(crate) mod tests {
|
|||
"object": announce.ap_url,
|
||||
"type": "Undo",
|
||||
});
|
||||
assert!(super::inbox(&r, ok_act).is_ok());
|
||||
assert!(super::inbox(&conn, ok_act).is_ok());
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
@ -602,13 +588,12 @@ pub(crate) mod tests {
|
|||
fn undo_follow() {
|
||||
use crate::follows::*;
|
||||
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (_, users, _) = fill_database(&r);
|
||||
let (_, users, _) = fill_database(&conn);
|
||||
|
||||
let follow = Follow::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewFollow {
|
||||
follower_id: users[0].id,
|
||||
following_id: users[1].id,
|
||||
|
@ -623,7 +608,7 @@ pub(crate) mod tests {
|
|||
"object": follow.ap_url,
|
||||
"type": "Undo",
|
||||
});
|
||||
assert!(super::inbox(&r, fail_act).is_err());
|
||||
assert!(super::inbox(&conn, fail_act).is_err());
|
||||
|
||||
let ok_act = json!({
|
||||
"id": "https://plu.me/undo/1",
|
||||
|
@ -631,7 +616,7 @@ pub(crate) mod tests {
|
|||
"object": follow.ap_url,
|
||||
"type": "Undo",
|
||||
});
|
||||
assert!(super::inbox(&r, ok_act).is_ok());
|
||||
assert!(super::inbox(&conn, ok_act).is_ok());
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
@ -640,13 +625,12 @@ pub(crate) mod tests {
|
|||
fn undo_like() {
|
||||
use crate::likes::*;
|
||||
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (posts, users, _) = fill_database(&r);
|
||||
let (posts, users, _) = fill_database(&conn);
|
||||
|
||||
let like = Like::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewLike {
|
||||
post_id: posts[0].id,
|
||||
user_id: users[1].id,
|
||||
|
@ -661,7 +645,7 @@ pub(crate) mod tests {
|
|||
"object": like.ap_url,
|
||||
"type": "Undo",
|
||||
});
|
||||
assert!(super::inbox(&r, fail_act).is_err());
|
||||
assert!(super::inbox(&conn, fail_act).is_err());
|
||||
|
||||
let ok_act = json!({
|
||||
"id": "https://plu.me/undo/1",
|
||||
|
@ -669,17 +653,16 @@ pub(crate) mod tests {
|
|||
"object": like.ap_url,
|
||||
"type": "Undo",
|
||||
});
|
||||
assert!(super::inbox(&r, ok_act).is_ok());
|
||||
assert!(super::inbox(&conn, ok_act).is_ok());
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_post() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (posts, users, _) = fill_database(&r);
|
||||
let (posts, users, _) = fill_database(&conn);
|
||||
|
||||
let act = json!({
|
||||
"id": "https://plu.me/update/1",
|
||||
|
@ -698,7 +681,7 @@ pub(crate) mod tests {
|
|||
"type": "Update",
|
||||
});
|
||||
|
||||
super::inbox(&r, act).unwrap();
|
||||
super::inbox(&conn, act).unwrap();
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ use once_cell::sync::Lazy;
|
|||
use plume_common::activity_pub::inbox::InboxError;
|
||||
use posts::PostEvent;
|
||||
use riker::actors::{channel, ActorSystem, ChannelRef, SystemBuilder};
|
||||
use users::UserEvent;
|
||||
|
||||
#[cfg(not(any(feature = "sqlite", feature = "postgres")))]
|
||||
compile_error!("Either feature \"sqlite\" or \"postgres\" must be enabled for this crate.");
|
||||
|
@ -40,6 +41,9 @@ pub(crate) static ACTOR_SYS: Lazy<ActorSystem> = Lazy::new(|| {
|
|||
.expect("Failed to create actor system")
|
||||
});
|
||||
|
||||
pub(crate) static USER_CHAN: Lazy<ChannelRef<UserEvent>> =
|
||||
Lazy::new(|| channel("user_events", &*ACTOR_SYS).expect("Failed to create user channel"));
|
||||
|
||||
pub(crate) static POST_CHAN: Lazy<ChannelRef<PostEvent>> =
|
||||
Lazy::new(|| channel("post_events", &*ACTOR_SYS).expect("Failed to create post channel"));
|
||||
|
||||
|
@ -294,12 +298,10 @@ pub fn ap_url(url: &str) -> String {
|
|||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
mod tests {
|
||||
use crate::{db_conn, migrations::IMPORTED_MIGRATIONS, search, Connection as Conn, CONFIG};
|
||||
use crate::{db_conn, migrations::IMPORTED_MIGRATIONS, Connection as Conn, CONFIG};
|
||||
use diesel::r2d2::ConnectionManager;
|
||||
use plume_common::utils::random_hex;
|
||||
use scheduled_thread_pool::ScheduledThreadPool;
|
||||
use std::env::temp_dir;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! part_eq {
|
||||
|
@ -329,15 +331,6 @@ mod tests {
|
|||
pool
|
||||
};
|
||||
}
|
||||
|
||||
pub fn rockets() -> super::PlumeRocket {
|
||||
super::PlumeRocket {
|
||||
conn: db_conn::DbConn((*DB_POOL).get().unwrap()),
|
||||
searcher: Arc::new(search::tests::get_searcher(&CONFIG.search_tokenizers)),
|
||||
worker: Arc::new(ScheduledThreadPool::new(2)),
|
||||
user: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod admin;
|
||||
|
@ -363,6 +356,7 @@ pub mod password_reset_requests;
|
|||
pub mod plume_rocket;
|
||||
pub mod post_authors;
|
||||
pub mod posts;
|
||||
pub mod remote_fetch_actor;
|
||||
pub mod reshares;
|
||||
pub mod safe_string;
|
||||
#[allow(unused_imports)]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
notifications::*, posts::Post, schema::likes, timeline::*, users::User, Connection, Error,
|
||||
PlumeRocket, Result, CONFIG,
|
||||
db_conn::DbConn, notifications::*, posts::Post, schema::likes, timeline::*, users::User,
|
||||
Connection, Error, Result, CONFIG,
|
||||
};
|
||||
use activitypub::activity;
|
||||
use chrono::NaiveDateTime;
|
||||
|
@ -83,40 +83,40 @@ impl Like {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsObject<User, activity::Like, &PlumeRocket> for Post {
|
||||
impl AsObject<User, activity::Like, &DbConn> for Post {
|
||||
type Error = Error;
|
||||
type Output = Like;
|
||||
|
||||
fn activity(self, c: &PlumeRocket, actor: User, id: &str) -> Result<Like> {
|
||||
fn activity(self, conn: &DbConn, actor: User, id: &str) -> Result<Like> {
|
||||
let res = Like::insert(
|
||||
&c.conn,
|
||||
conn,
|
||||
NewLike {
|
||||
post_id: self.id,
|
||||
user_id: actor.id,
|
||||
ap_url: id.to_string(),
|
||||
},
|
||||
)?;
|
||||
res.notify(&c.conn)?;
|
||||
res.notify(conn)?;
|
||||
|
||||
Timeline::add_to_all_timelines(c, &self, Kind::Like(&actor))?;
|
||||
Timeline::add_to_all_timelines(conn, &self, Kind::Like(&actor))?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromId<PlumeRocket> for Like {
|
||||
impl FromId<DbConn> for Like {
|
||||
type Error = Error;
|
||||
type Object = activity::Like;
|
||||
|
||||
fn from_db(c: &PlumeRocket, id: &str) -> Result<Self> {
|
||||
Like::find_by_ap_url(&c.conn, id)
|
||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
||||
Like::find_by_ap_url(conn, id)
|
||||
}
|
||||
|
||||
fn from_activity(c: &PlumeRocket, act: activity::Like) -> Result<Self> {
|
||||
fn from_activity(conn: &DbConn, act: activity::Like) -> Result<Self> {
|
||||
let res = Like::insert(
|
||||
&c.conn,
|
||||
conn,
|
||||
NewLike {
|
||||
post_id: Post::from_id(
|
||||
c,
|
||||
conn,
|
||||
&act.like_props.object_link::<Id>()?,
|
||||
None,
|
||||
CONFIG.proxy(),
|
||||
|
@ -124,7 +124,7 @@ impl FromId<PlumeRocket> for Like {
|
|||
.map_err(|(_, e)| e)?
|
||||
.id,
|
||||
user_id: User::from_id(
|
||||
c,
|
||||
conn,
|
||||
&act.like_props.actor_link::<Id>()?,
|
||||
None,
|
||||
CONFIG.proxy(),
|
||||
|
@ -134,23 +134,22 @@ impl FromId<PlumeRocket> for Like {
|
|||
ap_url: act.object_props.id_string()?,
|
||||
},
|
||||
)?;
|
||||
res.notify(&c.conn)?;
|
||||
res.notify(conn)?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsObject<User, activity::Undo, &PlumeRocket> for Like {
|
||||
impl AsObject<User, activity::Undo, &DbConn> for Like {
|
||||
type Error = Error;
|
||||
type Output = ();
|
||||
|
||||
fn activity(self, c: &PlumeRocket, actor: User, _id: &str) -> Result<()> {
|
||||
let conn = &*c.conn;
|
||||
fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> {
|
||||
if actor.id == self.user_id {
|
||||
diesel::delete(&self).execute(conn)?;
|
||||
diesel::delete(&self).execute(&**conn)?;
|
||||
|
||||
// delete associated notification if any
|
||||
if let Ok(notif) = Notification::find(conn, notification_kind::LIKE, self.id) {
|
||||
diesel::delete(¬if).execute(conn)?;
|
||||
if let Ok(notif) = Notification::find(&conn, notification_kind::LIKE, self.id) {
|
||||
diesel::delete(¬if).execute(&**conn)?;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
ap_url, instance::Instance, safe_string::SafeString, schema::medias, users::User, Connection,
|
||||
Error, PlumeRocket, Result, CONFIG,
|
||||
ap_url, db_conn::DbConn, instance::Instance, safe_string::SafeString, schema::medias,
|
||||
users::User, Connection, Error, Result, CONFIG,
|
||||
};
|
||||
use activitypub::object::Image;
|
||||
use askama_escape::escape;
|
||||
|
@ -196,8 +196,7 @@ impl Media {
|
|||
}
|
||||
|
||||
// TODO: merge with save_remote?
|
||||
pub fn from_activity(c: &PlumeRocket, image: &Image) -> Result<Media> {
|
||||
let conn = &*c.conn;
|
||||
pub fn from_activity(conn: &DbConn, image: &Image) -> Result<Media> {
|
||||
let remote_url = image.object_props.url_string().ok()?;
|
||||
let ext = remote_url
|
||||
.rsplit('.')
|
||||
|
@ -232,7 +231,7 @@ impl Media {
|
|||
sensitive: image.object_props.summary_string().is_ok(),
|
||||
content_warning: image.object_props.summary_string().ok(),
|
||||
owner_id: User::from_id(
|
||||
c,
|
||||
conn,
|
||||
image
|
||||
.object_props
|
||||
.attributed_to_link_vec::<Id>()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
comments::Comment, notifications::*, posts::Post, schema::mentions, users::User, Connection,
|
||||
Error, PlumeRocket, Result,
|
||||
comments::Comment, db_conn::DbConn, notifications::*, posts::Post, schema::mentions,
|
||||
users::User, Connection, Error, Result,
|
||||
};
|
||||
use activitypub::link;
|
||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
|
@ -52,8 +52,8 @@ impl Mention {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn build_activity(c: &PlumeRocket, ment: &str) -> Result<link::Mention> {
|
||||
let user = User::find_by_fqn(c, ment)?;
|
||||
pub fn build_activity(conn: &DbConn, ment: &str) -> Result<link::Mention> {
|
||||
let user = User::find_by_fqn(conn, ment)?;
|
||||
let mut mention = link::Mention::default();
|
||||
mention.link_props.set_href_string(user.ap_url)?;
|
||||
mention.link_props.set_name_string(format!("@{}", ment))?;
|
||||
|
|
|
@ -2,7 +2,7 @@ pub use self::module::PlumeRocket;
|
|||
|
||||
#[cfg(not(test))]
|
||||
mod module {
|
||||
use crate::{db_conn::DbConn, search, users};
|
||||
use crate::{search, users};
|
||||
use rocket::{
|
||||
request::{self, FlashMessage, FromRequest, Request},
|
||||
Outcome, State,
|
||||
|
@ -12,7 +12,6 @@ mod module {
|
|||
|
||||
/// Common context needed by most routes and operations on models
|
||||
pub struct PlumeRocket {
|
||||
pub conn: DbConn,
|
||||
pub intl: rocket_i18n::I18n,
|
||||
pub user: Option<users::User>,
|
||||
pub searcher: Arc<search::Searcher>,
|
||||
|
@ -24,14 +23,12 @@ mod module {
|
|||
type Error = ();
|
||||
|
||||
fn from_request(request: &'a Request<'r>) -> request::Outcome<PlumeRocket, ()> {
|
||||
let conn = request.guard::<DbConn>()?;
|
||||
let intl = request.guard::<rocket_i18n::I18n>()?;
|
||||
let user = request.guard::<users::User>().succeeded();
|
||||
let worker = request.guard::<'_, State<'_, Arc<ScheduledThreadPool>>>()?;
|
||||
let searcher = request.guard::<'_, State<'_, Arc<search::Searcher>>>()?;
|
||||
let flash_msg = request.guard::<FlashMessage<'_, '_>>().succeeded();
|
||||
Outcome::Success(PlumeRocket {
|
||||
conn,
|
||||
intl,
|
||||
user,
|
||||
flash_msg: flash_msg.map(|f| (f.name().into(), f.msg().into())),
|
||||
|
@ -44,7 +41,7 @@ mod module {
|
|||
|
||||
#[cfg(test)]
|
||||
mod module {
|
||||
use crate::{db_conn::DbConn, search, users};
|
||||
use crate::{search, users};
|
||||
use rocket::{
|
||||
request::{self, FromRequest, Request},
|
||||
Outcome, State,
|
||||
|
@ -54,7 +51,6 @@ mod module {
|
|||
|
||||
/// Common context needed by most routes and operations on models
|
||||
pub struct PlumeRocket {
|
||||
pub conn: DbConn,
|
||||
pub user: Option<users::User>,
|
||||
pub searcher: Arc<search::Searcher>,
|
||||
pub worker: Arc<ScheduledThreadPool>,
|
||||
|
@ -64,12 +60,10 @@ mod module {
|
|||
type Error = ();
|
||||
|
||||
fn from_request(request: &'a Request<'r>) -> request::Outcome<PlumeRocket, ()> {
|
||||
let conn = request.guard::<DbConn>()?;
|
||||
let user = request.guard::<users::User>().succeeded();
|
||||
let worker = request.guard::<'_, State<'_, Arc<ScheduledThreadPool>>>()?;
|
||||
let searcher = request.guard::<'_, State<'_, Arc<search::Searcher>>>()?;
|
||||
Outcome::Success(PlumeRocket {
|
||||
conn,
|
||||
user,
|
||||
worker: worker.clone(),
|
||||
searcher: searcher.clone(),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
ap_url, blogs::Blog, instance::Instance, medias::Media, mentions::Mention, post_authors::*,
|
||||
safe_string::SafeString, schema::posts, tags::*, timeline::*, users::User, Connection, Error,
|
||||
PlumeRocket, PostEvent::*, Result, CONFIG, POST_CHAN,
|
||||
ap_url, blogs::Blog, db_conn::DbConn, instance::Instance, medias::Media, mentions::Mention,
|
||||
post_authors::*, safe_string::SafeString, schema::posts, tags::*, timeline::*, users::User,
|
||||
Connection, Error, PostEvent::*, Result, CONFIG, POST_CHAN,
|
||||
};
|
||||
use activitypub::{
|
||||
activity::{Create, Delete, Update},
|
||||
|
@ -606,16 +606,16 @@ impl Post {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromId<PlumeRocket> for Post {
|
||||
impl FromId<DbConn> for Post {
|
||||
type Error = Error;
|
||||
type Object = LicensedArticle;
|
||||
|
||||
fn from_db(c: &PlumeRocket, id: &str) -> Result<Self> {
|
||||
Self::find_by_ap_url(&c.conn, id)
|
||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
||||
Self::find_by_ap_url(conn, id)
|
||||
}
|
||||
|
||||
fn from_activity(c: &PlumeRocket, article: LicensedArticle) -> Result<Self> {
|
||||
let conn = &*c.conn;
|
||||
fn from_activity(conn: &DbConn, article: LicensedArticle) -> Result<Self> {
|
||||
let conn = conn;
|
||||
let license = article.custom_props.license_string().unwrap_or_default();
|
||||
let article = article.object;
|
||||
|
||||
|
@ -625,13 +625,13 @@ impl FromId<PlumeRocket> for Post {
|
|||
.into_iter()
|
||||
.fold((None, vec![]), |(blog, mut authors), link| {
|
||||
let url = link;
|
||||
match User::from_id(&c, &url, None, CONFIG.proxy()) {
|
||||
match User::from_id(conn, &url, None, CONFIG.proxy()) {
|
||||
Ok(u) => {
|
||||
authors.push(u);
|
||||
(blog, authors)
|
||||
}
|
||||
Err(_) => (
|
||||
blog.or_else(|| Blog::from_id(&c, &url, None, CONFIG.proxy()).ok()),
|
||||
blog.or_else(|| Blog::from_id(conn, &url, None, CONFIG.proxy()).ok()),
|
||||
authors,
|
||||
),
|
||||
}
|
||||
|
@ -641,7 +641,7 @@ impl FromId<PlumeRocket> for Post {
|
|||
.object_props
|
||||
.icon_object::<Image>()
|
||||
.ok()
|
||||
.and_then(|img| Media::from_activity(&c, &img).ok().map(|m| m.id));
|
||||
.and_then(|img| Media::from_activity(conn, &img).ok().map(|m| m.id));
|
||||
|
||||
let title = article.object_props.name_string()?;
|
||||
let post = Post::insert(
|
||||
|
@ -701,33 +701,33 @@ impl FromId<PlumeRocket> for Post {
|
|||
}
|
||||
}
|
||||
|
||||
Timeline::add_to_all_timelines(c, &post, Kind::Original)?;
|
||||
Timeline::add_to_all_timelines(conn, &post, Kind::Original)?;
|
||||
|
||||
Ok(post)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsObject<User, Create, &PlumeRocket> for Post {
|
||||
impl AsObject<User, Create, &DbConn> for Post {
|
||||
type Error = Error;
|
||||
type Output = Post;
|
||||
|
||||
fn activity(self, _c: &PlumeRocket, _actor: User, _id: &str) -> Result<Post> {
|
||||
fn activity(self, _conn: &DbConn, _actor: User, _id: &str) -> Result<Post> {
|
||||
// TODO: check that _actor is actually one of the author?
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsObject<User, Delete, &PlumeRocket> for Post {
|
||||
impl AsObject<User, Delete, &DbConn> for Post {
|
||||
type Error = Error;
|
||||
type Output = ();
|
||||
|
||||
fn activity(self, c: &PlumeRocket, actor: User, _id: &str) -> Result<()> {
|
||||
fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> {
|
||||
let can_delete = self
|
||||
.get_authors(&c.conn)?
|
||||
.get_authors(conn)?
|
||||
.into_iter()
|
||||
.any(|a| actor.id == a.id);
|
||||
if can_delete {
|
||||
self.delete(&c.conn).map(|_| ())
|
||||
self.delete(conn).map(|_| ())
|
||||
} else {
|
||||
Err(Error::Unauthorized)
|
||||
}
|
||||
|
@ -745,16 +745,16 @@ pub struct PostUpdate {
|
|||
pub tags: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
impl FromId<PlumeRocket> for PostUpdate {
|
||||
impl FromId<DbConn> for PostUpdate {
|
||||
type Error = Error;
|
||||
type Object = LicensedArticle;
|
||||
|
||||
fn from_db(_: &PlumeRocket, _: &str) -> Result<Self> {
|
||||
fn from_db(_: &DbConn, _: &str) -> Result<Self> {
|
||||
// Always fail because we always want to deserialize the AP object
|
||||
Err(Error::NotFound)
|
||||
}
|
||||
|
||||
fn from_activity(c: &PlumeRocket, updated: LicensedArticle) -> Result<Self> {
|
||||
fn from_activity(conn: &DbConn, updated: LicensedArticle) -> Result<Self> {
|
||||
Ok(PostUpdate {
|
||||
ap_url: updated.object.object_props.id_string()?,
|
||||
title: updated.object.object_props.name_string().ok(),
|
||||
|
@ -765,7 +765,7 @@ impl FromId<PlumeRocket> for PostUpdate {
|
|||
.object_props
|
||||
.icon_object::<Image>()
|
||||
.ok()
|
||||
.and_then(|img| Media::from_activity(&c, &img).ok().map(|m| m.id)),
|
||||
.and_then(|img| Media::from_activity(conn, &img).ok().map(|m| m.id)),
|
||||
source: updated
|
||||
.object
|
||||
.ap_object_props
|
||||
|
@ -778,13 +778,13 @@ impl FromId<PlumeRocket> for PostUpdate {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsObject<User, Update, &PlumeRocket> for PostUpdate {
|
||||
impl AsObject<User, Update, &DbConn> for PostUpdate {
|
||||
type Error = Error;
|
||||
type Output = ();
|
||||
|
||||
fn activity(self, c: &PlumeRocket, actor: User, _id: &str) -> Result<()> {
|
||||
let conn = &*c.conn;
|
||||
let mut post = Post::from_id(c, &self.ap_url, None, CONFIG.proxy()).map_err(|(_, e)| e)?;
|
||||
fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> {
|
||||
let mut post =
|
||||
Post::from_id(conn, &self.ap_url, None, CONFIG.proxy()).map_err(|(_, e)| e)?;
|
||||
|
||||
if !post.is_author(conn, actor.id)? {
|
||||
// TODO: maybe the author was added in the meantime
|
||||
|
@ -880,19 +880,18 @@ mod tests {
|
|||
use super::*;
|
||||
use crate::inbox::{inbox, tests::fill_database, InboxResult};
|
||||
use crate::safe_string::SafeString;
|
||||
use crate::tests::rockets;
|
||||
use crate::tests::db;
|
||||
use diesel::Connection;
|
||||
|
||||
// creates a post, get it's Create activity, delete the post,
|
||||
// "send" the Create to the inbox, and check it works
|
||||
#[test]
|
||||
fn self_federation() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (_, users, blogs) = fill_database(&r);
|
||||
let (_, users, blogs) = fill_database(&conn);
|
||||
let post = Post::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewPost {
|
||||
blog_id: blogs[0].id,
|
||||
slug: "yo".into(),
|
||||
|
@ -909,19 +908,19 @@ mod tests {
|
|||
)
|
||||
.unwrap();
|
||||
PostAuthor::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewPostAuthor {
|
||||
post_id: post.id,
|
||||
author_id: users[0].id,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let create = post.create_activity(conn).unwrap();
|
||||
post.delete(conn).unwrap();
|
||||
let create = post.create_activity(&conn).unwrap();
|
||||
post.delete(&conn).unwrap();
|
||||
|
||||
match inbox(&r, serde_json::to_value(create).unwrap()).unwrap() {
|
||||
match inbox(&conn, serde_json::to_value(create).unwrap()).unwrap() {
|
||||
InboxResult::Post(p) => {
|
||||
assert!(p.is_author(conn, users[0].id).unwrap());
|
||||
assert!(p.is_author(&conn, users[0].id).unwrap());
|
||||
assert_eq!(p.source, "Hello".to_owned());
|
||||
assert_eq!(p.blog_id, blogs[0].id);
|
||||
assert_eq!(p.content, SafeString::new("Hello"));
|
||||
|
|
125
plume-models/src/remote_fetch_actor.rs
Normal file
125
plume-models/src/remote_fetch_actor.rs
Normal file
|
@ -0,0 +1,125 @@
|
|||
use crate::{
|
||||
db_conn::{DbConn, DbPool},
|
||||
follows,
|
||||
posts::{LicensedArticle, Post},
|
||||
users::{User, UserEvent},
|
||||
ACTOR_SYS, CONFIG, USER_CHAN,
|
||||
};
|
||||
use activitypub::activity::Create;
|
||||
use plume_common::activity_pub::inbox::FromId;
|
||||
use riker::actors::{Actor, ActorFactoryArgs, ActorRefFactory, Context, Sender, Subscribe, Tell};
|
||||
use std::sync::Arc;
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
pub struct RemoteFetchActor {
|
||||
conn: DbPool,
|
||||
}
|
||||
|
||||
impl RemoteFetchActor {
|
||||
pub fn init(conn: DbPool) {
|
||||
ACTOR_SYS
|
||||
.actor_of_args::<RemoteFetchActor, _>("remote-fetch", conn)
|
||||
.expect("Failed to initialize remote fetch actor");
|
||||
}
|
||||
}
|
||||
|
||||
impl Actor for RemoteFetchActor {
|
||||
type Msg = UserEvent;
|
||||
|
||||
fn pre_start(&mut self, ctx: &Context<Self::Msg>) {
|
||||
USER_CHAN.tell(
|
||||
Subscribe {
|
||||
actor: Box::new(ctx.myself()),
|
||||
topic: "*".into(),
|
||||
},
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
fn recv(&mut self, _ctx: &Context<Self::Msg>, msg: Self::Msg, _sender: Sender) {
|
||||
use UserEvent::*;
|
||||
|
||||
match msg {
|
||||
RemoteUserFound(user) => match self.conn.get() {
|
||||
Ok(conn) => {
|
||||
let conn = DbConn(conn);
|
||||
// Don't call these functions in parallel
|
||||
// for the case database connections limit is too small
|
||||
fetch_and_cache_articles(&user, &conn);
|
||||
fetch_and_cache_followers(&user, &conn);
|
||||
if user.needs_update() {
|
||||
fetch_and_cache_user(&user, &conn);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("Failed to get database connection");
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ActorFactoryArgs<DbPool> for RemoteFetchActor {
|
||||
fn create_args(conn: DbPool) -> Self {
|
||||
Self { conn }
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_and_cache_articles(user: &Arc<User>, conn: &DbConn) {
|
||||
let create_acts = user.fetch_outbox::<Create>();
|
||||
match create_acts {
|
||||
Ok(create_acts) => {
|
||||
for create_act in create_acts {
|
||||
match create_act.create_props.object_object::<LicensedArticle>() {
|
||||
Ok(article) => {
|
||||
Post::from_activity(conn, article)
|
||||
.expect("Article from remote user couldn't be saved");
|
||||
info!("Fetched article from remote user");
|
||||
}
|
||||
Err(e) => warn!("Error while fetching articles in background: {:?}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to fetch outboxes: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_and_cache_followers(user: &Arc<User>, conn: &DbConn) {
|
||||
let follower_ids = user.fetch_followers_ids();
|
||||
match follower_ids {
|
||||
Ok(user_ids) => {
|
||||
for user_id in user_ids {
|
||||
let follower = User::from_id(conn, &user_id, None, CONFIG.proxy());
|
||||
match follower {
|
||||
Ok(follower) => {
|
||||
let inserted = follows::Follow::insert(
|
||||
conn,
|
||||
follows::NewFollow {
|
||||
follower_id: follower.id,
|
||||
following_id: user.id,
|
||||
ap_url: String::new(),
|
||||
},
|
||||
);
|
||||
if inserted.is_err() {
|
||||
error!("Couldn't save follower for remote user: {:?}", user_id);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Couldn't fetch follower: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to fetch follower: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_and_cache_user(user: &Arc<User>, conn: &DbConn) {
|
||||
if user.refetch(conn).is_err() {
|
||||
error!("Couldn't update user info: {:?}", user);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
notifications::*, posts::Post, schema::reshares, timeline::*, users::User, Connection, Error,
|
||||
PlumeRocket, Result, CONFIG,
|
||||
db_conn::DbConn, notifications::*, posts::Post, schema::reshares, timeline::*, users::User,
|
||||
Connection, Error, Result, CONFIG,
|
||||
};
|
||||
use activitypub::activity::{Announce, Undo};
|
||||
use chrono::NaiveDateTime;
|
||||
|
@ -107,12 +107,12 @@ impl Reshare {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsObject<User, Announce, &PlumeRocket> for Post {
|
||||
impl AsObject<User, Announce, &DbConn> for Post {
|
||||
type Error = Error;
|
||||
type Output = Reshare;
|
||||
|
||||
fn activity(self, c: &PlumeRocket, actor: User, id: &str) -> Result<Reshare> {
|
||||
let conn = &*c.conn;
|
||||
fn activity(self, conn: &DbConn, actor: User, id: &str) -> Result<Reshare> {
|
||||
let conn = conn;
|
||||
let reshare = Reshare::insert(
|
||||
conn,
|
||||
NewReshare {
|
||||
|
@ -123,25 +123,25 @@ impl AsObject<User, Announce, &PlumeRocket> for Post {
|
|||
)?;
|
||||
reshare.notify(conn)?;
|
||||
|
||||
Timeline::add_to_all_timelines(c, &self, Kind::Reshare(&actor))?;
|
||||
Timeline::add_to_all_timelines(conn, &self, Kind::Reshare(&actor))?;
|
||||
Ok(reshare)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromId<PlumeRocket> for Reshare {
|
||||
impl FromId<DbConn> for Reshare {
|
||||
type Error = Error;
|
||||
type Object = Announce;
|
||||
|
||||
fn from_db(c: &PlumeRocket, id: &str) -> Result<Self> {
|
||||
Reshare::find_by_ap_url(&c.conn, id)
|
||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
||||
Reshare::find_by_ap_url(conn, id)
|
||||
}
|
||||
|
||||
fn from_activity(c: &PlumeRocket, act: Announce) -> Result<Self> {
|
||||
fn from_activity(conn: &DbConn, act: Announce) -> Result<Self> {
|
||||
let res = Reshare::insert(
|
||||
&c.conn,
|
||||
conn,
|
||||
NewReshare {
|
||||
post_id: Post::from_id(
|
||||
c,
|
||||
conn,
|
||||
&act.announce_props.object_link::<Id>()?,
|
||||
None,
|
||||
CONFIG.proxy(),
|
||||
|
@ -149,7 +149,7 @@ impl FromId<PlumeRocket> for Reshare {
|
|||
.map_err(|(_, e)| e)?
|
||||
.id,
|
||||
user_id: User::from_id(
|
||||
c,
|
||||
conn,
|
||||
&act.announce_props.actor_link::<Id>()?,
|
||||
None,
|
||||
CONFIG.proxy(),
|
||||
|
@ -159,23 +159,22 @@ impl FromId<PlumeRocket> for Reshare {
|
|||
ap_url: act.object_props.id_string()?,
|
||||
},
|
||||
)?;
|
||||
res.notify(&c.conn)?;
|
||||
res.notify(conn)?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsObject<User, Undo, &PlumeRocket> for Reshare {
|
||||
impl AsObject<User, Undo, &DbConn> for Reshare {
|
||||
type Error = Error;
|
||||
type Output = ();
|
||||
|
||||
fn activity(self, c: &PlumeRocket, actor: User, _id: &str) -> Result<()> {
|
||||
let conn = &*c.conn;
|
||||
fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> {
|
||||
if actor.id == self.user_id {
|
||||
diesel::delete(&self).execute(conn)?;
|
||||
diesel::delete(&self).execute(&**conn)?;
|
||||
|
||||
// delete associated notification if any
|
||||
if let Ok(notif) = Notification::find(&conn, notification_kind::RESHARE, self.id) {
|
||||
diesel::delete(¬if).execute(conn)?;
|
||||
diesel::delete(¬if).execute(&**conn)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use crate::{
|
||||
db_conn::DbConn,
|
||||
lists::List,
|
||||
posts::Post,
|
||||
schema::{posts, timeline, timeline_definition},
|
||||
Connection, Error, PlumeRocket, Result,
|
||||
Connection, Error, Result,
|
||||
};
|
||||
use diesel::{self, BoolExpressionMethods, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
use std::ops::Deref;
|
||||
|
@ -208,14 +209,14 @@ impl Timeline {
|
|||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
pub fn add_to_all_timelines(rocket: &PlumeRocket, post: &Post, kind: Kind<'_>) -> Result<()> {
|
||||
pub fn add_to_all_timelines(conn: &DbConn, post: &Post, kind: Kind<'_>) -> Result<()> {
|
||||
let timelines = timeline_definition::table
|
||||
.load::<Self>(rocket.conn.deref())
|
||||
.load::<Self>(conn.deref())
|
||||
.map_err(Error::from)?;
|
||||
|
||||
for t in timelines {
|
||||
if t.matches(rocket, post, kind)? {
|
||||
t.add_post(&rocket.conn, post)?;
|
||||
if t.matches(conn, post, kind)? {
|
||||
t.add_post(conn, post)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -231,9 +232,9 @@ impl Timeline {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn matches(&self, rocket: &PlumeRocket, post: &Post, kind: Kind<'_>) -> Result<bool> {
|
||||
pub fn matches(&self, conn: &DbConn, post: &Post, kind: Kind<'_>) -> Result<bool> {
|
||||
let query = TimelineQuery::parse(&self.query)?;
|
||||
query.matches(rocket, self, post, kind)
|
||||
query.matches(conn, self, post, kind)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,7 +249,7 @@ mod tests {
|
|||
posts::NewPost,
|
||||
safe_string::SafeString,
|
||||
tags::Tag,
|
||||
tests::{db, rockets},
|
||||
tests::db,
|
||||
users::tests as userTests,
|
||||
};
|
||||
use diesel::Connection;
|
||||
|
@ -257,63 +258,69 @@ mod tests {
|
|||
fn test_timeline() {
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let users = userTests::fill_database(conn);
|
||||
let users = userTests::fill_database(&conn);
|
||||
|
||||
let mut tl1_u1 = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"my timeline".to_owned(),
|
||||
"all".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
List::new(conn, "languages I speak", Some(&users[1]), ListType::Prefix).unwrap();
|
||||
List::new(
|
||||
&conn,
|
||||
"languages I speak",
|
||||
Some(&users[1]),
|
||||
ListType::Prefix,
|
||||
)
|
||||
.unwrap();
|
||||
let tl2_u1 = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"another timeline".to_owned(),
|
||||
"followed".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
let tl1_u2 = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[1].id,
|
||||
"english posts".to_owned(),
|
||||
"lang in \"languages I speak\"".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
let tl1_instance = Timeline::new_for_instance(
|
||||
conn,
|
||||
&conn,
|
||||
"english posts".to_owned(),
|
||||
"license in [cc]".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(tl1_u1, Timeline::get(conn, tl1_u1.id).unwrap());
|
||||
assert_eq!(tl1_u1, Timeline::get(&conn, tl1_u1.id).unwrap());
|
||||
assert_eq!(
|
||||
tl2_u1,
|
||||
Timeline::find_for_user_by_name(conn, Some(users[0].id), "another timeline")
|
||||
Timeline::find_for_user_by_name(&conn, Some(users[0].id), "another timeline")
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
tl1_instance,
|
||||
Timeline::find_for_user_by_name(conn, None, "english posts").unwrap()
|
||||
Timeline::find_for_user_by_name(&conn, None, "english posts").unwrap()
|
||||
);
|
||||
|
||||
let tl_u1 = Timeline::list_for_user(conn, Some(users[0].id)).unwrap();
|
||||
let tl_u1 = Timeline::list_for_user(&conn, Some(users[0].id)).unwrap();
|
||||
assert_eq!(3, tl_u1.len()); // it is not 2 because there is a "Your feed" tl created for each user automatically
|
||||
assert!(tl_u1.iter().fold(false, |res, tl| { res || *tl == tl1_u1 }));
|
||||
assert!(tl_u1.iter().fold(false, |res, tl| { res || *tl == tl2_u1 }));
|
||||
|
||||
let tl_instance = Timeline::list_for_user(conn, None).unwrap();
|
||||
let tl_instance = Timeline::list_for_user(&conn, None).unwrap();
|
||||
assert_eq!(3, tl_instance.len()); // there are also the local and federated feed by default
|
||||
assert!(tl_instance
|
||||
.iter()
|
||||
.fold(false, |res, tl| { res || *tl == tl1_instance }));
|
||||
|
||||
tl1_u1.name = "My Super TL".to_owned();
|
||||
let new_tl1_u2 = tl1_u2.update(conn).unwrap();
|
||||
let new_tl1_u2 = tl1_u2.update(&conn).unwrap();
|
||||
|
||||
let tl_u2 = Timeline::list_for_user(conn, Some(users[1].id)).unwrap();
|
||||
let tl_u2 = Timeline::list_for_user(&conn, Some(users[1].id)).unwrap();
|
||||
assert_eq!(2, tl_u2.len()); // same here
|
||||
assert!(tl_u2
|
||||
.iter()
|
||||
|
@ -327,48 +334,48 @@ mod tests {
|
|||
fn test_timeline_creation_error() {
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let users = userTests::fill_database(conn);
|
||||
let users = userTests::fill_database(&conn);
|
||||
|
||||
assert!(Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"my timeline".to_owned(),
|
||||
"invalid keyword".to_owned(),
|
||||
)
|
||||
.is_err());
|
||||
assert!(Timeline::new_for_instance(
|
||||
conn,
|
||||
&conn,
|
||||
"my timeline".to_owned(),
|
||||
"invalid keyword".to_owned(),
|
||||
)
|
||||
.is_err());
|
||||
|
||||
assert!(Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"my timeline".to_owned(),
|
||||
"author in non_existant_list".to_owned(),
|
||||
)
|
||||
.is_err());
|
||||
assert!(Timeline::new_for_instance(
|
||||
conn,
|
||||
&conn,
|
||||
"my timeline".to_owned(),
|
||||
"lang in dont-exist".to_owned(),
|
||||
)
|
||||
.is_err());
|
||||
|
||||
List::new(conn, "friends", Some(&users[0]), ListType::User).unwrap();
|
||||
List::new(conn, "idk", None, ListType::Blog).unwrap();
|
||||
List::new(&conn, "friends", Some(&users[0]), ListType::User).unwrap();
|
||||
List::new(&conn, "idk", None, ListType::Blog).unwrap();
|
||||
|
||||
assert!(Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"my timeline".to_owned(),
|
||||
"blog in friends".to_owned(),
|
||||
)
|
||||
.is_err());
|
||||
assert!(Timeline::new_for_instance(
|
||||
conn,
|
||||
&conn,
|
||||
"my timeline".to_owned(),
|
||||
"not author in idk".to_owned(),
|
||||
)
|
||||
|
@ -380,13 +387,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple_match() {
|
||||
let r = &rockets();
|
||||
let conn = &r.conn;
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (users, blogs) = blogTests::fill_database(conn);
|
||||
let (users, blogs) = blogTests::fill_database(&conn);
|
||||
|
||||
let gnu_tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"GNU timeline".to_owned(),
|
||||
"license in [AGPL, LGPL, GPL]".to_owned(),
|
||||
|
@ -394,7 +400,7 @@ mod tests {
|
|||
.unwrap();
|
||||
|
||||
let gnu_post = Post::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewPost {
|
||||
blog_id: blogs[0].id,
|
||||
slug: "slug".to_string(),
|
||||
|
@ -410,10 +416,10 @@ mod tests {
|
|||
},
|
||||
)
|
||||
.unwrap();
|
||||
assert!(gnu_tl.matches(r, &gnu_post, Kind::Original).unwrap());
|
||||
assert!(gnu_tl.matches(&conn, &gnu_post, Kind::Original).unwrap());
|
||||
|
||||
let non_free_post = Post::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewPost {
|
||||
blog_id: blogs[0].id,
|
||||
slug: "slug2".to_string(),
|
||||
|
@ -429,7 +435,9 @@ mod tests {
|
|||
},
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!gnu_tl.matches(r, &non_free_post, Kind::Original).unwrap());
|
||||
assert!(!gnu_tl
|
||||
.matches(&conn, &non_free_post, Kind::Original)
|
||||
.unwrap());
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
@ -437,12 +445,11 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_complex_match() {
|
||||
let r = &rockets();
|
||||
let conn = &r.conn;
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (users, blogs) = blogTests::fill_database(conn);
|
||||
let (users, blogs) = blogTests::fill_database(&conn);
|
||||
Follow::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewFollow {
|
||||
follower_id: users[0].id,
|
||||
following_id: users[1].id,
|
||||
|
@ -452,11 +459,11 @@ mod tests {
|
|||
.unwrap();
|
||||
|
||||
let fav_blogs_list =
|
||||
List::new(conn, "fav_blogs", Some(&users[0]), ListType::Blog).unwrap();
|
||||
fav_blogs_list.add_blogs(conn, &[blogs[0].id]).unwrap();
|
||||
List::new(&conn, "fav_blogs", Some(&users[0]), ListType::Blog).unwrap();
|
||||
fav_blogs_list.add_blogs(&conn, &[blogs[0].id]).unwrap();
|
||||
|
||||
let my_tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"My timeline".to_owned(),
|
||||
"blog in fav_blogs and not has_cover or local and followed exclude likes"
|
||||
|
@ -465,7 +472,7 @@ mod tests {
|
|||
.unwrap();
|
||||
|
||||
let post = Post::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewPost {
|
||||
blog_id: blogs[0].id,
|
||||
slug: "about-linux".to_string(),
|
||||
|
@ -481,10 +488,10 @@ mod tests {
|
|||
},
|
||||
)
|
||||
.unwrap();
|
||||
assert!(my_tl.matches(r, &post, Kind::Original).unwrap()); // matches because of "blog in fav_blogs" (and there is no cover)
|
||||
assert!(my_tl.matches(&conn, &post, Kind::Original).unwrap()); // matches because of "blog in fav_blogs" (and there is no cover)
|
||||
|
||||
let post = Post::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewPost {
|
||||
blog_id: blogs[1].id,
|
||||
slug: "about-linux-2".to_string(),
|
||||
|
@ -502,7 +509,7 @@ mod tests {
|
|||
},
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!my_tl.matches(r, &post, Kind::Like(&users[1])).unwrap());
|
||||
assert!(!my_tl.matches(&conn, &post, Kind::Like(&users[1])).unwrap());
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
@ -510,20 +517,19 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_add_to_all_timelines() {
|
||||
let r = &rockets();
|
||||
let conn = &r.conn;
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (users, blogs) = blogTests::fill_database(conn);
|
||||
let (users, blogs) = blogTests::fill_database(&conn);
|
||||
|
||||
let gnu_tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"GNU timeline".to_owned(),
|
||||
"license in [AGPL, LGPL, GPL]".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
let non_gnu_tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"Stallman disapproved timeline".to_owned(),
|
||||
"not license in [AGPL, LGPL, GPL]".to_owned(),
|
||||
|
@ -531,7 +537,7 @@ mod tests {
|
|||
.unwrap();
|
||||
|
||||
let gnu_post = Post::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewPost {
|
||||
blog_id: blogs[0].id,
|
||||
slug: "slug".to_string(),
|
||||
|
@ -549,7 +555,7 @@ mod tests {
|
|||
.unwrap();
|
||||
|
||||
let non_free_post = Post::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewPost {
|
||||
blog_id: blogs[0].id,
|
||||
slug: "slug2".to_string(),
|
||||
|
@ -566,13 +572,13 @@ mod tests {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
Timeline::add_to_all_timelines(r, &gnu_post, Kind::Original).unwrap();
|
||||
Timeline::add_to_all_timelines(r, &non_free_post, Kind::Original).unwrap();
|
||||
Timeline::add_to_all_timelines(&conn, &gnu_post, Kind::Original).unwrap();
|
||||
Timeline::add_to_all_timelines(&conn, &non_free_post, Kind::Original).unwrap();
|
||||
|
||||
let res = gnu_tl.get_latest(conn, 2).unwrap();
|
||||
let res = gnu_tl.get_latest(&conn, 2).unwrap();
|
||||
assert_eq!(res.len(), 1);
|
||||
assert_eq!(res[0].id, gnu_post.id);
|
||||
let res = non_gnu_tl.get_latest(conn, 2).unwrap();
|
||||
let res = non_gnu_tl.get_latest(&conn, 2).unwrap();
|
||||
assert_eq!(res.len(), 1);
|
||||
assert_eq!(res[0].id, non_free_post.id);
|
||||
|
||||
|
@ -582,13 +588,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_matches_lists_direct() {
|
||||
let r = &rockets();
|
||||
let conn = &r.conn;
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (users, blogs) = blogTests::fill_database(conn);
|
||||
let (users, blogs) = blogTests::fill_database(&conn);
|
||||
|
||||
let gnu_post = Post::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewPost {
|
||||
blog_id: blogs[0].id,
|
||||
slug: "slug".to_string(),
|
||||
|
@ -605,61 +610,63 @@ mod tests {
|
|||
)
|
||||
.unwrap();
|
||||
gnu_post
|
||||
.update_tags(conn, vec![Tag::build_activity("free".to_owned()).unwrap()])
|
||||
.update_tags(&conn, vec![Tag::build_activity("free".to_owned()).unwrap()])
|
||||
.unwrap();
|
||||
PostAuthor::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewPostAuthor {
|
||||
post_id: gnu_post.id,
|
||||
author_id: blogs[0].list_authors(conn).unwrap()[0].id,
|
||||
author_id: blogs[0].list_authors(&conn).unwrap()[0].id,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"blog timeline".to_owned(),
|
||||
format!("blog in [{}]", blogs[0].fqn),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(tl.matches(r, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(conn).unwrap();
|
||||
assert!(tl.matches(&conn, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(&conn).unwrap();
|
||||
let tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"blog timeline".to_owned(),
|
||||
"blog in [no_one@nowhere]".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!tl.matches(r, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(conn).unwrap();
|
||||
assert!(!tl.matches(&conn, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(&conn).unwrap();
|
||||
|
||||
let tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"author timeline".to_owned(),
|
||||
format!(
|
||||
"author in [{}]",
|
||||
blogs[0].list_authors(conn).unwrap()[0].fqn
|
||||
blogs[0].list_authors(&conn).unwrap()[0].fqn
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(tl.matches(r, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(conn).unwrap();
|
||||
assert!(tl.matches(&conn, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(&conn).unwrap();
|
||||
let tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"author timeline".to_owned(),
|
||||
format!("author in [{}]", users[2].fqn),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!tl.matches(r, &gnu_post, Kind::Original).unwrap());
|
||||
assert!(tl.matches(r, &gnu_post, Kind::Reshare(&users[2])).unwrap());
|
||||
assert!(!tl.matches(r, &gnu_post, Kind::Like(&users[2])).unwrap());
|
||||
tl.delete(conn).unwrap();
|
||||
assert!(!tl.matches(&conn, &gnu_post, Kind::Original).unwrap());
|
||||
assert!(tl
|
||||
.matches(&conn, &gnu_post, Kind::Reshare(&users[2]))
|
||||
.unwrap());
|
||||
assert!(!tl.matches(&conn, &gnu_post, Kind::Like(&users[2])).unwrap());
|
||||
tl.delete(&conn).unwrap();
|
||||
let tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"author timeline".to_owned(),
|
||||
format!(
|
||||
|
@ -668,48 +675,50 @@ mod tests {
|
|||
),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!tl.matches(r, &gnu_post, Kind::Original).unwrap());
|
||||
assert!(!tl.matches(r, &gnu_post, Kind::Reshare(&users[2])).unwrap());
|
||||
assert!(tl.matches(r, &gnu_post, Kind::Like(&users[2])).unwrap());
|
||||
tl.delete(conn).unwrap();
|
||||
assert!(!tl.matches(&conn, &gnu_post, Kind::Original).unwrap());
|
||||
assert!(!tl
|
||||
.matches(&conn, &gnu_post, Kind::Reshare(&users[2]))
|
||||
.unwrap());
|
||||
assert!(tl.matches(&conn, &gnu_post, Kind::Like(&users[2])).unwrap());
|
||||
tl.delete(&conn).unwrap();
|
||||
|
||||
let tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"tag timeline".to_owned(),
|
||||
"tags in [free]".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(tl.matches(r, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(conn).unwrap();
|
||||
assert!(tl.matches(&conn, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(&conn).unwrap();
|
||||
let tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"tag timeline".to_owned(),
|
||||
"tags in [private]".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!tl.matches(r, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(conn).unwrap();
|
||||
assert!(!tl.matches(&conn, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(&conn).unwrap();
|
||||
|
||||
let tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"english timeline".to_owned(),
|
||||
"lang in [en]".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(tl.matches(r, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(conn).unwrap();
|
||||
assert!(tl.matches(&conn, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(&conn).unwrap();
|
||||
let tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"franco-italian timeline".to_owned(),
|
||||
"lang in [fr, it]".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!tl.matches(r, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(conn).unwrap();
|
||||
assert!(!tl.matches(&conn, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(&conn).unwrap();
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
@ -719,12 +728,12 @@ mod tests {
|
|||
#[test]
|
||||
fn test_matches_lists_saved() {
|
||||
let r = &rockets();
|
||||
let conn = &r.conn;
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (users, blogs) = blogTests::fill_database(conn);
|
||||
let (users, blogs) = blogTests::fill_database(&conn);
|
||||
|
||||
let gnu_post = Post::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewPost {
|
||||
blog_id: blogs[0].id,
|
||||
slug: "slug".to_string(),
|
||||
|
@ -740,8 +749,8 @@ mod tests {
|
|||
},
|
||||
)
|
||||
.unwrap();
|
||||
gnu_post.update_tags(conn, vec![Tag::build_activity("free".to_owned()).unwrap()]).unwrap();
|
||||
PostAuthor::insert(conn, NewPostAuthor {post_id: gnu_post.id, author_id: blogs[0].list_authors(conn).unwrap()[0].id}).unwrap();
|
||||
gnu_post.update_tags(&conn, vec![Tag::build_activity("free".to_owned()).unwrap()]).unwrap();
|
||||
PostAuthor::insert(&conn, NewPostAuthor {post_id: gnu_post.id, author_id: blogs[0].list_authors(&conn).unwrap()[0].id}).unwrap();
|
||||
|
||||
unimplemented!();
|
||||
|
||||
|
@ -751,13 +760,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_matches_keyword() {
|
||||
let r = &rockets();
|
||||
let conn = &r.conn;
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (users, blogs) = blogTests::fill_database(conn);
|
||||
let (users, blogs) = blogTests::fill_database(&conn);
|
||||
|
||||
let gnu_post = Post::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewPost {
|
||||
blog_id: blogs[0].id,
|
||||
slug: "slug".to_string(),
|
||||
|
@ -775,61 +783,61 @@ mod tests {
|
|||
.unwrap();
|
||||
|
||||
let tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"Linux title".to_owned(),
|
||||
"title contains Linux".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(tl.matches(r, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(conn).unwrap();
|
||||
assert!(tl.matches(&conn, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(&conn).unwrap();
|
||||
let tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"Microsoft title".to_owned(),
|
||||
"title contains Microsoft".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!tl.matches(r, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(conn).unwrap();
|
||||
assert!(!tl.matches(&conn, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(&conn).unwrap();
|
||||
|
||||
let tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"Linux subtitle".to_owned(),
|
||||
"subtitle contains Stallman".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(tl.matches(r, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(conn).unwrap();
|
||||
assert!(tl.matches(&conn, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(&conn).unwrap();
|
||||
let tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"Microsoft subtitle".to_owned(),
|
||||
"subtitle contains Nadella".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!tl.matches(r, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(conn).unwrap();
|
||||
assert!(!tl.matches(&conn, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(&conn).unwrap();
|
||||
|
||||
let tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"Linux content".to_owned(),
|
||||
"content contains Linux".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(tl.matches(r, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(conn).unwrap();
|
||||
assert!(tl.matches(&conn, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(&conn).unwrap();
|
||||
let tl = Timeline::new_for_user(
|
||||
conn,
|
||||
&conn,
|
||||
users[0].id,
|
||||
"Microsoft content".to_owned(),
|
||||
"subtitle contains Windows".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!tl.matches(r, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(conn).unwrap();
|
||||
assert!(!tl.matches(&conn, &gnu_post, Kind::Original).unwrap());
|
||||
tl.delete(&conn).unwrap();
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use crate::{
|
||||
blogs::Blog,
|
||||
db_conn::DbConn,
|
||||
lists::{self, ListType},
|
||||
posts::Post,
|
||||
tags::Tag,
|
||||
timeline::Timeline,
|
||||
users::User,
|
||||
PlumeRocket, Result,
|
||||
Result,
|
||||
};
|
||||
use plume_common::activity_pub::inbox::AsActor;
|
||||
use whatlang::{self, Lang};
|
||||
|
@ -160,19 +161,19 @@ enum TQ<'a> {
|
|||
impl<'a> TQ<'a> {
|
||||
fn matches(
|
||||
&self,
|
||||
rocket: &PlumeRocket,
|
||||
conn: &DbConn,
|
||||
timeline: &Timeline,
|
||||
post: &Post,
|
||||
kind: Kind<'_>,
|
||||
) -> Result<bool> {
|
||||
match self {
|
||||
TQ::Or(inner) => inner.iter().try_fold(false, |s, e| {
|
||||
e.matches(rocket, timeline, post, kind).map(|r| s || r)
|
||||
e.matches(conn, timeline, post, kind).map(|r| s || r)
|
||||
}),
|
||||
TQ::And(inner) => inner.iter().try_fold(true, |s, e| {
|
||||
e.matches(rocket, timeline, post, kind).map(|r| s && r)
|
||||
e.matches(conn, timeline, post, kind).map(|r| s && r)
|
||||
}),
|
||||
TQ::Arg(inner, invert) => Ok(inner.matches(rocket, timeline, post, kind)? ^ invert),
|
||||
TQ::Arg(inner, invert) => Ok(inner.matches(conn, timeline, post, kind)? ^ invert),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,15 +206,15 @@ enum Arg<'a> {
|
|||
impl<'a> Arg<'a> {
|
||||
pub fn matches(
|
||||
&self,
|
||||
rocket: &PlumeRocket,
|
||||
conn: &DbConn,
|
||||
timeline: &Timeline,
|
||||
post: &Post,
|
||||
kind: Kind<'_>,
|
||||
) -> Result<bool> {
|
||||
match self {
|
||||
Arg::In(t, l) => t.matches(rocket, timeline, post, l, kind),
|
||||
Arg::In(t, l) => t.matches(conn, timeline, post, l, kind),
|
||||
Arg::Contains(t, v) => t.matches(post, v),
|
||||
Arg::Boolean(t) => t.matches(rocket, timeline, post, kind),
|
||||
Arg::Boolean(t) => t.matches(conn, timeline, post, kind),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -230,7 +231,7 @@ enum WithList {
|
|||
impl WithList {
|
||||
pub fn matches(
|
||||
&self,
|
||||
rocket: &PlumeRocket,
|
||||
conn: &DbConn,
|
||||
timeline: &Timeline,
|
||||
post: &Post,
|
||||
list: &List<'_>,
|
||||
|
@ -238,39 +239,34 @@ impl WithList {
|
|||
) -> Result<bool> {
|
||||
match list {
|
||||
List::List(name) => {
|
||||
let list =
|
||||
lists::List::find_for_user_by_name(&rocket.conn, timeline.user_id, &name)?;
|
||||
let list = lists::List::find_for_user_by_name(conn, timeline.user_id, &name)?;
|
||||
match (self, list.kind()) {
|
||||
(WithList::Blog, ListType::Blog) => {
|
||||
list.contains_blog(&rocket.conn, post.blog_id)
|
||||
}
|
||||
(WithList::Blog, ListType::Blog) => list.contains_blog(conn, post.blog_id),
|
||||
(WithList::Author { boosts, likes }, ListType::User) => match kind {
|
||||
Kind::Original => Ok(list
|
||||
.list_users(&rocket.conn)?
|
||||
.list_users(conn)?
|
||||
.iter()
|
||||
.any(|a| post.is_author(&rocket.conn, a.id).unwrap_or(false))),
|
||||
.any(|a| post.is_author(conn, a.id).unwrap_or(false))),
|
||||
Kind::Reshare(u) => {
|
||||
if *boosts {
|
||||
list.contains_user(&rocket.conn, u.id)
|
||||
list.contains_user(conn, u.id)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
Kind::Like(u) => {
|
||||
if *likes {
|
||||
list.contains_user(&rocket.conn, u.id)
|
||||
list.contains_user(conn, u.id)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
},
|
||||
(WithList::License, ListType::Word) => {
|
||||
list.contains_word(&rocket.conn, &post.license)
|
||||
}
|
||||
(WithList::License, ListType::Word) => list.contains_word(conn, &post.license),
|
||||
(WithList::Tags, ListType::Word) => {
|
||||
let tags = Tag::for_post(&rocket.conn, post.id)?;
|
||||
let tags = Tag::for_post(conn, post.id)?;
|
||||
Ok(list
|
||||
.list_words(&rocket.conn)?
|
||||
.list_words(conn)?
|
||||
.iter()
|
||||
.any(|s| tags.iter().any(|t| s == &t.tag)))
|
||||
}
|
||||
|
@ -285,7 +281,7 @@ impl WithList {
|
|||
})
|
||||
.unwrap_or(Lang::Eng)
|
||||
.name();
|
||||
list.contains_prefix(&rocket.conn, lang)
|
||||
list.contains_prefix(conn, lang)
|
||||
}
|
||||
(_, _) => Err(QueryError::RuntimeError(format!(
|
||||
"The list '{}' is of the wrong type for this usage",
|
||||
|
@ -297,13 +293,13 @@ impl WithList {
|
|||
List::Array(list) => match self {
|
||||
WithList::Blog => Ok(list
|
||||
.iter()
|
||||
.filter_map(|b| Blog::find_by_fqn(rocket, b).ok())
|
||||
.filter_map(|b| Blog::find_by_fqn(conn, b).ok())
|
||||
.any(|b| b.id == post.blog_id)),
|
||||
WithList::Author { boosts, likes } => match kind {
|
||||
Kind::Original => Ok(list
|
||||
.iter()
|
||||
.filter_map(|a| User::find_by_fqn(rocket, a).ok())
|
||||
.any(|a| post.is_author(&rocket.conn, a.id).unwrap_or(false))),
|
||||
.filter_map(|a| User::find_by_fqn(&*conn, a).ok())
|
||||
.any(|a| post.is_author(conn, a.id).unwrap_or(false))),
|
||||
Kind::Reshare(u) => {
|
||||
if *boosts {
|
||||
Ok(list.iter().any(|user| &u.fqn == user))
|
||||
|
@ -321,7 +317,7 @@ impl WithList {
|
|||
},
|
||||
WithList::License => Ok(list.iter().any(|s| s == &post.license)),
|
||||
WithList::Tags => {
|
||||
let tags = Tag::for_post(&rocket.conn, post.id)?;
|
||||
let tags = Tag::for_post(conn, post.id)?;
|
||||
Ok(list.iter().any(|s| tags.iter().any(|t| s == &t.tag)))
|
||||
}
|
||||
WithList::Lang => {
|
||||
|
@ -371,7 +367,7 @@ enum Bool {
|
|||
impl Bool {
|
||||
pub fn matches(
|
||||
&self,
|
||||
rocket: &PlumeRocket,
|
||||
conn: &DbConn,
|
||||
timeline: &Timeline,
|
||||
post: &Post,
|
||||
kind: Kind<'_>,
|
||||
|
@ -384,21 +380,19 @@ impl Bool {
|
|||
let user = timeline.user_id.unwrap();
|
||||
match kind {
|
||||
Kind::Original => post
|
||||
.get_authors(&rocket.conn)?
|
||||
.get_authors(conn)?
|
||||
.iter()
|
||||
.try_fold(false, |s, a| {
|
||||
a.is_followed_by(&rocket.conn, user).map(|r| s || r)
|
||||
}),
|
||||
.try_fold(false, |s, a| a.is_followed_by(conn, user).map(|r| s || r)),
|
||||
Kind::Reshare(u) => {
|
||||
if *boosts {
|
||||
u.is_followed_by(&rocket.conn, user)
|
||||
u.is_followed_by(conn, user)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
Kind::Like(u) => {
|
||||
if *likes {
|
||||
u.is_followed_by(&rocket.conn, user)
|
||||
u.is_followed_by(conn, user)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
|
@ -406,7 +400,7 @@ impl Bool {
|
|||
}
|
||||
}
|
||||
Bool::HasCover => Ok(post.cover_id.is_some()),
|
||||
Bool::Local => Ok(post.get_blog(&rocket.conn)?.is_local() && kind == Kind::Original),
|
||||
Bool::Local => Ok(post.get_blog(conn)?.is_local() && kind == Kind::Original),
|
||||
Bool::All => Ok(kind == Kind::Original),
|
||||
}
|
||||
}
|
||||
|
@ -642,12 +636,12 @@ impl<'a> TimelineQuery<'a> {
|
|||
|
||||
pub fn matches(
|
||||
&self,
|
||||
rocket: &PlumeRocket,
|
||||
conn: &DbConn,
|
||||
timeline: &Timeline,
|
||||
post: &Post,
|
||||
kind: Kind<'_>,
|
||||
) -> Result<bool> {
|
||||
self.0.matches(rocket, timeline, post, kind)
|
||||
self.0.matches(conn, timeline, post, kind)
|
||||
}
|
||||
|
||||
pub fn list_used_lists(&self) -> Vec<(String, ListType)> {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::{
|
||||
ap_url, blocklisted_emails::BlocklistedEmail, blogs::Blog, db_conn::DbConn, follows::Follow,
|
||||
instance::*, medias::Media, notifications::Notification, post_authors::PostAuthor, posts::Post,
|
||||
safe_string::SafeString, schema::users, timeline::Timeline, Connection, Error, PlumeRocket,
|
||||
Result, CONFIG, ITEMS_PER_PAGE,
|
||||
safe_string::SafeString, schema::users, timeline::Timeline, Connection, Error, Result,
|
||||
UserEvent::*, CONFIG, ITEMS_PER_PAGE, USER_CHAN,
|
||||
};
|
||||
use activitypub::{
|
||||
activity::Delete,
|
||||
|
@ -33,6 +33,7 @@ use reqwest::{
|
|||
header::{HeaderValue, ACCEPT},
|
||||
ClientBuilder,
|
||||
};
|
||||
use riker::actors::{Publish, Tell};
|
||||
use rocket::{
|
||||
outcome::IntoOutcome,
|
||||
request::{self, FromRequest, Request},
|
||||
|
@ -40,6 +41,7 @@ use rocket::{
|
|||
use std::{
|
||||
cmp::PartialEq,
|
||||
hash::{Hash, Hasher},
|
||||
sync::Arc,
|
||||
};
|
||||
use url::Url;
|
||||
use webfinger::*;
|
||||
|
@ -186,29 +188,29 @@ impl User {
|
|||
users::table
|
||||
.filter(users::instance_id.eq(Instance::get_local()?.id))
|
||||
.count()
|
||||
.get_result(conn)
|
||||
.get_result(&*conn)
|
||||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
pub fn find_by_fqn(c: &PlumeRocket, fqn: &str) -> Result<User> {
|
||||
pub fn find_by_fqn(conn: &DbConn, fqn: &str) -> Result<User> {
|
||||
let from_db = users::table
|
||||
.filter(users::fqn.eq(fqn))
|
||||
.first(&*c.conn)
|
||||
.first(&**conn)
|
||||
.optional()?;
|
||||
if let Some(from_db) = from_db {
|
||||
Ok(from_db)
|
||||
} else {
|
||||
User::fetch_from_webfinger(c, fqn)
|
||||
User::fetch_from_webfinger(conn, fqn)
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_from_webfinger(c: &PlumeRocket, acct: &str) -> Result<User> {
|
||||
fn fetch_from_webfinger(conn: &DbConn, acct: &str) -> Result<User> {
|
||||
let link = resolve(acct.to_owned(), true)?
|
||||
.links
|
||||
.into_iter()
|
||||
.find(|l| l.mime_type == Some(String::from("application/activity+json")))
|
||||
.ok_or(Error::Webfinger)?;
|
||||
User::from_id(c, link.href.as_ref()?, None, CONFIG.proxy()).map_err(|(_, e)| e)
|
||||
User::from_id(conn, link.href.as_ref()?, None, CONFIG.proxy()).map_err(|(_, e)| e)
|
||||
}
|
||||
|
||||
pub fn fetch_remote_interact_uri(acct: &str) -> Result<String> {
|
||||
|
@ -243,8 +245,8 @@ impl User {
|
|||
Ok(json)
|
||||
}
|
||||
|
||||
pub fn fetch_from_url(c: &PlumeRocket, url: &str) -> Result<User> {
|
||||
User::fetch(url).and_then(|json| User::from_activity(c, json))
|
||||
pub fn fetch_from_url(conn: &DbConn, url: &str) -> Result<User> {
|
||||
User::fetch(url).and_then(|json| User::from_activity(conn, json))
|
||||
}
|
||||
|
||||
pub fn refetch(&self, conn: &Connection) -> Result<()> {
|
||||
|
@ -891,6 +893,21 @@ impl User {
|
|||
self.fqn.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remote_user_found(&self) {
|
||||
tracing::trace!("{:?}", self);
|
||||
self.publish_remote_user_found();
|
||||
}
|
||||
|
||||
fn publish_remote_user_found(&self) {
|
||||
USER_CHAN.tell(
|
||||
Publish {
|
||||
msg: RemoteUserFound(Arc::new(self.clone())),
|
||||
topic: "user.remote_user_found".into(),
|
||||
},
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'r> FromRequest<'a, 'r> for User {
|
||||
|
@ -915,20 +932,20 @@ impl IntoId for User {
|
|||
|
||||
impl Eq for User {}
|
||||
|
||||
impl FromId<PlumeRocket> for User {
|
||||
impl FromId<DbConn> for User {
|
||||
type Error = Error;
|
||||
type Object = CustomPerson;
|
||||
|
||||
fn from_db(c: &PlumeRocket, id: &str) -> Result<Self> {
|
||||
Self::find_by_ap_url(&c.conn, id)
|
||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
||||
Self::find_by_ap_url(conn, id)
|
||||
}
|
||||
|
||||
fn from_activity(c: &PlumeRocket, acct: CustomPerson) -> Result<Self> {
|
||||
fn from_activity(conn: &DbConn, acct: CustomPerson) -> Result<Self> {
|
||||
let url = Url::parse(&acct.object.object_props.id_string()?)?;
|
||||
let inst = url.host_str()?;
|
||||
let instance = Instance::find_by_domain(&c.conn, inst).or_else(|_| {
|
||||
let instance = Instance::find_by_domain(conn, inst).or_else(|_| {
|
||||
Instance::insert(
|
||||
&c.conn,
|
||||
conn,
|
||||
NewInstance {
|
||||
name: inst.to_owned(),
|
||||
public_domain: inst.to_owned(),
|
||||
|
@ -957,7 +974,7 @@ impl FromId<PlumeRocket> for User {
|
|||
};
|
||||
|
||||
let user = User::insert(
|
||||
&c.conn,
|
||||
conn,
|
||||
NewUser {
|
||||
display_name: acct
|
||||
.object
|
||||
|
@ -1003,10 +1020,10 @@ impl FromId<PlumeRocket> for User {
|
|||
|
||||
if let Ok(icon) = acct.object.object_props.icon_image() {
|
||||
if let Ok(url) = icon.object_props.url_string() {
|
||||
let avatar = Media::save_remote(&c.conn, url, &user);
|
||||
let avatar = Media::save_remote(conn, url, &user);
|
||||
|
||||
if let Ok(avatar) = avatar {
|
||||
user.set_avatar(&c.conn, avatar.id)?;
|
||||
user.set_avatar(conn, avatar.id)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1015,7 +1032,7 @@ impl FromId<PlumeRocket> for User {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsActor<&PlumeRocket> for User {
|
||||
impl AsActor<&DbConn> for User {
|
||||
fn get_inbox_url(&self) -> String {
|
||||
self.inbox_url.clone()
|
||||
}
|
||||
|
@ -1031,13 +1048,13 @@ impl AsActor<&PlumeRocket> for User {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsObject<User, Delete, &PlumeRocket> for User {
|
||||
impl AsObject<User, Delete, &DbConn> for User {
|
||||
type Error = Error;
|
||||
type Output = ();
|
||||
|
||||
fn activity(self, c: &PlumeRocket, actor: User, _id: &str) -> Result<()> {
|
||||
fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> {
|
||||
if self.id == actor.id {
|
||||
self.delete(&c.conn).map(|_| ())
|
||||
self.delete(conn).map(|_| ())
|
||||
} else {
|
||||
Err(Error::Unauthorized)
|
||||
}
|
||||
|
@ -1126,12 +1143,27 @@ impl NewUser {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum UserEvent {
|
||||
RemoteUserFound(Arc<User>),
|
||||
}
|
||||
|
||||
impl From<UserEvent> for Arc<User> {
|
||||
fn from(event: UserEvent) -> Self {
|
||||
use UserEvent::*;
|
||||
|
||||
match event {
|
||||
RemoteUserFound(user) => user,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
instance::{tests as instance_tests, Instance},
|
||||
tests::{db, rockets},
|
||||
tests::db,
|
||||
Connection as Conn,
|
||||
};
|
||||
use diesel::Connection;
|
||||
|
@ -1173,12 +1205,11 @@ pub(crate) mod tests {
|
|||
|
||||
#[test]
|
||||
fn find_by() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
fill_database(conn);
|
||||
fill_database(&conn);
|
||||
let test_user = NewUser::new_local(
|
||||
conn,
|
||||
&conn,
|
||||
"test".to_owned(),
|
||||
"test user".to_owned(),
|
||||
Role::Normal,
|
||||
|
@ -1189,22 +1220,22 @@ pub(crate) mod tests {
|
|||
.unwrap();
|
||||
assert_eq!(
|
||||
test_user.id,
|
||||
User::find_by_name(conn, "test", Instance::get_local().unwrap().id)
|
||||
User::find_by_name(&conn, "test", Instance::get_local().unwrap().id)
|
||||
.unwrap()
|
||||
.id
|
||||
);
|
||||
assert_eq!(
|
||||
test_user.id,
|
||||
User::find_by_fqn(&r, &test_user.fqn).unwrap().id
|
||||
User::find_by_fqn(&conn, &test_user.fqn).unwrap().id
|
||||
);
|
||||
assert_eq!(
|
||||
test_user.id,
|
||||
User::find_by_email(conn, "test@example.com").unwrap().id
|
||||
User::find_by_email(&conn, "test@example.com").unwrap().id
|
||||
);
|
||||
assert_eq!(
|
||||
test_user.id,
|
||||
User::find_by_ap_url(
|
||||
conn,
|
||||
&conn,
|
||||
&format!(
|
||||
"https://{}/@/{}/",
|
||||
Instance::get_local().unwrap().public_domain,
|
||||
|
@ -1222,11 +1253,11 @@ pub(crate) mod tests {
|
|||
fn delete() {
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let inserted = fill_database(conn);
|
||||
let inserted = fill_database(&conn);
|
||||
|
||||
assert!(User::get(conn, inserted[0].id).is_ok());
|
||||
inserted[0].delete(conn).unwrap();
|
||||
assert!(User::get(conn, inserted[0].id).is_err());
|
||||
assert!(User::get(&conn, inserted[0].id).is_ok());
|
||||
inserted[0].delete(&conn).unwrap();
|
||||
assert!(User::get(&conn, inserted[0].id).is_err());
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
@ -1235,20 +1266,20 @@ pub(crate) mod tests {
|
|||
fn admin() {
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let inserted = fill_database(conn);
|
||||
let inserted = fill_database(&conn);
|
||||
let local_inst = Instance::get_local().unwrap();
|
||||
let mut i = 0;
|
||||
while local_inst.has_admin(conn).unwrap() {
|
||||
while local_inst.has_admin(&conn).unwrap() {
|
||||
assert!(i < 100); //prevent from looping indefinitelly
|
||||
local_inst
|
||||
.main_admin(conn)
|
||||
.main_admin(&conn)
|
||||
.unwrap()
|
||||
.set_role(conn, Role::Normal)
|
||||
.set_role(&conn, Role::Normal)
|
||||
.unwrap();
|
||||
i += 1;
|
||||
}
|
||||
inserted[0].set_role(conn, Role::Admin).unwrap();
|
||||
assert_eq!(inserted[0].id, local_inst.main_admin(conn).unwrap().id);
|
||||
inserted[0].set_role(&conn, Role::Admin).unwrap();
|
||||
assert_eq!(inserted[0].id, local_inst.main_admin(&conn).unwrap().id);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
@ -1257,9 +1288,9 @@ pub(crate) mod tests {
|
|||
fn auth() {
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
fill_database(conn);
|
||||
fill_database(&conn);
|
||||
let test_user = NewUser::new_local(
|
||||
conn,
|
||||
&conn,
|
||||
"test".to_owned(),
|
||||
"test user".to_owned(),
|
||||
Role::Normal,
|
||||
|
@ -1270,10 +1301,10 @@ pub(crate) mod tests {
|
|||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
User::login(conn, "test", "test_password").unwrap().id,
|
||||
User::login(&conn, "test", "test_password").unwrap().id,
|
||||
test_user.id
|
||||
);
|
||||
assert!(User::login(conn, "test", "other_password").is_err());
|
||||
assert!(User::login(&conn, "test", "other_password").is_err());
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
@ -1282,26 +1313,26 @@ pub(crate) mod tests {
|
|||
fn get_local_page() {
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
fill_database(conn);
|
||||
fill_database(&conn);
|
||||
|
||||
let page = User::get_local_page(conn, (0, 2)).unwrap();
|
||||
let page = User::get_local_page(&conn, (0, 2)).unwrap();
|
||||
assert_eq!(page.len(), 2);
|
||||
assert!(page[0].username <= page[1].username);
|
||||
|
||||
let mut last_username = User::get_local_page(conn, (0, 1)).unwrap()[0]
|
||||
let mut last_username = User::get_local_page(&conn, (0, 1)).unwrap()[0]
|
||||
.username
|
||||
.clone();
|
||||
for i in 1..User::count_local(conn).unwrap() as i32 {
|
||||
let page = User::get_local_page(conn, (i, i + 1)).unwrap();
|
||||
for i in 1..User::count_local(&conn).unwrap() as i32 {
|
||||
let page = User::get_local_page(&conn, (i, i + 1)).unwrap();
|
||||
assert_eq!(page.len(), 1);
|
||||
assert!(last_username <= page[0].username);
|
||||
last_username = page[0].username.clone();
|
||||
}
|
||||
assert_eq!(
|
||||
User::get_local_page(conn, (0, User::count_local(conn).unwrap() as i32 + 10))
|
||||
User::get_local_page(&conn, (0, User::count_local(&conn).unwrap() as i32 + 10))
|
||||
.unwrap()
|
||||
.len() as i64,
|
||||
User::count_local(conn).unwrap()
|
||||
User::count_local(&conn).unwrap()
|
||||
);
|
||||
Ok(())
|
||||
});
|
||||
|
@ -1309,14 +1340,13 @@ pub(crate) mod tests {
|
|||
|
||||
#[test]
|
||||
fn self_federation() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
let conn = db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let users = fill_database(conn);
|
||||
let users = fill_database(&conn);
|
||||
|
||||
let ap_repr = users[0].to_activity(conn).unwrap();
|
||||
users[0].delete(conn).unwrap();
|
||||
let user = User::from_activity(&r, ap_repr).unwrap();
|
||||
let ap_repr = users[0].to_activity(&conn).unwrap();
|
||||
users[0].delete(&conn).unwrap();
|
||||
let user = User::from_activity(&conn, ap_repr).unwrap();
|
||||
|
||||
assert_eq!(user.username, users[0].username);
|
||||
assert_eq!(user.display_name, users[0].display_name);
|
||||
|
@ -1327,7 +1357,7 @@ pub(crate) mod tests {
|
|||
assert_eq!(user.public_key, users[0].public_key);
|
||||
assert_eq!(user.shared_inbox_url, users[0].shared_inbox_url);
|
||||
assert_eq!(user.followers_endpoint, users[0].followers_endpoint);
|
||||
assert_eq!(user.avatar_url(conn), users[0].avatar_url(conn));
|
||||
assert_eq!(user.avatar_url(&conn), users[0].avatar_url(&conn));
|
||||
assert_eq!(user.fqn, users[0].fqn);
|
||||
assert_eq!(user.summary_html, users[0].summary_html);
|
||||
Ok(())
|
||||
|
|
|
@ -6,7 +6,7 @@ use rocket::{
|
|||
use rocket_contrib::json::Json;
|
||||
|
||||
use plume_common::utils::random_hex;
|
||||
use plume_models::{api_tokens::*, apps::App, users::User, Error, PlumeRocket};
|
||||
use plume_models::{api_tokens::*, apps::App, db_conn::DbConn, users::User, Error};
|
||||
|
||||
type Api<T> = Result<Json<T>, ApiError>;
|
||||
|
||||
|
@ -54,16 +54,12 @@ pub struct OAuthRequest {
|
|||
}
|
||||
|
||||
#[get("/oauth2?<query..>")]
|
||||
pub fn oauth(
|
||||
query: Form<OAuthRequest>,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Json<serde_json::Value>, ApiError> {
|
||||
let conn = &*rockets.conn;
|
||||
let app = App::find_by_client_id(conn, &query.client_id)?;
|
||||
pub fn oauth(query: Form<OAuthRequest>, conn: DbConn) -> Result<Json<serde_json::Value>, ApiError> {
|
||||
let app = App::find_by_client_id(&conn, &query.client_id)?;
|
||||
if app.client_secret == query.client_secret {
|
||||
if let Ok(user) = User::login(conn, &query.username, &query.password) {
|
||||
if let Ok(user) = User::login(&conn, &query.username, &query.password) {
|
||||
let token = ApiToken::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewApiToken {
|
||||
app_id: app.id,
|
||||
user_id: user.id,
|
||||
|
|
|
@ -102,12 +102,12 @@ pub fn list(
|
|||
pub fn create(
|
||||
auth: Authorization<Write, Post>,
|
||||
payload: Json<NewPostData>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Api<PostData> {
|
||||
let conn = &*rockets.conn;
|
||||
let worker = &rockets.worker;
|
||||
|
||||
let author = User::get(conn, auth.0.user_id)?;
|
||||
let author = User::get(&conn, auth.0.user_id)?;
|
||||
|
||||
let slug = &payload.title.clone().to_kebab_case();
|
||||
let date = payload.creation_date.clone().and_then(|d| {
|
||||
|
@ -119,11 +119,11 @@ pub fn create(
|
|||
&payload.source,
|
||||
Some(domain),
|
||||
false,
|
||||
Some(Media::get_media_processor(conn, vec![&author])),
|
||||
Some(Media::get_media_processor(&conn, vec![&author])),
|
||||
);
|
||||
|
||||
let blog = payload.blog_id.or_else(|| {
|
||||
let blogs = Blog::find_for_author(conn, &author).ok()?;
|
||||
let blogs = Blog::find_for_author(&conn, &author).ok()?;
|
||||
if blogs.len() == 1 {
|
||||
Some(blogs[0].id)
|
||||
} else {
|
||||
|
@ -131,12 +131,12 @@ pub fn create(
|
|||
}
|
||||
})?;
|
||||
|
||||
if Post::find_by_slug(conn, slug, blog).is_ok() {
|
||||
if Post::find_by_slug(&conn, slug, blog).is_ok() {
|
||||
return Err(Error::InvalidValue.into());
|
||||
}
|
||||
|
||||
let post = Post::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewPost {
|
||||
blog_id: blog,
|
||||
slug: slug.to_string(),
|
||||
|
@ -157,7 +157,7 @@ pub fn create(
|
|||
)?;
|
||||
|
||||
PostAuthor::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewPostAuthor {
|
||||
author_id: author.id,
|
||||
post_id: post.id,
|
||||
|
@ -167,7 +167,7 @@ pub fn create(
|
|||
if let Some(ref tags) = payload.tags {
|
||||
for tag in tags {
|
||||
Tag::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewTag {
|
||||
tag: tag.to_string(),
|
||||
is_hashtag: false,
|
||||
|
@ -178,7 +178,7 @@ pub fn create(
|
|||
}
|
||||
for hashtag in hashtags {
|
||||
Tag::insert(
|
||||
conn,
|
||||
&conn,
|
||||
NewTag {
|
||||
tag: hashtag,
|
||||
is_hashtag: true,
|
||||
|
@ -190,25 +190,29 @@ pub fn create(
|
|||
if post.published {
|
||||
for m in mentions.into_iter() {
|
||||
Mention::from_activity(
|
||||
&*conn,
|
||||
&Mention::build_activity(&rockets, &m)?,
|
||||
&conn,
|
||||
&Mention::build_activity(&conn, &m)?,
|
||||
post.id,
|
||||
true,
|
||||
true,
|
||||
)?;
|
||||
}
|
||||
|
||||
let act = post.create_activity(&*conn)?;
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
let act = post.create_activity(&conn)?;
|
||||
let dest = User::one_by_instance(&conn)?;
|
||||
worker.execute(move || broadcast(&author, act, dest, CONFIG.proxy().cloned()));
|
||||
}
|
||||
|
||||
Timeline::add_to_all_timelines(&rockets, &post, Kind::Original)?;
|
||||
Timeline::add_to_all_timelines(&conn, &post, Kind::Original)?;
|
||||
|
||||
Ok(Json(PostData {
|
||||
authors: post.get_authors(conn)?.into_iter().map(|a| a.fqn).collect(),
|
||||
authors: post
|
||||
.get_authors(&conn)?
|
||||
.into_iter()
|
||||
.map(|a| a.fqn)
|
||||
.collect(),
|
||||
creation_date: post.creation_date.format("%Y-%m-%d").to_string(),
|
||||
tags: Tag::for_post(conn, post.id)?
|
||||
tags: Tag::for_post(&conn, post.id)?
|
||||
.into_iter()
|
||||
.map(|t| t.tag)
|
||||
.collect(),
|
||||
|
@ -226,11 +230,11 @@ pub fn create(
|
|||
}
|
||||
|
||||
#[delete("/posts/<id>")]
|
||||
pub fn delete(auth: Authorization<Write, Post>, rockets: PlumeRocket, id: i32) -> Api<()> {
|
||||
let author = User::get(&*rockets.conn, auth.0.user_id)?;
|
||||
if let Ok(post) = Post::get(&*rockets.conn, id) {
|
||||
if post.is_author(&*rockets.conn, author.id).unwrap_or(false) {
|
||||
post.delete(&*rockets.conn)?;
|
||||
pub fn delete(auth: Authorization<Write, Post>, conn: DbConn, id: i32) -> Api<()> {
|
||||
let author = User::get(&conn, auth.0.user_id)?;
|
||||
if let Ok(post) = Post::get(&conn, id) {
|
||||
if post.is_author(&conn, author.id).unwrap_or(false) {
|
||||
post.delete(&conn)?;
|
||||
}
|
||||
}
|
||||
Ok(Json(()))
|
||||
|
|
15
src/inbox.rs
15
src/inbox.rs
|
@ -4,7 +4,7 @@ use plume_common::activity_pub::{
|
|||
sign::{verify_http_headers, Signable},
|
||||
};
|
||||
use plume_models::{
|
||||
headers::Headers, inbox::inbox, instance::Instance, users::User, Error, PlumeRocket, CONFIG,
|
||||
db_conn::DbConn, headers::Headers, inbox::inbox, instance::Instance, users::User, Error, CONFIG,
|
||||
};
|
||||
use rocket::{data::*, http::Status, response::status, Outcome::*, Request};
|
||||
use rocket_contrib::json::*;
|
||||
|
@ -13,11 +13,10 @@ use std::io::Read;
|
|||
use tracing::warn;
|
||||
|
||||
pub fn handle_incoming(
|
||||
rockets: PlumeRocket,
|
||||
conn: DbConn,
|
||||
data: SignedJson<serde_json::Value>,
|
||||
headers: Headers<'_>,
|
||||
) -> Result<String, status::BadRequest<&'static str>> {
|
||||
let conn = &*rockets.conn;
|
||||
let act = data.1.into_inner();
|
||||
let sig = data.0;
|
||||
|
||||
|
@ -27,13 +26,13 @@ pub fn handle_incoming(
|
|||
.or_else(|| activity["actor"]["id"].as_str())
|
||||
.ok_or(status::BadRequest(Some("Missing actor id for activity")))?;
|
||||
|
||||
let actor = User::from_id(&rockets, actor_id, None, CONFIG.proxy())
|
||||
let actor = User::from_id(&conn, actor_id, None, CONFIG.proxy())
|
||||
.expect("instance::shared_inbox: user error");
|
||||
if !verify_http_headers(&actor, &headers.0, &sig).is_secure() && !act.clone().verify(&actor) {
|
||||
// maybe we just know an old key?
|
||||
actor
|
||||
.refetch(conn)
|
||||
.and_then(|_| User::get(conn, actor.id))
|
||||
.refetch(&conn)
|
||||
.and_then(|_| User::get(&conn, actor.id))
|
||||
.and_then(|u| {
|
||||
if verify_http_headers(&u, &headers.0, &sig).is_secure() || act.clone().verify(&u) {
|
||||
Ok(())
|
||||
|
@ -50,13 +49,13 @@ pub fn handle_incoming(
|
|||
})?;
|
||||
}
|
||||
|
||||
if Instance::is_blocked(conn, actor_id)
|
||||
if Instance::is_blocked(&conn, actor_id)
|
||||
.map_err(|_| status::BadRequest(Some("Can't tell if instance is blocked")))?
|
||||
{
|
||||
return Ok(String::new());
|
||||
}
|
||||
|
||||
Ok(match inbox(&rockets, act) {
|
||||
Ok(match inbox(&conn, act) {
|
||||
Ok(_) => String::new(),
|
||||
Err(e) => {
|
||||
warn!("Shared inbox error: {:?}", e);
|
||||
|
|
|
@ -16,6 +16,7 @@ use plume_models::{
|
|||
db_conn::{DbPool, PragmaForeignKey},
|
||||
instance::Instance,
|
||||
migrations::IMPORTED_MIGRATIONS,
|
||||
remote_fetch_actor::RemoteFetchActor,
|
||||
search::{actor::SearchActor, Searcher as UnmanagedSearcher},
|
||||
Connection, CONFIG,
|
||||
};
|
||||
|
@ -104,6 +105,7 @@ Then try to restart Plume.
|
|||
&CONFIG.search_index,
|
||||
&CONFIG.search_tokenizers,
|
||||
));
|
||||
RemoteFetchActor::init(dbpool.clone());
|
||||
SearchActor::init(searcher.clone(), dbpool.clone());
|
||||
let commiter = searcher.clone();
|
||||
workpool.execute_with_fixed_delay(
|
||||
|
|
|
@ -14,21 +14,25 @@ use crate::template_utils::{IntoContext, Ructe};
|
|||
use plume_common::activity_pub::{ActivityStream, ApRequest};
|
||||
use plume_common::utils;
|
||||
use plume_models::{
|
||||
blog_authors::*, blogs::*, instance::Instance, medias::*, posts::Post, safe_string::SafeString,
|
||||
users::User, Connection, PlumeRocket,
|
||||
blog_authors::*, blogs::*, db_conn::DbConn, instance::Instance, medias::*, posts::Post,
|
||||
safe_string::SafeString, users::User, Connection, PlumeRocket,
|
||||
};
|
||||
|
||||
#[get("/~/<name>?<page>", rank = 2)]
|
||||
pub fn details(name: String, page: Option<Page>, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
pub fn details(
|
||||
name: String,
|
||||
page: Option<Page>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
let conn = &*rockets.conn;
|
||||
let blog = Blog::find_by_fqn(&rockets, &name)?;
|
||||
let posts = Post::blog_page(conn, &blog, page.limits())?;
|
||||
let articles_count = Post::count_for_blog(conn, &blog)?;
|
||||
let authors = &blog.list_authors(conn)?;
|
||||
let blog = Blog::find_by_fqn(&conn, &name)?;
|
||||
let posts = Post::blog_page(&conn, &blog, page.limits())?;
|
||||
let articles_count = Post::count_for_blog(&conn, &blog)?;
|
||||
let authors = &blog.list_authors(&conn)?;
|
||||
|
||||
Ok(render!(blogs::details(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
blog,
|
||||
authors,
|
||||
page.0,
|
||||
|
@ -40,17 +44,17 @@ pub fn details(name: String, page: Option<Page>, rockets: PlumeRocket) -> Result
|
|||
#[get("/~/<name>", rank = 1)]
|
||||
pub fn activity_details(
|
||||
name: String,
|
||||
rockets: PlumeRocket,
|
||||
conn: DbConn,
|
||||
_ap: ApRequest,
|
||||
) -> Option<ActivityStream<CustomGroup>> {
|
||||
let blog = Blog::find_by_fqn(&rockets, &name).ok()?;
|
||||
Some(ActivityStream::new(blog.to_activity(&*rockets.conn).ok()?))
|
||||
let blog = Blog::find_by_fqn(&conn, &name).ok()?;
|
||||
Some(ActivityStream::new(blog.to_activity(&conn).ok()?))
|
||||
}
|
||||
|
||||
#[get("/blogs/new")]
|
||||
pub fn new(rockets: PlumeRocket, _user: User) -> Ructe {
|
||||
pub fn new(conn: DbConn, rockets: PlumeRocket, _user: User) -> Ructe {
|
||||
render!(blogs::new(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
&NewBlogForm::default(),
|
||||
ValidationErrors::default()
|
||||
))
|
||||
|
@ -83,9 +87,12 @@ fn valid_slug(title: &str) -> Result<(), ValidationError> {
|
|||
}
|
||||
|
||||
#[post("/blogs/new", data = "<form>")]
|
||||
pub fn create(form: LenientForm<NewBlogForm>, rockets: PlumeRocket) -> RespondOrRedirect {
|
||||
pub fn create(
|
||||
form: LenientForm<NewBlogForm>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> RespondOrRedirect {
|
||||
let slug = utils::make_actor_id(&form.title);
|
||||
let conn = &*rockets.conn;
|
||||
let intl = &rockets.intl.catalog;
|
||||
let user = rockets.user.clone().unwrap();
|
||||
|
||||
|
@ -93,7 +100,7 @@ pub fn create(form: LenientForm<NewBlogForm>, rockets: PlumeRocket) -> RespondOr
|
|||
Ok(_) => ValidationErrors::new(),
|
||||
Err(e) => e,
|
||||
};
|
||||
if Blog::find_by_fqn(&rockets, &slug).is_ok() {
|
||||
if Blog::find_by_fqn(&conn, &slug).is_ok() {
|
||||
errors.add(
|
||||
"title",
|
||||
ValidationError {
|
||||
|
@ -108,11 +115,11 @@ pub fn create(form: LenientForm<NewBlogForm>, rockets: PlumeRocket) -> RespondOr
|
|||
}
|
||||
|
||||
if !errors.is_empty() {
|
||||
return render!(blogs::new(&rockets.to_context(), &*form, errors)).into();
|
||||
return render!(blogs::new(&(&conn, &rockets).to_context(), &*form, errors)).into();
|
||||
}
|
||||
|
||||
let blog = Blog::insert(
|
||||
&*conn,
|
||||
&conn,
|
||||
NewBlog::new_local(
|
||||
slug.clone(),
|
||||
form.title.to_string(),
|
||||
|
@ -126,7 +133,7 @@ pub fn create(form: LenientForm<NewBlogForm>, rockets: PlumeRocket) -> RespondOr
|
|||
.expect("blog::create: error");
|
||||
|
||||
BlogAuthor::insert(
|
||||
&*conn,
|
||||
&conn,
|
||||
NewBlogAuthor {
|
||||
blog_id: blog.id,
|
||||
author_id: user.id,
|
||||
|
@ -143,17 +150,16 @@ pub fn create(form: LenientForm<NewBlogForm>, rockets: PlumeRocket) -> RespondOr
|
|||
}
|
||||
|
||||
#[post("/~/<name>/delete")]
|
||||
pub fn delete(name: String, rockets: PlumeRocket) -> RespondOrRedirect {
|
||||
let conn = &*rockets.conn;
|
||||
let blog = Blog::find_by_fqn(&rockets, &name).expect("blog::delete: blog not found");
|
||||
pub fn delete(name: String, conn: DbConn, rockets: PlumeRocket) -> RespondOrRedirect {
|
||||
let blog = Blog::find_by_fqn(&conn, &name).expect("blog::delete: blog not found");
|
||||
|
||||
if rockets
|
||||
.user
|
||||
.clone()
|
||||
.and_then(|u| u.is_author_in(&*conn, &blog).ok())
|
||||
.and_then(|u| u.is_author_in(&conn, &blog).ok())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
blog.delete(&conn).expect("blog::expect: deletion error");
|
||||
blog.delete(&*conn).expect("blog::expect: deletion error");
|
||||
Flash::success(
|
||||
Redirect::to(uri!(super::instance::index)),
|
||||
i18n!(rockets.intl.catalog, "Your blog was deleted."),
|
||||
|
@ -162,7 +168,7 @@ pub fn delete(name: String, rockets: PlumeRocket) -> RespondOrRedirect {
|
|||
} else {
|
||||
// TODO actually return 403 error code
|
||||
render!(errors::not_authorized(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
i18n!(
|
||||
rockets.intl.catalog,
|
||||
"You are not allowed to delete this blog."
|
||||
|
@ -183,22 +189,21 @@ pub struct EditForm {
|
|||
}
|
||||
|
||||
#[get("/~/<name>/edit")]
|
||||
pub fn edit(name: String, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
let conn = &*rockets.conn;
|
||||
let blog = Blog::find_by_fqn(&rockets, &name)?;
|
||||
pub fn edit(name: String, conn: DbConn, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
let blog = Blog::find_by_fqn(&conn, &name)?;
|
||||
if rockets
|
||||
.user
|
||||
.clone()
|
||||
.and_then(|u| u.is_author_in(conn, &blog).ok())
|
||||
.and_then(|u| u.is_author_in(&conn, &blog).ok())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
let user = rockets
|
||||
.user
|
||||
.clone()
|
||||
.expect("blogs::edit: User was None while it shouldn't");
|
||||
let medias = Media::for_user(conn, user.id).expect("Couldn't list media");
|
||||
let medias = Media::for_user(&conn, user.id).expect("Couldn't list media");
|
||||
Ok(render!(blogs::edit(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
&blog,
|
||||
medias,
|
||||
&EditForm {
|
||||
|
@ -213,7 +218,7 @@ pub fn edit(name: String, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
|||
} else {
|
||||
// TODO actually return 403 error code
|
||||
Ok(render!(errors::not_authorized(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
i18n!(
|
||||
rockets.intl.catalog,
|
||||
"You are not allowed to edit this blog."
|
||||
|
@ -235,20 +240,20 @@ fn check_media(conn: &Connection, id: i32, user: &User) -> bool {
|
|||
pub fn update(
|
||||
name: String,
|
||||
form: LenientForm<EditForm>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> RespondOrRedirect {
|
||||
let conn = &*rockets.conn;
|
||||
let intl = &rockets.intl.catalog;
|
||||
let mut blog = Blog::find_by_fqn(&rockets, &name).expect("blog::update: blog not found");
|
||||
let mut blog = Blog::find_by_fqn(&conn, &name).expect("blog::update: blog not found");
|
||||
if !rockets
|
||||
.user
|
||||
.clone()
|
||||
.and_then(|u| u.is_author_in(&*conn, &blog).ok())
|
||||
.and_then(|u| u.is_author_in(&conn, &blog).ok())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
// TODO actually return 403 error code
|
||||
return render!(errors::not_authorized(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
i18n!(
|
||||
rockets.intl.catalog,
|
||||
"You are not allowed to edit this blog."
|
||||
|
@ -264,7 +269,7 @@ pub fn update(
|
|||
form.validate()
|
||||
.and_then(|_| {
|
||||
if let Some(icon) = form.icon {
|
||||
if !check_media(&*conn, icon, &user) {
|
||||
if !check_media(&conn, icon, &user) {
|
||||
let mut errors = ValidationErrors::new();
|
||||
errors.add(
|
||||
"",
|
||||
|
@ -282,7 +287,7 @@ pub fn update(
|
|||
}
|
||||
|
||||
if let Some(banner) = form.banner {
|
||||
if !check_media(&*conn, banner, &user) {
|
||||
if !check_media(&conn, banner, &user) {
|
||||
let mut errors = ValidationErrors::new();
|
||||
errors.add(
|
||||
"",
|
||||
|
@ -327,9 +332,9 @@ pub fn update(
|
|||
))
|
||||
})
|
||||
.map_err(|err| {
|
||||
let medias = Media::for_user(&*conn, user.id).expect("Couldn't list media");
|
||||
let medias = Media::for_user(&conn, user.id).expect("Couldn't list media");
|
||||
render!(blogs::edit(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
&blog,
|
||||
medias,
|
||||
&*form,
|
||||
|
@ -341,31 +346,30 @@ pub fn update(
|
|||
}
|
||||
|
||||
#[get("/~/<name>/outbox")]
|
||||
pub fn outbox(name: String, rockets: PlumeRocket) -> Option<ActivityStream<OrderedCollection>> {
|
||||
let blog = Blog::find_by_fqn(&rockets, &name).ok()?;
|
||||
Some(blog.outbox(&*rockets.conn).ok()?)
|
||||
pub fn outbox(name: String, conn: DbConn) -> Option<ActivityStream<OrderedCollection>> {
|
||||
let blog = Blog::find_by_fqn(&conn, &name).ok()?;
|
||||
Some(blog.outbox(&conn).ok()?)
|
||||
}
|
||||
#[allow(unused_variables)]
|
||||
#[get("/~/<name>/outbox?<page>")]
|
||||
pub fn outbox_page(
|
||||
name: String,
|
||||
page: Page,
|
||||
rockets: PlumeRocket,
|
||||
conn: DbConn,
|
||||
) -> Option<ActivityStream<OrderedCollectionPage>> {
|
||||
let blog = Blog::find_by_fqn(&rockets, &name).ok()?;
|
||||
Some(blog.outbox_page(&*rockets.conn, page.limits()).ok()?)
|
||||
let blog = Blog::find_by_fqn(&conn, &name).ok()?;
|
||||
Some(blog.outbox_page(&conn, page.limits()).ok()?)
|
||||
}
|
||||
#[get("/~/<name>/atom.xml")]
|
||||
pub fn atom_feed(name: String, rockets: PlumeRocket) -> Option<Content<String>> {
|
||||
let blog = Blog::find_by_fqn(&rockets, &name).ok()?;
|
||||
let conn = &*rockets.conn;
|
||||
pub fn atom_feed(name: String, conn: DbConn) -> Option<Content<String>> {
|
||||
let blog = Blog::find_by_fqn(&conn, &name).ok()?;
|
||||
let entries = Post::get_recents_for_blog(&*conn, &blog, 15).ok()?;
|
||||
let uri = Instance::get_local()
|
||||
.ok()?
|
||||
.compute_box("~", &name, "atom.xml");
|
||||
let title = &blog.title;
|
||||
let default_updated = &blog.creation_date;
|
||||
let feed = super::build_atom_feed(entries, &uri, title, default_updated, conn);
|
||||
let feed = super::build_atom_feed(entries, &uri, title, default_updated, &conn);
|
||||
Some(Content(
|
||||
ContentType::new("application", "atom+xml"),
|
||||
feed.to_string(),
|
||||
|
|
|
@ -15,8 +15,9 @@ use plume_common::{
|
|||
utils,
|
||||
};
|
||||
use plume_models::{
|
||||
blogs::Blog, comments::*, inbox::inbox, instance::Instance, medias::Media, mentions::Mention,
|
||||
posts::Post, safe_string::SafeString, tags::Tag, users::User, Error, PlumeRocket, CONFIG,
|
||||
blogs::Blog, comments::*, db_conn::DbConn, inbox::inbox, instance::Instance, medias::Media,
|
||||
mentions::Mention, posts::Post, safe_string::SafeString, tags::Tag, users::User, Error,
|
||||
PlumeRocket, CONFIG,
|
||||
};
|
||||
|
||||
#[derive(Default, FromForm, Debug, Validate)]
|
||||
|
@ -33,11 +34,11 @@ pub fn create(
|
|||
slug: String,
|
||||
form: LenientForm<NewCommentForm>,
|
||||
user: User,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Flash<Redirect>, Ructe> {
|
||||
let conn = &*rockets.conn;
|
||||
let blog = Blog::find_by_fqn(&rockets, &blog_name).expect("comments::create: blog error");
|
||||
let post = Post::find_by_slug(&*conn, &slug, blog.id).expect("comments::create: post error");
|
||||
let blog = Blog::find_by_fqn(&conn, &blog_name).expect("comments::create: blog error");
|
||||
let post = Post::find_by_slug(&conn, &slug, blog.id).expect("comments::create: post error");
|
||||
form.validate()
|
||||
.map(|_| {
|
||||
let (html, mentions, _hashtags) = utils::md_to_html(
|
||||
|
@ -51,7 +52,7 @@ pub fn create(
|
|||
Some(Media::get_media_processor(&conn, vec![&user])),
|
||||
);
|
||||
let comm = Comment::insert(
|
||||
&*conn,
|
||||
&conn,
|
||||
NewComment {
|
||||
content: SafeString::new(html.as_ref()),
|
||||
in_response_to_id: form.responding_to,
|
||||
|
@ -65,14 +66,14 @@ pub fn create(
|
|||
)
|
||||
.expect("comments::create: insert error");
|
||||
let new_comment = comm
|
||||
.create_activity(&rockets)
|
||||
.create_activity(&conn)
|
||||
.expect("comments::create: activity error");
|
||||
|
||||
// save mentions
|
||||
for ment in mentions {
|
||||
Mention::from_activity(
|
||||
&*conn,
|
||||
&Mention::build_activity(&rockets, &ment)
|
||||
&conn,
|
||||
&Mention::build_activity(&conn, &ment)
|
||||
.expect("comments::create: build mention error"),
|
||||
comm.id,
|
||||
false,
|
||||
|
@ -81,10 +82,10 @@ pub fn create(
|
|||
.expect("comments::create: mention save error");
|
||||
}
|
||||
|
||||
comm.notify(&*conn).expect("comments::create: notify error");
|
||||
comm.notify(&conn).expect("comments::create: notify error");
|
||||
|
||||
// federate
|
||||
let dest = User::one_by_instance(&*conn).expect("comments::create: dest error");
|
||||
let dest = User::one_by_instance(&conn).expect("comments::create: dest error");
|
||||
let user_clone = user.clone();
|
||||
rockets.worker.execute(move || {
|
||||
broadcast(&user_clone, new_comment, dest, CONFIG.proxy().cloned())
|
||||
|
@ -101,38 +102,36 @@ pub fn create(
|
|||
})
|
||||
.map_err(|errors| {
|
||||
// TODO: de-duplicate this code
|
||||
let comments = CommentTree::from_post(&*conn, &post, Some(&user))
|
||||
let comments = CommentTree::from_post(&conn, &post, Some(&user))
|
||||
.expect("comments::create: comments error");
|
||||
|
||||
let previous = form
|
||||
.responding_to
|
||||
.and_then(|r| Comment::get(&*conn, r).ok());
|
||||
let previous = form.responding_to.and_then(|r| Comment::get(&conn, r).ok());
|
||||
|
||||
render!(posts::details(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
post.clone(),
|
||||
blog,
|
||||
&*form,
|
||||
errors,
|
||||
Tag::for_post(&*conn, post.id).expect("comments::create: tags error"),
|
||||
Tag::for_post(&conn, post.id).expect("comments::create: tags error"),
|
||||
comments,
|
||||
previous,
|
||||
post.count_likes(&*conn)
|
||||
post.count_likes(&conn)
|
||||
.expect("comments::create: count likes error"),
|
||||
post.count_reshares(&*conn)
|
||||
post.count_reshares(&conn)
|
||||
.expect("comments::create: count reshares error"),
|
||||
user.has_liked(&*conn, &post)
|
||||
user.has_liked(&conn, &post)
|
||||
.expect("comments::create: liked error"),
|
||||
user.has_reshared(&*conn, &post)
|
||||
user.has_reshared(&conn, &post)
|
||||
.expect("comments::create: reshared error"),
|
||||
user.is_following(
|
||||
&*conn,
|
||||
post.get_authors(&*conn)
|
||||
post.get_authors(&conn)
|
||||
.expect("comments::create: authors error")[0]
|
||||
.id
|
||||
)
|
||||
.expect("comments::create: following error"),
|
||||
post.get_authors(&*conn)
|
||||
post.get_authors(&conn)
|
||||
.expect("comments::create: authors error")[0]
|
||||
.clone()
|
||||
))
|
||||
|
@ -145,14 +144,15 @@ pub fn delete(
|
|||
slug: String,
|
||||
id: i32,
|
||||
user: User,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Flash<Redirect>, ErrorPage> {
|
||||
if let Ok(comment) = Comment::get(&*rockets.conn, id) {
|
||||
if let Ok(comment) = Comment::get(&conn, id) {
|
||||
if comment.author_id == user.id {
|
||||
let dest = User::one_by_instance(&*rockets.conn)?;
|
||||
let delete_activity = comment.build_delete(&*rockets.conn)?;
|
||||
let dest = User::one_by_instance(&conn)?;
|
||||
let delete_activity = comment.build_delete(&conn)?;
|
||||
inbox(
|
||||
&rockets,
|
||||
&conn,
|
||||
serde_json::to_value(&delete_activity).map_err(Error::from)?,
|
||||
)?;
|
||||
|
||||
|
@ -160,7 +160,6 @@ pub fn delete(
|
|||
rockets.worker.execute(move || {
|
||||
broadcast(&user_c, delete_activity, dest, CONFIG.proxy().cloned())
|
||||
});
|
||||
let conn = rockets.conn;
|
||||
rockets
|
||||
.worker
|
||||
.execute_after(Duration::from_secs(10 * 60), move || {
|
||||
|
@ -185,10 +184,10 @@ pub fn activity_pub(
|
|||
_slug: String,
|
||||
id: i32,
|
||||
_ap: ApRequest,
|
||||
rockets: PlumeRocket,
|
||||
conn: DbConn,
|
||||
) -> Option<ActivityStream<Note>> {
|
||||
Comment::get(&*rockets.conn, id)
|
||||
.and_then(|c| c.to_activity(&rockets))
|
||||
Comment::get(&conn, id)
|
||||
.and_then(|c| c.to_activity(&conn))
|
||||
.ok()
|
||||
.map(ActivityStream::new)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::template_utils::{IntoContext, Ructe};
|
||||
use plume_models::{Error, PlumeRocket};
|
||||
use plume_models::{db_conn::DbConn, Error, PlumeRocket};
|
||||
use rocket::{
|
||||
response::{self, Responder},
|
||||
Request,
|
||||
|
@ -17,40 +17,48 @@ impl From<Error> for ErrorPage {
|
|||
|
||||
impl<'r> Responder<'r> for ErrorPage {
|
||||
fn respond_to(self, req: &Request<'_>) -> response::Result<'r> {
|
||||
let conn = req.guard::<DbConn>().unwrap();
|
||||
let rockets = req.guard::<PlumeRocket>().unwrap();
|
||||
|
||||
match self.0 {
|
||||
Error::NotFound => render!(errors::not_found(&rockets.to_context())).respond_to(req),
|
||||
Error::Unauthorized => {
|
||||
render!(errors::not_found(&rockets.to_context())).respond_to(req)
|
||||
Error::NotFound => {
|
||||
render!(errors::not_found(&(&conn, &rockets).to_context())).respond_to(req)
|
||||
}
|
||||
_ => render!(errors::not_found(&rockets.to_context())).respond_to(req),
|
||||
Error::Unauthorized => {
|
||||
render!(errors::not_found(&(&conn, &rockets).to_context())).respond_to(req)
|
||||
}
|
||||
_ => render!(errors::not_found(&(&conn, &rockets).to_context())).respond_to(req),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[catch(404)]
|
||||
pub fn not_found(req: &Request<'_>) -> Ructe {
|
||||
let conn = req.guard::<DbConn>().unwrap();
|
||||
let rockets = req.guard::<PlumeRocket>().unwrap();
|
||||
render!(errors::not_found(&rockets.to_context()))
|
||||
render!(errors::not_found(&(&conn, &rockets).to_context()))
|
||||
}
|
||||
|
||||
#[catch(422)]
|
||||
pub fn unprocessable_entity(req: &Request<'_>) -> Ructe {
|
||||
let conn = req.guard::<DbConn>().unwrap();
|
||||
let rockets = req.guard::<PlumeRocket>().unwrap();
|
||||
render!(errors::unprocessable_entity(&rockets.to_context()))
|
||||
render!(errors::unprocessable_entity(
|
||||
&(&conn, &rockets).to_context()
|
||||
))
|
||||
}
|
||||
|
||||
#[catch(500)]
|
||||
pub fn server_error(req: &Request<'_>) -> Ructe {
|
||||
let conn = req.guard::<DbConn>().unwrap();
|
||||
let rockets = req.guard::<PlumeRocket>().unwrap();
|
||||
render!(errors::server_error(&rockets.to_context()))
|
||||
render!(errors::server_error(&(&conn, &rockets).to_context()))
|
||||
}
|
||||
|
||||
#[post("/csrf-violation?<target>")]
|
||||
pub fn csrf_violation(target: Option<String>, rockets: PlumeRocket) -> Ructe {
|
||||
pub fn csrf_violation(target: Option<String>, conn: DbConn, rockets: PlumeRocket) -> Ructe {
|
||||
if let Some(uri) = target {
|
||||
warn!("Csrf violation while accessing \"{}\"", uri)
|
||||
}
|
||||
render!(errors::csrf(&rockets.to_context()))
|
||||
render!(errors::csrf(&(&conn, &rockets).to_context()))
|
||||
}
|
||||
|
|
|
@ -27,8 +27,7 @@ use plume_models::{
|
|||
};
|
||||
|
||||
#[get("/")]
|
||||
pub fn index(rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
let conn = &*rockets.conn;
|
||||
pub fn index(conn: DbConn, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
let inst = Instance::get_local()?;
|
||||
let timelines = Timeline::list_all_for_user(&conn, rockets.user.clone().map(|u| u.id))?
|
||||
.into_iter()
|
||||
|
@ -42,19 +41,19 @@ pub fn index(rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
|||
.collect();
|
||||
|
||||
Ok(render!(instance::index(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
inst,
|
||||
User::count_local(conn)?,
|
||||
Post::count_local(conn)?,
|
||||
User::count_local(&conn)?,
|
||||
Post::count_local(&conn)?,
|
||||
timelines
|
||||
)))
|
||||
}
|
||||
|
||||
#[get("/admin")]
|
||||
pub fn admin(_admin: Admin, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
pub fn admin(_admin: Admin, conn: DbConn, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
let local_inst = Instance::get_local()?;
|
||||
Ok(render!(instance::admin(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
local_inst.clone(),
|
||||
InstanceSettingsForm {
|
||||
name: local_inst.name.clone(),
|
||||
|
@ -68,8 +67,8 @@ pub fn admin(_admin: Admin, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
|||
}
|
||||
|
||||
#[get("/admin", rank = 2)]
|
||||
pub fn admin_mod(_mod: Moderator, rockets: PlumeRocket) -> Ructe {
|
||||
render!(instance::admin_mod(&rockets.to_context()))
|
||||
pub fn admin_mod(_mod: Moderator, conn: DbConn, rockets: PlumeRocket) -> Ructe {
|
||||
render!(instance::admin_mod(&(&conn, &rockets).to_context()))
|
||||
}
|
||||
|
||||
#[derive(Clone, FromForm, Validate)]
|
||||
|
@ -87,14 +86,14 @@ pub struct InstanceSettingsForm {
|
|||
pub fn update_settings(
|
||||
_admin: Admin,
|
||||
form: LenientForm<InstanceSettingsForm>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> RespondOrRedirect {
|
||||
let conn = &*rockets.conn;
|
||||
if let Err(e) = form.validate() {
|
||||
let local_inst =
|
||||
Instance::get_local().expect("instance::update_settings: local instance error");
|
||||
render!(instance::admin(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
local_inst,
|
||||
form.clone(),
|
||||
e
|
||||
|
@ -105,7 +104,7 @@ pub fn update_settings(
|
|||
Instance::get_local().expect("instance::update_settings: local instance error");
|
||||
instance
|
||||
.update(
|
||||
conn,
|
||||
&*conn,
|
||||
form.name.clone(),
|
||||
form.open_registrations,
|
||||
form.short_description.clone(),
|
||||
|
@ -125,16 +124,17 @@ pub fn update_settings(
|
|||
pub fn admin_instances(
|
||||
_mod: Moderator,
|
||||
page: Option<Page>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
let instances = Instance::page(&*rockets.conn, page.limits())?;
|
||||
let instances = Instance::page(&conn, page.limits())?;
|
||||
Ok(render!(instance::list(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
Instance::get_local()?,
|
||||
instances,
|
||||
page.0,
|
||||
Page::total(Instance::count(&*rockets.conn)? as i32)
|
||||
Page::total(Instance::count(&conn)? as i32)
|
||||
)))
|
||||
}
|
||||
|
||||
|
@ -145,14 +145,14 @@ pub fn toggle_block(
|
|||
id: i32,
|
||||
intl: I18n,
|
||||
) -> Result<Flash<Redirect>, ErrorPage> {
|
||||
let inst = Instance::get(&*conn, id)?;
|
||||
let inst = Instance::get(&conn, id)?;
|
||||
let message = if inst.blocked {
|
||||
i18n!(intl.catalog, "{} has been unblocked."; &inst.name)
|
||||
} else {
|
||||
i18n!(intl.catalog, "{} has been blocked."; &inst.name)
|
||||
};
|
||||
|
||||
inst.toggle_block(&*conn)?;
|
||||
inst.toggle_block(&conn)?;
|
||||
Ok(Flash::success(
|
||||
Redirect::to(uri!(admin_instances: page = _)),
|
||||
message,
|
||||
|
@ -163,14 +163,15 @@ pub fn toggle_block(
|
|||
pub fn admin_users(
|
||||
_mod: Moderator,
|
||||
page: Option<Page>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
Ok(render!(instance::users(
|
||||
&rockets.to_context(),
|
||||
User::get_local_page(&*rockets.conn, page.limits())?,
|
||||
&(&conn, &rockets).to_context(),
|
||||
User::get_local_page(&conn, page.limits())?,
|
||||
page.0,
|
||||
Page::total(User::count_local(&*rockets.conn)? as i32)
|
||||
Page::total(User::count_local(&conn)? as i32)
|
||||
)))
|
||||
}
|
||||
pub struct BlocklistEmailDeletion {
|
||||
|
@ -193,9 +194,10 @@ impl<'f> FromForm<'f> for BlocklistEmailDeletion {
|
|||
pub fn delete_email_blocklist(
|
||||
_mod: Moderator,
|
||||
form: Form<BlocklistEmailDeletion>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Flash<Redirect>, ErrorPage> {
|
||||
BlocklistedEmail::delete_entries(&*rockets.conn, form.0.ids)?;
|
||||
BlocklistedEmail::delete_entries(&conn, form.0.ids)?;
|
||||
Ok(Flash::success(
|
||||
Redirect::to(uri!(admin_email_blocklist: page = None)),
|
||||
i18n!(rockets.intl.catalog, "Blocks deleted"),
|
||||
|
@ -206,9 +208,10 @@ pub fn delete_email_blocklist(
|
|||
pub fn add_email_blocklist(
|
||||
_mod: Moderator,
|
||||
form: LenientForm<NewBlocklistedEmail>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Flash<Redirect>, ErrorPage> {
|
||||
let result = BlocklistedEmail::insert(&*rockets.conn, form.0);
|
||||
let result = BlocklistedEmail::insert(&conn, form.0);
|
||||
|
||||
if let Err(Error::Db(_)) = result {
|
||||
Ok(Flash::error(
|
||||
|
@ -226,14 +229,15 @@ pub fn add_email_blocklist(
|
|||
pub fn admin_email_blocklist(
|
||||
_mod: Moderator,
|
||||
page: Option<Page>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
Ok(render!(instance::emailblocklist(
|
||||
&rockets.to_context(),
|
||||
BlocklistedEmail::page(&*rockets.conn, page.limits())?,
|
||||
&(&conn, &rockets).to_context(),
|
||||
BlocklistedEmail::page(&conn, page.limits())?,
|
||||
page.0,
|
||||
Page::total(BlocklistedEmail::count(&*rockets.conn)? as i32)
|
||||
Page::total(BlocklistedEmail::count(&conn)? as i32)
|
||||
)))
|
||||
}
|
||||
|
||||
|
@ -303,6 +307,7 @@ impl FromStr for UserActions {
|
|||
pub fn edit_users(
|
||||
moderator: Moderator,
|
||||
form: LenientForm<MultiAction<UserActions>>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Flash<Redirect>, ErrorPage> {
|
||||
// you can't change your own rights
|
||||
|
@ -329,27 +334,26 @@ pub fn edit_users(
|
|||
}
|
||||
}
|
||||
|
||||
let conn = &rockets.conn;
|
||||
let worker = &*rockets.worker;
|
||||
match form.action {
|
||||
UserActions::Admin => {
|
||||
for u in form.ids.clone() {
|
||||
User::get(conn, u)?.set_role(conn, Role::Admin)?;
|
||||
User::get(&conn, u)?.set_role(&conn, Role::Admin)?;
|
||||
}
|
||||
}
|
||||
UserActions::Moderator => {
|
||||
for u in form.ids.clone() {
|
||||
User::get(conn, u)?.set_role(conn, Role::Moderator)?;
|
||||
User::get(&conn, u)?.set_role(&conn, Role::Moderator)?;
|
||||
}
|
||||
}
|
||||
UserActions::RevokeAdmin | UserActions::RevokeModerator => {
|
||||
for u in form.ids.clone() {
|
||||
User::get(conn, u)?.set_role(conn, Role::Normal)?;
|
||||
User::get(&conn, u)?.set_role(&conn, Role::Normal)?;
|
||||
}
|
||||
}
|
||||
UserActions::Ban => {
|
||||
for u in form.ids.clone() {
|
||||
ban(u, conn, worker)?;
|
||||
ban(u, &conn, worker)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -387,40 +391,33 @@ fn ban(id: i32, conn: &Connection, worker: &ScheduledThreadPool) -> Result<(), E
|
|||
|
||||
#[post("/inbox", data = "<data>")]
|
||||
pub fn shared_inbox(
|
||||
rockets: PlumeRocket,
|
||||
conn: DbConn,
|
||||
data: inbox::SignedJson<serde_json::Value>,
|
||||
headers: Headers<'_>,
|
||||
) -> Result<String, status::BadRequest<&'static str>> {
|
||||
inbox::handle_incoming(rockets, data, headers)
|
||||
inbox::handle_incoming(conn, data, headers)
|
||||
}
|
||||
|
||||
#[get("/remote_interact?<target>")]
|
||||
pub fn interact(rockets: PlumeRocket, user: Option<User>, target: String) -> Option<Redirect> {
|
||||
if User::find_by_fqn(&rockets, &target).is_ok() {
|
||||
pub fn interact(conn: DbConn, user: Option<User>, target: String) -> Option<Redirect> {
|
||||
if User::find_by_fqn(&conn, &target).is_ok() {
|
||||
return Some(Redirect::to(uri!(super::user::details: name = target)));
|
||||
}
|
||||
|
||||
if let Ok(post) = Post::from_id(&rockets, &target, None, CONFIG.proxy()) {
|
||||
if let Ok(post) = Post::from_id(&conn, &target, None, CONFIG.proxy()) {
|
||||
return Some(Redirect::to(uri!(
|
||||
super::posts::details: blog = post
|
||||
.get_blog(&rockets.conn)
|
||||
.expect("Can't retrieve blog")
|
||||
.fqn,
|
||||
super::posts::details: blog = post.get_blog(&conn).expect("Can't retrieve blog").fqn,
|
||||
slug = &post.slug,
|
||||
responding_to = _
|
||||
)));
|
||||
}
|
||||
|
||||
if let Ok(comment) = Comment::from_id(&rockets, &target, None, CONFIG.proxy()) {
|
||||
if comment.can_see(&rockets.conn, user.as_ref()) {
|
||||
let post = comment
|
||||
.get_post(&rockets.conn)
|
||||
.expect("Can't retrieve post");
|
||||
if let Ok(comment) = Comment::from_id(&conn, &target, None, CONFIG.proxy()) {
|
||||
if comment.can_see(&conn, user.as_ref()) {
|
||||
let post = comment.get_post(&conn).expect("Can't retrieve post");
|
||||
return Some(Redirect::to(uri!(
|
||||
super::posts::details: blog = post
|
||||
.get_blog(&rockets.conn)
|
||||
.expect("Can't retrieve blog")
|
||||
.fqn,
|
||||
super::posts::details: blog =
|
||||
post.get_blog(&conn).expect("Can't retrieve blog").fqn,
|
||||
slug = &post.slug,
|
||||
responding_to = comment.id
|
||||
)));
|
||||
|
@ -450,10 +447,10 @@ pub fn nodeinfo(conn: DbConn, version: String) -> Result<Json<serde_json::Value>
|
|||
"openRegistrations": local_inst.open_registrations,
|
||||
"usage": {
|
||||
"users": {
|
||||
"total": User::count_local(&*conn)?
|
||||
"total": User::count_local(&conn)?
|
||||
},
|
||||
"localPosts": Post::count_local(&*conn)?,
|
||||
"localComments": Comment::count_local(&*conn)?
|
||||
"localPosts": Post::count_local(&conn)?,
|
||||
"localComments": Comment::count_local(&conn)?
|
||||
},
|
||||
"metadata": {
|
||||
"nodeName": local_inst.name,
|
||||
|
@ -469,21 +466,20 @@ pub fn nodeinfo(conn: DbConn, version: String) -> Result<Json<serde_json::Value>
|
|||
}
|
||||
|
||||
#[get("/about")]
|
||||
pub fn about(rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
let conn = &*rockets.conn;
|
||||
pub fn about(conn: DbConn, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
Ok(render!(instance::about(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
Instance::get_local()?,
|
||||
Instance::get_local()?.main_admin(conn)?,
|
||||
User::count_local(conn)?,
|
||||
Post::count_local(conn)?,
|
||||
Instance::count(conn)? - 1
|
||||
Instance::get_local()?.main_admin(&conn)?,
|
||||
User::count_local(&conn)?,
|
||||
Post::count_local(&conn)?,
|
||||
Instance::count(&conn)? - 1
|
||||
)))
|
||||
}
|
||||
|
||||
#[get("/privacy")]
|
||||
pub fn privacy(rockets: PlumeRocket) -> Ructe {
|
||||
render!(instance::privacy(&rockets.to_context()))
|
||||
pub fn privacy(conn: DbConn, rockets: PlumeRocket) -> Ructe {
|
||||
render!(instance::privacy(&(&conn, &rockets).to_context()))
|
||||
}
|
||||
|
||||
#[get("/manifest.json")]
|
||||
|
|
|
@ -5,8 +5,8 @@ use crate::routes::errors::ErrorPage;
|
|||
use plume_common::activity_pub::broadcast;
|
||||
use plume_common::utils;
|
||||
use plume_models::{
|
||||
blogs::Blog, inbox::inbox, likes, posts::Post, timeline::*, users::User, Error, PlumeRocket,
|
||||
CONFIG,
|
||||
blogs::Blog, db_conn::DbConn, inbox::inbox, likes, posts::Post, timeline::*, users::User,
|
||||
Error, PlumeRocket, CONFIG,
|
||||
};
|
||||
|
||||
#[post("/~/<blog>/<slug>/like")]
|
||||
|
@ -14,17 +14,17 @@ pub fn create(
|
|||
blog: String,
|
||||
slug: String,
|
||||
user: User,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Redirect, ErrorPage> {
|
||||
let conn = &*rockets.conn;
|
||||
let b = Blog::find_by_fqn(&rockets, &blog)?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, b.id)?;
|
||||
let b = Blog::find_by_fqn(&conn, &blog)?;
|
||||
let post = Post::find_by_slug(&conn, &slug, b.id)?;
|
||||
|
||||
if !user.has_liked(&*conn, &post)? {
|
||||
let like = likes::Like::insert(&*conn, likes::NewLike::new(&post, &user))?;
|
||||
like.notify(&*conn)?;
|
||||
|
||||
Timeline::add_to_all_timelines(&rockets, &post, Kind::Like(&user))?;
|
||||
Timeline::add_to_all_timelines(&conn, &post, Kind::Like(&user))?;
|
||||
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
let act = like.to_activity(&*conn)?;
|
||||
|
@ -32,14 +32,14 @@ pub fn create(
|
|||
.worker
|
||||
.execute(move || broadcast(&user, act, dest, CONFIG.proxy().cloned()));
|
||||
} else {
|
||||
let like = likes::Like::find_by_user_on_post(&*conn, user.id, post.id)?;
|
||||
let delete_act = like.build_undo(&*conn)?;
|
||||
let like = likes::Like::find_by_user_on_post(&conn, user.id, post.id)?;
|
||||
let delete_act = like.build_undo(&conn)?;
|
||||
inbox(
|
||||
&rockets,
|
||||
&conn,
|
||||
serde_json::to_value(&delete_act).map_err(Error::from)?,
|
||||
)?;
|
||||
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
let dest = User::one_by_instance(&conn)?;
|
||||
rockets
|
||||
.worker
|
||||
.execute(move || broadcast(&user, delete_act, dest, CONFIG.proxy().cloned()));
|
||||
|
|
|
@ -15,20 +15,25 @@ use rocket_i18n::I18n;
|
|||
use std::fs;
|
||||
|
||||
#[get("/medias?<page>")]
|
||||
pub fn list(user: User, page: Option<Page>, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
pub fn list(
|
||||
user: User,
|
||||
page: Option<Page>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
let medias = Media::page_for_user(&*rockets.conn, &user, page.limits())?;
|
||||
let medias = Media::page_for_user(&conn, &user, page.limits())?;
|
||||
Ok(render!(medias::index(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
medias,
|
||||
page.0,
|
||||
Page::total(Media::count_for_user(&*rockets.conn, &user)? as i32)
|
||||
Page::total(Media::count_for_user(&conn, &user)? as i32)
|
||||
)))
|
||||
}
|
||||
|
||||
#[get("/medias/new")]
|
||||
pub fn new(_user: User, rockets: PlumeRocket) -> Ructe {
|
||||
render!(medias::new(&rockets.to_context()))
|
||||
pub fn new(_user: User, conn: DbConn, rockets: PlumeRocket) -> Ructe {
|
||||
render!(medias::new(&(&conn, &rockets).to_context()))
|
||||
}
|
||||
|
||||
#[post("/medias/new", data = "<data>")]
|
||||
|
@ -95,7 +100,7 @@ pub fn upload(
|
|||
.map(|cw| cw.is_empty())
|
||||
.unwrap_or(false);
|
||||
let media = Media::insert(
|
||||
&*conn,
|
||||
&conn,
|
||||
NewMedia {
|
||||
file_path: dest,
|
||||
alt_text: read(&fields["alt"][0].data)?,
|
||||
|
@ -126,10 +131,18 @@ fn read(data: &SavedData) -> Result<String, status::BadRequest<&'static str>> {
|
|||
}
|
||||
|
||||
#[get("/medias/<id>")]
|
||||
pub fn details(id: i32, user: User, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
let media = Media::get(&*rockets.conn, id)?;
|
||||
pub fn details(
|
||||
id: i32,
|
||||
user: User,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let media = Media::get(&conn, id)?;
|
||||
if media.owner_id == user.id {
|
||||
Ok(render!(medias::details(&rockets.to_context(), media)))
|
||||
Ok(render!(medias::details(
|
||||
&(&conn, &rockets).to_context(),
|
||||
media
|
||||
)))
|
||||
} else {
|
||||
Err(Error::Unauthorized.into())
|
||||
}
|
||||
|
|
|
@ -4,20 +4,21 @@ use rocket_i18n::I18n;
|
|||
use crate::routes::{errors::ErrorPage, Page};
|
||||
use crate::template_utils::{IntoContext, Ructe};
|
||||
use plume_common::utils;
|
||||
use plume_models::{notifications::Notification, users::User, PlumeRocket};
|
||||
use plume_models::{db_conn::DbConn, notifications::Notification, users::User, PlumeRocket};
|
||||
|
||||
#[get("/notifications?<page>")]
|
||||
pub fn notifications(
|
||||
user: User,
|
||||
page: Option<Page>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
Ok(render!(notifications::index(
|
||||
&rockets.to_context(),
|
||||
Notification::page_for_user(&*rockets.conn, &user, page.limits())?,
|
||||
&(&conn, &rockets).to_context(),
|
||||
Notification::page_for_user(&conn, &user, page.limits())?,
|
||||
page.0,
|
||||
Page::total(Notification::count_for_user(&*rockets.conn, &user)? as i32)
|
||||
Page::total(Notification::count_for_user(&conn, &user)? as i32)
|
||||
)))
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ use plume_common::utils;
|
|||
use plume_models::{
|
||||
blogs::*,
|
||||
comments::{Comment, CommentTree},
|
||||
db_conn::DbConn,
|
||||
inbox::inbox,
|
||||
instance::Instance,
|
||||
medias::Media,
|
||||
|
@ -38,42 +39,42 @@ pub fn details(
|
|||
blog: String,
|
||||
slug: String,
|
||||
responding_to: Option<i32>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let conn = &*rockets.conn;
|
||||
let user = rockets.user.clone();
|
||||
let blog = Blog::find_by_fqn(&rockets, &blog)?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, blog.id)?;
|
||||
let blog = Blog::find_by_fqn(&conn, &blog)?;
|
||||
let post = Post::find_by_slug(&conn, &slug, blog.id)?;
|
||||
if !(post.published
|
||||
|| post
|
||||
.get_authors(&*conn)?
|
||||
.get_authors(&conn)?
|
||||
.into_iter()
|
||||
.any(|a| a.id == user.clone().map(|u| u.id).unwrap_or(0)))
|
||||
{
|
||||
return Ok(render!(errors::not_authorized(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
i18n!(rockets.intl.catalog, "This post isn't published yet.")
|
||||
)));
|
||||
}
|
||||
|
||||
let comments = CommentTree::from_post(&*conn, &post, user.as_ref())?;
|
||||
let comments = CommentTree::from_post(&conn, &post, user.as_ref())?;
|
||||
|
||||
let previous = responding_to.and_then(|r| Comment::get(&*conn, r).ok());
|
||||
let previous = responding_to.and_then(|r| Comment::get(&conn, r).ok());
|
||||
|
||||
Ok(render!(posts::details(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
post.clone(),
|
||||
blog,
|
||||
&NewCommentForm {
|
||||
warning: previous.clone().map(|p| p.spoiler_text).unwrap_or_default(),
|
||||
content: previous.clone().and_then(|p| Some(format!(
|
||||
"@{} {}",
|
||||
p.get_author(&*conn).ok()?.fqn,
|
||||
Mention::list_for_comment(&*conn, p.id).ok()?
|
||||
p.get_author(&conn).ok()?.fqn,
|
||||
Mention::list_for_comment(&conn, p.id).ok()?
|
||||
.into_iter()
|
||||
.filter_map(|m| {
|
||||
let user = user.clone();
|
||||
if let Ok(mentioned) = m.get_mentioned(&*conn) {
|
||||
if let Ok(mentioned) = m.get_mentioned(&conn) {
|
||||
if user.is_none() || mentioned.id != user.expect("posts::details_response: user error while listing mentions").id {
|
||||
Some(format!("@{}", mentioned.fqn))
|
||||
} else {
|
||||
|
@ -87,15 +88,15 @@ pub fn details(
|
|||
..NewCommentForm::default()
|
||||
},
|
||||
ValidationErrors::default(),
|
||||
Tag::for_post(&*conn, post.id)?,
|
||||
Tag::for_post(&conn, post.id)?,
|
||||
comments,
|
||||
previous,
|
||||
post.count_likes(&*conn)?,
|
||||
post.count_reshares(&*conn)?,
|
||||
user.clone().and_then(|u| u.has_liked(&*conn, &post).ok()).unwrap_or(false),
|
||||
user.clone().and_then(|u| u.has_reshared(&*conn, &post).ok()).unwrap_or(false),
|
||||
user.and_then(|u| u.is_following(&*conn, post.get_authors(&*conn).ok()?[0].id).ok()).unwrap_or(false),
|
||||
post.get_authors(&*conn)?[0].clone()
|
||||
post.count_likes(&conn)?,
|
||||
post.count_reshares(&conn)?,
|
||||
user.clone().and_then(|u| u.has_liked(&conn, &post).ok()).unwrap_or(false),
|
||||
user.clone().and_then(|u| u.has_reshared(&conn, &post).ok()).unwrap_or(false),
|
||||
user.and_then(|u| u.is_following(&conn, post.get_authors(&conn).ok()?[0].id).ok()).unwrap_or(false),
|
||||
post.get_authors(&conn)?[0].clone()
|
||||
)))
|
||||
}
|
||||
|
||||
|
@ -104,14 +105,13 @@ pub fn activity_details(
|
|||
blog: String,
|
||||
slug: String,
|
||||
_ap: ApRequest,
|
||||
rockets: PlumeRocket,
|
||||
conn: DbConn,
|
||||
) -> Result<ActivityStream<LicensedArticle>, Option<String>> {
|
||||
let conn = &*rockets.conn;
|
||||
let blog = Blog::find_by_fqn(&rockets, &blog).map_err(|_| None)?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, blog.id).map_err(|_| None)?;
|
||||
let blog = Blog::find_by_fqn(&conn, &blog).map_err(|_| None)?;
|
||||
let post = Post::find_by_slug(&conn, &slug, blog.id).map_err(|_| None)?;
|
||||
if post.published {
|
||||
Ok(ActivityStream::new(
|
||||
post.to_activity(&*conn)
|
||||
post.to_activity(&conn)
|
||||
.map_err(|_| String::from("Post serialization error"))?,
|
||||
))
|
||||
} else {
|
||||
|
@ -131,22 +131,26 @@ pub fn new_auth(blog: String, i18n: I18n) -> Flash<Redirect> {
|
|||
}
|
||||
|
||||
#[get("/~/<blog>/new", rank = 1)]
|
||||
pub fn new(blog: String, cl: ContentLen, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
let conn = &*rockets.conn;
|
||||
let b = Blog::find_by_fqn(&rockets, &blog)?;
|
||||
pub fn new(
|
||||
blog: String,
|
||||
cl: ContentLen,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let b = Blog::find_by_fqn(&conn, &blog)?;
|
||||
let user = rockets.user.clone().unwrap();
|
||||
|
||||
if !user.is_author_in(&*conn, &b)? {
|
||||
if !user.is_author_in(&conn, &b)? {
|
||||
// TODO actually return 403 error code
|
||||
return Ok(render!(errors::not_authorized(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
i18n!(rockets.intl.catalog, "You are not an author of this blog.")
|
||||
)));
|
||||
}
|
||||
|
||||
let medias = Media::for_user(&*conn, user.id)?;
|
||||
let medias = Media::for_user(&conn, user.id)?;
|
||||
Ok(render!(posts::new(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
i18n!(rockets.intl.catalog, "New post"),
|
||||
b,
|
||||
false,
|
||||
|
@ -167,17 +171,17 @@ pub fn edit(
|
|||
blog: String,
|
||||
slug: String,
|
||||
cl: ContentLen,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let conn = &*rockets.conn;
|
||||
let intl = &rockets.intl.catalog;
|
||||
let b = Blog::find_by_fqn(&rockets, &blog)?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, b.id)?;
|
||||
let b = Blog::find_by_fqn(&conn, &blog)?;
|
||||
let post = Post::find_by_slug(&conn, &slug, b.id)?;
|
||||
let user = rockets.user.clone().unwrap();
|
||||
|
||||
if !user.is_author_in(&*conn, &b)? {
|
||||
if !user.is_author_in(&conn, &b)? {
|
||||
return Ok(render!(errors::not_authorized(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
i18n!(intl, "You are not an author of this blog.")
|
||||
)));
|
||||
}
|
||||
|
@ -188,10 +192,10 @@ pub fn edit(
|
|||
post.content.get().clone() // fallback to HTML if the markdown was not stored
|
||||
};
|
||||
|
||||
let medias = Media::for_user(&*conn, user.id)?;
|
||||
let medias = Media::for_user(&conn, user.id)?;
|
||||
let title = post.title.clone();
|
||||
Ok(render!(posts::new(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
i18n!(intl, "Edit {0}"; &title),
|
||||
b,
|
||||
true,
|
||||
|
@ -199,7 +203,7 @@ pub fn edit(
|
|||
title: post.title.clone(),
|
||||
subtitle: post.subtitle.clone(),
|
||||
content: source,
|
||||
tags: Tag::for_post(&*conn, post.id)?
|
||||
tags: Tag::for_post(&conn, post.id)?
|
||||
.into_iter()
|
||||
.filter_map(|t| if !t.is_hashtag { Some(t.tag) } else { None })
|
||||
.collect::<Vec<String>>()
|
||||
|
@ -222,12 +226,12 @@ pub fn update(
|
|||
slug: String,
|
||||
cl: ContentLen,
|
||||
form: LenientForm<NewPostForm>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> RespondOrRedirect {
|
||||
let conn = &*rockets.conn;
|
||||
let b = Blog::find_by_fqn(&rockets, &blog).expect("post::update: blog error");
|
||||
let b = Blog::find_by_fqn(&conn, &blog).expect("post::update: blog error");
|
||||
let mut post =
|
||||
Post::find_by_slug(&*conn, &slug, b.id).expect("post::update: find by slug error");
|
||||
Post::find_by_slug(&conn, &slug, b.id).expect("post::update: find by slug error");
|
||||
let user = rockets.user.clone().unwrap();
|
||||
let intl = &rockets.intl.catalog;
|
||||
|
||||
|
@ -242,7 +246,7 @@ pub fn update(
|
|||
Err(e) => e,
|
||||
};
|
||||
|
||||
if new_slug != slug && Post::find_by_slug(&*conn, &new_slug, b.id).is_ok() {
|
||||
if new_slug != slug && Post::find_by_slug(&conn, &new_slug, b.id).is_ok() {
|
||||
errors.add(
|
||||
"title",
|
||||
ValidationError {
|
||||
|
@ -255,7 +259,7 @@ pub fn update(
|
|||
|
||||
if errors.is_empty() {
|
||||
if !user
|
||||
.is_author_in(&*conn, &b)
|
||||
.is_author_in(&conn, &b)
|
||||
.expect("posts::update: is author in error")
|
||||
{
|
||||
// actually it's not "Ok"…
|
||||
|
@ -298,14 +302,14 @@ pub fn update(
|
|||
post.source = form.content.clone();
|
||||
post.license = form.license.clone();
|
||||
post.cover_id = form.cover;
|
||||
post.update(&*conn).expect("post::update: update error");
|
||||
post.update(&conn).expect("post::update: update error");
|
||||
|
||||
if post.published {
|
||||
post.update_mentions(
|
||||
&conn,
|
||||
mentions
|
||||
.into_iter()
|
||||
.filter_map(|m| Mention::build_activity(&rockets, &m).ok())
|
||||
.filter_map(|m| Mention::build_activity(&conn, &m).ok())
|
||||
.collect(),
|
||||
)
|
||||
.expect("post::update: mentions error");
|
||||
|
@ -337,17 +341,17 @@ pub fn update(
|
|||
let act = post
|
||||
.create_activity(&conn)
|
||||
.expect("post::update: act error");
|
||||
let dest = User::one_by_instance(&*conn).expect("post::update: dest error");
|
||||
let dest = User::one_by_instance(&conn).expect("post::update: dest error");
|
||||
rockets
|
||||
.worker
|
||||
.execute(move || broadcast(&user, act, dest, CONFIG.proxy().cloned()));
|
||||
|
||||
Timeline::add_to_all_timelines(&rockets, &post, Kind::Original).ok();
|
||||
Timeline::add_to_all_timelines(&conn, &post, Kind::Original).ok();
|
||||
} else {
|
||||
let act = post
|
||||
.update_activity(&*conn)
|
||||
.update_activity(&conn)
|
||||
.expect("post::update: act error");
|
||||
let dest = User::one_by_instance(&*conn).expect("posts::update: dest error");
|
||||
let dest = User::one_by_instance(&conn).expect("posts::update: dest error");
|
||||
rockets
|
||||
.worker
|
||||
.execute(move || broadcast(&user, act, dest, CONFIG.proxy().cloned()));
|
||||
|
@ -365,9 +369,9 @@ pub fn update(
|
|||
.into()
|
||||
}
|
||||
} else {
|
||||
let medias = Media::for_user(&*conn, user.id).expect("posts:update: medias error");
|
||||
let medias = Media::for_user(&conn, user.id).expect("posts:update: medias error");
|
||||
render!(posts::new(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
i18n!(intl, "Edit {0}"; &form.title),
|
||||
b,
|
||||
true,
|
||||
|
@ -410,10 +414,10 @@ pub fn create(
|
|||
blog_name: String,
|
||||
form: LenientForm<NewPostForm>,
|
||||
cl: ContentLen,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<RespondOrRedirect, ErrorPage> {
|
||||
let conn = &*rockets.conn;
|
||||
let blog = Blog::find_by_fqn(&rockets, &blog_name).expect("post::create: blog error");
|
||||
let blog = Blog::find_by_fqn(&conn, &blog_name).expect("post::create: blog error");
|
||||
let slug = form.title.to_string().to_kebab_case();
|
||||
let user = rockets.user.clone().unwrap();
|
||||
|
||||
|
@ -421,7 +425,7 @@ pub fn create(
|
|||
Ok(_) => ValidationErrors::new(),
|
||||
Err(e) => e,
|
||||
};
|
||||
if Post::find_by_slug(&*conn, &slug, blog.id).is_ok() {
|
||||
if Post::find_by_slug(&conn, &slug, blog.id).is_ok() {
|
||||
errors.add(
|
||||
"title",
|
||||
ValidationError {
|
||||
|
@ -434,7 +438,7 @@ pub fn create(
|
|||
|
||||
if errors.is_empty() {
|
||||
if !user
|
||||
.is_author_in(&*conn, &blog)
|
||||
.is_author_in(&conn, &blog)
|
||||
.expect("post::create: is author in error")
|
||||
{
|
||||
// actually it's not "Ok"…
|
||||
|
@ -466,7 +470,7 @@ pub fn create(
|
|||
);
|
||||
|
||||
let post = Post::insert(
|
||||
&*conn,
|
||||
&conn,
|
||||
NewPost {
|
||||
blog_id: blog.id,
|
||||
slug: slug.to_string(),
|
||||
|
@ -484,7 +488,7 @@ pub fn create(
|
|||
.expect("post::create: post save error");
|
||||
|
||||
PostAuthor::insert(
|
||||
&*conn,
|
||||
&conn,
|
||||
NewPostAuthor {
|
||||
post_id: post.id,
|
||||
author_id: user.id,
|
||||
|
@ -500,7 +504,7 @@ pub fn create(
|
|||
.collect::<HashSet<_>>();
|
||||
for tag in tags {
|
||||
Tag::insert(
|
||||
&*conn,
|
||||
&conn,
|
||||
NewTag {
|
||||
tag: tag.to_string(),
|
||||
is_hashtag: false,
|
||||
|
@ -511,7 +515,7 @@ pub fn create(
|
|||
}
|
||||
for hashtag in hashtags {
|
||||
Tag::insert(
|
||||
&*conn,
|
||||
&conn,
|
||||
NewTag {
|
||||
tag: hashtag,
|
||||
is_hashtag: true,
|
||||
|
@ -524,9 +528,8 @@ pub fn create(
|
|||
if post.published {
|
||||
for m in mentions {
|
||||
Mention::from_activity(
|
||||
&*conn,
|
||||
&Mention::build_activity(&rockets, &m)
|
||||
.expect("post::create: mention build error"),
|
||||
&conn,
|
||||
&Mention::build_activity(&conn, &m).expect("post::create: mention build error"),
|
||||
post.id,
|
||||
true,
|
||||
true,
|
||||
|
@ -535,13 +538,13 @@ pub fn create(
|
|||
}
|
||||
|
||||
let act = post
|
||||
.create_activity(&*conn)
|
||||
.create_activity(&conn)
|
||||
.expect("posts::create: activity error");
|
||||
let dest = User::one_by_instance(&*conn).expect("posts::create: dest error");
|
||||
let dest = User::one_by_instance(&conn).expect("posts::create: dest error");
|
||||
let worker = &rockets.worker;
|
||||
worker.execute(move || broadcast(&user, act, dest, CONFIG.proxy().cloned()));
|
||||
|
||||
Timeline::add_to_all_timelines(&rockets, &post, Kind::Original)?;
|
||||
Timeline::add_to_all_timelines(&conn, &post, Kind::Original)?;
|
||||
}
|
||||
|
||||
Ok(Flash::success(
|
||||
|
@ -554,9 +557,9 @@ pub fn create(
|
|||
)
|
||||
.into())
|
||||
} else {
|
||||
let medias = Media::for_user(&*conn, user.id).expect("posts::create: medias error");
|
||||
let medias = Media::for_user(&conn, user.id).expect("posts::create: medias error");
|
||||
Ok(render!(posts::new(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
i18n!(rockets.intl.catalog, "New article"),
|
||||
blog,
|
||||
false,
|
||||
|
@ -575,16 +578,17 @@ pub fn create(
|
|||
pub fn delete(
|
||||
blog_name: String,
|
||||
slug: String,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
intl: I18n,
|
||||
) -> Result<Flash<Redirect>, ErrorPage> {
|
||||
let user = rockets.user.clone().unwrap();
|
||||
let post = Blog::find_by_fqn(&rockets, &blog_name)
|
||||
.and_then(|blog| Post::find_by_slug(&*rockets.conn, &slug, blog.id));
|
||||
let post = Blog::find_by_fqn(&conn, &blog_name)
|
||||
.and_then(|blog| Post::find_by_slug(&conn, &slug, blog.id));
|
||||
|
||||
if let Ok(post) = post {
|
||||
if !post
|
||||
.get_authors(&*rockets.conn)?
|
||||
.get_authors(&conn)?
|
||||
.into_iter()
|
||||
.any(|a| a.id == user.id)
|
||||
{
|
||||
|
@ -598,10 +602,10 @@ pub fn delete(
|
|||
));
|
||||
}
|
||||
|
||||
let dest = User::one_by_instance(&*rockets.conn)?;
|
||||
let delete_activity = post.build_delete(&*rockets.conn)?;
|
||||
let dest = User::one_by_instance(&conn)?;
|
||||
let delete_activity = post.build_delete(&conn)?;
|
||||
inbox(
|
||||
&rockets,
|
||||
&conn,
|
||||
serde_json::to_value(&delete_activity).map_err(Error::from)?,
|
||||
)?;
|
||||
|
||||
|
@ -609,11 +613,10 @@ pub fn delete(
|
|||
rockets
|
||||
.worker
|
||||
.execute(move || broadcast(&user_c, delete_activity, dest, CONFIG.proxy().cloned()));
|
||||
let conn = rockets.conn;
|
||||
rockets
|
||||
.worker
|
||||
.execute_after(Duration::from_secs(10 * 60), move || {
|
||||
user.rotate_keypair(&*conn)
|
||||
user.rotate_keypair(&conn)
|
||||
.expect("Failed to rotate keypair");
|
||||
});
|
||||
|
||||
|
@ -630,14 +633,15 @@ pub fn delete(
|
|||
|
||||
#[get("/~/<blog_name>/<slug>/remote_interact")]
|
||||
pub fn remote_interact(
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
blog_name: String,
|
||||
slug: String,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let target = Blog::find_by_fqn(&rockets, &blog_name)
|
||||
.and_then(|blog| Post::find_by_slug(&rockets.conn, &slug, blog.id))?;
|
||||
let target = Blog::find_by_fqn(&conn, &blog_name)
|
||||
.and_then(|blog| Post::find_by_slug(&conn, &slug, blog.id))?;
|
||||
Ok(render!(posts::remote_interact(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
target,
|
||||
super::session::LoginForm::default(),
|
||||
ValidationErrors::default(),
|
||||
|
@ -648,13 +652,14 @@ pub fn remote_interact(
|
|||
|
||||
#[post("/~/<blog_name>/<slug>/remote_interact", data = "<remote>")]
|
||||
pub fn remote_interact_post(
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
blog_name: String,
|
||||
slug: String,
|
||||
remote: LenientForm<RemoteForm>,
|
||||
) -> Result<RespondOrRedirect, ErrorPage> {
|
||||
let target = Blog::find_by_fqn(&rockets, &blog_name)
|
||||
.and_then(|blog| Post::find_by_slug(&rockets.conn, &slug, blog.id))?;
|
||||
let target = Blog::find_by_fqn(&conn, &blog_name)
|
||||
.and_then(|blog| Post::find_by_slug(&conn, &slug, blog.id))?;
|
||||
if let Some(uri) = User::fetch_remote_interact_uri(&remote.remote)
|
||||
.ok()
|
||||
.map(|uri| uri.replace("{uri}", &Uri::percent_encode(&target.ap_url)))
|
||||
|
@ -669,7 +674,7 @@ pub fn remote_interact_post(
|
|||
});
|
||||
//could not get your remote url?
|
||||
Ok(render!(posts::remote_interact(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
target,
|
||||
super::session::LoginForm::default(),
|
||||
ValidationErrors::default(),
|
||||
|
|
|
@ -5,8 +5,8 @@ use crate::routes::errors::ErrorPage;
|
|||
use plume_common::activity_pub::broadcast;
|
||||
use plume_common::utils;
|
||||
use plume_models::{
|
||||
blogs::Blog, inbox::inbox, posts::Post, reshares::*, timeline::*, users::User, Error,
|
||||
PlumeRocket, CONFIG,
|
||||
blogs::Blog, db_conn::DbConn, inbox::inbox, posts::Post, reshares::*, timeline::*, users::User,
|
||||
Error, PlumeRocket, CONFIG,
|
||||
};
|
||||
|
||||
#[post("/~/<blog>/<slug>/reshare")]
|
||||
|
@ -14,32 +14,32 @@ pub fn create(
|
|||
blog: String,
|
||||
slug: String,
|
||||
user: User,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Redirect, ErrorPage> {
|
||||
let conn = &*rockets.conn;
|
||||
let b = Blog::find_by_fqn(&rockets, &blog)?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, b.id)?;
|
||||
let b = Blog::find_by_fqn(&conn, &blog)?;
|
||||
let post = Post::find_by_slug(&conn, &slug, b.id)?;
|
||||
|
||||
if !user.has_reshared(&*conn, &post)? {
|
||||
let reshare = Reshare::insert(&*conn, NewReshare::new(&post, &user))?;
|
||||
reshare.notify(&*conn)?;
|
||||
if !user.has_reshared(&conn, &post)? {
|
||||
let reshare = Reshare::insert(&conn, NewReshare::new(&post, &user))?;
|
||||
reshare.notify(&conn)?;
|
||||
|
||||
Timeline::add_to_all_timelines(&rockets, &post, Kind::Reshare(&user))?;
|
||||
Timeline::add_to_all_timelines(&conn, &post, Kind::Reshare(&user))?;
|
||||
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
let act = reshare.to_activity(&*conn)?;
|
||||
let dest = User::one_by_instance(&conn)?;
|
||||
let act = reshare.to_activity(&conn)?;
|
||||
rockets
|
||||
.worker
|
||||
.execute(move || broadcast(&user, act, dest, CONFIG.proxy().cloned()));
|
||||
} else {
|
||||
let reshare = Reshare::find_by_user_on_post(&*conn, user.id, post.id)?;
|
||||
let delete_act = reshare.build_undo(&*conn)?;
|
||||
let reshare = Reshare::find_by_user_on_post(&conn, user.id, post.id)?;
|
||||
let delete_act = reshare.build_undo(&conn)?;
|
||||
inbox(
|
||||
&rockets,
|
||||
&conn,
|
||||
serde_json::to_value(&delete_act).map_err(Error::from)?,
|
||||
)?;
|
||||
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
let dest = User::one_by_instance(&conn)?;
|
||||
rockets
|
||||
.worker
|
||||
.execute(move || broadcast(&user, delete_act, dest, CONFIG.proxy().cloned()));
|
||||
|
|
|
@ -3,7 +3,7 @@ use rocket::request::Form;
|
|||
|
||||
use crate::routes::Page;
|
||||
use crate::template_utils::{IntoContext, Ructe};
|
||||
use plume_models::{search::Query, PlumeRocket};
|
||||
use plume_models::{db_conn::DbConn, search::Query, PlumeRocket};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Default, FromForm)]
|
||||
|
@ -50,8 +50,7 @@ macro_rules! param_to_query {
|
|||
}
|
||||
|
||||
#[get("/search?<query..>")]
|
||||
pub fn search(query: Option<Form<SearchQuery>>, rockets: PlumeRocket) -> Ructe {
|
||||
let conn = &*rockets.conn;
|
||||
pub fn search(query: Option<Form<SearchQuery>>, conn: DbConn, rockets: PlumeRocket) -> Ructe {
|
||||
let query = query.map(Form::into_inner).unwrap_or_default();
|
||||
let page = query.page.unwrap_or_default();
|
||||
let mut parsed_query =
|
||||
|
@ -65,7 +64,7 @@ pub fn search(query: Option<Form<SearchQuery>>, rockets: PlumeRocket) -> Ructe {
|
|||
|
||||
if str_query.is_empty() {
|
||||
render!(search::index(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
&format!("{}", Utc::today().format("%Y-%m-d"))
|
||||
))
|
||||
} else {
|
||||
|
@ -74,7 +73,7 @@ pub fn search(query: Option<Form<SearchQuery>>, rockets: PlumeRocket) -> Ructe {
|
|||
.search_document(&conn, parsed_query, page.limits());
|
||||
let next_page = if res.is_empty() { 0 } else { page.0 + 1 };
|
||||
render!(search::result(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
&str_query,
|
||||
res,
|
||||
page.0,
|
||||
|
|
|
@ -19,15 +19,16 @@ use validator::{Validate, ValidationError, ValidationErrors};
|
|||
use crate::mail::{build_mail, Mailer};
|
||||
use crate::template_utils::{IntoContext, Ructe};
|
||||
use plume_models::{
|
||||
db_conn::DbConn,
|
||||
password_reset_requests::*,
|
||||
users::{User, AUTH_COOKIE},
|
||||
Error, PlumeRocket, CONFIG,
|
||||
};
|
||||
|
||||
#[get("/login?<m>")]
|
||||
pub fn new(m: Option<String>, rockets: PlumeRocket) -> Ructe {
|
||||
pub fn new(m: Option<String>, conn: DbConn, rockets: PlumeRocket) -> Ructe {
|
||||
render!(session::login(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
m,
|
||||
&LoginForm::default(),
|
||||
ValidationErrors::default()
|
||||
|
@ -46,21 +47,27 @@ pub struct LoginForm {
|
|||
pub fn create(
|
||||
form: LenientForm<LoginForm>,
|
||||
mut cookies: Cookies<'_>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> RespondOrRedirect {
|
||||
let conn = &*rockets.conn;
|
||||
let mut errors = match form.validate() {
|
||||
Ok(_) => ValidationErrors::new(),
|
||||
Err(e) => e,
|
||||
};
|
||||
let user = User::login(conn, &form.email_or_name, &form.password);
|
||||
let user = User::login(&conn, &form.email_or_name, &form.password);
|
||||
let user_id = if let Ok(user) = user {
|
||||
user.id.to_string()
|
||||
} else {
|
||||
let mut err = ValidationError::new("invalid_login");
|
||||
err.message = Some(Cow::from("Invalid username, or password"));
|
||||
errors.add("email_or_name", err);
|
||||
return render!(session::login(&rockets.to_context(), None, &*form, errors)).into();
|
||||
return render!(session::login(
|
||||
&(&conn, &rockets).to_context(),
|
||||
None,
|
||||
&*form,
|
||||
errors
|
||||
))
|
||||
.into();
|
||||
};
|
||||
|
||||
cookies.add_private(
|
||||
|
@ -90,7 +97,7 @@ pub fn create(
|
|||
.into()
|
||||
} else {
|
||||
render!(session::login(
|
||||
&(conn, &rockets.intl.catalog, None, None),
|
||||
&(&conn, &rockets.intl.catalog, None, None),
|
||||
None,
|
||||
&*form,
|
||||
errors
|
||||
|
@ -124,9 +131,9 @@ impl PartialEq for ResetRequest {
|
|||
}
|
||||
|
||||
#[get("/password-reset")]
|
||||
pub fn password_reset_request_form(rockets: PlumeRocket) -> Ructe {
|
||||
pub fn password_reset_request_form(conn: DbConn, rockets: PlumeRocket) -> Ructe {
|
||||
render!(session::password_reset_request(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
&ResetForm::default(),
|
||||
ValidationErrors::default()
|
||||
))
|
||||
|
@ -142,10 +149,11 @@ pub struct ResetForm {
|
|||
pub fn password_reset_request(
|
||||
mail: State<'_, Arc<Mutex<Mailer>>>,
|
||||
form: LenientForm<ResetForm>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Ructe {
|
||||
if User::find_by_email(&*rockets.conn, &form.email).is_ok() {
|
||||
let token = PasswordResetRequest::insert(&*rockets.conn, &form.email)
|
||||
if User::find_by_email(&conn, &form.email).is_ok() {
|
||||
let token = PasswordResetRequest::insert(&conn, &form.email)
|
||||
.expect("password_reset_request::insert: error");
|
||||
|
||||
let url = format!("https://{}/password-reset/{}", CONFIG.base_url, token);
|
||||
|
@ -161,16 +169,22 @@ pub fn password_reset_request(
|
|||
}
|
||||
}
|
||||
}
|
||||
render!(session::password_reset_request_ok(&rockets.to_context()))
|
||||
render!(session::password_reset_request_ok(
|
||||
&(&conn, &rockets).to_context()
|
||||
))
|
||||
}
|
||||
|
||||
#[get("/password-reset/<token>")]
|
||||
pub fn password_reset_form(token: String, rockets: PlumeRocket) -> Result<Ructe, Ructe> {
|
||||
PasswordResetRequest::find_by_token(&*rockets.conn, &token)
|
||||
.map_err(|err| password_reset_error_response(err, &rockets))?;
|
||||
pub fn password_reset_form(
|
||||
token: String,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, Ructe> {
|
||||
PasswordResetRequest::find_by_token(&conn, &token)
|
||||
.map_err(|err| password_reset_error_response(err, &conn, &rockets))?;
|
||||
|
||||
Ok(render!(session::password_reset(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
&NewPasswordForm::default(),
|
||||
ValidationErrors::default()
|
||||
)))
|
||||
|
@ -199,15 +213,21 @@ fn passwords_match(form: &NewPasswordForm) -> Result<(), ValidationError> {
|
|||
pub fn password_reset(
|
||||
token: String,
|
||||
form: LenientForm<NewPasswordForm>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Flash<Redirect>, Ructe> {
|
||||
form.validate()
|
||||
.map_err(|err| render!(session::password_reset(&rockets.to_context(), &form, err)))?;
|
||||
form.validate().map_err(|err| {
|
||||
render!(session::password_reset(
|
||||
&(&conn, &rockets).to_context(),
|
||||
&form,
|
||||
err
|
||||
))
|
||||
})?;
|
||||
|
||||
PasswordResetRequest::find_and_delete_by_token(&*rockets.conn, &token)
|
||||
.and_then(|request| User::find_by_email(&*rockets.conn, &request.email))
|
||||
.and_then(|user| user.reset_password(&*rockets.conn, &form.password))
|
||||
.map_err(|err| password_reset_error_response(err, &rockets))?;
|
||||
PasswordResetRequest::find_and_delete_by_token(&conn, &token)
|
||||
.and_then(|request| User::find_by_email(&conn, &request.email))
|
||||
.and_then(|user| user.reset_password(&conn, &form.password))
|
||||
.map_err(|err| password_reset_error_response(err, &conn, &rockets))?;
|
||||
|
||||
Ok(Flash::success(
|
||||
Redirect::to(uri!(new: m = _)),
|
||||
|
@ -218,11 +238,11 @@ pub fn password_reset(
|
|||
))
|
||||
}
|
||||
|
||||
fn password_reset_error_response(err: Error, rockets: &PlumeRocket) -> Ructe {
|
||||
fn password_reset_error_response(err: Error, conn: &DbConn, rockets: &PlumeRocket) -> Ructe {
|
||||
match err {
|
||||
Error::Expired => render!(session::password_reset_request_expired(
|
||||
&rockets.to_context()
|
||||
&(conn, rockets).to_context()
|
||||
)),
|
||||
_ => render!(errors::not_found(&rockets.to_context())),
|
||||
_ => render!(errors::not_found(&(conn, rockets).to_context())),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
use crate::routes::{errors::ErrorPage, Page};
|
||||
use crate::template_utils::{IntoContext, Ructe};
|
||||
use plume_models::{posts::Post, PlumeRocket};
|
||||
use plume_models::{db_conn::DbConn, posts::Post, PlumeRocket};
|
||||
|
||||
#[get("/tag/<name>?<page>")]
|
||||
pub fn tag(name: String, page: Option<Page>, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
pub fn tag(
|
||||
name: String,
|
||||
page: Option<Page>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
let posts = Post::list_by_tag(&*rockets.conn, name.clone(), page.limits())?;
|
||||
let posts = Post::list_by_tag(&conn, name.clone(), page.limits())?;
|
||||
Ok(render!(tags::index(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
name.clone(),
|
||||
posts,
|
||||
page.0,
|
||||
Page::total(Post::count_for_tag(&*rockets.conn, name)? as i32)
|
||||
Page::total(Post::count_for_tag(&conn, name)? as i32)
|
||||
)))
|
||||
}
|
||||
|
|
|
@ -3,18 +3,23 @@
|
|||
use crate::routes::Page;
|
||||
use crate::template_utils::IntoContext;
|
||||
use crate::{routes::errors::ErrorPage, template_utils::Ructe};
|
||||
use plume_models::{timeline::*, PlumeRocket};
|
||||
use plume_models::{db_conn::DbConn, timeline::*, PlumeRocket};
|
||||
use rocket::response::Redirect;
|
||||
|
||||
#[get("/timeline/<id>?<page>")]
|
||||
pub fn details(id: i32, rockets: PlumeRocket, page: Option<Page>) -> Result<Ructe, ErrorPage> {
|
||||
pub fn details(
|
||||
id: i32,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
page: Option<Page>,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
let all_tl = Timeline::list_all_for_user(&rockets.conn, rockets.user.clone().map(|u| u.id))?;
|
||||
let tl = Timeline::get(&rockets.conn, id)?;
|
||||
let posts = tl.get_page(&rockets.conn, page.limits())?;
|
||||
let total_posts = tl.count_posts(&rockets.conn)?;
|
||||
let all_tl = Timeline::list_all_for_user(&conn, rockets.user.clone().map(|u| u.id))?;
|
||||
let tl = Timeline::get(&conn, id)?;
|
||||
let posts = tl.get_page(&conn, page.limits())?;
|
||||
let total_posts = tl.count_posts(&conn)?;
|
||||
Ok(render!(timelines::details(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
tl,
|
||||
posts,
|
||||
all_tl,
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
use activitypub::{
|
||||
activity::Create,
|
||||
collection::{OrderedCollection, OrderedCollectionPage},
|
||||
};
|
||||
use activitypub::collection::{OrderedCollection, OrderedCollectionPage};
|
||||
use diesel::SaveChangesDsl;
|
||||
use rocket::{
|
||||
http::{ContentType, Cookies},
|
||||
|
@ -10,27 +7,17 @@ use rocket::{
|
|||
};
|
||||
use rocket_i18n::I18n;
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
use tracing::{info, warn};
|
||||
use validator::{Validate, ValidationError, ValidationErrors};
|
||||
|
||||
use crate::inbox;
|
||||
use crate::routes::{errors::ErrorPage, Page, RemoteForm, RespondOrRedirect};
|
||||
use crate::template_utils::{IntoContext, Ructe};
|
||||
use plume_common::activity_pub::{broadcast, inbox::FromId, ActivityStream, ApRequest, Id};
|
||||
use plume_common::activity_pub::{broadcast, ActivityStream, ApRequest, Id};
|
||||
use plume_common::utils;
|
||||
use plume_models::{
|
||||
blogs::Blog,
|
||||
db_conn::DbConn,
|
||||
follows,
|
||||
headers::Headers,
|
||||
inbox::inbox as local_inbox,
|
||||
instance::Instance,
|
||||
medias::Media,
|
||||
posts::{LicensedArticle, Post},
|
||||
reshares::Reshare,
|
||||
safe_string::SafeString,
|
||||
users::*,
|
||||
Error, PlumeRocket, CONFIG,
|
||||
blogs::Blog, db_conn::DbConn, follows, headers::Headers, inbox::inbox as local_inbox,
|
||||
instance::Instance, medias::Media, posts::Post, reshares::Reshare, safe_string::SafeString,
|
||||
users::*, Error, PlumeRocket, CONFIG,
|
||||
};
|
||||
|
||||
#[get("/me")]
|
||||
|
@ -42,73 +29,18 @@ pub fn me(user: Option<User>) -> RespondOrRedirect {
|
|||
}
|
||||
|
||||
#[get("/@/<name>", rank = 2)]
|
||||
pub fn details(
|
||||
name: String,
|
||||
rockets: PlumeRocket,
|
||||
fetch_rockets: PlumeRocket,
|
||||
fetch_followers_rockets: PlumeRocket,
|
||||
update_conn: DbConn,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let conn = &*rockets.conn;
|
||||
let user = User::find_by_fqn(&rockets, &name)?;
|
||||
pub fn details(name: String, rockets: PlumeRocket, conn: DbConn) -> Result<Ructe, ErrorPage> {
|
||||
let user = User::find_by_fqn(&conn, &name)?;
|
||||
let recents = Post::get_recents_for_author(&*conn, &user, 6)?;
|
||||
let reshares = Reshare::get_recents_for_author(&*conn, &user, 6)?;
|
||||
let worker = &rockets.worker;
|
||||
|
||||
if !user.get_instance(&*conn)?.local {
|
||||
// Fetch new articles
|
||||
let user_clone = user.clone();
|
||||
worker.execute(move || {
|
||||
for create_act in user_clone
|
||||
.fetch_outbox::<Create>()
|
||||
.expect("Remote user: outbox couldn't be fetched")
|
||||
{
|
||||
match create_act.create_props.object_object::<LicensedArticle>() {
|
||||
Ok(article) => {
|
||||
Post::from_activity(&fetch_rockets, article)
|
||||
.expect("Article from remote user couldn't be saved");
|
||||
info!("Fetched article from remote user");
|
||||
}
|
||||
Err(e) => warn!("Error while fetching articles in background: {:?}", e),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Fetch followers
|
||||
let user_clone = user.clone();
|
||||
worker.execute(move || {
|
||||
for user_id in user_clone
|
||||
.fetch_followers_ids()
|
||||
.expect("Remote user: fetching followers error")
|
||||
{
|
||||
let follower =
|
||||
User::from_id(&fetch_followers_rockets, &user_id, None, CONFIG.proxy())
|
||||
.expect("user::details: Couldn't fetch follower");
|
||||
follows::Follow::insert(
|
||||
&*fetch_followers_rockets.conn,
|
||||
follows::NewFollow {
|
||||
follower_id: follower.id,
|
||||
following_id: user_clone.id,
|
||||
ap_url: String::new(),
|
||||
},
|
||||
)
|
||||
.expect("Couldn't save follower for remote user");
|
||||
}
|
||||
});
|
||||
|
||||
// Update profile information if needed
|
||||
let user_clone = user.clone();
|
||||
if user.needs_update() {
|
||||
worker.execute(move || {
|
||||
user_clone
|
||||
.refetch(&*update_conn)
|
||||
.expect("Couldn't update user info");
|
||||
});
|
||||
}
|
||||
tracing::trace!("remote user found");
|
||||
user.remote_user_found(); // Doesn't block
|
||||
}
|
||||
|
||||
Ok(render!(users::details(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
user.clone(),
|
||||
rockets
|
||||
.user
|
||||
|
@ -126,12 +58,12 @@ pub fn details(
|
|||
}
|
||||
|
||||
#[get("/dashboard")]
|
||||
pub fn dashboard(user: User, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
let blogs = Blog::find_for_author(&*rockets.conn, &user)?;
|
||||
pub fn dashboard(user: User, conn: DbConn, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
let blogs = Blog::find_for_author(&conn, &user)?;
|
||||
Ok(render!(users::dashboard(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
blogs,
|
||||
Post::drafts_by_author(&*rockets.conn, &user)?
|
||||
Post::drafts_by_author(&conn, &user)?
|
||||
)))
|
||||
}
|
||||
|
||||
|
@ -150,14 +82,14 @@ pub fn dashboard_auth(i18n: I18n) -> Flash<Redirect> {
|
|||
pub fn follow(
|
||||
name: String,
|
||||
user: User,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Flash<Redirect>, ErrorPage> {
|
||||
let conn = &*rockets.conn;
|
||||
let target = User::find_by_fqn(&rockets, &name)?;
|
||||
let message = if let Ok(follow) = follows::Follow::find(&*conn, user.id, target.id) {
|
||||
let delete_act = follow.build_undo(&*conn)?;
|
||||
let target = User::find_by_fqn(&conn, &name)?;
|
||||
let message = if let Ok(follow) = follows::Follow::find(&conn, user.id, target.id) {
|
||||
let delete_act = follow.build_undo(&conn)?;
|
||||
local_inbox(
|
||||
&rockets,
|
||||
&conn,
|
||||
serde_json::to_value(&delete_act).map_err(Error::from)?,
|
||||
)?;
|
||||
|
||||
|
@ -168,16 +100,16 @@ pub fn follow(
|
|||
msg
|
||||
} else {
|
||||
let f = follows::Follow::insert(
|
||||
&*conn,
|
||||
&conn,
|
||||
follows::NewFollow {
|
||||
follower_id: user.id,
|
||||
following_id: target.id,
|
||||
ap_url: String::new(),
|
||||
},
|
||||
)?;
|
||||
f.notify(&*conn)?;
|
||||
f.notify(&conn)?;
|
||||
|
||||
let act = f.to_activity(&*conn)?;
|
||||
let act = f.to_activity(&conn)?;
|
||||
let msg = i18n!(rockets.intl.catalog, "You are now following {}."; target.name());
|
||||
rockets
|
||||
.worker
|
||||
|
@ -192,12 +124,13 @@ pub fn follow(
|
|||
|
||||
#[post("/@/<name>/follow", data = "<remote_form>", rank = 2)]
|
||||
pub fn follow_not_connected(
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
name: String,
|
||||
remote_form: Option<LenientForm<RemoteForm>>,
|
||||
i18n: I18n,
|
||||
) -> Result<RespondOrRedirect, ErrorPage> {
|
||||
let target = User::find_by_fqn(&rockets, &name)?;
|
||||
let target = User::find_by_fqn(&conn, &name)?;
|
||||
if let Some(remote_form) = remote_form {
|
||||
if let Some(uri) = User::fetch_remote_interact_uri(&remote_form)
|
||||
.ok()
|
||||
|
@ -207,7 +140,7 @@ pub fn follow_not_connected(
|
|||
&format!(
|
||||
"{}@{}",
|
||||
target.fqn,
|
||||
target.get_instance(&rockets.conn).ok()?.public_domain
|
||||
target.get_instance(&conn).ok()?.public_domain
|
||||
),
|
||||
))
|
||||
})
|
||||
|
@ -224,7 +157,7 @@ pub fn follow_not_connected(
|
|||
);
|
||||
Ok(Flash::new(
|
||||
render!(users::follow_remote(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
target,
|
||||
super::session::LoginForm::default(),
|
||||
ValidationErrors::default(),
|
||||
|
@ -239,7 +172,7 @@ pub fn follow_not_connected(
|
|||
} else {
|
||||
Ok(Flash::new(
|
||||
render!(users::follow_remote(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
target,
|
||||
super::session::LoginForm::default(),
|
||||
ValidationErrors::default(),
|
||||
|
@ -269,24 +202,24 @@ pub fn follow_auth(name: String, i18n: I18n) -> Flash<Redirect> {
|
|||
pub fn followers(
|
||||
name: String,
|
||||
page: Option<Page>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let conn = &*rockets.conn;
|
||||
let page = page.unwrap_or_default();
|
||||
let user = User::find_by_fqn(&rockets, &name)?;
|
||||
let followers_count = user.count_followers(&*conn)?;
|
||||
let user = User::find_by_fqn(&conn, &name)?;
|
||||
let followers_count = user.count_followers(&conn)?;
|
||||
|
||||
Ok(render!(users::followers(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
user.clone(),
|
||||
rockets
|
||||
.user
|
||||
.clone()
|
||||
.and_then(|x| x.is_following(&*conn, user.id).ok())
|
||||
.and_then(|x| x.is_following(&conn, user.id).ok())
|
||||
.unwrap_or(false),
|
||||
user.instance_id != Instance::get_local()?.id,
|
||||
user.get_instance(&*conn)?.public_domain,
|
||||
user.get_followers_page(&*conn, page.limits())?,
|
||||
user.get_instance(&conn)?.public_domain,
|
||||
user.get_followers_page(&conn, page.limits())?,
|
||||
page.0,
|
||||
Page::total(followers_count as i32)
|
||||
)))
|
||||
|
@ -296,24 +229,24 @@ pub fn followers(
|
|||
pub fn followed(
|
||||
name: String,
|
||||
page: Option<Page>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let conn = &*rockets.conn;
|
||||
let page = page.unwrap_or_default();
|
||||
let user = User::find_by_fqn(&rockets, &name)?;
|
||||
let followed_count = user.count_followed(conn)?;
|
||||
let user = User::find_by_fqn(&conn, &name)?;
|
||||
let followed_count = user.count_followed(&conn)?;
|
||||
|
||||
Ok(render!(users::followed(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
user.clone(),
|
||||
rockets
|
||||
.user
|
||||
.clone()
|
||||
.and_then(|x| x.is_following(conn, user.id).ok())
|
||||
.and_then(|x| x.is_following(&conn, user.id).ok())
|
||||
.unwrap_or(false),
|
||||
user.instance_id != Instance::get_local()?.id,
|
||||
user.get_instance(conn)?.public_domain,
|
||||
user.get_followed_page(conn, page.limits())?,
|
||||
user.get_instance(&conn)?.public_domain,
|
||||
user.get_followed_page(&conn, page.limits())?,
|
||||
page.0,
|
||||
Page::total(followed_count as i32)
|
||||
)))
|
||||
|
@ -322,17 +255,17 @@ pub fn followed(
|
|||
#[get("/@/<name>", rank = 1)]
|
||||
pub fn activity_details(
|
||||
name: String,
|
||||
rockets: PlumeRocket,
|
||||
conn: DbConn,
|
||||
_ap: ApRequest,
|
||||
) -> Option<ActivityStream<CustomPerson>> {
|
||||
let user = User::find_by_fqn(&rockets, &name).ok()?;
|
||||
Some(ActivityStream::new(user.to_activity(&*rockets.conn).ok()?))
|
||||
let user = User::find_by_fqn(&conn, &name).ok()?;
|
||||
Some(ActivityStream::new(user.to_activity(&conn).ok()?))
|
||||
}
|
||||
|
||||
#[get("/users/new")]
|
||||
pub fn new(rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
pub fn new(conn: DbConn, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
Ok(render!(users::new(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
Instance::get_local()?.open_registrations,
|
||||
&NewUserForm::default(),
|
||||
ValidationErrors::default()
|
||||
|
@ -340,10 +273,15 @@ pub fn new(rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
|||
}
|
||||
|
||||
#[get("/@/<name>/edit")]
|
||||
pub fn edit(name: String, user: User, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
pub fn edit(
|
||||
name: String,
|
||||
user: User,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
if user.username == name && !name.contains('@') {
|
||||
Ok(render!(users::edit(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
UpdateUserForm {
|
||||
display_name: user.display_name.clone(),
|
||||
email: user.email.clone().unwrap_or_default(),
|
||||
|
@ -417,14 +355,15 @@ pub fn delete(
|
|||
name: String,
|
||||
user: User,
|
||||
mut cookies: Cookies<'_>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Flash<Redirect>, ErrorPage> {
|
||||
let account = User::find_by_fqn(&rockets, &name)?;
|
||||
let account = User::find_by_fqn(&conn, &name)?;
|
||||
if user.id == account.id {
|
||||
account.delete(&*rockets.conn)?;
|
||||
account.delete(&conn)?;
|
||||
|
||||
let target = User::one_by_instance(&*rockets.conn)?;
|
||||
let delete_act = account.delete_activity(&*rockets.conn)?;
|
||||
let target = User::one_by_instance(&conn)?;
|
||||
let delete_act = account.delete_activity(&conn)?;
|
||||
rockets
|
||||
.worker
|
||||
.execute(move || broadcast(&account, delete_act, target, CONFIG.proxy().cloned()));
|
||||
|
@ -515,9 +454,9 @@ fn to_validation(x: Error) -> ValidationErrors {
|
|||
#[post("/users/new", data = "<form>")]
|
||||
pub fn create(
|
||||
form: LenientForm<NewUserForm>,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Flash<Redirect>, Ructe> {
|
||||
let conn = &*rockets.conn;
|
||||
if !Instance::get_local()
|
||||
.map(|i| i.open_registrations)
|
||||
.unwrap_or(true)
|
||||
|
@ -537,7 +476,7 @@ pub fn create(
|
|||
form.validate()
|
||||
.and_then(|_| {
|
||||
NewUser::new_local(
|
||||
conn,
|
||||
&conn,
|
||||
form.username.to_string(),
|
||||
form.username.to_string(),
|
||||
Role::Normal,
|
||||
|
@ -555,7 +494,7 @@ pub fn create(
|
|||
})
|
||||
.map_err(|err| {
|
||||
render!(users::new(
|
||||
&rockets.to_context(),
|
||||
&(&conn, &rockets).to_context(),
|
||||
Instance::get_local()
|
||||
.map(|i| i.open_registrations)
|
||||
.unwrap_or(true),
|
||||
|
@ -566,39 +505,39 @@ pub fn create(
|
|||
}
|
||||
|
||||
#[get("/@/<name>/outbox")]
|
||||
pub fn outbox(name: String, rockets: PlumeRocket) -> Option<ActivityStream<OrderedCollection>> {
|
||||
let user = User::find_by_fqn(&rockets, &name).ok()?;
|
||||
user.outbox(&*rockets.conn).ok()
|
||||
pub fn outbox(name: String, conn: DbConn) -> Option<ActivityStream<OrderedCollection>> {
|
||||
let user = User::find_by_fqn(&conn, &name).ok()?;
|
||||
user.outbox(&conn).ok()
|
||||
}
|
||||
#[get("/@/<name>/outbox?<page>")]
|
||||
pub fn outbox_page(
|
||||
name: String,
|
||||
page: Page,
|
||||
rockets: PlumeRocket,
|
||||
conn: DbConn,
|
||||
) -> Option<ActivityStream<OrderedCollectionPage>> {
|
||||
let user = User::find_by_fqn(&rockets, &name).ok()?;
|
||||
user.outbox_page(&*rockets.conn, page.limits()).ok()
|
||||
let user = User::find_by_fqn(&conn, &name).ok()?;
|
||||
user.outbox_page(&conn, page.limits()).ok()
|
||||
}
|
||||
#[post("/@/<name>/inbox", data = "<data>")]
|
||||
pub fn inbox(
|
||||
name: String,
|
||||
data: inbox::SignedJson<serde_json::Value>,
|
||||
headers: Headers<'_>,
|
||||
rockets: PlumeRocket,
|
||||
conn: DbConn,
|
||||
) -> Result<String, status::BadRequest<&'static str>> {
|
||||
User::find_by_fqn(&rockets, &name).map_err(|_| status::BadRequest(Some("User not found")))?;
|
||||
inbox::handle_incoming(rockets, data, headers)
|
||||
User::find_by_fqn(&conn, &name).map_err(|_| status::BadRequest(Some("User not found")))?;
|
||||
inbox::handle_incoming(conn, data, headers)
|
||||
}
|
||||
|
||||
#[get("/@/<name>/followers", rank = 1)]
|
||||
pub fn ap_followers(
|
||||
name: String,
|
||||
rockets: PlumeRocket,
|
||||
conn: DbConn,
|
||||
_ap: ApRequest,
|
||||
) -> Option<ActivityStream<OrderedCollection>> {
|
||||
let user = User::find_by_fqn(&rockets, &name).ok()?;
|
||||
let user = User::find_by_fqn(&conn, &name).ok()?;
|
||||
let followers = user
|
||||
.get_followers(&*rockets.conn)
|
||||
.get_followers(&conn)
|
||||
.ok()?
|
||||
.into_iter()
|
||||
.map(|f| Id::new(f.ap_url))
|
||||
|
@ -616,16 +555,16 @@ pub fn ap_followers(
|
|||
}
|
||||
|
||||
#[get("/@/<name>/atom.xml")]
|
||||
pub fn atom_feed(name: String, rockets: PlumeRocket) -> Option<Content<String>> {
|
||||
let conn = &*rockets.conn;
|
||||
let author = User::find_by_fqn(&rockets, &name).ok()?;
|
||||
let entries = Post::get_recents_for_author(conn, &author, 15).ok()?;
|
||||
pub fn atom_feed(name: String, conn: DbConn) -> Option<Content<String>> {
|
||||
let conn = &conn;
|
||||
let author = User::find_by_fqn(&conn, &name).ok()?;
|
||||
let entries = Post::get_recents_for_author(&conn, &author, 15).ok()?;
|
||||
let uri = Instance::get_local()
|
||||
.ok()?
|
||||
.compute_box("@", &name, "atom.xml");
|
||||
let title = &author.display_name;
|
||||
let default_updated = &author.creation_date;
|
||||
let feed = super::build_atom_feed(entries, &uri, title, default_updated, conn);
|
||||
let feed = super::build_atom_feed(entries, &uri, title, default_updated, &conn);
|
||||
Some(Content(
|
||||
ContentType::new("application", "atom+xml"),
|
||||
feed.to_string(),
|
||||
|
|
|
@ -2,7 +2,7 @@ use rocket::http::ContentType;
|
|||
use rocket::response::Content;
|
||||
use webfinger::*;
|
||||
|
||||
use plume_models::{ap_url, blogs::Blog, users::User, PlumeRocket, CONFIG};
|
||||
use plume_models::{ap_url, blogs::Blog, db_conn::DbConn, users::User, CONFIG};
|
||||
|
||||
#[get("/.well-known/nodeinfo")]
|
||||
pub fn nodeinfo() -> Content<String> {
|
||||
|
@ -42,18 +42,18 @@ pub fn host_meta() -> String {
|
|||
|
||||
struct WebfingerResolver;
|
||||
|
||||
impl Resolver<PlumeRocket> for WebfingerResolver {
|
||||
impl Resolver<DbConn> for WebfingerResolver {
|
||||
fn instance_domain<'a>() -> &'a str {
|
||||
CONFIG.base_url.as_str()
|
||||
}
|
||||
|
||||
fn find(prefix: Prefix, acct: String, ctx: PlumeRocket) -> Result<Webfinger, ResolverError> {
|
||||
fn find(prefix: Prefix, acct: String, conn: DbConn) -> Result<Webfinger, ResolverError> {
|
||||
match prefix {
|
||||
Prefix::Acct => User::find_by_fqn(&ctx, &acct)
|
||||
.and_then(|usr| usr.webfinger(&*ctx.conn))
|
||||
Prefix::Acct => User::find_by_fqn(&conn, &acct)
|
||||
.and_then(|usr| usr.webfinger(&*conn))
|
||||
.or(Err(ResolverError::NotFound)),
|
||||
Prefix::Group => Blog::find_by_fqn(&ctx, &acct)
|
||||
.and_then(|blog| blog.webfinger(&*ctx.conn))
|
||||
Prefix::Group => Blog::find_by_fqn(&conn, &acct)
|
||||
.and_then(|blog| blog.webfinger(&*conn))
|
||||
.or(Err(ResolverError::NotFound)),
|
||||
Prefix::Custom(_) => Err(ResolverError::NotFound),
|
||||
}
|
||||
|
@ -61,8 +61,8 @@ impl Resolver<PlumeRocket> for WebfingerResolver {
|
|||
}
|
||||
|
||||
#[get("/.well-known/webfinger?<resource>")]
|
||||
pub fn webfinger(resource: String, rockets: PlumeRocket) -> Content<String> {
|
||||
match WebfingerResolver::endpoint(resource, rockets)
|
||||
pub fn webfinger(resource: String, conn: DbConn) -> Content<String> {
|
||||
match WebfingerResolver::endpoint(resource, conn)
|
||||
.and_then(|wf| serde_json::to_string(&wf).map_err(|_| ResolverError::NotFound))
|
||||
{
|
||||
Ok(wf) => Content(ContentType::new("application", "jrd+json"), wf),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use plume_models::{notifications::*, users::User, Connection, PlumeRocket};
|
||||
use plume_models::{db_conn::DbConn, notifications::*, users::User, Connection, PlumeRocket};
|
||||
|
||||
use crate::templates::Html;
|
||||
use rocket::http::hyper::header::{ETag, EntityTag};
|
||||
|
@ -31,7 +31,7 @@ pub trait IntoContext {
|
|||
);
|
||||
}
|
||||
|
||||
impl IntoContext for PlumeRocket {
|
||||
impl IntoContext for (&DbConn, &PlumeRocket) {
|
||||
fn to_context(
|
||||
&self,
|
||||
) -> (
|
||||
|
@ -41,10 +41,10 @@ impl IntoContext for PlumeRocket {
|
|||
Option<(String, String)>,
|
||||
) {
|
||||
(
|
||||
&*self.conn,
|
||||
&self.intl.catalog,
|
||||
self.user.clone(),
|
||||
self.flash_msg.clone(),
|
||||
&self.0,
|
||||
&self.1.intl.catalog,
|
||||
self.1.user.clone(),
|
||||
self.1.flash_msg.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue