mirror of
https://github.com/Nutomic/ibis.git
synced 2024-11-24 13:41:08 +00:00
Use db pool
This commit is contained in:
parent
dd56a3f0d5
commit
d22654261f
31 changed files with 211 additions and 182 deletions
21
Cargo.lock
generated
21
Cargo.lock
generated
|
@ -906,6 +906,7 @@ dependencies = [
|
||||||
"diesel_derives",
|
"diesel_derives",
|
||||||
"itoa",
|
"itoa",
|
||||||
"pq-sys",
|
"pq-sys",
|
||||||
|
"r2d2",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2750,6 +2751,17 @@ dependencies = [
|
||||||
"syn 2.0.51",
|
"syn 2.0.51",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r2d2"
|
||||||
|
version = "0.8.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"parking_lot",
|
||||||
|
"scheduled-thread-pool",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
|
@ -3033,6 +3045,15 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scheduled-thread-pool"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19"
|
||||||
|
dependencies = [
|
||||||
|
"parking_lot",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
|
|
@ -44,6 +44,7 @@ diesel = { version = "2.1.4", features = [
|
||||||
"postgres",
|
"postgres",
|
||||||
"chrono",
|
"chrono",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
"r2d2"
|
||||||
], optional = true }
|
], optional = true }
|
||||||
diesel-derive-newtype = { version = "2.1.0", optional = true }
|
diesel-derive-newtype = { version = "2.1.0", optional = true }
|
||||||
diesel_migrations = { version = "2.1.0", optional = true }
|
diesel_migrations = { version = "2.1.0", optional = true }
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
# Address where ibis should listen for incoming requests
|
# Address where ibis should listen for incoming requests
|
||||||
bind = "127.0.0.1:8081"
|
bind = "127.0.0.1:8081"
|
||||||
|
|
||||||
# Database connection url
|
|
||||||
database_url = "postgres://ibis:password@localhost:5432/ibis"
|
|
||||||
|
|
||||||
# Whether users can create new accounts
|
# Whether users can create new accounts
|
||||||
registration_open = true
|
registration_open = true
|
||||||
|
|
||||||
|
# Details about the PostgreSQL database connection
|
||||||
|
[database]
|
||||||
|
# Database connection url
|
||||||
|
connection_url = "postgres://ibis:password@localhost:5432/ibis"
|
||||||
|
|
||||||
|
# Database connection pool size
|
||||||
|
pool_size = 5
|
||||||
|
|
||||||
# Details of the initial admin account
|
# Details of the initial admin account
|
||||||
[setup]
|
[setup]
|
||||||
admin_username = "ibis"
|
admin_username = "ibis"
|
||||||
|
|
|
@ -36,7 +36,7 @@ pub(in crate::backend::api) async fn create_article(
|
||||||
return Err(anyhow!("Title must not be empty").into());
|
return Err(anyhow!("Title must not be empty").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let local_instance = DbInstance::read_local_instance(&data)?;
|
||||||
let ap_id = ObjectId::parse(&format!(
|
let ap_id = ObjectId::parse(&format!(
|
||||||
"{}://{}:{}/article/{}",
|
"{}://{}:{}/article/{}",
|
||||||
http_protocol_str(),
|
http_protocol_str(),
|
||||||
|
@ -51,18 +51,18 @@ pub(in crate::backend::api) async fn create_article(
|
||||||
instance_id: local_instance.id,
|
instance_id: local_instance.id,
|
||||||
local: true,
|
local: true,
|
||||||
};
|
};
|
||||||
let article = DbArticle::create(form, &data.db_connection)?;
|
let article = DbArticle::create(form, &data)?;
|
||||||
|
|
||||||
let edit_data = EditArticleData {
|
let edit_data = EditArticleData {
|
||||||
article_id: article.id,
|
article_id: article.id,
|
||||||
new_text: create_article.text,
|
new_text: create_article.text,
|
||||||
summary: create_article.summary,
|
summary: create_article.summary,
|
||||||
previous_version_id: article.latest_edit_version(&data.db_connection)?,
|
previous_version_id: article.latest_edit_version(&data)?,
|
||||||
resolve_conflict_id: None,
|
resolve_conflict_id: None,
|
||||||
};
|
};
|
||||||
let _ = edit_article(Extension(user), data.reset_request_count(), Form(edit_data)).await?;
|
let _ = edit_article(Extension(user), data.reset_request_count(), Form(edit_data)).await?;
|
||||||
|
|
||||||
let article_view = DbArticle::read_view(article.id, &data.db_connection)?;
|
let article_view = DbArticle::read_view(article.id, &data)?;
|
||||||
CreateArticle::send_to_followers(article_view.article.clone(), &data).await?;
|
CreateArticle::send_to_followers(article_view.article.clone(), &data).await?;
|
||||||
|
|
||||||
Ok(Json(article_view))
|
Ok(Json(article_view))
|
||||||
|
@ -85,9 +85,9 @@ pub(in crate::backend::api) async fn edit_article(
|
||||||
) -> MyResult<Json<Option<ApiConflict>>> {
|
) -> MyResult<Json<Option<ApiConflict>>> {
|
||||||
// resolve conflict if any
|
// resolve conflict if any
|
||||||
if let Some(resolve_conflict_id) = edit_form.resolve_conflict_id {
|
if let Some(resolve_conflict_id) = edit_form.resolve_conflict_id {
|
||||||
DbConflict::delete(resolve_conflict_id, &data.db_connection)?;
|
DbConflict::delete(resolve_conflict_id, &data)?;
|
||||||
}
|
}
|
||||||
let original_article = DbArticle::read_view(edit_form.article_id, &data.db_connection)?;
|
let original_article = DbArticle::read_view(edit_form.article_id, &data)?;
|
||||||
if edit_form.new_text == original_article.article.text {
|
if edit_form.new_text == original_article.article.text {
|
||||||
return Err(anyhow!("Edit contains no changes").into());
|
return Err(anyhow!("Edit contains no changes").into());
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ pub(in crate::backend::api) async fn edit_article(
|
||||||
generate_article_version(&original_article.edits, &edit_form.previous_version_id)?;
|
generate_article_version(&original_article.edits, &edit_form.previous_version_id)?;
|
||||||
let patch = create_patch(&ancestor, &edit_form.new_text);
|
let patch = create_patch(&ancestor, &edit_form.new_text);
|
||||||
|
|
||||||
let previous_version = DbEdit::read(&edit_form.previous_version_id, &data.db_connection)?;
|
let previous_version = DbEdit::read(&edit_form.previous_version_id, &data)?;
|
||||||
let form = DbConflictForm {
|
let form = DbConflictForm {
|
||||||
hash: EditVersion::new(&patch.to_string()),
|
hash: EditVersion::new(&patch.to_string()),
|
||||||
diff: patch.to_string(),
|
diff: patch.to_string(),
|
||||||
|
@ -128,7 +128,7 @@ pub(in crate::backend::api) async fn edit_article(
|
||||||
article_id: original_article.article.id,
|
article_id: original_article.article.id,
|
||||||
previous_version_id: previous_version.hash,
|
previous_version_id: previous_version.hash,
|
||||||
};
|
};
|
||||||
let conflict = DbConflict::create(&form, &data.db_connection)?;
|
let conflict = DbConflict::create(&form, &data)?;
|
||||||
Ok(Json(conflict.to_api_conflict(&data).await?))
|
Ok(Json(conflict.to_api_conflict(&data).await?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,13 +143,13 @@ pub(in crate::backend::api) async fn get_article(
|
||||||
(Some(title), None) => Ok(Json(DbArticle::read_view_title(
|
(Some(title), None) => Ok(Json(DbArticle::read_view_title(
|
||||||
&title,
|
&title,
|
||||||
query.domain,
|
query.domain,
|
||||||
&data.db_connection,
|
&data,
|
||||||
)?)),
|
)?)),
|
||||||
(None, Some(id)) => {
|
(None, Some(id)) => {
|
||||||
if query.domain.is_some() {
|
if query.domain.is_some() {
|
||||||
return Err(anyhow!("Cant combine id and instance_domain").into());
|
return Err(anyhow!("Cant combine id and instance_domain").into());
|
||||||
}
|
}
|
||||||
Ok(Json(DbArticle::read_view(id, &data.db_connection)?))
|
Ok(Json(DbArticle::read_view(id, &data)?))
|
||||||
}
|
}
|
||||||
_ => Err(anyhow!("Must pass exactly one of title, id").into()),
|
_ => Err(anyhow!("Must pass exactly one of title, id").into()),
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ pub(in crate::backend::api) async fn list_articles(
|
||||||
data: Data<IbisData>,
|
data: Data<IbisData>,
|
||||||
) -> MyResult<Json<Vec<DbArticle>>> {
|
) -> MyResult<Json<Vec<DbArticle>>> {
|
||||||
let only_local = query.only_local.unwrap_or(false);
|
let only_local = query.only_local.unwrap_or(false);
|
||||||
Ok(Json(DbArticle::read_all(only_local, &data.db_connection)?))
|
Ok(Json(DbArticle::read_all(only_local, &data)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fork a remote article to local instance. This is useful if there are disagreements about
|
/// Fork a remote article to local instance. This is useful if there are disagreements about
|
||||||
|
@ -173,9 +173,9 @@ pub(in crate::backend::api) async fn fork_article(
|
||||||
Form(fork_form): Form<ForkArticleData>,
|
Form(fork_form): Form<ForkArticleData>,
|
||||||
) -> MyResult<Json<ArticleView>> {
|
) -> MyResult<Json<ArticleView>> {
|
||||||
// TODO: lots of code duplicated from create_article(), can move it into helper
|
// TODO: lots of code duplicated from create_article(), can move it into helper
|
||||||
let original_article = DbArticle::read(fork_form.article_id, &data.db_connection)?;
|
let original_article = DbArticle::read(fork_form.article_id, &data)?;
|
||||||
|
|
||||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let local_instance = DbInstance::read_local_instance(&data)?;
|
||||||
let ap_id = ObjectId::parse(&format!(
|
let ap_id = ObjectId::parse(&format!(
|
||||||
"{}://{}:{}/article/{}",
|
"{}://{}:{}/article/{}",
|
||||||
http_protocol_str(),
|
http_protocol_str(),
|
||||||
|
@ -190,11 +190,11 @@ pub(in crate::backend::api) async fn fork_article(
|
||||||
instance_id: local_instance.id,
|
instance_id: local_instance.id,
|
||||||
local: true,
|
local: true,
|
||||||
};
|
};
|
||||||
let article = DbArticle::create(form, &data.db_connection)?;
|
let article = DbArticle::create(form, &data)?;
|
||||||
|
|
||||||
// copy edits to new article
|
// copy edits to new article
|
||||||
// this could also be done in sql
|
// this could also be done in sql
|
||||||
let edits = DbEdit::read_for_article(&original_article, &data.db_connection)?
|
let edits = DbEdit::read_for_article(&original_article, &data)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|e| e.edit)
|
.map(|e| e.edit)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -210,12 +210,12 @@ pub(in crate::backend::api) async fn fork_article(
|
||||||
previous_version_id: e.previous_version_id,
|
previous_version_id: e.previous_version_id,
|
||||||
created: Utc::now(),
|
created: Utc::now(),
|
||||||
};
|
};
|
||||||
DbEdit::create(&form, &data.db_connection)?;
|
DbEdit::create(&form, &data)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateArticle::send_to_followers(article.clone(), &data).await?;
|
CreateArticle::send_to_followers(article.clone(), &data).await?;
|
||||||
|
|
||||||
Ok(Json(DbArticle::read_view(article.id, &data.db_connection)?))
|
Ok(Json(DbArticle::read_view(article.id, &data)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch a remote article, including edits collection. Allows viewing and editing. Note that new
|
/// Fetch a remote article, including edits collection. Allows viewing and editing. Note that new
|
||||||
|
@ -226,7 +226,7 @@ pub(super) async fn resolve_article(
|
||||||
data: Data<IbisData>,
|
data: Data<IbisData>,
|
||||||
) -> MyResult<Json<ArticleView>> {
|
) -> MyResult<Json<ArticleView>> {
|
||||||
let article: DbArticle = ObjectId::from(query.id).dereference(&data).await?;
|
let article: DbArticle = ObjectId::from(query.id).dereference(&data).await?;
|
||||||
let edits = DbEdit::read_for_article(&article, &data.db_connection)?;
|
let edits = DbEdit::read_for_article(&article, &data)?;
|
||||||
let latest_version = edits.last().unwrap().edit.hash.clone();
|
let latest_version = edits.last().unwrap().edit.hash.clone();
|
||||||
Ok(Json(ArticleView {
|
Ok(Json(ArticleView {
|
||||||
article,
|
article,
|
||||||
|
@ -244,6 +244,6 @@ pub(super) async fn search_article(
|
||||||
if query.query.is_empty() {
|
if query.query.is_empty() {
|
||||||
return Err(anyhow!("Query is empty").into());
|
return Err(anyhow!("Query is empty").into());
|
||||||
}
|
}
|
||||||
let article = DbArticle::search(&query.query, &data.db_connection)?;
|
let article = DbArticle::search(&query.query, &data)?;
|
||||||
Ok(Json(article))
|
Ok(Json(article))
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,10 +27,10 @@ pub(in crate::backend::api) async fn follow_instance(
|
||||||
data: Data<IbisData>,
|
data: Data<IbisData>,
|
||||||
Form(query): Form<FollowInstance>,
|
Form(query): Form<FollowInstance>,
|
||||||
) -> MyResult<()> {
|
) -> MyResult<()> {
|
||||||
let target = DbInstance::read(query.id, &data.db_connection)?;
|
let target = DbInstance::read(query.id, &data)?;
|
||||||
let pending = !target.local;
|
let pending = !target.local;
|
||||||
DbInstance::follow(&user.person, &target, pending, &data)?;
|
DbInstance::follow(&user.person, &target, pending, &data)?;
|
||||||
let instance = DbInstance::read(query.id, &data.db_connection)?;
|
let instance = DbInstance::read(query.id, &data)?;
|
||||||
Follow::send(user.person, &instance, &data).await?;
|
Follow::send(user.person, &instance, &data).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ async fn edit_conflicts(
|
||||||
Extension(user): Extension<LocalUserView>,
|
Extension(user): Extension<LocalUserView>,
|
||||||
data: Data<IbisData>,
|
data: Data<IbisData>,
|
||||||
) -> MyResult<Json<Vec<ApiConflict>>> {
|
) -> MyResult<Json<Vec<ApiConflict>>> {
|
||||||
let conflicts = DbConflict::list(&user.local_user, &data.db_connection)?;
|
let conflicts = DbConflict::list(&user.local_user, &data)?;
|
||||||
let conflicts: Vec<ApiConflict> = try_join_all(conflicts.into_iter().map(|c| {
|
let conflicts: Vec<ApiConflict> = try_join_all(conflicts.into_iter().map(|c| {
|
||||||
let data = data.reset_request_count();
|
let data = data.reset_request_count();
|
||||||
async move { c.to_api_conflict(&data).await }
|
async move { c.to_api_conflict(&data).await }
|
||||||
|
|
|
@ -3,18 +3,17 @@ use doku::Document;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use smart_default::SmartDefault;
|
use smart_default::SmartDefault;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use crate::backend::error::MyResult;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Eq, Clone, Document, SmartDefault)]
|
#[derive(Debug, Deserialize, PartialEq, Eq, Clone, Document, SmartDefault)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct IbisConfig {
|
pub struct IbisConfig {
|
||||||
/// Address where ibis should listen for incoming requests
|
/// Address where ibis should listen for incoming requests
|
||||||
#[default("127.0.0.1:8081".parse().unwrap())]
|
#[default("127.0.0.1:8081".parse().expect("parse config bind"))]
|
||||||
#[doku(as = "String", example = "127.0.0.1:8081")]
|
#[doku(as = "String", example = "127.0.0.1:8081")]
|
||||||
pub bind: SocketAddr,
|
pub bind: SocketAddr,
|
||||||
/// Database connection url
|
/// Details about the PostgreSQL database connection
|
||||||
#[default("postgres://ibis:password@localhost:5432/ibis")]
|
pub database: IbisConfigDatabase,
|
||||||
#[doku(example = "postgres://ibis:password@localhost:5432/ibis")]
|
|
||||||
pub database_url: String,
|
|
||||||
/// Whether users can create new accounts
|
/// Whether users can create new accounts
|
||||||
#[default = true]
|
#[default = true]
|
||||||
#[doku(example = "true")]
|
#[doku(example = "true")]
|
||||||
|
@ -25,18 +24,30 @@ pub struct IbisConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IbisConfig {
|
impl IbisConfig {
|
||||||
pub fn read() -> Self {
|
pub fn read() -> MyResult<Self> {
|
||||||
let config = Config::builder()
|
let config = Config::builder()
|
||||||
.add_source(config::File::with_name("config/config.toml"))
|
.add_source(config::File::with_name("config/config.toml"))
|
||||||
// Cant use _ as separator due to https://github.com/mehcode/config-rs/issues/391
|
// Cant use _ as separator due to https://github.com/mehcode/config-rs/issues/391
|
||||||
.add_source(config::Environment::with_prefix("IBIS").separator("__"))
|
.add_source(config::Environment::with_prefix("IBIS").separator("__"))
|
||||||
.build()
|
.build()?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
config.try_deserialize().unwrap()
|
Ok(config.try_deserialize()?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, PartialEq, Eq, Clone, Document, SmartDefault)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct IbisConfigDatabase {
|
||||||
|
/// Database connection url
|
||||||
|
#[default("postgres://ibis:password@localhost:5432/ibis")]
|
||||||
|
#[doku(example = "postgres://ibis:password@localhost:5432/ibis")]
|
||||||
|
pub connection_url: String,
|
||||||
|
/// Database connection pool size
|
||||||
|
#[default(5)]
|
||||||
|
#[doku(example = "5")]
|
||||||
|
pub pool_size: u32,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Eq, Clone, Document, SmartDefault)]
|
#[derive(Debug, Deserialize, PartialEq, Eq, Clone, Document, SmartDefault)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct IbisConfigSetup {
|
pub struct IbisConfigSetup {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::backend::database::schema::{article, edit, instance};
|
use crate::backend::database::schema::{article, edit, instance};
|
||||||
|
use crate::backend::database::IbisData;
|
||||||
use crate::backend::error::MyResult;
|
use crate::backend::error::MyResult;
|
||||||
use crate::backend::federation::objects::edits_collection::DbEditCollection;
|
use crate::backend::federation::objects::edits_collection::DbEditCollection;
|
||||||
use crate::common::DbEdit;
|
use crate::common::DbEdit;
|
||||||
|
@ -7,14 +8,14 @@ use crate::common::{ArticleView, DbArticle};
|
||||||
use activitypub_federation::fetch::collection_id::CollectionId;
|
use activitypub_federation::fetch::collection_id::CollectionId;
|
||||||
use activitypub_federation::fetch::object_id::ObjectId;
|
use activitypub_federation::fetch::object_id::ObjectId;
|
||||||
use diesel::dsl::max;
|
use diesel::dsl::max;
|
||||||
use diesel::pg::PgConnection;
|
|
||||||
use diesel::ExpressionMethods;
|
use diesel::ExpressionMethods;
|
||||||
use diesel::{
|
use diesel::{
|
||||||
insert_into, AsChangeset, BoolExpressionMethods, Insertable, PgTextExpressionMethods, QueryDsl,
|
insert_into, AsChangeset, BoolExpressionMethods, Insertable, PgTextExpressionMethods, QueryDsl,
|
||||||
RunQueryDsl,
|
RunQueryDsl,
|
||||||
};
|
};
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Insertable, AsChangeset)]
|
#[derive(Debug, Clone, Insertable, AsChangeset)]
|
||||||
#[diesel(table_name = article, check_for_backend(diesel::pg::Pg))]
|
#[diesel(table_name = article, check_for_backend(diesel::pg::Pg))]
|
||||||
|
@ -32,17 +33,17 @@ impl DbArticle {
|
||||||
Ok(CollectionId::parse(&format!("{}/edits", self.ap_id))?)
|
Ok(CollectionId::parse(&format!("{}/edits", self.ap_id))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(mut form: DbArticleForm, conn: &Mutex<PgConnection>) -> MyResult<Self> {
|
pub fn create(mut form: DbArticleForm, data: &IbisData) -> MyResult<Self> {
|
||||||
form.title = form.title.replace(' ', "_");
|
form.title = form.title.replace(' ', "_");
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(insert_into(article::table)
|
Ok(insert_into(article::table)
|
||||||
.values(form)
|
.values(form)
|
||||||
.get_result(conn.deref_mut())?)
|
.get_result(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_or_update(mut form: DbArticleForm, conn: &Mutex<PgConnection>) -> MyResult<Self> {
|
pub fn create_or_update(mut form: DbArticleForm, data: &IbisData) -> MyResult<Self> {
|
||||||
form.title = form.title.replace(' ', "_");
|
form.title = form.title.replace(' ', "_");
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(insert_into(article::table)
|
Ok(insert_into(article::table)
|
||||||
.values(&form)
|
.values(&form)
|
||||||
.on_conflict(article::dsl::ap_id)
|
.on_conflict(article::dsl::ap_id)
|
||||||
|
@ -51,25 +52,23 @@ impl DbArticle {
|
||||||
.get_result(conn.deref_mut())?)
|
.get_result(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_text(id: i32, text: &str, conn: &Mutex<PgConnection>) -> MyResult<Self> {
|
pub fn update_text(id: i32, text: &str, data: &IbisData) -> MyResult<Self> {
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(diesel::update(article::dsl::article.find(id))
|
Ok(diesel::update(article::dsl::article.find(id))
|
||||||
.set(article::dsl::text.eq(text))
|
.set(article::dsl::text.eq(text))
|
||||||
.get_result::<Self>(conn.deref_mut())?)
|
.get_result::<Self>(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(id: i32, conn: &Mutex<PgConnection>) -> MyResult<Self> {
|
pub fn read(id: i32, data: &IbisData) -> MyResult<Self> {
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(article::table.find(id).get_result(conn.deref_mut())?)
|
Ok(article::table.find(id).get_result(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_view(id: i32, conn: &Mutex<PgConnection>) -> MyResult<ArticleView> {
|
pub fn read_view(id: i32, data: &IbisData) -> MyResult<ArticleView> {
|
||||||
let article: DbArticle = {
|
let mut conn = data.db_pool.get()?;
|
||||||
let mut conn = conn.lock().unwrap();
|
let article: DbArticle = { article::table.find(id).get_result(conn.deref_mut())? };
|
||||||
article::table.find(id).get_result(conn.deref_mut())?
|
let latest_version = article.latest_edit_version(data)?;
|
||||||
};
|
let edits = DbEdit::read_for_article(&article, data)?;
|
||||||
let latest_version = article.latest_edit_version(conn)?;
|
|
||||||
let edits = DbEdit::read_for_article(&article, conn)?;
|
|
||||||
Ok(ArticleView {
|
Ok(ArticleView {
|
||||||
article,
|
article,
|
||||||
edits,
|
edits,
|
||||||
|
@ -80,10 +79,10 @@ impl DbArticle {
|
||||||
pub fn read_view_title(
|
pub fn read_view_title(
|
||||||
title: &str,
|
title: &str,
|
||||||
domain: Option<String>,
|
domain: Option<String>,
|
||||||
conn: &Mutex<PgConnection>,
|
data: &IbisData,
|
||||||
) -> MyResult<ArticleView> {
|
) -> MyResult<ArticleView> {
|
||||||
|
let mut conn = data.db_pool.get()?;
|
||||||
let article: DbArticle = {
|
let article: DbArticle = {
|
||||||
let mut conn = conn.lock().unwrap();
|
|
||||||
let query = article::table
|
let query = article::table
|
||||||
.inner_join(instance::table)
|
.inner_join(instance::table)
|
||||||
.filter(article::dsl::title.eq(title))
|
.filter(article::dsl::title.eq(title))
|
||||||
|
@ -99,8 +98,8 @@ impl DbArticle {
|
||||||
.select(article::all_columns)
|
.select(article::all_columns)
|
||||||
.get_result(conn.deref_mut())?
|
.get_result(conn.deref_mut())?
|
||||||
};
|
};
|
||||||
let latest_version = article.latest_edit_version(conn)?;
|
let latest_version = article.latest_edit_version(data)?;
|
||||||
let edits = DbEdit::read_for_article(&article, conn)?;
|
let edits = DbEdit::read_for_article(&article, data)?;
|
||||||
Ok(ArticleView {
|
Ok(ArticleView {
|
||||||
article,
|
article,
|
||||||
edits,
|
edits,
|
||||||
|
@ -108,18 +107,15 @@ impl DbArticle {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_from_ap_id(
|
pub fn read_from_ap_id(ap_id: &ObjectId<DbArticle>, data: &IbisData) -> MyResult<Self> {
|
||||||
ap_id: &ObjectId<DbArticle>,
|
let mut conn = data.db_pool.get()?;
|
||||||
conn: &Mutex<PgConnection>,
|
|
||||||
) -> MyResult<Self> {
|
|
||||||
let mut conn = conn.lock().unwrap();
|
|
||||||
Ok(article::table
|
Ok(article::table
|
||||||
.filter(article::dsl::ap_id.eq(ap_id))
|
.filter(article::dsl::ap_id.eq(ap_id))
|
||||||
.get_result(conn.deref_mut())?)
|
.get_result(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_local_title(title: &str, conn: &Mutex<PgConnection>) -> MyResult<Self> {
|
pub fn read_local_title(title: &str, data: &IbisData) -> MyResult<Self> {
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(article::table
|
Ok(article::table
|
||||||
.filter(article::dsl::title.eq(title))
|
.filter(article::dsl::title.eq(title))
|
||||||
.filter(article::dsl::local.eq(true))
|
.filter(article::dsl::local.eq(true))
|
||||||
|
@ -127,8 +123,8 @@ impl DbArticle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read all articles, ordered by most recently edited first.
|
/// Read all articles, ordered by most recently edited first.
|
||||||
pub fn read_all(only_local: bool, conn: &Mutex<PgConnection>) -> MyResult<Vec<Self>> {
|
pub fn read_all(only_local: bool, data: &IbisData) -> MyResult<Vec<Self>> {
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
let query = article::table
|
let query = article::table
|
||||||
.inner_join(edit::table)
|
.inner_join(edit::table)
|
||||||
.group_by(article::dsl::id)
|
.group_by(article::dsl::id)
|
||||||
|
@ -137,14 +133,14 @@ impl DbArticle {
|
||||||
Ok(if only_local {
|
Ok(if only_local {
|
||||||
query
|
query
|
||||||
.filter(article::dsl::local.eq(true))
|
.filter(article::dsl::local.eq(true))
|
||||||
.get_results(conn.deref_mut())?
|
.get_results(&mut conn)?
|
||||||
} else {
|
} else {
|
||||||
query.get_results(conn.deref_mut())?
|
query.get_results(&mut conn)?
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search(query: &str, conn: &Mutex<PgConnection>) -> MyResult<Vec<Self>> {
|
pub fn search(query: &str, data: &IbisData) -> MyResult<Vec<Self>> {
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
let replaced = query
|
let replaced = query
|
||||||
.replace('%', "\\%")
|
.replace('%', "\\%")
|
||||||
.replace('_', "\\_")
|
.replace('_', "\\_")
|
||||||
|
@ -159,8 +155,8 @@ impl DbArticle {
|
||||||
.get_results(conn.deref_mut())?)
|
.get_results(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn latest_edit_version(&self, conn: &Mutex<PgConnection>) -> MyResult<EditVersion> {
|
pub fn latest_edit_version(&self, data: &IbisData) -> MyResult<EditVersion> {
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
let latest_version: Option<EditVersion> = edit::table
|
let latest_version: Option<EditVersion> = edit::table
|
||||||
.filter(edit::dsl::article_id.eq(self.id))
|
.filter(edit::dsl::article_id.eq(self.id))
|
||||||
.order_by(edit::dsl::id.desc())
|
.order_by(edit::dsl::id.desc())
|
||||||
|
|
|
@ -10,13 +10,13 @@ use crate::common::{ApiConflict, DbArticle};
|
||||||
use activitypub_federation::config::Data;
|
use activitypub_federation::config::Data;
|
||||||
use diesel::ExpressionMethods;
|
use diesel::ExpressionMethods;
|
||||||
use diesel::{
|
use diesel::{
|
||||||
delete, insert_into, Identifiable, Insertable, PgConnection, QueryDsl, Queryable, RunQueryDsl,
|
delete, insert_into, Identifiable, Insertable, QueryDsl, Queryable, RunQueryDsl,
|
||||||
Selectable,
|
Selectable,
|
||||||
};
|
};
|
||||||
use diffy::{apply, merge, Patch};
|
use diffy::{apply, merge, Patch};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
/// A local only object which represents a merge conflict. It is created
|
/// A local only object which represents a merge conflict. It is created
|
||||||
/// when a local user edit conflicts with another concurrent edit.
|
/// when a local user edit conflicts with another concurrent edit.
|
||||||
|
@ -44,33 +44,33 @@ pub struct DbConflictForm {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DbConflict {
|
impl DbConflict {
|
||||||
pub fn create(form: &DbConflictForm, conn: &Mutex<PgConnection>) -> MyResult<Self> {
|
pub fn create(form: &DbConflictForm, data: &IbisData) -> MyResult<Self> {
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(insert_into(conflict::table)
|
Ok(insert_into(conflict::table)
|
||||||
.values(form)
|
.values(form)
|
||||||
.get_result(conn.deref_mut())?)
|
.get_result(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list(local_user: &DbLocalUser, conn: &Mutex<PgConnection>) -> MyResult<Vec<Self>> {
|
pub fn list(local_user: &DbLocalUser, data: &IbisData) -> MyResult<Vec<Self>> {
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(conflict::table
|
Ok(conflict::table
|
||||||
.filter(conflict::dsl::creator_id.eq(local_user.id))
|
.filter(conflict::dsl::creator_id.eq(local_user.id))
|
||||||
.get_results(conn.deref_mut())?)
|
.get_results(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete a merge conflict after it is resolved.
|
/// Delete a merge conflict after it is resolved.
|
||||||
pub fn delete(id: i32, conn: &Mutex<PgConnection>) -> MyResult<Self> {
|
pub fn delete(id: i32, data: &IbisData) -> MyResult<Self> {
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(delete(conflict::table.find(id)).get_result(conn.deref_mut())?)
|
Ok(delete(conflict::table.find(id)).get_result(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn to_api_conflict(&self, data: &Data<IbisData>) -> MyResult<Option<ApiConflict>> {
|
pub async fn to_api_conflict(&self, data: &Data<IbisData>) -> MyResult<Option<ApiConflict>> {
|
||||||
let article = DbArticle::read(self.article_id, &data.db_connection)?;
|
let article = DbArticle::read(self.article_id, data)?;
|
||||||
// Make sure to get latest version from origin so that all conflicts can be resolved
|
// Make sure to get latest version from origin so that all conflicts can be resolved
|
||||||
let original_article = article.ap_id.dereference_forced(data).await?;
|
let original_article = article.ap_id.dereference_forced(data).await?;
|
||||||
|
|
||||||
// create common ancestor version
|
// create common ancestor version
|
||||||
let edits = DbEdit::read_for_article(&original_article, &data.db_connection)?;
|
let edits = DbEdit::read_for_article(&original_article, data)?;
|
||||||
let ancestor = generate_article_version(&edits, &self.previous_version_id)?;
|
let ancestor = generate_article_version(&edits, &self.previous_version_id)?;
|
||||||
|
|
||||||
let patch = Patch::from_str(&self.diff)?;
|
let patch = Patch::from_str(&self.diff)?;
|
||||||
|
@ -89,7 +89,7 @@ impl DbConflict {
|
||||||
data,
|
data,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
DbConflict::delete(self.id, &data.db_connection)?;
|
DbConflict::delete(self.id, data)?;
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
Err(three_way_merge) => {
|
Err(three_way_merge) => {
|
||||||
|
@ -100,8 +100,7 @@ impl DbConflict {
|
||||||
three_way_merge,
|
three_way_merge,
|
||||||
summary: self.summary.clone(),
|
summary: self.summary.clone(),
|
||||||
article: original_article.clone(),
|
article: original_article.clone(),
|
||||||
previous_version_id: original_article
|
previous_version_id: original_article.latest_edit_version(data)?,
|
||||||
.latest_edit_version(&data.db_connection)?,
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
use crate::backend::database::schema::{edit, person};
|
use crate::backend::database::schema::{edit, person};
|
||||||
use crate::backend::error::MyResult;
|
use crate::backend::error::MyResult;
|
||||||
|
use crate::backend::IbisData;
|
||||||
use crate::common::{DbArticle, DbEdit};
|
use crate::common::{DbArticle, DbEdit};
|
||||||
use crate::common::{EditVersion, EditView};
|
use crate::common::{EditVersion, EditView};
|
||||||
use activitypub_federation::fetch::object_id::ObjectId;
|
use activitypub_federation::fetch::object_id::ObjectId;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use diesel::ExpressionMethods;
|
use diesel::ExpressionMethods;
|
||||||
use diesel::{insert_into, AsChangeset, Insertable, PgConnection, QueryDsl, RunQueryDsl};
|
use diesel::{insert_into, AsChangeset, Insertable, QueryDsl, RunQueryDsl};
|
||||||
use diffy::create_patch;
|
use diffy::create_patch;
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Insertable, AsChangeset)]
|
#[derive(Debug, Clone, Insertable, AsChangeset)]
|
||||||
#[diesel(table_name = edit, check_for_backend(diesel::pg::Pg))]
|
#[diesel(table_name = edit, check_for_backend(diesel::pg::Pg))]
|
||||||
|
@ -59,8 +60,8 @@ impl DbEditForm {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DbEdit {
|
impl DbEdit {
|
||||||
pub fn create(form: &DbEditForm, conn: &Mutex<PgConnection>) -> MyResult<Self> {
|
pub fn create(form: &DbEditForm, data: &IbisData) -> MyResult<Self> {
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(insert_into(edit::table)
|
Ok(insert_into(edit::table)
|
||||||
.values(form)
|
.values(form)
|
||||||
.on_conflict(edit::dsl::ap_id)
|
.on_conflict(edit::dsl::ap_id)
|
||||||
|
@ -69,26 +70,23 @@ impl DbEdit {
|
||||||
.get_result(conn.deref_mut())?)
|
.get_result(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(version: &EditVersion, conn: &Mutex<PgConnection>) -> MyResult<Self> {
|
pub fn read(version: &EditVersion, data: &IbisData) -> MyResult<Self> {
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(edit::table
|
Ok(edit::table
|
||||||
.filter(edit::dsl::hash.eq(version))
|
.filter(edit::dsl::hash.eq(version))
|
||||||
.get_result(conn.deref_mut())?)
|
.get_result(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_from_ap_id(ap_id: &ObjectId<DbEdit>, conn: &Mutex<PgConnection>) -> MyResult<Self> {
|
pub fn read_from_ap_id(ap_id: &ObjectId<DbEdit>, data: &IbisData) -> MyResult<Self> {
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(edit::table
|
Ok(edit::table
|
||||||
.filter(edit::dsl::ap_id.eq(ap_id))
|
.filter(edit::dsl::ap_id.eq(ap_id))
|
||||||
.get_result(conn.deref_mut())?)
|
.get_result(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: create internal variant which doesnt return person?
|
// TODO: create internal variant which doesnt return person?
|
||||||
pub fn read_for_article(
|
pub fn read_for_article(article: &DbArticle, data: &IbisData) -> MyResult<Vec<EditView>> {
|
||||||
article: &DbArticle,
|
let mut conn = data.db_pool.get()?;
|
||||||
conn: &Mutex<PgConnection>,
|
|
||||||
) -> MyResult<Vec<EditView>> {
|
|
||||||
let mut conn = conn.lock().unwrap();
|
|
||||||
Ok(edit::table
|
Ok(edit::table
|
||||||
.inner_join(person::table)
|
.inner_join(person::table)
|
||||||
.filter(edit::article_id.eq(article.id))
|
.filter(edit::article_id.eq(article.id))
|
||||||
|
|
|
@ -9,11 +9,11 @@ use activitypub_federation::fetch::object_id::ObjectId;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use diesel::ExpressionMethods;
|
use diesel::ExpressionMethods;
|
||||||
use diesel::{
|
use diesel::{
|
||||||
insert_into, AsChangeset, Insertable, JoinOnDsl, PgConnection, QueryDsl, RunQueryDsl,
|
insert_into, AsChangeset, Insertable, JoinOnDsl, QueryDsl, RunQueryDsl,
|
||||||
};
|
};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Insertable, AsChangeset)]
|
#[derive(Debug, Clone, Insertable, AsChangeset)]
|
||||||
#[diesel(table_name = instance, check_for_backend(diesel::pg::Pg))]
|
#[diesel(table_name = instance, check_for_backend(diesel::pg::Pg))]
|
||||||
|
@ -30,8 +30,8 @@ pub struct DbInstanceForm {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DbInstance {
|
impl DbInstance {
|
||||||
pub fn create(form: &DbInstanceForm, conn: &Mutex<PgConnection>) -> MyResult<Self> {
|
pub fn create(form: &DbInstanceForm, data: &IbisData) -> MyResult<Self> {
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(insert_into(instance::table)
|
Ok(insert_into(instance::table)
|
||||||
.values(form)
|
.values(form)
|
||||||
.on_conflict(instance::ap_id)
|
.on_conflict(instance::ap_id)
|
||||||
|
@ -40,8 +40,8 @@ impl DbInstance {
|
||||||
.get_result(conn.deref_mut())?)
|
.get_result(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(id: i32, conn: &Mutex<PgConnection>) -> MyResult<Self> {
|
pub fn read(id: i32, data: &IbisData) -> MyResult<Self> {
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(instance::table.find(id).get_result(conn.deref_mut())?)
|
Ok(instance::table.find(id).get_result(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,22 +49,22 @@ impl DbInstance {
|
||||||
ap_id: &ObjectId<DbInstance>,
|
ap_id: &ObjectId<DbInstance>,
|
||||||
data: &Data<IbisData>,
|
data: &Data<IbisData>,
|
||||||
) -> MyResult<DbInstance> {
|
) -> MyResult<DbInstance> {
|
||||||
let mut conn = data.db_connection.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(instance::table
|
Ok(instance::table
|
||||||
.filter(instance::ap_id.eq(ap_id))
|
.filter(instance::ap_id.eq(ap_id))
|
||||||
.get_result(conn.deref_mut())?)
|
.get_result(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_local_instance(conn: &Mutex<PgConnection>) -> MyResult<Self> {
|
pub fn read_local_instance(data: &IbisData) -> MyResult<Self> {
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(instance::table
|
Ok(instance::table
|
||||||
.filter(instance::local.eq(true))
|
.filter(instance::local.eq(true))
|
||||||
.get_result(conn.deref_mut())?)
|
.get_result(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_local_view(data: &Data<IbisData>) -> MyResult<InstanceView> {
|
pub fn read_local_view(data: &Data<IbisData>) -> MyResult<InstanceView> {
|
||||||
let instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let instance = DbInstance::read_local_instance(data)?;
|
||||||
let followers = DbInstance::read_followers(instance.id, &data.db_connection)?;
|
let followers = DbInstance::read_followers(instance.id, data)?;
|
||||||
|
|
||||||
Ok(InstanceView {
|
Ok(InstanceView {
|
||||||
instance,
|
instance,
|
||||||
|
@ -80,7 +80,7 @@ impl DbInstance {
|
||||||
data: &Data<IbisData>,
|
data: &Data<IbisData>,
|
||||||
) -> MyResult<()> {
|
) -> MyResult<()> {
|
||||||
use instance_follow::dsl::{follower_id, instance_id, pending};
|
use instance_follow::dsl::{follower_id, instance_id, pending};
|
||||||
let mut conn = data.db_connection.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
let form = (
|
let form = (
|
||||||
instance_id.eq(instance.id),
|
instance_id.eq(instance.id),
|
||||||
follower_id.eq(follower.id),
|
follower_id.eq(follower.id),
|
||||||
|
@ -96,10 +96,10 @@ impl DbInstance {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_followers(id_: i32, conn: &Mutex<PgConnection>) -> MyResult<Vec<DbPerson>> {
|
pub fn read_followers(id_: i32, data: &IbisData) -> MyResult<Vec<DbPerson>> {
|
||||||
use crate::backend::database::schema::person;
|
use crate::backend::database::schema::person;
|
||||||
use instance_follow::dsl::{follower_id, instance_id};
|
use instance_follow::dsl::{follower_id, instance_id};
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(instance_follow::table
|
Ok(instance_follow::table
|
||||||
.inner_join(person::table.on(follower_id.eq(person::id)))
|
.inner_join(person::table.on(follower_id.eq(person::id)))
|
||||||
.filter(instance_id.eq(id_))
|
.filter(instance_id.eq(id_))
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use crate::backend::config::IbisConfig;
|
use crate::backend::config::IbisConfig;
|
||||||
use crate::backend::database::schema::jwt_secret;
|
use crate::backend::database::schema::jwt_secret;
|
||||||
use crate::backend::error::MyResult;
|
use crate::backend::error::MyResult;
|
||||||
|
use diesel::r2d2::ConnectionManager;
|
||||||
|
use diesel::r2d2::Pool;
|
||||||
use diesel::PgConnection;
|
use diesel::PgConnection;
|
||||||
use diesel::{QueryDsl, RunQueryDsl};
|
use diesel::{QueryDsl, RunQueryDsl};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
@ -13,24 +15,15 @@ pub mod edit;
|
||||||
pub mod instance;
|
pub mod instance;
|
||||||
pub(crate) mod schema;
|
pub(crate) mod schema;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
pub mod version;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct IbisData {
|
pub struct IbisData {
|
||||||
pub db_connection: Arc<Mutex<PgConnection>>,
|
pub db_pool: Pool<ConnectionManager<PgConnection>>,
|
||||||
pub config: IbisConfig,
|
pub config: IbisConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for IbisData {
|
pub fn read_jwt_secret(data: &IbisData) -> MyResult<String> {
|
||||||
type Target = Arc<Mutex<PgConnection>>;
|
let mut conn = data.db_pool.get()?;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.db_connection
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_jwt_secret(conn: &Mutex<PgConnection>) -> MyResult<String> {
|
|
||||||
let mut conn = conn.lock().unwrap();
|
|
||||||
Ok(jwt_secret::table
|
Ok(jwt_secret::table
|
||||||
.select(jwt_secret::dsl::secret)
|
.select(jwt_secret::dsl::secret)
|
||||||
.first(conn.deref_mut())?)
|
.first(conn.deref_mut())?)
|
||||||
|
|
|
@ -10,11 +10,11 @@ use activitypub_federation::http_signatures::generate_actor_keypair;
|
||||||
use bcrypt::hash;
|
use bcrypt::hash;
|
||||||
use bcrypt::DEFAULT_COST;
|
use bcrypt::DEFAULT_COST;
|
||||||
use chrono::{DateTime, Local, Utc};
|
use chrono::{DateTime, Local, Utc};
|
||||||
use diesel::{insert_into, AsChangeset, Insertable, PgConnection, RunQueryDsl};
|
use diesel::{insert_into, AsChangeset, Insertable, RunQueryDsl};
|
||||||
use diesel::{ExpressionMethods, JoinOnDsl};
|
use diesel::{ExpressionMethods, JoinOnDsl};
|
||||||
use diesel::{PgTextExpressionMethods, QueryDsl};
|
use diesel::{PgTextExpressionMethods, QueryDsl};
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
use std::sync::{Mutex, MutexGuard};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Insertable, AsChangeset)]
|
#[derive(Debug, Clone, Insertable, AsChangeset)]
|
||||||
#[diesel(table_name = local_user, check_for_backend(diesel::pg::Pg))]
|
#[diesel(table_name = local_user, check_for_backend(diesel::pg::Pg))]
|
||||||
|
@ -37,8 +37,8 @@ pub struct DbPersonForm {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DbPerson {
|
impl DbPerson {
|
||||||
pub fn create(person_form: &DbPersonForm, conn: &Mutex<PgConnection>) -> MyResult<Self> {
|
pub fn create(person_form: &DbPersonForm, data: &Data<IbisData>) -> MyResult<Self> {
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(insert_into(person::table)
|
Ok(insert_into(person::table)
|
||||||
.values(person_form)
|
.values(person_form)
|
||||||
.on_conflict(person::dsl::ap_id)
|
.on_conflict(person::dsl::ap_id)
|
||||||
|
@ -48,7 +48,7 @@ impl DbPerson {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(id: i32, data: &Data<IbisData>) -> MyResult<DbPerson> {
|
pub fn read(id: i32, data: &Data<IbisData>) -> MyResult<DbPerson> {
|
||||||
let mut conn = data.db_connection.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(person::table.find(id).get_result(conn.deref_mut())?)
|
Ok(person::table.find(id).get_result(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ impl DbPerson {
|
||||||
admin: bool,
|
admin: bool,
|
||||||
data: &IbisData,
|
data: &IbisData,
|
||||||
) -> MyResult<LocalUserView> {
|
) -> MyResult<LocalUserView> {
|
||||||
let mut conn = data.db_connection.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
let domain = &data.config.federation.domain;
|
let domain = &data.config.federation.domain;
|
||||||
let ap_id = ObjectId::parse(&format!(
|
let ap_id = ObjectId::parse(&format!(
|
||||||
"{}://{domain}/user/{username}",
|
"{}://{domain}/user/{username}",
|
||||||
|
@ -101,7 +101,7 @@ impl DbPerson {
|
||||||
ap_id: &ObjectId<DbPerson>,
|
ap_id: &ObjectId<DbPerson>,
|
||||||
data: &Data<IbisData>,
|
data: &Data<IbisData>,
|
||||||
) -> MyResult<DbPerson> {
|
) -> MyResult<DbPerson> {
|
||||||
let mut conn = data.db_connection.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(person::table
|
Ok(person::table
|
||||||
.filter(person::dsl::ap_id.eq(ap_id))
|
.filter(person::dsl::ap_id.eq(ap_id))
|
||||||
.get_result(conn.deref_mut())?)
|
.get_result(conn.deref_mut())?)
|
||||||
|
@ -112,7 +112,7 @@ impl DbPerson {
|
||||||
domain: &Option<String>,
|
domain: &Option<String>,
|
||||||
data: &Data<IbisData>,
|
data: &Data<IbisData>,
|
||||||
) -> MyResult<DbPerson> {
|
) -> MyResult<DbPerson> {
|
||||||
let mut conn = data.db_connection.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
let mut query = person::table
|
let mut query = person::table
|
||||||
.filter(person::username.eq(username))
|
.filter(person::username.eq(username))
|
||||||
.select(person::all_columns)
|
.select(person::all_columns)
|
||||||
|
@ -129,14 +129,14 @@ impl DbPerson {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_local_from_name(username: &str, data: &Data<IbisData>) -> MyResult<LocalUserView> {
|
pub fn read_local_from_name(username: &str, data: &Data<IbisData>) -> MyResult<LocalUserView> {
|
||||||
let mut conn = data.db_connection.lock().unwrap();
|
let mut conn = data.db_pool.get()?;
|
||||||
let (person, local_user) = person::table
|
let (person, local_user) = person::table
|
||||||
.inner_join(local_user::table)
|
.inner_join(local_user::table)
|
||||||
.filter(person::dsl::local)
|
.filter(person::dsl::local)
|
||||||
.filter(person::dsl::username.eq(username))
|
.filter(person::dsl::username.eq(username))
|
||||||
.get_result::<(DbPerson, DbLocalUser)>(conn.deref_mut())?;
|
.get_result::<(DbPerson, DbLocalUser)>(conn.deref_mut())?;
|
||||||
// TODO: handle this in single query
|
// TODO: handle this in single query
|
||||||
let following = Self::read_following(person.id, conn)?;
|
let following = Self::read_following(person.id, data)?;
|
||||||
Ok(LocalUserView {
|
Ok(LocalUserView {
|
||||||
person,
|
person,
|
||||||
local_user,
|
local_user,
|
||||||
|
@ -144,8 +144,9 @@ impl DbPerson {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_following(id_: i32, mut conn: MutexGuard<PgConnection>) -> MyResult<Vec<DbInstance>> {
|
fn read_following(id_: i32, data: &Data<IbisData>) -> MyResult<Vec<DbInstance>> {
|
||||||
use instance_follow::dsl::{follower_id, instance_id};
|
use instance_follow::dsl::{follower_id, instance_id};
|
||||||
|
let mut conn = data.db_pool.get()?;
|
||||||
Ok(instance_follow::table
|
Ok(instance_follow::table
|
||||||
.inner_join(instance::table.on(instance_id.eq(instance::dsl::id)))
|
.inner_join(instance::table.on(instance_id.eq(instance::dsl::id)))
|
||||||
.filter(follower_id.eq(id_))
|
.filter(follower_id.eq(id_))
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
|
|
|
@ -28,7 +28,7 @@ pub struct CreateArticle {
|
||||||
|
|
||||||
impl CreateArticle {
|
impl CreateArticle {
|
||||||
pub async fn send_to_followers(article: DbArticle, data: &Data<IbisData>) -> MyResult<()> {
|
pub async fn send_to_followers(article: DbArticle, data: &Data<IbisData>) -> MyResult<()> {
|
||||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let local_instance = DbInstance::read_local_instance(data)?;
|
||||||
let object = article.clone().into_json(data).await?;
|
let object = article.clone().into_json(data).await?;
|
||||||
let id = generate_activity_id(&local_instance.ap_id)?;
|
let id = generate_activity_id(&local_instance.ap_id)?;
|
||||||
let to = local_instance.follower_ids(data)?;
|
let to = local_instance.follower_ids(data)?;
|
||||||
|
@ -65,7 +65,7 @@ impl ActivityHandler for CreateArticle {
|
||||||
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||||
let article = DbArticle::from_json(self.object.clone(), data).await?;
|
let article = DbArticle::from_json(self.object.clone(), data).await?;
|
||||||
if article.local {
|
if article.local {
|
||||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let local_instance = DbInstance::read_local_instance(data)?;
|
||||||
local_instance.send_to_followers(self, vec![], data).await?;
|
local_instance.send_to_followers(self, vec![], data).await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -58,7 +58,7 @@ impl ActivityHandler for Follow {
|
||||||
|
|
||||||
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||||
let actor = self.actor.dereference(data).await?;
|
let actor = self.actor.dereference(data).await?;
|
||||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let local_instance = DbInstance::read_local_instance(data)?;
|
||||||
verify_urls_match(self.object.inner(), local_instance.ap_id.inner())?;
|
verify_urls_match(self.object.inner(), local_instance.ap_id.inner())?;
|
||||||
DbInstance::follow(&actor, &local_instance, false, data)?;
|
DbInstance::follow(&actor, &local_instance, false, data)?;
|
||||||
|
|
||||||
|
|
|
@ -32,9 +32,8 @@ pub async fn submit_article_update(
|
||||||
previous_version,
|
previous_version,
|
||||||
)?;
|
)?;
|
||||||
if original_article.local {
|
if original_article.local {
|
||||||
let edit = DbEdit::create(&form, &data.db_connection)?;
|
let edit = DbEdit::create(&form, data)?;
|
||||||
let updated_article =
|
let updated_article = DbArticle::update_text(edit.article_id, &new_text, data)?;
|
||||||
DbArticle::update_text(edit.article_id, &new_text, &data.db_connection)?;
|
|
||||||
|
|
||||||
UpdateLocalArticle::send(updated_article, vec![], data).await?;
|
UpdateLocalArticle::send(updated_article, vec![], data).await?;
|
||||||
} else {
|
} else {
|
||||||
|
@ -50,7 +49,7 @@ pub async fn submit_article_update(
|
||||||
previous_version_id: form.previous_version_id,
|
previous_version_id: form.previous_version_id,
|
||||||
created: Utc::now(),
|
created: Utc::now(),
|
||||||
};
|
};
|
||||||
let instance = DbInstance::read(original_article.instance_id, &data.db_connection)?;
|
let instance = DbInstance::read(original_article.instance_id, data)?;
|
||||||
UpdateRemoteArticle::send(edit, instance, data).await?;
|
UpdateRemoteArticle::send(edit, instance, data).await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -33,7 +33,7 @@ impl RejectEdit {
|
||||||
user_instance: DbInstance,
|
user_instance: DbInstance,
|
||||||
data: &Data<IbisData>,
|
data: &Data<IbisData>,
|
||||||
) -> MyResult<()> {
|
) -> MyResult<()> {
|
||||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let local_instance = DbInstance::read_local_instance(data)?;
|
||||||
let id = generate_activity_id(&local_instance.ap_id)?;
|
let id = generate_activity_id(&local_instance.ap_id)?;
|
||||||
let reject = RejectEdit {
|
let reject = RejectEdit {
|
||||||
actor: local_instance.ap_id.clone(),
|
actor: local_instance.ap_id.clone(),
|
||||||
|
@ -82,7 +82,7 @@ impl ActivityHandler for RejectEdit {
|
||||||
article_id: article.id,
|
article_id: article.id,
|
||||||
previous_version_id: self.object.previous_version,
|
previous_version_id: self.object.previous_version,
|
||||||
};
|
};
|
||||||
DbConflict::create(&form, &data.db_connection)?;
|
DbConflict::create(&form, data)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ impl UpdateLocalArticle {
|
||||||
data: &Data<IbisData>,
|
data: &Data<IbisData>,
|
||||||
) -> MyResult<()> {
|
) -> MyResult<()> {
|
||||||
debug_assert!(article.local);
|
debug_assert!(article.local);
|
||||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let local_instance = DbInstance::read_local_instance(data)?;
|
||||||
let id = generate_activity_id(&local_instance.ap_id)?;
|
let id = generate_activity_id(&local_instance.ap_id)?;
|
||||||
let mut to = local_instance.follower_ids(data)?;
|
let mut to = local_instance.follower_ids(data)?;
|
||||||
to.extend(extra_recipients.iter().map(|i| i.ap_id.inner().clone()));
|
to.extend(extra_recipients.iter().map(|i| i.ap_id.inner().clone()));
|
||||||
|
|
|
@ -40,7 +40,7 @@ impl UpdateRemoteArticle {
|
||||||
article_instance: DbInstance,
|
article_instance: DbInstance,
|
||||||
data: &Data<IbisData>,
|
data: &Data<IbisData>,
|
||||||
) -> MyResult<()> {
|
) -> MyResult<()> {
|
||||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let local_instance = DbInstance::read_local_instance(data)?;
|
||||||
let id = generate_activity_id(&local_instance.ap_id)?;
|
let id = generate_activity_id(&local_instance.ap_id)?;
|
||||||
let update = UpdateRemoteArticle {
|
let update = UpdateRemoteArticle {
|
||||||
actor: local_instance.ap_id.clone(),
|
actor: local_instance.ap_id.clone(),
|
||||||
|
@ -74,21 +74,20 @@ impl ActivityHandler for UpdateRemoteArticle {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn verify(&self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
async fn verify(&self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||||
let article = DbArticle::read_from_ap_id(&self.object.object, &data.db_connection)?;
|
let article = DbArticle::read_from_ap_id(&self.object.object, data)?;
|
||||||
can_edit_article(&article, false)?;
|
can_edit_article(&article, false)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Received on article origin instances
|
/// Received on article origin instances
|
||||||
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||||
let local_article = DbArticle::read_from_ap_id(&self.object.object, &data.db_connection)?;
|
let local_article = DbArticle::read_from_ap_id(&self.object.object, data)?;
|
||||||
let patch = Patch::from_str(&self.object.content)?;
|
let patch = Patch::from_str(&self.object.content)?;
|
||||||
|
|
||||||
match apply(&local_article.text, &patch) {
|
match apply(&local_article.text, &patch) {
|
||||||
Ok(applied) => {
|
Ok(applied) => {
|
||||||
let edit = DbEdit::from_json(self.object.clone(), data).await?;
|
let edit = DbEdit::from_json(self.object.clone(), data).await?;
|
||||||
let article =
|
let article = DbArticle::update_text(edit.article_id, &applied, data)?;
|
||||||
DbArticle::update_text(edit.article_id, &applied, &data.db_connection)?;
|
|
||||||
UpdateLocalArticle::send(article, vec![self.actor.dereference(data).await?], data)
|
UpdateLocalArticle::send(article, vec![self.actor.dereference(data).await?], data)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,19 +41,19 @@ impl Object for DbArticle {
|
||||||
object_id: Url,
|
object_id: Url,
|
||||||
data: &Data<Self::DataType>,
|
data: &Data<Self::DataType>,
|
||||||
) -> Result<Option<Self>, Self::Error> {
|
) -> Result<Option<Self>, Self::Error> {
|
||||||
let article = DbArticle::read_from_ap_id(&object_id.into(), &data.db_connection).ok();
|
let article = DbArticle::read_from_ap_id(&object_id.into(), data).ok();
|
||||||
Ok(article)
|
Ok(article)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn into_json(self, data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
|
async fn into_json(self, data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
|
||||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let local_instance = DbInstance::read_local_instance(data)?;
|
||||||
Ok(ApubArticle {
|
Ok(ApubArticle {
|
||||||
kind: Default::default(),
|
kind: Default::default(),
|
||||||
id: self.ap_id.clone(),
|
id: self.ap_id.clone(),
|
||||||
attributed_to: local_instance.ap_id.clone(),
|
attributed_to: local_instance.ap_id.clone(),
|
||||||
to: vec![public(), local_instance.followers_url()?],
|
to: vec![public(), local_instance.followers_url()?],
|
||||||
edits: self.edits_id()?,
|
edits: self.edits_id()?,
|
||||||
latest_version: self.latest_edit_version(&data.db_connection)?,
|
latest_version: self.latest_edit_version(data)?,
|
||||||
content: self.text,
|
content: self.text,
|
||||||
name: self.title,
|
name: self.title,
|
||||||
})
|
})
|
||||||
|
@ -77,7 +77,7 @@ impl Object for DbArticle {
|
||||||
local: false,
|
local: false,
|
||||||
instance_id: instance.id,
|
instance_id: instance.id,
|
||||||
};
|
};
|
||||||
let article = DbArticle::create_or_update(form, &data.db_connection)?;
|
let article = DbArticle::create_or_update(form, data)?;
|
||||||
|
|
||||||
json.edits.dereference(&article, data).await?;
|
json.edits.dereference(&article, data).await?;
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ impl Collection for DbArticleCollection {
|
||||||
owner: &Self::Owner,
|
owner: &Self::Owner,
|
||||||
data: &Data<Self::DataType>,
|
data: &Data<Self::DataType>,
|
||||||
) -> Result<Self::Kind, Self::Error> {
|
) -> Result<Self::Kind, Self::Error> {
|
||||||
let local_articles = DbArticle::read_all(true, &data.db_connection)?;
|
let local_articles = DbArticle::read_all(true, data)?;
|
||||||
let articles = future::try_join_all(
|
let articles = future::try_join_all(
|
||||||
local_articles
|
local_articles
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
@ -48,7 +48,7 @@ impl Object for DbEdit {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn into_json(self, data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
|
async fn into_json(self, data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
|
||||||
let article = DbArticle::read(self.article_id, &data.db_connection)?;
|
let article = DbArticle::read(self.article_id, data)?;
|
||||||
let creator = DbPerson::read(self.creator_id, data)?;
|
let creator = DbPerson::read(self.creator_id, data)?;
|
||||||
Ok(ApubEdit {
|
Ok(ApubEdit {
|
||||||
kind: PatchType::Patch,
|
kind: PatchType::Patch,
|
||||||
|
@ -85,7 +85,7 @@ impl Object for DbEdit {
|
||||||
previous_version_id: json.previous_version,
|
previous_version_id: json.previous_version,
|
||||||
created: json.published,
|
created: json.published,
|
||||||
};
|
};
|
||||||
let edit = DbEdit::create(&form, &data.db_connection)?;
|
let edit = DbEdit::create(&form, data)?;
|
||||||
Ok(edit)
|
Ok(edit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ impl Collection for DbEditCollection {
|
||||||
owner: &Self::Owner,
|
owner: &Self::Owner,
|
||||||
data: &Data<Self::DataType>,
|
data: &Data<Self::DataType>,
|
||||||
) -> Result<Self::Kind, Self::Error> {
|
) -> Result<Self::Kind, Self::Error> {
|
||||||
let article = DbArticle::read_view(owner.id, &data.db_connection)?;
|
let article = DbArticle::read_view(owner.id, data)?;
|
||||||
|
|
||||||
let edits = future::try_join_all(
|
let edits = future::try_join_all(
|
||||||
article
|
article
|
||||||
|
@ -49,7 +49,7 @@ impl Collection for DbEditCollection {
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let local_instance = DbInstance::read_local_instance(data)?;
|
||||||
let collection = ApubEditCollection {
|
let collection = ApubEditCollection {
|
||||||
r#type: Default::default(),
|
r#type: Default::default(),
|
||||||
id: Url::from(local_instance.articles_url),
|
id: Url::from(local_instance.articles_url),
|
||||||
|
|
|
@ -38,7 +38,7 @@ impl DbInstance {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn follower_ids(&self, data: &Data<IbisData>) -> MyResult<Vec<Url>> {
|
pub fn follower_ids(&self, data: &Data<IbisData>) -> MyResult<Vec<Url>> {
|
||||||
Ok(DbInstance::read_followers(self.id, &data.db_connection)?
|
Ok(DbInstance::read_followers(self.id, data)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|f| f.ap_id.into())
|
.map(|f| f.ap_id.into())
|
||||||
.collect())
|
.collect())
|
||||||
|
@ -55,7 +55,7 @@ impl DbInstance {
|
||||||
<Activity as ActivityHandler>::Error: From<activitypub_federation::error::Error>,
|
<Activity as ActivityHandler>::Error: From<activitypub_federation::error::Error>,
|
||||||
<Activity as ActivityHandler>::Error: From<Error>,
|
<Activity as ActivityHandler>::Error: From<Error>,
|
||||||
{
|
{
|
||||||
let mut inboxes: Vec<_> = DbInstance::read_followers(self.id, &data.db_connection)?
|
let mut inboxes: Vec<_> = DbInstance::read_followers(self.id, data)?
|
||||||
.iter()
|
.iter()
|
||||||
.map(|f| Url::parse(&f.inbox_url).unwrap())
|
.map(|f| Url::parse(&f.inbox_url).unwrap())
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -119,7 +119,7 @@ impl Object for DbInstance {
|
||||||
last_refreshed_at: Local::now().into(),
|
last_refreshed_at: Local::now().into(),
|
||||||
local: false,
|
local: false,
|
||||||
};
|
};
|
||||||
let instance = DbInstance::create(&form, &data.db_connection)?;
|
let instance = DbInstance::create(&form, data)?;
|
||||||
// TODO: very inefficient to sync all articles every time
|
// TODO: very inefficient to sync all articles every time
|
||||||
instance.articles_url.dereference(&instance, data).await?;
|
instance.articles_url.dereference(&instance, data).await?;
|
||||||
Ok(instance)
|
Ok(instance)
|
||||||
|
|
|
@ -71,7 +71,7 @@ impl Object for DbPerson {
|
||||||
last_refreshed_at: Local::now().into(),
|
last_refreshed_at: Local::now().into(),
|
||||||
local: false,
|
local: false,
|
||||||
};
|
};
|
||||||
DbPerson::create(&form, &data.db_connection)
|
DbPerson::create(&form, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ pub fn federation_routes() -> Router {
|
||||||
async fn http_get_instance(
|
async fn http_get_instance(
|
||||||
data: Data<IbisData>,
|
data: Data<IbisData>,
|
||||||
) -> MyResult<FederationJson<WithContext<ApubInstance>>> {
|
) -> MyResult<FederationJson<WithContext<ApubInstance>>> {
|
||||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let local_instance = DbInstance::read_local_instance(&data)?;
|
||||||
let json_instance = local_instance.into_json(&data).await?;
|
let json_instance = local_instance.into_json(&data).await?;
|
||||||
Ok(FederationJson(WithContext::new_default(json_instance)))
|
Ok(FederationJson(WithContext::new_default(json_instance)))
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ async fn http_get_person(
|
||||||
async fn http_get_all_articles(
|
async fn http_get_all_articles(
|
||||||
data: Data<IbisData>,
|
data: Data<IbisData>,
|
||||||
) -> MyResult<FederationJson<WithContext<ArticleCollection>>> {
|
) -> MyResult<FederationJson<WithContext<ArticleCollection>>> {
|
||||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let local_instance = DbInstance::read_local_instance(&data)?;
|
||||||
let collection = DbArticleCollection::read_local(&local_instance, &data).await?;
|
let collection = DbArticleCollection::read_local(&local_instance, &data).await?;
|
||||||
Ok(FederationJson(WithContext::new_default(collection)))
|
Ok(FederationJson(WithContext::new_default(collection)))
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ async fn http_get_article(
|
||||||
Path(title): Path<String>,
|
Path(title): Path<String>,
|
||||||
data: Data<IbisData>,
|
data: Data<IbisData>,
|
||||||
) -> MyResult<FederationJson<WithContext<ApubArticle>>> {
|
) -> MyResult<FederationJson<WithContext<ApubArticle>>> {
|
||||||
let article = DbArticle::read_local_title(&title, &data.db_connection)?;
|
let article = DbArticle::read_local_title(&title, &data)?;
|
||||||
let json = article.into_json(&data).await?;
|
let json = article.into_json(&data).await?;
|
||||||
Ok(FederationJson(WithContext::new_default(json)))
|
Ok(FederationJson(WithContext::new_default(json)))
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ async fn http_get_article_edits(
|
||||||
Path(title): Path<String>,
|
Path(title): Path<String>,
|
||||||
data: Data<IbisData>,
|
data: Data<IbisData>,
|
||||||
) -> MyResult<FederationJson<WithContext<ApubEditCollection>>> {
|
) -> MyResult<FederationJson<WithContext<ApubEditCollection>>> {
|
||||||
let article = DbArticle::read_local_title(&title, &data.db_connection)?;
|
let article = DbArticle::read_local_title(&title, &data)?;
|
||||||
let json = DbEditCollection::read_local(&article, &data).await?;
|
let json = DbEditCollection::read_local(&article, &data).await?;
|
||||||
Ok(FederationJson(WithContext::new_default(json)))
|
Ok(FederationJson(WithContext::new_default(json)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ use axum::Server;
|
||||||
use axum::ServiceExt;
|
use axum::ServiceExt;
|
||||||
use axum::{middleware::Next, response::Response, Router};
|
use axum::{middleware::Next, response::Response, Router};
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
|
use diesel::r2d2::ConnectionManager;
|
||||||
|
use diesel::r2d2::Pool;
|
||||||
use diesel::Connection;
|
use diesel::Connection;
|
||||||
use diesel::PgConnection;
|
use diesel::PgConnection;
|
||||||
use diesel_migrations::embed_migrations;
|
use diesel_migrations::embed_migrations;
|
||||||
|
@ -46,15 +48,17 @@ const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
|
||||||
const FEDERATION_ROUTES_PREFIX: &str = "/federation_routes";
|
const FEDERATION_ROUTES_PREFIX: &str = "/federation_routes";
|
||||||
|
|
||||||
pub async fn start(config: IbisConfig) -> MyResult<()> {
|
pub async fn start(config: IbisConfig) -> MyResult<()> {
|
||||||
let db_connection = Arc::new(Mutex::new(PgConnection::establish(&config.database_url)?));
|
let manager = ConnectionManager::<PgConnection>::new(&config.database.connection_url);
|
||||||
db_connection
|
let db_pool = Pool::builder()
|
||||||
.lock()
|
.max_size(config.database.pool_size)
|
||||||
.unwrap()
|
.build(manager)?;
|
||||||
.run_pending_migrations(MIGRATIONS)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
|
db_pool
|
||||||
|
.get()?
|
||||||
|
.run_pending_migrations(MIGRATIONS)
|
||||||
|
.expect("run migrations");
|
||||||
let data = IbisData {
|
let data = IbisData {
|
||||||
db_connection,
|
db_pool,
|
||||||
config,
|
config,
|
||||||
};
|
};
|
||||||
let data = FederationConfig::builder()
|
let data = FederationConfig::builder()
|
||||||
|
@ -66,7 +70,7 @@ pub async fn start(config: IbisConfig) -> MyResult<()> {
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Create local instance if it doesnt exist yet
|
// Create local instance if it doesnt exist yet
|
||||||
if DbInstance::read_local_instance(&data.db_connection).is_err() {
|
if DbInstance::read_local_instance(&data).is_err() {
|
||||||
setup(&data.to_request_data()).await?;
|
setup(&data.to_request_data()).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +133,7 @@ async fn setup(data: &Data<IbisData>) -> Result<(), Error> {
|
||||||
last_refreshed_at: Local::now().into(),
|
last_refreshed_at: Local::now().into(),
|
||||||
local: true,
|
local: true,
|
||||||
};
|
};
|
||||||
let instance = DbInstance::create(&form, &data.db_connection)?;
|
let instance = DbInstance::create(&form, data)?;
|
||||||
|
|
||||||
let person = DbPerson::create_local(
|
let person = DbPerson::create_local(
|
||||||
data.config.setup.admin_username.clone(),
|
data.config.setup.admin_username.clone(),
|
||||||
|
@ -149,7 +153,7 @@ async fn setup(data: &Data<IbisData>) -> Result<(), Error> {
|
||||||
instance_id: instance.id,
|
instance_id: instance.id,
|
||||||
local: true,
|
local: true,
|
||||||
};
|
};
|
||||||
let article = DbArticle::create(form, &data.db_connection)?;
|
let article = DbArticle::create(form, data)?;
|
||||||
// also create an article so its included in most recently edited list
|
// also create an article so its included in most recently edited list
|
||||||
submit_article_update(
|
submit_article_update(
|
||||||
MAIN_PAGE_DEFAULT_TEXT.to_string(),
|
MAIN_PAGE_DEFAULT_TEXT.to_string(),
|
||||||
|
|
|
@ -57,7 +57,7 @@ fn backend_hostname() -> String {
|
||||||
}
|
}
|
||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
{
|
{
|
||||||
backend_hostname = crate::backend::config::IbisConfig::read().bind.to_string();
|
backend_hostname = crate::backend::config::IbisConfig::read().unwrap().bind.to_string();
|
||||||
}
|
}
|
||||||
backend_hostname
|
backend_hostname
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ pub async fn main() -> ibis_lib::backend::error::MyResult<()> {
|
||||||
.filter_module("ibis", LevelFilter::Info)
|
.filter_module("ibis", LevelFilter::Info)
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
let ibis_config = IbisConfig::read();
|
let ibis_config = IbisConfig::read()?;
|
||||||
ibis_lib::backend::start(ibis_config).await?;
|
ibis_lib::backend::start(ibis_config).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ibis_lib::backend::config::{IbisConfig, IbisConfigFederation};
|
use ibis_lib::backend::config::{IbisConfig, IbisConfigDatabase, IbisConfigFederation};
|
||||||
use ibis_lib::backend::start;
|
use ibis_lib::backend::start;
|
||||||
use ibis_lib::common::RegisterUserData;
|
use ibis_lib::common::RegisterUserData;
|
||||||
use ibis_lib::frontend::api::ApiClient;
|
use ibis_lib::frontend::api::ApiClient;
|
||||||
|
@ -105,12 +105,15 @@ impl IbisInstance {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start(db_path: String, port: i32, username: &str) -> Self {
|
async fn start(db_path: String, port: i32, username: &str) -> Self {
|
||||||
let database_url = format!("postgresql://ibis:password@/ibis?host={db_path}");
|
let connection_url = format!("postgresql://ibis:password@/ibis?host={db_path}");
|
||||||
let hostname = format!("localhost:{port}");
|
let hostname = format!("localhost:{port}");
|
||||||
let bind = format!("127.0.0.1:{port}").parse().unwrap();
|
let bind = format!("127.0.0.1:{port}").parse().unwrap();
|
||||||
let config = IbisConfig {
|
let config = IbisConfig {
|
||||||
bind,
|
bind,
|
||||||
database_url,
|
database: IbisConfigDatabase {
|
||||||
|
connection_url,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
registration_open: true,
|
registration_open: true,
|
||||||
federation: IbisConfigFederation {
|
federation: IbisConfigFederation {
|
||||||
domain: hostname.clone(),
|
domain: hostname.clone(),
|
||||||
|
|
Loading…
Reference in a new issue