mirror of
https://github.com/Nutomic/ibis.git
synced 2024-11-22 01:41:08 +00:00
add config file
This commit is contained in:
parent
dbd8e931a4
commit
334dc3826f
22 changed files with 557 additions and 116 deletions
|
@ -11,13 +11,13 @@ steps:
|
|||
- rustup component add rustfmt
|
||||
- cargo fmt -- --check
|
||||
|
||||
frontend_wasm_build:
|
||||
check_config_defaults_updated:
|
||||
image: *rust_image
|
||||
environment:
|
||||
CARGO_HOME: .cargo_home
|
||||
commands:
|
||||
- "rustup target add wasm32-unknown-unknown"
|
||||
- "cargo check --target wasm32-unknown-unknown --features csr,hydrate --no-default-features"
|
||||
- cargo run -- --print-config > config/defaults_current.toml
|
||||
- diff config/defaults.toml config/defaults_current.toml
|
||||
|
||||
check_diesel_schema:
|
||||
image: willsquire/diesel-cli
|
||||
|
@ -29,6 +29,14 @@ steps:
|
|||
- diesel print-schema --config-file=diesel.toml > tmp.schema
|
||||
- diff tmp.schema src/backend/database/schema.rs
|
||||
|
||||
frontend_wasm_build:
|
||||
image: *rust_image
|
||||
environment:
|
||||
CARGO_HOME: .cargo_home
|
||||
commands:
|
||||
- "rustup target add wasm32-unknown-unknown"
|
||||
- "cargo check --target wasm32-unknown-unknown --features csr,hydrate --no-default-features"
|
||||
|
||||
cargo_clippy:
|
||||
image: *rust_image
|
||||
environment:
|
||||
|
|
272
Cargo.lock
generated
272
Cargo.lock
generated
|
@ -348,6 +348,9 @@ name = "bitflags"
|
|||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
|
@ -412,7 +415,7 @@ version = "0.18.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c878c71c2821aa2058722038a59a67583a4240524687c6028571c9b395ded61f"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"darling 0.14.4",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
|
@ -542,6 +545,26 @@ dependencies = [
|
|||
"toml 0.5.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "config"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"convert_case 0.6.0",
|
||||
"json5",
|
||||
"lazy_static",
|
||||
"nom",
|
||||
"pathdiff",
|
||||
"ron",
|
||||
"rust-ini",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"toml 0.8.8",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console_error_panic_hook"
|
||||
version = "0.1.7"
|
||||
|
@ -562,6 +585,26 @@ dependencies = [
|
|||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-random"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aaf16c9c2c612020bcfd042e170f6e32de9b9d75adb5277cdbbd2e2c8c8299a"
|
||||
dependencies = [
|
||||
"const-random-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-random-macro"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"tiny-keccak",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const_format"
|
||||
version = "0.2.32"
|
||||
|
@ -702,6 +745,12 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
|
@ -712,14 +761,38 @@ dependencies = [
|
|||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
|
||||
dependencies = [
|
||||
"darling_core 0.13.4",
|
||||
"darling_macro 0.13.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
"darling_core 0.14.4",
|
||||
"darling_macro 0.14.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -736,13 +809,24 @@ dependencies = [
|
|||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
|
||||
dependencies = [
|
||||
"darling_core 0.13.4",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_core 0.14.4",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
@ -793,7 +877,7 @@ version = "0.12.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"darling 0.14.4",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
|
@ -905,6 +989,38 @@ dependencies = [
|
|||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlv-list"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f"
|
||||
dependencies = [
|
||||
"const-random",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "doku"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d018fadaf95088d2c12b66fe5b9d7c04a027b996c42a7b403b83fbd7a1c31531"
|
||||
dependencies = [
|
||||
"doku-derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "doku-derive"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74073dd10495ce912909655131925b0459d49363751b93676148d843097fe825"
|
||||
dependencies = [
|
||||
"darling 0.13.4",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "downcast-rs"
|
||||
version = "1.2.0"
|
||||
|
@ -1491,12 +1607,14 @@ dependencies = [
|
|||
"axum-macros",
|
||||
"bcrypt",
|
||||
"chrono",
|
||||
"config 0.14.0",
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
"diesel",
|
||||
"diesel-derive-newtype",
|
||||
"diesel_migrations",
|
||||
"diffy",
|
||||
"doku",
|
||||
"enum_delegate",
|
||||
"env_logger",
|
||||
"futures",
|
||||
|
@ -1515,6 +1633,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"smart-default",
|
||||
"time",
|
||||
"tokio",
|
||||
"tower",
|
||||
|
@ -1661,6 +1780,17 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "json5"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonwebtoken"
|
||||
version = "9.2.0"
|
||||
|
@ -1731,7 +1861,7 @@ version = "0.5.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afcaa5db5b22b794b624e14ffe2aefae215b2d21c60a230ae2d06fe21ae5da64"
|
||||
dependencies = [
|
||||
"config",
|
||||
"config 0.13.4",
|
||||
"regex",
|
||||
"serde",
|
||||
"thiserror",
|
||||
|
@ -2299,6 +2429,16 @@ dependencies = [
|
|||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ordered-multimap"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e"
|
||||
dependencies = [
|
||||
"dlv-list",
|
||||
"hashbrown 0.13.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
|
@ -2362,6 +2502,51 @@ version = "2.3.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror",
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcd6ab1236bbdb3a49027e920e693192ebfe8913f6d60e294de57463a493cfde"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7ff62f5259e53b78d1af898941cdcdccfae7385cf7d793a6e55de5d05bb4b7d"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"pest",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.3"
|
||||
|
@ -2770,6 +2955,18 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ron"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
|
||||
dependencies = [
|
||||
"base64 0.21.5",
|
||||
"bitflags 2.4.1",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rstml"
|
||||
version = "0.11.2"
|
||||
|
@ -2784,6 +2981,16 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-ini"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"ordered-multimap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.23"
|
||||
|
@ -3125,6 +3332,17 @@ version = "1.11.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
||||
|
||||
[[package]]
|
||||
name = "smart-default"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.10"
|
||||
|
@ -3335,6 +3553,15 @@ dependencies = [
|
|||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
|
@ -3424,7 +3651,19 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
"toml_edit 0.19.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit 0.21.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3449,6 +3688,19 @@ dependencies = [
|
|||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
|
||||
dependencies = [
|
||||
"indexmap 2.1.0",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.4.13"
|
||||
|
@ -3572,6 +3824,12 @@ version = "1.17.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.7.0"
|
||||
|
|
|
@ -21,6 +21,9 @@ ssr = [
|
|||
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"]
|
||||
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
|
||||
|
||||
[lints.clippy]
|
||||
dbg_macro = "deny"
|
||||
|
||||
[dependencies]
|
||||
activitypub_federation = { version = "0.5.0-beta.6", features = [
|
||||
"axum",
|
||||
|
@ -69,6 +72,9 @@ time = "0.3.31"
|
|||
tower = "0.4.13"
|
||||
markdown-it = "0.6.0"
|
||||
web-sys = "0.3.67"
|
||||
config = { version = "0.14.0", features = ["toml"] }
|
||||
doku = "0.21.1"
|
||||
smart-default = "0.7.1"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.4.0"
|
||||
|
|
3
config/config.toml
Normal file
3
config/config.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
[setup]
|
||||
admin_username = "ibis"
|
||||
admin_password = "ibis"
|
25
config/defaults.toml
Normal file
25
config/defaults.toml
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Address where ibis should listen for incoming requests
|
||||
bind = "127.0.0.1:8081"
|
||||
|
||||
# Database connection url
|
||||
database_url = "postgres://ibis:password@localhost:5432/ibis"
|
||||
|
||||
# Whether users can create new accounts
|
||||
registration_open = true
|
||||
|
||||
# Details of the initial admin account
|
||||
[setup]
|
||||
admin_username = "admin"
|
||||
admin_password = "hunter2"
|
||||
|
||||
[federation]
|
||||
# Domain name of the instance, mandatory for federation
|
||||
domain = "example.com"
|
||||
|
||||
# Comma separated list of instances which are allowed for federation. If set, federation
|
||||
# with other domains is blocked
|
||||
# Optional
|
||||
allowlist = "good.com,friends.org"
|
||||
|
||||
# Comma separated list of instances which are blocked for federation; optional
|
||||
blocklist = "evil.com,bad.org"
|
|
@ -6,12 +6,12 @@ use crate::backend::error::MyResult;
|
|||
use crate::backend::federation::activities::create_article::CreateArticle;
|
||||
use crate::backend::federation::activities::submit_article_update;
|
||||
use crate::backend::utils::generate_article_version;
|
||||
use crate::common::LocalUserView;
|
||||
use crate::common::{ApiConflict, ResolveObject};
|
||||
use crate::common::{ArticleView, DbArticle, DbEdit};
|
||||
use crate::common::{CreateArticleData, EditArticleData, EditVersion, ForkArticleData};
|
||||
use crate::common::{DbInstance, SearchArticleData};
|
||||
use crate::common::{GetArticleData, ListArticlesData};
|
||||
use crate::common::{LocalUserView, MAIN_PAGE_NAME};
|
||||
use activitypub_federation::config::Data;
|
||||
use activitypub_federation::fetch::object_id::ObjectId;
|
||||
use anyhow::anyhow;
|
||||
|
@ -91,6 +91,12 @@ pub(in crate::backend::api) async fn edit_article(
|
|||
if edit_form.summary.is_empty() {
|
||||
return Err(anyhow!("No summary given").into());
|
||||
}
|
||||
if original_article.article.local
|
||||
&& original_article.article.title == MAIN_PAGE_NAME
|
||||
&& !user.local_user.admin
|
||||
{
|
||||
return Err(anyhow!("Only admin can edit main page").into());
|
||||
}
|
||||
// ensure trailing newline for clean diffs
|
||||
if !edit_form.new_text.ends_with('\n') {
|
||||
edit_form.new_text.push('\n');
|
||||
|
|
|
@ -58,7 +58,9 @@ pub(in crate::backend::api) async fn register_user(
|
|||
jar: CookieJar,
|
||||
Form(form): Form<RegisterUserData>,
|
||||
) -> MyResult<(CookieJar, Json<LocalUserView>)> {
|
||||
// TODO: make admin if its the first user account
|
||||
if !data.config.registration_open {
|
||||
return Err(anyhow!("Registration is closed").into());
|
||||
}
|
||||
let user = DbPerson::create_local(form.username, form.password, false, &data)?;
|
||||
let token = generate_login_token(&user.local_user, &data)?;
|
||||
let jar = jar.add(create_cookie(token, &data));
|
||||
|
|
|
@ -64,12 +64,10 @@ impl DbInstance {
|
|||
pub fn read_local_view(conn: &Mutex<PgConnection>) -> MyResult<InstanceView> {
|
||||
let instance = DbInstance::read_local_instance(conn)?;
|
||||
let followers = DbInstance::read_followers(instance.id, conn)?;
|
||||
let following = DbInstance::read_following(instance.id, conn)?;
|
||||
|
||||
Ok(InstanceView {
|
||||
instance,
|
||||
followers,
|
||||
following,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -106,14 +104,4 @@ impl DbInstance {
|
|||
.select(person::all_columns)
|
||||
.get_results(conn.deref_mut())?)
|
||||
}
|
||||
|
||||
pub fn read_following(id_: i32, conn: &Mutex<PgConnection>) -> MyResult<Vec<Self>> {
|
||||
use instance_follow::dsl::{follower_id, instance_id};
|
||||
let mut conn = conn.lock().unwrap();
|
||||
Ok(instance_follow::table
|
||||
.inner_join(instance::table.on(instance_id.eq(instance::dsl::id)))
|
||||
.filter(follower_id.eq(id_))
|
||||
.select(instance::all_columns)
|
||||
.get_results(conn.deref_mut())?)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use diesel::PgConnection;
|
||||
use std::ops::Deref;
|
||||
use std::sync::{Arc, Mutex};
|
||||
pub type MyDataHandle = MyData;
|
||||
// TODO: can remove this
|
||||
pub type MyDataHandle = IbisData;
|
||||
use crate::backend::database::schema::jwt_secret;
|
||||
use crate::backend::error::MyResult;
|
||||
use crate::config::IbisConfig;
|
||||
use diesel::{QueryDsl, RunQueryDsl};
|
||||
use std::ops::DerefMut;
|
||||
|
||||
|
@ -16,11 +18,12 @@ pub mod user;
|
|||
pub mod version;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MyData {
|
||||
pub struct IbisData {
|
||||
pub db_connection: Arc<Mutex<PgConnection>>,
|
||||
pub config: IbisConfig,
|
||||
}
|
||||
|
||||
impl Deref for MyData {
|
||||
impl Deref for IbisData {
|
||||
type Target = Arc<Mutex<PgConnection>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
use crate::backend::database::schema::{instance, instance_follow};
|
||||
use crate::backend::database::schema::{local_user, person};
|
||||
use crate::backend::database::MyDataHandle;
|
||||
use crate::backend::database::{IbisData, MyDataHandle};
|
||||
use crate::backend::error::MyResult;
|
||||
use crate::common::{DbLocalUser, DbPerson, LocalUserView};
|
||||
use crate::common::{DbInstance, DbLocalUser, DbPerson, LocalUserView};
|
||||
use activitypub_federation::config::Data;
|
||||
use activitypub_federation::fetch::object_id::ObjectId;
|
||||
use activitypub_federation::http_signatures::generate_actor_keypair;
|
||||
use bcrypt::hash;
|
||||
use bcrypt::DEFAULT_COST;
|
||||
use chrono::{DateTime, Local, Utc};
|
||||
use diesel::ExpressionMethods;
|
||||
use diesel::QueryDsl;
|
||||
use diesel::{insert_into, AsChangeset, Insertable, PgConnection, RunQueryDsl};
|
||||
use diesel::{ExpressionMethods, JoinOnDsl};
|
||||
use std::ops::DerefMut;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
|
||||
#[derive(Debug, Clone, Insertable, AsChangeset)]
|
||||
#[diesel(table_name = local_user, check_for_backend(diesel::pg::Pg))]
|
||||
|
@ -54,12 +55,12 @@ impl DbPerson {
|
|||
username: String,
|
||||
password: String,
|
||||
admin: bool,
|
||||
data: &Data<MyDataHandle>,
|
||||
data: &IbisData,
|
||||
) -> MyResult<LocalUserView> {
|
||||
let mut conn = data.db_connection.lock().unwrap();
|
||||
let hostname = data.domain();
|
||||
let ap_id = ObjectId::parse(&format!("http://{hostname}/user/{username}"))?;
|
||||
let inbox_url = format!("http://{hostname}/inbox");
|
||||
let domain = &data.config.federation.domain;
|
||||
let ap_id = ObjectId::parse(&format!("http://{domain}/user/{username}"))?;
|
||||
let inbox_url = format!("http://{domain}/inbox");
|
||||
let keypair = generate_actor_keypair()?;
|
||||
let person_form = DbPersonForm {
|
||||
username,
|
||||
|
@ -85,7 +86,11 @@ impl DbPerson {
|
|||
.values(local_user_form)
|
||||
.get_result::<DbLocalUser>(conn.deref_mut())?;
|
||||
|
||||
Ok(LocalUserView { local_user, person })
|
||||
Ok(LocalUserView {
|
||||
local_user,
|
||||
person,
|
||||
following: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read_from_ap_id(
|
||||
|
@ -103,19 +108,42 @@ impl DbPerson {
|
|||
data: &Data<MyDataHandle>,
|
||||
) -> MyResult<LocalUserView> {
|
||||
let mut conn = data.db_connection.lock().unwrap();
|
||||
Ok(person::table
|
||||
let (person, local_user) = person::table
|
||||
.inner_join(local_user::table)
|
||||
.filter(person::dsl::local)
|
||||
.filter(person::dsl::username.eq(username))
|
||||
.get_result(conn.deref_mut())?)
|
||||
.get_result::<(DbPerson, DbLocalUser)>(conn.deref_mut())?;
|
||||
// TODO: handle this in single query
|
||||
let following = Self::read_following(person.id, conn)?;
|
||||
Ok(LocalUserView {
|
||||
person,
|
||||
local_user,
|
||||
following,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read_local_from_id(id: i32, data: &Data<MyDataHandle>) -> MyResult<LocalUserView> {
|
||||
let mut conn = data.db_connection.lock().unwrap();
|
||||
Ok(person::table
|
||||
let (person, local_user) = person::table
|
||||
.inner_join(local_user::table)
|
||||
.filter(person::dsl::local)
|
||||
.filter(person::dsl::id.eq(id))
|
||||
.get_result(conn.deref_mut())?)
|
||||
.get_result::<(DbPerson, DbLocalUser)>(conn.deref_mut())?;
|
||||
// TODO: handle this in single query
|
||||
let following = Self::read_following(person.id, conn)?;
|
||||
Ok(LocalUserView {
|
||||
person,
|
||||
local_user,
|
||||
following,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_following(id_: i32, mut conn: MutexGuard<PgConnection>) -> MyResult<Vec<DbInstance>> {
|
||||
use instance_follow::dsl::{follower_id, instance_id};
|
||||
Ok(instance_follow::table
|
||||
.inner_join(instance::table.on(instance_id.eq(instance::dsl::id)))
|
||||
.filter(follower_id.eq(id_))
|
||||
.select(instance::all_columns)
|
||||
.get_results(conn.deref_mut())?)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::backend::{
|
|||
};
|
||||
use crate::common::DbInstance;
|
||||
use crate::common::DbPerson;
|
||||
use activitypub_federation::protocol::verification::verify_urls_match;
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
|
@ -58,6 +59,7 @@ impl ActivityHandler for Follow {
|
|||
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||
let actor = self.actor.dereference(data).await?;
|
||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
||||
verify_urls_match(self.object.inner(), local_instance.ap_id.inner())?;
|
||||
DbInstance::follow(&actor, &local_instance, false, data)?;
|
||||
|
||||
// send back an accept
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
use crate::backend::database::MyDataHandle;
|
||||
use crate::config::IbisConfig;
|
||||
use activitypub_federation::activity_sending::SendActivityTask;
|
||||
use activitypub_federation::config::Data;
|
||||
use activitypub_federation::config::{Data, UrlVerifier};
|
||||
use activitypub_federation::error::Error as ActivityPubError;
|
||||
use activitypub_federation::protocol::context::WithContext;
|
||||
use activitypub_federation::traits::{ActivityHandler, Actor};
|
||||
use async_trait::async_trait;
|
||||
use log::warn;
|
||||
use serde::Serialize;
|
||||
use std::fmt::Debug;
|
||||
|
@ -32,3 +35,31 @@ where
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VerifyUrlData(pub IbisConfig);
|
||||
|
||||
#[async_trait]
|
||||
impl UrlVerifier for VerifyUrlData {
|
||||
/// Check domain against allowlist and blocklist from config file.
|
||||
async fn verify(&self, url: &Url) -> Result<(), ActivityPubError> {
|
||||
let domain = url.domain().unwrap();
|
||||
if let Some(allowlist) = &self.0.federation.allowlist {
|
||||
let allowlist = allowlist.split(',').collect::<Vec<_>>();
|
||||
if !allowlist.contains(&domain) {
|
||||
return Err(ActivityPubError::Other(format!(
|
||||
"Domain {domain} is not allowed"
|
||||
)));
|
||||
}
|
||||
}
|
||||
if let Some(blocklist) = &self.0.federation.blocklist {
|
||||
let blocklist = blocklist.split(',').collect::<Vec<_>>();
|
||||
if blocklist.contains(&domain) {
|
||||
return Err(ActivityPubError::Other(format!(
|
||||
"Domain {domain} is blocked"
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
use crate::backend::database::article::DbArticleForm;
|
||||
use crate::backend::database::instance::DbInstanceForm;
|
||||
use crate::backend::database::MyData;
|
||||
use crate::backend::database::IbisData;
|
||||
use crate::backend::error::Error;
|
||||
use crate::backend::error::MyResult;
|
||||
use crate::backend::federation::routes::federation_routes;
|
||||
use crate::backend::federation::VerifyUrlData;
|
||||
use crate::backend::utils::generate_activity_id;
|
||||
use crate::common::{DbArticle, DbInstance};
|
||||
use crate::common::{DbArticle, DbInstance, DbPerson, MAIN_PAGE_NAME};
|
||||
use crate::config::IbisConfig;
|
||||
use crate::frontend::app::App;
|
||||
use activitypub_federation::config::{FederationConfig, FederationMiddleware};
|
||||
use activitypub_federation::fetch::collection_id::CollectionId;
|
||||
|
@ -24,7 +27,6 @@ use diesel_migrations::MigrationHarness;
|
|||
use leptos::*;
|
||||
use leptos_axum::{generate_route_list, LeptosRoutes};
|
||||
use log::info;
|
||||
use std::net::ToSocketAddrs;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tower::Layer;
|
||||
use tower_http::cors::CorsLayer;
|
||||
|
@ -40,62 +42,37 @@ const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
|
|||
|
||||
const FEDERATION_ROUTES_PREFIX: &str = "/federation_routes";
|
||||
|
||||
pub async fn start(hostname: &str, database_url: &str) -> MyResult<()> {
|
||||
let db_connection = Arc::new(Mutex::new(PgConnection::establish(database_url)?));
|
||||
pub async fn start(config: IbisConfig) -> MyResult<()> {
|
||||
let db_connection = Arc::new(Mutex::new(PgConnection::establish(&config.database_url)?));
|
||||
db_connection
|
||||
.lock()
|
||||
.unwrap()
|
||||
.run_pending_migrations(MIGRATIONS)
|
||||
.unwrap();
|
||||
|
||||
let data = MyData { db_connection };
|
||||
let config = FederationConfig::builder()
|
||||
.domain(hostname)
|
||||
let data = IbisData {
|
||||
db_connection,
|
||||
config,
|
||||
};
|
||||
let data = FederationConfig::builder()
|
||||
.domain(data.config.federation.domain.clone())
|
||||
.url_verifier(Box::new(VerifyUrlData(data.config.clone())))
|
||||
.app_data(data)
|
||||
.debug(true)
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
// Create local instance if it doesnt exist yet
|
||||
// TODO: Move this into setup api call
|
||||
if DbInstance::read_local_instance(&config.db_connection).is_err() {
|
||||
let ap_id = ObjectId::parse(&format!("http://{hostname}"))?;
|
||||
let articles_url = CollectionId::parse(&format!("http://{}/all_articles", hostname))?;
|
||||
let inbox_url = format!("http://{}/inbox", hostname);
|
||||
let keypair = generate_actor_keypair()?;
|
||||
let form = DbInstanceForm {
|
||||
ap_id,
|
||||
description: Some("New Ibis instance".to_string()),
|
||||
articles_url,
|
||||
inbox_url,
|
||||
public_key: keypair.public_key,
|
||||
private_key: Some(keypair.private_key),
|
||||
last_refreshed_at: Local::now().into(),
|
||||
local: true,
|
||||
};
|
||||
let instance = DbInstance::create(&form, &config.db_connection)?;
|
||||
|
||||
// Create the main page which is shown by default
|
||||
let form = DbArticleForm {
|
||||
title: "Main_Page".to_string(),
|
||||
text: "Hello world!".to_string(),
|
||||
ap_id: ObjectId::parse(&format!("http://{hostname}/article/Main_Page"))?,
|
||||
instance_id: instance.id,
|
||||
local: true,
|
||||
};
|
||||
DbArticle::create(&form, &config.db_connection)?;
|
||||
if DbInstance::read_local_instance(&data.db_connection).is_err() {
|
||||
setup(&data)?;
|
||||
}
|
||||
|
||||
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
|
||||
let mut leptos_options = conf.leptos_options;
|
||||
let addr = hostname
|
||||
.to_socket_addrs()?
|
||||
.next()
|
||||
.expect("Failed to lookup domain name");
|
||||
leptos_options.site_addr = addr;
|
||||
leptos_options.site_addr = data.config.bind;
|
||||
let routes = generate_route_list(App);
|
||||
|
||||
let config = config.clone();
|
||||
let config = data.clone();
|
||||
let app = Router::new()
|
||||
.leptos_routes(&leptos_options, routes, || view! { <App/> })
|
||||
.with_state(leptos_options)
|
||||
|
@ -118,14 +95,51 @@ pub async fn start(hostname: &str, database_url: &str) -> MyResult<()> {
|
|||
let middleware = axum::middleware::from_fn(federation_routes_middleware);
|
||||
let app_with_middleware = middleware.layer(app);
|
||||
|
||||
info!("{addr}");
|
||||
Server::bind(&addr)
|
||||
info!("Listening on {}", &data.config.bind);
|
||||
Server::bind(&data.config.bind)
|
||||
.serve(app_with_middleware.into_make_service())
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup(data: &IbisData) -> Result<(), Error> {
|
||||
let domain = &data.config.federation.domain;
|
||||
let ap_id = ObjectId::parse(&format!("http://{domain}"))?;
|
||||
let articles_url = CollectionId::parse(&format!("http://{domain}/all_articles"))?;
|
||||
let inbox_url = format!("http://{domain}/inbox");
|
||||
let keypair = generate_actor_keypair()?;
|
||||
let form = DbInstanceForm {
|
||||
ap_id,
|
||||
description: Some("New Ibis instance".to_string()),
|
||||
articles_url,
|
||||
inbox_url,
|
||||
public_key: keypair.public_key,
|
||||
private_key: Some(keypair.private_key),
|
||||
last_refreshed_at: Local::now().into(),
|
||||
local: true,
|
||||
};
|
||||
let instance = DbInstance::create(&form, &data.db_connection)?;
|
||||
|
||||
// Create the main page which is shown by default
|
||||
let form = DbArticleForm {
|
||||
title: MAIN_PAGE_NAME.to_string(),
|
||||
text: "Hello world!".to_string(),
|
||||
ap_id: ObjectId::parse(&format!("http://{domain}/article/{MAIN_PAGE_NAME}"))?,
|
||||
instance_id: instance.id,
|
||||
local: true,
|
||||
};
|
||||
DbArticle::create(&form, &data.db_connection)?;
|
||||
|
||||
DbPerson::create_local(
|
||||
data.config.setup.admin_username.clone(),
|
||||
data.config.setup.admin_password.clone(),
|
||||
true,
|
||||
data,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Rewrite federation routes to use `FEDERATION_ROUTES_PREFIX`, to avoid conflicts
|
||||
/// with frontend routes. If a request is an Activitypub fetch as indicated by
|
||||
/// `Accept: application/activity+json` header, use the federation routes. Otherwise
|
||||
|
|
|
@ -13,6 +13,8 @@ use {
|
|||
diesel::{Identifiable, Queryable, Selectable},
|
||||
};
|
||||
|
||||
pub const MAIN_PAGE_NAME: &str = "Main_Page";
|
||||
|
||||
/// Should be an enum Title/Id but fails due to https://github.com/nox/serde_urlencoded/issues/66
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
pub struct GetArticleData {
|
||||
|
@ -123,6 +125,7 @@ pub struct LoginUserData {
|
|||
pub struct LocalUserView {
|
||||
pub person: DbPerson,
|
||||
pub local_user: DbLocalUser,
|
||||
pub following: Vec<DbInstance>,
|
||||
}
|
||||
|
||||
/// A user with account registered on local instance.
|
||||
|
@ -241,7 +244,6 @@ pub struct DbInstance {
|
|||
pub struct InstanceView {
|
||||
pub instance: DbInstance,
|
||||
pub followers: Vec<DbPerson>,
|
||||
pub following: Vec<DbInstance>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
51
src/config.rs
Normal file
51
src/config.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
use doku::Document;
|
||||
use serde::Deserialize;
|
||||
use smart_default::SmartDefault;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Clone, Document, SmartDefault)]
|
||||
#[serde(default)]
|
||||
pub struct IbisConfig {
|
||||
/// Address where ibis should listen for incoming requests
|
||||
#[default("127.0.0.1:8081".parse().unwrap())]
|
||||
#[doku(as = "String", example = "127.0.0.1:8081")]
|
||||
pub bind: SocketAddr,
|
||||
/// Database connection url
|
||||
#[default("postgres://ibis:password@localhost:5432/ibis")]
|
||||
#[doku(example = "postgres://ibis:password@localhost:5432/ibis")]
|
||||
pub database_url: String,
|
||||
/// Whether users can create new accounts
|
||||
#[default = true]
|
||||
#[doku(example = "true")]
|
||||
pub registration_open: bool,
|
||||
/// Details of the initial admin account
|
||||
pub setup: IbisConfigSetup,
|
||||
pub federation: IbisConfigFederation,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Clone, Document, SmartDefault)]
|
||||
#[serde(default)]
|
||||
pub struct IbisConfigSetup {
|
||||
#[doku(example = "admin")]
|
||||
pub admin_username: String,
|
||||
#[doku(example = "hunter2")]
|
||||
pub admin_password: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Clone, Document, SmartDefault)]
|
||||
#[serde(default)]
|
||||
pub struct IbisConfigFederation {
|
||||
/// Domain name of the instance, mandatory for federation
|
||||
#[default("example.com")]
|
||||
#[doku(example = "example.com")]
|
||||
pub domain: String,
|
||||
/// Comma separated list of instances which are allowed for federation. If set, federation
|
||||
/// with other domains is blocked
|
||||
#[default(None)]
|
||||
#[doku(example = "good.com,friends.org")]
|
||||
pub allowlist: Option<String>,
|
||||
/// Comma separated list of instances which are blocked for federation
|
||||
#[default(None)]
|
||||
#[doku(example = "evil.com,bad.org")]
|
||||
pub blocklist: Option<String>,
|
||||
}
|
|
@ -13,6 +13,7 @@ pub fn Nav() -> impl IntoView {
|
|||
.update_my_profile();
|
||||
});
|
||||
let (search_query, set_search_query) = create_signal(String::new());
|
||||
// TODO: hide register button if disabled in config
|
||||
view! {
|
||||
<nav class="inner">
|
||||
<li>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::common::{ArticleView, GetArticleData};
|
||||
use crate::common::{ArticleView, GetArticleData, MAIN_PAGE_NAME};
|
||||
use crate::frontend::app::GlobalState;
|
||||
use leptos::{create_resource, Resource};
|
||||
|
||||
|
@ -13,7 +13,7 @@ fn article_resource(
|
|||
title: impl Fn() -> Option<String> + 'static,
|
||||
) -> Resource<Option<String>, ArticleView> {
|
||||
create_resource(title, move |title| async move {
|
||||
let title = title.unwrap_or("Main_Page".to_string());
|
||||
let title = title.unwrap_or(MAIN_PAGE_NAME.to_string());
|
||||
GlobalState::api_client()
|
||||
.get_article(GetArticleData {
|
||||
title: Some(title),
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#[cfg(feature = "ssr")]
|
||||
pub mod backend;
|
||||
pub mod common;
|
||||
pub mod config;
|
||||
pub mod frontend;
|
||||
|
|
21
src/main.rs
21
src/main.rs
|
@ -1,16 +1,29 @@
|
|||
#[cfg(feature = "ssr")]
|
||||
#[tokio::main]
|
||||
pub async fn main() -> ibis_lib::backend::error::MyResult<()> {
|
||||
use config::Config;
|
||||
use ibis_lib::config::IbisConfig;
|
||||
use log::LevelFilter;
|
||||
|
||||
if std::env::args().collect::<Vec<_>>().get(1) == Some(&"--print-config".to_string()) {
|
||||
println!("{}", doku::to_toml::<IbisConfig>());
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
env_logger::builder()
|
||||
.filter_level(LevelFilter::Warn)
|
||||
.filter_module("activitypub_federation", LevelFilter::Info)
|
||||
.filter_module("ibis", LevelFilter::Info)
|
||||
.init();
|
||||
let database_url = std::env::var("IBIS_DATABASE_URL")
|
||||
.unwrap_or("postgres://ibis:password@localhost:5432/ibis".to_string());
|
||||
let port = std::env::var("IBIS_BACKEND_PORT").unwrap_or("8081".to_string());
|
||||
ibis_lib::backend::start(&format!("127.0.0.1:{port}"), &database_url).await?;
|
||||
|
||||
let config = Config::builder()
|
||||
.add_source(config::File::with_name("config/config.toml"))
|
||||
.add_source(config::Environment::with_prefix("IBIS"))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let ibis_config: IbisConfig = config.try_deserialize().unwrap();
|
||||
ibis_lib::backend::start(ibis_config).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
use ibis_lib::backend::start;
|
||||
|
||||
use ibis_lib::common::RegisterUserData;
|
||||
use ibis_lib::config::{IbisConfig, IbisConfigFederation};
|
||||
use ibis_lib::frontend::api::ApiClient;
|
||||
use ibis_lib::frontend::error::MyResult;
|
||||
|
||||
use reqwest::ClientBuilder;
|
||||
use std::env::current_dir;
|
||||
use std::fs::create_dir_all;
|
||||
use std::ops::Deref;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::atomic::{AtomicI32, Ordering};
|
||||
|
||||
use std::sync::Once;
|
||||
use std::thread::{sleep, spawn};
|
||||
use std::time::Duration;
|
||||
|
@ -103,11 +101,21 @@ impl IbisInstance {
|
|||
}
|
||||
|
||||
async fn start(db_path: String, port: i32, username: &str) -> Self {
|
||||
let db_url = format!("postgresql://lemmy:password@/lemmy?host={db_path}");
|
||||
let database_url = format!("postgresql://ibis:password@/ibis?host={db_path}");
|
||||
let hostname = format!("localhost:{port}");
|
||||
let hostname_ = hostname.clone();
|
||||
let bind = format!("127.0.0.1:{port}").parse().unwrap();
|
||||
let config = IbisConfig {
|
||||
bind,
|
||||
database_url,
|
||||
registration_open: true,
|
||||
federation: IbisConfigFederation {
|
||||
domain: hostname.clone(),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
let handle = tokio::task::spawn(async move {
|
||||
start(&hostname_, &db_url).await.unwrap();
|
||||
start(config).await.unwrap();
|
||||
});
|
||||
// wait a moment for the backend to start
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
|
|
|
@ -25,5 +25,5 @@ echo "$PGHOST/.s.PGSQL.5432"
|
|||
pg_ctl start --options="-c listen_addresses= -c unix_socket_directories=$PGHOST"
|
||||
|
||||
# Setup database
|
||||
psql -c "CREATE USER lemmy WITH PASSWORD 'password' SUPERUSER;" -U postgres
|
||||
psql -c "CREATE DATABASE lemmy WITH OWNER lemmy;" -U postgres
|
||||
psql -c "CREATE USER ibis WITH PASSWORD 'password' SUPERUSER;" -U postgres
|
||||
psql -c "CREATE DATABASE ibis WITH OWNER ibis;" -U postgres
|
||||
|
|
|
@ -98,32 +98,23 @@ async fn test_follow_instance() -> MyResult<()> {
|
|||
let data = TestData::start().await;
|
||||
|
||||
// check initial state
|
||||
let alpha_instance = data.alpha.get_local_instance().await?;
|
||||
assert_eq!(0, alpha_instance.followers.len());
|
||||
assert_eq!(0, alpha_instance.following.len());
|
||||
let alpha_user = data.alpha.my_profile().await?;
|
||||
assert_eq!(0, alpha_user.following.len());
|
||||
let beta_instance = data.beta.get_local_instance().await?;
|
||||
assert_eq!(0, beta_instance.followers.len());
|
||||
assert_eq!(0, beta_instance.following.len());
|
||||
|
||||
data.alpha.follow_instance(&data.beta.hostname).await?;
|
||||
dbg!(&data.alpha.hostname, &data.beta.hostname);
|
||||
|
||||
// check that follow was federated
|
||||
let alpha_instance = data.alpha.get_local_instance().await?;
|
||||
assert_eq!(1, alpha_instance.following.len());
|
||||
assert_eq!(0, alpha_instance.followers.len());
|
||||
assert_eq!(
|
||||
beta_instance.instance.ap_id,
|
||||
alpha_instance.following[0].ap_id
|
||||
);
|
||||
let alpha_user = data.alpha.my_profile().await?;
|
||||
dbg!(&alpha_user);
|
||||
assert_eq!(1, alpha_user.following.len());
|
||||
assert_eq!(beta_instance.instance.ap_id, alpha_user.following[0].ap_id);
|
||||
|
||||
let beta_instance = data.beta.get_local_instance().await?;
|
||||
assert_eq!(0, beta_instance.following.len());
|
||||
assert_eq!(1, beta_instance.followers.len());
|
||||
// TODO: compare full ap_id of alpha user, but its not available through api yet
|
||||
assert_eq!(
|
||||
alpha_instance.instance.ap_id.inner().domain(),
|
||||
beta_instance.followers[0].ap_id.inner().domain()
|
||||
);
|
||||
assert_eq!(alpha_user.person.ap_id, beta_instance.followers[0].ap_id);
|
||||
|
||||
data.stop()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue