Introduce features to choose between SQlite or Postgres

This commit is contained in:
Bat 2018-09-26 16:22:42 +01:00 committed by Igor Galić
parent 88456faf84
commit 38d737ed0c
No known key found for this signature in database
GPG Key ID: ACFEFF7F6A123A86
20 changed files with 265 additions and 239 deletions

View File

@ -62,5 +62,10 @@ rev = "b326a9893a1849c9abdb39cab9fd7c4a52eb9674"
git = "https://github.com/BaptisteGelez/rocket_i18n" git = "https://github.com/BaptisteGelez/rocket_i18n"
rev = "75a3bfd7b847324c078a355a7f101f8241a9f59b" rev = "75a3bfd7b847324c078a355a7f101f8241a9f59b"
[features]
default = ["postgres"]
postgres = ["plume-models/postgres"]
sqlite = ["plume-models/sqlite"]
[workspace] [workspace]
members = ["plume-api", "plume-models", "plume-common"] members = ["plume-api", "plume-models", "plume-common"]

View File

@ -3,6 +3,6 @@ CREATE TABLE likes (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE NOT NULL, user_id INTEGER REFERENCES users(id) ON DELETE CASCADE NOT NULL,
post_id INTEGER REFERENCES posts(id) ON DELETE CASCADE NOT NULL, post_id INTEGER REFERENCES posts(id) ON DELETE CASCADE NOT NULL,
ap_url VARCHAR NOT NULL default '', creation_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
creation_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ap_url VARCHAR NOT NULL default ''
) )

View File

@ -23,7 +23,7 @@ features = ["serde"]
version = "0.4" version = "0.4"
[dependencies.diesel] [dependencies.diesel]
features = ["postgres", "sqlite", "r2d2", "chrono"] features = ["r2d2", "chrono"]
version = "1.3.2" version = "1.3.2"
[dependencies.plume-api] [dependencies.plume-api]
@ -35,3 +35,8 @@ path = "../plume-common"
[dependencies.rocket] [dependencies.rocket]
git = "https://github.com/SergioBenitez/Rocket" git = "https://github.com/SergioBenitez/Rocket"
rev = "55459db7732b9a240826a5c120c650f87e3372ce" rev = "55459db7732b9a240826a5c120c650f87e3372ce"
[features]
default = ["postgres"]
postgres = ["diesel/postgres"]
sqlite = ["diesel/sqlite"]

View File

@ -1,4 +1,4 @@
use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods, PgConnection}; use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods};
use schema::blog_authors; use schema::blog_authors;

View File

@ -6,8 +6,7 @@ use reqwest::{
}; };
use serde_json; use serde_json;
use url::Url; use url::Url;
use chrono::NaiveDateTime; use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods, dsl::any};
use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods, PgConnection, dsl::any};
use openssl::{ use openssl::{
hash::MessageDigest, hash::MessageDigest,
pkey::{PKey, Private}, pkey::{PKey, Private},
@ -16,7 +15,7 @@ use openssl::{
}; };
use webfinger::*; use webfinger::*;
use {BASE_URL, USE_HTTPS}; use {BASE_URL, USE_HTTPS, Connection, SqlDateTime};
use plume_common::activity_pub::{ use plume_common::activity_pub::{
ap_accept_header, ApSignature, ActivityStream, Id, IntoId, PublicKey, ap_accept_header, ApSignature, ActivityStream, Id, IntoId, PublicKey,
inbox::WithInbox, inbox::WithInbox,
@ -38,7 +37,7 @@ pub struct Blog {
pub outbox_url: String, pub outbox_url: String,
pub inbox_url: String, pub inbox_url: String,
pub instance_id: i32, pub instance_id: i32,
pub creation_date: NaiveDateTime, pub creation_date: SqlDateTime,
pub ap_url: String, pub ap_url: String,
pub private_key: Option<String>, pub private_key: Option<String>,
pub public_key: String pub public_key: String
@ -66,11 +65,11 @@ impl Blog {
find_by!(blogs, find_by_ap_url, ap_url as String); find_by!(blogs, find_by_ap_url, ap_url as String);
find_by!(blogs, find_by_name, actor_id as String, instance_id as i32); find_by!(blogs, find_by_name, actor_id as String, instance_id as i32);
pub fn get_instance(&self, conn: &PgConnection) -> Instance { pub fn get_instance(&self, conn: &Connection) -> Instance {
Instance::get(conn, self.instance_id).expect("Couldn't find instance") Instance::get(conn, self.instance_id).expect("Couldn't find instance")
} }
pub fn list_authors(&self, conn: &PgConnection) -> Vec<User> { pub fn list_authors(&self, conn: &Connection) -> Vec<User> {
use schema::blog_authors; use schema::blog_authors;
use schema::users; use schema::users;
let authors_ids = blog_authors::table.filter(blog_authors::blog_id.eq(self.id)).select(blog_authors::author_id); let authors_ids = blog_authors::table.filter(blog_authors::blog_id.eq(self.id)).select(blog_authors::author_id);
@ -79,7 +78,7 @@ impl Blog {
.expect("Couldn't load authors of a blog") .expect("Couldn't load authors of a blog")
} }
pub fn find_for_author(conn: &PgConnection, author_id: i32) -> Vec<Blog> { pub fn find_for_author(conn: &Connection, author_id: i32) -> Vec<Blog> {
use schema::blog_authors; use schema::blog_authors;
let author_ids = blog_authors::table.filter(blog_authors::author_id.eq(author_id)).select(blog_authors::blog_id); let author_ids = blog_authors::table.filter(blog_authors::author_id.eq(author_id)).select(blog_authors::blog_id);
blogs::table.filter(blogs::id.eq(any(author_ids))) blogs::table.filter(blogs::id.eq(any(author_ids)))
@ -87,11 +86,11 @@ impl Blog {
.expect("Couldn't load blogs ") .expect("Couldn't load blogs ")
} }
pub fn find_local(conn: &PgConnection, name: String) -> Option<Blog> { pub fn find_local(conn: &Connection, name: String) -> Option<Blog> {
Blog::find_by_name(conn, name, Instance::local_id(conn)) Blog::find_by_name(conn, name, Instance::local_id(conn))
} }
pub fn find_by_fqn(conn: &PgConnection, fqn: String) -> Option<Blog> { pub fn find_by_fqn(conn: &Connection, fqn: String) -> Option<Blog> {
if fqn.contains("@") { // remote blog if fqn.contains("@") { // remote blog
match Instance::find_by_domain(conn, String::from(fqn.split("@").last().unwrap())) { match Instance::find_by_domain(conn, String::from(fqn.split("@").last().unwrap())) {
Some(instance) => { Some(instance) => {
@ -107,7 +106,7 @@ impl Blog {
} }
} }
fn fetch_from_webfinger(conn: &PgConnection, acct: String) -> Option<Blog> { fn fetch_from_webfinger(conn: &Connection, acct: String) -> Option<Blog> {
match resolve(acct.clone(), *USE_HTTPS) { match resolve(acct.clone(), *USE_HTTPS) {
Ok(wf) => wf.links.into_iter().find(|l| l.mime_type == Some(String::from("application/activity+json"))).and_then(|l| Blog::fetch_from_url(conn, l.href.expect("No href for AP WF link"))), Ok(wf) => wf.links.into_iter().find(|l| l.mime_type == Some(String::from("application/activity+json"))).and_then(|l| Blog::fetch_from_url(conn, l.href.expect("No href for AP WF link"))),
Err(details) => { Err(details) => {
@ -117,7 +116,7 @@ impl Blog {
} }
} }
fn fetch_from_url(conn: &PgConnection, url: String) -> Option<Blog> { fn fetch_from_url(conn: &Connection, url: String) -> Option<Blog> {
let req = Client::new() let req = Client::new()
.get(&url[..]) .get(&url[..])
.header(Accept(ap_accept_header().into_iter().map(|h| qitem(h.parse::<Mime>().expect("Invalid Content-Type"))).collect())) .header(Accept(ap_accept_header().into_iter().map(|h| qitem(h.parse::<Mime>().expect("Invalid Content-Type"))).collect()))
@ -134,7 +133,7 @@ impl Blog {
} }
} }
fn from_activity(conn: &PgConnection, acct: CustomGroup, inst: String) -> Blog { fn from_activity(conn: &Connection, acct: CustomGroup, inst: String) -> Blog {
let instance = match Instance::find_by_domain(conn, inst.clone()) { let instance = match Instance::find_by_domain(conn, inst.clone()) {
Some(instance) => instance, Some(instance) => instance,
None => { None => {
@ -166,7 +165,7 @@ impl Blog {
}) })
} }
pub fn into_activity(&self, _conn: &PgConnection) -> CustomGroup { pub fn into_activity(&self, _conn: &Connection) -> CustomGroup {
let mut blog = Group::default(); let mut blog = Group::default();
blog.ap_actor_props.set_preferred_username_string(self.actor_id.clone()).expect("Blog::into_activity: preferredUsername error"); blog.ap_actor_props.set_preferred_username_string(self.actor_id.clone()).expect("Blog::into_activity: preferredUsername error");
blog.object_props.set_name_string(self.title.clone()).expect("Blog::into_activity: name error"); blog.object_props.set_name_string(self.title.clone()).expect("Blog::into_activity: name error");
@ -185,7 +184,7 @@ impl Blog {
CustomGroup::new(blog, ap_signature) CustomGroup::new(blog, ap_signature)
} }
pub fn update_boxes(&self, conn: &PgConnection) { pub fn update_boxes(&self, conn: &Connection) {
let instance = self.get_instance(conn); let instance = self.get_instance(conn);
if self.outbox_url.len() == 0 { if self.outbox_url.len() == 0 {
diesel::update(self) diesel::update(self)
@ -206,14 +205,14 @@ impl Blog {
} }
} }
pub fn outbox(&self, conn: &PgConnection) -> ActivityStream<OrderedCollection> { pub fn outbox(&self, conn: &Connection) -> ActivityStream<OrderedCollection> {
let mut coll = OrderedCollection::default(); let mut coll = OrderedCollection::default();
coll.collection_props.items = serde_json::to_value(self.get_activities(conn)).unwrap(); coll.collection_props.items = serde_json::to_value(self.get_activities(conn)).unwrap();
coll.collection_props.set_total_items_u64(self.get_activities(conn).len() as u64).unwrap(); coll.collection_props.set_total_items_u64(self.get_activities(conn).len() as u64).unwrap();
ActivityStream::new(coll) ActivityStream::new(coll)
} }
fn get_activities(&self, _conn: &PgConnection) -> Vec<serde_json::Value> { fn get_activities(&self, _conn: &Connection) -> Vec<serde_json::Value> {
vec![] vec![]
} }
@ -221,7 +220,7 @@ impl Blog {
PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.clone().unwrap().as_ref()).unwrap()).unwrap() PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.clone().unwrap().as_ref()).unwrap()).unwrap()
} }
pub fn webfinger(&self, conn: &PgConnection) -> Webfinger { pub fn webfinger(&self, conn: &Connection) -> Webfinger {
Webfinger { Webfinger {
subject: format!("acct:{}@{}", self.actor_id, self.get_instance(conn).public_domain), subject: format!("acct:{}@{}", self.actor_id, self.get_instance(conn).public_domain),
aliases: vec![self.ap_url.clone()], aliases: vec![self.ap_url.clone()],
@ -248,7 +247,7 @@ impl Blog {
} }
} }
pub fn from_url(conn: &PgConnection, url: String) -> Option<Blog> { pub fn from_url(conn: &Connection, url: String) -> Option<Blog> {
Blog::find_by_ap_url(conn, url.clone()).or_else(|| { Blog::find_by_ap_url(conn, url.clone()).or_else(|| {
// The requested user was not in the DB // The requested user was not in the DB
// We try to fetch it if it is remote // We try to fetch it if it is remote
@ -260,7 +259,7 @@ impl Blog {
}) })
} }
pub fn get_fqn(&self, conn: &PgConnection) -> String { pub fn get_fqn(&self, conn: &Connection) -> String {
if self.instance_id == Instance::local_id(conn) { if self.instance_id == Instance::local_id(conn) {
self.actor_id.clone() self.actor_id.clone()
} else { } else {
@ -268,7 +267,7 @@ impl Blog {
} }
} }
pub fn to_json(&self, conn: &PgConnection) -> serde_json::Value { pub fn to_json(&self, conn: &Connection) -> serde_json::Value {
let mut json = serde_json::to_value(self).unwrap(); let mut json = serde_json::to_value(self).unwrap();
json["fqn"] = json!(self.get_fqn(conn)); json["fqn"] = json!(self.get_fqn(conn));
json json

View File

@ -12,6 +12,7 @@ use plume_common::activity_pub::{
inbox::{FromActivity, Notify} inbox::{FromActivity, Notify}
}; };
use plume_common::utils; use plume_common::utils;
use {Connection, SqlDateTime};
use instance::Instance; use instance::Instance;
use mentions::Mention; use mentions::Mention;
use notifications::*; use notifications::*;
@ -27,7 +28,7 @@ pub struct Comment {
pub in_response_to_id: Option<i32>, pub in_response_to_id: Option<i32>,
pub post_id: i32, pub post_id: i32,
pub author_id: i32, pub author_id: i32,
pub creation_date: chrono::NaiveDateTime, pub creation_date: SqlDateTime,
pub ap_url: Option<String>, pub ap_url: Option<String>,
pub sensitive: bool, pub sensitive: bool,
pub spoiler_text: String pub spoiler_text: String
@ -51,15 +52,15 @@ impl Comment {
list_by!(comments, list_by_post, post_id as i32); list_by!(comments, list_by_post, post_id as i32);
find_by!(comments, find_by_ap_url, ap_url as String); find_by!(comments, find_by_ap_url, ap_url as String);
pub fn get_author(&self, conn: &PgConnection) -> User { pub fn get_author(&self, conn: &Connection) -> User {
User::get(conn, self.author_id).unwrap() User::get(conn, self.author_id).unwrap()
} }
pub fn get_post(&self, conn: &PgConnection) -> Post { pub fn get_post(&self, conn: &Connection) -> Post {
Post::get(conn, self.post_id).unwrap() Post::get(conn, self.post_id).unwrap()
} }
pub fn count_local(conn: &PgConnection) -> usize { pub fn count_local(conn: &Connection) -> usize {
use schema::users; use schema::users;
let local_authors = users::table.filter(users::instance_id.eq(Instance::local_id(conn))).select(users::id); let local_authors = users::table.filter(users::instance_id.eq(Instance::local_id(conn))).select(users::id);
comments::table.filter(comments::author_id.eq(any(local_authors))) comments::table.filter(comments::author_id.eq(any(local_authors)))
@ -68,7 +69,7 @@ impl Comment {
.len() .len()
} }
pub fn to_json(&self, conn: &PgConnection, others: &Vec<Comment>) -> serde_json::Value { pub fn to_json(&self, conn: &Connection, others: &Vec<Comment>) -> serde_json::Value {
let mut json = serde_json::to_value(self).unwrap(); let mut json = serde_json::to_value(self).unwrap();
json["author"] = self.get_author(conn).to_json(conn); json["author"] = self.get_author(conn).to_json(conn);
let mentions = Mention::list_for_comment(conn, self.id).into_iter() let mentions = Mention::list_for_comment(conn, self.id).into_iter()
@ -82,7 +83,7 @@ impl Comment {
json json
} }
pub fn update_ap_url(&self, conn: &PgConnection) -> Comment { pub fn update_ap_url(&self, conn: &Connection) -> Comment {
if self.ap_url.is_none() { if self.ap_url.is_none() {
diesel::update(self) diesel::update(self)
.set(comments::ap_url.eq(self.compute_id(conn))) .set(comments::ap_url.eq(self.compute_id(conn)))
@ -93,11 +94,11 @@ impl Comment {
} }
} }
pub fn compute_id(&self, conn: &PgConnection) -> String { pub fn compute_id(&self, conn: &Connection) -> String {
format!("{}comment/{}", self.get_post(conn).ap_url, self.id) format!("{}comment/{}", self.get_post(conn).ap_url, self.id)
} }
pub fn into_activity(&self, conn: &PgConnection) -> Note { pub fn into_activity(&self, conn: &Connection) -> Note {
let (html, mentions) = utils::md_to_html(self.content.get().as_ref()); let (html, mentions) = utils::md_to_html(self.content.get().as_ref());
let author = User::get(conn, self.author_id).unwrap(); let author = User::get(conn, self.author_id).unwrap();
@ -119,7 +120,7 @@ impl Comment {
note note
} }
pub fn create_activity(&self, conn: &PgConnection) -> Create { pub fn create_activity(&self, conn: &Connection) -> Create {
let author = User::get(conn, self.author_id).unwrap(); let author = User::get(conn, self.author_id).unwrap();
let note = self.into_activity(conn); let note = self.into_activity(conn);
@ -134,7 +135,7 @@ impl Comment {
} }
impl FromActivity<Note, PgConnection> for Comment { impl FromActivity<Note, PgConnection> for Comment {
fn from_activity(conn: &PgConnection, note: Note, actor: Id) -> Comment { fn from_activity(conn: &Connection, note: Note, actor: Id) -> Comment {
let previous_url = note.object_props.in_reply_to.clone().unwrap().as_str().unwrap().to_string(); let previous_url = note.object_props.in_reply_to.clone().unwrap().as_str().unwrap().to_string();
let previous_comment = Comment::find_by_ap_url(conn, previous_url.clone()); let previous_comment = Comment::find_by_ap_url(conn, previous_url.clone());
@ -168,7 +169,7 @@ impl FromActivity<Note, PgConnection> for Comment {
} }
impl Notify<PgConnection> for Comment { impl Notify<PgConnection> for Comment {
fn notify(&self, conn: &PgConnection) { fn notify(&self, conn: &Connection) {
for author in self.get_post(conn).get_authors(conn) { for author in self.get_post(conn).get_authors(conn) {
Notification::insert(conn, NewNotification { Notification::insert(conn, NewNotification {
kind: notification_kind::COMMENT.to_string(), kind: notification_kind::COMMENT.to_string(),

View File

@ -27,7 +27,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for DbConn {
} }
} }
// For the convenience of using an &DbConn as an &PgConnection. // For the convenience of using an &DbConn as an &Connection.
impl Deref for DbConn { impl Deref for DbConn {
type Target = PgConnection; type Target = PgConnection;

View File

@ -2,6 +2,7 @@ use activitypub::{Actor, activity::{Accept, Follow as FollowAct, Undo}, actor::P
use diesel::{self, PgConnection, ExpressionMethods, QueryDsl, RunQueryDsl}; use diesel::{self, PgConnection, ExpressionMethods, QueryDsl, RunQueryDsl};
use plume_common::activity_pub::{broadcast, Id, IntoId, inbox::{FromActivity, Notify, WithInbox, Deletable}, sign::Signer}; use plume_common::activity_pub::{broadcast, Id, IntoId, inbox::{FromActivity, Notify, WithInbox, Deletable}, sign::Signer};
use Connection;
use blogs::Blog; use blogs::Blog;
use notifications::*; use notifications::*;
use users::User; use users::User;
@ -29,14 +30,14 @@ impl Follow {
get!(follows); get!(follows);
find_by!(follows, find_by_ap_url, ap_url as String); find_by!(follows, find_by_ap_url, ap_url as String);
pub fn find(conn: &PgConnection, from: i32, to: i32) -> Option<Follow> { pub fn find(conn: &Connection, from: i32, to: i32) -> Option<Follow> {
follows::table.filter(follows::follower_id.eq(from)) follows::table.filter(follows::follower_id.eq(from))
.filter(follows::following_id.eq(to)) .filter(follows::following_id.eq(to))
.get_result(conn) .get_result(conn)
.ok() .ok()
} }
pub fn into_activity(&self, conn: &PgConnection) -> FollowAct { pub fn into_activity(&self, conn: &Connection) -> FollowAct {
let user = User::get(conn, self.follower_id).unwrap(); let user = User::get(conn, self.follower_id).unwrap();
let target = User::get(conn, self.following_id).unwrap(); let target = User::get(conn, self.following_id).unwrap();
@ -52,7 +53,7 @@ impl Follow {
/// from -> The one sending the follow request /// from -> The one sending the follow request
/// target -> The target of the request, responding with Accept /// target -> The target of the request, responding with Accept
pub fn accept_follow<A: Signer + IntoId + Clone, B: Clone + WithInbox + Actor + IntoId>( pub fn accept_follow<A: Signer + IntoId + Clone, B: Clone + WithInbox + Actor + IntoId>(
conn: &PgConnection, conn: &Connection,
from: &B, from: &B,
target: &A, target: &A,
follow: FollowAct, follow: FollowAct,
@ -80,7 +81,7 @@ impl Follow {
} }
impl FromActivity<FollowAct, PgConnection> for Follow { impl FromActivity<FollowAct, PgConnection> for Follow {
fn from_activity(conn: &PgConnection, follow: FollowAct, _actor: Id) -> Follow { fn from_activity(conn: &Connection, follow: FollowAct, _actor: Id) -> Follow {
let from_id = follow.follow_props.actor_link::<Id>().map(|l| l.into()) let from_id = follow.follow_props.actor_link::<Id>().map(|l| l.into())
.unwrap_or_else(|_| follow.follow_props.actor_object::<Person>().expect("No actor object (nor ID) on Follow").object_props.id_string().expect("No ID on actor on Follow")); .unwrap_or_else(|_| follow.follow_props.actor_object::<Person>().expect("No actor object (nor ID) on Follow").object_props.id_string().expect("No ID on actor on Follow"));
let from = User::from_url(conn, from_id).unwrap(); let from = User::from_url(conn, from_id).unwrap();
@ -95,7 +96,7 @@ impl FromActivity<FollowAct, PgConnection> for Follow {
} }
impl Notify<PgConnection> for Follow { impl Notify<PgConnection> for Follow {
fn notify(&self, conn: &PgConnection) { fn notify(&self, conn: &Connection) {
Notification::insert(conn, NewNotification { Notification::insert(conn, NewNotification {
kind: notification_kind::FOLLOW.to_string(), kind: notification_kind::FOLLOW.to_string(),
object_id: self.id, object_id: self.id,
@ -105,7 +106,7 @@ impl Notify<PgConnection> for Follow {
} }
impl Deletable<PgConnection, Undo> for Follow { impl Deletable<PgConnection, Undo> for Follow {
fn delete(&self, conn: &PgConnection) -> Undo { fn delete(&self, conn: &Connection) -> Undo {
diesel::delete(self).execute(conn).expect("Coudn't delete follow"); diesel::delete(self).execute(conn).expect("Coudn't delete follow");
// delete associated notification if any // delete associated notification if any
@ -120,7 +121,7 @@ impl Deletable<PgConnection, Undo> for Follow {
undo undo
} }
fn delete_id(id: String, conn: &PgConnection) { fn delete_id(id: String, conn: &Connection) {
if let Some(follow) = Follow::find_by_ap_url(conn, id) { if let Some(follow) = Follow::find_by_ap_url(conn, id) {
follow.delete(conn); follow.delete(conn);
} }

View File

@ -1,8 +1,8 @@
use chrono::NaiveDateTime; use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods};
use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods, PgConnection};
use std::iter::Iterator; use std::iter::Iterator;
use plume_common::utils::md_to_html; use plume_common::utils::md_to_html;
use {Connection, SqlDateTime};
use safe_string::SafeString; use safe_string::SafeString;
use ap_url; use ap_url;
use users::User; use users::User;
@ -15,7 +15,7 @@ pub struct Instance {
pub name: String, pub name: String,
pub local: bool, pub local: bool,
pub blocked: bool, pub blocked: bool,
pub creation_date: NaiveDateTime, pub creation_date: SqlDateTime,
pub open_registrations: bool, pub open_registrations: bool,
pub short_description: SafeString, pub short_description: SafeString,
pub long_description: SafeString, pub long_description: SafeString,
@ -39,7 +39,7 @@ pub struct NewInstance {
} }
impl Instance { impl Instance {
pub fn get_local(conn: &PgConnection) -> Option<Instance> { pub fn get_local(conn: &Connection) -> Option<Instance> {
instances::table.filter(instances::local.eq(true)) instances::table.filter(instances::local.eq(true))
.limit(1) .limit(1)
.load::<Instance>(conn) .load::<Instance>(conn)
@ -47,13 +47,13 @@ impl Instance {
.into_iter().nth(0) .into_iter().nth(0)
} }
pub fn get_remotes(conn: &PgConnection) -> Vec<Instance> { pub fn get_remotes(conn: &Connection) -> Vec<Instance> {
instances::table.filter(instances::local.eq(false)) instances::table.filter(instances::local.eq(false))
.load::<Instance>(conn) .load::<Instance>(conn)
.expect("Error loading remote instances infos") .expect("Error loading remote instances infos")
} }
pub fn page(conn: &PgConnection, (min, max): (i32, i32)) -> Vec<Instance> { pub fn page(conn: &Connection, (min, max): (i32, i32)) -> Vec<Instance> {
instances::table.order(instances::public_domain.asc()) instances::table.order(instances::public_domain.asc())
.offset(min.into()) .offset(min.into())
.limit((max - min).into()) .limit((max - min).into())
@ -61,7 +61,7 @@ impl Instance {
.expect("Error loading a page of instances") .expect("Error loading a page of instances")
} }
pub fn local_id(conn: &PgConnection) -> i32 { pub fn local_id(conn: &Connection) -> i32 {
Instance::get_local(conn).unwrap().id Instance::get_local(conn).unwrap().id
} }
@ -69,7 +69,7 @@ impl Instance {
get!(instances); get!(instances);
find_by!(instances, find_by_domain, public_domain as String); find_by!(instances, find_by_domain, public_domain as String);
pub fn toggle_block(&self, conn: &PgConnection) { pub fn toggle_block(&self, conn: &Connection) {
diesel::update(self) diesel::update(self)
.set(instances::blocked.eq(!self.blocked)) .set(instances::blocked.eq(!self.blocked))
.get_result::<Instance>(conn) .get_result::<Instance>(conn)
@ -77,7 +77,7 @@ impl Instance {
} }
/// id: AP object id /// id: AP object id
pub fn is_blocked(conn: &PgConnection, id: String) -> bool { pub fn is_blocked(conn: &Connection, id: String) -> bool {
for block in instances::table.filter(instances::blocked.eq(true)) for block in instances::table.filter(instances::blocked.eq(true))
.get_results::<Instance>(conn) .get_results::<Instance>(conn)
.expect("Error listing blocked instances") { .expect("Error listing blocked instances") {
@ -89,7 +89,7 @@ impl Instance {
false false
} }
pub fn has_admin(&self, conn: &PgConnection) -> bool { pub fn has_admin(&self, conn: &Connection) -> bool {
users::table.filter(users::instance_id.eq(self.id)) users::table.filter(users::instance_id.eq(self.id))
.filter(users::is_admin.eq(true)) .filter(users::is_admin.eq(true))
.load::<User>(conn) .load::<User>(conn)
@ -97,7 +97,7 @@ impl Instance {
.len() > 0 .len() > 0
} }
pub fn main_admin(&self, conn: &PgConnection) -> User { pub fn main_admin(&self, conn: &Connection) -> User {
users::table.filter(users::instance_id.eq(self.id)) users::table.filter(users::instance_id.eq(self.id))
.filter(users::is_admin.eq(true)) .filter(users::is_admin.eq(true))
.limit(1) .limit(1)
@ -115,7 +115,7 @@ impl Instance {
)) ))
} }
pub fn update(&self, conn: &PgConnection, name: String, open_registrations: bool, short_description: SafeString, long_description: SafeString) -> Instance { pub fn update(&self, conn: &Connection, name: String, open_registrations: bool, short_description: SafeString, long_description: SafeString) -> Instance {
let (sd, _) = md_to_html(short_description.as_ref()); let (sd, _) = md_to_html(short_description.as_ref());
let (ld, _) = md_to_html(long_description.as_ref()); let (ld, _) = md_to_html(long_description.as_ref());
diesel::update(self) diesel::update(self)
@ -130,7 +130,7 @@ impl Instance {
.expect("Couldn't update instance") .expect("Couldn't update instance")
} }
pub fn count(conn: &PgConnection) -> i64 { pub fn count(conn: &Connection) -> i64 {
instances::table.count().get_result(conn).expect("Couldn't count instances") instances::table.count().get_result(conn).expect("Couldn't count instances")
} }
} }

View File

@ -1,4 +1,5 @@
#![allow(proc_macro_derive_resolution_fallback)] // This can be removed after diesel-1.4 #![allow(proc_macro_derive_resolution_fallback)] // This can be removed after diesel-1.4
#![feature(crate_in_paths)]
extern crate activitypub; extern crate activitypub;
extern crate ammonia; extern crate ammonia;
@ -28,7 +29,7 @@ use std::env;
macro_rules! find_by { macro_rules! find_by {
($table:ident, $fn:ident, $($col:ident as $type:ident),+) => { ($table:ident, $fn:ident, $($col:ident as $type:ident),+) => {
/// Try to find a $table with a given $col /// Try to find a $table with a given $col
pub fn $fn(conn: &PgConnection, $($col: $type),+) -> Option<Self> { pub fn $fn(conn: &crate::Connection, $($col: $type),+) -> Option<Self> {
$table::table $table::table
$(.filter($table::$col.eq($col)))+ $(.filter($table::$col.eq($col)))+
.limit(1) .limit(1)
@ -42,7 +43,7 @@ macro_rules! find_by {
macro_rules! list_by { macro_rules! list_by {
($table:ident, $fn:ident, $($col:ident as $type:ident),+) => { ($table:ident, $fn:ident, $($col:ident as $type:ident),+) => {
/// Try to find a $table with a given $col /// Try to find a $table with a given $col
pub fn $fn(conn: &PgConnection, $($col: $type),+) -> Vec<Self> { pub fn $fn(conn: &crate::Connection, $($col: $type),+) -> Vec<Self> {
$table::table $table::table
$(.filter($table::$col.eq($col)))+ $(.filter($table::$col.eq($col)))+
.load::<Self>(conn) .load::<Self>(conn)
@ -53,7 +54,7 @@ macro_rules! list_by {
macro_rules! get { macro_rules! get {
($table:ident) => { ($table:ident) => {
pub fn get(conn: &PgConnection, id: i32) -> Option<Self> { pub fn get(conn: &crate::Connection, id: i32) -> Option<Self> {
$table::table.filter($table::id.eq(id)) $table::table.filter($table::id.eq(id))
.limit(1) .limit(1)
.load::<Self>(conn) .load::<Self>(conn)
@ -65,7 +66,7 @@ macro_rules! get {
macro_rules! insert { macro_rules! insert {
($table:ident, $from:ident) => { ($table:ident, $from:ident) => {
pub fn insert(conn: &PgConnection, new: $from) -> Self { pub fn insert(conn: &crate::Connection, new: $from) -> Self {
diesel::insert_into($table::table) diesel::insert_into($table::table)
.values(new) .values(new)
.get_result(conn) .get_result(conn)
@ -76,7 +77,7 @@ macro_rules! insert {
macro_rules! update { macro_rules! update {
($table:ident) => { ($table:ident) => {
pub fn update(&self, conn: &PgConnection) -> Self { pub fn update(&self, conn: &crate::Connection) -> Self {
diesel::update(self) diesel::update(self)
.set(self) .set(self)
.get_result(conn) .get_result(conn)
@ -104,6 +105,18 @@ pub fn ap_url(url: String) -> String {
format!("{}://{}", scheme, url) format!("{}://{}", scheme, url)
} }
#[cfg(all(not(feature = "postgres"), feature = "sqlite"))]
pub type SqlDateTime = chrono::NaiveDateTime;
#[cfg(all(not(feature = "postgres"), feature = "sqlite"))]
pub type Connection = diesel::SqliteConnection;
#[cfg(all(not(feature = "sqlite"), feature = "postgres"))]
pub type SqlDateTime = chrono::NaiveDateTime;
#[cfg(all(not(feature = "sqlite"), feature = "postgres"))]
pub type Connection = diesel::PgConnection;
pub mod admin; pub mod admin;
pub mod blog_authors; pub mod blog_authors;
pub mod blogs; pub mod blogs;

View File

@ -1,6 +1,6 @@
use activitypub::activity; use activitypub::activity;
use chrono; use chrono::NaiveDateTime;
use diesel::{self, PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods}; use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods};
use plume_common::activity_pub::{ use plume_common::activity_pub::{
PUBLIC_VISIBILTY, PUBLIC_VISIBILTY,
@ -8,6 +8,7 @@ use plume_common::activity_pub::{
IntoId, IntoId,
inbox::{FromActivity, Deletable, Notify} inbox::{FromActivity, Deletable, Notify}
}; };
use Connection;
use notifications::*; use notifications::*;
use posts::Post; use posts::Post;
use users::User; use users::User;
@ -18,7 +19,7 @@ pub struct Like {
pub id: i32, pub id: i32,
pub user_id: i32, pub user_id: i32,
pub post_id: i32, pub post_id: i32,
pub creation_date: chrono::NaiveDateTime, pub creation_date: NaiveDateTime,
pub ap_url: String pub ap_url: String
} }
@ -36,7 +37,7 @@ impl Like {
find_by!(likes, find_by_ap_url, ap_url as String); find_by!(likes, find_by_ap_url, ap_url as String);
find_by!(likes, find_by_user_on_post, user_id as i32, post_id as i32); find_by!(likes, find_by_user_on_post, user_id as i32, post_id as i32);
pub fn update_ap_url(&self, conn: &PgConnection) { pub fn update_ap_url(&self, conn: &Connection) {
if self.ap_url.len() == 0 { if self.ap_url.len() == 0 {
diesel::update(self) diesel::update(self)
.set(likes::ap_url.eq(format!( .set(likes::ap_url.eq(format!(
@ -48,7 +49,7 @@ impl Like {
} }
} }
pub fn into_activity(&self, conn: &PgConnection) -> activity::Like { pub fn into_activity(&self, conn: &Connection) -> activity::Like {
let mut act = activity::Like::default(); let mut act = activity::Like::default();
act.like_props.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).expect("Like::into_activity: actor error"); act.like_props.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).expect("Like::into_activity: actor error");
act.like_props.set_object_link(Post::get(conn, self.post_id).unwrap().into_id()).expect("Like::into_activity: object error"); act.like_props.set_object_link(Post::get(conn, self.post_id).unwrap().into_id()).expect("Like::into_activity: object error");
@ -60,8 +61,8 @@ impl Like {
} }
} }
impl FromActivity<activity::Like, PgConnection> for Like { impl FromActivity<activity::Like, Connection> for Like {
fn from_activity(conn: &PgConnection, like: activity::Like, _actor: Id) -> Like { fn from_activity(conn: &Connection, like: activity::Like, _actor: Id) -> Like {
let liker = User::from_url(conn, like.like_props.actor.as_str().unwrap().to_string()); let liker = User::from_url(conn, like.like_props.actor.as_str().unwrap().to_string());
let post = Post::find_by_ap_url(conn, like.like_props.object.as_str().unwrap().to_string()); let post = Post::find_by_ap_url(conn, like.like_props.object.as_str().unwrap().to_string());
let res = Like::insert(conn, NewLike { let res = Like::insert(conn, NewLike {
@ -74,8 +75,8 @@ impl FromActivity<activity::Like, PgConnection> for Like {
} }
} }
impl Notify<PgConnection> for Like { impl Notify<Connection> for Like {
fn notify(&self, conn: &PgConnection) { fn notify(&self, conn: &Connection) {
let post = Post::get(conn, self.post_id).unwrap(); let post = Post::get(conn, self.post_id).unwrap();
for author in post.get_authors(conn) { for author in post.get_authors(conn) {
Notification::insert(conn, NewNotification { Notification::insert(conn, NewNotification {
@ -87,8 +88,8 @@ impl Notify<PgConnection> for Like {
} }
} }
impl Deletable<PgConnection, activity::Undo> for Like { impl Deletable<Connection, activity::Undo> for Like {
fn delete(&self, conn: &PgConnection) -> activity::Undo { fn delete(&self, conn: &Connection) -> activity::Undo {
diesel::delete(self).execute(conn).unwrap(); diesel::delete(self).execute(conn).unwrap();
// delete associated notification if any // delete associated notification if any
@ -106,7 +107,7 @@ impl Deletable<PgConnection, activity::Undo> for Like {
act act
} }
fn delete_id(id: String, conn: &PgConnection) { fn delete_id(id: String, conn: &Connection) {
if let Some(like) = Like::find_by_ap_url(conn, id.into()) { if let Some(like) = Like::find_by_ap_url(conn, id.into()) {
like.delete(conn); like.delete(conn);
} }

View File

@ -1,8 +1,8 @@
use diesel::{self, PgConnection, QueryDsl, ExpressionMethods, RunQueryDsl}; use diesel::{self, QueryDsl, ExpressionMethods, RunQueryDsl};
use serde_json; use serde_json;
use std::fs; use std::fs;
use ap_url; use {ap_url, Connection};
use instance::Instance; use instance::Instance;
use schema::medias; use schema::medias;
@ -35,7 +35,7 @@ impl Media {
get!(medias); get!(medias);
list_by!(medias, for_user, owner_id as i32); list_by!(medias, for_user, owner_id as i32);
pub fn to_json(&self, conn: &PgConnection) -> serde_json::Value { pub fn to_json(&self, conn: &Connection) -> serde_json::Value {
let mut json = serde_json::to_value(self).unwrap(); let mut json = serde_json::to_value(self).unwrap();
let url = self.url(conn); let url = self.url(conn);
let (preview, html, md) = match self.file_path.rsplitn(2, '.').next().unwrap() { let (preview, html, md) = match self.file_path.rsplitn(2, '.').next().unwrap() {
@ -63,7 +63,7 @@ impl Media {
json json
} }
pub fn url(&self, conn: &PgConnection) -> String { pub fn url(&self, conn: &Connection) -> String {
if self.is_remote { if self.is_remote {
self.remote_url.clone().unwrap_or(String::new()) self.remote_url.clone().unwrap_or(String::new())
} else { } else {
@ -71,12 +71,12 @@ impl Media {
} }
} }
pub fn delete(&self, conn: &PgConnection) { pub fn delete(&self, conn: &Connection) {
fs::remove_file(self.file_path.as_str()).expect("Couldn't delete media from disk"); fs::remove_file(self.file_path.as_str()).expect("Couldn't delete media from disk");
diesel::delete(self).execute(conn).expect("Couldn't remove media from DB"); diesel::delete(self).execute(conn).expect("Couldn't remove media from DB");
} }
pub fn save_remote(conn: &PgConnection, url: String) -> Media { pub fn save_remote(conn: &Connection, url: String) -> Media {
Media::insert(conn, NewMedia { Media::insert(conn, NewMedia {
file_path: String::new(), file_path: String::new(),
alt_text: String::new(), alt_text: String::new(),
@ -88,7 +88,7 @@ impl Media {
}) })
} }
pub fn set_owner(&self, conn: &PgConnection, id: i32) { pub fn set_owner(&self, conn: &Connection, id: i32) {
diesel::update(self) diesel::update(self)
.set(medias::owner_id.eq(id)) .set(medias::owner_id.eq(id))
.execute(conn) .execute(conn)

View File

@ -2,6 +2,7 @@ use activitypub::link;
use diesel::{self, PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods}; use diesel::{self, PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods};
use plume_common::activity_pub::inbox::Notify; use plume_common::activity_pub::inbox::Notify;
use Connection;
use comments::Comment; use comments::Comment;
use notifications::*; use notifications::*;
use posts::Post; use posts::Post;
@ -34,26 +35,26 @@ impl Mention {
list_by!(mentions, list_for_post, post_id as i32); list_by!(mentions, list_for_post, post_id as i32);
list_by!(mentions, list_for_comment, comment_id as i32); list_by!(mentions, list_for_comment, comment_id as i32);
pub fn get_mentioned(&self, conn: &PgConnection) -> Option<User> { pub fn get_mentioned(&self, conn: &Connection) -> Option<User> {
User::get(conn, self.mentioned_id) User::get(conn, self.mentioned_id)
} }
pub fn get_post(&self, conn: &PgConnection) -> Option<Post> { pub fn get_post(&self, conn: &Connection) -> Option<Post> {
self.post_id.and_then(|id| Post::get(conn, id)) self.post_id.and_then(|id| Post::get(conn, id))
} }
pub fn get_comment(&self, conn: &PgConnection) -> Option<Comment> { pub fn get_comment(&self, conn: &Connection) -> Option<Comment> {
self.comment_id.and_then(|id| Comment::get(conn, id)) self.comment_id.and_then(|id| Comment::get(conn, id))
} }
pub fn get_user(&self, conn: &PgConnection) -> Option<User> { pub fn get_user(&self, conn: &Connection) -> Option<User> {
match self.get_post(conn) { match self.get_post(conn) {
Some(p) => p.get_authors(conn).into_iter().next(), Some(p) => p.get_authors(conn).into_iter().next(),
None => self.get_comment(conn).map(|c| c.get_author(conn)) None => self.get_comment(conn).map(|c| c.get_author(conn))
} }
} }
pub fn build_activity(conn: &PgConnection, ment: String) -> link::Mention { pub fn build_activity(conn: &Connection, ment: String) -> link::Mention {
let user = User::find_by_fqn(conn, ment.clone()); let user = User::find_by_fqn(conn, ment.clone());
let mut mention = link::Mention::default(); let mut mention = link::Mention::default();
mention.link_props.set_href_string(user.clone().map(|u| u.ap_url).unwrap_or(String::new())).expect("Error setting mention's href"); mention.link_props.set_href_string(user.clone().map(|u| u.ap_url).unwrap_or(String::new())).expect("Error setting mention's href");
@ -61,7 +62,7 @@ impl Mention {
mention mention
} }
pub fn to_activity(&self, conn: &PgConnection) -> link::Mention { pub fn to_activity(&self, conn: &Connection) -> link::Mention {
let user = self.get_mentioned(conn); let user = self.get_mentioned(conn);
let mut mention = link::Mention::default(); let mut mention = link::Mention::default();
mention.link_props.set_href_string(user.clone().map(|u| u.ap_url).unwrap_or(String::new())).expect("Error setting mention's href"); mention.link_props.set_href_string(user.clone().map(|u| u.ap_url).unwrap_or(String::new())).expect("Error setting mention's href");
@ -69,7 +70,7 @@ impl Mention {
mention mention
} }
pub fn from_activity(conn: &PgConnection, ment: link::Mention, inside: i32, in_post: bool, notify: bool) -> Option<Self> { pub fn from_activity(conn: &Connection, ment: link::Mention, inside: i32, in_post: bool, notify: bool) -> Option<Self> {
let ap_url = ment.link_props.href_string().ok()?; let ap_url = ment.link_props.href_string().ok()?;
let mentioned = User::find_by_ap_url(conn, ap_url)?; let mentioned = User::find_by_ap_url(conn, ap_url)?;
@ -104,7 +105,7 @@ impl Mention {
} }
impl Notify<PgConnection> for Mention { impl Notify<PgConnection> for Mention {
fn notify(&self, conn: &PgConnection) { fn notify(&self, conn: &Connection) {
self.get_mentioned(conn).map(|m| { self.get_mentioned(conn).map(|m| {
Notification::insert(conn, NewNotification { Notification::insert(conn, NewNotification {
kind: notification_kind::MENTION.to_string(), kind: notification_kind::MENTION.to_string(),

View File

@ -1,7 +1,7 @@
use chrono::NaiveDateTime; use diesel::{self, RunQueryDsl, QueryDsl, ExpressionMethods};
use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods};
use serde_json; use serde_json;
use {Connection, SqlDateTime};
use comments::Comment; use comments::Comment;
use follows::Follow; use follows::Follow;
use likes::Like; use likes::Like;
@ -23,7 +23,7 @@ pub mod notification_kind {
pub struct Notification { pub struct Notification {
pub id: i32, pub id: i32,
pub user_id: i32, pub user_id: i32,
pub creation_date: NaiveDateTime, pub creation_date: SqlDateTime,
pub kind: String, pub kind: String,
pub object_id: i32 pub object_id: i32
} }
@ -40,14 +40,14 @@ impl Notification {
insert!(notifications, NewNotification); insert!(notifications, NewNotification);
get!(notifications); get!(notifications);
pub fn find_for_user(conn: &PgConnection, user: &User) -> Vec<Notification> { pub fn find_for_user(conn: &Connection, user: &User) -> Vec<Notification> {
notifications::table.filter(notifications::user_id.eq(user.id)) notifications::table.filter(notifications::user_id.eq(user.id))
.order_by(notifications::creation_date.desc()) .order_by(notifications::creation_date.desc())
.load::<Notification>(conn) .load::<Notification>(conn)
.expect("Couldn't load user notifications") .expect("Couldn't load user notifications")
} }
pub fn page_for_user(conn: &PgConnection, user: &User, (min, max): (i32, i32)) -> Vec<Notification> { pub fn page_for_user(conn: &Connection, user: &User, (min, max): (i32, i32)) -> Vec<Notification> {
notifications::table.filter(notifications::user_id.eq(user.id)) notifications::table.filter(notifications::user_id.eq(user.id))
.order_by(notifications::creation_date.desc()) .order_by(notifications::creation_date.desc())
.offset(min.into()) .offset(min.into())
@ -56,14 +56,14 @@ impl Notification {
.expect("Couldn't load user notifications page") .expect("Couldn't load user notifications page")
} }
pub fn find<S: Into<String>>(conn: &PgConnection, kind: S, obj: i32) -> Option<Notification> { pub fn find<S: Into<String>>(conn: &Connection, kind: S, obj: i32) -> Option<Notification> {
notifications::table.filter(notifications::kind.eq(kind.into())) notifications::table.filter(notifications::kind.eq(kind.into()))
.filter(notifications::object_id.eq(obj)) .filter(notifications::object_id.eq(obj))
.get_result::<Notification>(conn) .get_result::<Notification>(conn)
.ok() .ok()
} }
pub fn to_json(&self, conn: &PgConnection) -> serde_json::Value { pub fn to_json(&self, conn: &Connection) -> serde_json::Value {
let mut json = json!(self); let mut json = json!(self);
json["object"] = json!(match self.kind.as_ref() { json["object"] = json!(match self.kind.as_ref() {
notification_kind::COMMENT => Comment::get(conn, self.object_id).map(|comment| notification_kind::COMMENT => Comment::get(conn, self.object_id).map(|comment|

View File

@ -1,4 +1,4 @@
use diesel::{self, PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods}; use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods};
use posts::Post; use posts::Post;
use users::User; use users::User;

View File

@ -5,7 +5,7 @@ use activitypub::{
}; };
use canapi::{Error, Provider}; use canapi::{Error, Provider};
use chrono::{NaiveDateTime, TimeZone, Utc}; use chrono::{NaiveDateTime, TimeZone, Utc};
use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods, BelongingToDsl, dsl::any}; use diesel::{self, RunQueryDsl, QueryDsl, ExpressionMethods, BelongingToDsl, dsl::any};
use heck::KebabCase; use heck::KebabCase;
use serde_json; use serde_json;
@ -15,7 +15,7 @@ use plume_common::activity_pub::{
PUBLIC_VISIBILTY, Id, IntoId, PUBLIC_VISIBILTY, Id, IntoId,
inbox::{Deletable, FromActivity} inbox::{Deletable, FromActivity}
}; };
use {BASE_URL, ap_url}; use {BASE_URL, ap_url, Connection, SqlDateTime};
use blogs::Blog; use blogs::Blog;
use instance::Instance; use instance::Instance;
use likes::Like; use likes::Like;
@ -36,7 +36,7 @@ pub struct Post {
pub content: SafeString, pub content: SafeString,
pub published: bool, pub published: bool,
pub license: String, pub license: String,
pub creation_date: NaiveDateTime, pub creation_date: SqlDateTime,
pub ap_url: String, pub ap_url: String,
pub subtitle: String, pub subtitle: String,
pub source: String, pub source: String,
@ -112,7 +112,7 @@ impl Post {
find_by!(posts, find_by_slug, slug as String, blog_id as i32); find_by!(posts, find_by_slug, slug as String, blog_id as i32);
find_by!(posts, find_by_ap_url, ap_url as String); find_by!(posts, find_by_ap_url, ap_url as String);
pub fn list_by_tag(conn: &PgConnection, tag: String, (min, max): (i32, i32)) -> Vec<Post> { pub fn list_by_tag(conn: &Connection, tag: String, (min, max): (i32, i32)) -> Vec<Post> {
use schema::tags; use schema::tags;
let ids = tags::table.filter(tags::tag.eq(tag)).select(tags::post_id); let ids = tags::table.filter(tags::tag.eq(tag)).select(tags::post_id);
@ -125,7 +125,7 @@ impl Post {
.expect("Error loading posts by tag") .expect("Error loading posts by tag")
} }
pub fn count_for_tag(conn: &PgConnection, tag: String) -> i64 { pub fn count_for_tag(conn: &Connection, tag: String) -> i64 {
use schema::tags; use schema::tags;
let ids = tags::table.filter(tags::tag.eq(tag)).select(tags::post_id); let ids = tags::table.filter(tags::tag.eq(tag)).select(tags::post_id);
posts::table.filter(posts::id.eq(any(ids))) posts::table.filter(posts::id.eq(any(ids)))
@ -135,7 +135,7 @@ impl Post {
.expect("Error counting posts by tag") .expect("Error counting posts by tag")
} }
pub fn count_local(conn: &PgConnection) -> usize { pub fn count_local(conn: &Connection) -> usize {
use schema::post_authors; use schema::post_authors;
use schema::users; use schema::users;
let local_authors = users::table.filter(users::instance_id.eq(Instance::local_id(conn))).select(users::id); let local_authors = users::table.filter(users::instance_id.eq(Instance::local_id(conn))).select(users::id);
@ -147,11 +147,11 @@ impl Post {
.len() .len()
} }
pub fn count(conn: &PgConnection) -> i64 { pub fn count(conn: &Connection) -> i64 {
posts::table.filter(posts::published.eq(true)).count().get_result(conn).expect("Couldn't count posts") posts::table.filter(posts::published.eq(true)).count().get_result(conn).expect("Couldn't count posts")
} }
pub fn get_recents(conn: &PgConnection, limit: i64) -> Vec<Post> { pub fn get_recents(conn: &Connection, limit: i64) -> Vec<Post> {
posts::table.order(posts::creation_date.desc()) posts::table.order(posts::creation_date.desc())
.filter(posts::published.eq(true)) .filter(posts::published.eq(true))
.limit(limit) .limit(limit)
@ -159,7 +159,7 @@ impl Post {
.expect("Error loading recent posts") .expect("Error loading recent posts")
} }
pub fn get_recents_for_author(conn: &PgConnection, author: &User, limit: i64) -> Vec<Post> { pub fn get_recents_for_author(conn: &Connection, author: &User, limit: i64) -> Vec<Post> {
use schema::post_authors; use schema::post_authors;
let posts = PostAuthor::belonging_to(author).select(post_authors::post_id); let posts = PostAuthor::belonging_to(author).select(post_authors::post_id);
@ -171,7 +171,7 @@ impl Post {
.expect("Error loading recent posts for author") .expect("Error loading recent posts for author")
} }
pub fn get_recents_for_blog(conn: &PgConnection, blog: &Blog, limit: i64) -> Vec<Post> { pub fn get_recents_for_blog(conn: &Connection, blog: &Blog, limit: i64) -> Vec<Post> {
posts::table.filter(posts::blog_id.eq(blog.id)) posts::table.filter(posts::blog_id.eq(blog.id))
.filter(posts::published.eq(true)) .filter(posts::published.eq(true))
.order(posts::creation_date.desc()) .order(posts::creation_date.desc())
@ -180,14 +180,14 @@ impl Post {
.expect("Error loading recent posts for blog") .expect("Error loading recent posts for blog")
} }
pub fn get_for_blog(conn: &PgConnection, blog:&Blog) -> Vec<Post> { pub fn get_for_blog(conn: &Connection, blog:&Blog) -> Vec<Post> {
posts::table.filter(posts::blog_id.eq(blog.id)) posts::table.filter(posts::blog_id.eq(blog.id))
.filter(posts::published.eq(true)) .filter(posts::published.eq(true))
.load::<Post>(conn) .load::<Post>(conn)
.expect("Error loading posts for blog") .expect("Error loading posts for blog")
} }
pub fn blog_page(conn: &PgConnection, blog: &Blog, (min, max): (i32, i32)) -> Vec<Post> { pub fn blog_page(conn: &Connection, blog: &Blog, (min, max): (i32, i32)) -> Vec<Post> {
posts::table.filter(posts::blog_id.eq(blog.id)) posts::table.filter(posts::blog_id.eq(blog.id))
.filter(posts::published.eq(true)) .filter(posts::published.eq(true))
.order(posts::creation_date.desc()) .order(posts::creation_date.desc())
@ -198,7 +198,7 @@ impl Post {
} }
/// Give a page of all the recent posts known to this instance (= federated timeline) /// Give a page of all the recent posts known to this instance (= federated timeline)
pub fn get_recents_page(conn: &PgConnection, (min, max): (i32, i32)) -> Vec<Post> { pub fn get_recents_page(conn: &Connection, (min, max): (i32, i32)) -> Vec<Post> {
posts::table.order(posts::creation_date.desc()) posts::table.order(posts::creation_date.desc())
.filter(posts::published.eq(true)) .filter(posts::published.eq(true))
.offset(min.into()) .offset(min.into())
@ -208,7 +208,7 @@ impl Post {
} }
/// Give a page of posts from a specific instance /// Give a page of posts from a specific instance
pub fn get_instance_page(conn: &PgConnection, instance_id: i32, (min, max): (i32, i32)) -> Vec<Post> { pub fn get_instance_page(conn: &Connection, instance_id: i32, (min, max): (i32, i32)) -> Vec<Post> {
use schema::blogs; use schema::blogs;
let blog_ids = blogs::table.filter(blogs::instance_id.eq(instance_id)).select(blogs::id); let blog_ids = blogs::table.filter(blogs::instance_id.eq(instance_id)).select(blogs::id);
@ -223,7 +223,7 @@ impl Post {
} }
/// Give a page of customized user feed, based on a list of followed users /// Give a page of customized user feed, based on a list of followed users
pub fn user_feed_page(conn: &PgConnection, followed: Vec<i32>, (min, max): (i32, i32)) -> Vec<Post> { pub fn user_feed_page(conn: &Connection, followed: Vec<i32>, (min, max): (i32, i32)) -> Vec<Post> {
use schema::post_authors; use schema::post_authors;
let post_ids = post_authors::table.filter(post_authors::author_id.eq(any(followed))) let post_ids = post_authors::table.filter(post_authors::author_id.eq(any(followed)))
.select(post_authors::post_id); .select(post_authors::post_id);
@ -237,7 +237,7 @@ impl Post {
.expect("Error loading user feed page") .expect("Error loading user feed page")
} }
pub fn drafts_by_author(conn: &PgConnection, author: &User) -> Vec<Post> { pub fn drafts_by_author(conn: &Connection, author: &User) -> Vec<Post> {
use schema::post_authors; use schema::post_authors;
let posts = PostAuthor::belonging_to(author).select(post_authors::post_id); let posts = PostAuthor::belonging_to(author).select(post_authors::post_id);
@ -248,14 +248,14 @@ impl Post {
.expect("Error listing drafts") .expect("Error listing drafts")
} }
pub fn get_authors(&self, conn: &PgConnection) -> Vec<User> { pub fn get_authors(&self, conn: &Connection) -> Vec<User> {
use schema::users; use schema::users;
use schema::post_authors; use schema::post_authors;
let author_list = PostAuthor::belonging_to(self).select(post_authors::author_id); let author_list = PostAuthor::belonging_to(self).select(post_authors::author_id);
users::table.filter(users::id.eq(any(author_list))).load::<User>(conn).unwrap() users::table.filter(users::id.eq(any(author_list))).load::<User>(conn).unwrap()
} }
pub fn get_blog(&self, conn: &PgConnection) -> Blog { pub fn get_blog(&self, conn: &Connection) -> Blog {
use schema::blogs; use schema::blogs;
blogs::table.filter(blogs::id.eq(self.blog_id)) blogs::table.filter(blogs::id.eq(self.blog_id))
.limit(1) .limit(1)
@ -264,21 +264,21 @@ impl Post {
.into_iter().nth(0).unwrap() .into_iter().nth(0).unwrap()
} }
pub fn get_likes(&self, conn: &PgConnection) -> Vec<Like> { pub fn get_likes(&self, conn: &Connection) -> Vec<Like> {
use schema::likes; use schema::likes;
likes::table.filter(likes::post_id.eq(self.id)) likes::table.filter(likes::post_id.eq(self.id))
.load::<Like>(conn) .load::<Like>(conn)
.expect("Couldn't load likes associted to post") .expect("Couldn't load likes associted to post")
} }
pub fn get_reshares(&self, conn: &PgConnection) -> Vec<Reshare> { pub fn get_reshares(&self, conn: &Connection) -> Vec<Reshare> {
use schema::reshares; use schema::reshares;
reshares::table.filter(reshares::post_id.eq(self.id)) reshares::table.filter(reshares::post_id.eq(self.id))
.load::<Reshare>(conn) .load::<Reshare>(conn)
.expect("Couldn't load reshares associted to post") .expect("Couldn't load reshares associted to post")
} }
pub fn update_ap_url(&self, conn: &PgConnection) -> Post { pub fn update_ap_url(&self, conn: &Connection) -> Post {
if self.ap_url.len() == 0 { if self.ap_url.len() == 0 {
diesel::update(self) diesel::update(self)
.set(posts::ap_url.eq(self.compute_id(conn))) .set(posts::ap_url.eq(self.compute_id(conn)))
@ -288,7 +288,7 @@ impl Post {
} }
} }
pub fn get_receivers_urls(&self, conn: &PgConnection) -> Vec<String> { pub fn get_receivers_urls(&self, conn: &Connection) -> Vec<String> {
let followers = self.get_authors(conn).into_iter().map(|a| a.get_followers(conn)).collect::<Vec<Vec<User>>>(); let followers = self.get_authors(conn).into_iter().map(|a| a.get_followers(conn)).collect::<Vec<Vec<User>>>();
let to = followers.into_iter().fold(vec![], |mut acc, f| { let to = followers.into_iter().fold(vec![], |mut acc, f| {
for x in f { for x in f {
@ -299,7 +299,7 @@ impl Post {
to to
} }
pub fn into_activity(&self, conn: &PgConnection) -> Article { pub fn into_activity(&self, conn: &Connection) -> Article {
let mut to = self.get_receivers_urls(conn); let mut to = self.get_receivers_urls(conn);
to.push(PUBLIC_VISIBILTY.to_string()); to.push(PUBLIC_VISIBILTY.to_string());
@ -328,7 +328,7 @@ impl Post {
article article
} }
pub fn create_activity(&self, conn: &PgConnection) -> Create { pub fn create_activity(&self, conn: &Connection) -> Create {
let article = self.into_activity(conn); let article = self.into_activity(conn);
let mut act = Create::default(); let mut act = Create::default();
act.object_props.set_id_string(format!("{}activity", self.ap_url)).expect("Post::create_activity: id error"); act.object_props.set_id_string(format!("{}activity", self.ap_url)).expect("Post::create_activity: id error");
@ -341,7 +341,7 @@ impl Post {
act act
} }
pub fn update_activity(&self, conn: &PgConnection) -> Update { pub fn update_activity(&self, conn: &Connection) -> Update {
let article = self.into_activity(conn); let article = self.into_activity(conn);
let mut act = Update::default(); let mut act = Update::default();
act.object_props.set_id_string(format!("{}/update-{}", self.ap_url, Utc::now().timestamp())).expect("Post::update_activity: id error"); act.object_props.set_id_string(format!("{}/update-{}", self.ap_url, Utc::now().timestamp())).expect("Post::update_activity: id error");
@ -354,7 +354,7 @@ impl Post {
act act
} }
pub fn handle_update(conn: &PgConnection, updated: Article) { pub fn handle_update(conn: &Connection, updated: Article) {
let id = updated.object_props.id_string().expect("Post::handle_update: id error"); let id = updated.object_props.id_string().expect("Post::handle_update: id error");
let mut post = Post::find_by_ap_url(conn, id).unwrap(); let mut post = Post::find_by_ap_url(conn, id).unwrap();
@ -382,7 +382,7 @@ impl Post {
post.update(conn); post.update(conn);
} }
pub fn to_json(&self, conn: &PgConnection) -> serde_json::Value { pub fn to_json(&self, conn: &Connection) -> serde_json::Value {
let blog = self.get_blog(conn); let blog = self.get_blog(conn);
json!({ json!({
"post": self, "post": self,
@ -394,13 +394,13 @@ impl Post {
}) })
} }
pub fn compute_id(&self, conn: &PgConnection) -> String { pub fn compute_id(&self, conn: &Connection) -> String {
ap_url(format!("{}/~/{}/{}/", BASE_URL.as_str(), self.get_blog(conn).get_fqn(conn), self.slug)) ap_url(format!("{}/~/{}/{}/", BASE_URL.as_str(), self.get_blog(conn).get_fqn(conn), self.slug))
} }
} }
impl FromActivity<Article, PgConnection> for Post { impl FromActivity<Article, Connection> for Post {
fn from_activity(conn: &PgConnection, article: Article, _actor: Id) -> Post { fn from_activity(conn: &Connection, article: Article, _actor: Id) -> Post {
if let Some(post) = Post::find_by_ap_url(conn, article.object_props.id_string().unwrap_or(String::new())) { if let Some(post) = Post::find_by_ap_url(conn, article.object_props.id_string().unwrap_or(String::new())) {
post post
} else { } else {
@ -457,8 +457,8 @@ impl FromActivity<Article, PgConnection> for Post {
} }
} }
impl Deletable<PgConnection, Delete> for Post { impl Deletable<Connection, Delete> for Post {
fn delete(&self, conn: &PgConnection) -> Delete { fn delete(&self, conn: &Connection) -> Delete {
let mut act = Delete::default(); let mut act = Delete::default();
act.delete_props.set_actor_link(self.get_authors(conn)[0].clone().into_id()).expect("Post::delete: actor error"); act.delete_props.set_actor_link(self.get_authors(conn)[0].clone().into_id()).expect("Post::delete: actor error");
@ -473,7 +473,7 @@ impl Deletable<PgConnection, Delete> for Post {
act act
} }
fn delete_id(id: String, conn: &PgConnection) { fn delete_id(id: String, conn: &Connection) {
Post::find_by_ap_url(conn, id).map(|p| p.delete(conn)); Post::find_by_ap_url(conn, id).map(|p| p.delete(conn));
} }
} }

View File

@ -1,8 +1,8 @@
use activitypub::activity::{Announce, Undo}; use activitypub::activity::{Announce, Undo};
use chrono::NaiveDateTime;
use diesel::{self, PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods}; use diesel::{self, PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods};
use plume_common::activity_pub::{Id, IntoId, inbox::{FromActivity, Notify, Deletable}, PUBLIC_VISIBILTY}; use plume_common::activity_pub::{Id, IntoId, inbox::{FromActivity, Notify, Deletable}, PUBLIC_VISIBILTY};
use {Connection, SqlDateTime};
use notifications::*; use notifications::*;
use posts::Post; use posts::Post;
use users::User; use users::User;
@ -14,7 +14,7 @@ pub struct Reshare {
pub user_id: i32, pub user_id: i32,
pub post_id: i32, pub post_id: i32,
pub ap_url: String, pub ap_url: String,
pub creation_date: NaiveDateTime pub creation_date: SqlDateTime
} }
#[derive(Insertable)] #[derive(Insertable)]
@ -31,7 +31,7 @@ impl Reshare {
find_by!(reshares, find_by_ap_url, ap_url as String); find_by!(reshares, find_by_ap_url, ap_url as String);
find_by!(reshares, find_by_user_on_post, user_id as i32, post_id as i32); find_by!(reshares, find_by_user_on_post, user_id as i32, post_id as i32);
pub fn update_ap_url(&self, conn: &PgConnection) { pub fn update_ap_url(&self, conn: &Connection) {
if self.ap_url.len() == 0 { if self.ap_url.len() == 0 {
diesel::update(self) diesel::update(self)
.set(reshares::ap_url.eq(format!( .set(reshares::ap_url.eq(format!(
@ -43,7 +43,7 @@ impl Reshare {
} }
} }
pub fn get_recents_for_author(conn: &PgConnection, user: &User, limit: i64) -> Vec<Reshare> { pub fn get_recents_for_author(conn: &Connection, user: &User, limit: i64) -> Vec<Reshare> {
reshares::table.filter(reshares::user_id.eq(user.id)) reshares::table.filter(reshares::user_id.eq(user.id))
.order(reshares::creation_date.desc()) .order(reshares::creation_date.desc())
.limit(limit) .limit(limit)
@ -51,15 +51,15 @@ impl Reshare {
.expect("Error loading recent reshares for user") .expect("Error loading recent reshares for user")
} }
pub fn get_post(&self, conn: &PgConnection) -> Option<Post> { pub fn get_post(&self, conn: &Connection) -> Option<Post> {
Post::get(conn, self.post_id) Post::get(conn, self.post_id)
} }
pub fn get_user(&self, conn: &PgConnection) -> Option<User> { pub fn get_user(&self, conn: &Connection) -> Option<User> {
User::get(conn, self.user_id) User::get(conn, self.user_id)
} }
pub fn into_activity(&self, conn: &PgConnection) -> Announce { pub fn into_activity(&self, conn: &Connection) -> Announce {
let mut act = Announce::default(); let mut act = Announce::default();
act.announce_props.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).unwrap(); act.announce_props.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).unwrap();
act.announce_props.set_object_link(Post::get(conn, self.post_id).unwrap().into_id()).unwrap(); act.announce_props.set_object_link(Post::get(conn, self.post_id).unwrap().into_id()).unwrap();
@ -72,7 +72,7 @@ impl Reshare {
} }
impl FromActivity<Announce, PgConnection> for Reshare { impl FromActivity<Announce, PgConnection> for Reshare {
fn from_activity(conn: &PgConnection, announce: Announce, _actor: Id) -> Reshare { fn from_activity(conn: &Connection, announce: Announce, _actor: Id) -> Reshare {
let user = User::from_url(conn, announce.announce_props.actor_link::<Id>().expect("Reshare::from_activity: actor error").into()); let user = User::from_url(conn, announce.announce_props.actor_link::<Id>().expect("Reshare::from_activity: actor error").into());
let post = Post::find_by_ap_url(conn, announce.announce_props.object_link::<Id>().expect("Reshare::from_activity: object error").into()); let post = Post::find_by_ap_url(conn, announce.announce_props.object_link::<Id>().expect("Reshare::from_activity: object error").into());
let reshare = Reshare::insert(conn, NewReshare { let reshare = Reshare::insert(conn, NewReshare {
@ -86,7 +86,7 @@ impl FromActivity<Announce, PgConnection> for Reshare {
} }
impl Notify<PgConnection> for Reshare { impl Notify<PgConnection> for Reshare {
fn notify(&self, conn: &PgConnection) { fn notify(&self, conn: &Connection) {
let post = self.get_post(conn).unwrap(); let post = self.get_post(conn).unwrap();
for author in post.get_authors(conn) { for author in post.get_authors(conn) {
Notification::insert(conn, NewNotification { Notification::insert(conn, NewNotification {
@ -99,7 +99,7 @@ impl Notify<PgConnection> for Reshare {
} }
impl Deletable<PgConnection, Undo> for Reshare { impl Deletable<PgConnection, Undo> for Reshare {
fn delete(&self, conn: &PgConnection) -> Undo { fn delete(&self, conn: &Connection) -> Undo {
diesel::delete(self).execute(conn).unwrap(); diesel::delete(self).execute(conn).unwrap();
// delete associated notification if any // delete associated notification if any
@ -117,7 +117,7 @@ impl Deletable<PgConnection, Undo> for Reshare {
act act
} }
fn delete_id(id: String, conn: &PgConnection) { fn delete_id(id: String, conn: &Connection) {
if let Some(reshare) = Reshare::find_by_ap_url(conn, id) { if let Some(reshare) = Reshare::find_by_ap_url(conn, id) {
reshare.delete(conn); reshare.delete(conn);
} }

View File

@ -1,21 +1,21 @@
table! { table! {
blog_authors (id) { blog_authors (id) {
id -> Integer, id -> Int4,
blog_id -> Integer, blog_id -> Int4,
author_id -> Integer, author_id -> Int4,
is_owner -> Bool, is_owner -> Bool,
} }
} }
table! { table! {
blogs (id) { blogs (id) {
id -> Integer, id -> Int4,
actor_id -> Text, actor_id -> Varchar,
title -> Text, title -> Varchar,
summary -> Text, summary -> Text,
outbox_url -> Text, outbox_url -> Varchar,
inbox_url -> Text, inbox_url -> Varchar,
instance_id -> Integer, instance_id -> Int4,
creation_date -> Timestamp, creation_date -> Timestamp,
ap_url -> Text, ap_url -> Text,
private_key -> Nullable<Text>, private_key -> Nullable<Text>,
@ -25,13 +25,13 @@ table! {
table! { table! {
comments (id) { comments (id) {
id -> Integer, id -> Int4,
content -> Text, content -> Text,
in_response_to_id -> Nullable<Integer>, in_response_to_id -> Nullable<Int4>,
post_id -> Integer, post_id -> Int4,
author_id -> Integer, author_id -> Int4,
creation_date -> Timestamp, creation_date -> Timestamp,
ap_url -> Nullable<Text>, ap_url -> Nullable<Varchar>,
sensitive -> Bool, sensitive -> Bool,
spoiler_text -> Text, spoiler_text -> Text,
} }
@ -39,18 +39,18 @@ table! {
table! { table! {
follows (id) { follows (id) {
id -> Integer, id -> Int4,
follower_id -> Integer, follower_id -> Int4,
following_id -> Integer, following_id -> Int4,
ap_url -> Text, ap_url -> Text,
} }
} }
table! { table! {
instances (id) { instances (id) {
id -> Integer, id -> Int4,
public_domain -> Text, public_domain -> Varchar,
name -> Text, name -> Varchar,
local -> Bool, local -> Bool,
blocked -> Bool, blocked -> Bool,
creation_date -> Timestamp, creation_date -> Timestamp,
@ -58,73 +58,73 @@ table! {
short_description -> Text, short_description -> Text,
long_description -> Text, long_description -> Text,
default_license -> Text, default_license -> Text,
long_description_html -> Text, long_description_html -> Varchar,
short_description_html -> Text, short_description_html -> Varchar,
} }
} }
table! { table! {
likes (id) { likes (id) {
id -> Integer, id -> Int4,
user_id -> Integer, user_id -> Int4,
post_id -> Integer, post_id -> Int4,
ap_url -> Text,
creation_date -> Timestamp, creation_date -> Timestamp,
ap_url -> Varchar,
} }
} }
table! { table! {
medias (id) { medias (id) {
id -> Integer, id -> Int4,
file_path -> Text, file_path -> Text,
alt_text -> Text, alt_text -> Text,
is_remote -> Bool, is_remote -> Bool,
remote_url -> Nullable<Text>, remote_url -> Nullable<Text>,
sensitive -> Bool, sensitive -> Bool,
content_warning -> Nullable<Text>, content_warning -> Nullable<Text>,
owner_id -> Integer, owner_id -> Int4,
} }
} }
table! { table! {
mentions (id) { mentions (id) {
id -> Integer, id -> Int4,
mentioned_id -> Integer, mentioned_id -> Int4,
post_id -> Nullable<Integer>, post_id -> Nullable<Int4>,
comment_id -> Nullable<Integer>, comment_id -> Nullable<Int4>,
ap_url -> Text, ap_url -> Varchar,
} }
} }
table! { table! {
notifications (id) { notifications (id) {
id -> Integer, id -> Int4,
user_id -> Integer, user_id -> Int4,
creation_date -> Timestamp, creation_date -> Timestamp,
kind -> Text, kind -> Varchar,
object_id -> Integer, object_id -> Int4,
} }
} }
table! { table! {
post_authors (id) { post_authors (id) {
id -> Integer, id -> Int4,
post_id -> Integer, post_id -> Int4,
author_id -> Integer, author_id -> Int4,
} }
} }
table! { table! {
posts (id) { posts (id) {
id -> Integer, id -> Int4,
blog_id -> Integer, blog_id -> Int4,
slug -> Text, slug -> Varchar,
title -> Text, title -> Varchar,
content -> Text, content -> Text,
published -> Bool, published -> Bool,
license -> Text, license -> Varchar,
creation_date -> Timestamp, creation_date -> Timestamp,
ap_url -> Text, ap_url -> Varchar,
subtitle -> Text, subtitle -> Text,
source -> Text, source -> Text,
} }
@ -132,42 +132,42 @@ table! {
table! { table! {
reshares (id) { reshares (id) {
id -> Integer, id -> Int4,
user_id -> Integer, user_id -> Int4,
post_id -> Integer, post_id -> Int4,
ap_url -> Text, ap_url -> Varchar,
creation_date -> Timestamp, creation_date -> Timestamp,
} }
} }
table! { table! {
tags (id) { tags (id) {
id -> Integer, id -> Int4,
tag -> Text, tag -> Text,
is_hastag -> Bool, is_hastag -> Bool,
post_id -> Integer, post_id -> Int4,
} }
} }
table! { table! {
users (id) { users (id) {
id -> Integer, id -> Int4,
username -> Text, username -> Varchar,
display_name -> Text, display_name -> Varchar,
outbox_url -> Text, outbox_url -> Varchar,
inbox_url -> Text, inbox_url -> Varchar,
is_admin -> Bool, is_admin -> Bool,
summary -> Text, summary -> Text,
email -> Nullable<Text>, email -> Nullable<Text>,
hashed_password -> Nullable<Text>, hashed_password -> Nullable<Text>,
instance_id -> Integer, instance_id -> Int4,
creation_date -> Timestamp, creation_date -> Timestamp,
ap_url -> Text, ap_url -> Text,
private_key -> Nullable<Text>, private_key -> Nullable<Text>,
public_key -> Text, public_key -> Text,
shared_inbox_url -> Nullable<Text>, shared_inbox_url -> Nullable<Varchar>,
followers_endpoint -> Text, followers_endpoint -> Varchar,
avatar_id -> Nullable<Integer>, avatar_id -> Nullable<Int4>,
last_fetched_date -> Timestamp, last_fetched_date -> Timestamp,
} }
} }

View File

@ -1,7 +1,7 @@
use diesel::{self, PgConnection, ExpressionMethods, RunQueryDsl, QueryDsl}; use diesel::{self, ExpressionMethods, RunQueryDsl, QueryDsl};
use plume_common::activity_pub::Hashtag; use plume_common::activity_pub::Hashtag;
use ap_url; use {ap_url, Connection};
use instance::Instance; use instance::Instance;
use schema::tags; use schema::tags;
@ -27,14 +27,14 @@ impl Tag {
find_by!(tags, find_by_name, tag as String); find_by!(tags, find_by_name, tag as String);
list_by!(tags, for_post, post_id as i32); list_by!(tags, for_post, post_id as i32);
pub fn into_activity(&self, conn: &PgConnection) -> Hashtag { pub fn into_activity(&self, conn: &Connection) -> Hashtag {
let mut ht = Hashtag::default(); let mut ht = Hashtag::default();
ht.set_href_string(ap_url(format!("{}/tag/{}", Instance::get_local(conn).unwrap().public_domain, self.tag))).expect("Tag::into_activity: href error"); ht.set_href_string(ap_url(format!("{}/tag/{}", Instance::get_local(conn).unwrap().public_domain, self.tag))).expect("Tag::into_activity: href error");
ht.set_name_string(self.tag.clone()).expect("Tag::into_activity: name error"); ht.set_name_string(self.tag.clone()).expect("Tag::into_activity: name error");
ht ht
} }
pub fn from_activity(conn: &PgConnection, tag: Hashtag, post: i32) -> Tag { pub fn from_activity(conn: &Connection, tag: Hashtag, post: i32) -> Tag {
Tag::insert(conn, NewTag { Tag::insert(conn, NewTag {
tag: tag.name_string().expect("Tag::from_activity: name error"), tag: tag.name_string().expect("Tag::from_activity: name error"),
is_hastag: false, is_hastag: false,

View File

@ -5,8 +5,8 @@ use activitypub::{
object::Image, object::Image,
}; };
use bcrypt; use bcrypt;
use chrono::{NaiveDateTime, Utc}; use chrono::{Utc, NaiveDateTime};
use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods, BelongingToDsl, PgConnection, dsl::any}; use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods, BelongingToDsl, dsl::any};
use openssl::{ use openssl::{
hash::MessageDigest, hash::MessageDigest,
pkey::{PKey, Private}, pkey::{PKey, Private},
@ -31,7 +31,7 @@ use serde_json;
use url::Url; use url::Url;
use webfinger::*; use webfinger::*;
use {BASE_URL, USE_HTTPS, ap_url}; use {BASE_URL, USE_HTTPS, ap_url, Connection};
use db_conn::DbConn; use db_conn::DbConn;
use blogs::Blog; use blogs::Blog;
use blog_authors::BlogAuthor; use blog_authors::BlogAuthor;
@ -100,28 +100,28 @@ impl User {
find_by!(users, find_by_name, username as String, instance_id as i32); find_by!(users, find_by_name, username as String, instance_id as i32);
find_by!(users, find_by_ap_url, ap_url as String); find_by!(users, find_by_ap_url, ap_url as String);
pub fn one_by_instance(conn: &PgConnection) -> Vec<User> { pub fn one_by_instance(conn: &Connection) -> Vec<User> {
users::table.distinct_on(users::instance_id) users::table.distinct_on(users::instance_id)
.get_results::<User>(conn) .get_results::<User>(conn)
.expect("Error in User::on_by_instance") .expect("Error in User::on_by_instance")
} }
pub fn delete(&self, conn: &PgConnection) { pub fn delete(&self, conn: &Connection) {
diesel::delete(self).execute(conn).expect("Couldn't remove user from DB"); diesel::delete(self).execute(conn).expect("Couldn't remove user from DB");
} }
pub fn get_instance(&self, conn: &PgConnection) -> Instance { pub fn get_instance(&self, conn: &Connection) -> Instance {
Instance::get(conn, self.instance_id).expect("Couldn't find instance") Instance::get(conn, self.instance_id).expect("Couldn't find instance")
} }
pub fn grant_admin_rights(&self, conn: &PgConnection) { pub fn grant_admin_rights(&self, conn: &Connection) {
diesel::update(self) diesel::update(self)
.set(users::is_admin.eq(true)) .set(users::is_admin.eq(true))
.load::<User>(conn) .load::<User>(conn)
.expect("Couldn't grant admin rights"); .expect("Couldn't grant admin rights");
} }
pub fn update(&self, conn: &PgConnection, name: String, email: String, summary: String) -> User { pub fn update(&self, conn: &Connection, name: String, email: String, summary: String) -> User {
diesel::update(self) diesel::update(self)
.set(( .set((
users::display_name.eq(name), users::display_name.eq(name),
@ -132,18 +132,18 @@ impl User {
.into_iter().nth(0).unwrap() .into_iter().nth(0).unwrap()
} }
pub fn count_local(conn: &PgConnection) -> usize { pub fn count_local(conn: &Connection) -> usize {
users::table.filter(users::instance_id.eq(Instance::local_id(conn))) users::table.filter(users::instance_id.eq(Instance::local_id(conn)))
.load::<User>(conn) .load::<User>(conn)
.expect("Couldn't load local users") .expect("Couldn't load local users")
.len() .len()
} }
pub fn find_local(conn: &PgConnection, username: String) -> Option<User> { pub fn find_local(conn: &Connection, username: String) -> Option<User> {
User::find_by_name(conn, username, Instance::local_id(conn)) User::find_by_name(conn, username, Instance::local_id(conn))
} }
pub fn find_by_fqn(conn: &PgConnection, fqn: String) -> Option<User> { pub fn find_by_fqn(conn: &Connection, fqn: String) -> Option<User> {
if fqn.contains("@") { // remote user if fqn.contains("@") { // remote user
match Instance::find_by_domain(conn, String::from(fqn.split("@").last().unwrap())) { match Instance::find_by_domain(conn, String::from(fqn.split("@").last().unwrap())) {
Some(instance) => { Some(instance) => {
@ -159,7 +159,7 @@ impl User {
} }
} }
fn fetch_from_webfinger(conn: &PgConnection, acct: String) -> Option<User> { fn fetch_from_webfinger(conn: &Connection, acct: String) -> Option<User> {
match resolve(acct.clone(), *USE_HTTPS) { match resolve(acct.clone(), *USE_HTTPS) {
Ok(wf) => wf.links.into_iter().find(|l| l.mime_type == Some(String::from("application/activity+json"))).and_then(|l| User::fetch_from_url(conn, l.href.expect("No href for AP WF link"))), Ok(wf) => wf.links.into_iter().find(|l| l.mime_type == Some(String::from("application/activity+json"))).and_then(|l| User::fetch_from_url(conn, l.href.expect("No href for AP WF link"))),
Err(details) => { Err(details) => {
@ -192,11 +192,11 @@ impl User {
} }
} }
pub fn fetch_from_url(conn: &PgConnection, url: String) -> Option<User> { pub fn fetch_from_url(conn: &Connection, url: String) -> Option<User> {
User::fetch(url.clone()).map(|json| (User::from_activity(conn, json, Url::parse(url.as_ref()).unwrap().host_str().unwrap().to_string()))) User::fetch(url.clone()).map(|json| (User::from_activity(conn, json, Url::parse(url.as_ref()).unwrap().host_str().unwrap().to_string())))
} }
fn from_activity(conn: &PgConnection, acct: CustomPerson, inst: String) -> User { fn from_activity(conn: &Connection, acct: CustomPerson, inst: String) -> User {
let instance = match Instance::find_by_domain(conn, inst.clone()) { let instance = match Instance::find_by_domain(conn, inst.clone()) {
Some(instance) => instance, Some(instance) => instance,
None => { None => {
@ -242,7 +242,7 @@ impl User {
user user
} }
pub fn refetch(&self, conn: &PgConnection) { pub fn refetch(&self, conn: &Connection) {
User::fetch(self.ap_url.clone()).map(|json| { User::fetch(self.ap_url.clone()).map(|json| {
let avatar = Media::save_remote(conn, json.object.object_props.icon_image().expect("User::refetch: icon error") let avatar = Media::save_remote(conn, json.object.object_props.icon_image().expect("User::refetch: icon error")
.object_props.url_string().expect("User::refetch: icon.url error")); .object_props.url_string().expect("User::refetch: icon.url error"));
@ -274,7 +274,7 @@ impl User {
} }
} }
pub fn update_boxes(&self, conn: &PgConnection) { pub fn update_boxes(&self, conn: &Connection) {
let instance = self.get_instance(conn); let instance = self.get_instance(conn);
if self.outbox_url.len() == 0 { if self.outbox_url.len() == 0 {
diesel::update(self) diesel::update(self)
@ -307,7 +307,7 @@ impl User {
} }
} }
pub fn get_local_page(conn: &PgConnection, (min, max): (i32, i32)) -> Vec<User> { pub fn get_local_page(conn: &Connection, (min, max): (i32, i32)) -> Vec<User> {
users::table.filter(users::instance_id.eq(1)) users::table.filter(users::instance_id.eq(1))
.order(users::username.asc()) .order(users::username.asc())
.offset(min.into()) .offset(min.into())
@ -316,7 +316,7 @@ impl User {
.expect("Error getting local users page") .expect("Error getting local users page")
} }
pub fn outbox(&self, conn: &PgConnection) -> ActivityStream<OrderedCollection> { pub fn outbox(&self, conn: &Connection) -> ActivityStream<OrderedCollection> {
let acts = self.get_activities(conn); let acts = self.get_activities(conn);
let n_acts = acts.len(); let n_acts = acts.len();
let mut coll = OrderedCollection::default(); let mut coll = OrderedCollection::default();
@ -369,7 +369,7 @@ impl User {
} }
} }
fn get_activities(&self, conn: &PgConnection) -> Vec<serde_json::Value> { fn get_activities(&self, conn: &Connection) -> Vec<serde_json::Value> {
use schema::posts; use schema::posts;
use schema::post_authors; use schema::post_authors;
let posts_by_self = PostAuthor::belonging_to(self).select(post_authors::post_id); let posts_by_self = PostAuthor::belonging_to(self).select(post_authors::post_id);
@ -382,7 +382,7 @@ impl User {
}).collect::<Vec<serde_json::Value>>() }).collect::<Vec<serde_json::Value>>()
} }
pub fn get_fqn(&self, conn: &PgConnection) -> String { pub fn get_fqn(&self, conn: &Connection) -> String {
if self.instance_id == Instance::local_id(conn) { if self.instance_id == Instance::local_id(conn) {
self.username.clone() self.username.clone()
} else { } else {
@ -390,13 +390,13 @@ impl User {
} }
} }
pub fn get_followers(&self, conn: &PgConnection) -> Vec<User> { pub fn get_followers(&self, conn: &Connection) -> Vec<User> {
use schema::follows; use schema::follows;
let follows = Follow::belonging_to(self).select(follows::follower_id); let follows = Follow::belonging_to(self).select(follows::follower_id);
users::table.filter(users::id.eq(any(follows))).load::<User>(conn).unwrap() users::table.filter(users::id.eq(any(follows))).load::<User>(conn).unwrap()
} }
pub fn get_followers_page(&self, conn: &PgConnection, (min, max): (i32, i32)) -> Vec<User> { pub fn get_followers_page(&self, conn: &Connection, (min, max): (i32, i32)) -> Vec<User> {
use schema::follows; use schema::follows;
let follows = Follow::belonging_to(self).select(follows::follower_id); let follows = Follow::belonging_to(self).select(follows::follower_id);
users::table.filter(users::id.eq(any(follows))) users::table.filter(users::id.eq(any(follows)))
@ -405,13 +405,13 @@ impl User {
.load::<User>(conn).unwrap() .load::<User>(conn).unwrap()
} }
pub fn get_following(&self, conn: &PgConnection) -> Vec<User> { pub fn get_following(&self, conn: &Connection) -> Vec<User> {
use schema::follows; use schema::follows;
let follows = follows::table.filter(follows::follower_id.eq(self.id)).select(follows::following_id); let follows = follows::table.filter(follows::follower_id.eq(self.id)).select(follows::following_id);
users::table.filter(users::id.eq(any(follows))).load::<User>(conn).unwrap() users::table.filter(users::id.eq(any(follows))).load::<User>(conn).unwrap()
} }
pub fn is_followed_by(&self, conn: &PgConnection, other_id: i32) -> bool { pub fn is_followed_by(&self, conn: &Connection, other_id: i32) -> bool {
use schema::follows; use schema::follows;
follows::table follows::table
.filter(follows::follower_id.eq(other_id)) .filter(follows::follower_id.eq(other_id))
@ -421,7 +421,7 @@ impl User {
.len() > 0 .len() > 0
} }
pub fn is_following(&self, conn: &PgConnection, other_id: i32) -> bool { pub fn is_following(&self, conn: &Connection, other_id: i32) -> bool {
use schema::follows; use schema::follows;
follows::table follows::table
.filter(follows::follower_id.eq(self.id)) .filter(follows::follower_id.eq(self.id))
@ -431,7 +431,7 @@ impl User {
.len() > 0 .len() > 0
} }
pub fn has_liked(&self, conn: &PgConnection, post: &Post) -> bool { pub fn has_liked(&self, conn: &Connection, post: &Post) -> bool {
use schema::likes; use schema::likes;
likes::table likes::table
.filter(likes::post_id.eq(post.id)) .filter(likes::post_id.eq(post.id))
@ -441,7 +441,7 @@ impl User {
.len() > 0 .len() > 0
} }
pub fn has_reshared(&self, conn: &PgConnection, post: &Post) -> bool { pub fn has_reshared(&self, conn: &Connection, post: &Post) -> bool {
use schema::reshares; use schema::reshares;
reshares::table reshares::table
.filter(reshares::post_id.eq(post.id)) .filter(reshares::post_id.eq(post.id))
@ -451,7 +451,7 @@ impl User {
.len() > 0 .len() > 0
} }
pub fn is_author_in(&self, conn: &PgConnection, blog: Blog) -> bool { pub fn is_author_in(&self, conn: &Connection, blog: Blog) -> bool {
use schema::blog_authors; use schema::blog_authors;
blog_authors::table.filter(blog_authors::author_id.eq(self.id)) blog_authors::table.filter(blog_authors::author_id.eq(self.id))
.filter(blog_authors::blog_id.eq(blog.id)) .filter(blog_authors::blog_id.eq(blog.id))
@ -464,7 +464,7 @@ impl User {
PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.clone().unwrap().as_ref()).unwrap()).unwrap() PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.clone().unwrap().as_ref()).unwrap()).unwrap()
} }
pub fn into_activity(&self, conn: &PgConnection) -> CustomPerson { pub fn into_activity(&self, conn: &Connection) -> CustomPerson {
let mut actor = Person::default(); let mut actor = Person::default();
actor.object_props.set_id_string(self.ap_url.clone()).expect("User::into_activity: id error"); actor.object_props.set_id_string(self.ap_url.clone()).expect("User::into_activity: id error");
actor.object_props.set_name_string(self.display_name.clone()).expect("User::into_activity: name error"); actor.object_props.set_name_string(self.display_name.clone()).expect("User::into_activity: name error");
@ -494,7 +494,7 @@ impl User {
CustomPerson::new(actor, ap_signature) CustomPerson::new(actor, ap_signature)
} }
pub fn to_json(&self, conn: &PgConnection) -> serde_json::Value { pub fn to_json(&self, conn: &Connection) -> serde_json::Value {
let mut json = serde_json::to_value(self).unwrap(); let mut json = serde_json::to_value(self).unwrap();
json["fqn"] = serde_json::Value::String(self.get_fqn(conn)); json["fqn"] = serde_json::Value::String(self.get_fqn(conn));
json["name"] = if self.display_name.len() > 0 { json["name"] = if self.display_name.len() > 0 {
@ -506,7 +506,7 @@ impl User {
json json
} }
pub fn webfinger(&self, conn: &PgConnection) -> Webfinger { pub fn webfinger(&self, conn: &Connection) -> Webfinger {
Webfinger { Webfinger {
subject: format!("acct:{}@{}", self.username, self.get_instance(conn).public_domain), subject: format!("acct:{}@{}", self.username, self.get_instance(conn).public_domain),
aliases: vec![self.ap_url.clone()], aliases: vec![self.ap_url.clone()],
@ -533,7 +533,7 @@ impl User {
} }
} }
pub fn from_url(conn: &PgConnection, url: String) -> Option<User> { pub fn from_url(conn: &Connection, url: String) -> Option<User> {
User::find_by_ap_url(conn, url.clone()).or_else(|| { User::find_by_ap_url(conn, url.clone()).or_else(|| {
// The requested user was not in the DB // The requested user was not in the DB
// We try to fetch it if it is remote // We try to fetch it if it is remote
@ -545,7 +545,7 @@ impl User {
}) })
} }
pub fn set_avatar(&self, conn: &PgConnection, id: i32) { pub fn set_avatar(&self, conn: &Connection, id: i32) {
diesel::update(self) diesel::update(self)
.set(users::avatar_id.eq(id)) .set(users::avatar_id.eq(id))
.execute(conn) .execute(conn)
@ -609,7 +609,7 @@ impl Signer for User {
impl NewUser { impl NewUser {
/// Creates a new local user /// Creates a new local user
pub fn new_local( pub fn new_local(
conn: &PgConnection, conn: &Connection,
username: String, username: String,
display_name: String, display_name: String,
is_admin: bool, is_admin: bool,