mirror of
https://github.com/Nutomic/ibis.git
synced 2024-11-22 07:21:10 +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
|
- rustup component add rustfmt
|
||||||
- cargo fmt -- --check
|
- cargo fmt -- --check
|
||||||
|
|
||||||
frontend_wasm_build:
|
check_config_defaults_updated:
|
||||||
image: *rust_image
|
image: *rust_image
|
||||||
environment:
|
environment:
|
||||||
CARGO_HOME: .cargo_home
|
CARGO_HOME: .cargo_home
|
||||||
commands:
|
commands:
|
||||||
- "rustup target add wasm32-unknown-unknown"
|
- cargo run -- --print-config > config/defaults_current.toml
|
||||||
- "cargo check --target wasm32-unknown-unknown --features csr,hydrate --no-default-features"
|
- diff config/defaults.toml config/defaults_current.toml
|
||||||
|
|
||||||
check_diesel_schema:
|
check_diesel_schema:
|
||||||
image: willsquire/diesel-cli
|
image: willsquire/diesel-cli
|
||||||
|
@ -29,6 +29,14 @@ steps:
|
||||||
- diesel print-schema --config-file=diesel.toml > tmp.schema
|
- diesel print-schema --config-file=diesel.toml > tmp.schema
|
||||||
- diff tmp.schema src/backend/database/schema.rs
|
- 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:
|
cargo_clippy:
|
||||||
image: *rust_image
|
image: *rust_image
|
||||||
environment:
|
environment:
|
||||||
|
|
272
Cargo.lock
generated
272
Cargo.lock
generated
|
@ -348,6 +348,9 @@ name = "bitflags"
|
||||||
version = "2.4.1"
|
version = "2.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
|
@ -412,7 +415,7 @@ version = "0.18.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c878c71c2821aa2058722038a59a67583a4240524687c6028571c9b395ded61f"
|
checksum = "c878c71c2821aa2058722038a59a67583a4240524687c6028571c9b395ded61f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling 0.14.4",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
|
@ -542,6 +545,26 @@ dependencies = [
|
||||||
"toml 0.5.11",
|
"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]]
|
[[package]]
|
||||||
name = "console_error_panic_hook"
|
name = "console_error_panic_hook"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
|
@ -562,6 +585,26 @@ dependencies = [
|
||||||
"web-sys",
|
"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]]
|
[[package]]
|
||||||
name = "const_format"
|
name = "const_format"
|
||||||
version = "0.2.32"
|
version = "0.2.32"
|
||||||
|
@ -702,6 +745,12 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crunchy"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
|
@ -712,14 +761,38 @@ dependencies = [
|
||||||
"typenum",
|
"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]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.14.4"
|
version = "0.14.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
|
checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core 0.14.4",
|
||||||
"darling_macro",
|
"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]]
|
[[package]]
|
||||||
|
@ -736,13 +809,24 @@ dependencies = [
|
||||||
"syn 1.0.109",
|
"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]]
|
[[package]]
|
||||||
name = "darling_macro"
|
name = "darling_macro"
|
||||||
version = "0.14.4"
|
version = "0.14.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
|
checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core 0.14.4",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
@ -793,7 +877,7 @@ version = "0.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f"
|
checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling 0.14.4",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
|
@ -905,6 +989,38 @@ dependencies = [
|
||||||
"crypto-common",
|
"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]]
|
[[package]]
|
||||||
name = "downcast-rs"
|
name = "downcast-rs"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
@ -1491,12 +1607,14 @@ dependencies = [
|
||||||
"axum-macros",
|
"axum-macros",
|
||||||
"bcrypt",
|
"bcrypt",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"config 0.14.0",
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"console_log",
|
"console_log",
|
||||||
"diesel",
|
"diesel",
|
||||||
"diesel-derive-newtype",
|
"diesel-derive-newtype",
|
||||||
"diesel_migrations",
|
"diesel_migrations",
|
||||||
"diffy",
|
"diffy",
|
||||||
|
"doku",
|
||||||
"enum_delegate",
|
"enum_delegate",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"futures",
|
"futures",
|
||||||
|
@ -1515,6 +1633,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
|
"smart-default",
|
||||||
"time",
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower",
|
"tower",
|
||||||
|
@ -1661,6 +1780,17 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"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]]
|
[[package]]
|
||||||
name = "jsonwebtoken"
|
name = "jsonwebtoken"
|
||||||
version = "9.2.0"
|
version = "9.2.0"
|
||||||
|
@ -1731,7 +1861,7 @@ version = "0.5.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "afcaa5db5b22b794b624e14ffe2aefae215b2d21c60a230ae2d06fe21ae5da64"
|
checksum = "afcaa5db5b22b794b624e14ffe2aefae215b2d21c60a230ae2d06fe21ae5da64"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"config",
|
"config 0.13.4",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -2299,6 +2429,16 @@ dependencies = [
|
||||||
"vcpkg",
|
"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]]
|
[[package]]
|
||||||
name = "overload"
|
name = "overload"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
@ -2362,6 +2502,51 @@ version = "2.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
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]]
|
[[package]]
|
||||||
name = "pin-project"
|
name = "pin-project"
|
||||||
version = "1.1.3"
|
version = "1.1.3"
|
||||||
|
@ -2770,6 +2955,18 @@ dependencies = [
|
||||||
"windows-sys",
|
"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]]
|
[[package]]
|
||||||
name = "rstml"
|
name = "rstml"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
|
@ -2784,6 +2981,16 @@ dependencies = [
|
||||||
"thiserror",
|
"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]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.23"
|
version = "0.1.23"
|
||||||
|
@ -3125,6 +3332,17 @@ version = "1.11.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
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]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.4.10"
|
version = "0.4.10"
|
||||||
|
@ -3335,6 +3553,15 @@ dependencies = [
|
||||||
"time-core",
|
"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]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.6.0"
|
version = "1.6.0"
|
||||||
|
@ -3424,7 +3651,19 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"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]]
|
[[package]]
|
||||||
|
@ -3449,6 +3688,19 @@ dependencies = [
|
||||||
"winnow",
|
"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]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
version = "0.4.13"
|
version = "0.4.13"
|
||||||
|
@ -3572,6 +3824,12 @@ version = "1.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ucd-trie"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicase"
|
name = "unicase"
|
||||||
version = "2.7.0"
|
version = "2.7.0"
|
||||||
|
|
|
@ -21,6 +21,9 @@ ssr = [
|
||||||
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"]
|
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"]
|
||||||
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
|
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
|
||||||
|
|
||||||
|
[lints.clippy]
|
||||||
|
dbg_macro = "deny"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
activitypub_federation = { version = "0.5.0-beta.6", features = [
|
activitypub_federation = { version = "0.5.0-beta.6", features = [
|
||||||
"axum",
|
"axum",
|
||||||
|
@ -69,6 +72,9 @@ time = "0.3.31"
|
||||||
tower = "0.4.13"
|
tower = "0.4.13"
|
||||||
markdown-it = "0.6.0"
|
markdown-it = "0.6.0"
|
||||||
web-sys = "0.3.67"
|
web-sys = "0.3.67"
|
||||||
|
config = { version = "0.14.0", features = ["toml"] }
|
||||||
|
doku = "0.21.1"
|
||||||
|
smart-default = "0.7.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1.4.0"
|
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::create_article::CreateArticle;
|
||||||
use crate::backend::federation::activities::submit_article_update;
|
use crate::backend::federation::activities::submit_article_update;
|
||||||
use crate::backend::utils::generate_article_version;
|
use crate::backend::utils::generate_article_version;
|
||||||
use crate::common::LocalUserView;
|
|
||||||
use crate::common::{ApiConflict, ResolveObject};
|
use crate::common::{ApiConflict, ResolveObject};
|
||||||
use crate::common::{ArticleView, DbArticle, DbEdit};
|
use crate::common::{ArticleView, DbArticle, DbEdit};
|
||||||
use crate::common::{CreateArticleData, EditArticleData, EditVersion, ForkArticleData};
|
use crate::common::{CreateArticleData, EditArticleData, EditVersion, ForkArticleData};
|
||||||
use crate::common::{DbInstance, SearchArticleData};
|
use crate::common::{DbInstance, SearchArticleData};
|
||||||
use crate::common::{GetArticleData, ListArticlesData};
|
use crate::common::{GetArticleData, ListArticlesData};
|
||||||
|
use crate::common::{LocalUserView, MAIN_PAGE_NAME};
|
||||||
use activitypub_federation::config::Data;
|
use activitypub_federation::config::Data;
|
||||||
use activitypub_federation::fetch::object_id::ObjectId;
|
use activitypub_federation::fetch::object_id::ObjectId;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
@ -91,6 +91,12 @@ pub(in crate::backend::api) async fn edit_article(
|
||||||
if edit_form.summary.is_empty() {
|
if edit_form.summary.is_empty() {
|
||||||
return Err(anyhow!("No summary given").into());
|
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
|
// ensure trailing newline for clean diffs
|
||||||
if !edit_form.new_text.ends_with('\n') {
|
if !edit_form.new_text.ends_with('\n') {
|
||||||
edit_form.new_text.push('\n');
|
edit_form.new_text.push('\n');
|
||||||
|
|
|
@ -58,7 +58,9 @@ pub(in crate::backend::api) async fn register_user(
|
||||||
jar: CookieJar,
|
jar: CookieJar,
|
||||||
Form(form): Form<RegisterUserData>,
|
Form(form): Form<RegisterUserData>,
|
||||||
) -> MyResult<(CookieJar, Json<LocalUserView>)> {
|
) -> 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 user = DbPerson::create_local(form.username, form.password, false, &data)?;
|
||||||
let token = generate_login_token(&user.local_user, &data)?;
|
let token = generate_login_token(&user.local_user, &data)?;
|
||||||
let jar = jar.add(create_cookie(token, &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> {
|
pub fn read_local_view(conn: &Mutex<PgConnection>) -> MyResult<InstanceView> {
|
||||||
let instance = DbInstance::read_local_instance(conn)?;
|
let instance = DbInstance::read_local_instance(conn)?;
|
||||||
let followers = DbInstance::read_followers(instance.id, conn)?;
|
let followers = DbInstance::read_followers(instance.id, conn)?;
|
||||||
let following = DbInstance::read_following(instance.id, conn)?;
|
|
||||||
|
|
||||||
Ok(InstanceView {
|
Ok(InstanceView {
|
||||||
instance,
|
instance,
|
||||||
followers,
|
followers,
|
||||||
following,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,14 +104,4 @@ impl DbInstance {
|
||||||
.select(person::all_columns)
|
.select(person::all_columns)
|
||||||
.get_results(conn.deref_mut())?)
|
.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 diesel::PgConnection;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::{Arc, Mutex};
|
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::database::schema::jwt_secret;
|
||||||
use crate::backend::error::MyResult;
|
use crate::backend::error::MyResult;
|
||||||
|
use crate::config::IbisConfig;
|
||||||
use diesel::{QueryDsl, RunQueryDsl};
|
use diesel::{QueryDsl, RunQueryDsl};
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
|
@ -16,11 +18,12 @@ pub mod user;
|
||||||
pub mod version;
|
pub mod version;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MyData {
|
pub struct IbisData {
|
||||||
pub db_connection: Arc<Mutex<PgConnection>>,
|
pub db_connection: Arc<Mutex<PgConnection>>,
|
||||||
|
pub config: IbisConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for MyData {
|
impl Deref for IbisData {
|
||||||
type Target = Arc<Mutex<PgConnection>>;
|
type Target = Arc<Mutex<PgConnection>>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
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::schema::{local_user, person};
|
||||||
use crate::backend::database::MyDataHandle;
|
use crate::backend::database::{IbisData, MyDataHandle};
|
||||||
use crate::backend::error::MyResult;
|
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::config::Data;
|
||||||
use activitypub_federation::fetch::object_id::ObjectId;
|
use activitypub_federation::fetch::object_id::ObjectId;
|
||||||
use activitypub_federation::http_signatures::generate_actor_keypair;
|
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::ExpressionMethods;
|
|
||||||
use diesel::QueryDsl;
|
use diesel::QueryDsl;
|
||||||
use diesel::{insert_into, AsChangeset, Insertable, PgConnection, RunQueryDsl};
|
use diesel::{insert_into, AsChangeset, Insertable, PgConnection, RunQueryDsl};
|
||||||
|
use diesel::{ExpressionMethods, JoinOnDsl};
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
use std::sync::Mutex;
|
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))]
|
||||||
|
@ -54,12 +55,12 @@ impl DbPerson {
|
||||||
username: String,
|
username: String,
|
||||||
password: String,
|
password: String,
|
||||||
admin: bool,
|
admin: bool,
|
||||||
data: &Data<MyDataHandle>,
|
data: &IbisData,
|
||||||
) -> MyResult<LocalUserView> {
|
) -> MyResult<LocalUserView> {
|
||||||
let mut conn = data.db_connection.lock().unwrap();
|
let mut conn = data.db_connection.lock().unwrap();
|
||||||
let hostname = data.domain();
|
let domain = &data.config.federation.domain;
|
||||||
let ap_id = ObjectId::parse(&format!("http://{hostname}/user/{username}"))?;
|
let ap_id = ObjectId::parse(&format!("http://{domain}/user/{username}"))?;
|
||||||
let inbox_url = format!("http://{hostname}/inbox");
|
let inbox_url = format!("http://{domain}/inbox");
|
||||||
let keypair = generate_actor_keypair()?;
|
let keypair = generate_actor_keypair()?;
|
||||||
let person_form = DbPersonForm {
|
let person_form = DbPersonForm {
|
||||||
username,
|
username,
|
||||||
|
@ -85,7 +86,11 @@ impl DbPerson {
|
||||||
.values(local_user_form)
|
.values(local_user_form)
|
||||||
.get_result::<DbLocalUser>(conn.deref_mut())?;
|
.get_result::<DbLocalUser>(conn.deref_mut())?;
|
||||||
|
|
||||||
Ok(LocalUserView { local_user, person })
|
Ok(LocalUserView {
|
||||||
|
local_user,
|
||||||
|
person,
|
||||||
|
following: vec![],
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_from_ap_id(
|
pub fn read_from_ap_id(
|
||||||
|
@ -103,19 +108,42 @@ impl DbPerson {
|
||||||
data: &Data<MyDataHandle>,
|
data: &Data<MyDataHandle>,
|
||||||
) -> MyResult<LocalUserView> {
|
) -> MyResult<LocalUserView> {
|
||||||
let mut conn = data.db_connection.lock().unwrap();
|
let mut conn = data.db_connection.lock().unwrap();
|
||||||
Ok(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(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> {
|
pub fn read_local_from_id(id: i32, data: &Data<MyDataHandle>) -> MyResult<LocalUserView> {
|
||||||
let mut conn = data.db_connection.lock().unwrap();
|
let mut conn = data.db_connection.lock().unwrap();
|
||||||
Ok(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::id.eq(id))
|
.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::DbInstance;
|
||||||
use crate::common::DbPerson;
|
use crate::common::DbPerson;
|
||||||
|
use activitypub_federation::protocol::verification::verify_urls_match;
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{
|
||||||
config::Data,
|
config::Data,
|
||||||
fetch::object_id::ObjectId,
|
fetch::object_id::ObjectId,
|
||||||
|
@ -58,6 +59,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.db_connection)?;
|
||||||
|
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)?;
|
||||||
|
|
||||||
// send back an accept
|
// send back an accept
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
use crate::backend::database::MyDataHandle;
|
use crate::backend::database::MyDataHandle;
|
||||||
|
use crate::config::IbisConfig;
|
||||||
use activitypub_federation::activity_sending::SendActivityTask;
|
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::protocol::context::WithContext;
|
||||||
use activitypub_federation::traits::{ActivityHandler, Actor};
|
use activitypub_federation::traits::{ActivityHandler, Actor};
|
||||||
|
use async_trait::async_trait;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
@ -32,3 +35,31 @@ where
|
||||||
}
|
}
|
||||||
Ok(())
|
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::article::DbArticleForm;
|
||||||
use crate::backend::database::instance::DbInstanceForm;
|
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::error::MyResult;
|
||||||
use crate::backend::federation::routes::federation_routes;
|
use crate::backend::federation::routes::federation_routes;
|
||||||
|
use crate::backend::federation::VerifyUrlData;
|
||||||
use crate::backend::utils::generate_activity_id;
|
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 crate::frontend::app::App;
|
||||||
use activitypub_federation::config::{FederationConfig, FederationMiddleware};
|
use activitypub_federation::config::{FederationConfig, FederationMiddleware};
|
||||||
use activitypub_federation::fetch::collection_id::CollectionId;
|
use activitypub_federation::fetch::collection_id::CollectionId;
|
||||||
|
@ -24,7 +27,6 @@ use diesel_migrations::MigrationHarness;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use leptos_axum::{generate_route_list, LeptosRoutes};
|
use leptos_axum::{generate_route_list, LeptosRoutes};
|
||||||
use log::info;
|
use log::info;
|
||||||
use std::net::ToSocketAddrs;
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use tower::Layer;
|
use tower::Layer;
|
||||||
use tower_http::cors::CorsLayer;
|
use tower_http::cors::CorsLayer;
|
||||||
|
@ -40,62 +42,37 @@ const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
|
||||||
|
|
||||||
const FEDERATION_ROUTES_PREFIX: &str = "/federation_routes";
|
const FEDERATION_ROUTES_PREFIX: &str = "/federation_routes";
|
||||||
|
|
||||||
pub async fn start(hostname: &str, database_url: &str) -> MyResult<()> {
|
pub async fn start(config: IbisConfig) -> MyResult<()> {
|
||||||
let db_connection = Arc::new(Mutex::new(PgConnection::establish(database_url)?));
|
let db_connection = Arc::new(Mutex::new(PgConnection::establish(&config.database_url)?));
|
||||||
db_connection
|
db_connection
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.run_pending_migrations(MIGRATIONS)
|
.run_pending_migrations(MIGRATIONS)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let data = MyData { db_connection };
|
let data = IbisData {
|
||||||
let config = FederationConfig::builder()
|
db_connection,
|
||||||
.domain(hostname)
|
config,
|
||||||
|
};
|
||||||
|
let data = FederationConfig::builder()
|
||||||
|
.domain(data.config.federation.domain.clone())
|
||||||
|
.url_verifier(Box::new(VerifyUrlData(data.config.clone())))
|
||||||
.app_data(data)
|
.app_data(data)
|
||||||
.debug(true)
|
.debug(true)
|
||||||
.build()
|
.build()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Create local instance if it doesnt exist yet
|
// Create local instance if it doesnt exist yet
|
||||||
// TODO: Move this into setup api call
|
if DbInstance::read_local_instance(&data.db_connection).is_err() {
|
||||||
if DbInstance::read_local_instance(&config.db_connection).is_err() {
|
setup(&data)?;
|
||||||
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)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
|
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
|
||||||
let mut leptos_options = conf.leptos_options;
|
let mut leptos_options = conf.leptos_options;
|
||||||
let addr = hostname
|
leptos_options.site_addr = data.config.bind;
|
||||||
.to_socket_addrs()?
|
|
||||||
.next()
|
|
||||||
.expect("Failed to lookup domain name");
|
|
||||||
leptos_options.site_addr = addr;
|
|
||||||
let routes = generate_route_list(App);
|
let routes = generate_route_list(App);
|
||||||
|
|
||||||
let config = config.clone();
|
let config = data.clone();
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.leptos_routes(&leptos_options, routes, || view! { <App/> })
|
.leptos_routes(&leptos_options, routes, || view! { <App/> })
|
||||||
.with_state(leptos_options)
|
.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 middleware = axum::middleware::from_fn(federation_routes_middleware);
|
||||||
let app_with_middleware = middleware.layer(app);
|
let app_with_middleware = middleware.layer(app);
|
||||||
|
|
||||||
info!("{addr}");
|
info!("Listening on {}", &data.config.bind);
|
||||||
Server::bind(&addr)
|
Server::bind(&data.config.bind)
|
||||||
.serve(app_with_middleware.into_make_service())
|
.serve(app_with_middleware.into_make_service())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
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
|
/// Rewrite federation routes to use `FEDERATION_ROUTES_PREFIX`, to avoid conflicts
|
||||||
/// with frontend routes. If a request is an Activitypub fetch as indicated by
|
/// with frontend routes. If a request is an Activitypub fetch as indicated by
|
||||||
/// `Accept: application/activity+json` header, use the federation routes. Otherwise
|
/// `Accept: application/activity+json` header, use the federation routes. Otherwise
|
||||||
|
|
|
@ -13,6 +13,8 @@ use {
|
||||||
diesel::{Identifiable, Queryable, Selectable},
|
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
|
/// Should be an enum Title/Id but fails due to https://github.com/nox/serde_urlencoded/issues/66
|
||||||
#[derive(Deserialize, Serialize, Clone)]
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
pub struct GetArticleData {
|
pub struct GetArticleData {
|
||||||
|
@ -123,6 +125,7 @@ pub struct LoginUserData {
|
||||||
pub struct LocalUserView {
|
pub struct LocalUserView {
|
||||||
pub person: DbPerson,
|
pub person: DbPerson,
|
||||||
pub local_user: DbLocalUser,
|
pub local_user: DbLocalUser,
|
||||||
|
pub following: Vec<DbInstance>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A user with account registered on local instance.
|
/// A user with account registered on local instance.
|
||||||
|
@ -241,7 +244,6 @@ pub struct DbInstance {
|
||||||
pub struct InstanceView {
|
pub struct InstanceView {
|
||||||
pub instance: DbInstance,
|
pub instance: DbInstance,
|
||||||
pub followers: Vec<DbPerson>,
|
pub followers: Vec<DbPerson>,
|
||||||
pub following: Vec<DbInstance>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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();
|
.update_my_profile();
|
||||||
});
|
});
|
||||||
let (search_query, set_search_query) = create_signal(String::new());
|
let (search_query, set_search_query) = create_signal(String::new());
|
||||||
|
// TODO: hide register button if disabled in config
|
||||||
view! {
|
view! {
|
||||||
<nav class="inner">
|
<nav class="inner">
|
||||||
<li>
|
<li>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::common::{ArticleView, GetArticleData};
|
use crate::common::{ArticleView, GetArticleData, MAIN_PAGE_NAME};
|
||||||
use crate::frontend::app::GlobalState;
|
use crate::frontend::app::GlobalState;
|
||||||
use leptos::{create_resource, Resource};
|
use leptos::{create_resource, Resource};
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ fn article_resource(
|
||||||
title: impl Fn() -> Option<String> + 'static,
|
title: impl Fn() -> Option<String> + 'static,
|
||||||
) -> Resource<Option<String>, ArticleView> {
|
) -> Resource<Option<String>, ArticleView> {
|
||||||
create_resource(title, move |title| async move {
|
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()
|
GlobalState::api_client()
|
||||||
.get_article(GetArticleData {
|
.get_article(GetArticleData {
|
||||||
title: Some(title),
|
title: Some(title),
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
pub mod common;
|
pub mod common;
|
||||||
|
pub mod config;
|
||||||
pub mod frontend;
|
pub mod frontend;
|
||||||
|
|
21
src/main.rs
21
src/main.rs
|
@ -1,16 +1,29 @@
|
||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
pub async fn main() -> ibis_lib::backend::error::MyResult<()> {
|
pub async fn main() -> ibis_lib::backend::error::MyResult<()> {
|
||||||
|
use config::Config;
|
||||||
|
use ibis_lib::config::IbisConfig;
|
||||||
use log::LevelFilter;
|
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()
|
env_logger::builder()
|
||||||
.filter_level(LevelFilter::Warn)
|
.filter_level(LevelFilter::Warn)
|
||||||
.filter_module("activitypub_federation", LevelFilter::Info)
|
.filter_module("activitypub_federation", LevelFilter::Info)
|
||||||
.filter_module("ibis", LevelFilter::Info)
|
.filter_module("ibis", LevelFilter::Info)
|
||||||
.init();
|
.init();
|
||||||
let database_url = std::env::var("IBIS_DATABASE_URL")
|
|
||||||
.unwrap_or("postgres://ibis:password@localhost:5432/ibis".to_string());
|
let config = Config::builder()
|
||||||
let port = std::env::var("IBIS_BACKEND_PORT").unwrap_or("8081".to_string());
|
.add_source(config::File::with_name("config/config.toml"))
|
||||||
ibis_lib::backend::start(&format!("127.0.0.1:{port}"), &database_url).await?;
|
.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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
use ibis_lib::backend::start;
|
use ibis_lib::backend::start;
|
||||||
|
|
||||||
use ibis_lib::common::RegisterUserData;
|
use ibis_lib::common::RegisterUserData;
|
||||||
|
use ibis_lib::config::{IbisConfig, IbisConfigFederation};
|
||||||
use ibis_lib::frontend::api::ApiClient;
|
use ibis_lib::frontend::api::ApiClient;
|
||||||
use ibis_lib::frontend::error::MyResult;
|
use ibis_lib::frontend::error::MyResult;
|
||||||
|
|
||||||
use reqwest::ClientBuilder;
|
use reqwest::ClientBuilder;
|
||||||
use std::env::current_dir;
|
use std::env::current_dir;
|
||||||
use std::fs::create_dir_all;
|
use std::fs::create_dir_all;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use std::sync::atomic::{AtomicI32, Ordering};
|
use std::sync::atomic::{AtomicI32, Ordering};
|
||||||
|
|
||||||
use std::sync::Once;
|
use std::sync::Once;
|
||||||
use std::thread::{sleep, spawn};
|
use std::thread::{sleep, spawn};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -103,11 +101,21 @@ 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 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 = 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 {
|
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
|
// wait a moment for the backend to start
|
||||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
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"
|
pg_ctl start --options="-c listen_addresses= -c unix_socket_directories=$PGHOST"
|
||||||
|
|
||||||
# Setup database
|
# Setup database
|
||||||
psql -c "CREATE USER lemmy WITH PASSWORD 'password' SUPERUSER;" -U postgres
|
psql -c "CREATE USER ibis WITH PASSWORD 'password' SUPERUSER;" -U postgres
|
||||||
psql -c "CREATE DATABASE lemmy WITH OWNER lemmy;" -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;
|
let data = TestData::start().await;
|
||||||
|
|
||||||
// check initial state
|
// check initial state
|
||||||
let alpha_instance = data.alpha.get_local_instance().await?;
|
let alpha_user = data.alpha.my_profile().await?;
|
||||||
assert_eq!(0, alpha_instance.followers.len());
|
assert_eq!(0, alpha_user.following.len());
|
||||||
assert_eq!(0, alpha_instance.following.len());
|
|
||||||
let beta_instance = data.beta.get_local_instance().await?;
|
let beta_instance = data.beta.get_local_instance().await?;
|
||||||
assert_eq!(0, beta_instance.followers.len());
|
assert_eq!(0, beta_instance.followers.len());
|
||||||
assert_eq!(0, beta_instance.following.len());
|
|
||||||
|
|
||||||
data.alpha.follow_instance(&data.beta.hostname).await?;
|
data.alpha.follow_instance(&data.beta.hostname).await?;
|
||||||
|
dbg!(&data.alpha.hostname, &data.beta.hostname);
|
||||||
|
|
||||||
// check that follow was federated
|
// check that follow was federated
|
||||||
let alpha_instance = data.alpha.get_local_instance().await?;
|
let alpha_user = data.alpha.my_profile().await?;
|
||||||
assert_eq!(1, alpha_instance.following.len());
|
dbg!(&alpha_user);
|
||||||
assert_eq!(0, alpha_instance.followers.len());
|
assert_eq!(1, alpha_user.following.len());
|
||||||
assert_eq!(
|
assert_eq!(beta_instance.instance.ap_id, alpha_user.following[0].ap_id);
|
||||||
beta_instance.instance.ap_id,
|
|
||||||
alpha_instance.following[0].ap_id
|
|
||||||
);
|
|
||||||
|
|
||||||
let beta_instance = data.beta.get_local_instance().await?;
|
let beta_instance = data.beta.get_local_instance().await?;
|
||||||
assert_eq!(0, beta_instance.following.len());
|
|
||||||
assert_eq!(1, beta_instance.followers.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_user.person.ap_id, beta_instance.followers[0].ap_id);
|
||||||
assert_eq!(
|
|
||||||
alpha_instance.instance.ap_id.inner().domain(),
|
|
||||||
beta_instance.followers[0].ap_id.inner().domain()
|
|
||||||
);
|
|
||||||
|
|
||||||
data.stop()
|
data.stop()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue