Federated blogging application, thanks to ActivityPub https://joinplu.me
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

372 lines
9.0 KiB

  1. #![feature(try_trait)]
  2. #![feature(never_type)]
  3. #![feature(proc_macro_hygiene)]
  4. #[macro_use]
  5. extern crate diesel;
  6. #[macro_use]
  7. extern crate lazy_static;
  8. #[macro_use]
  9. extern crate plume_macro;
  10. #[macro_use]
  11. extern crate rocket;
  12. #[macro_use]
  13. extern crate serde_derive;
  14. #[macro_use]
  15. extern crate serde_json;
  16. #[macro_use]
  17. extern crate tantivy;
  18. extern crate riker;
  19. use plume_common::activity_pub::inbox::InboxError;
  20. #[cfg(not(any(feature = "sqlite", feature = "postgres")))]
  21. compile_error!("Either feature \"sqlite\" or \"postgres\" must be enabled for this crate.");
  22. #[cfg(all(feature = "sqlite", feature = "postgres"))]
  23. compile_error!("Either feature \"sqlite\" or \"postgres\" must be enabled for this crate.");
  24. #[cfg(all(feature = "sqlite", not(feature = "postgres")))]
  25. pub type Connection = diesel::SqliteConnection;
  26. #[cfg(all(not(feature = "sqlite"), feature = "postgres"))]
  27. pub type Connection = diesel::PgConnection;
  28. /// All the possible errors that can be encoutered in this crate
  29. #[derive(Debug)]
  30. pub enum Error {
  31. Blocklisted(bool, String),
  32. Db(diesel::result::Error),
  33. Inbox(Box<InboxError<Error>>),
  34. InvalidValue,
  35. Io(std::io::Error),
  36. MissingApProperty,
  37. NotFound,
  38. DbPool,
  39. Request,
  40. SerDe,
  41. Search(search::SearcherError),
  42. Signature,
  43. TimelineQuery(timeline::query::QueryError),
  44. Unauthorized,
  45. Url,
  46. Webfinger,
  47. Expired,
  48. }
  49. impl From<bcrypt::BcryptError> for Error {
  50. fn from(_: bcrypt::BcryptError) -> Self {
  51. Error::Signature
  52. }
  53. }
  54. pub const ITEMS_PER_PAGE: i32 = 12;
  55. impl From<openssl::error::ErrorStack> for Error {
  56. fn from(_: openssl::error::ErrorStack) -> Self {
  57. Error::Signature
  58. }
  59. }
  60. impl From<diesel::result::Error> for Error {
  61. fn from(err: diesel::result::Error) -> Self {
  62. Error::Db(err)
  63. }
  64. }
  65. impl From<std::option::NoneError> for Error {
  66. fn from(_: std::option::NoneError) -> Self {
  67. Error::NotFound
  68. }
  69. }
  70. impl From<url::ParseError> for Error {
  71. fn from(_: url::ParseError) -> Self {
  72. Error::Url
  73. }
  74. }
  75. impl From<serde_json::Error> for Error {
  76. fn from(_: serde_json::Error) -> Self {
  77. Error::SerDe
  78. }
  79. }
  80. impl From<reqwest::Error> for Error {
  81. fn from(_: reqwest::Error) -> Self {
  82. Error::Request
  83. }
  84. }
  85. impl From<reqwest::header::InvalidHeaderValue> for Error {
  86. fn from(_: reqwest::header::InvalidHeaderValue) -> Self {
  87. Error::Request
  88. }
  89. }
  90. impl From<activitypub::Error> for Error {
  91. fn from(err: activitypub::Error) -> Self {
  92. match err {
  93. activitypub::Error::NotFound => Error::MissingApProperty,
  94. _ => Error::SerDe,
  95. }
  96. }
  97. }
  98. impl From<webfinger::WebfingerError> for Error {
  99. fn from(_: webfinger::WebfingerError) -> Self {
  100. Error::Webfinger
  101. }
  102. }
  103. impl From<search::SearcherError> for Error {
  104. fn from(err: search::SearcherError) -> Self {
  105. Error::Search(err)
  106. }
  107. }
  108. impl From<timeline::query::QueryError> for Error {
  109. fn from(err: timeline::query::QueryError) -> Self {
  110. Error::TimelineQuery(err)
  111. }
  112. }
  113. impl From<std::io::Error> for Error {
  114. fn from(err: std::io::Error) -> Self {
  115. Error::Io(err)
  116. }
  117. }
  118. impl From<InboxError<Error>> for Error {
  119. fn from(err: InboxError<Error>) -> Error {
  120. match err {
  121. InboxError::InvalidActor(Some(e)) | InboxError::InvalidObject(Some(e)) => e,
  122. e => Error::Inbox(Box::new(e)),
  123. }
  124. }
  125. }
  126. pub type Result<T> = std::result::Result<T, Error>;
  127. /// Adds a function to a model, that returns the first
  128. /// matching row for a given list of fields.
  129. ///
  130. /// Usage:
  131. ///
  132. /// ```rust
  133. /// impl Model {
  134. /// find_by!(model_table, name_of_the_function, field1 as String, field2 as i32);
  135. /// }
  136. ///
  137. /// // Get the Model with field1 == "", and field2 == 0
  138. /// Model::name_of_the_function(connection, String::new(), 0);
  139. /// ```
  140. macro_rules! find_by {
  141. ($table:ident, $fn:ident, $($col:ident as $type:ty),+) => {
  142. /// Try to find a $table with a given $col
  143. pub fn $fn(conn: &crate::Connection, $($col: $type),+) -> Result<Self> {
  144. $table::table
  145. $(.filter($table::$col.eq($col)))+
  146. .first(conn)
  147. .map_err(Error::from)
  148. }
  149. };
  150. }
  151. /// List all rows of a model, with field-based filtering.
  152. ///
  153. /// Usage:
  154. ///
  155. /// ```rust
  156. /// impl Model {
  157. /// list_by!(model_table, name_of_the_function, field1 as String);
  158. /// }
  159. ///
  160. /// // To get all Models with field1 == ""
  161. /// Model::name_of_the_function(connection, String::new());
  162. /// ```
  163. macro_rules! list_by {
  164. ($table:ident, $fn:ident, $($col:ident as $type:ty),+) => {
  165. /// Try to find a $table with a given $col
  166. pub fn $fn(conn: &crate::Connection, $($col: $type),+) -> Result<Vec<Self>> {
  167. $table::table
  168. $(.filter($table::$col.eq($col)))+
  169. .load::<Self>(conn)
  170. .map_err(Error::from)
  171. }
  172. };
  173. }
  174. /// Adds a function to a model to retrieve a row by ID
  175. ///
  176. /// # Usage
  177. ///
  178. /// ```rust
  179. /// impl Model {
  180. /// get!(model_table);
  181. /// }
  182. ///
  183. /// // Get the Model with ID 1
  184. /// Model::get(connection, 1);
  185. /// ```
  186. macro_rules! get {
  187. ($table:ident) => {
  188. pub fn get(conn: &crate::Connection, id: i32) -> Result<Self> {
  189. $table::table
  190. .filter($table::id.eq(id))
  191. .first(conn)
  192. .map_err(Error::from)
  193. }
  194. };
  195. }
  196. /// Adds a function to a model to insert a new row
  197. ///
  198. /// # Usage
  199. ///
  200. /// ```rust
  201. /// impl Model {
  202. /// insert!(model_table, NewModelType);
  203. /// }
  204. ///
  205. /// // Insert a new row
  206. /// Model::insert(connection, NewModelType::new());
  207. /// ```
  208. macro_rules! insert {
  209. ($table:ident, $from:ty) => {
  210. insert!($table, $from, |x, _conn| Ok(x));
  211. };
  212. ($table:ident, $from:ty, |$val:ident, $conn:ident | $( $after:tt )+) => {
  213. last!($table);
  214. #[allow(dead_code)]
  215. pub fn insert(conn: &crate::Connection, new: $from) -> Result<Self> {
  216. diesel::insert_into($table::table)
  217. .values(new)
  218. .execute(conn)?;
  219. #[allow(unused_mut)]
  220. let mut $val = Self::last(conn)?;
  221. let $conn = conn;
  222. $( $after )+
  223. }
  224. };
  225. }
  226. /// Returns the last row of a table.
  227. ///
  228. /// # Usage
  229. ///
  230. /// ```rust
  231. /// impl Model {
  232. /// last!(model_table);
  233. /// }
  234. ///
  235. /// // Get the last Model
  236. /// Model::last(connection)
  237. /// ```
  238. macro_rules! last {
  239. ($table:ident) => {
  240. #[allow(dead_code)]
  241. pub fn last(conn: &crate::Connection) -> Result<Self> {
  242. $table::table
  243. .order_by($table::id.desc())
  244. .first(conn)
  245. .map_err(Error::from)
  246. }
  247. };
  248. }
  249. mod config;
  250. pub use config::CONFIG;
  251. pub fn ap_url(url: &str) -> String {
  252. format!("https://{}", url)
  253. }
  254. #[cfg(test)]
  255. #[macro_use]
  256. mod tests {
  257. use crate::{db_conn, migrations::IMPORTED_MIGRATIONS, search, Connection as Conn, CONFIG};
  258. use diesel::r2d2::ConnectionManager;
  259. use plume_common::utils::random_hex;
  260. use scheduled_thread_pool::ScheduledThreadPool;
  261. use std::env::temp_dir;
  262. use std::sync::Arc;
  263. #[macro_export]
  264. macro_rules! part_eq {
  265. ( $x:expr, $y:expr, [$( $var:ident ),*] ) => {
  266. {
  267. $(
  268. assert_eq!($x.$var, $y.$var);
  269. )*
  270. }
  271. };
  272. }
  273. pub fn db<'a>() -> db_conn::DbConn {
  274. db_conn::DbConn((*DB_POOL).get().unwrap())
  275. }
  276. pub fn pool<'a>() -> db_conn::DbPool {
  277. (*DB_POOL).clone()
  278. }
  279. lazy_static! {
  280. static ref DB_POOL: db_conn::DbPool = {
  281. let pool = db_conn::DbPool::builder()
  282. .connection_customizer(Box::new(db_conn::tests::TestConnectionCustomizer))
  283. .build(ConnectionManager::<Conn>::new(CONFIG.database_url.as_str()))
  284. .unwrap();
  285. let dir = temp_dir().join(format!("plume-test-{}", random_hex()));
  286. IMPORTED_MIGRATIONS
  287. .run_pending_migrations(&pool.get().unwrap(), &dir)
  288. .expect("Migrations error");
  289. pool
  290. };
  291. }
  292. pub fn rockets() -> super::PlumeRocket {
  293. super::PlumeRocket {
  294. conn: db_conn::DbConn((*DB_POOL).get().unwrap()),
  295. searcher: Arc::new(search::tests::get_searcher(&CONFIG.search_tokenizers)),
  296. worker: Arc::new(ScheduledThreadPool::new(2)),
  297. user: None,
  298. actors: Arc::new(
  299. riker::actors::ActorSystem::new().expect("Couldn't create test actor system"),
  300. ),
  301. }
  302. }
  303. }
  304. pub mod admin;
  305. pub mod api_tokens;
  306. pub mod apps;
  307. pub mod blocklisted_emails;
  308. pub mod blog_authors;
  309. pub mod blogs;
  310. pub mod comment_seers;
  311. pub mod comments;
  312. pub mod db_conn;
  313. pub mod follows;
  314. pub mod headers;
  315. pub mod inbox;
  316. pub mod instance;
  317. pub mod likes;
  318. pub mod lists;
  319. pub mod medias;
  320. pub mod mentions;
  321. pub mod migrations;
  322. pub mod notifications;
  323. pub mod password_reset_requests;
  324. pub mod plume_rocket;
  325. pub mod post_authors;
  326. pub mod posts;
  327. pub mod reshares;
  328. pub mod safe_string;
  329. #[allow(unused_imports)]
  330. pub mod schema;
  331. pub mod search;
  332. pub mod tags;
  333. pub mod timeline;
  334. pub mod users;
  335. pub use plume_rocket::PlumeRocket;