~80% done

This commit is contained in:
Dessalines 2021-03-10 17:33:55 -05:00
commit ddf4a667b1
172 changed files with 3783 additions and 2669 deletions

View file

@ -9,7 +9,7 @@ platform:
steps: steps:
- name: chown repo - name: chown repo
image: ekidd/rust-musl-builder:1.47.0 image: ekidd/rust-musl-builder:1.50.0
user: root user: root
commands: commands:
- chown 1000:1000 . -R - chown 1000:1000 . -R
@ -20,23 +20,23 @@ steps:
- /root/.cargo/bin/cargo fmt -- --check - /root/.cargo/bin/cargo fmt -- --check
- name: cargo clippy - name: cargo clippy
image: ekidd/rust-musl-builder:1.47.0 image: ekidd/rust-musl-builder:1.50.0
commands: commands:
- cargo clippy --workspace --tests --all-targets --all-features -- -D warnings -D deprecated -D clippy::perf -D clippy::complexity -D clippy::dbg_macro - cargo clippy --workspace --tests --all-targets --all-features -- -D warnings -D deprecated -D clippy::perf -D clippy::complexity -D clippy::dbg_macro
- cargo clippy --workspace -- -D clippy::unwrap_used
- name: cargo test - name: cargo test
image: ekidd/rust-musl-builder:1.47.0 image: ekidd/rust-musl-builder:1.50.0
environment: environment:
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
RUST_BACKTRACE: 1 RUST_BACKTRACE: 1
RUST_TEST_THREADS: 1
commands: commands:
- sudo apt-get update - sudo apt-get update
- sudo apt-get -y install --no-install-recommends espeak postgresql-client - sudo apt-get -y install --no-install-recommends espeak postgresql-client
- cargo test --workspace --no-fail-fast - cargo test --workspace --no-fail-fast
- name: cargo build - name: cargo build
image: ekidd/rust-musl-builder:1.47.0 image: ekidd/rust-musl-builder:1.50.0
commands: commands:
- cargo build - cargo build
- mv target/x86_64-unknown-linux-musl/debug/lemmy_server target/lemmy_server - mv target/x86_64-unknown-linux-musl/debug/lemmy_server target/lemmy_server
@ -103,11 +103,10 @@ platform:
steps: steps:
- name: cargo test - name: cargo test
image: rust:1.47-slim-buster image: rust:1.50-slim-buster
environment: environment:
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
RUST_BACKTRACE: 1 RUST_BACKTRACE: 1
RUST_TEST_THREADS: 1
commands: commands:
- apt-get update - apt-get update
- apt-get -y install --no-install-recommends espeak postgresql-client libssl-dev pkg-config libpq-dev - apt-get -y install --no-install-recommends espeak postgresql-client libssl-dev pkg-config libpq-dev
@ -116,7 +115,7 @@ steps:
# Using Debian here because there seems to be no official Alpine-based Rust docker image for ARM. # Using Debian here because there seems to be no official Alpine-based Rust docker image for ARM.
- name: cargo build - name: cargo build
image: rust:1.47-slim-buster image: rust:1.50-slim-buster
commands: commands:
- apt-get update - apt-get update
- apt-get -y install --no-install-recommends libssl-dev pkg-config libpq-dev - apt-get -y install --no-install-recommends libssl-dev pkg-config libpq-dev

319
Cargo.lock generated
View file

@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3
[[package]] [[package]]
name = "activitystreams" name = "activitystreams"
version = "0.7.0-alpha.10" version = "0.7.0-alpha.10"
@ -8,7 +10,7 @@ checksum = "fe7ceed015dfca322d3bcec3653909c77557e7e57df72e98cb8806e2c93cc919"
dependencies = [ dependencies = [
"chrono", "chrono",
"mime", "mime",
"serde 1.0.123", "serde",
"serde_json", "serde_json",
"thiserror", "thiserror",
"url", "url",
@ -21,7 +23,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb8e19a0810cc25df3535061a08b7d8f8a734d309ea4411c57a9767e4a2ffa0e" checksum = "bb8e19a0810cc25df3535061a08b7d8f8a734d309ea4411c57a9767e4a2ffa0e"
dependencies = [ dependencies = [
"activitystreams", "activitystreams",
"serde 1.0.123", "serde",
"serde_json", "serde_json",
] ]
@ -128,7 +130,7 @@ dependencies = [
"pin-project 1.0.4", "pin-project 1.0.4",
"rand 0.7.3", "rand 0.7.3",
"regex", "regex",
"serde 1.0.123", "serde",
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"sha-1 0.9.3", "sha-1 0.9.3",
@ -156,7 +158,7 @@ dependencies = [
"http", "http",
"log", "log",
"regex", "regex",
"serde 1.0.123", "serde",
] ]
[[package]] [[package]]
@ -300,7 +302,7 @@ dependencies = [
"pin-project 1.0.4", "pin-project 1.0.4",
"regex", "regex",
"rustls", "rustls",
"serde 1.0.123", "serde",
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"socket2", "socket2",
@ -389,12 +391,6 @@ version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1"
[[package]]
name = "arrayvec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]] [[package]]
name = "async-mutex" name = "async-mutex"
version = "1.4.0" version = "1.4.0"
@ -464,7 +460,7 @@ dependencies = [
"percent-encoding", "percent-encoding",
"rand 0.7.3", "rand 0.7.3",
"rustls", "rustls",
"serde 1.0.123", "serde",
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
] ]
@ -494,7 +490,7 @@ dependencies = [
"log", "log",
"num_cpus", "num_cpus",
"rand 0.7.3", "rand 0.7.3",
"serde 1.0.123", "serde",
"serde_json", "serde_json",
"thiserror", "thiserror",
"tokio 0.2.25", "tokio 0.2.25",
@ -513,7 +509,7 @@ dependencies = [
"async-trait", "async-trait",
"chrono", "chrono",
"log", "log",
"serde 1.0.123", "serde",
"serde_json", "serde_json",
"thiserror", "thiserror",
"tokio 0.2.25", "tokio 0.2.25",
@ -733,8 +729,8 @@ checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [ dependencies = [
"libc", "libc",
"num-integer", "num-integer",
"num-traits 0.2.14", "num-traits",
"serde 1.0.123", "serde",
"time 0.1.44", "time 0.1.44",
"winapi 0.3.9", "winapi 0.3.9",
] ]
@ -781,18 +777,6 @@ dependencies = [
"xdg", "xdg",
] ]
[[package]]
name = "config"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3"
dependencies = [
"lazy_static",
"nom 5.1.2",
"serde 1.0.123",
"serde-hjson",
]
[[package]] [[package]]
name = "const_fn" name = "const_fn"
version = "0.4.5" version = "0.4.5"
@ -995,6 +979,15 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "deser-hjson"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d995b60ff81bc6af01a98f0bf5db70a7418a1ac8bd74ada633968f388139da5e"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "diesel" name = "diesel"
version = "1.4.5" version = "1.4.5"
@ -1110,6 +1103,15 @@ dependencies = [
"termcolor", "termcolor",
] ]
[[package]]
name = "envy"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "event-listener" name = "event-listener"
version = "2.5.1" version = "2.5.1"
@ -1590,7 +1592,7 @@ dependencies = [
"jpeg-decoder", "jpeg-decoder",
"num-iter", "num-iter",
"num-rational", "num-rational",
"num-traits 0.2.14", "num-traits",
"png", "png",
"scoped_threadpool", "scoped_threadpool",
"tiff", "tiff",
@ -1684,7 +1686,7 @@ dependencies = [
"base64 0.12.3", "base64 0.12.3",
"pem", "pem",
"ring", "ring",
"serde 1.0.123", "serde",
"serde_json", "serde_json",
"simple_asn1", "simple_asn1",
] ]
@ -1732,20 +1734,20 @@ dependencies = [
"http-signature-normalization-actix", "http-signature-normalization-actix",
"itertools", "itertools",
"lazy_static", "lazy_static",
"lemmy_api_structs",
"lemmy_apub", "lemmy_apub",
"lemmy_db_queries", "lemmy_db_queries",
"lemmy_db_schema", "lemmy_db_schema",
"lemmy_db_views", "lemmy_db_views",
"lemmy_db_views_actor", "lemmy_db_views_actor",
"lemmy_db_views_moderator", "lemmy_db_views_moderator",
"lemmy_structs",
"lemmy_utils", "lemmy_utils",
"lemmy_websocket", "lemmy_websocket",
"log", "log",
"openssl", "openssl",
"rand 0.8.3", "rand 0.8.3",
"reqwest", "reqwest",
"serde 1.0.123", "serde",
"serde_json", "serde_json",
"sha2", "sha2",
"strum", "strum",
@ -1756,6 +1758,25 @@ dependencies = [
"uuid", "uuid",
] ]
[[package]]
name = "lemmy_api_structs"
version = "0.1.0"
dependencies = [
"actix-web",
"chrono",
"diesel",
"lemmy_db_queries",
"lemmy_db_schema",
"lemmy_db_views",
"lemmy_db_views_actor",
"lemmy_db_views_moderator",
"lemmy_utils",
"log",
"serde",
"serde_json",
"url",
]
[[package]] [[package]]
name = "lemmy_apub" name = "lemmy_apub"
version = "0.1.0" version = "0.1.0"
@ -1780,11 +1801,11 @@ dependencies = [
"http-signature-normalization-reqwest", "http-signature-normalization-reqwest",
"itertools", "itertools",
"lazy_static", "lazy_static",
"lemmy_api_structs",
"lemmy_db_queries", "lemmy_db_queries",
"lemmy_db_schema", "lemmy_db_schema",
"lemmy_db_views", "lemmy_db_views",
"lemmy_db_views_actor", "lemmy_db_views_actor",
"lemmy_structs",
"lemmy_utils", "lemmy_utils",
"lemmy_websocket", "lemmy_websocket",
"log", "log",
@ -1792,7 +1813,7 @@ dependencies = [
"percent-encoding", "percent-encoding",
"rand 0.8.3", "rand 0.8.3",
"reqwest", "reqwest",
"serde 1.0.123", "serde",
"serde_json", "serde_json",
"sha2", "sha2",
"strum", "strum",
@ -1816,8 +1837,9 @@ dependencies = [
"lemmy_utils", "lemmy_utils",
"log", "log",
"regex", "regex",
"serde 1.0.123", "serde",
"serde_json", "serde_json",
"serial_test",
"sha2", "sha2",
"strum", "strum",
"strum_macros", "strum_macros",
@ -1831,7 +1853,7 @@ dependencies = [
"chrono", "chrono",
"diesel", "diesel",
"log", "log",
"serde 1.0.123", "serde",
"serde_json", "serde_json",
"url", "url",
] ]
@ -1844,7 +1866,8 @@ dependencies = [
"lemmy_db_queries", "lemmy_db_queries",
"lemmy_db_schema", "lemmy_db_schema",
"log", "log",
"serde 1.0.123", "serde",
"serial_test",
"url", "url",
] ]
@ -1855,7 +1878,7 @@ dependencies = [
"diesel", "diesel",
"lemmy_db_queries", "lemmy_db_queries",
"lemmy_db_schema", "lemmy_db_schema",
"serde 1.0.123", "serde",
] ]
[[package]] [[package]]
@ -1865,7 +1888,7 @@ dependencies = [
"diesel", "diesel",
"lemmy_db_queries", "lemmy_db_queries",
"lemmy_db_schema", "lemmy_db_schema",
"serde 1.0.123", "serde",
] ]
[[package]] [[package]]
@ -1880,16 +1903,16 @@ dependencies = [
"chrono", "chrono",
"diesel", "diesel",
"lazy_static", "lazy_static",
"lemmy_api_structs",
"lemmy_db_queries", "lemmy_db_queries",
"lemmy_db_schema", "lemmy_db_schema",
"lemmy_db_views", "lemmy_db_views",
"lemmy_db_views_actor", "lemmy_db_views_actor",
"lemmy_structs",
"lemmy_utils", "lemmy_utils",
"lemmy_websocket", "lemmy_websocket",
"log", "log",
"rss", "rss",
"serde 1.0.123", "serde",
"sha2", "sha2",
"strum", "strum",
"url", "url",
@ -1912,6 +1935,7 @@ dependencies = [
"env_logger", "env_logger",
"http-signature-normalization-actix", "http-signature-normalization-actix",
"lemmy_api", "lemmy_api",
"lemmy_api_structs",
"lemmy_apub", "lemmy_apub",
"lemmy_db_queries", "lemmy_db_queries",
"lemmy_db_schema", "lemmy_db_schema",
@ -1919,38 +1943,18 @@ dependencies = [
"lemmy_db_views_actor", "lemmy_db_views_actor",
"lemmy_db_views_moderator", "lemmy_db_views_moderator",
"lemmy_routes", "lemmy_routes",
"lemmy_structs",
"lemmy_utils", "lemmy_utils",
"lemmy_websocket", "lemmy_websocket",
"log", "log",
"openssl", "openssl",
"reqwest", "reqwest",
"serde 1.0.123", "serde",
"serde_json", "serde_json",
"strum", "strum",
"tokio 0.3.7", "tokio 0.3.7",
"url", "url",
] ]
[[package]]
name = "lemmy_structs"
version = "0.1.0"
dependencies = [
"actix-web",
"chrono",
"diesel",
"lemmy_db_queries",
"lemmy_db_schema",
"lemmy_db_views",
"lemmy_db_views_actor",
"lemmy_db_views_moderator",
"lemmy_utils",
"log",
"serde 1.0.123",
"serde_json",
"url",
]
[[package]] [[package]]
name = "lemmy_utils" name = "lemmy_utils"
version = "0.1.0" version = "0.1.0"
@ -1960,8 +1964,9 @@ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
"comrak", "comrak",
"config", "deser-hjson",
"diesel", "diesel",
"envy",
"futures", "futures",
"http", "http",
"itertools", "itertools",
@ -1969,12 +1974,13 @@ dependencies = [
"lazy_static", "lazy_static",
"lettre", "lettre",
"log", "log",
"merge",
"openssl", "openssl",
"percent-encoding", "percent-encoding",
"rand 0.8.3", "rand 0.8.3",
"regex", "regex",
"reqwest", "reqwest",
"serde 1.0.123", "serde",
"serde_json", "serde_json",
"strum", "strum",
"strum_macros", "strum_macros",
@ -1994,14 +2000,14 @@ dependencies = [
"background-jobs", "background-jobs",
"chrono", "chrono",
"diesel", "diesel",
"lemmy_api_structs",
"lemmy_db_queries", "lemmy_db_queries",
"lemmy_db_schema", "lemmy_db_schema",
"lemmy_structs",
"lemmy_utils", "lemmy_utils",
"log", "log",
"rand 0.8.3", "rand 0.8.3",
"reqwest", "reqwest",
"serde 1.0.123", "serde",
"serde_json", "serde_json",
"strum", "strum",
"strum_macros", "strum_macros",
@ -2020,46 +2026,23 @@ dependencies = [
"idna", "idna",
"mime", "mime",
"native-tls", "native-tls",
"nom 6.1.0", "nom",
"once_cell", "once_cell",
"quoted_printable", "quoted_printable",
"r2d2", "r2d2",
"rand 0.8.3", "rand 0.8.3",
"regex", "regex",
"serde 1.0.123", "serde",
"serde_json", "serde_json",
"uuid", "uuid",
] ]
[[package]]
name = "lexical-core"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616"
dependencies = [
"arrayvec",
"bitflags",
"cfg-if 0.1.10",
"ryu",
"static_assertions",
]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.84" version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cca32fa0182e8c0989459524dc356b8f2b5c10f1b9eb521b7d182c03cf8c5ff" checksum = "1cca32fa0182e8c0989459524dc356b8f2b5c10f1b9eb521b7d182c03cf8c5ff"
[[package]]
name = "linked-hash-map"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd"
dependencies = [
"serde 0.8.23",
"serde_test",
]
[[package]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
version = "0.5.4" version = "0.5.4"
@ -2102,7 +2085,7 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
dependencies = [ dependencies = [
"linked-hash-map 0.5.4", "linked-hash-map",
] ]
[[package]] [[package]]
@ -2144,6 +2127,28 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "merge"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10bbef93abb1da61525bbc45eeaff6473a41907d19f8f9aa5168d214e10693e9"
dependencies = [
"merge_derive",
"num-traits",
]
[[package]]
name = "merge_derive"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "209d075476da2e63b4b29e72a2ef627b840589588e71400a25e3565c4f849d07"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "migrations_internals" name = "migrations_internals"
version = "1.4.1" version = "1.4.1"
@ -2271,17 +2276,6 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "nom"
version = "5.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
dependencies = [
"lexical-core",
"memchr",
"version_check",
]
[[package]] [[package]]
name = "nom" name = "nom"
version = "6.1.0" version = "6.1.0"
@ -2301,7 +2295,7 @@ checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"num-integer", "num-integer",
"num-traits 0.2.14", "num-traits",
] ]
[[package]] [[package]]
@ -2311,7 +2305,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"num-traits 0.2.14", "num-traits",
] ]
[[package]] [[package]]
@ -2322,7 +2316,7 @@ checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"num-integer", "num-integer",
"num-traits 0.2.14", "num-traits",
] ]
[[package]] [[package]]
@ -2333,16 +2327,7 @@ checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"num-integer", "num-integer",
"num-traits 0.2.14", "num-traits",
]
[[package]]
name = "num-traits"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
dependencies = [
"num-traits 0.2.14",
] ]
[[package]] [[package]]
@ -2597,6 +2582,30 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]] [[package]]
name = "proc-macro-hack" name = "proc-macro-hack"
version = "0.5.19" version = "0.5.19"
@ -2838,7 +2847,7 @@ dependencies = [
"native-tls", "native-tls",
"percent-encoding", "percent-encoding",
"pin-project-lite 0.2.4", "pin-project-lite 0.2.4",
"serde 1.0.123", "serde",
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"tokio 0.2.25", "tokio 0.2.25",
@ -3008,12 +3017,6 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.123" version = "1.0.123"
@ -3023,19 +3026,6 @@ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "serde-hjson"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8"
dependencies = [
"lazy_static",
"linked-hash-map 0.3.0",
"num-traits 0.1.43",
"regex",
"serde 0.8.23",
]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.123" version = "1.0.123"
@ -3056,16 +3046,7 @@ dependencies = [
"indexmap", "indexmap",
"itoa", "itoa",
"ryu", "ryu",
"serde 1.0.123", "serde",
]
[[package]]
name = "serde_test"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "110b3dbdf8607ec493c22d5d947753282f3bae73c0f56d322af1e8c78e4c23d5"
dependencies = [
"serde 0.8.23",
] ]
[[package]] [[package]]
@ -3077,7 +3058,29 @@ dependencies = [
"form_urlencoded", "form_urlencoded",
"itoa", "itoa",
"ryu", "ryu",
"serde 1.0.123", "serde",
]
[[package]]
name = "serial_test"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0bccbcf40c8938196944a3da0e133e031a33f4d6b72db3bda3cc556e361905d"
dependencies = [
"lazy_static",
"parking_lot",
"serial_test_derive",
]
[[package]]
name = "serial_test_derive"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5"
dependencies = [
"proc-macro2",
"quote",
"syn",
] ]
[[package]] [[package]]
@ -3147,7 +3150,7 @@ checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b"
dependencies = [ dependencies = [
"chrono", "chrono",
"num-bigint", "num-bigint",
"num-traits 0.2.14", "num-traits",
] ]
[[package]] [[package]]
@ -3188,12 +3191,6 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]] [[package]]
name = "stdweb" name = "stdweb"
version = "0.4.20" version = "0.4.20"
@ -3216,7 +3213,7 @@ checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde 1.0.123", "serde",
"serde_derive", "serde_derive",
"syn", "syn",
] ]
@ -3230,7 +3227,7 @@ dependencies = [
"base-x", "base-x",
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde 1.0.123", "serde",
"serde_derive", "serde_derive",
"serde_json", "serde_json",
"sha1", "sha1",
@ -3658,15 +3655,15 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]] [[package]]
name = "url" name = "url"
version = "2.2.0" version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b"
dependencies = [ dependencies = [
"form_urlencoded", "form_urlencoded",
"idna", "idna",
"matches", "matches",
"percent-encoding", "percent-encoding",
"serde 1.0.123", "serde",
] ]
[[package]] [[package]]
@ -3676,7 +3673,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
dependencies = [ dependencies = [
"getrandom 0.2.2", "getrandom 0.2.2",
"serde 1.0.123", "serde",
] ]
[[package]] [[package]]
@ -3720,7 +3717,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"serde 1.0.123", "serde",
"serde_json", "serde_json",
"wasm-bindgen-macro", "wasm-bindgen-macro",
] ]

View file

@ -3,6 +3,9 @@ name = "lemmy_server"
version = "0.0.1" version = "0.0.1"
edition = "2018" edition = "2018"
[lib]
doctest = false
[profile.dev] [profile.dev]
debug = 0 debug = 0
@ -16,7 +19,7 @@ members = [
"crates/db_views", "crates/db_views",
"crates/db_views_actor", "crates/db_views_actor",
"crates/db_views_actor", "crates/db_views_actor",
"crates/structs", "crates/api_structs",
"crates/websocket", "crates/websocket",
"crates/routes" "crates/routes"
] ]
@ -30,7 +33,7 @@ lemmy_db_queries = { path = "./crates/db_queries" }
lemmy_db_views = { path = "./crates/db_views" } lemmy_db_views = { path = "./crates/db_views" }
lemmy_db_views_moderator = { path = "./crates/db_views_moderator" } lemmy_db_views_moderator = { path = "./crates/db_views_moderator" }
lemmy_db_views_actor = { path = "./crates/db_views_actor" } lemmy_db_views_actor = { path = "./crates/db_views_actor" }
lemmy_structs = { path = "./crates/structs" } lemmy_api_structs = { path = "crates/api_structs" }
lemmy_websocket = { path = "./crates/websocket" } lemmy_websocket = { path = "./crates/websocket" }
lemmy_routes = { path = "./crates/routes" } lemmy_routes = { path = "./crates/routes" }
diesel = "1.4.5" diesel = "1.4.5"
@ -42,7 +45,7 @@ actix-web = { version = "3.3.2", default-features = false, features = ["rustls"]
log = "0.4.14" log = "0.4.14"
env_logger = "0.8.2" env_logger = "0.8.2"
strum = "0.20.0" strum = "0.20.0"
url = { version = "2.2.0", features = ["serde"] } url = { version = "2.2.1", features = ["serde"] }
openssl = "0.10.32" openssl = "0.10.32"
http-signature-normalization-actix = { version = "0.4.1", default-features = false, features = ["sha-2"] } http-signature-normalization-actix = { version = "0.4.1", default-features = false, features = ["sha-2"] }
tokio = "0.3.6" tokio = "0.3.6"

View file

@ -47,7 +47,7 @@ The overall goal is to create an easily self-hostable, decentralized alternative
Each Lemmy server can set its own moderation policy; appointing site-wide admins, and community moderators to keep out the trolls, and foster a healthy, non-toxic environment where all can feel comfortable contributing. Each Lemmy server can set its own moderation policy; appointing site-wide admins, and community moderators to keep out the trolls, and foster a healthy, non-toxic environment where all can feel comfortable contributing.
*Note: Federation is still in active development and the WebSocket, as well as, HTTP API are currently unstable* *Note: The WebSocket and HTTP APIs are currently unstable*
### Why's it called Lemmy? ### Why's it called Lemmy?

View file

@ -29,8 +29,9 @@
# https://join.lemmy.ml/docs/en/federation/administration.html#instance-allowlist-and-blocklist # https://join.lemmy.ml/docs/en/federation/administration.html#instance-allowlist-and-blocklist
# #
# comma separated list of instances with which federation is allowed # comma separated list of instances with which federation is allowed
# allowed_instances: "" # Only one of these blocks should be uncommented
# allowed_instances: ["instance1.tld","instance2.tld"]
# comma separated list of instances which are blocked from federating # comma separated list of instances which are blocked from federating
# blocked_instances: "" # blocked_instances: []
} }
} }

View file

@ -53,6 +53,7 @@ services:
volumes: volumes:
- ./iframely.config.local.js:/iframely/config.local.js:ro - ./iframely.config.local.js:/iframely/config.local.js:ro
restart: always restart: always
mem_limit: 200m
postfix: postfix:
image: mwader/postfix-relay image: mwader/postfix-relay

View file

@ -1,13 +1,6 @@
#!/bin/bash #!/bin/bash
set -e set -e
export LEMMY_JWT_SECRET=changeme
export LEMMY_FEDERATION__ENABLED=true
export LEMMY_TLS_ENABLED=false
export LEMMY_SETUP__ADMIN_PASSWORD=lemmy
export LEMMY_RATE_LIMIT__POST=99999
export LEMMY_RATE_LIMIT__REGISTER=99999
export LEMMY_CAPTCHA__ENABLED=false
export LEMMY_TEST_SEND_SYNC=1 export LEMMY_TEST_SEND_SYNC=1
export RUST_BACKTRACE=1 export RUST_BACKTRACE=1
@ -35,52 +28,40 @@ fi
killall lemmy_server || true killall lemmy_server || true
echo "$PWD"
echo "start alpha" echo "start alpha"
LEMMY_HOSTNAME=lemmy-alpha:8541 \ LEMMY_HOSTNAME=lemmy-alpha:8541 \
LEMMY_PORT=8541 \ LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_alpha.hjson \
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_alpha" \ LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_alpha" \
LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-beta,lemmy-gamma,lemmy-delta,lemmy-epsilon \ LEMMY_HOSTNAME="lemmy-alpha:8541" \
LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha \ target/lemmy_server >/tmp/lemmy_alpha.out 2>&1 &
LEMMY_SETUP__SITE_NAME=lemmy-alpha \
target/lemmy_server >/dev/null 2>&1 &
echo "start beta" echo "start beta"
LEMMY_HOSTNAME=lemmy-beta:8551 \ LEMMY_HOSTNAME=lemmy-beta:8551 \
LEMMY_PORT=8551 \ LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_beta.hjson \
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_beta" \ LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_beta" \
LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-alpha,lemmy-gamma,lemmy-delta,lemmy-epsilon \ target/lemmy_server >/tmp/lemmy_beta.out 2>&1 &
LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta \
LEMMY_SETUP__SITE_NAME=lemmy-beta \
target/lemmy_server >/dev/null 2>&1 &
echo "start gamma" echo "start gamma"
LEMMY_HOSTNAME=lemmy-gamma:8561 \ LEMMY_HOSTNAME=lemmy-gamma:8561 \
LEMMY_PORT=8561 \ LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_gamma.hjson \
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_gamma" \ LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_gamma" \
LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-alpha,lemmy-beta,lemmy-delta,lemmy-epsilon \ target/lemmy_server >/tmp/lemmy_gamma.out 2>&1 &
LEMMY_SETUP__ADMIN_USERNAME=lemmy_gamma \
LEMMY_SETUP__SITE_NAME=lemmy-gamma \
target/lemmy_server >/dev/null 2>&1 &
echo "start delta" echo "start delta"
# An instance with only an allowlist for beta # An instance with only an allowlist for beta
LEMMY_HOSTNAME=lemmy-delta:8571 \ LEMMY_HOSTNAME=lemmy-delta:8571 \
LEMMY_PORT=8571 \ LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_delta.hjson \
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_delta" \ LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_delta" \
LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-beta \ target/lemmy_server >/tmp/lemmy_delta.out 2>&1 &
LEMMY_SETUP__ADMIN_USERNAME=lemmy_delta \
LEMMY_SETUP__SITE_NAME=lemmy-delta \
target/lemmy_server >/dev/null 2>&1 &
echo "start epsilon" echo "start epsilon"
# An instance who has a blocklist, with lemmy-alpha blocked # An instance who has a blocklist, with lemmy-alpha blocked
LEMMY_HOSTNAME=lemmy-epsilon:8581 \ LEMMY_HOSTNAME=lemmy-epsilon:8581 \
LEMMY_PORT=8581 \ LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_epsilon.hjson \
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_epsilon" \ LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_epsilon" \
LEMMY_FEDERATION__BLOCKED_INSTANCES=lemmy-alpha \ target/lemmy_server >/tmp/lemmy_epsilon.out 2>&1 &
LEMMY_SETUP__ADMIN_USERNAME=lemmy_epsilon \
LEMMY_SETUP__SITE_NAME=lemmy-epsilon \
target/lemmy_server >/dev/null 2>&1 &
echo "wait for all instances to start" echo "wait for all instances to start"
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8541/api/v2/site')" != "200" ]]; do sleep 1; done while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8541/api/v2/site')" != "200" ]]; do sleep 1; done

View file

@ -4,7 +4,7 @@ set -e
export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432 export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432
pushd .. pushd ..
cargo +1.47.0 build cargo build
rm target/lemmy_server || true rm target/lemmy_server || true
cp target/debug/lemmy_server target/lemmy_server cp target/debug/lemmy_server target/lemmy_server
./api_tests/prepare-drone-federation-test.sh ./api_tests/prepare-drone-federation-test.sh

View file

@ -66,9 +66,10 @@
# https://join.lemmy.ml/docs/en/federation/administration.html#instance-allowlist-and-blocklist # https://join.lemmy.ml/docs/en/federation/administration.html#instance-allowlist-and-blocklist
# #
# comma separated list of instances with which federation is allowed # comma separated list of instances with which federation is allowed
allowed_instances: "" # Only one of these blocks should be uncommented
# allowed_instances: ["instance1.tld","instance2.tld"]
# comma separated list of instances which are blocked from federating # comma separated list of instances which are blocked from federating
blocked_instances: "" # blocked_instances: []
} }
captcha: { captcha: {
enabled: true enabled: true

View file

@ -1,12 +1,12 @@
[package] [package]
name = "lemmy_api" name = "lemmy_api"
version = "0.1.0" version = "0.1.0"
authors = ["Felix Ableitner <me@nutomic.com>"]
edition = "2018" edition = "2018"
[lib] [lib]
name = "lemmy_api" name = "lemmy_api"
path = "src/lib.rs" path = "src/lib.rs"
doctest = false
[dependencies] [dependencies]
lemmy_apub = { path = "../apub" } lemmy_apub = { path = "../apub" }
@ -16,7 +16,7 @@ lemmy_db_schema = { path = "../db_schema" }
lemmy_db_views = { path = "../db_views" } lemmy_db_views = { path = "../db_views" }
lemmy_db_views_moderator = { path = "../db_views_moderator" } lemmy_db_views_moderator = { path = "../db_views_moderator" }
lemmy_db_views_actor = { path = "../db_views_actor" } lemmy_db_views_actor = { path = "../db_views_actor" }
lemmy_structs = { path = "../structs" } lemmy_api_structs = { path = "../api_structs" }
lemmy_websocket = { path = "../websocket" } lemmy_websocket = { path = "../websocket" }
diesel = "1.4.5" diesel = "1.4.5"
bcrypt = "0.9.0" bcrypt = "0.9.0"
@ -32,7 +32,7 @@ rand = "0.8.3"
strum = "0.20.0" strum = "0.20.0"
strum_macros = "0.20.1" strum_macros = "0.20.1"
lazy_static = "1.4.0" lazy_static = "1.4.0"
url = { version = "2.2.0", features = ["serde"] } url = { version = "2.2.1", features = ["serde"] }
openssl = "0.10.32" openssl = "0.10.32"
http = "0.2.3" http = "0.2.3"
http-signature-normalization-actix = { version = "0.4.1", default-features = false, features = ["sha-2"] } http-signature-normalization-actix = { version = "0.4.1", default-features = false, features = ["sha-2"] }

View file

@ -3,12 +3,13 @@ use crate::{
check_downvotes_enabled, check_downvotes_enabled,
collect_moderated_communities, collect_moderated_communities,
get_post, get_post,
get_user_from_jwt, get_local_user_view_from_jwt,
get_user_from_jwt_opt, get_local_user_view_from_jwt_opt,
is_mod_or_admin, is_mod_or_admin,
Perform, Perform,
}; };
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_structs::{blocking, comment::*, send_local_notifs};
use lemmy_apub::{generate_apub_endpoint, ApubLikeableType, ApubObjectType, EndpointType}; use lemmy_apub::{generate_apub_endpoint, ApubLikeableType, ApubObjectType, EndpointType};
use lemmy_db_queries::{ use lemmy_db_queries::{
source::comment::Comment_, source::comment::Comment_,
@ -24,7 +25,6 @@ use lemmy_db_views::{
comment_report_view::{CommentReportQueryBuilder, CommentReportView}, comment_report_view::{CommentReportQueryBuilder, CommentReportView},
comment_view::{CommentQueryBuilder, CommentView}, comment_view::{CommentQueryBuilder, CommentView},
}; };
use lemmy_structs::{blocking, comment::*, send_local_notifs};
use lemmy_utils::{ use lemmy_utils::{
utils::{remove_slurs, scrape_text_for_mentions}, utils::{remove_slurs, scrape_text_for_mentions},
ApiError, ApiError,
@ -48,7 +48,7 @@ impl Perform for CreateComment {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<CommentResponse, LemmyError> { ) -> Result<CommentResponse, LemmyError> {
let data: &CreateComment = &self; let data: &CreateComment = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let content_slurs_removed = remove_slurs(&data.content.to_owned()); let content_slurs_removed = remove_slurs(&data.content.to_owned());
@ -56,7 +56,7 @@ impl Perform for CreateComment {
let post_id = data.post_id; let post_id = data.post_id;
let post = get_post(post_id, context.pool()).await?; let post = get_post(post_id, context.pool()).await?;
check_community_ban(user.id, post.community_id, context.pool()).await?; check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?;
// Check if post is locked, no new comments // Check if post is locked, no new comments
if post.locked { if post.locked {
@ -80,7 +80,7 @@ impl Perform for CreateComment {
content: content_slurs_removed, content: content_slurs_removed,
parent_id: data.parent_id.to_owned(), parent_id: data.parent_id.to_owned(),
post_id: data.post_id, post_id: data.post_id,
creator_id: user.id, creator_id: local_user_view.person.id,
removed: None, removed: None,
deleted: None, deleted: None,
read: None, read: None,
@ -115,7 +115,7 @@ impl Perform for CreateComment {
Err(_e) => return Err(ApiError::err("couldnt_create_comment").into()), Err(_e) => return Err(ApiError::err("couldnt_create_comment").into()),
}; };
updated_comment.send_create(&user, context).await?; updated_comment.send_create(&local_user_view.person, context).await?;
// Scan the comment for user mentions, add those rows // Scan the comment for user mentions, add those rows
let post_id = post.id; let post_id = post.id;
@ -123,7 +123,7 @@ impl Perform for CreateComment {
let recipient_ids = send_local_notifs( let recipient_ids = send_local_notifs(
mentions, mentions,
updated_comment.clone(), updated_comment.clone(),
&user, local_user_view.person.clone(),
post, post,
context.pool(), context.pool(),
true, true,
@ -134,7 +134,7 @@ impl Perform for CreateComment {
let like_form = CommentLikeForm { let like_form = CommentLikeForm {
comment_id: inserted_comment.id, comment_id: inserted_comment.id,
post_id, post_id,
person_id: user.id, person_id: local_user_view.person.id,
score: 1, score: 1,
}; };
@ -143,17 +143,17 @@ impl Perform for CreateComment {
return Err(ApiError::err("couldnt_like_comment").into()); return Err(ApiError::err("couldnt_like_comment").into());
} }
updated_comment.send_like(&user, context).await?; updated_comment.send_like(&local_user_view.person, context).await?;
let user_id = user.id; let person_id = local_user_view.person.id;
let mut comment_view = blocking(context.pool(), move |conn| { let mut comment_view = blocking(context.pool(), move |conn| {
CommentView::read(&conn, inserted_comment.id, Some(user_id)) CommentView::read(&conn, inserted_comment.id, Some(person_id))
}) })
.await??; .await??;
// If its a comment to yourself, mark it as read // If its a comment to yourself, mark it as read
let comment_id = comment_view.comment.id; let comment_id = comment_view.comment.id;
if user.id == comment_view.get_recipient_id() { if local_user_view.person.id == comment_view.get_recipient_id() {
match blocking(context.pool(), move |conn| { match blocking(context.pool(), move |conn| {
Comment::update_read(conn, comment_id, true) Comment::update_read(conn, comment_id, true)
}) })
@ -193,7 +193,7 @@ impl Perform for EditComment {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<CommentResponse, LemmyError> { ) -> Result<CommentResponse, LemmyError> {
let data: &EditComment = &self; let data: &EditComment = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let comment_id = data.comment_id; let comment_id = data.comment_id;
let orig_comment = blocking(context.pool(), move |conn| { let orig_comment = blocking(context.pool(), move |conn| {
@ -201,10 +201,10 @@ impl Perform for EditComment {
}) })
.await??; .await??;
check_community_ban(user.id, orig_comment.community.id, context.pool()).await?; check_community_ban(local_user_view.person.id, orig_comment.community.id, context.pool()).await?;
// Verify that only the creator can edit // Verify that only the creator can edit
if user.id != orig_comment.creator.id { if local_user_view.person.id != orig_comment.creator.id {
return Err(ApiError::err("no_comment_edit_allowed").into()); return Err(ApiError::err("no_comment_edit_allowed").into());
} }
@ -221,7 +221,7 @@ impl Perform for EditComment {
}; };
// Send the apub update // Send the apub update
updated_comment.send_update(&user, context).await?; updated_comment.send_update(&local_user_view.person, context).await?;
// Do the mentions / recipients // Do the mentions / recipients
let updated_comment_content = updated_comment.content.to_owned(); let updated_comment_content = updated_comment.content.to_owned();
@ -229,7 +229,7 @@ impl Perform for EditComment {
let recipient_ids = send_local_notifs( let recipient_ids = send_local_notifs(
mentions, mentions,
updated_comment, updated_comment,
&user, local_user_view.person.clone(),
orig_comment.post, orig_comment.post,
context.pool(), context.pool(),
false, false,
@ -237,9 +237,9 @@ impl Perform for EditComment {
.await?; .await?;
let comment_id = data.comment_id; let comment_id = data.comment_id;
let user_id = user.id; let person_id = local_user_view.person.id;
let comment_view = blocking(context.pool(), move |conn| { let comment_view = blocking(context.pool(), move |conn| {
CommentView::read(conn, comment_id, Some(user_id)) CommentView::read(conn, comment_id, Some(person_id))
}) })
.await??; .await??;
@ -269,7 +269,7 @@ impl Perform for DeleteComment {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<CommentResponse, LemmyError> { ) -> Result<CommentResponse, LemmyError> {
let data: &DeleteComment = &self; let data: &DeleteComment = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let comment_id = data.comment_id; let comment_id = data.comment_id;
let orig_comment = blocking(context.pool(), move |conn| { let orig_comment = blocking(context.pool(), move |conn| {
@ -277,10 +277,10 @@ impl Perform for DeleteComment {
}) })
.await??; .await??;
check_community_ban(user.id, orig_comment.community.id, context.pool()).await?; check_community_ban(local_user_view.person.id, orig_comment.community.id, context.pool()).await?;
// Verify that only the creator can delete // Verify that only the creator can delete
if user.id != orig_comment.creator.id { if local_user_view.person.id != orig_comment.creator.id {
return Err(ApiError::err("no_comment_edit_allowed").into()); return Err(ApiError::err("no_comment_edit_allowed").into());
} }
@ -297,16 +297,16 @@ impl Perform for DeleteComment {
// Send the apub message // Send the apub message
if deleted { if deleted {
updated_comment.send_delete(&user, context).await?; updated_comment.send_delete(&local_user_view.person, context).await?;
} else { } else {
updated_comment.send_undo_delete(&user, context).await?; updated_comment.send_undo_delete(&local_user_view.person, context).await?;
} }
// Refetch it // Refetch it
let comment_id = data.comment_id; let comment_id = data.comment_id;
let user_id = user.id; let person_id = local_user_view.person.id;
let comment_view = blocking(context.pool(), move |conn| { let comment_view = blocking(context.pool(), move |conn| {
CommentView::read(conn, comment_id, Some(user_id)) CommentView::read(conn, comment_id, Some(person_id))
}) })
.await??; .await??;
@ -316,7 +316,7 @@ impl Perform for DeleteComment {
let recipient_ids = send_local_notifs( let recipient_ids = send_local_notifs(
mentions, mentions,
updated_comment, updated_comment,
&user, local_user_view.person.clone(),
comment_view_2.post, comment_view_2.post,
context.pool(), context.pool(),
false, false,
@ -349,7 +349,7 @@ impl Perform for RemoveComment {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<CommentResponse, LemmyError> { ) -> Result<CommentResponse, LemmyError> {
let data: &RemoveComment = &self; let data: &RemoveComment = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let comment_id = data.comment_id; let comment_id = data.comment_id;
let orig_comment = blocking(context.pool(), move |conn| { let orig_comment = blocking(context.pool(), move |conn| {
@ -357,10 +357,10 @@ impl Perform for RemoveComment {
}) })
.await??; .await??;
check_community_ban(user.id, orig_comment.community.id, context.pool()).await?; check_community_ban(local_user_view.person.id, orig_comment.community.id, context.pool()).await?;
// Verify that only a mod or admin can remove // Verify that only a mod or admin can remove
is_mod_or_admin(context.pool(), user.id, orig_comment.community.id).await?; is_mod_or_admin(context.pool(), local_user_view.person.id, orig_comment.community.id).await?;
// Do the remove // Do the remove
let removed = data.removed; let removed = data.removed;
@ -375,7 +375,7 @@ impl Perform for RemoveComment {
// Mod tables // Mod tables
let form = ModRemoveCommentForm { let form = ModRemoveCommentForm {
mod_person_id: user.id, mod_person_id: local_user_view.person.id,
comment_id: data.comment_id, comment_id: data.comment_id,
removed: Some(removed), removed: Some(removed),
reason: data.reason.to_owned(), reason: data.reason.to_owned(),
@ -387,16 +387,16 @@ impl Perform for RemoveComment {
// Send the apub message // Send the apub message
if removed { if removed {
updated_comment.send_remove(&user, context).await?; updated_comment.send_remove(&local_user_view.person, context).await?;
} else { } else {
updated_comment.send_undo_remove(&user, context).await?; updated_comment.send_undo_remove(&local_user_view.person, context).await?;
} }
// Refetch it // Refetch it
let comment_id = data.comment_id; let comment_id = data.comment_id;
let user_id = user.id; let person_id = local_user_view.person.id;
let comment_view = blocking(context.pool(), move |conn| { let comment_view = blocking(context.pool(), move |conn| {
CommentView::read(conn, comment_id, Some(user_id)) CommentView::read(conn, comment_id, Some(person_id))
}) })
.await??; .await??;
@ -407,7 +407,7 @@ impl Perform for RemoveComment {
let recipient_ids = send_local_notifs( let recipient_ids = send_local_notifs(
mentions, mentions,
updated_comment, updated_comment,
&user, local_user_view.person.clone(),
comment_view_2.post, comment_view_2.post,
context.pool(), context.pool(),
false, false,
@ -440,7 +440,7 @@ impl Perform for MarkCommentAsRead {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<CommentResponse, LemmyError> { ) -> Result<CommentResponse, LemmyError> {
let data: &MarkCommentAsRead = &self; let data: &MarkCommentAsRead = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let comment_id = data.comment_id; let comment_id = data.comment_id;
let orig_comment = blocking(context.pool(), move |conn| { let orig_comment = blocking(context.pool(), move |conn| {
@ -448,10 +448,10 @@ impl Perform for MarkCommentAsRead {
}) })
.await??; .await??;
check_community_ban(user.id, orig_comment.community.id, context.pool()).await?; check_community_ban(local_user_view.person.id, orig_comment.community.id, context.pool()).await?;
// Verify that only the recipient can mark as read // Verify that only the recipient can mark as read
if user.id != orig_comment.get_recipient_id() { if local_user_view.person.id != orig_comment.get_recipient_id() {
return Err(ApiError::err("no_comment_edit_allowed").into()); return Err(ApiError::err("no_comment_edit_allowed").into());
} }
@ -468,9 +468,9 @@ impl Perform for MarkCommentAsRead {
// Refetch it // Refetch it
let comment_id = data.comment_id; let comment_id = data.comment_id;
let user_id = user.id; let person_id = local_user_view.person.id;
let comment_view = blocking(context.pool(), move |conn| { let comment_view = blocking(context.pool(), move |conn| {
CommentView::read(conn, comment_id, Some(user_id)) CommentView::read(conn, comment_id, Some(person_id))
}) })
.await??; .await??;
@ -494,11 +494,11 @@ impl Perform for SaveComment {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<CommentResponse, LemmyError> { ) -> Result<CommentResponse, LemmyError> {
let data: &SaveComment = &self; let data: &SaveComment = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let comment_saved_form = CommentSavedForm { let comment_saved_form = CommentSavedForm {
comment_id: data.comment_id, comment_id: data.comment_id,
person_id: user.id, person_id: local_user_view.person.id,
}; };
if data.save { if data.save {
@ -514,9 +514,9 @@ impl Perform for SaveComment {
} }
let comment_id = data.comment_id; let comment_id = data.comment_id;
let user_id = user.id; let person_id = local_user_view.person.id;
let comment_view = blocking(context.pool(), move |conn| { let comment_view = blocking(context.pool(), move |conn| {
CommentView::read(conn, comment_id, Some(user_id)) CommentView::read(conn, comment_id, Some(person_id))
}) })
.await??; .await??;
@ -538,7 +538,7 @@ impl Perform for CreateCommentLike {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<CommentResponse, LemmyError> { ) -> Result<CommentResponse, LemmyError> {
let data: &CreateCommentLike = &self; let data: &CreateCommentLike = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let mut recipient_ids = Vec::new(); let mut recipient_ids = Vec::new();
@ -551,7 +551,7 @@ impl Perform for CreateCommentLike {
}) })
.await??; .await??;
check_community_ban(user.id, orig_comment.community.id, context.pool()).await?; check_community_ban(local_user_view.person.id, orig_comment.community.id, context.pool()).await?;
// Add parent user to recipients // Add parent user to recipients
recipient_ids.push(orig_comment.get_recipient_id()); recipient_ids.push(orig_comment.get_recipient_id());
@ -559,14 +559,14 @@ impl Perform for CreateCommentLike {
let like_form = CommentLikeForm { let like_form = CommentLikeForm {
comment_id: data.comment_id, comment_id: data.comment_id,
post_id: orig_comment.post.id, post_id: orig_comment.post.id,
person_id: user.id, person_id: local_user_view.person.id,
score: data.score, score: data.score,
}; };
// Remove any likes first // Remove any likes first
let user_id = user.id; let person_id = local_user_view.person.id;
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
CommentLike::remove(conn, user_id, comment_id) CommentLike::remove(conn, person_id, comment_id)
}) })
.await??; .await??;
@ -581,19 +581,19 @@ impl Perform for CreateCommentLike {
} }
if like_form.score == 1 { if like_form.score == 1 {
comment.send_like(&user, context).await?; comment.send_like(&local_user_view.person, context).await?;
} else if like_form.score == -1 { } else if like_form.score == -1 {
comment.send_dislike(&user, context).await?; comment.send_dislike(&local_user_view.person, context).await?;
} }
} else { } else {
comment.send_undo_like(&user, context).await?; comment.send_undo_like(&local_user_view.person, context).await?;
} }
// Have to refetch the comment to get the current state // Have to refetch the comment to get the current state
let comment_id = data.comment_id; let comment_id = data.comment_id;
let user_id = user.id; let person_id = local_user_view.person.id;
let liked_comment = blocking(context.pool(), move |conn| { let liked_comment = blocking(context.pool(), move |conn| {
CommentView::read(conn, comment_id, Some(user_id)) CommentView::read(conn, comment_id, Some(person_id))
}) })
.await??; .await??;
@ -623,8 +623,8 @@ impl Perform for GetComments {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<GetCommentsResponse, LemmyError> { ) -> Result<GetCommentsResponse, LemmyError> {
let data: &GetComments = &self; let data: &GetComments = &self;
let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
let user_id = user.map(|u| u.id); let person_id = local_user_view.map(|u| u.person.id);
let type_ = ListingType::from_str(&data.type_)?; let type_ = ListingType::from_str(&data.type_)?;
let sort = SortType::from_str(&data.sort)?; let sort = SortType::from_str(&data.sort)?;
@ -639,7 +639,7 @@ impl Perform for GetComments {
.sort(&sort) .sort(&sort)
.community_id(community_id) .community_id(community_id)
.community_name(community_name) .community_name(community_name)
.my_user_id(user_id) .my_person_id(person_id)
.page(page) .page(page)
.limit(limit) .limit(limit)
.list() .list()
@ -665,7 +665,7 @@ impl Perform for CreateCommentReport {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<CreateCommentReportResponse, LemmyError> { ) -> Result<CreateCommentReportResponse, LemmyError> {
let data: &CreateCommentReport = &self; let data: &CreateCommentReport = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
// check size of report and check for whitespace // check size of report and check for whitespace
let reason = data.reason.trim(); let reason = data.reason.trim();
@ -676,17 +676,17 @@ impl Perform for CreateCommentReport {
return Err(ApiError::err("report_too_long").into()); return Err(ApiError::err("report_too_long").into());
} }
let user_id = user.id; let person_id = local_user_view.person.id;
let comment_id = data.comment_id; let comment_id = data.comment_id;
let comment_view = blocking(context.pool(), move |conn| { let comment_view = blocking(context.pool(), move |conn| {
CommentView::read(&conn, comment_id, None) CommentView::read(&conn, comment_id, None)
}) })
.await??; .await??;
check_community_ban(user_id, comment_view.community.id, context.pool()).await?; check_community_ban(person_id, comment_view.community.id, context.pool()).await?;
let report_form = CommentReportForm { let report_form = CommentReportForm {
creator_id: user_id, creator_id: person_id,
comment_id, comment_id,
original_comment_text: comment_view.comment.content, original_comment_text: comment_view.comment.content,
reason: data.reason.to_owned(), reason: data.reason.to_owned(),
@ -706,7 +706,7 @@ impl Perform for CreateCommentReport {
context.chat_server().do_send(SendUserRoomMessage { context.chat_server().do_send(SendUserRoomMessage {
op: UserOperation::CreateCommentReport, op: UserOperation::CreateCommentReport,
response: res.clone(), response: res.clone(),
recipient_id: user.id, recipient_id: local_user_view.person.id,
websocket_id, websocket_id,
}); });
@ -732,7 +732,7 @@ impl Perform for ResolveCommentReport {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<ResolveCommentReportResponse, LemmyError> { ) -> Result<ResolveCommentReportResponse, LemmyError> {
let data: &ResolveCommentReport = &self; let data: &ResolveCommentReport = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let report_id = data.report_id; let report_id = data.report_id;
let report = blocking(context.pool(), move |conn| { let report = blocking(context.pool(), move |conn| {
@ -740,15 +740,15 @@ impl Perform for ResolveCommentReport {
}) })
.await??; .await??;
let user_id = user.id; let person_id = local_user_view.person.id;
is_mod_or_admin(context.pool(), user_id, report.community.id).await?; is_mod_or_admin(context.pool(), person_id, report.community.id).await?;
let resolved = data.resolved; let resolved = data.resolved;
let resolve_fun = move |conn: &'_ _| { let resolve_fun = move |conn: &'_ _| {
if resolved { if resolved {
CommentReport::resolve(conn, report_id, user_id) CommentReport::resolve(conn, report_id, person_id)
} else { } else {
CommentReport::unresolve(conn, report_id, user_id) CommentReport::unresolve(conn, report_id, person_id)
} }
}; };
@ -785,12 +785,12 @@ impl Perform for ListCommentReports {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<ListCommentReportsResponse, LemmyError> { ) -> Result<ListCommentReportsResponse, LemmyError> {
let data: &ListCommentReports = &self; let data: &ListCommentReports = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let user_id = user.id; let person_id = local_user_view.person.id;
let community_id = data.community; let community_id = data.community;
let community_ids = let community_ids =
collect_moderated_communities(user_id, community_id, context.pool()).await?; collect_moderated_communities(person_id, community_id, context.pool()).await?;
let page = data.page; let page = data.page;
let limit = data.limit; let limit = data.limit;
@ -808,7 +808,7 @@ impl Perform for ListCommentReports {
context.chat_server().do_send(SendUserRoomMessage { context.chat_server().do_send(SendUserRoomMessage {
op: UserOperation::ListCommentReports, op: UserOperation::ListCommentReports,
response: res.clone(), response: res.clone(),
recipient_id: user.id, recipient_id: local_user_view.person.id,
websocket_id, websocket_id,
}); });

View file

@ -1,14 +1,14 @@
use crate::{ use crate::{
check_community_ban, check_community_ban,
check_optional_url, get_local_user_view_from_jwt,
get_user_from_jwt, get_local_user_view_from_jwt_opt,
get_user_from_jwt_opt,
is_admin, is_admin,
is_mod_or_admin, is_mod_or_admin,
Perform, Perform,
}; };
use actix_web::web::Data; use actix_web::web::Data;
use anyhow::Context; use anyhow::Context;
use lemmy_api_structs::{blocking, community::*};
use lemmy_apub::{ use lemmy_apub::{
generate_apub_endpoint, generate_apub_endpoint,
generate_followers_url, generate_followers_url,
@ -18,7 +18,7 @@ use lemmy_apub::{
EndpointType, EndpointType,
}; };
use lemmy_db_queries::{ use lemmy_db_queries::{
diesel_option_overwrite, diesel_option_overwrite_to_url,
source::{ source::{
comment::Comment_, comment::Comment_,
community::{CommunityModerator_, Community_}, community::{CommunityModerator_, Community_},
@ -41,9 +41,8 @@ use lemmy_db_views_actor::{
community_follower_view::CommunityFollowerView, community_follower_view::CommunityFollowerView,
community_moderator_view::CommunityModeratorView, community_moderator_view::CommunityModeratorView,
community_view::{CommunityQueryBuilder, CommunityView}, community_view::{CommunityQueryBuilder, CommunityView},
user_view::UserViewSafe, person_view::PersonViewSafe,
}; };
use lemmy_structs::{blocking, community::*};
use lemmy_utils::{ use lemmy_utils::{
apub::generate_actor_keypair, apub::generate_actor_keypair,
location_info, location_info,
@ -69,8 +68,8 @@ impl Perform for GetCommunity {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<GetCommunityResponse, LemmyError> { ) -> Result<GetCommunityResponse, LemmyError> {
let data: &GetCommunity = &self; let data: &GetCommunity = &self;
let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
let user_id = user.map(|u| u.id); let person_id = local_user_view.map(|u| u.person.id);
let community_id = match data.id { let community_id = match data.id {
Some(id) => id, Some(id) => id,
@ -89,7 +88,7 @@ impl Perform for GetCommunity {
}; };
let community_view = match blocking(context.pool(), move |conn| { let community_view = match blocking(context.pool(), move |conn| {
CommunityView::read(conn, community_id, user_id) CommunityView::read(conn, community_id, person_id)
}) })
.await? .await?
{ {
@ -133,7 +132,7 @@ impl Perform for CreateCommunity {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<CommunityResponse, LemmyError> { ) -> Result<CommunityResponse, LemmyError> {
let data: &CreateCommunity = &self; let data: &CreateCommunity = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
check_slurs(&data.name)?; check_slurs(&data.name)?;
check_slurs(&data.title)?; check_slurs(&data.title)?;
@ -155,11 +154,8 @@ impl Perform for CreateCommunity {
} }
// Check to make sure the icon and banners are urls // Check to make sure the icon and banners are urls
let icon = diesel_option_overwrite(&data.icon); let icon = diesel_option_overwrite_to_url(&data.icon)?;
let banner = diesel_option_overwrite(&data.banner); let banner = diesel_option_overwrite_to_url(&data.banner)?;
check_optional_url(&icon)?;
check_optional_url(&banner)?;
// When you create a community, make sure the user becomes a moderator and a follower // When you create a community, make sure the user becomes a moderator and a follower
let keypair = generate_actor_keypair()?; let keypair = generate_actor_keypair()?;
@ -170,7 +166,7 @@ impl Perform for CreateCommunity {
description: data.description.to_owned(), description: data.description.to_owned(),
icon, icon,
banner, banner,
creator_id: user.id, creator_id: local_user_view.person.id,
removed: None, removed: None,
deleted: None, deleted: None,
nsfw: data.nsfw, nsfw: data.nsfw,
@ -198,7 +194,7 @@ impl Perform for CreateCommunity {
// The community creator becomes a moderator // The community creator becomes a moderator
let community_moderator_form = CommunityModeratorForm { let community_moderator_form = CommunityModeratorForm {
community_id: inserted_community.id, community_id: inserted_community.id,
person_id: user.id, person_id: local_user_view.person.id,
}; };
let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form); let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
@ -209,7 +205,7 @@ impl Perform for CreateCommunity {
// Follow your own community // Follow your own community
let community_follower_form = CommunityFollowerForm { let community_follower_form = CommunityFollowerForm {
community_id: inserted_community.id, community_id: inserted_community.id,
person_id: user.id, person_id: local_user_view.person.id,
pending: false, pending: false,
}; };
@ -218,9 +214,9 @@ impl Perform for CreateCommunity {
return Err(ApiError::err("community_follower_already_exists").into()); return Err(ApiError::err("community_follower_already_exists").into());
} }
let user_id = user.id; let person_id = local_user_view.person.id;
let community_view = blocking(context.pool(), move |conn| { let community_view = blocking(context.pool(), move |conn| {
CommunityView::read(conn, inserted_community.id, Some(user_id)) CommunityView::read(conn, inserted_community.id, Some(person_id))
}) })
.await??; .await??;
@ -238,7 +234,7 @@ impl Perform for EditCommunity {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<CommunityResponse, LemmyError> { ) -> Result<CommunityResponse, LemmyError> {
let data: &EditCommunity = &self; let data: &EditCommunity = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
check_slurs(&data.title)?; check_slurs(&data.title)?;
check_slurs_opt(&data.description)?; check_slurs_opt(&data.description)?;
@ -250,7 +246,7 @@ impl Perform for EditCommunity {
.map(|v| v.into_iter().map(|m| m.moderator.id).collect()) .map(|v| v.into_iter().map(|m| m.moderator.id).collect())
}) })
.await??; .await??;
if !mods.contains(&user.id) { if !mods.contains(&local_user_view.person.id) {
return Err(ApiError::err("not_a_moderator").into()); return Err(ApiError::err("not_a_moderator").into());
} }
@ -260,11 +256,8 @@ impl Perform for EditCommunity {
}) })
.await??; .await??;
let icon = diesel_option_overwrite(&data.icon); let icon = diesel_option_overwrite_to_url(&data.icon)?;
let banner = diesel_option_overwrite(&data.banner); let banner = diesel_option_overwrite_to_url(&data.banner)?;
check_optional_url(&icon)?;
check_optional_url(&banner)?;
let community_form = CommunityForm { let community_form = CommunityForm {
name: read_community.name, name: read_community.name,
@ -302,9 +295,9 @@ impl Perform for EditCommunity {
// process for communities and users // process for communities and users
let community_id = data.community_id; let community_id = data.community_id;
let user_id = user.id; let person_id = local_user_view.person.id;
let community_view = blocking(context.pool(), move |conn| { let community_view = blocking(context.pool(), move |conn| {
CommunityView::read(conn, community_id, Some(user_id)) CommunityView::read(conn, community_id, Some(person_id))
}) })
.await??; .await??;
@ -326,7 +319,7 @@ impl Perform for DeleteCommunity {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<CommunityResponse, LemmyError> { ) -> Result<CommunityResponse, LemmyError> {
let data: &DeleteCommunity = &self; let data: &DeleteCommunity = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
// Verify its the creator (only a creator can delete the community) // Verify its the creator (only a creator can delete the community)
let community_id = data.community_id; let community_id = data.community_id;
@ -334,7 +327,7 @@ impl Perform for DeleteCommunity {
Community::read(conn, community_id) Community::read(conn, community_id)
}) })
.await??; .await??;
if read_community.creator_id != user.id { if read_community.creator_id != local_user_view.person.id {
return Err(ApiError::err("no_community_edit_allowed").into()); return Err(ApiError::err("no_community_edit_allowed").into());
} }
@ -358,9 +351,9 @@ impl Perform for DeleteCommunity {
} }
let community_id = data.community_id; let community_id = data.community_id;
let user_id = user.id; let person_id = local_user_view.person.id;
let community_view = blocking(context.pool(), move |conn| { let community_view = blocking(context.pool(), move |conn| {
CommunityView::read(conn, community_id, Some(user_id)) CommunityView::read(conn, community_id, Some(person_id))
}) })
.await??; .await??;
@ -382,10 +375,10 @@ impl Perform for RemoveCommunity {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<CommunityResponse, LemmyError> { ) -> Result<CommunityResponse, LemmyError> {
let data: &RemoveCommunity = &self; let data: &RemoveCommunity = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
// Verify its an admin (only an admin can remove a community) // Verify its an admin (only an admin can remove a community)
is_admin(context.pool(), user.id).await?; is_admin(&local_user_view)?;
// Do the remove // Do the remove
let community_id = data.community_id; let community_id = data.community_id;
@ -405,7 +398,7 @@ impl Perform for RemoveCommunity {
None => None, None => None,
}; };
let form = ModRemoveCommunityForm { let form = ModRemoveCommunityForm {
mod_person_id: user.id, mod_person_id: local_user_view.person.id,
community_id: data.community_id, community_id: data.community_id,
removed: Some(removed), removed: Some(removed),
reason: data.reason.to_owned(), reason: data.reason.to_owned(),
@ -424,9 +417,9 @@ impl Perform for RemoveCommunity {
} }
let community_id = data.community_id; let community_id = data.community_id;
let user_id = user.id; let person_id = local_user_view.person.id;
let community_view = blocking(context.pool(), move |conn| { let community_view = blocking(context.pool(), move |conn| {
CommunityView::read(conn, community_id, Some(user_id)) CommunityView::read(conn, community_id, Some(person_id))
}) })
.await??; .await??;
@ -448,15 +441,16 @@ impl Perform for ListCommunities {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<ListCommunitiesResponse, LemmyError> { ) -> Result<ListCommunitiesResponse, LemmyError> {
let data: &ListCommunities = &self; let data: &ListCommunities = &self;
let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
let user_id = match &user { let person_id = match &local_user_view {
Some(user) => Some(user.id), Some(uv) => Some(uv.person.id),
None => None, None => None,
}; };
let show_nsfw = match &user { // Don't show NSFW by default
Some(user) => user.show_nsfw, let show_nsfw = match &local_user_view {
Some(uv) => uv.local_user.show_nsfw,
None => false, None => false,
}; };
@ -470,7 +464,7 @@ impl Perform for ListCommunities {
.listing_type(&type_) .listing_type(&type_)
.sort(&sort) .sort(&sort)
.show_nsfw(show_nsfw) .show_nsfw(show_nsfw)
.my_user_id(user_id) .my_person_id(person_id)
.page(page) .page(page)
.limit(limit) .limit(limit)
.list() .list()
@ -492,7 +486,7 @@ impl Perform for FollowCommunity {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<CommunityResponse, LemmyError> { ) -> Result<CommunityResponse, LemmyError> {
let data: &FollowCommunity = &self; let data: &FollowCommunity = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let community_id = data.community_id; let community_id = data.community_id;
let community = blocking(context.pool(), move |conn| { let community = blocking(context.pool(), move |conn| {
@ -501,13 +495,13 @@ impl Perform for FollowCommunity {
.await??; .await??;
let community_follower_form = CommunityFollowerForm { let community_follower_form = CommunityFollowerForm {
community_id: data.community_id, community_id: data.community_id,
person_id: user.id, person_id: local_user_view.person.id,
pending: false, pending: false,
}; };
if community.local { if community.local {
if data.follow { if data.follow {
check_community_ban(user.id, community_id, context.pool()).await?; check_community_ban(local_user_view.person.id, community_id, context.pool()).await?;
let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form); let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
if blocking(context.pool(), follow).await?.is_err() { if blocking(context.pool(), follow).await?.is_err() {
@ -523,9 +517,9 @@ impl Perform for FollowCommunity {
} else if data.follow { } else if data.follow {
// Dont actually add to the community followers here, because you need // Dont actually add to the community followers here, because you need
// to wait for the accept // to wait for the accept
user.send_follow(&community.actor_id(), context).await?; local_user_view.person.send_follow(&community.actor_id(), context).await?;
} else { } else {
user.send_unfollow(&community.actor_id(), context).await?; local_user_view.person.send_unfollow(&community.actor_id(), context).await?;
let unfollow = move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form); let unfollow = move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form);
if blocking(context.pool(), unfollow).await?.is_err() { if blocking(context.pool(), unfollow).await?.is_err() {
return Err(ApiError::err("community_follower_already_exists").into()); return Err(ApiError::err("community_follower_already_exists").into());
@ -533,9 +527,9 @@ impl Perform for FollowCommunity {
} }
let community_id = data.community_id; let community_id = data.community_id;
let user_id = user.id; let person_id = local_user_view.person.id;
let mut community_view = blocking(context.pool(), move |conn| { let mut community_view = blocking(context.pool(), move |conn| {
CommunityView::read(conn, community_id, Some(user_id)) CommunityView::read(conn, community_id, Some(person_id))
}) })
.await??; .await??;
@ -560,11 +554,11 @@ impl Perform for GetFollowedCommunities {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<GetFollowedCommunitiesResponse, LemmyError> { ) -> Result<GetFollowedCommunitiesResponse, LemmyError> {
let data: &GetFollowedCommunities = &self; let data: &GetFollowedCommunities = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let user_id = user.id; let person_id = local_user_view.person.id;
let communities = match blocking(context.pool(), move |conn| { let communities = match blocking(context.pool(), move |conn| {
CommunityFollowerView::for_user(conn, user_id) CommunityFollowerView::for_person(conn, person_id)
}) })
.await? .await?
{ {
@ -587,17 +581,17 @@ impl Perform for BanFromCommunity {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<BanFromCommunityResponse, LemmyError> { ) -> Result<BanFromCommunityResponse, LemmyError> {
let data: &BanFromCommunity = &self; let data: &BanFromCommunity = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let community_id = data.community_id; let community_id = data.community_id;
let banned_user_id = data.user_id; let banned_person_id = data.person_id;
// Verify that only mods or admins can ban // Verify that only mods or admins can ban
is_mod_or_admin(context.pool(), user.id, community_id).await?; is_mod_or_admin(context.pool(), local_user_view.person.id, community_id).await?;
let community_user_ban_form = CommunityPersonBanForm { let community_user_ban_form = CommunityPersonBanForm {
community_id: data.community_id, community_id: data.community_id,
person_id: data.user_id, person_id: data.person_id,
}; };
if data.ban { if data.ban {
@ -609,7 +603,7 @@ impl Perform for BanFromCommunity {
// Also unsubscribe them from the community, if they are subscribed // Also unsubscribe them from the community, if they are subscribed
let community_follower_form = CommunityFollowerForm { let community_follower_form = CommunityFollowerForm {
community_id: data.community_id, community_id: data.community_id,
person_id: banned_user_id, person_id: banned_person_id,
pending: false, pending: false,
}; };
blocking(context.pool(), move |conn: &'_ _| { blocking(context.pool(), move |conn: &'_ _| {
@ -628,7 +622,7 @@ impl Perform for BanFromCommunity {
if data.remove_data { if data.remove_data {
// Posts // Posts
blocking(context.pool(), move |conn: &'_ _| { blocking(context.pool(), move |conn: &'_ _| {
Post::update_removed_for_creator(conn, banned_user_id, Some(community_id), true) Post::update_removed_for_creator(conn, banned_person_id, Some(community_id), true)
}) })
.await??; .await??;
@ -636,7 +630,7 @@ impl Perform for BanFromCommunity {
// TODO Diesel doesn't allow updates with joins, so this has to be a loop // TODO Diesel doesn't allow updates with joins, so this has to be a loop
let comments = blocking(context.pool(), move |conn| { let comments = blocking(context.pool(), move |conn| {
CommentQueryBuilder::create(conn) CommentQueryBuilder::create(conn)
.creator_id(banned_user_id) .creator_id(banned_person_id)
.community_id(community_id) .community_id(community_id)
.limit(std::i64::MAX) .limit(std::i64::MAX)
.list() .list()
@ -660,8 +654,8 @@ impl Perform for BanFromCommunity {
}; };
let form = ModBanFromCommunityForm { let form = ModBanFromCommunityForm {
mod_person_id: user.id, mod_person_id: local_user_view.person.id,
other_person_id: data.user_id, other_person_id: data.person_id,
community_id: data.community_id, community_id: data.community_id,
reason: data.reason.to_owned(), reason: data.reason.to_owned(),
banned: Some(data.ban), banned: Some(data.ban),
@ -672,14 +666,14 @@ impl Perform for BanFromCommunity {
}) })
.await??; .await??;
let user_id = data.user_id; let person_id = data.person_id;
let user_view = blocking(context.pool(), move |conn| { let person_view = blocking(context.pool(), move |conn| {
UserViewSafe::read(conn, user_id) PersonViewSafe::read(conn, person_id)
}) })
.await??; .await??;
let res = BanFromCommunityResponse { let res = BanFromCommunityResponse {
user_view, person_view,
banned: data.ban, banned: data.ban,
}; };
@ -704,17 +698,17 @@ impl Perform for AddModToCommunity {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<AddModToCommunityResponse, LemmyError> { ) -> Result<AddModToCommunityResponse, LemmyError> {
let data: &AddModToCommunity = &self; let data: &AddModToCommunity = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let community_moderator_form = CommunityModeratorForm { let community_moderator_form = CommunityModeratorForm {
community_id: data.community_id, community_id: data.community_id,
person_id: data.user_id, person_id: data.person_id,
}; };
let community_id = data.community_id; let community_id = data.community_id;
// Verify that only mods or admins can add mod // Verify that only mods or admins can add mod
is_mod_or_admin(context.pool(), user.id, community_id).await?; is_mod_or_admin(context.pool(), local_user_view.person.id, community_id).await?;
if data.added { if data.added {
let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form); let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
@ -730,8 +724,8 @@ impl Perform for AddModToCommunity {
// Mod tables // Mod tables
let form = ModAddCommunityForm { let form = ModAddCommunityForm {
mod_person_id: user.id, mod_person_id: local_user_view.person.id,
other_person_id: data.user_id, other_person_id: data.person_id,
community_id: data.community_id, community_id: data.community_id,
removed: Some(!data.added), removed: Some(!data.added),
}; };
@ -769,7 +763,7 @@ impl Perform for TransferCommunity {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<GetCommunityResponse, LemmyError> { ) -> Result<GetCommunityResponse, LemmyError> {
let data: &TransferCommunity = &self; let data: &TransferCommunity = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let community_id = data.community_id; let community_id = data.community_id;
let read_community = blocking(context.pool(), move |conn| { let read_community = blocking(context.pool(), move |conn| {
@ -782,25 +776,25 @@ impl Perform for TransferCommunity {
}) })
.await??; .await??;
let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??; let mut admins = blocking(context.pool(), move |conn| PersonViewSafe::admins(conn)).await??;
// Making sure the creator, if an admin, is at the top // Making sure the creator, if an admin, is at the top
let creator_index = admins let creator_index = admins
.iter() .iter()
.position(|r| r.user.id == site_creator_id) .position(|r| r.person.id == site_creator_id)
.context(location_info!())?; .context(location_info!())?;
let creator_user = admins.remove(creator_index); let creator_person = admins.remove(creator_index);
admins.insert(0, creator_user); admins.insert(0, creator_person);
// Make sure user is the creator, or an admin // Make sure user is the creator, or an admin
if user.id != read_community.creator_id if local_user_view.person.id != read_community.creator_id
&& !admins.iter().map(|a| a.user.id).any(|x| x == user.id) && !admins.iter().map(|a| a.person.id).any(|x| x == local_user_view.person.id)
{ {
return Err(ApiError::err("not_an_admin").into()); return Err(ApiError::err("not_an_admin").into());
} }
let community_id = data.community_id; let community_id = data.community_id;
let new_creator = data.user_id; let new_creator = data.person_id;
let update = move |conn: &'_ _| Community::update_creator(conn, community_id, new_creator); let update = move |conn: &'_ _| Community::update_creator(conn, community_id, new_creator);
if blocking(context.pool(), update).await?.is_err() { if blocking(context.pool(), update).await?.is_err() {
return Err(ApiError::err("couldnt_update_community").into()); return Err(ApiError::err("couldnt_update_community").into());
@ -814,10 +808,10 @@ impl Perform for TransferCommunity {
.await??; .await??;
let creator_index = community_mods let creator_index = community_mods
.iter() .iter()
.position(|r| r.moderator.id == data.user_id) .position(|r| r.moderator.id == data.person_id)
.context(location_info!())?; .context(location_info!())?;
let creator_user = community_mods.remove(creator_index); let creator_person = community_mods.remove(creator_index);
community_mods.insert(0, creator_user); community_mods.insert(0, creator_person);
let community_id = data.community_id; let community_id = data.community_id;
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
@ -840,8 +834,8 @@ impl Perform for TransferCommunity {
// Mod tables // Mod tables
let form = ModAddCommunityForm { let form = ModAddCommunityForm {
mod_person_id: user.id, mod_person_id: local_user_view.person.id,
other_person_id: data.user_id, other_person_id: data.person_id,
community_id: data.community_id, community_id: data.community_id,
removed: Some(false), removed: Some(false),
}; };
@ -851,9 +845,9 @@ impl Perform for TransferCommunity {
.await??; .await??;
let community_id = data.community_id; let community_id = data.community_id;
let user_id = user.id; let person_id = local_user_view.person.id;
let community_view = match blocking(context.pool(), move |conn| { let community_view = match blocking(context.pool(), move |conn| {
CommunityView::read(conn, community_id, Some(user_id)) CommunityView::read(conn, community_id, Some(person_id))
}) })
.await? .await?
{ {
@ -886,7 +880,7 @@ fn send_community_websocket(
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
op: UserOperation, op: UserOperation,
) { ) {
// Strip out the user id and subscribed when sending to others // Strip out the person id and subscribed when sending to others
let mut res_sent = res.clone(); let mut res_sent = res.clone();
res_sent.community_view.subscribed = false; res_sent.community_view.subscribed = false;

View file

@ -1,9 +1,19 @@
use actix_web::{web, web::Data}; use actix_web::{web, web::Data};
use lemmy_api_structs::{
blocking,
comment::*,
community::*,
post::*,
site::*,
person::*,
websocket::*,
};
use lemmy_db_queries::{ use lemmy_db_queries::{
source::{ source::{
community::{CommunityModerator_, Community_}, community::{CommunityModerator_, Community_},
site::Site_, site::Site_,
user::UserSafeSettings_, local_user::LocalUserSettings_,
local_user::LocalUser_,
}, },
Crud, Crud,
DbPool, DbPool,
@ -12,14 +22,22 @@ use lemmy_db_schema::source::{
community::{Community, CommunityModerator}, community::{Community, CommunityModerator},
post::Post, post::Post,
site::Site, site::Site,
user::{UserSafeSettings, User_}, person::{Person, PersonSafe},
local_user::LocalUserSettings,
local_user::LocalUser,
}; };
use lemmy_db_views_actor::{ use lemmy_db_views_actor::{
community_user_ban_view::CommunityUserBanView, community_person_ban_view::CommunityPersonBanView,
community_view::CommunityView, community_view::CommunityView,
}; };
use lemmy_structs::{blocking, comment::*, community::*, post::*, site::*, user::*, websocket::*}; use lemmy_db_views::local_user_view::{LocalUserView, LocalUserSettingsView};
use lemmy_utils::{claims::Claims, settings::Settings, ApiError, ConnectionId, LemmyError}; use lemmy_utils::{
claims::Claims,
settings::structs::Settings,
ApiError,
ConnectionId,
LemmyError,
};
use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation}; use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation};
use serde::Deserialize; use serde::Deserialize;
use std::process::Command; use std::process::Command;
@ -46,11 +64,11 @@ pub trait Perform {
pub(crate) async fn is_mod_or_admin( pub(crate) async fn is_mod_or_admin(
pool: &DbPool, pool: &DbPool,
user_id: i32, person_id: i32,
community_id: i32, community_id: i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let is_mod_or_admin = blocking(pool, move |conn| { let is_mod_or_admin = blocking(pool, move |conn| {
CommunityView::is_mod_or_admin(conn, user_id, community_id) CommunityView::is_mod_or_admin(conn, person_id, community_id)
}) })
.await?; .await?;
if !is_mod_or_admin { if !is_mod_or_admin {
@ -58,9 +76,18 @@ pub(crate) async fn is_mod_or_admin(
} }
Ok(()) Ok(())
} }
pub async fn is_admin(pool: &DbPool, user_id: i32) -> Result<(), LemmyError> {
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??; // TODO this probably isn't necessary anymore
if !user.admin { // pub async fn is_admin(pool: &DbPool, person_id: i32) -> Result<(), LemmyError> {
// let user = blocking(pool, move |conn| LocalUser::read(conn, person_id)).await??;
// if !user.admin {
// return Err(ApiError::err("not_an_admin").into());
// }
// Ok(())
// }
pub fn is_admin(local_user_view: &LocalUserView) -> Result<(), LemmyError> {
if !local_user_view.local_user.admin {
return Err(ApiError::err("not_an_admin").into()); return Err(ApiError::err("not_an_admin").into());
} }
Ok(()) Ok(())
@ -73,63 +100,60 @@ pub(crate) async fn get_post(post_id: i32, pool: &DbPool) -> Result<Post, LemmyE
} }
} }
pub(crate) async fn get_user_from_jwt(jwt: &str, pool: &DbPool) -> Result<User_, LemmyError> { pub(crate) async fn get_local_user_view_from_jwt(jwt: &str, pool: &DbPool) -> Result<LocalUserView, LemmyError> {
let claims = match Claims::decode(&jwt) { let claims = match Claims::decode(&jwt) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(ApiError::err("not_logged_in").into()), Err(_e) => return Err(ApiError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let person_id = claims.id;
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??; let local_user_view = blocking(pool, move |conn| LocalUserView::read(conn, person_id)).await??;
// Check for a site ban // Check for a site ban
if user.banned { if local_user_view.person.banned {
return Err(ApiError::err("site_ban").into()); return Err(ApiError::err("site_ban").into());
} }
Ok(user) Ok(local_user_view)
} }
pub(crate) async fn get_user_from_jwt_opt( pub(crate) async fn get_local_user_view_from_jwt_opt(
jwt: &Option<String>, jwt: &Option<String>,
pool: &DbPool, pool: &DbPool,
) -> Result<Option<User_>, LemmyError> { ) -> Result<Option<LocalUserView>, LemmyError> {
match jwt { match jwt {
Some(jwt) => Ok(Some(get_user_from_jwt(jwt, pool).await?)), Some(jwt) => Ok(Some(get_local_user_view_from_jwt(jwt, pool).await?)),
None => Ok(None), None => Ok(None),
} }
} }
pub(crate) async fn get_user_safe_settings_from_jwt( pub(crate) async fn get_local_user_settings_view_from_jwt(jwt: &str, pool: &DbPool) -> Result<LocalUserSettingsView, LemmyError> {
jwt: &str,
pool: &DbPool,
) -> Result<UserSafeSettings, LemmyError> {
let claims = match Claims::decode(&jwt) { let claims = match Claims::decode(&jwt) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(ApiError::err("not_logged_in").into()), Err(_e) => return Err(ApiError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let person_id = claims.id;
let user = blocking(pool, move |conn| UserSafeSettings::read(conn, user_id)).await??; let local_user_view = blocking(pool, move |conn| LocalUserSettingsView::read(conn, person_id)).await??;
// Check for a site ban // Check for a site ban
if user.banned { if local_user_view.person.banned {
return Err(ApiError::err("site_ban").into()); return Err(ApiError::err("site_ban").into());
} }
Ok(user) Ok(local_user_view)
} }
pub(crate) async fn get_user_safe_settings_from_jwt_opt( pub(crate) async fn get_local_user_settings_view_from_jwt_opt(
jwt: &Option<String>, jwt: &Option<String>,
pool: &DbPool, pool: &DbPool,
) -> Result<Option<UserSafeSettings>, LemmyError> { ) -> Result<Option<LocalUserSettingsView>, LemmyError> {
match jwt { match jwt {
Some(jwt) => Ok(Some(get_user_safe_settings_from_jwt(jwt, pool).await?)), Some(jwt) => Ok(Some(get_local_user_settings_view_from_jwt(jwt, pool).await?)),
None => Ok(None), None => Ok(None),
} }
} }
pub(crate) async fn check_community_ban( pub(crate) async fn check_community_ban(
user_id: i32, person_id: i32,
community_id: i32, community_id: i32,
pool: &DbPool, pool: &DbPool,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let is_banned = move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok(); let is_banned = move |conn: &'_ _| CommunityPersonBanView::get(conn, person_id, community_id).is_ok();
if blocking(pool, is_banned).await? { if blocking(pool, is_banned).await? {
Err(ApiError::err("community_ban").into()) Err(ApiError::err("community_ban").into())
} else { } else {
@ -172,19 +196,10 @@ pub(crate) async fn collect_moderated_communities(
} }
} }
pub(crate) fn check_optional_url(item: &Option<Option<String>>) -> Result<(), LemmyError> {
if let Some(Some(item)) = &item {
if Url::parse(item).is_err() {
return Err(ApiError::err("invalid_url").into());
}
}
Ok(())
}
pub(crate) async fn build_federated_instances( pub(crate) async fn build_federated_instances(
pool: &DbPool, pool: &DbPool,
) -> Result<Option<FederatedInstances>, LemmyError> { ) -> Result<Option<FederatedInstances>, LemmyError> {
if Settings::get().federation.enabled { if Settings::get().federation().enabled {
let distinct_communities = blocking(pool, move |conn| { let distinct_communities = blocking(pool, move |conn| {
Community::distinct_federated_communities(conn) Community::distinct_federated_communities(conn)
}) })
@ -198,8 +213,13 @@ pub(crate) async fn build_federated_instances(
.map(|actor_id| Ok(Url::parse(actor_id)?.host_str().unwrap_or("").to_string())) .map(|actor_id| Ok(Url::parse(actor_id)?.host_str().unwrap_or("").to_string()))
.collect::<Result<Vec<String>, LemmyError>>()?; .collect::<Result<Vec<String>, LemmyError>>()?;
linked.extend_from_slice(&allowed); if let Some(allowed) = allowed.as_ref() {
linked.retain(|a| !blocked.contains(a) && !a.eq("") && !a.eq(&Settings::get().hostname)); linked.extend_from_slice(allowed);
}
if let Some(blocked) = blocked.as_ref() {
linked.retain(|a| !blocked.contains(a) && !a.eq(&Settings::get().hostname()));
}
// Sort and remove dupes // Sort and remove dupes
linked.sort_unstable(); linked.sort_unstable();
@ -226,17 +246,17 @@ pub async fn match_websocket_operation(
UserOperation::Login => do_websocket_operation::<Login>(context, id, op, data).await, UserOperation::Login => do_websocket_operation::<Login>(context, id, op, data).await,
UserOperation::Register => do_websocket_operation::<Register>(context, id, op, data).await, UserOperation::Register => do_websocket_operation::<Register>(context, id, op, data).await,
UserOperation::GetCaptcha => do_websocket_operation::<GetCaptcha>(context, id, op, data).await, UserOperation::GetCaptcha => do_websocket_operation::<GetCaptcha>(context, id, op, data).await,
UserOperation::GetUserDetails => { UserOperation::GetPersonDetails => {
do_websocket_operation::<GetUserDetails>(context, id, op, data).await do_websocket_operation::<GetPersonDetails>(context, id, op, data).await
} }
UserOperation::GetReplies => do_websocket_operation::<GetReplies>(context, id, op, data).await, UserOperation::GetReplies => do_websocket_operation::<GetReplies>(context, id, op, data).await,
UserOperation::AddAdmin => do_websocket_operation::<AddAdmin>(context, id, op, data).await, UserOperation::AddAdmin => do_websocket_operation::<AddAdmin>(context, id, op, data).await,
UserOperation::BanUser => do_websocket_operation::<BanUser>(context, id, op, data).await, UserOperation::BanPerson => do_websocket_operation::<BanPerson>(context, id, op, data).await,
UserOperation::GetUserMentions => { UserOperation::GetPersonMentions => {
do_websocket_operation::<GetUserMentions>(context, id, op, data).await do_websocket_operation::<GetPersonMentions>(context, id, op, data).await
} }
UserOperation::MarkUserMentionAsRead => { UserOperation::MarkPersonMentionAsRead => {
do_websocket_operation::<MarkUserMentionAsRead>(context, id, op, data).await do_websocket_operation::<MarkPersonMentionAsRead>(context, id, op, data).await
} }
UserOperation::MarkAllAsRead => { UserOperation::MarkAllAsRead => {
do_websocket_operation::<MarkAllAsRead>(context, id, op, data).await do_websocket_operation::<MarkAllAsRead>(context, id, op, data).await
@ -455,6 +475,15 @@ pub(crate) fn espeak_wav_base64(text: &str) -> Result<String, LemmyError> {
Ok(base64) Ok(base64)
} }
/// Checks the password length
pub(crate) fn password_length_check(pass: &str) -> Result<(), LemmyError> {
if pass.len() > 60 {
Err(ApiError::err("invalid_password").into())
} else {
Ok(())
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::captcha_espeak_wav_base64; use crate::captcha_espeak_wav_base64;

View file

@ -1,14 +1,14 @@
use crate::{ use crate::{
check_community_ban, check_community_ban,
check_downvotes_enabled, check_downvotes_enabled,
check_optional_url,
collect_moderated_communities, collect_moderated_communities,
get_user_from_jwt, get_local_user_view_from_jwt,
get_user_from_jwt_opt, get_local_user_view_from_jwt_opt,
is_mod_or_admin, is_mod_or_admin,
Perform, Perform,
}; };
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_structs::{blocking, post::*};
use lemmy_apub::{generate_apub_endpoint, ApubLikeableType, ApubObjectType, EndpointType}; use lemmy_apub::{generate_apub_endpoint, ApubLikeableType, ApubObjectType, EndpointType};
use lemmy_db_queries::{ use lemmy_db_queries::{
source::post::Post_, source::post::Post_,
@ -36,7 +36,6 @@ use lemmy_db_views_actor::{
community_moderator_view::CommunityModeratorView, community_moderator_view::CommunityModeratorView,
community_view::CommunityView, community_view::CommunityView,
}; };
use lemmy_structs::{blocking, post::*};
use lemmy_utils::{ use lemmy_utils::{
request::fetch_iframely_and_pictrs_data, request::fetch_iframely_and_pictrs_data,
utils::{check_slurs, check_slurs_opt, is_valid_post_title}, utils::{check_slurs, check_slurs_opt, is_valid_post_title},
@ -61,7 +60,7 @@ impl Perform for CreatePost {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<PostResponse, LemmyError> { ) -> Result<PostResponse, LemmyError> {
let data: &CreatePost = &self; let data: &CreatePost = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
check_slurs(&data.name)?; check_slurs(&data.name)?;
check_slurs_opt(&data.body)?; check_slurs_opt(&data.body)?;
@ -70,20 +69,19 @@ impl Perform for CreatePost {
return Err(ApiError::err("invalid_post_title").into()); return Err(ApiError::err("invalid_post_title").into());
} }
check_community_ban(user.id, data.community_id, context.pool()).await?; check_community_ban(local_user_view.person.id, data.community_id, context.pool()).await?;
check_optional_url(&Some(data.url.to_owned()))?;
// Fetch Iframely and pictrs cached image // Fetch Iframely and pictrs cached image
let data_url = data.url.as_ref();
let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) = let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
fetch_iframely_and_pictrs_data(context.client(), data.url.to_owned()).await; fetch_iframely_and_pictrs_data(context.client(), data_url).await;
let post_form = PostForm { let post_form = PostForm {
name: data.name.trim().to_owned(), name: data.name.trim().to_owned(),
url: data.url.to_owned(), url: data_url.map(|u| u.to_owned().into()),
body: data.body.to_owned(), body: data.body.to_owned(),
community_id: data.community_id, community_id: data.community_id,
creator_id: user.id, creator_id: local_user_view.person.id,
removed: None, removed: None,
deleted: None, deleted: None,
nsfw: data.nsfw, nsfw: data.nsfw,
@ -93,7 +91,7 @@ impl Perform for CreatePost {
embed_title: iframely_title, embed_title: iframely_title,
embed_description: iframely_description, embed_description: iframely_description,
embed_html: iframely_html, embed_html: iframely_html,
thumbnail_url: pictrs_thumbnail, thumbnail_url: pictrs_thumbnail.map(|u| u.into()),
ap_id: None, ap_id: None,
local: true, local: true,
published: None, published: None,
@ -124,12 +122,12 @@ impl Perform for CreatePost {
Err(_e) => return Err(ApiError::err("couldnt_create_post").into()), Err(_e) => return Err(ApiError::err("couldnt_create_post").into()),
}; };
updated_post.send_create(&user, context).await?; updated_post.send_create(&local_user_view.person, context).await?;
// They like their own post by default // They like their own post by default
let like_form = PostLikeForm { let like_form = PostLikeForm {
post_id: inserted_post.id, post_id: inserted_post.id,
person_id: user.id, person_id: local_user_view.person.id,
score: 1, score: 1,
}; };
@ -138,12 +136,12 @@ impl Perform for CreatePost {
return Err(ApiError::err("couldnt_like_post").into()); return Err(ApiError::err("couldnt_like_post").into());
} }
updated_post.send_like(&user, context).await?; updated_post.send_like(&local_user_view.person, context).await?;
// Refetch the view // Refetch the view
let inserted_post_id = inserted_post.id; let inserted_post_id = inserted_post.id;
let post_view = match blocking(context.pool(), move |conn| { let post_view = match blocking(context.pool(), move |conn| {
PostView::read(conn, inserted_post_id, Some(user.id)) PostView::read(conn, inserted_post_id, Some(local_user_view.person.id))
}) })
.await? .await?
{ {
@ -173,12 +171,12 @@ impl Perform for GetPost {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<GetPostResponse, LemmyError> { ) -> Result<GetPostResponse, LemmyError> {
let data: &GetPost = &self; let data: &GetPost = &self;
let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
let user_id = user.map(|u| u.id); let person_id = local_user_view.map(|u| u.person.id);
let id = data.id; let id = data.id;
let post_view = match blocking(context.pool(), move |conn| { let post_view = match blocking(context.pool(), move |conn| {
PostView::read(conn, id, user_id) PostView::read(conn, id, person_id)
}) })
.await? .await?
{ {
@ -189,7 +187,7 @@ impl Perform for GetPost {
let id = data.id; let id = data.id;
let comments = blocking(context.pool(), move |conn| { let comments = blocking(context.pool(), move |conn| {
CommentQueryBuilder::create(conn) CommentQueryBuilder::create(conn)
.my_user_id(user_id) .my_person_id(person_id)
.post_id(id) .post_id(id)
.limit(9999) .limit(9999)
.list() .list()
@ -204,7 +202,7 @@ impl Perform for GetPost {
// Necessary for the sidebar // Necessary for the sidebar
let community_view = match blocking(context.pool(), move |conn| { let community_view = match blocking(context.pool(), move |conn| {
CommunityView::read(conn, community_id, user_id) CommunityView::read(conn, community_id, person_id)
}) })
.await? .await?
{ {
@ -239,15 +237,15 @@ impl Perform for GetPosts {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<GetPostsResponse, LemmyError> { ) -> Result<GetPostsResponse, LemmyError> {
let data: &GetPosts = &self; let data: &GetPosts = &self;
let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
let user_id = match &user { let person_id = match &local_user_view {
Some(user) => Some(user.id), Some(uv) => Some(uv.person.id),
None => None, None => None,
}; };
let show_nsfw = match &user { let show_nsfw = match &local_user_view {
Some(user) => user.show_nsfw, Some(uv) => uv.local_user.show_nsfw,
None => false, None => false,
}; };
@ -265,7 +263,7 @@ impl Perform for GetPosts {
.show_nsfw(show_nsfw) .show_nsfw(show_nsfw)
.community_id(community_id) .community_id(community_id)
.community_name(community_name) .community_name(community_name)
.my_user_id(user_id) .my_person_id(person_id)
.page(page) .page(page)
.limit(limit) .limit(limit)
.list() .list()
@ -290,7 +288,7 @@ impl Perform for CreatePostLike {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<PostResponse, LemmyError> { ) -> Result<PostResponse, LemmyError> {
let data: &CreatePostLike = &self; let data: &CreatePostLike = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
// Don't do a downvote if site has downvotes disabled // Don't do a downvote if site has downvotes disabled
check_downvotes_enabled(data.score, context.pool()).await?; check_downvotes_enabled(data.score, context.pool()).await?;
@ -299,18 +297,18 @@ impl Perform for CreatePostLike {
let post_id = data.post_id; let post_id = data.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
check_community_ban(user.id, post.community_id, context.pool()).await?; check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?;
let like_form = PostLikeForm { let like_form = PostLikeForm {
post_id: data.post_id, post_id: data.post_id,
person_id: user.id, person_id: local_user_view.person.id,
score: data.score, score: data.score,
}; };
// Remove any likes first // Remove any likes first
let user_id = user.id; let person_id = local_user_view.person.id;
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
PostLike::remove(conn, user_id, post_id) PostLike::remove(conn, person_id, post_id)
}) })
.await??; .await??;
@ -324,18 +322,18 @@ impl Perform for CreatePostLike {
} }
if like_form.score == 1 { if like_form.score == 1 {
post.send_like(&user, context).await?; post.send_like(&local_user_view.person, context).await?;
} else if like_form.score == -1 { } else if like_form.score == -1 {
post.send_dislike(&user, context).await?; post.send_dislike(&local_user_view.person, context).await?;
} }
} else { } else {
post.send_undo_like(&user, context).await?; post.send_undo_like(&local_user_view.person, context).await?;
} }
let post_id = data.post_id; let post_id = data.post_id;
let user_id = user.id; let person_id = local_user_view.person.id;
let post_view = match blocking(context.pool(), move |conn| { let post_view = match blocking(context.pool(), move |conn| {
PostView::read(conn, post_id, Some(user_id)) PostView::read(conn, post_id, Some(person_id))
}) })
.await? .await?
{ {
@ -365,7 +363,7 @@ impl Perform for EditPost {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<PostResponse, LemmyError> { ) -> Result<PostResponse, LemmyError> {
let data: &EditPost = &self; let data: &EditPost = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
check_slurs(&data.name)?; check_slurs(&data.name)?;
check_slurs_opt(&data.body)?; check_slurs_opt(&data.body)?;
@ -377,20 +375,21 @@ impl Perform for EditPost {
let post_id = data.post_id; let post_id = data.post_id;
let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
check_community_ban(user.id, orig_post.community_id, context.pool()).await?; check_community_ban(local_user_view.person.id, orig_post.community_id, context.pool()).await?;
// Verify that only the creator can edit // Verify that only the creator can edit
if !Post::is_post_creator(user.id, orig_post.creator_id) { if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
return Err(ApiError::err("no_post_edit_allowed").into()); return Err(ApiError::err("no_post_edit_allowed").into());
} }
// Fetch Iframely and Pictrs cached image // Fetch Iframely and Pictrs cached image
let data_url = data.url.as_ref();
let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) = let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
fetch_iframely_and_pictrs_data(context.client(), data.url.to_owned()).await; fetch_iframely_and_pictrs_data(context.client(), data_url).await;
let post_form = PostForm { let post_form = PostForm {
name: data.name.trim().to_owned(), name: data.name.trim().to_owned(),
url: data.url.to_owned(), url: data_url.map(|u| u.to_owned().into()),
body: data.body.to_owned(), body: data.body.to_owned(),
nsfw: data.nsfw, nsfw: data.nsfw,
creator_id: orig_post.creator_id.to_owned(), creator_id: orig_post.creator_id.to_owned(),
@ -403,7 +402,7 @@ impl Perform for EditPost {
embed_title: iframely_title, embed_title: iframely_title,
embed_description: iframely_description, embed_description: iframely_description,
embed_html: iframely_html, embed_html: iframely_html,
thumbnail_url: pictrs_thumbnail, thumbnail_url: pictrs_thumbnail.map(|u| u.into()),
ap_id: Some(orig_post.ap_id), ap_id: Some(orig_post.ap_id),
local: orig_post.local, local: orig_post.local,
published: None, published: None,
@ -428,11 +427,11 @@ impl Perform for EditPost {
}; };
// Send apub update // Send apub update
updated_post.send_update(&user, context).await?; updated_post.send_update(&local_user_view.person, context).await?;
let post_id = data.post_id; let post_id = data.post_id;
let post_view = blocking(context.pool(), move |conn| { let post_view = blocking(context.pool(), move |conn| {
PostView::read(conn, post_id, Some(user.id)) PostView::read(conn, post_id, Some(local_user_view.person.id))
}) })
.await??; .await??;
@ -458,15 +457,15 @@ impl Perform for DeletePost {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<PostResponse, LemmyError> { ) -> Result<PostResponse, LemmyError> {
let data: &DeletePost = &self; let data: &DeletePost = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let post_id = data.post_id; let post_id = data.post_id;
let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
check_community_ban(user.id, orig_post.community_id, context.pool()).await?; check_community_ban(local_user_view.person.id, orig_post.community_id, context.pool()).await?;
// Verify that only the creator can delete // Verify that only the creator can delete
if !Post::is_post_creator(user.id, orig_post.creator_id) { if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
return Err(ApiError::err("no_post_edit_allowed").into()); return Err(ApiError::err("no_post_edit_allowed").into());
} }
@ -480,15 +479,15 @@ impl Perform for DeletePost {
// apub updates // apub updates
if deleted { if deleted {
updated_post.send_delete(&user, context).await?; updated_post.send_delete(&local_user_view.person, context).await?;
} else { } else {
updated_post.send_undo_delete(&user, context).await?; updated_post.send_undo_delete(&local_user_view.person, context).await?;
} }
// Refetch the post // Refetch the post
let post_id = data.post_id; let post_id = data.post_id;
let post_view = blocking(context.pool(), move |conn| { let post_view = blocking(context.pool(), move |conn| {
PostView::read(conn, post_id, Some(user.id)) PostView::read(conn, post_id, Some(local_user_view.person.id))
}) })
.await??; .await??;
@ -514,15 +513,15 @@ impl Perform for RemovePost {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<PostResponse, LemmyError> { ) -> Result<PostResponse, LemmyError> {
let data: &RemovePost = &self; let data: &RemovePost = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let post_id = data.post_id; let post_id = data.post_id;
let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
check_community_ban(user.id, orig_post.community_id, context.pool()).await?; check_community_ban(local_user_view.person.id, orig_post.community_id, context.pool()).await?;
// Verify that only the mods can remove // Verify that only the mods can remove
is_mod_or_admin(context.pool(), user.id, orig_post.community_id).await?; is_mod_or_admin(context.pool(), local_user_view.person.id, orig_post.community_id).await?;
// Update the post // Update the post
let post_id = data.post_id; let post_id = data.post_id;
@ -534,7 +533,7 @@ impl Perform for RemovePost {
// Mod tables // Mod tables
let form = ModRemovePostForm { let form = ModRemovePostForm {
mod_person_id: user.id, mod_person_id: local_user_view.person.id,
post_id: data.post_id, post_id: data.post_id,
removed: Some(removed), removed: Some(removed),
reason: data.reason.to_owned(), reason: data.reason.to_owned(),
@ -546,16 +545,16 @@ impl Perform for RemovePost {
// apub updates // apub updates
if removed { if removed {
updated_post.send_remove(&user, context).await?; updated_post.send_remove(&local_user_view.person, context).await?;
} else { } else {
updated_post.send_undo_remove(&user, context).await?; updated_post.send_undo_remove(&local_user_view.person, context).await?;
} }
// Refetch the post // Refetch the post
let post_id = data.post_id; let post_id = data.post_id;
let user_id = user.id; let person_id = local_user_view.person.id;
let post_view = blocking(context.pool(), move |conn| { let post_view = blocking(context.pool(), move |conn| {
PostView::read(conn, post_id, Some(user_id)) PostView::read(conn, post_id, Some(person_id))
}) })
.await??; .await??;
@ -581,15 +580,15 @@ impl Perform for LockPost {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<PostResponse, LemmyError> { ) -> Result<PostResponse, LemmyError> {
let data: &LockPost = &self; let data: &LockPost = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let post_id = data.post_id; let post_id = data.post_id;
let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
check_community_ban(user.id, orig_post.community_id, context.pool()).await?; check_community_ban(local_user_view.person.id, orig_post.community_id, context.pool()).await?;
// Verify that only the mods can lock // Verify that only the mods can lock
is_mod_or_admin(context.pool(), user.id, orig_post.community_id).await?; is_mod_or_admin(context.pool(), local_user_view.person.id, orig_post.community_id).await?;
// Update the post // Update the post
let post_id = data.post_id; let post_id = data.post_id;
@ -601,19 +600,19 @@ impl Perform for LockPost {
// Mod tables // Mod tables
let form = ModLockPostForm { let form = ModLockPostForm {
mod_person_id: user.id, mod_person_id: local_user_view.person.id,
post_id: data.post_id, post_id: data.post_id,
locked: Some(locked), locked: Some(locked),
}; };
blocking(context.pool(), move |conn| ModLockPost::create(conn, &form)).await??; blocking(context.pool(), move |conn| ModLockPost::create(conn, &form)).await??;
// apub updates // apub updates
updated_post.send_update(&user, context).await?; updated_post.send_update(&local_user_view.person, context).await?;
// Refetch the post // Refetch the post
let post_id = data.post_id; let post_id = data.post_id;
let post_view = blocking(context.pool(), move |conn| { let post_view = blocking(context.pool(), move |conn| {
PostView::read(conn, post_id, Some(user.id)) PostView::read(conn, post_id, Some(local_user_view.person.id))
}) })
.await??; .await??;
@ -639,15 +638,15 @@ impl Perform for StickyPost {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<PostResponse, LemmyError> { ) -> Result<PostResponse, LemmyError> {
let data: &StickyPost = &self; let data: &StickyPost = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let post_id = data.post_id; let post_id = data.post_id;
let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
check_community_ban(user.id, orig_post.community_id, context.pool()).await?; check_community_ban(local_user_view.person.id, orig_post.community_id, context.pool()).await?;
// Verify that only the mods can sticky // Verify that only the mods can sticky
is_mod_or_admin(context.pool(), user.id, orig_post.community_id).await?; is_mod_or_admin(context.pool(), local_user_view.person.id, orig_post.community_id).await?;
// Update the post // Update the post
let post_id = data.post_id; let post_id = data.post_id;
@ -659,7 +658,7 @@ impl Perform for StickyPost {
// Mod tables // Mod tables
let form = ModStickyPostForm { let form = ModStickyPostForm {
mod_person_id: user.id, mod_person_id: local_user_view.person.id,
post_id: data.post_id, post_id: data.post_id,
stickied: Some(stickied), stickied: Some(stickied),
}; };
@ -670,12 +669,12 @@ impl Perform for StickyPost {
// Apub updates // Apub updates
// TODO stickied should pry work like locked for ease of use // TODO stickied should pry work like locked for ease of use
updated_post.send_update(&user, context).await?; updated_post.send_update(&local_user_view.person, context).await?;
// Refetch the post // Refetch the post
let post_id = data.post_id; let post_id = data.post_id;
let post_view = blocking(context.pool(), move |conn| { let post_view = blocking(context.pool(), move |conn| {
PostView::read(conn, post_id, Some(user.id)) PostView::read(conn, post_id, Some(local_user_view.person.id))
}) })
.await??; .await??;
@ -701,11 +700,11 @@ impl Perform for SavePost {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<PostResponse, LemmyError> { ) -> Result<PostResponse, LemmyError> {
let data: &SavePost = &self; let data: &SavePost = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let post_saved_form = PostSavedForm { let post_saved_form = PostSavedForm {
post_id: data.post_id, post_id: data.post_id,
person_id: user.id, person_id: local_user_view.person.id,
}; };
if data.save { if data.save {
@ -721,9 +720,9 @@ impl Perform for SavePost {
} }
let post_id = data.post_id; let post_id = data.post_id;
let user_id = user.id; let person_id = local_user_view.person.id;
let post_view = blocking(context.pool(), move |conn| { let post_view = blocking(context.pool(), move |conn| {
PostView::read(conn, post_id, Some(user_id)) PostView::read(conn, post_id, Some(person_id))
}) })
.await??; .await??;
@ -742,7 +741,7 @@ impl Perform for CreatePostReport {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<CreatePostReportResponse, LemmyError> { ) -> Result<CreatePostReportResponse, LemmyError> {
let data: &CreatePostReport = &self; let data: &CreatePostReport = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
// check size of report and check for whitespace // check size of report and check for whitespace
let reason = data.reason.trim(); let reason = data.reason.trim();
@ -753,17 +752,17 @@ impl Perform for CreatePostReport {
return Err(ApiError::err("report_too_long").into()); return Err(ApiError::err("report_too_long").into());
} }
let user_id = user.id; let person_id = local_user_view.person.id;
let post_id = data.post_id; let post_id = data.post_id;
let post_view = blocking(context.pool(), move |conn| { let post_view = blocking(context.pool(), move |conn| {
PostView::read(&conn, post_id, None) PostView::read(&conn, post_id, None)
}) })
.await??; .await??;
check_community_ban(user_id, post_view.community.id, context.pool()).await?; check_community_ban(person_id, post_view.community.id, context.pool()).await?;
let report_form = PostReportForm { let report_form = PostReportForm {
creator_id: user_id, creator_id: person_id,
post_id, post_id,
original_post_name: post_view.post.name, original_post_name: post_view.post.name,
original_post_url: post_view.post.url, original_post_url: post_view.post.url,
@ -785,7 +784,7 @@ impl Perform for CreatePostReport {
context.chat_server().do_send(SendUserRoomMessage { context.chat_server().do_send(SendUserRoomMessage {
op: UserOperation::CreatePostReport, op: UserOperation::CreatePostReport,
response: res.clone(), response: res.clone(),
recipient_id: user.id, recipient_id: local_user_view.person.id,
websocket_id, websocket_id,
}); });
@ -811,7 +810,7 @@ impl Perform for ResolvePostReport {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<ResolvePostReportResponse, LemmyError> { ) -> Result<ResolvePostReportResponse, LemmyError> {
let data: &ResolvePostReport = &self; let data: &ResolvePostReport = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let report_id = data.report_id; let report_id = data.report_id;
let report = blocking(context.pool(), move |conn| { let report = blocking(context.pool(), move |conn| {
@ -819,15 +818,15 @@ impl Perform for ResolvePostReport {
}) })
.await??; .await??;
let user_id = user.id; let person_id = local_user_view.person.id;
is_mod_or_admin(context.pool(), user_id, report.community.id).await?; is_mod_or_admin(context.pool(), person_id, report.community.id).await?;
let resolved = data.resolved; let resolved = data.resolved;
let resolve_fun = move |conn: &'_ _| { let resolve_fun = move |conn: &'_ _| {
if resolved { if resolved {
PostReport::resolve(conn, report_id, user_id) PostReport::resolve(conn, report_id, person_id)
} else { } else {
PostReport::unresolve(conn, report_id, user_id) PostReport::unresolve(conn, report_id, person_id)
} }
}; };
@ -863,12 +862,12 @@ impl Perform for ListPostReports {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<ListPostReportsResponse, LemmyError> { ) -> Result<ListPostReportsResponse, LemmyError> {
let data: &ListPostReports = &self; let data: &ListPostReports = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let user_id = user.id; let person_id = local_user_view.person.id;
let community_id = data.community; let community_id = data.community;
let community_ids = let community_ids =
collect_moderated_communities(user_id, community_id, context.pool()).await?; collect_moderated_communities(person_id, community_id, context.pool()).await?;
let page = data.page; let page = data.page;
let limit = data.limit; let limit = data.limit;
@ -886,7 +885,7 @@ impl Perform for ListPostReports {
context.chat_server().do_send(SendUserRoomMessage { context.chat_server().do_send(SendUserRoomMessage {
op: UserOperation::ListPostReports, op: UserOperation::ListPostReports,
response: res.clone(), response: res.clone(),
recipient_id: user.id, recipient_id: local_user_view.person.id,
websocket_id, websocket_id,
}); });

View file

@ -1,6 +1,6 @@
use crate::Perform; use crate::Perform;
use actix_web::{error::ErrorBadRequest, *}; use actix_web::{error::ErrorBadRequest, *};
use lemmy_structs::{comment::*, community::*, post::*, site::*, user::*, websocket::*}; use lemmy_api_structs::{comment::*, community::*, post::*, site::*, person::*, websocket::*};
use lemmy_utils::rate_limit::RateLimit; use lemmy_utils::rate_limit::RateLimit;
use lemmy_websocket::{routes::chat_route, LemmyContext}; use lemmy_websocket::{routes::chat_route, LemmyContext};
use serde::Deserialize; use serde::Deserialize;
@ -137,11 +137,11 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
.service( .service(
web::scope("/user") web::scope("/user")
.wrap(rate_limit.message()) .wrap(rate_limit.message())
.route("", web::get().to(route_get::<GetUserDetails>)) .route("", web::get().to(route_get::<GetPersonDetails>))
.route("/mention", web::get().to(route_get::<GetUserMentions>)) .route("/mention", web::get().to(route_get::<GetPersonMentions>))
.route( .route(
"/mention/mark_as_read", "/mention/mark_as_read",
web::post().to(route_post::<MarkUserMentionAsRead>), web::post().to(route_post::<MarkPersonMentionAsRead>),
) )
.route("/replies", web::get().to(route_get::<GetReplies>)) .route("/replies", web::get().to(route_get::<GetReplies>))
.route( .route(
@ -150,7 +150,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
) )
.route("/join", web::post().to(route_post::<UserJoin>)) .route("/join", web::post().to(route_post::<UserJoin>))
// Admin action. I don't like that it's in /user // Admin action. I don't like that it's in /user
.route("/ban", web::post().to(route_post::<BanUser>)) .route("/ban", web::post().to(route_post::<BanPerson>))
// Account actions. I don't like that they're in /user maybe /accounts // Account actions. I don't like that they're in /user maybe /accounts
.route("/login", web::post().to(route_post::<Login>)) .route("/login", web::post().to(route_post::<Login>))
.route("/get_captcha", web::get().to(route_get::<GetCaptcha>)) .route("/get_captcha", web::get().to(route_get::<GetCaptcha>))

View file

@ -1,16 +1,22 @@
use crate::{ use crate::{
build_federated_instances, build_federated_instances,
get_user_from_jwt, get_local_user_view_from_jwt,
get_user_from_jwt_opt, get_local_user_view_from_jwt_opt,
get_user_safe_settings_from_jwt, get_local_user_settings_view_from_jwt_opt,
get_user_safe_settings_from_jwt_opt,
is_admin, is_admin,
Perform, Perform,
}; };
use actix_web::web::Data; use actix_web::web::Data;
use anyhow::Context; use anyhow::Context;
use lemmy_api_structs::{blocking, site::*, person::Register};
use lemmy_apub::fetcher::search::search_by_apub_id; use lemmy_apub::fetcher::search::search_by_apub_id;
use lemmy_db_queries::{diesel_option_overwrite, source::site::Site_, Crud, SearchType, SortType}; use lemmy_db_queries::{
diesel_option_overwrite_to_url,
source::site::Site_,
Crud,
SearchType,
SortType,
};
use lemmy_db_schema::{ use lemmy_db_schema::{
naive_now, naive_now,
source::{ source::{
@ -25,7 +31,7 @@ use lemmy_db_views::{
}; };
use lemmy_db_views_actor::{ use lemmy_db_views_actor::{
community_view::CommunityQueryBuilder, community_view::CommunityQueryBuilder,
user_view::{UserQueryBuilder, UserViewSafe}, person_view::{PersonQueryBuilder, PersonViewSafe},
}; };
use lemmy_db_views_moderator::{ use lemmy_db_views_moderator::{
mod_add_community_view::ModAddCommunityView, mod_add_community_view::ModAddCommunityView,
@ -38,10 +44,9 @@ use lemmy_db_views_moderator::{
mod_remove_post_view::ModRemovePostView, mod_remove_post_view::ModRemovePostView,
mod_sticky_post_view::ModStickyPostView, mod_sticky_post_view::ModStickyPostView,
}; };
use lemmy_structs::{blocking, site::*, user::Register};
use lemmy_utils::{ use lemmy_utils::{
location_info, location_info,
settings::Settings, settings::structs::Settings,
utils::{check_slurs, check_slurs_opt}, utils::{check_slurs, check_slurs_opt},
version, version,
ApiError, ApiError,
@ -68,36 +73,36 @@ impl Perform for GetModlog {
let data: &GetModlog = &self; let data: &GetModlog = &self;
let community_id = data.community_id; let community_id = data.community_id;
let mod_user_id = data.mod_user_id; let mod_person_id = data.mod_person_id;
let page = data.page; let page = data.page;
let limit = data.limit; let limit = data.limit;
let removed_posts = blocking(context.pool(), move |conn| { let removed_posts = blocking(context.pool(), move |conn| {
ModRemovePostView::list(conn, community_id, mod_user_id, page, limit) ModRemovePostView::list(conn, community_id, mod_person_id, page, limit)
}) })
.await??; .await??;
let locked_posts = blocking(context.pool(), move |conn| { let locked_posts = blocking(context.pool(), move |conn| {
ModLockPostView::list(conn, community_id, mod_user_id, page, limit) ModLockPostView::list(conn, community_id, mod_person_id, page, limit)
}) })
.await??; .await??;
let stickied_posts = blocking(context.pool(), move |conn| { let stickied_posts = blocking(context.pool(), move |conn| {
ModStickyPostView::list(conn, community_id, mod_user_id, page, limit) ModStickyPostView::list(conn, community_id, mod_person_id, page, limit)
}) })
.await??; .await??;
let removed_comments = blocking(context.pool(), move |conn| { let removed_comments = blocking(context.pool(), move |conn| {
ModRemoveCommentView::list(conn, community_id, mod_user_id, page, limit) ModRemoveCommentView::list(conn, community_id, mod_person_id, page, limit)
}) })
.await??; .await??;
let banned_from_community = blocking(context.pool(), move |conn| { let banned_from_community = blocking(context.pool(), move |conn| {
ModBanFromCommunityView::list(conn, community_id, mod_user_id, page, limit) ModBanFromCommunityView::list(conn, community_id, mod_person_id, page, limit)
}) })
.await??; .await??;
let added_to_community = blocking(context.pool(), move |conn| { let added_to_community = blocking(context.pool(), move |conn| {
ModAddCommunityView::list(conn, community_id, mod_user_id, page, limit) ModAddCommunityView::list(conn, community_id, mod_person_id, page, limit)
}) })
.await??; .await??;
@ -105,9 +110,9 @@ impl Perform for GetModlog {
let (removed_communities, banned, added) = if data.community_id.is_none() { let (removed_communities, banned, added) = if data.community_id.is_none() {
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
Ok(( Ok((
ModRemoveCommunityView::list(conn, mod_user_id, page, limit)?, ModRemoveCommunityView::list(conn, mod_person_id, page, limit)?,
ModBanView::list(conn, mod_user_id, page, limit)?, ModBanView::list(conn, mod_person_id, page, limit)?,
ModAddView::list(conn, mod_user_id, page, limit)?, ModAddView::list(conn, mod_person_id, page, limit)?,
)) as Result<_, LemmyError> )) as Result<_, LemmyError>
}) })
.await?? .await??
@ -146,20 +151,20 @@ impl Perform for CreateSite {
return Err(ApiError::err("site_already_exists").into()); return Err(ApiError::err("site_already_exists").into());
}; };
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
check_slurs(&data.name)?; check_slurs(&data.name)?;
check_slurs_opt(&data.description)?; check_slurs_opt(&data.description)?;
// Make sure user is an admin // Make sure user is an admin
is_admin(context.pool(), user.id).await?; is_admin(&local_user_view)?;
let site_form = SiteForm { let site_form = SiteForm {
name: data.name.to_owned(), name: data.name.to_owned(),
description: data.description.to_owned(), description: data.description.to_owned(),
icon: Some(data.icon.to_owned()), icon: Some(data.icon.to_owned().map(|url| url.into())),
banner: Some(data.banner.to_owned()), banner: Some(data.banner.to_owned().map(|url| url.into())),
creator_id: user.id, creator_id: local_user_view.person.id,
enable_downvotes: data.enable_downvotes, enable_downvotes: data.enable_downvotes,
open_registration: data.open_registration, open_registration: data.open_registration,
enable_nsfw: data.enable_nsfw, enable_nsfw: data.enable_nsfw,
@ -186,18 +191,18 @@ impl Perform for EditSite {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<SiteResponse, LemmyError> { ) -> Result<SiteResponse, LemmyError> {
let data: &EditSite = &self; let data: &EditSite = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
check_slurs(&data.name)?; check_slurs(&data.name)?;
check_slurs_opt(&data.description)?; check_slurs_opt(&data.description)?;
// Make sure user is an admin // Make sure user is an admin
is_admin(context.pool(), user.id).await?; is_admin(&local_user_view)?;
let found_site = blocking(context.pool(), move |conn| Site::read_simple(conn)).await??; let found_site = blocking(context.pool(), move |conn| Site::read_simple(conn)).await??;
let icon = diesel_option_overwrite(&data.icon); let icon = diesel_option_overwrite_to_url(&data.icon)?;
let banner = diesel_option_overwrite(&data.banner); let banner = diesel_option_overwrite_to_url(&data.banner)?;
let site_form = SiteForm { let site_form = SiteForm {
name: data.name.to_owned(), name: data.name.to_owned(),
@ -245,7 +250,7 @@ impl Perform for GetSite {
Ok(site_view) => Some(site_view), Ok(site_view) => Some(site_view),
// If the site isn't created yet, check the setup // If the site isn't created yet, check the setup
Err(_) => { Err(_) => {
if let Some(setup) = Settings::get().setup.as_ref() { if let Some(setup) = Settings::get().setup().as_ref() {
let register = Register { let register = Register {
username: setup.admin_username.to_owned(), username: setup.admin_username.to_owned(),
email: setup.admin_email.to_owned(), email: setup.admin_email.to_owned(),
@ -277,20 +282,20 @@ impl Perform for GetSite {
} }
}; };
let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??; let mut admins = blocking(context.pool(), move |conn| PersonViewSafe::admins(conn)).await??;
// Make sure the site creator is the top admin // Make sure the site creator is the top admin
if let Some(site_view) = site_view.to_owned() { if let Some(site_view) = site_view.to_owned() {
let site_creator_id = site_view.creator.id; let site_creator_id = site_view.creator.id;
// TODO investigate why this is sometimes coming back null // TODO investigate why this is sometimes coming back null
// Maybe user_.admin isn't being set to true? // Maybe user_.admin isn't being set to true?
if let Some(creator_index) = admins.iter().position(|r| r.user.id == site_creator_id) { if let Some(creator_index) = admins.iter().position(|r| r.person.id == site_creator_id) {
let creator_user = admins.remove(creator_index); let creator_person = admins.remove(creator_index);
admins.insert(0, creator_user); admins.insert(0, creator_person);
} }
} }
let banned = blocking(context.pool(), move |conn| UserViewSafe::banned(conn)).await??; let banned = blocking(context.pool(), move |conn| PersonViewSafe::banned(conn)).await??;
let online = context let online = context
.chat_server() .chat_server()
@ -298,7 +303,7 @@ impl Perform for GetSite {
.await .await
.unwrap_or(1); .unwrap_or(1);
let my_user = get_user_safe_settings_from_jwt_opt(&data.auth, context.pool()).await?; let my_user = get_local_user_settings_view_from_jwt_opt(&data.auth, context.pool()).await?;
let federated_instances = build_federated_instances(context.pool()).await?; let federated_instances = build_federated_instances(context.pool()).await?;
Ok(GetSiteResponse { Ok(GetSiteResponse {
@ -329,8 +334,8 @@ impl Perform for Search {
Err(e) => debug!("Failed to resolve search query as activitypub ID: {}", e), Err(e) => debug!("Failed to resolve search query as activitypub ID: {}", e),
} }
let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
let user_id = user.map(|u| u.id); let person_id = local_user_view.map(|u| u.person.id);
let type_ = SearchType::from_str(&data.type_)?; let type_ = SearchType::from_str(&data.type_)?;
@ -355,7 +360,7 @@ impl Perform for Search {
.show_nsfw(true) .show_nsfw(true)
.community_id(community_id) .community_id(community_id)
.community_name(community_name) .community_name(community_name)
.my_user_id(user_id) .my_person_id(person_id)
.search_term(q) .search_term(q)
.page(page) .page(page)
.limit(limit) .limit(limit)
@ -368,7 +373,7 @@ impl Perform for Search {
CommentQueryBuilder::create(&conn) CommentQueryBuilder::create(&conn)
.sort(&sort) .sort(&sort)
.search_term(q) .search_term(q)
.my_user_id(user_id) .my_person_id(person_id)
.page(page) .page(page)
.limit(limit) .limit(limit)
.list() .list()
@ -380,7 +385,7 @@ impl Perform for Search {
CommunityQueryBuilder::create(conn) CommunityQueryBuilder::create(conn)
.sort(&sort) .sort(&sort)
.search_term(q) .search_term(q)
.my_user_id(user_id) .my_person_id(person_id)
.page(page) .page(page)
.limit(limit) .limit(limit)
.list() .list()
@ -389,7 +394,7 @@ impl Perform for Search {
} }
SearchType::Users => { SearchType::Users => {
users = blocking(context.pool(), move |conn| { users = blocking(context.pool(), move |conn| {
UserQueryBuilder::create(conn) PersonQueryBuilder::create(conn)
.sort(&sort) .sort(&sort)
.search_term(q) .search_term(q)
.page(page) .page(page)
@ -405,7 +410,7 @@ impl Perform for Search {
.show_nsfw(true) .show_nsfw(true)
.community_id(community_id) .community_id(community_id)
.community_name(community_name) .community_name(community_name)
.my_user_id(user_id) .my_person_id(person_id)
.search_term(q) .search_term(q)
.page(page) .page(page)
.limit(limit) .limit(limit)
@ -420,7 +425,7 @@ impl Perform for Search {
CommentQueryBuilder::create(conn) CommentQueryBuilder::create(conn)
.sort(&sort) .sort(&sort)
.search_term(q) .search_term(q)
.my_user_id(user_id) .my_person_id(person_id)
.page(page) .page(page)
.limit(limit) .limit(limit)
.list() .list()
@ -434,7 +439,7 @@ impl Perform for Search {
CommunityQueryBuilder::create(conn) CommunityQueryBuilder::create(conn)
.sort(&sort) .sort(&sort)
.search_term(q) .search_term(q)
.my_user_id(user_id) .my_person_id(person_id)
.page(page) .page(page)
.limit(limit) .limit(limit)
.list() .list()
@ -445,7 +450,7 @@ impl Perform for Search {
let sort = SortType::from_str(&data.sort)?; let sort = SortType::from_str(&data.sort)?;
users = blocking(context.pool(), move |conn| { users = blocking(context.pool(), move |conn| {
UserQueryBuilder::create(conn) PersonQueryBuilder::create(conn)
.sort(&sort) .sort(&sort)
.search_term(q) .search_term(q)
.page(page) .page(page)
@ -459,7 +464,7 @@ impl Perform for Search {
PostQueryBuilder::create(conn) PostQueryBuilder::create(conn)
.sort(&sort) .sort(&sort)
.show_nsfw(true) .show_nsfw(true)
.my_user_id(user_id) .my_person_id(person_id)
.community_id(community_id) .community_id(community_id)
.community_name(community_name) .community_name(community_name)
.url_search(q) .url_search(q)
@ -492,18 +497,18 @@ impl Perform for TransferSite {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<GetSiteResponse, LemmyError> { ) -> Result<GetSiteResponse, LemmyError> {
let data: &TransferSite = &self; let data: &TransferSite = &self;
let user = get_user_safe_settings_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
is_admin(context.pool(), user.id).await?; is_admin(&local_user_view)?;
let read_site = blocking(context.pool(), move |conn| Site::read_simple(conn)).await??; let read_site = blocking(context.pool(), move |conn| Site::read_simple(conn)).await??;
// Make sure user is the creator // Make sure user is the creator
if read_site.creator_id != user.id { if read_site.creator_id != local_user_view.person.id {
return Err(ApiError::err("not_an_admin").into()); return Err(ApiError::err("not_an_admin").into());
} }
let new_creator_id = data.user_id; let new_creator_id = data.person_id;
let transfer_site = move |conn: &'_ _| Site::transfer(conn, new_creator_id); let transfer_site = move |conn: &'_ _| Site::transfer(conn, new_creator_id);
if blocking(context.pool(), transfer_site).await?.is_err() { if blocking(context.pool(), transfer_site).await?.is_err() {
return Err(ApiError::err("couldnt_update_site").into()); return Err(ApiError::err("couldnt_update_site").into());
@ -511,8 +516,8 @@ impl Perform for TransferSite {
// Mod tables // Mod tables
let form = ModAddForm { let form = ModAddForm {
mod_person_id: user.id, mod_person_id: local_user_view.person.id,
other_person_id: data.user_id, other_person_id: data.person_id,
removed: Some(false), removed: Some(false),
}; };
@ -520,15 +525,15 @@ impl Perform for TransferSite {
let site_view = blocking(context.pool(), move |conn| SiteView::read(conn)).await??; let site_view = blocking(context.pool(), move |conn| SiteView::read(conn)).await??;
let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??; let mut admins = blocking(context.pool(), move |conn| PersonViewSafe::admins(conn)).await??;
let creator_index = admins let creator_index = admins
.iter() .iter()
.position(|r| r.user.id == site_view.creator.id) .position(|r| r.person.id == site_view.creator.id)
.context(location_info!())?; .context(location_info!())?;
let creator_user = admins.remove(creator_index); let creator_person = admins.remove(creator_index);
admins.insert(0, creator_user); admins.insert(0, creator_person);
let banned = blocking(context.pool(), move |conn| UserViewSafe::banned(conn)).await??; let banned = blocking(context.pool(), move |conn| PersonViewSafe::banned(conn)).await??;
let federated_instances = build_federated_instances(context.pool()).await?; let federated_instances = build_federated_instances(context.pool()).await?;
Ok(GetSiteResponse { Ok(GetSiteResponse {
@ -537,7 +542,7 @@ impl Perform for TransferSite {
banned, banned,
online: 0, online: 0,
version: version::VERSION.to_string(), version: version::VERSION.to_string(),
my_user: Some(user), my_user: Some(local_user_view),
federated_instances, federated_instances,
}) })
} }
@ -553,10 +558,10 @@ impl Perform for GetSiteConfig {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<GetSiteConfigResponse, LemmyError> { ) -> Result<GetSiteConfigResponse, LemmyError> {
let data: &GetSiteConfig = &self; let data: &GetSiteConfig = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
// Only let admins read this // Only let admins read this
is_admin(context.pool(), user.id).await?; is_admin(&local_user_view)?;
let config_hjson = Settings::read_config_file()?; let config_hjson = Settings::read_config_file()?;
@ -574,11 +579,11 @@ impl Perform for SaveSiteConfig {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<GetSiteConfigResponse, LemmyError> { ) -> Result<GetSiteConfigResponse, LemmyError> {
let data: &SaveSiteConfig = &self; let data: &SaveSiteConfig = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
// Only let admins read this // Only let admins read this
let user_id = user.id; let person_id = local_user_view.person.id;
is_admin(context.pool(), user_id).await?; is_admin(&local_user_view)?;
// Make sure docker doesn't have :ro at the end of the volume, so its not a read-only filesystem // Make sure docker doesn't have :ro at the end of the volume, so its not a read-only filesystem
let config_hjson = match Settings::save_config_file(&data.config_hjson) { let config_hjson = match Settings::save_config_file(&data.config_hjson) {

View file

@ -1,10 +1,10 @@
use crate::{ use crate::{
captcha_espeak_wav_base64, captcha_espeak_wav_base64,
check_optional_url,
collect_moderated_communities, collect_moderated_communities,
get_user_from_jwt, get_local_user_view_from_jwt,
get_user_from_jwt_opt, get_local_user_view_from_jwt_opt,
is_admin, is_admin,
password_length_check,
Perform, Perform,
}; };
use actix_web::web::Data; use actix_web::web::Data;
@ -12,6 +12,7 @@ use anyhow::Context;
use bcrypt::verify; use bcrypt::verify;
use captcha::{gen, Difficulty}; use captcha::{gen, Difficulty};
use chrono::Duration; use chrono::Duration;
use lemmy_api_structs::{blocking, send_email_to_user, person::*};
use lemmy_apub::{ use lemmy_apub::{
generate_apub_endpoint, generate_apub_endpoint,
generate_followers_url, generate_followers_url,
@ -22,6 +23,7 @@ use lemmy_apub::{
}; };
use lemmy_db_queries::{ use lemmy_db_queries::{
diesel_option_overwrite, diesel_option_overwrite,
diesel_option_overwrite_to_url,
source::{ source::{
comment::Comment_, comment::Comment_,
community::Community_, community::Community_,
@ -29,8 +31,8 @@ use lemmy_db_queries::{
post::Post_, post::Post_,
private_message::PrivateMessage_, private_message::PrivateMessage_,
site::Site_, site::Site_,
user::User, person::Person_,
user_mention::UserMention_, person_mention::PersonMention_,
}, },
Crud, Crud,
Followable, Followable,
@ -38,40 +40,27 @@ use lemmy_db_queries::{
ListingType, ListingType,
SortType, SortType,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{naive_now, source::{comment::Comment, community::*, local_user::LocalUserForm, moderator::*, password_reset_request::*, person::*, person_mention::*, post::Post, private_message::*, site::*}};
naive_now,
source::{
comment::Comment,
community::*,
moderator::*,
password_reset_request::*,
post::Post,
private_message::*,
site::*,
user::*,
user_mention::*,
},
};
use lemmy_db_views::{ use lemmy_db_views::{
comment_report_view::CommentReportView, comment_report_view::CommentReportView,
comment_view::CommentQueryBuilder, comment_view::CommentQueryBuilder,
post_report_view::PostReportView, post_report_view::PostReportView,
post_view::PostQueryBuilder, post_view::PostQueryBuilder,
private_message_view::{PrivateMessageQueryBuilder, PrivateMessageView}, private_message_view::{PrivateMessageQueryBuilder, PrivateMessageView},
local_user_view::LocalUserView,
}; };
use lemmy_db_views_actor::{ use lemmy_db_views_actor::{
community_follower_view::CommunityFollowerView, community_follower_view::CommunityFollowerView,
community_moderator_view::CommunityModeratorView, community_moderator_view::CommunityModeratorView,
user_mention_view::{UserMentionQueryBuilder, UserMentionView}, person_mention_view::{PersonMentionQueryBuilder, PersonMentionView},
user_view::UserViewSafe, person_view::PersonViewSafe,
}; };
use lemmy_structs::{blocking, send_email_to_user, user::*};
use lemmy_utils::{ use lemmy_utils::{
apub::generate_actor_keypair, apub::generate_actor_keypair,
claims::Claims, claims::Claims,
email::send_email, email::send_email,
location_info, location_info,
settings::Settings, settings::structs::Settings,
utils::{ utils::{
check_slurs, check_slurs,
generate_random_string, generate_random_string,
@ -104,24 +93,24 @@ impl Perform for Login {
// Fetch that username / email // Fetch that username / email
let username_or_email = data.username_or_email.clone(); let username_or_email = data.username_or_email.clone();
let user = match blocking(context.pool(), move |conn| { let local_user_view = match blocking(context.pool(), move |conn| {
User_::find_by_email_or_username(conn, &username_or_email) LocalUserView::find_by_email_or_name(conn, &username_or_email)
}) })
.await? .await?
{ {
Ok(user) => user, Ok(uv) => uv,
Err(_e) => return Err(ApiError::err("couldnt_find_that_username_or_email").into()), Err(_e) => return Err(ApiError::err("couldnt_find_that_username_or_email").into()),
}; };
// Verify the password // Verify the password
let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false); let valid: bool = verify(&data.password, &local_user_view.local_user.password_encrypted).unwrap_or(false);
if !valid { if !valid {
return Err(ApiError::err("password_incorrect").into()); return Err(ApiError::err("password_incorrect").into());
} }
// Return the jwt // Return the jwt
Ok(LoginResponse { Ok(LoginResponse {
jwt: Claims::jwt(user.id, Settings::get().hostname)?, jwt: Claims::jwt(local_user_view.person.id, Settings::get().hostname())?,
}) })
} }
} }
@ -144,10 +133,7 @@ impl Perform for Register {
} }
} }
// Password length check password_length_check(&data.password)?;
if data.password.len() > 60 {
return Err(ApiError::err("invalid_password").into());
}
// Make sure passwords match // Make sure passwords match
if data.password != data.password_verify { if data.password != data.password_verify {
@ -156,12 +142,12 @@ impl Perform for Register {
// Check if there are admins. False if admins exist // Check if there are admins. False if admins exist
let no_admins = blocking(context.pool(), move |conn| { let no_admins = blocking(context.pool(), move |conn| {
UserViewSafe::admins(conn).map(|a| a.is_empty()) PersonViewSafe::admins(conn).map(|a| a.is_empty())
}) })
.await??; .await??;
// If its not the admin, check the captcha // If its not the admin, check the captcha
if !no_admins && Settings::get().captcha.enabled { if !no_admins && Settings::get().captcha().enabled {
let check = context let check = context
.chat_server() .chat_server()
.send(CheckCaptcha { .send(CheckCaptcha {
@ -182,25 +168,24 @@ impl Perform for Register {
check_slurs(&data.username)?; check_slurs(&data.username)?;
let user_keypair = generate_actor_keypair()?; let actor_keypair = generate_actor_keypair()?;
if !is_valid_username(&data.username) { if !is_valid_username(&data.username) {
return Err(ApiError::err("invalid_username").into()); return Err(ApiError::err("invalid_username").into());
} }
let user_actor_id = generate_apub_endpoint(EndpointType::User, &data.username)?; let actor_id = generate_apub_endpoint(EndpointType::Person, &data.username)?;
// Register the new user // We have to create both a person, and local_user
let user_form = UserForm {
// Register the new person
let person_form = PersonForm {
name: data.username.to_owned(), name: data.username.to_owned(),
email: Some(data.email.to_owned()),
matrix_user_id: None,
avatar: None, avatar: None,
banner: None, banner: None,
password_encrypted: data.password.to_owned(),
preferred_username: None, preferred_username: None,
published: None, published: None,
updated: None, updated: None,
admin: no_admins, banned: None,
banned: Some(false), deleted: None,
show_nsfw: data.show_nsfw, show_nsfw: data.show_nsfw,
theme: "browser".into(), theme: "browser".into(),
default_sort_type: SortType::Active as i16, default_sort_type: SortType::Active as i16,
@ -208,36 +193,61 @@ impl Perform for Register {
lang: "browser".into(), lang: "browser".into(),
show_avatars: true, show_avatars: true,
send_notifications_to_email: false, send_notifications_to_email: false,
actor_id: Some(user_actor_id.clone()), actor_id: Some(actor_id.clone()),
bio: None, bio: None,
local: true, local: true,
private_key: Some(user_keypair.private_key), private_key: Some(actor_keypair.private_key),
public_key: Some(user_keypair.public_key), public_key: Some(actor_keypair.public_key),
last_refreshed_at: None, last_refreshed_at: None,
inbox_url: Some(generate_inbox_url(&user_actor_id)?), inbox_url: Some(generate_inbox_url(&actor_id)?),
shared_inbox_url: Some(Some(generate_shared_inbox_url(&user_actor_id)?)), shared_inbox_url: Some(Some(generate_shared_inbox_url(&actor_id)?)),
}; };
// Create the user // insert the person
let inserted_user = match blocking(context.pool(), move |conn| { let inserted_person = match blocking(context.pool(), move |conn| {
User_::register(conn, &user_form) Person::create(conn, &person_form)
}) })
.await? .await?
{ {
Ok(user) => user, Ok(user) => user,
Err(e) => {
return Err(ApiError::err("user_already_exists").into());
}
};
// Create the local user
let local_user_form = LocalUserForm {
person_id: inserted_person.id,
email: Some(data.email.to_owned()),
matrix_user_id: None,
password_encrypted: data.password.to_owned(),
admin: no_admins,
};
let inserted_local_user = match blocking(context.pool(), move |conn| {
LocalUser::register(conn, &local_user_form)
})
.await?
{
Ok(lu) => lu,
Err(e) => { Err(e) => {
let err_type = if e.to_string() let err_type = if e.to_string()
== "duplicate key value violates unique constraint \"user__email_key\"" == "duplicate key value violates unique constraint \"local_user_email_key\""
{ {
"email_already_exists" "email_already_exists"
} else { } else {
"user_already_exists" "user_already_exists"
}; };
// If the local user creation errored, then delete that person
blocking(context.pool(), move |conn| Person::delete(&conn, inserted_person.id)).await??;
return Err(ApiError::err(err_type).into()); return Err(ApiError::err(err_type).into());
} }
}; };
let main_community_keypair = generate_actor_keypair()?; let main_community_keypair = generate_actor_keypair()?;
// Create the main community if it doesn't exist // Create the main community if it doesn't exist
@ -252,7 +262,7 @@ impl Perform for Register {
title: "The Default Community".to_string(), title: "The Default Community".to_string(),
description: Some("The Default Community".to_string()), description: Some("The Default Community".to_string()),
nsfw: false, nsfw: false,
creator_id: inserted_user.id, creator_id: inserted_person.id,
removed: None, removed: None,
deleted: None, deleted: None,
updated: None, updated: None,
@ -278,7 +288,7 @@ impl Perform for Register {
// Sign them up for main community no matter what // Sign them up for main community no matter what
let community_follower_form = CommunityFollowerForm { let community_follower_form = CommunityFollowerForm {
community_id: main_community.id, community_id: main_community.id,
person_id: inserted_user.id, person_id: inserted_person.id,
pending: false, pending: false,
}; };
@ -291,7 +301,7 @@ impl Perform for Register {
if no_admins { if no_admins {
let community_moderator_form = CommunityModeratorForm { let community_moderator_form = CommunityModeratorForm {
community_id: main_community.id, community_id: main_community.id,
person_id: inserted_user.id, person_id: inserted_person.id,
}; };
let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form); let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
@ -302,7 +312,7 @@ impl Perform for Register {
// Return the jwt // Return the jwt
Ok(LoginResponse { Ok(LoginResponse {
jwt: Claims::jwt(inserted_user.id, Settings::get().hostname)?, jwt: Claims::jwt(inserted_person.id, Settings::get().hostname())?,
}) })
} }
} }
@ -316,7 +326,7 @@ impl Perform for GetCaptcha {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<Self::Response, LemmyError> { ) -> Result<Self::Response, LemmyError> {
let captcha_settings = Settings::get().captcha; let captcha_settings = Settings::get().captcha();
if !captcha_settings.enabled { if !captcha_settings.enabled {
return Ok(GetCaptchaResponse { ok: None }); return Ok(GetCaptchaResponse { ok: None });
@ -364,19 +374,15 @@ impl Perform for SaveUserSettings {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<LoginResponse, LemmyError> { ) -> Result<LoginResponse, LemmyError> {
let data: &SaveUserSettings = &self; let data: &SaveUserSettings = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let avatar = diesel_option_overwrite(&data.avatar); let avatar = diesel_option_overwrite_to_url(&data.avatar)?;
let banner = diesel_option_overwrite(&data.banner); let banner = diesel_option_overwrite_to_url(&data.banner)?;
let email = diesel_option_overwrite(&data.email); let email = diesel_option_overwrite(&data.email);
let bio = diesel_option_overwrite(&data.bio); let bio = diesel_option_overwrite(&data.bio);
let preferred_username = diesel_option_overwrite(&data.preferred_username); let preferred_username = diesel_option_overwrite(&data.preferred_username);
let matrix_user_id = diesel_option_overwrite(&data.matrix_user_id); let matrix_user_id = diesel_option_overwrite(&data.matrix_user_id);
// Check to make sure the avatar and banners are urls
check_optional_url(&avatar)?;
check_optional_url(&banner)?;
if let Some(Some(bio)) = &bio { if let Some(Some(bio)) = &bio {
if bio.chars().count() > 300 { if bio.chars().count() > 300 {
return Err(ApiError::err("bio_length_overflow").into()); return Err(ApiError::err("bio_length_overflow").into());
@ -394,6 +400,8 @@ impl Perform for SaveUserSettings {
Some(new_password) => { Some(new_password) => {
match &data.new_password_verify { match &data.new_password_verify {
Some(new_password_verify) => { Some(new_password_verify) => {
password_length_check(&new_password)?;
// Make sure passwords match // Make sure passwords match
if new_password != new_password_verify { if new_password != new_password_verify {
return Err(ApiError::err("passwords_dont_match").into()); return Err(ApiError::err("passwords_dont_match").into());
@ -475,7 +483,7 @@ impl Perform for SaveUserSettings {
// Return the jwt // Return the jwt
Ok(LoginResponse { Ok(LoginResponse {
jwt: Claims::jwt(updated_user.id, Settings::get().hostname)?, jwt: Claims::jwt(updated_user.id, Settings::get().hostname())?,
}) })
} }
} }
@ -490,7 +498,7 @@ impl Perform for GetUserDetails {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<GetUserDetailsResponse, LemmyError> { ) -> Result<GetUserDetailsResponse, LemmyError> {
let data: &GetUserDetails = &self; let data: &GetUserDetails = &self;
let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?; let user = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
let show_nsfw = match &user { let show_nsfw = match &user {
Some(user) => user.show_nsfw, Some(user) => user.show_nsfw,
@ -542,7 +550,7 @@ impl Perform for GetUserDetails {
.limit(limit); .limit(limit);
let mut comments_query = CommentQueryBuilder::create(conn) let mut comments_query = CommentQueryBuilder::create(conn)
.my_user_id(user_id) .my_person_id(user_id)
.sort(&sort) .sort(&sort)
.saved_only(saved_only) .saved_only(saved_only)
.page(page) .page(page)
@ -572,7 +580,7 @@ impl Perform for GetUserDetails {
} }
}; };
let moderates = blocking(context.pool(), move |conn| { let moderates = blocking(context.pool(), move |conn| {
CommunityModeratorView::for_user(conn, user_details_id) CommunityModeratorView::for_person(conn, user_details_id)
}) })
.await??; .await??;
@ -597,7 +605,7 @@ impl Perform for AddAdmin {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<AddAdminResponse, LemmyError> { ) -> Result<AddAdminResponse, LemmyError> {
let data: &AddAdmin = &self; let data: &AddAdmin = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
// Make sure user is an admin // Make sure user is an admin
is_admin(context.pool(), user.id).await?; is_admin(context.pool(), user.id).await?;
@ -653,7 +661,7 @@ impl Perform for BanUser {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<BanUserResponse, LemmyError> { ) -> Result<BanUserResponse, LemmyError> {
let data: &BanUser = &self; let data: &BanUser = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
// Make sure user is an admin // Make sure user is an admin
is_admin(context.pool(), user.id).await?; is_admin(context.pool(), user.id).await?;
@ -714,7 +722,7 @@ impl Perform for BanUser {
}; };
context.chat_server().do_send(SendAllMessage { context.chat_server().do_send(SendAllMessage {
op: UserOperation::BanUser, op: UserOperation::BanPerson,
response: res.clone(), response: res.clone(),
websocket_id, websocket_id,
}); });
@ -733,7 +741,7 @@ impl Perform for GetReplies {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<GetRepliesResponse, LemmyError> { ) -> Result<GetRepliesResponse, LemmyError> {
let data: &GetReplies = &self; let data: &GetReplies = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let sort = SortType::from_str(&data.sort)?; let sort = SortType::from_str(&data.sort)?;
@ -746,7 +754,7 @@ impl Perform for GetReplies {
.sort(&sort) .sort(&sort)
.unread_only(unread_only) .unread_only(unread_only)
.recipient_id(user_id) .recipient_id(user_id)
.my_user_id(user_id) .my_person_id(user_id)
.page(page) .page(page)
.limit(limit) .limit(limit)
.list() .list()
@ -767,7 +775,7 @@ impl Perform for GetUserMentions {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<GetUserMentionsResponse, LemmyError> { ) -> Result<GetUserMentionsResponse, LemmyError> {
let data: &GetUserMentions = &self; let data: &GetUserMentions = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let sort = SortType::from_str(&data.sort)?; let sort = SortType::from_str(&data.sort)?;
@ -801,7 +809,7 @@ impl Perform for MarkUserMentionAsRead {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<UserMentionResponse, LemmyError> { ) -> Result<UserMentionResponse, LemmyError> {
let data: &MarkUserMentionAsRead = &self; let data: &MarkUserMentionAsRead = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let user_mention_id = data.user_mention_id; let user_mention_id = data.user_mention_id;
let read_user_mention = blocking(context.pool(), move |conn| { let read_user_mention = blocking(context.pool(), move |conn| {
@ -841,12 +849,12 @@ impl Perform for MarkAllAsRead {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<GetRepliesResponse, LemmyError> { ) -> Result<GetRepliesResponse, LemmyError> {
let data: &MarkAllAsRead = &self; let data: &MarkAllAsRead = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let user_id = user.id; let user_id = user.id;
let replies = blocking(context.pool(), move |conn| { let replies = blocking(context.pool(), move |conn| {
CommentQueryBuilder::create(conn) CommentQueryBuilder::create(conn)
.my_user_id(user_id) .my_person_id(user_id)
.recipient_id(user_id) .recipient_id(user_id)
.unread_only(true) .unread_only(true)
.page(1) .page(1)
@ -895,7 +903,7 @@ impl Perform for DeleteAccount {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<LoginResponse, LemmyError> { ) -> Result<LoginResponse, LemmyError> {
let data: &DeleteAccount = &self; let data: &DeleteAccount = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
// Verify the password // Verify the password
let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false); let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false);
@ -993,6 +1001,8 @@ impl Perform for PasswordChange {
}) })
.await??; .await??;
password_length_check(&data.password)?;
// Make sure passwords match // Make sure passwords match
if data.password != data.password_verify { if data.password != data.password_verify {
return Err(ApiError::err("passwords_dont_match").into()); return Err(ApiError::err("passwords_dont_match").into());
@ -1011,7 +1021,7 @@ impl Perform for PasswordChange {
// Return the jwt // Return the jwt
Ok(LoginResponse { Ok(LoginResponse {
jwt: Claims::jwt(updated_user.id, Settings::get().hostname)?, jwt: Claims::jwt(updated_user.id, Settings::get().hostname())?,
}) })
} }
} }
@ -1026,7 +1036,7 @@ impl Perform for CreatePrivateMessage {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<PrivateMessageResponse, LemmyError> { ) -> Result<PrivateMessageResponse, LemmyError> {
let data: &CreatePrivateMessage = &self; let data: &CreatePrivateMessage = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let content_slurs_removed = remove_slurs(&data.content.to_owned()); let content_slurs_removed = remove_slurs(&data.content.to_owned());
@ -1119,7 +1129,7 @@ impl Perform for EditPrivateMessage {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<PrivateMessageResponse, LemmyError> { ) -> Result<PrivateMessageResponse, LemmyError> {
let data: &EditPrivateMessage = &self; let data: &EditPrivateMessage = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
// Checking permissions // Checking permissions
let private_message_id = data.private_message_id; let private_message_id = data.private_message_id;
@ -1178,7 +1188,7 @@ impl Perform for DeletePrivateMessage {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<PrivateMessageResponse, LemmyError> { ) -> Result<PrivateMessageResponse, LemmyError> {
let data: &DeletePrivateMessage = &self; let data: &DeletePrivateMessage = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
// Checking permissions // Checking permissions
let private_message_id = data.private_message_id; let private_message_id = data.private_message_id;
@ -1243,7 +1253,7 @@ impl Perform for MarkPrivateMessageAsRead {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<PrivateMessageResponse, LemmyError> { ) -> Result<PrivateMessageResponse, LemmyError> {
let data: &MarkPrivateMessageAsRead = &self; let data: &MarkPrivateMessageAsRead = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
// Checking permissions // Checking permissions
let private_message_id = data.private_message_id; let private_message_id = data.private_message_id;
@ -1301,7 +1311,7 @@ impl Perform for GetPrivateMessages {
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<PrivateMessagesResponse, LemmyError> { ) -> Result<PrivateMessagesResponse, LemmyError> {
let data: &GetPrivateMessages = &self; let data: &GetPrivateMessages = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let user_id = user.id; let user_id = user.id;
let page = data.page; let page = data.page;
@ -1332,7 +1342,7 @@ impl Perform for GetReportCount {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<GetReportCountResponse, LemmyError> { ) -> Result<GetReportCountResponse, LemmyError> {
let data: &GetReportCount = &self; let data: &GetReportCount = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let user_id = user.id; let user_id = user.id;
let community_id = data.community; let community_id = data.community;

View file

@ -1,6 +1,6 @@
use crate::{get_user_from_jwt, Perform}; use crate::{get_local_user_view_from_jwt, Perform};
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_structs::websocket::*; use lemmy_api_structs::websocket::*;
use lemmy_utils::{ConnectionId, LemmyError}; use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::{ use lemmy_websocket::{
messages::{JoinCommunityRoom, JoinModRoom, JoinPostRoom, JoinUserRoom}, messages::{JoinCommunityRoom, JoinModRoom, JoinPostRoom, JoinUserRoom},
@ -17,11 +17,12 @@ impl Perform for UserJoin {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<UserJoinResponse, LemmyError> { ) -> Result<UserJoinResponse, LemmyError> {
let data: &UserJoin = &self; let data: &UserJoin = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
if let Some(ws_id) = websocket_id { if let Some(ws_id) = websocket_id {
context.chat_server().do_send(JoinUserRoom { context.chat_server().do_send(JoinUserRoom {
user_id: user.id, // TODO this should probably be the local_user_id
user_id: local_user_view.person.id,
id: ws_id, id: ws_id,
}); });
} }

View file

@ -1,12 +1,12 @@
[package] [package]
name = "lemmy_structs" name = "lemmy_api_structs"
version = "0.1.0" version = "0.1.0"
authors = ["Felix Ableitner <me@nutomic.com>"]
edition = "2018" edition = "2018"
[lib] [lib]
name = "lemmy_structs" name = "lemmy_api_structs"
path = "src/lib.rs" path = "src/lib.rs"
doctest = false
[dependencies] [dependencies]
lemmy_db_queries = { path = "../db_queries" } lemmy_db_queries = { path = "../db_queries" }
@ -21,4 +21,4 @@ diesel = "1.4.5"
actix-web = "3.3.2" actix-web = "3.3.2"
chrono = { version = "0.4.19", features = ["serde"] } chrono = { version = "0.4.19", features = ["serde"] }
serde_json = { version = "1.0.61", features = ["preserve_order"] } serde_json = { version = "1.0.61", features = ["preserve_order"] }
url = "2.2.0" url = "2.2.1"

View file

@ -2,7 +2,7 @@ use lemmy_db_views_actor::{
community_follower_view::CommunityFollowerView, community_follower_view::CommunityFollowerView,
community_moderator_view::CommunityModeratorView, community_moderator_view::CommunityModeratorView,
community_view::CommunityView, community_view::CommunityView,
user_view::UserViewSafe, person_view::PersonViewSafe,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -27,7 +27,6 @@ pub struct CreateCommunity {
pub description: Option<String>, pub description: Option<String>,
pub icon: Option<String>, pub icon: Option<String>,
pub banner: Option<String>, pub banner: Option<String>,
pub category_id: i32,
pub nsfw: bool, pub nsfw: bool,
pub auth: String, pub auth: String,
} }
@ -54,7 +53,7 @@ pub struct ListCommunitiesResponse {
#[derive(Deserialize, Clone)] #[derive(Deserialize, Clone)]
pub struct BanFromCommunity { pub struct BanFromCommunity {
pub community_id: i32, pub community_id: i32,
pub user_id: i32, pub person_id: i32,
pub ban: bool, pub ban: bool,
pub remove_data: bool, pub remove_data: bool,
pub reason: Option<String>, pub reason: Option<String>,
@ -64,14 +63,14 @@ pub struct BanFromCommunity {
#[derive(Serialize, Clone)] #[derive(Serialize, Clone)]
pub struct BanFromCommunityResponse { pub struct BanFromCommunityResponse {
pub user_view: UserViewSafe, pub person_view: PersonViewSafe,
pub banned: bool, pub banned: bool,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct AddModToCommunity { pub struct AddModToCommunity {
pub community_id: i32, pub community_id: i32,
pub user_id: i32, pub person_id: i32,
pub added: bool, pub added: bool,
pub auth: String, pub auth: String,
} }
@ -88,7 +87,6 @@ pub struct EditCommunity {
pub description: Option<String>, pub description: Option<String>,
pub icon: Option<String>, pub icon: Option<String>,
pub banner: Option<String>, pub banner: Option<String>,
pub category_id: i32,
pub nsfw: bool, pub nsfw: bool,
pub auth: String, pub auth: String,
} }
@ -129,6 +127,6 @@ pub struct GetFollowedCommunitiesResponse {
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct TransferCommunity { pub struct TransferCommunity {
pub community_id: i32, pub community_id: i32,
pub user_id: i32, pub person_id: i32,
pub auth: String, pub auth: String,
} }

View file

@ -2,18 +2,19 @@ pub mod comment;
pub mod community; pub mod community;
pub mod post; pub mod post;
pub mod site; pub mod site;
pub mod user; pub mod person;
pub mod websocket; pub mod websocket;
use diesel::PgConnection; use diesel::PgConnection;
use lemmy_db_queries::{source::user::User, Crud, DbPool}; use lemmy_db_queries::{Crud, DbPool};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
comment::Comment, comment::Comment,
post::Post, post::Post,
user::User_, person::Person,
user_mention::{UserMention, UserMentionForm}, person_mention::{PersonMention, PersonMentionForm},
}; };
use lemmy_utils::{email::send_email, settings::Settings, utils::MentionData, LemmyError}; use lemmy_db_views::local_user_view::LocalUserView;
use lemmy_utils::{email::send_email, settings::structs::Settings, utils::MentionData, LemmyError};
use log::error; use log::error;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
@ -54,25 +55,25 @@ where
pub async fn send_local_notifs( pub async fn send_local_notifs(
mentions: Vec<MentionData>, mentions: Vec<MentionData>,
comment: Comment, comment: Comment,
user: &User_, person: Person,
post: Post, post: Post,
pool: &DbPool, pool: &DbPool,
do_send_email: bool, do_send_email: bool,
) -> Result<Vec<i32>, LemmyError> { ) -> Result<Vec<i32>, LemmyError> {
let user2 = user.clone();
let ids = blocking(pool, move |conn| { let ids = blocking(pool, move |conn| {
do_send_local_notifs(conn, &mentions, &comment, &user2, &post, do_send_email) do_send_local_notifs(conn, &mentions, &comment, &person, &post, do_send_email)
}) })
.await?; .await?;
Ok(ids) Ok(ids)
} }
// TODO should this really use person_ids as recipient ids? or local_user_ids ?
fn do_send_local_notifs( fn do_send_local_notifs(
conn: &PgConnection, conn: &PgConnection,
mentions: &[MentionData], mentions: &[MentionData],
comment: &Comment, comment: &Comment,
user: &User_, person: &Person,
post: &Post, post: &Post,
do_send_email: bool, do_send_email: bool,
) -> Vec<i32> { ) -> Vec<i32> {
@ -81,31 +82,32 @@ fn do_send_local_notifs(
// Send the local mentions // Send the local mentions
for mention in mentions for mention in mentions
.iter() .iter()
.filter(|m| m.is_local() && m.name.ne(&user.name)) .filter(|m| m.is_local() && m.name.ne(&person.name))
.collect::<Vec<&MentionData>>() .collect::<Vec<&MentionData>>()
{ {
if let Ok(mention_user) = User_::read_from_name(&conn, &mention.name) { // TODO do a local user fetch
if let Ok(mention_user_view) = LocalUserView::read_from_name(&conn, &mention.name) {
// TODO // TODO
// At some point, make it so you can't tag the parent creator either // At some point, make it so you can't tag the parent creator either
// This can cause two notifications, one for reply and the other for mention // This can cause two notifications, one for reply and the other for mention
recipient_ids.push(mention_user.id); recipient_ids.push(mention_user_view.person.id);
let user_mention_form = UserMentionForm { let user_mention_form = PersonMentionForm {
recipient_id: mention_user.id, recipient_id: mention_user_view.person.id,
comment_id: comment.id, comment_id: comment.id,
read: None, read: None,
}; };
// Allow this to fail softly, since comment edits might re-update or replace it // Allow this to fail softly, since comment edits might re-update or replace it
// Let the uniqueness handle this fail // Let the uniqueness handle this fail
let _ = UserMention::create(&conn, &user_mention_form); PersonMention::create(&conn, &user_mention_form).ok();
// Send an email to those users that have notifications on // Send an email to those users that have notifications on
if do_send_email && mention_user.send_notifications_to_email { if do_send_email && mention_user_view.local_user.send_notifications_to_email {
send_email_to_user( send_email_to_user(
mention_user, mention_user_view,
"Mentioned by", "Mentioned by",
"User Mention", "Person Mention",
&comment.content, &comment.content,
) )
} }
@ -116,12 +118,12 @@ fn do_send_local_notifs(
match comment.parent_id { match comment.parent_id {
Some(parent_id) => { Some(parent_id) => {
if let Ok(parent_comment) = Comment::read(&conn, parent_id) { if let Ok(parent_comment) = Comment::read(&conn, parent_id) {
if parent_comment.creator_id != user.id { if parent_comment.creator_id != person.id {
if let Ok(parent_user) = User_::read(&conn, parent_comment.creator_id) { if let Ok(parent_user_view) = LocalUserView::read(&conn, parent_comment.creator_id) {
recipient_ids.push(parent_user.id); recipient_ids.push(parent_user_view.person.id);
if do_send_email && parent_user.send_notifications_to_email { if do_send_email && parent_user_view.local_user.send_notifications_to_email {
send_email_to_user(parent_user, "Reply from", "Comment Reply", &comment.content) send_email_to_user(parent_user_view, "Reply from", "Comment Reply", &comment.content)
} }
} }
} }
@ -129,12 +131,12 @@ fn do_send_local_notifs(
} }
// Its a post // Its a post
None => { None => {
if post.creator_id != user.id { if post.creator_id != person.id {
if let Ok(parent_user) = User_::read(&conn, post.creator_id) { if let Ok(parent_user_view) = LocalUserView::read(&conn, post.creator_id) {
recipient_ids.push(parent_user.id); recipient_ids.push(parent_user_view.person.id);
if do_send_email && parent_user.send_notifications_to_email { if do_send_email && parent_user_view.local_user.send_notifications_to_email {
send_email_to_user(parent_user, "Reply from", "Post Reply", &comment.content) send_email_to_user(parent_user_view, "Reply from", "Post Reply", &comment.content)
} }
} }
} }
@ -143,26 +145,26 @@ fn do_send_local_notifs(
recipient_ids recipient_ids
} }
pub fn send_email_to_user(user: User_, subject_text: &str, body_text: &str, comment_content: &str) { pub fn send_email_to_user(local_user_view: LocalUserView, subject_text: &str, body_text: &str, comment_content: &str) {
if user.banned { if local_user_view.person.banned {
return; return;
} }
if let Some(user_email) = user.email { if let Some(user_email) = local_user_view.local_user.email {
let subject = &format!( let subject = &format!(
"{} - {} {}", "{} - {} {}",
subject_text, subject_text,
Settings::get().hostname, Settings::get().hostname(),
user.name, local_user_view.person.name,
); );
let html = &format!( let html = &format!(
"<h1>{}</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>", "<h1>{}</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
body_text, body_text,
user.name, local_user_view.person.name,
comment_content, comment_content,
Settings::get().get_protocol_and_hostname() Settings::get().get_protocol_and_hostname()
); );
match send_email(subject, &user_email, &user.name, html) { match send_email(subject, &user_email, &local_user_view.person.name, html) {
Ok(_o) => _o, Ok(_o) => _o,
Err(e) => error!("{}", e), Err(e) => error!("{}", e),
}; };

View file

@ -6,8 +6,8 @@ use lemmy_db_views::{
use lemmy_db_views_actor::{ use lemmy_db_views_actor::{
community_follower_view::CommunityFollowerView, community_follower_view::CommunityFollowerView,
community_moderator_view::CommunityModeratorView, community_moderator_view::CommunityModeratorView,
user_mention_view::UserMentionView, person_mention_view::PersonMentionView,
user_view::UserViewSafe, person_view::PersonViewSafe,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -70,8 +70,8 @@ pub struct LoginResponse {
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct GetUserDetails { pub struct GetPersonDetails {
pub user_id: Option<i32>, pub person_id: Option<i32>,
pub username: Option<String>, pub username: Option<String>,
pub sort: String, pub sort: String,
pub page: Option<i64>, pub page: Option<i64>,
@ -82,8 +82,8 @@ pub struct GetUserDetails {
} }
#[derive(Serialize)] #[derive(Serialize)]
pub struct GetUserDetailsResponse { pub struct GetPersonDetailsResponse {
pub user_view: UserViewSafe, pub person_view: PersonViewSafe,
pub follows: Vec<CommunityFollowerView>, pub follows: Vec<CommunityFollowerView>,
pub moderates: Vec<CommunityModeratorView>, pub moderates: Vec<CommunityModeratorView>,
pub comments: Vec<CommentView>, pub comments: Vec<CommentView>,
@ -96,8 +96,8 @@ pub struct GetRepliesResponse {
} }
#[derive(Serialize)] #[derive(Serialize)]
pub struct GetUserMentionsResponse { pub struct GetPersonMentionsResponse {
pub mentions: Vec<UserMentionView>, pub mentions: Vec<PersonMentionView>,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -107,19 +107,19 @@ pub struct MarkAllAsRead {
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct AddAdmin { pub struct AddAdmin {
pub user_id: i32, pub person_id: i32,
pub added: bool, pub added: bool,
pub auth: String, pub auth: String,
} }
#[derive(Serialize, Clone)] #[derive(Serialize, Clone)]
pub struct AddAdminResponse { pub struct AddAdminResponse {
pub admins: Vec<UserViewSafe>, pub admins: Vec<PersonViewSafe>,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct BanUser { pub struct BanPerson {
pub user_id: i32, pub person_id: i32,
pub ban: bool, pub ban: bool,
pub remove_data: bool, pub remove_data: bool,
pub reason: Option<String>, pub reason: Option<String>,
@ -128,8 +128,8 @@ pub struct BanUser {
} }
#[derive(Serialize, Clone)] #[derive(Serialize, Clone)]
pub struct BanUserResponse { pub struct BanPersonResponse {
pub user_view: UserViewSafe, pub person_view: PersonViewSafe,
pub banned: bool, pub banned: bool,
} }
@ -143,7 +143,7 @@ pub struct GetReplies {
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct GetUserMentions { pub struct GetPersonMentions {
pub sort: String, pub sort: String,
pub page: Option<i64>, pub page: Option<i64>,
pub limit: Option<i64>, pub limit: Option<i64>,
@ -152,15 +152,15 @@ pub struct GetUserMentions {
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct MarkUserMentionAsRead { pub struct MarkPersonMentionAsRead {
pub user_mention_id: i32, pub person_mention_id: i32,
pub read: bool, pub read: bool,
pub auth: String, pub auth: String,
} }
#[derive(Serialize, Clone)] #[derive(Serialize, Clone)]
pub struct UserMentionResponse { pub struct PersonMentionResponse {
pub user_mention_view: UserMentionView, pub person_mention_view: PersonMentionView,
} }
#[derive(Deserialize)] #[derive(Deserialize)]

View file

@ -8,11 +8,12 @@ use lemmy_db_views_actor::{
community_view::CommunityView, community_view::CommunityView,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct CreatePost { pub struct CreatePost {
pub name: String, pub name: String,
pub url: Option<String>, pub url: Option<Url>,
pub body: Option<String>, pub body: Option<String>,
pub nsfw: bool, pub nsfw: bool,
pub community_id: i32, pub community_id: i32,
@ -66,7 +67,7 @@ pub struct CreatePostLike {
pub struct EditPost { pub struct EditPost {
pub post_id: i32, pub post_id: i32,
pub name: String, pub name: String,
pub url: Option<String>, pub url: Option<Url>,
pub body: Option<String>, pub body: Option<String>,
pub nsfw: bool, pub nsfw: bool,
pub auth: String, pub auth: String,

View file

@ -1,6 +1,5 @@
use lemmy_db_schema::source::user::UserSafeSettings; use lemmy_db_views::{comment_view::CommentView, post_view::PostView, site_view::SiteView, local_user_view::LocalUserSettingsView};
use lemmy_db_views::{comment_view::CommentView, post_view::PostView, site_view::SiteView}; use lemmy_db_views_actor::{community_view::CommunityView, person_view::PersonViewSafe};
use lemmy_db_views_actor::{community_view::CommunityView, user_view::UserViewSafe};
use lemmy_db_views_moderator::{ use lemmy_db_views_moderator::{
mod_add_community_view::ModAddCommunityView, mod_add_community_view::ModAddCommunityView,
mod_add_view::ModAddView, mod_add_view::ModAddView,
@ -13,6 +12,7 @@ use lemmy_db_views_moderator::{
mod_sticky_post_view::ModStickyPostView, mod_sticky_post_view::ModStickyPostView,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct Search { pub struct Search {
@ -32,12 +32,12 @@ pub struct SearchResponse {
pub comments: Vec<CommentView>, pub comments: Vec<CommentView>,
pub posts: Vec<PostView>, pub posts: Vec<PostView>,
pub communities: Vec<CommunityView>, pub communities: Vec<CommunityView>,
pub users: Vec<UserViewSafe>, pub users: Vec<PersonViewSafe>,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct GetModlog { pub struct GetModlog {
pub mod_user_id: Option<i32>, pub mod_person_id: Option<i32>,
pub community_id: Option<i32>, pub community_id: Option<i32>,
pub page: Option<i64>, pub page: Option<i64>,
pub limit: Option<i64>, pub limit: Option<i64>,
@ -60,8 +60,8 @@ pub struct GetModlogResponse {
pub struct CreateSite { pub struct CreateSite {
pub name: String, pub name: String,
pub description: Option<String>, pub description: Option<String>,
pub icon: Option<String>, pub icon: Option<Url>,
pub banner: Option<String>, pub banner: Option<Url>,
pub enable_downvotes: bool, pub enable_downvotes: bool,
pub open_registration: bool, pub open_registration: bool,
pub enable_nsfw: bool, pub enable_nsfw: bool,
@ -93,17 +93,17 @@ pub struct SiteResponse {
#[derive(Serialize)] #[derive(Serialize)]
pub struct GetSiteResponse { pub struct GetSiteResponse {
pub site_view: Option<SiteView>, // Because the site might not be set up yet pub site_view: Option<SiteView>, // Because the site might not be set up yet
pub admins: Vec<UserViewSafe>, pub admins: Vec<PersonViewSafe>,
pub banned: Vec<UserViewSafe>, pub banned: Vec<PersonViewSafe>,
pub online: usize, pub online: usize,
pub version: String, pub version: String,
pub my_user: Option<UserSafeSettings>, pub my_user: Option<LocalUserSettingsView>,
pub federated_instances: Option<FederatedInstances>, // Federation may be disabled pub federated_instances: Option<FederatedInstances>, // Federation may be disabled
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct TransferSite { pub struct TransferSite {
pub user_id: i32, pub person_id: i32,
pub auth: String, pub auth: String,
} }
@ -126,6 +126,6 @@ pub struct SaveSiteConfig {
#[derive(Serialize)] #[derive(Serialize)]
pub struct FederatedInstances { pub struct FederatedInstances {
pub linked: Vec<String>, pub linked: Vec<String>,
pub allowed: Vec<String>, pub allowed: Option<Vec<String>>,
pub blocked: Vec<String>, pub blocked: Option<Vec<String>>,
} }

View file

@ -1,12 +1,12 @@
[package] [package]
name = "lemmy_apub" name = "lemmy_apub"
version = "0.1.0" version = "0.1.0"
authors = ["Felix Ableitner <me@nutomic.com>"]
edition = "2018" edition = "2018"
[lib] [lib]
name = "lemmy_apub" name = "lemmy_apub"
path = "src/lib.rs" path = "src/lib.rs"
doctest = false
[dependencies] [dependencies]
lemmy_utils = { path = "../utils" } lemmy_utils = { path = "../utils" }
@ -14,7 +14,7 @@ lemmy_db_queries = { path = "../db_queries" }
lemmy_db_schema = { path = "../db_schema" } lemmy_db_schema = { path = "../db_schema" }
lemmy_db_views = { path = "../db_views" } lemmy_db_views = { path = "../db_views" }
lemmy_db_views_actor = { path = "../db_views_actor" } lemmy_db_views_actor = { path = "../db_views_actor" }
lemmy_structs = { path = "../structs" } lemmy_api_structs = { path = "../api_structs" }
lemmy_websocket = { path = "../websocket" } lemmy_websocket = { path = "../websocket" }
diesel = "1.4.5" diesel = "1.4.5"
activitystreams = "0.7.0-alpha.10" activitystreams = "0.7.0-alpha.10"
@ -32,7 +32,7 @@ rand = "0.8.3"
strum = "0.20.0" strum = "0.20.0"
strum_macros = "0.20.1" strum_macros = "0.20.1"
lazy_static = "1.4.0" lazy_static = "1.4.0"
url = { version = "2.2.0", features = ["serde"] } url = { version = "2.2.1", features = ["serde"] }
percent-encoding = "2.1.0" percent-encoding = "2.1.0"
openssl = "0.10.32" openssl = "0.10.32"
http = "0.2.3" http = "0.2.3"

View file

@ -1,16 +1,16 @@
use crate::{activities::receive::get_actor_as_user, objects::FromApub, ActorType, NoteExt}; use crate::{activities::receive::get_actor_as_person, objects::FromApub, ActorType, NoteExt};
use activitystreams::{ use activitystreams::{
activity::{ActorAndObjectRefExt, Create, Dislike, Like, Remove, Update}, activity::{ActorAndObjectRefExt, Create, Dislike, Like, Remove, Update},
base::ExtendsExt, base::ExtendsExt,
}; };
use anyhow::Context; use anyhow::Context;
use lemmy_api_structs::{blocking, comment::CommentResponse, send_local_notifs};
use lemmy_db_queries::{source::comment::Comment_, Crud, Likeable}; use lemmy_db_queries::{source::comment::Comment_, Crud, Likeable};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
comment::{Comment, CommentLike, CommentLikeForm}, comment::{Comment, CommentLike, CommentLikeForm},
post::Post, post::Post,
}; };
use lemmy_db_views::comment_view::CommentView; use lemmy_db_views::comment_view::CommentView;
use lemmy_structs::{blocking, comment::CommentResponse, send_local_notifs};
use lemmy_utils::{location_info, utils::scrape_text_for_mentions, LemmyError}; use lemmy_utils::{location_info, utils::scrape_text_for_mentions, LemmyError};
use lemmy_websocket::{messages::SendComment, LemmyContext, UserOperation}; use lemmy_websocket::{messages::SendComment, LemmyContext, UserOperation};
@ -19,11 +19,11 @@ pub(crate) async fn receive_create_comment(
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let user = get_actor_as_user(&create, context, request_counter).await?; let person = get_actor_as_person(&create, context, request_counter).await?;
let note = NoteExt::from_any_base(create.object().to_owned().one().context(location_info!())?)? let note = NoteExt::from_any_base(create.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?; .context(location_info!())?;
let comment = Comment::from_apub(&note, context, user.actor_id(), request_counter).await?; let comment = Comment::from_apub(&note, context, person.actor_id(), request_counter).await?;
let post_id = comment.post_id; let post_id = comment.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
@ -34,7 +34,7 @@ pub(crate) async fn receive_create_comment(
// anyway. // anyway.
let mentions = scrape_text_for_mentions(&comment.content); let mentions = scrape_text_for_mentions(&comment.content);
let recipient_ids = let recipient_ids =
send_local_notifs(mentions, comment.clone(), &user, post, context.pool(), true).await?; send_local_notifs(mentions, comment.clone(), person, post, context.pool(), true).await?;
// Refetch the view // Refetch the view
let comment_view = blocking(context.pool(), move |conn| { let comment_view = blocking(context.pool(), move |conn| {
@ -64,9 +64,9 @@ pub(crate) async fn receive_update_comment(
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let note = NoteExt::from_any_base(update.object().to_owned().one().context(location_info!())?)? let note = NoteExt::from_any_base(update.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?; .context(location_info!())?;
let user = get_actor_as_user(&update, context, request_counter).await?; let person = get_actor_as_person(&update, context, request_counter).await?;
let comment = Comment::from_apub(&note, context, user.actor_id(), request_counter).await?; let comment = Comment::from_apub(&note, context, person.actor_id(), request_counter).await?;
let comment_id = comment.id; let comment_id = comment.id;
let post_id = comment.post_id; let post_id = comment.post_id;
@ -74,7 +74,7 @@ pub(crate) async fn receive_update_comment(
let mentions = scrape_text_for_mentions(&comment.content); let mentions = scrape_text_for_mentions(&comment.content);
let recipient_ids = let recipient_ids =
send_local_notifs(mentions, comment, &user, post, context.pool(), false).await?; send_local_notifs(mentions, comment, person, post, context.pool(), false).await?;
// Refetch the view // Refetch the view
let comment_view = blocking(context.pool(), move |conn| { let comment_view = blocking(context.pool(), move |conn| {
@ -103,18 +103,18 @@ pub(crate) async fn receive_like_comment(
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let user = get_actor_as_user(&like, context, request_counter).await?; let person = get_actor_as_person(&like, context, request_counter).await?;
let comment_id = comment.id; let comment_id = comment.id;
let like_form = CommentLikeForm { let like_form = CommentLikeForm {
comment_id, comment_id,
post_id: comment.post_id, post_id: comment.post_id,
person_id: user.id, person_id: person.id,
score: 1, score: 1,
}; };
let user_id = user.id; let person_id = person.id;
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
CommentLike::remove(conn, user_id, comment_id)?; CommentLike::remove(conn, person_id, comment_id)?;
CommentLike::like(conn, &like_form) CommentLike::like(conn, &like_form)
}) })
.await??; .await??;
@ -148,18 +148,18 @@ pub(crate) async fn receive_dislike_comment(
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let user = get_actor_as_user(&dislike, context, request_counter).await?; let person = get_actor_as_person(&dislike, context, request_counter).await?;
let comment_id = comment.id; let comment_id = comment.id;
let like_form = CommentLikeForm { let like_form = CommentLikeForm {
comment_id, comment_id,
post_id: comment.post_id, post_id: comment.post_id,
person_id: user.id, person_id: person.id,
score: -1, score: -1,
}; };
let user_id = user.id; let person_id = person.id;
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
CommentLike::remove(conn, user_id, comment_id)?; CommentLike::remove(conn, person_id, comment_id)?;
CommentLike::like(conn, &like_form) CommentLike::like(conn, &like_form)
}) })
.await??; .await??;

View file

@ -1,9 +1,9 @@
use crate::activities::receive::get_actor_as_user; use crate::activities::receive::get_actor_as_person;
use activitystreams::activity::{Dislike, Like}; use activitystreams::activity::{Dislike, Like};
use lemmy_api_structs::{blocking, comment::CommentResponse};
use lemmy_db_queries::{source::comment::Comment_, Likeable}; use lemmy_db_queries::{source::comment::Comment_, Likeable};
use lemmy_db_schema::source::comment::{Comment, CommentLike}; use lemmy_db_schema::source::comment::{Comment, CommentLike};
use lemmy_db_views::comment_view::CommentView; use lemmy_db_views::comment_view::CommentView;
use lemmy_structs::{blocking, comment::CommentResponse};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{messages::SendComment, LemmyContext, UserOperation}; use lemmy_websocket::{messages::SendComment, LemmyContext, UserOperation};
@ -13,12 +13,12 @@ pub(crate) async fn receive_undo_like_comment(
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let user = get_actor_as_user(like, context, request_counter).await?; let person = get_actor_as_person(like, context, request_counter).await?;
let comment_id = comment.id; let comment_id = comment.id;
let user_id = user.id; let person_id = person.id;
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
CommentLike::remove(conn, user_id, comment_id) CommentLike::remove(conn, person_id, comment_id)
}) })
.await??; .await??;
@ -51,12 +51,12 @@ pub(crate) async fn receive_undo_dislike_comment(
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let user = get_actor_as_user(dislike, context, request_counter).await?; let person = get_actor_as_person(dislike, context, request_counter).await?;
let comment_id = comment.id; let comment_id = comment.id;
let user_id = user.id; let person_id = person.id;
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
CommentLike::remove(conn, user_id, comment_id) CommentLike::remove(conn, person_id, comment_id)
}) })
.await??; .await??;

View file

@ -4,10 +4,10 @@ use activitystreams::{
base::{AnyBase, ExtendsExt}, base::{AnyBase, ExtendsExt},
}; };
use anyhow::Context; use anyhow::Context;
use lemmy_api_structs::{blocking, community::CommunityResponse};
use lemmy_db_queries::{source::community::Community_, ApubObject}; use lemmy_db_queries::{source::community::Community_, ApubObject};
use lemmy_db_schema::source::community::Community; use lemmy_db_schema::source::community::Community;
use lemmy_db_views_actor::community_view::CommunityView; use lemmy_db_views_actor::community_view::CommunityView;
use lemmy_structs::{blocking, community::CommunityResponse};
use lemmy_utils::{location_info, LemmyError}; use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperation}; use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperation};
use url::Url; use url::Url;

View file

@ -1,11 +1,11 @@
use crate::fetcher::user::get_or_fetch_and_upsert_user; use crate::fetcher::person::get_or_fetch_and_upsert_person;
use activitystreams::{ use activitystreams::{
activity::{ActorAndObjectRef, ActorAndObjectRefExt}, activity::{ActorAndObjectRef, ActorAndObjectRefExt},
base::{AsBase, BaseExt}, base::{AsBase, BaseExt},
error::DomainError, error::DomainError,
}; };
use anyhow::{anyhow, Context}; use anyhow::{anyhow, Context};
use lemmy_db_schema::source::user::User_; use lemmy_db_schema::source::person::Person;
use lemmy_utils::{location_info, LemmyError}; use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use log::debug; use log::debug;
@ -28,18 +28,18 @@ where
Err(anyhow!("Activity not supported").into()) Err(anyhow!("Activity not supported").into())
} }
/// Reads the actor field of an activity and returns the corresponding `User_`. /// Reads the actor field of an activity and returns the corresponding `Person`.
pub(crate) async fn get_actor_as_user<T, A>( pub(crate) async fn get_actor_as_person<T, A>(
activity: &T, activity: &T,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<User_, LemmyError> ) -> Result<Person, LemmyError>
where where
T: AsBase<A> + ActorAndObjectRef, T: AsBase<A> + ActorAndObjectRef,
{ {
let actor = activity.actor()?; let actor = activity.actor()?;
let user_uri = actor.as_single_xsd_any_uri().context(location_info!())?; let person_uri = actor.as_single_xsd_any_uri().context(location_info!())?;
get_or_fetch_and_upsert_user(&user_uri, context, request_counter).await get_or_fetch_and_upsert_person(&person_uri, context, request_counter).await
} }
/// Ensure that the ID of an incoming activity comes from the same domain as the actor. Optionally /// Ensure that the ID of an incoming activity comes from the same domain as the actor. Optionally

View file

@ -1,13 +1,13 @@
use crate::{activities::receive::get_actor_as_user, objects::FromApub, ActorType, PageExt}; use crate::{activities::receive::get_actor_as_person, objects::FromApub, ActorType, PageExt};
use activitystreams::{ use activitystreams::{
activity::{Create, Dislike, Like, Remove, Update}, activity::{Create, Dislike, Like, Remove, Update},
prelude::*, prelude::*,
}; };
use anyhow::Context; use anyhow::Context;
use lemmy_api_structs::{blocking, post::PostResponse};
use lemmy_db_queries::{source::post::Post_, Likeable}; use lemmy_db_queries::{source::post::Post_, Likeable};
use lemmy_db_schema::source::post::{Post, PostLike, PostLikeForm}; use lemmy_db_schema::source::post::{Post, PostLike, PostLikeForm};
use lemmy_db_views::post_view::PostView; use lemmy_db_views::post_view::PostView;
use lemmy_structs::{blocking, post::PostResponse};
use lemmy_utils::{location_info, LemmyError}; use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::{messages::SendPost, LemmyContext, UserOperation}; use lemmy_websocket::{messages::SendPost, LemmyContext, UserOperation};
@ -16,11 +16,11 @@ pub(crate) async fn receive_create_post(
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let user = get_actor_as_user(&create, context, request_counter).await?; let person = get_actor_as_person(&create, context, request_counter).await?;
let page = PageExt::from_any_base(create.object().to_owned().one().context(location_info!())?)? let page = PageExt::from_any_base(create.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?; .context(location_info!())?;
let post = Post::from_apub(&page, context, user.actor_id(), request_counter).await?; let post = Post::from_apub(&page, context, person.actor_id(), request_counter).await?;
// Refetch the view // Refetch the view
let post_id = post.id; let post_id = post.id;
@ -45,11 +45,11 @@ pub(crate) async fn receive_update_post(
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let user = get_actor_as_user(&update, context, request_counter).await?; let person = get_actor_as_person(&update, context, request_counter).await?;
let page = PageExt::from_any_base(update.object().to_owned().one().context(location_info!())?)? let page = PageExt::from_any_base(update.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?; .context(location_info!())?;
let post = Post::from_apub(&page, context, user.actor_id(), request_counter).await?; let post = Post::from_apub(&page, context, person.actor_id(), request_counter).await?;
let post_id = post.id; let post_id = post.id;
// Refetch the view // Refetch the view
@ -75,17 +75,17 @@ pub(crate) async fn receive_like_post(
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let user = get_actor_as_user(&like, context, request_counter).await?; let person = get_actor_as_person(&like, context, request_counter).await?;
let post_id = post.id; let post_id = post.id;
let like_form = PostLikeForm { let like_form = PostLikeForm {
post_id, post_id,
person_id: user.id, person_id: person.id,
score: 1, score: 1,
}; };
let user_id = user.id; let person_id = person.id;
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
PostLike::remove(conn, user_id, post_id)?; PostLike::remove(conn, person_id, post_id)?;
PostLike::like(conn, &like_form) PostLike::like(conn, &like_form)
}) })
.await??; .await??;
@ -113,17 +113,17 @@ pub(crate) async fn receive_dislike_post(
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let user = get_actor_as_user(&dislike, context, request_counter).await?; let person = get_actor_as_person(&dislike, context, request_counter).await?;
let post_id = post.id; let post_id = post.id;
let like_form = PostLikeForm { let like_form = PostLikeForm {
post_id, post_id,
person_id: user.id, person_id: person.id,
score: -1, score: -1,
}; };
let user_id = user.id; let person_id = person.id;
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
PostLike::remove(conn, user_id, post_id)?; PostLike::remove(conn, person_id, post_id)?;
PostLike::like(conn, &like_form) PostLike::like(conn, &like_form)
}) })
.await??; .await??;

View file

@ -1,9 +1,9 @@
use crate::activities::receive::get_actor_as_user; use crate::activities::receive::get_actor_as_person;
use activitystreams::activity::{Dislike, Like}; use activitystreams::activity::{Dislike, Like};
use lemmy_api_structs::{blocking, post::PostResponse};
use lemmy_db_queries::{source::post::Post_, Likeable}; use lemmy_db_queries::{source::post::Post_, Likeable};
use lemmy_db_schema::source::post::{Post, PostLike}; use lemmy_db_schema::source::post::{Post, PostLike};
use lemmy_db_views::post_view::PostView; use lemmy_db_views::post_view::PostView;
use lemmy_structs::{blocking, post::PostResponse};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{messages::SendPost, LemmyContext, UserOperation}; use lemmy_websocket::{messages::SendPost, LemmyContext, UserOperation};
@ -13,12 +13,12 @@ pub(crate) async fn receive_undo_like_post(
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let user = get_actor_as_user(like, context, request_counter).await?; let person = get_actor_as_person(like, context, request_counter).await?;
let post_id = post.id; let post_id = post.id;
let user_id = user.id; let person_id = person.id;
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
PostLike::remove(conn, user_id, post_id) PostLike::remove(conn, person_id, post_id)
}) })
.await??; .await??;
@ -45,12 +45,12 @@ pub(crate) async fn receive_undo_dislike_post(
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let user = get_actor_as_user(dislike, context, request_counter).await?; let person = get_actor_as_person(dislike, context, request_counter).await?;
let post_id = post.id; let post_id = post.id;
let user_id = user.id; let person_id = person.id;
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
PostLike::remove(conn, user_id, post_id) PostLike::remove(conn, person_id, post_id)
}) })
.await??; .await??;

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
activities::receive::verify_activity_domains_valid, activities::receive::verify_activity_domains_valid,
check_is_apub_id_valid, check_is_apub_id_valid,
fetcher::user::get_or_fetch_and_upsert_user, fetcher::person::get_or_fetch_and_upsert_person,
inbox::get_activity_to_and_cc, inbox::get_activity_to_and_cc,
objects::FromApub, objects::FromApub,
NoteExt, NoteExt,
@ -13,10 +13,10 @@ use activitystreams::{
public, public,
}; };
use anyhow::{anyhow, Context}; use anyhow::{anyhow, Context};
use lemmy_api_structs::{blocking, person::PrivateMessageResponse};
use lemmy_db_queries::source::private_message::PrivateMessage_; use lemmy_db_queries::source::private_message::PrivateMessage_;
use lemmy_db_schema::source::private_message::PrivateMessage; use lemmy_db_schema::source::private_message::PrivateMessage;
use lemmy_db_views::private_message_view::PrivateMessageView; use lemmy_db_views::private_message_view::PrivateMessageView;
use lemmy_structs::{blocking, user::PrivateMessageResponse};
use lemmy_utils::{location_info, LemmyError}; use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::{messages::SendUserRoomMessage, LemmyContext, UserOperation}; use lemmy_websocket::{messages::SendUserRoomMessage, LemmyContext, UserOperation};
use url::Url; use url::Url;
@ -181,19 +181,19 @@ where
{ {
let to_and_cc = get_activity_to_and_cc(activity); let to_and_cc = get_activity_to_and_cc(activity);
if to_and_cc.len() != 1 { if to_and_cc.len() != 1 {
return Err(anyhow!("Private message can only be addressed to one user").into()); return Err(anyhow!("Private message can only be addressed to one person").into());
} }
if to_and_cc.contains(&public()) { if to_and_cc.contains(&public()) {
return Err(anyhow!("Private message cant be public").into()); return Err(anyhow!("Private message cant be public").into());
} }
let user_id = activity let person_id = activity
.actor()? .actor()?
.to_owned() .to_owned()
.single_xsd_any_uri() .single_xsd_any_uri()
.context(location_info!())?; .context(location_info!())?;
check_is_apub_id_valid(&user_id)?; check_is_apub_id_valid(&person_id)?;
// check that the sender is a user, not a community // check that the sender is a person, not a community
get_or_fetch_and_upsert_user(&user_id, &context, request_counter).await?; get_or_fetch_and_upsert_person(&person_id, &context, request_counter).await?;
Ok(()) Ok(())
} }

View file

@ -2,7 +2,7 @@ use crate::{
activities::send::generate_activity_id, activities::send::generate_activity_id,
activity_queue::{send_comment_mentions, send_to_community}, activity_queue::{send_comment_mentions, send_to_community},
extensions::context::lemmy_context, extensions::context::lemmy_context,
fetcher::user::get_or_fetch_and_upsert_user, fetcher::person::get_or_fetch_and_upsert_person,
objects::ToApub, objects::ToApub,
ActorType, ActorType,
ApubLikeableType, ApubLikeableType,
@ -26,12 +26,12 @@ use activitystreams::{
}; };
use anyhow::anyhow; use anyhow::anyhow;
use itertools::Itertools; use itertools::Itertools;
use lemmy_api_structs::{blocking, WebFingerResponse};
use lemmy_db_queries::{Crud, DbPool}; use lemmy_db_queries::{Crud, DbPool};
use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post, user::User_}; use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post, person::Person};
use lemmy_structs::{blocking, WebFingerResponse};
use lemmy_utils::{ use lemmy_utils::{
request::{retry, RecvError}, request::{retry, RecvError},
settings::Settings, settings::structs::Settings,
utils::{scrape_text_for_mentions, MentionData}, utils::{scrape_text_for_mentions, MentionData},
LemmyError, LemmyError,
}; };
@ -44,8 +44,8 @@ use url::Url;
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ApubObjectType for Comment { impl ApubObjectType for Comment {
/// Send out information about a newly created comment, to the followers of the community and /// Send out information about a newly created comment, to the followers of the community and
/// mentioned users. /// mentioned persons.
async fn send_create(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> { async fn send_create(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
let note = self.to_apub(context.pool()).await?; let note = self.to_apub(context.pool()).await?;
let post_id = self.post_id; let post_id = self.post_id;
@ -77,8 +77,8 @@ impl ApubObjectType for Comment {
} }
/// Send out information about an edited post, to the followers of the community and mentioned /// Send out information about an edited post, to the followers of the community and mentioned
/// users. /// persons.
async fn send_update(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> { async fn send_update(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
let note = self.to_apub(context.pool()).await?; let note = self.to_apub(context.pool()).await?;
let post_id = self.post_id; let post_id = self.post_id;
@ -109,7 +109,7 @@ impl ApubObjectType for Comment {
Ok(()) Ok(())
} }
async fn send_delete(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> { async fn send_delete(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
let post_id = self.post_id; let post_id = self.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
@ -135,7 +135,7 @@ impl ApubObjectType for Comment {
async fn send_undo_delete( async fn send_undo_delete(
&self, &self,
creator: &User_, creator: &Person,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let post_id = self.post_id; let post_id = self.post_id;
@ -173,7 +173,7 @@ impl ApubObjectType for Comment {
Ok(()) Ok(())
} }
async fn send_remove(&self, mod_: &User_, context: &LemmyContext) -> Result<(), LemmyError> { async fn send_remove(&self, mod_: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
let post_id = self.post_id; let post_id = self.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
@ -197,7 +197,7 @@ impl ApubObjectType for Comment {
Ok(()) Ok(())
} }
async fn send_undo_remove(&self, mod_: &User_, context: &LemmyContext) -> Result<(), LemmyError> { async fn send_undo_remove(&self, mod_: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
let post_id = self.post_id; let post_id = self.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
@ -236,7 +236,7 @@ impl ApubObjectType for Comment {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ApubLikeableType for Comment { impl ApubLikeableType for Comment {
async fn send_like(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> { async fn send_like(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
let post_id = self.post_id; let post_id = self.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
@ -260,7 +260,7 @@ impl ApubLikeableType for Comment {
Ok(()) Ok(())
} }
async fn send_dislike(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> { async fn send_dislike(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
let post_id = self.post_id; let post_id = self.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
@ -286,7 +286,7 @@ impl ApubLikeableType for Comment {
async fn send_undo_like( async fn send_undo_like(
&self, &self,
creator: &User_, creator: &Person,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let post_id = self.post_id; let post_id = self.post_id;
@ -342,7 +342,7 @@ impl MentionsAndAddresses {
/// This takes a comment, and builds a list of to_addresses, inboxes, /// This takes a comment, and builds a list of to_addresses, inboxes,
/// and mention tags, so they know where to be sent to. /// and mention tags, so they know where to be sent to.
/// Addresses are the users / addresses that go in the cc field. /// Addresses are the persons / addresses that go in the cc field.
async fn collect_non_local_mentions( async fn collect_non_local_mentions(
comment: &Comment, comment: &Comment,
community: &Community, community: &Community,
@ -356,7 +356,7 @@ async fn collect_non_local_mentions(
// Add the mention tag // Add the mention tag
let mut tags = Vec::new(); let mut tags = Vec::new();
// Get the user IDs for any mentions // Get the person IDs for any mentions
let mentions = scrape_text_for_mentions(&comment.content) let mentions = scrape_text_for_mentions(&comment.content)
.into_iter() .into_iter()
// Filter only the non-local ones // Filter only the non-local ones
@ -369,8 +369,8 @@ async fn collect_non_local_mentions(
debug!("mention actor_id: {}", actor_id); debug!("mention actor_id: {}", actor_id);
addressed_ccs.push(actor_id.to_owned().to_string().parse()?); addressed_ccs.push(actor_id.to_owned().to_string().parse()?);
let mention_user = get_or_fetch_and_upsert_user(&actor_id, context, &mut 0).await?; let mention_person = get_or_fetch_and_upsert_person(&actor_id, context, &mut 0).await?;
inboxes.push(mention_user.get_shared_inbox_or_inbox_url()); inboxes.push(mention_person.get_shared_inbox_or_inbox_url());
let mut mention_tag = Mention::new(); let mut mention_tag = Mention::new();
mention_tag.set_href(actor_id).set_name(mention.full_name()); mention_tag.set_href(actor_id).set_name(mention.full_name());
@ -387,9 +387,9 @@ async fn collect_non_local_mentions(
}) })
} }
/// Returns the apub ID of the user this comment is responding to. Meaning, in case this is a /// Returns the apub ID of the person this comment is responding to. Meaning, in case this is a
/// top-level comment, the creator of the post, otherwise the creator of the parent comment. /// top-level comment, the creator of the post, otherwise the creator of the parent comment.
async fn get_comment_parent_creator(pool: &DbPool, comment: &Comment) -> Result<User_, LemmyError> { async fn get_comment_parent_creator(pool: &DbPool, comment: &Comment) -> Result<Person, LemmyError> {
let parent_creator_id = if let Some(parent_comment_id) = comment.parent_id { let parent_creator_id = if let Some(parent_comment_id) = comment.parent_id {
let parent_comment = let parent_comment =
blocking(pool, move |conn| Comment::read(conn, parent_comment_id)).await??; blocking(pool, move |conn| Comment::read(conn, parent_comment_id)).await??;
@ -399,10 +399,10 @@ async fn get_comment_parent_creator(pool: &DbPool, comment: &Comment) -> Result<
let parent_post = blocking(pool, move |conn| Post::read(conn, parent_post_id)).await??; let parent_post = blocking(pool, move |conn| Post::read(conn, parent_post_id)).await??;
parent_post.creator_id parent_post.creator_id
}; };
Ok(blocking(pool, move |conn| User_::read(conn, parent_creator_id)).await??) Ok(blocking(pool, move |conn| Person::read(conn, parent_creator_id)).await??)
} }
/// Turns a user id like `@name@example.com` into an apub ID, like `https://example.com/user/name`, /// Turns a person id like `@name@example.com` into an apub ID, like `https://example.com/user/name`,
/// using webfinger. /// using webfinger.
async fn fetch_webfinger_url(mention: &MentionData, client: &Client) -> Result<Url, LemmyError> { async fn fetch_webfinger_url(mention: &MentionData, client: &Client) -> Result<Url, LemmyError> {
let fetch_url = format!( let fetch_url = format!(

View file

@ -3,7 +3,7 @@ use crate::{
activity_queue::{send_activity_single_dest, send_to_community_followers}, activity_queue::{send_activity_single_dest, send_to_community_followers},
check_is_apub_id_valid, check_is_apub_id_valid,
extensions::context::lemmy_context, extensions::context::lemmy_context,
fetcher::user::get_or_fetch_and_upsert_user, fetcher::person::get_or_fetch_and_upsert_person,
ActorType, ActorType,
}; };
use activitystreams::{ use activitystreams::{
@ -23,10 +23,10 @@ use activitystreams::{
}; };
use anyhow::Context; use anyhow::Context;
use itertools::Itertools; use itertools::Itertools;
use lemmy_api_structs::blocking;
use lemmy_db_queries::DbPool; use lemmy_db_queries::DbPool;
use lemmy_db_schema::source::community::Community; use lemmy_db_schema::source::community::Community;
use lemmy_db_views_actor::community_follower_view::CommunityFollowerView; use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
use lemmy_structs::blocking;
use lemmy_utils::{location_info, LemmyError}; use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use url::Url; use url::Url;
@ -70,7 +70,7 @@ impl ActorType for Community {
unimplemented!() unimplemented!()
} }
/// As a local community, accept the follow request from a remote user. /// As a local community, accept the follow request from a remote person.
async fn send_accept_follow( async fn send_accept_follow(
&self, &self,
follow: Follow, follow: Follow,
@ -80,7 +80,7 @@ impl ActorType for Community {
.actor()? .actor()?
.as_single_xsd_any_uri() .as_single_xsd_any_uri()
.context(location_info!())?; .context(location_info!())?;
let user = get_or_fetch_and_upsert_user(actor_uri, context, &mut 0).await?; let person = get_or_fetch_and_upsert_person(actor_uri, context, &mut 0).await?;
let mut accept = Accept::new( let mut accept = Accept::new(
self.actor_id.to_owned().into_inner(), self.actor_id.to_owned().into_inner(),
@ -89,9 +89,9 @@ impl ActorType for Community {
accept accept
.set_many_contexts(lemmy_context()?) .set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(AcceptType::Accept)?) .set_id(generate_activity_id(AcceptType::Accept)?)
.set_to(user.actor_id()); .set_to(person.actor_id());
send_activity_single_dest(accept, self, user.inbox_url.into(), context).await?; send_activity_single_dest(accept, self, person.inbox_url.into(), context).await?;
Ok(()) Ok(())
} }

View file

@ -1,4 +1,4 @@
use lemmy_utils::settings::Settings; use lemmy_utils::settings::structs::Settings;
use url::{ParseError, Url}; use url::{ParseError, Url};
use uuid::Uuid; use uuid::Uuid;
@ -6,7 +6,7 @@ pub(crate) mod comment;
pub(crate) mod community; pub(crate) mod community;
pub(crate) mod post; pub(crate) mod post;
pub(crate) mod private_message; pub(crate) mod private_message;
pub(crate) mod user; pub(crate) mod person;
/// Generate a unique ID for an activity, in the format: /// Generate a unique ID for an activity, in the format:
/// `http(s)://example.com/receive/create/202daf0a-1489-45df-8d2e-c8a3173fed36` /// `http(s)://example.com/receive/create/202daf0a-1489-45df-8d2e-c8a3173fed36`

View file

@ -13,18 +13,18 @@ use activitystreams::{
base::{AnyBase, BaseExt, ExtendsExt}, base::{AnyBase, BaseExt, ExtendsExt},
object::ObjectExt, object::ObjectExt,
}; };
use lemmy_api_structs::blocking;
use lemmy_db_queries::{ApubObject, DbPool, Followable}; use lemmy_db_queries::{ApubObject, DbPool, Followable};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
community::{Community, CommunityFollower, CommunityFollowerForm}, community::{Community, CommunityFollower, CommunityFollowerForm},
user::User_, person::Person,
}; };
use lemmy_structs::blocking;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use url::Url; use url::Url;
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ActorType for User_ { impl ActorType for Person {
fn is_local(&self) -> bool { fn is_local(&self) -> bool {
self.local self.local
} }
@ -48,7 +48,7 @@ impl ActorType for User_ {
.into() .into()
} }
/// As a given local user, send out a follow request to a remote community. /// As a given local person, send out a follow request to a remote community.
async fn send_follow( async fn send_follow(
&self, &self,
follow_actor_id: &Url, follow_actor_id: &Url,

View file

@ -21,16 +21,16 @@ use activitystreams::{
prelude::*, prelude::*,
public, public,
}; };
use lemmy_api_structs::blocking;
use lemmy_db_queries::Crud; use lemmy_db_queries::Crud;
use lemmy_db_schema::source::{community::Community, post::Post, user::User_}; use lemmy_db_schema::source::{community::Community, post::Post, person::Person};
use lemmy_structs::blocking;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ApubObjectType for Post { impl ApubObjectType for Post {
/// Send out information about a newly created post, to the followers of the community. /// Send out information about a newly created post, to the followers of the community.
async fn send_create(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> { async fn send_create(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
let page = self.to_apub(context.pool()).await?; let page = self.to_apub(context.pool()).await?;
let community_id = self.community_id; let community_id = self.community_id;
@ -54,7 +54,7 @@ impl ApubObjectType for Post {
} }
/// Send out information about an edited post, to the followers of the community. /// Send out information about an edited post, to the followers of the community.
async fn send_update(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> { async fn send_update(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
let page = self.to_apub(context.pool()).await?; let page = self.to_apub(context.pool()).await?;
let community_id = self.community_id; let community_id = self.community_id;
@ -77,7 +77,7 @@ impl ApubObjectType for Post {
Ok(()) Ok(())
} }
async fn send_delete(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> { async fn send_delete(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
let community_id = self.community_id; let community_id = self.community_id;
let community = blocking(context.pool(), move |conn| { let community = blocking(context.pool(), move |conn| {
Community::read(conn, community_id) Community::read(conn, community_id)
@ -100,7 +100,7 @@ impl ApubObjectType for Post {
async fn send_undo_delete( async fn send_undo_delete(
&self, &self,
creator: &User_, creator: &Person,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let community_id = self.community_id; let community_id = self.community_id;
@ -134,7 +134,7 @@ impl ApubObjectType for Post {
Ok(()) Ok(())
} }
async fn send_remove(&self, mod_: &User_, context: &LemmyContext) -> Result<(), LemmyError> { async fn send_remove(&self, mod_: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
let community_id = self.community_id; let community_id = self.community_id;
let community = blocking(context.pool(), move |conn| { let community = blocking(context.pool(), move |conn| {
Community::read(conn, community_id) Community::read(conn, community_id)
@ -155,7 +155,7 @@ impl ApubObjectType for Post {
Ok(()) Ok(())
} }
async fn send_undo_remove(&self, mod_: &User_, context: &LemmyContext) -> Result<(), LemmyError> { async fn send_undo_remove(&self, mod_: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
let community_id = self.community_id; let community_id = self.community_id;
let community = blocking(context.pool(), move |conn| { let community = blocking(context.pool(), move |conn| {
Community::read(conn, community_id) Community::read(conn, community_id)
@ -190,7 +190,7 @@ impl ApubObjectType for Post {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ApubLikeableType for Post { impl ApubLikeableType for Post {
async fn send_like(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> { async fn send_like(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
let community_id = self.community_id; let community_id = self.community_id;
let community = blocking(context.pool(), move |conn| { let community = blocking(context.pool(), move |conn| {
Community::read(conn, community_id) Community::read(conn, community_id)
@ -211,7 +211,7 @@ impl ApubLikeableType for Post {
Ok(()) Ok(())
} }
async fn send_dislike(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> { async fn send_dislike(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
let community_id = self.community_id; let community_id = self.community_id;
let community = blocking(context.pool(), move |conn| { let community = blocking(context.pool(), move |conn| {
Community::read(conn, community_id) Community::read(conn, community_id)
@ -234,7 +234,7 @@ impl ApubLikeableType for Post {
async fn send_undo_like( async fn send_undo_like(
&self, &self,
creator: &User_, creator: &Person,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let community_id = self.community_id; let community_id = self.community_id;

View file

@ -16,20 +16,20 @@ use activitystreams::{
}, },
prelude::*, prelude::*,
}; };
use lemmy_api_structs::blocking;
use lemmy_db_queries::Crud; use lemmy_db_queries::Crud;
use lemmy_db_schema::source::{private_message::PrivateMessage, user::User_}; use lemmy_db_schema::source::{private_message::PrivateMessage, person::Person};
use lemmy_structs::blocking;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ApubObjectType for PrivateMessage { impl ApubObjectType for PrivateMessage {
/// Send out information about a newly created private message /// Send out information about a newly created private message
async fn send_create(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> { async fn send_create(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
let note = self.to_apub(context.pool()).await?; let note = self.to_apub(context.pool()).await?;
let recipient_id = self.recipient_id; let recipient_id = self.recipient_id;
let recipient = blocking(context.pool(), move |conn| User_::read(conn, recipient_id)).await??; let recipient = blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
let mut create = Create::new( let mut create = Create::new(
creator.actor_id.to_owned().into_inner(), creator.actor_id.to_owned().into_inner(),
@ -46,11 +46,11 @@ impl ApubObjectType for PrivateMessage {
} }
/// Send out information about an edited private message, to the followers of the community. /// Send out information about an edited private message, to the followers of the community.
async fn send_update(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> { async fn send_update(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
let note = self.to_apub(context.pool()).await?; let note = self.to_apub(context.pool()).await?;
let recipient_id = self.recipient_id; let recipient_id = self.recipient_id;
let recipient = blocking(context.pool(), move |conn| User_::read(conn, recipient_id)).await??; let recipient = blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
let mut update = Update::new( let mut update = Update::new(
creator.actor_id.to_owned().into_inner(), creator.actor_id.to_owned().into_inner(),
@ -65,9 +65,9 @@ impl ApubObjectType for PrivateMessage {
Ok(()) Ok(())
} }
async fn send_delete(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> { async fn send_delete(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
let recipient_id = self.recipient_id; let recipient_id = self.recipient_id;
let recipient = blocking(context.pool(), move |conn| User_::read(conn, recipient_id)).await??; let recipient = blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
let mut delete = Delete::new( let mut delete = Delete::new(
creator.actor_id.to_owned().into_inner(), creator.actor_id.to_owned().into_inner(),
@ -84,11 +84,11 @@ impl ApubObjectType for PrivateMessage {
async fn send_undo_delete( async fn send_undo_delete(
&self, &self,
creator: &User_, creator: &Person,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let recipient_id = self.recipient_id; let recipient_id = self.recipient_id;
let recipient = blocking(context.pool(), move |conn| User_::read(conn, recipient_id)).await??; let recipient = blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
let mut delete = Delete::new( let mut delete = Delete::new(
creator.actor_id.to_owned().into_inner(), creator.actor_id.to_owned().into_inner(),
@ -113,13 +113,13 @@ impl ApubObjectType for PrivateMessage {
Ok(()) Ok(())
} }
async fn send_remove(&self, _mod_: &User_, _context: &LemmyContext) -> Result<(), LemmyError> { async fn send_remove(&self, _mod_: &Person, _context: &LemmyContext) -> Result<(), LemmyError> {
unimplemented!() unimplemented!()
} }
async fn send_undo_remove( async fn send_undo_remove(
&self, &self,
_mod_: &User_, _mod_: &Person,
_context: &LemmyContext, _context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
unimplemented!() unimplemented!()

View file

@ -21,8 +21,8 @@ use background_jobs::{
}; };
use itertools::Itertools; use itertools::Itertools;
use lemmy_db_queries::DbPool; use lemmy_db_queries::DbPool;
use lemmy_db_schema::source::{community::Community, user::User_}; use lemmy_db_schema::source::{community::Community, person::Person};
use lemmy_utils::{location_info, settings::Settings, LemmyError}; use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use log::{debug, warn}; use log::{debug, warn};
use reqwest::Client; use reqwest::Client;
@ -88,7 +88,7 @@ where
.await? .await?
.iter() .iter()
.unique() .unique()
.filter(|inbox| inbox.host_str() != Some(&Settings::get().hostname)) .filter(|inbox| inbox.host_str() != Some(&Settings::get().hostname()))
.filter(|inbox| check_is_apub_id_valid(inbox).is_ok()) .filter(|inbox| check_is_apub_id_valid(inbox).is_ok())
.map(|inbox| inbox.to_owned()) .map(|inbox| inbox.to_owned())
.collect(); .collect();
@ -112,7 +112,7 @@ where
Ok(()) Ok(())
} }
/// Sends an activity from a local user to a remote community. /// Sends an activity from a local person to a remote community.
/// ///
/// * `activity` the activity to send /// * `activity` the activity to send
/// * `creator` the creator of the activity /// * `creator` the creator of the activity
@ -120,7 +120,7 @@ where
/// ///
pub(crate) async fn send_to_community<T, Kind>( pub(crate) async fn send_to_community<T, Kind>(
activity: T, activity: T,
creator: &User_, creator: &Person,
community: &Community, community: &Community,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> ) -> Result<(), LemmyError>
@ -157,13 +157,13 @@ where
Ok(()) Ok(())
} }
/// Sends notification to any users mentioned in a comment /// Sends notification to any persons mentioned in a comment
/// ///
/// * `creator` user who created the comment /// * `creator` person who created the comment
/// * `mentions` list of inboxes of users which are mentioned in the comment /// * `mentions` list of inboxes of persons which are mentioned in the comment
/// * `activity` either a `Create/Note` or `Update/Note` /// * `activity` either a `Create/Note` or `Update/Note`
pub(crate) async fn send_comment_mentions<T, Kind>( pub(crate) async fn send_comment_mentions<T, Kind>(
creator: &User_, creator: &Person,
mentions: Vec<Url>, mentions: Vec<Url>,
activity: T, activity: T,
context: &LemmyContext, context: &LemmyContext,
@ -215,7 +215,7 @@ where
Kind: Serialize, Kind: Serialize,
<T as Extends<Kind>>::Error: From<serde_json::Error> + Send + Sync + 'static, <T as Extends<Kind>>::Error: From<serde_json::Error> + Send + Sync + 'static,
{ {
if !Settings::get().federation.enabled || inboxes.is_empty() { if !Settings::get().federation().enabled || inboxes.is_empty() {
return Ok(()); return Ok(());
} }
@ -223,7 +223,7 @@ where
let hostname = Settings::get().get_hostname_without_port()?; let hostname = Settings::get().get_hostname_without_port()?;
let inboxes: Vec<&Url> = inboxes let inboxes: Vec<&Url> = inboxes
.iter() .iter()
.filter(|i| i.domain().unwrap() != hostname) .filter(|i| i.domain().expect("valid inbox url") != hostname)
.collect(); .collect();
let activity = activity.into_any_base()?; let activity = activity.into_any_base()?;

View file

@ -6,12 +6,11 @@ pub(crate) fn lemmy_context() -> Result<Vec<AnyBase>, LemmyError> {
let context_ext = AnyBase::from_arbitrary_json(json!( let context_ext = AnyBase::from_arbitrary_json(json!(
{ {
"sc": "http://schema.org#", "sc": "http://schema.org#",
"category": "sc:category",
"sensitive": "as:sensitive", "sensitive": "as:sensitive",
"stickied": "as:stickied", "stickied": "as:stickied",
"comments_enabled": { "comments_enabled": {
"kind": "sc:Boolean", "kind": "sc:Boolean",
"id": "pt:commentsEnabled" "id": "pt:commentsEnabled"
} }
}))?; }))?;
Ok(vec![AnyBase::from(context()), context_ext]) Ok(vec![AnyBase::from(context()), context_ext])

View file

@ -95,7 +95,7 @@ pub(crate) fn verify_signature(
} }
} }
/// Extension for actor public key, which is needed on user and community for HTTP signatures. /// Extension for actor public key, which is needed on person and community for HTTP signatures.
/// ///
/// Taken from: https://docs.rs/activitystreams/0.5.0-alpha.17/activitystreams/ext/index.html /// Taken from: https://docs.rs/activitystreams/0.5.0-alpha.17/activitystreams/ext/index.html
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]

View file

@ -1,11 +1,11 @@
use crate::{ use crate::{
fetcher::{ fetcher::{
fetch::fetch_remote_object, fetch::fetch_remote_object,
get_or_fetch_and_upsert_user, get_or_fetch_and_upsert_person,
is_deleted, is_deleted,
should_refetch_actor, should_refetch_actor,
}, },
inbox::user_inbox::receive_announce, inbox::person_inbox::receive_announce,
objects::FromApub, objects::FromApub,
GroupExt, GroupExt,
}; };
@ -16,9 +16,9 @@ use activitystreams::{
}; };
use anyhow::Context; use anyhow::Context;
use diesel::result::Error::NotFound; use diesel::result::Error::NotFound;
use lemmy_api_structs::blocking;
use lemmy_db_queries::{source::community::Community_, ApubObject, Joinable}; use lemmy_db_queries::{source::community::Community_, ApubObject, Joinable};
use lemmy_db_schema::source::community::{Community, CommunityModerator, CommunityModeratorForm}; use lemmy_db_schema::source::community::{Community, CommunityModerator, CommunityModeratorForm};
use lemmy_structs::blocking;
use lemmy_utils::{location_info, LemmyError}; use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use log::debug; use log::debug;
@ -92,7 +92,7 @@ async fn fetch_remote_community(
let mut creator_and_moderators = Vec::new(); let mut creator_and_moderators = Vec::new();
for uri in creator_and_moderator_uris { for uri in creator_and_moderator_uris {
let c_or_m = get_or_fetch_and_upsert_user(uri, context, recursion_counter).await?; let c_or_m = get_or_fetch_and_upsert_person(uri, context, recursion_counter).await?;
creator_and_moderators.push(c_or_m); creator_and_moderators.push(c_or_m);
} }

View file

@ -2,13 +2,13 @@ pub(crate) mod community;
mod fetch; mod fetch;
pub(crate) mod objects; pub(crate) mod objects;
pub mod search; pub mod search;
pub(crate) mod user; pub(crate) mod person;
use crate::{ use crate::{
fetcher::{ fetcher::{
community::get_or_fetch_and_upsert_community, community::get_or_fetch_and_upsert_community,
fetch::FetchError, fetch::FetchError,
user::get_or_fetch_and_upsert_user, person::get_or_fetch_and_upsert_person,
}, },
ActorType, ActorType,
}; };
@ -37,8 +37,8 @@ where
false false
} }
/// Get a remote actor from its apub ID (either a user or a community). Thin wrapper around /// Get a remote actor from its apub ID (either a person or a community). Thin wrapper around
/// `get_or_fetch_and_upsert_user()` and `get_or_fetch_and_upsert_community()`. /// `get_or_fetch_and_upsert_person()` and `get_or_fetch_and_upsert_community()`.
/// ///
/// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database. /// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database.
/// Otherwise it is fetched from the remote instance, stored and returned. /// Otherwise it is fetched from the remote instance, stored and returned.
@ -50,7 +50,7 @@ pub(crate) async fn get_or_fetch_and_upsert_actor(
let community = get_or_fetch_and_upsert_community(apub_id, context, recursion_counter).await; let community = get_or_fetch_and_upsert_community(apub_id, context, recursion_counter).await;
let actor: Box<dyn ActorType> = match community { let actor: Box<dyn ActorType> = match community {
Ok(c) => Box::new(c), Ok(c) => Box::new(c),
Err(_) => Box::new(get_or_fetch_and_upsert_user(apub_id, context, recursion_counter).await?), Err(_) => Box::new(get_or_fetch_and_upsert_person(apub_id, context, recursion_counter).await?),
}; };
Ok(actor) Ok(actor)
} }

View file

@ -1,9 +1,9 @@
use crate::{fetcher::fetch::fetch_remote_object, objects::FromApub, NoteExt, PageExt}; use crate::{fetcher::fetch::fetch_remote_object, objects::FromApub, NoteExt, PageExt};
use anyhow::anyhow; use anyhow::anyhow;
use diesel::result::Error::NotFound; use diesel::result::Error::NotFound;
use lemmy_api_structs::blocking;
use lemmy_db_queries::{ApubObject, Crud}; use lemmy_db_queries::{ApubObject, Crud};
use lemmy_db_schema::source::{comment::Comment, post::Post}; use lemmy_db_schema::source::{comment::Comment, post::Post};
use lemmy_structs::blocking;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use log::debug; use log::debug;

View file

@ -5,66 +5,66 @@ use crate::{
}; };
use anyhow::anyhow; use anyhow::anyhow;
use diesel::result::Error::NotFound; use diesel::result::Error::NotFound;
use lemmy_db_queries::{source::user::User, ApubObject}; use lemmy_api_structs::blocking;
use lemmy_db_schema::source::user::User_; use lemmy_db_queries::{source::person::Person_, ApubObject};
use lemmy_structs::blocking; use lemmy_db_schema::source::person::Person;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use log::debug; use log::debug;
use url::Url; use url::Url;
/// Get a user from its apub ID. /// Get a person from its apub ID.
/// ///
/// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database. /// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database.
/// Otherwise it is fetched from the remote instance, stored and returned. /// Otherwise it is fetched from the remote instance, stored and returned.
pub(crate) async fn get_or_fetch_and_upsert_user( pub(crate) async fn get_or_fetch_and_upsert_person(
apub_id: &Url, apub_id: &Url,
context: &LemmyContext, context: &LemmyContext,
recursion_counter: &mut i32, recursion_counter: &mut i32,
) -> Result<User_, LemmyError> { ) -> Result<Person, LemmyError> {
let apub_id_owned = apub_id.to_owned(); let apub_id_owned = apub_id.to_owned();
let user = blocking(context.pool(), move |conn| { let person = blocking(context.pool(), move |conn| {
User_::read_from_apub_id(conn, &apub_id_owned.into()) Person::read_from_apub_id(conn, &apub_id_owned.into())
}) })
.await?; .await?;
match user { match person {
// If its older than a day, re-fetch it // If its older than a day, re-fetch it
Ok(u) if !u.local && should_refetch_actor(u.last_refreshed_at) => { Ok(u) if !u.local && should_refetch_actor(u.last_refreshed_at) => {
debug!("Fetching and updating from remote user: {}", apub_id); debug!("Fetching and updating from remote person: {}", apub_id);
let person = let person =
fetch_remote_object::<PersonExt>(context.client(), apub_id, recursion_counter).await; fetch_remote_object::<PersonExt>(context.client(), apub_id, recursion_counter).await;
if is_deleted(&person) { if is_deleted(&person) {
// TODO: use User_::update_deleted() once implemented // TODO: use Person::update_deleted() once implemented
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
User_::delete_account(conn, u.id) Person::delete_account(conn, u.id)
}) })
.await??; .await??;
return Err(anyhow!("User was deleted by remote instance").into()); return Err(anyhow!("Person was deleted by remote instance").into());
} else if person.is_err() { } else if person.is_err() {
return Ok(u); return Ok(u);
} }
let user = User_::from_apub(&person?, context, apub_id.to_owned(), recursion_counter).await?; let person = Person::from_apub(&person?, context, apub_id.to_owned(), recursion_counter).await?;
let user_id = user.id; let person_id = person.id;
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
User_::mark_as_updated(conn, user_id) Person::mark_as_updated(conn, person_id)
}) })
.await??; .await??;
Ok(user) Ok(person)
} }
Ok(u) => Ok(u), Ok(u) => Ok(u),
Err(NotFound {}) => { Err(NotFound {}) => {
debug!("Fetching and creating remote user: {}", apub_id); debug!("Fetching and creating remote person: {}", apub_id);
let person = let person =
fetch_remote_object::<PersonExt>(context.client(), apub_id, recursion_counter).await?; fetch_remote_object::<PersonExt>(context.client(), apub_id, recursion_counter).await?;
let user = User_::from_apub(&person, context, apub_id.to_owned(), recursion_counter).await?; let person = Person::from_apub(&person, context, apub_id.to_owned(), recursion_counter).await?;
Ok(user) Ok(person)
} }
Err(e) => Err(e.into()), Err(e) => Err(e.into()),
} }

View file

@ -2,7 +2,7 @@ use crate::{
fetcher::{ fetcher::{
fetch::fetch_remote_object, fetch::fetch_remote_object,
get_or_fetch_and_upsert_community, get_or_fetch_and_upsert_community,
get_or_fetch_and_upsert_user, get_or_fetch_and_upsert_person,
is_deleted, is_deleted,
}, },
find_object_by_id, find_object_by_id,
@ -15,13 +15,14 @@ use crate::{
}; };
use activitystreams::base::BaseExt; use activitystreams::base::BaseExt;
use anyhow::{anyhow, Context}; use anyhow::{anyhow, Context};
use lemmy_api_structs::{blocking, site::SearchResponse};
use lemmy_db_queries::{ use lemmy_db_queries::{
source::{ source::{
comment::Comment_, comment::Comment_,
community::Community_, community::Community_,
post::Post_, post::Post_,
private_message::PrivateMessage_, private_message::PrivateMessage_,
user::User, person::Person_,
}, },
SearchType, SearchType,
}; };
@ -30,12 +31,11 @@ use lemmy_db_schema::source::{
community::Community, community::Community,
post::Post, post::Post,
private_message::PrivateMessage, private_message::PrivateMessage,
user::User_, person::Person,
}; };
use lemmy_db_views::{comment_view::CommentView, post_view::PostView}; use lemmy_db_views::{comment_view::CommentView, post_view::PostView};
use lemmy_db_views_actor::{community_view::CommunityView, user_view::UserViewSafe}; use lemmy_db_views_actor::{community_view::CommunityView, person_view::PersonViewSafe};
use lemmy_structs::{blocking, site::SearchResponse}; use lemmy_utils::{settings::structs::Settings, LemmyError};
use lemmy_utils::{settings::Settings, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use log::debug; use log::debug;
use url::Url; use url::Url;
@ -66,7 +66,7 @@ pub async fn search_by_apub_id(
debug!("Search for {}", query); debug!("Search for {}", query);
let split = query.split('@').collect::<Vec<&str>>(); let split = query.split('@').collect::<Vec<&str>>();
// User type will look like ['', username, instance] // Person type will look like ['', username, instance]
// Community will look like [!community, instance] // Community will look like [!community, instance]
let (name, instance) = if split.len() == 3 { let (name, instance) = if split.len() == 3 {
(format!("/u/{}", split[1]), split[2]) (format!("/u/{}", split[1]), split[2])
@ -122,13 +122,13 @@ async fn build_response(
match fetch_response { match fetch_response {
SearchAcceptedObjects::Person(p) => { SearchAcceptedObjects::Person(p) => {
let user_uri = p.inner.id(domain)?.context("person has no id")?; let person_uri = p.inner.id(domain)?.context("person has no id")?;
let user = get_or_fetch_and_upsert_user(&user_uri, context, recursion_counter).await?; let person = get_or_fetch_and_upsert_person(&person_uri, context, recursion_counter).await?;
response.users = vec![ response.users = vec![
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
UserViewSafe::read(conn, user.id) PersonViewSafe::read(conn, person.id)
}) })
.await??, .await??,
]; ];
@ -182,10 +182,10 @@ async fn delete_object_locally(query_url: &Url, context: &LemmyContext) -> Resul
}) })
.await??; .await??;
} }
Object::User(u) => { Object::Person(u) => {
// TODO: implement update_deleted() for user, move it to ApubObject trait // TODO: implement update_deleted() for user, move it to ApubObject trait
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
User_::delete_account(conn, u.id) Person::delete_account(conn, u.id)
}) })
.await??; .await??;
} }

View file

@ -4,9 +4,9 @@ use crate::{
}; };
use actix_web::{body::Body, web, web::Path, HttpResponse}; use actix_web::{body::Body, web, web::Path, HttpResponse};
use diesel::result::Error::NotFound; use diesel::result::Error::NotFound;
use lemmy_api_structs::blocking;
use lemmy_db_queries::Crud; use lemmy_db_queries::Crud;
use lemmy_db_schema::source::comment::Comment; use lemmy_db_schema::source::comment::Comment;
use lemmy_structs::blocking;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::Deserialize; use serde::Deserialize;

View file

@ -9,10 +9,10 @@ use activitystreams::{
collection::{CollectionExt, OrderedCollection, UnorderedCollection}, collection::{CollectionExt, OrderedCollection, UnorderedCollection},
}; };
use actix_web::{body::Body, web, HttpResponse}; use actix_web::{body::Body, web, HttpResponse};
use lemmy_api_structs::blocking;
use lemmy_db_queries::source::{activity::Activity_, community::Community_}; use lemmy_db_queries::source::{activity::Activity_, community::Community_};
use lemmy_db_schema::source::{activity::Activity, community::Community}; use lemmy_db_schema::source::{activity::Activity, community::Community};
use lemmy_db_views_actor::community_follower_view::CommunityFollowerView; use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
use lemmy_structs::blocking;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::Deserialize; use serde::Deserialize;

View file

@ -1,17 +1,18 @@
use crate::APUB_JSON_CONTENT_TYPE; use crate::APUB_JSON_CONTENT_TYPE;
use actix_web::{body::Body, web, HttpResponse}; use actix_web::{body::Body, web, HttpResponse};
use http::StatusCode; use http::StatusCode;
use lemmy_api_structs::blocking;
use lemmy_db_queries::source::activity::Activity_; use lemmy_db_queries::source::activity::Activity_;
use lemmy_db_schema::source::activity::Activity; use lemmy_db_schema::source::activity::Activity;
use lemmy_structs::blocking; use lemmy_utils::{settings::structs::Settings, LemmyError};
use lemmy_utils::{settings::Settings, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url;
pub mod comment; pub mod comment;
pub mod community; pub mod community;
pub mod post; pub mod post;
pub mod user; pub mod person;
/// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub /// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub
/// headers. /// headers.
@ -46,12 +47,13 @@ pub async fn get_activity(
context: web::Data<LemmyContext>, context: web::Data<LemmyContext>,
) -> Result<HttpResponse<Body>, LemmyError> { ) -> Result<HttpResponse<Body>, LemmyError> {
let settings = Settings::get(); let settings = Settings::get();
let activity_id = format!( let activity_id = Url::parse(&format!(
"{}/activities/{}/{}", "{}/activities/{}/{}",
settings.get_protocol_and_hostname(), settings.get_protocol_and_hostname(),
info.type_, info.type_,
info.id info.id
); ))?
.into();
let activity = blocking(context.pool(), move |conn| { let activity = blocking(context.pool(), move |conn| {
Activity::read_from_apub_id(&conn, &activity_id) Activity::read_from_apub_id(&conn, &activity_id)
}) })

View file

@ -9,70 +9,70 @@ use activitystreams::{
collection::{CollectionExt, OrderedCollection}, collection::{CollectionExt, OrderedCollection},
}; };
use actix_web::{body::Body, web, HttpResponse}; use actix_web::{body::Body, web, HttpResponse};
use lemmy_db_queries::source::user::User; use lemmy_api_structs::blocking;
use lemmy_db_schema::source::user::User_; use lemmy_db_queries::source::person::Person_;
use lemmy_structs::blocking; use lemmy_db_schema::source::person::Person;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::Deserialize; use serde::Deserialize;
use url::Url; use url::Url;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct UserQuery { pub struct PersonQuery {
user_name: String, user_name: String,
} }
/// Return the ActivityPub json representation of a local user over HTTP. /// Return the ActivityPub json representation of a local person over HTTP.
pub async fn get_apub_user_http( pub async fn get_apub_person_http(
info: web::Path<UserQuery>, info: web::Path<PersonQuery>,
context: web::Data<LemmyContext>, context: web::Data<LemmyContext>,
) -> Result<HttpResponse<Body>, LemmyError> { ) -> Result<HttpResponse<Body>, LemmyError> {
let user_name = info.into_inner().user_name; let user_name = info.into_inner().user_name;
// TODO: this needs to be able to read deleted users, so that it can send tombstones // TODO: this needs to be able to read deleted persons, so that it can send tombstones
let user = blocking(context.pool(), move |conn| { let person = blocking(context.pool(), move |conn| {
User_::find_by_email_or_username(conn, &user_name) Person::find_by_name(conn, &user_name)
}) })
.await??; .await??;
if !user.deleted { if !person.deleted {
let apub = user.to_apub(context.pool()).await?; let apub = person.to_apub(context.pool()).await?;
Ok(create_apub_response(&apub)) Ok(create_apub_response(&apub))
} else { } else {
Ok(create_apub_tombstone_response(&user.to_tombstone()?)) Ok(create_apub_tombstone_response(&person.to_tombstone()?))
} }
} }
pub async fn get_apub_user_outbox( pub async fn get_apub_person_outbox(
info: web::Path<UserQuery>, info: web::Path<PersonQuery>,
context: web::Data<LemmyContext>, context: web::Data<LemmyContext>,
) -> Result<HttpResponse<Body>, LemmyError> { ) -> Result<HttpResponse<Body>, LemmyError> {
let user = blocking(context.pool(), move |conn| { let person = blocking(context.pool(), move |conn| {
User_::read_from_name(&conn, &info.user_name) Person::find_by_name(&conn, &info.user_name)
}) })
.await??; .await??;
// TODO: populate the user outbox // TODO: populate the person outbox
let mut collection = OrderedCollection::new(); let mut collection = OrderedCollection::new();
collection collection
.set_many_items(Vec::<Url>::new()) .set_many_items(Vec::<Url>::new())
.set_many_contexts(lemmy_context()?) .set_many_contexts(lemmy_context()?)
.set_id(user.get_outbox_url()?) .set_id(person.get_outbox_url()?)
.set_total_items(0_u64); .set_total_items(0_u64);
Ok(create_apub_response(&collection)) Ok(create_apub_response(&collection))
} }
pub async fn get_apub_user_inbox( pub async fn get_apub_person_inbox(
info: web::Path<UserQuery>, info: web::Path<PersonQuery>,
context: web::Data<LemmyContext>, context: web::Data<LemmyContext>,
) -> Result<HttpResponse<Body>, LemmyError> { ) -> Result<HttpResponse<Body>, LemmyError> {
let user = blocking(context.pool(), move |conn| { let person = blocking(context.pool(), move |conn| {
User_::read_from_name(&conn, &info.user_name) Person::find_by_name(&conn, &info.user_name)
}) })
.await??; .await??;
let mut collection = OrderedCollection::new(); let mut collection = OrderedCollection::new();
collection collection
.set_id(format!("{}/inbox", user.actor_id.into_inner()).parse()?) .set_id(format!("{}/inbox", person.actor_id.into_inner()).parse()?)
.set_many_contexts(lemmy_context()?); .set_many_contexts(lemmy_context()?);
Ok(create_apub_response(&collection)) Ok(create_apub_response(&collection))
} }

View file

@ -4,9 +4,9 @@ use crate::{
}; };
use actix_web::{body::Body, web, HttpResponse}; use actix_web::{body::Body, web, HttpResponse};
use diesel::result::Error::NotFound; use diesel::result::Error::NotFound;
use lemmy_api_structs::blocking;
use lemmy_db_queries::Crud; use lemmy_db_queries::Crud;
use lemmy_db_schema::source::post::Post; use lemmy_db_schema::source::post::Post;
use lemmy_structs::blocking;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::Deserialize; use serde::Deserialize;

View file

@ -26,13 +26,13 @@ use activitystreams::{
}; };
use actix_web::{web, HttpRequest, HttpResponse}; use actix_web::{web, HttpRequest, HttpResponse};
use anyhow::{anyhow, Context}; use anyhow::{anyhow, Context};
use lemmy_api_structs::blocking;
use lemmy_db_queries::{source::community::Community_, ApubObject, DbPool, Followable}; use lemmy_db_queries::{source::community::Community_, ApubObject, DbPool, Followable};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
community::{Community, CommunityFollower, CommunityFollowerForm}, community::{Community, CommunityFollower, CommunityFollowerForm},
user::User_, person::Person,
}; };
use lemmy_db_views_actor::community_user_ban_view::CommunityUserBanView; use lemmy_db_views_actor::community_person_ban_view::CommunityPersonBanView;
use lemmy_structs::blocking;
use lemmy_utils::{location_info, LemmyError}; use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use log::info; use log::info;
@ -44,8 +44,8 @@ use url::Url;
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]
pub enum CommunityValidTypes { pub enum CommunityValidTypes {
Follow, // follow request from a user Follow, // follow request from a person
Undo, // unfollow from a user Undo, // unfollow from a person
Create, // create post or comment Create, // create post or comment
Update, // update post or comment Update, // update post or comment
Like, // upvote post or comment Like, // upvote post or comment
@ -113,21 +113,21 @@ pub(crate) async fn community_receive_message(
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> { ) -> Result<HttpResponse, LemmyError> {
// Only users can send activities to the community, so we can get the actor as user // Only persons can send activities to the community, so we can get the actor as person
// unconditionally. // unconditionally.
let actor_id = actor.actor_id(); let actor_id = actor.actor_id();
let user = blocking(&context.pool(), move |conn| { let person = blocking(&context.pool(), move |conn| {
User_::read_from_apub_id(&conn, &actor_id.into()) Person::read_from_apub_id(&conn, &actor_id.into())
}) })
.await??; .await??;
check_community_or_site_ban(&user, to_community.id, context.pool()).await?; check_community_or_site_ban(&person, to_community.id, context.pool()).await?;
let any_base = activity.clone().into_any_base()?; let any_base = activity.clone().into_any_base()?;
let actor_url = actor.actor_id(); let actor_url = actor.actor_id();
let activity_kind = activity.kind().context(location_info!())?; let activity_kind = activity.kind().context(location_info!())?;
let do_announce = match activity_kind { let do_announce = match activity_kind {
CommunityValidTypes::Follow => { CommunityValidTypes::Follow => {
handle_follow(any_base.clone(), user, &to_community, &context).await?; handle_follow(any_base.clone(), person, &to_community, &context).await?;
false false
} }
CommunityValidTypes::Undo => { CommunityValidTypes::Undo => {
@ -162,7 +162,7 @@ pub(crate) async fn community_receive_message(
} }
CommunityValidTypes::Remove => { CommunityValidTypes::Remove => {
// TODO: we dont support remote mods, so this is ignored for now // TODO: we dont support remote mods, so this is ignored for now
//receive_remove_for_community(context, any_base.clone(), &user_url).await? //receive_remove_for_community(context, any_base.clone(), &person_url).await?
false false
} }
}; };
@ -178,20 +178,20 @@ pub(crate) async fn community_receive_message(
Ok(HttpResponse::Ok().finish()) Ok(HttpResponse::Ok().finish())
} }
/// Handle a follow request from a remote user, adding the user as follower and returning an /// Handle a follow request from a remote person, adding the person as follower and returning an
/// Accept activity. /// Accept activity.
async fn handle_follow( async fn handle_follow(
activity: AnyBase, activity: AnyBase,
user: User_, person: Person,
community: &Community, community: &Community,
context: &LemmyContext, context: &LemmyContext,
) -> Result<HttpResponse, LemmyError> { ) -> Result<HttpResponse, LemmyError> {
let follow = Follow::from_any_base(activity)?.context(location_info!())?; let follow = Follow::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&follow, &user.actor_id(), false)?; verify_activity_domains_valid(&follow, &person.actor_id(), false)?;
let community_follower_form = CommunityFollowerForm { let community_follower_form = CommunityFollowerForm {
community_id: community.id, community_id: community.id,
person_id: user.id, person_id: person.id,
pending: false, pending: false,
}; };
@ -226,27 +226,27 @@ async fn handle_undo(
} }
} }
/// Handle `Undo/Follow` from a user, removing the user from followers list. /// Handle `Undo/Follow` from a person, removing the person from followers list.
async fn handle_undo_follow( async fn handle_undo_follow(
activity: AnyBase, activity: AnyBase,
user_url: Url, person_url: Url,
community: &Community, community: &Community,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let undo = Undo::from_any_base(activity)?.context(location_info!())?; let undo = Undo::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&undo, &user_url, true)?; verify_activity_domains_valid(&undo, &person_url, true)?;
let object = undo.object().to_owned().one().context(location_info!())?; let object = undo.object().to_owned().one().context(location_info!())?;
let follow = Follow::from_any_base(object)?.context(location_info!())?; let follow = Follow::from_any_base(object)?.context(location_info!())?;
verify_activity_domains_valid(&follow, &user_url, false)?; verify_activity_domains_valid(&follow, &person_url, false)?;
let user = blocking(&context.pool(), move |conn| { let person = blocking(&context.pool(), move |conn| {
User_::read_from_apub_id(&conn, &user_url.into()) Person::read_from_apub_id(&conn, &person_url.into())
}) })
.await??; .await??;
let community_follower_form = CommunityFollowerForm { let community_follower_form = CommunityFollowerForm {
community_id: community.id, community_id: community.id,
person_id: user.id, person_id: person.id,
pending: false, pending: false,
}; };
@ -260,17 +260,17 @@ async fn handle_undo_follow(
} }
pub(crate) async fn check_community_or_site_ban( pub(crate) async fn check_community_or_site_ban(
user: &User_, person: &Person,
community_id: i32, community_id: i32,
pool: &DbPool, pool: &DbPool,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
if user.banned { if person.banned {
return Err(anyhow!("User is banned from site").into()); return Err(anyhow!("Person is banned from site").into());
} }
let user_id = user.id; let person_id = person.id;
let is_banned = move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok(); let is_banned = move |conn: &'_ _| CommunityPersonBanView::get(conn, person_id, community_id).is_ok();
if blocking(pool, is_banned).await? { if blocking(pool, is_banned).await? {
return Err(anyhow!("User is banned from community").into()); return Err(anyhow!("Person is banned from community").into());
} }
Ok(()) Ok(())

View file

@ -12,14 +12,14 @@ use activitystreams::{
}; };
use actix_web::HttpRequest; use actix_web::HttpRequest;
use anyhow::{anyhow, Context}; use anyhow::{anyhow, Context};
use lemmy_api_structs::blocking;
use lemmy_db_queries::{ use lemmy_db_queries::{
source::{activity::Activity_, community::Community_}, source::{activity::Activity_, community::Community_},
ApubObject, ApubObject,
DbPool, DbPool,
}; };
use lemmy_db_schema::source::{activity::Activity, community::Community, user::User_}; use lemmy_db_schema::source::{activity::Activity, community::Community, person::Person};
use lemmy_structs::blocking; use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
use lemmy_utils::{location_info, settings::Settings, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::Serialize; use serde::Serialize;
use std::fmt::Debug; use std::fmt::Debug;
@ -28,7 +28,7 @@ use url::Url;
pub mod community_inbox; pub mod community_inbox;
mod receive_for_community; mod receive_for_community;
pub mod shared_inbox; pub mod shared_inbox;
pub mod user_inbox; pub mod person_inbox;
pub(crate) fn get_activity_id<T, Kind>(activity: &T, creator_uri: &Url) -> Result<Url, LemmyError> pub(crate) fn get_activity_id<T, Kind>(activity: &T, creator_uri: &Url) -> Result<Url, LemmyError>
where where
@ -45,7 +45,7 @@ pub(crate) async fn is_activity_already_known(
pool: &DbPool, pool: &DbPool,
activity_id: &Url, activity_id: &Url,
) -> Result<bool, LemmyError> { ) -> Result<bool, LemmyError> {
let activity_id = activity_id.to_string(); let activity_id = activity_id.to_owned().into();
let existing = blocking(pool, move |conn| { let existing = blocking(pool, move |conn| {
Activity::read_from_apub_id(&conn, &activity_id) Activity::read_from_apub_id(&conn, &activity_id)
}) })
@ -119,17 +119,17 @@ where
} }
/// Returns true if `to_and_cc` contains at least one local user. /// Returns true if `to_and_cc` contains at least one local user.
pub(crate) async fn is_addressed_to_local_user( pub(crate) async fn is_addressed_to_local_person(
to_and_cc: &[Url], to_and_cc: &[Url],
pool: &DbPool, pool: &DbPool,
) -> Result<bool, LemmyError> { ) -> Result<bool, LemmyError> {
for url in to_and_cc { for url in to_and_cc {
let url = url.to_owned(); let url = url.to_owned();
let user = blocking(&pool, move |conn| { let person = blocking(&pool, move |conn| {
User_::read_from_apub_id(&conn, &url.into()) Person::read_from_apub_id(&conn, &url.into())
}) })
.await?; .await?;
if let Ok(u) = user { if let Ok(u) = person {
if u.local { if u.local {
return Ok(true); return Ok(true);
} }
@ -167,7 +167,7 @@ where
let id = activity.id_unchecked().context(location_info!())?; let id = activity.id_unchecked().context(location_info!())?;
let activity_domain = id.domain().context(location_info!())?; let activity_domain = id.domain().context(location_info!())?;
if activity_domain == Settings::get().hostname { if activity_domain == Settings::get().hostname() {
return Err( return Err(
anyhow!( anyhow!(
"Error: received activity which was sent by local instance: {:?}", "Error: received activity which was sent by local instance: {:?}",

View file

@ -25,7 +25,7 @@ use crate::{
inbox_verify_http_signature, inbox_verify_http_signature,
is_activity_already_known, is_activity_already_known,
is_addressed_to_community_followers, is_addressed_to_community_followers,
is_addressed_to_local_user, is_addressed_to_local_person,
is_addressed_to_public, is_addressed_to_public,
receive_for_community::{ receive_for_community::{
receive_create_for_community, receive_create_for_community,
@ -48,13 +48,13 @@ use activitystreams::{
use actix_web::{web, HttpRequest, HttpResponse}; use actix_web::{web, HttpRequest, HttpResponse};
use anyhow::{anyhow, Context}; use anyhow::{anyhow, Context};
use diesel::NotFound; use diesel::NotFound;
use lemmy_db_queries::{source::user::User, ApubObject, Followable}; use lemmy_api_structs::blocking;
use lemmy_db_queries::{source::person::Person_, ApubObject, Followable};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
community::{Community, CommunityFollower}, community::{Community, CommunityFollower},
private_message::PrivateMessage, private_message::PrivateMessage,
user::User_, person::Person,
}; };
use lemmy_structs::blocking;
use lemmy_utils::{location_info, LemmyError}; use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use log::debug; use log::debug;
@ -63,10 +63,10 @@ use std::fmt::Debug;
use strum_macros::EnumString; use strum_macros::EnumString;
use url::Url; use url::Url;
/// Allowed activities for user inbox. /// Allowed activities for person inbox.
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]
pub enum UserValidTypes { pub enum PersonValidTypes {
Accept, // community accepted our follow request Accept, // community accepted our follow request
Create, // create private message Create, // create private message
Update, // edit private message Update, // edit private message
@ -76,12 +76,12 @@ pub enum UserValidTypes {
Announce, // post, comment or vote in community Announce, // post, comment or vote in community
} }
pub type UserAcceptedActivities = ActorAndObject<UserValidTypes>; pub type PersonAcceptedActivities = ActorAndObject<PersonValidTypes>;
/// Handler for all incoming activities to user inboxes. /// Handler for all incoming activities to person inboxes.
pub async fn user_inbox( pub async fn person_inbox(
request: HttpRequest, request: HttpRequest,
input: web::Json<UserAcceptedActivities>, input: web::Json<PersonAcceptedActivities>,
path: web::Path<String>, path: web::Path<String>,
context: web::Data<LemmyContext>, context: web::Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> { ) -> Result<HttpResponse, LemmyError> {
@ -98,29 +98,29 @@ pub async fn user_inbox(
// Check if the activity is actually meant for us // Check if the activity is actually meant for us
let username = path.into_inner(); let username = path.into_inner();
let user = blocking(&context.pool(), move |conn| { let person = blocking(&context.pool(), move |conn| {
User_::read_from_name(&conn, &username) Person::find_by_name(&conn, &username)
}) })
.await??; .await??;
let to_and_cc = get_activity_to_and_cc(&activity); let to_and_cc = get_activity_to_and_cc(&activity);
// TODO: we should also accept activities that are sent to community followers // TODO: we should also accept activities that are sent to community followers
if !to_and_cc.contains(&&user.actor_id()) { if !to_and_cc.contains(&&person.actor_id()) {
return Err(anyhow!("Activity delivered to wrong user").into()); return Err(anyhow!("Activity delivered to wrong person").into());
} }
assert_activity_not_local(&activity)?; assert_activity_not_local(&activity)?;
insert_activity(&activity_id, activity.clone(), false, true, context.pool()).await?; insert_activity(&activity_id, activity.clone(), false, true, context.pool()).await?;
debug!( debug!(
"User {} received activity {:?} from {}", "Person {} received activity {:?} from {}",
user.name, person.name,
&activity.id_unchecked(), &activity.id_unchecked(),
&actor.actor_id() &actor.actor_id()
); );
user_receive_message( person_receive_message(
activity.clone(), activity.clone(),
Some(user.clone()), Some(person.clone()),
actor.as_ref(), actor.as_ref(),
&context, &context,
request_counter, request_counter,
@ -129,36 +129,43 @@ pub async fn user_inbox(
} }
/// Receives Accept/Follow, Announce, private messages and community (undo) remove, (undo) delete /// Receives Accept/Follow, Announce, private messages and community (undo) remove, (undo) delete
pub(crate) async fn user_receive_message( pub(crate) async fn person_receive_message(
activity: UserAcceptedActivities, activity: PersonAcceptedActivities,
to_user: Option<User_>, to_person: Option<Person>,
actor: &dyn ActorType, actor: &dyn ActorType,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> { ) -> Result<HttpResponse, LemmyError> {
is_for_user_inbox(context, &activity).await?; is_for_person_inbox(context, &activity).await?;
let any_base = activity.clone().into_any_base()?; let any_base = activity.clone().into_any_base()?;
let kind = activity.kind().context(location_info!())?; let kind = activity.kind().context(location_info!())?;
let actor_url = actor.actor_id(); let actor_url = actor.actor_id();
match kind { match kind {
UserValidTypes::Accept => { PersonValidTypes::Accept => {
receive_accept(&context, any_base, actor, to_user.unwrap(), request_counter).await?; receive_accept(
&context,
any_base,
actor,
to_person.expect("person provided"),
request_counter,
)
.await?;
} }
UserValidTypes::Announce => { PersonValidTypes::Announce => {
receive_announce(&context, any_base, actor, request_counter).await? receive_announce(&context, any_base, actor, request_counter).await?
} }
UserValidTypes::Create => { PersonValidTypes::Create => {
receive_create(&context, any_base, actor_url, request_counter).await? receive_create(&context, any_base, actor_url, request_counter).await?
} }
UserValidTypes::Update => { PersonValidTypes::Update => {
receive_update(&context, any_base, actor_url, request_counter).await? receive_update(&context, any_base, actor_url, request_counter).await?
} }
UserValidTypes::Delete => { PersonValidTypes::Delete => {
receive_delete(context, any_base, &actor_url, request_counter).await? receive_delete(context, any_base, &actor_url, request_counter).await?
} }
UserValidTypes::Undo => receive_undo(context, any_base, &actor_url, request_counter).await?, PersonValidTypes::Undo => receive_undo(context, any_base, &actor_url, request_counter).await?,
UserValidTypes::Remove => receive_remove_community(&context, any_base, &actor_url).await?, PersonValidTypes::Remove => receive_remove_community(&context, any_base, &actor_url).await?,
}; };
// TODO: would be logical to move websocket notification code here // TODO: would be logical to move websocket notification code here
@ -166,16 +173,16 @@ pub(crate) async fn user_receive_message(
Ok(HttpResponse::Ok().finish()) Ok(HttpResponse::Ok().finish())
} }
/// Returns true if the activity is addressed directly to one or more local users, or if it is /// Returns true if the activity is addressed directly to one or more local persons, or if it is
/// addressed to the followers collection of a remote community, and at least one local user follows /// addressed to the followers collection of a remote community, and at least one local person follows
/// it. /// it.
async fn is_for_user_inbox( async fn is_for_person_inbox(
context: &LemmyContext, context: &LemmyContext,
activity: &UserAcceptedActivities, activity: &PersonAcceptedActivities,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let to_and_cc = get_activity_to_and_cc(activity); let to_and_cc = get_activity_to_and_cc(activity);
// Check if it is addressed directly to any local user // Check if it is addressed directly to any local person
if is_addressed_to_local_user(&to_and_cc, context.pool()).await? { if is_addressed_to_local_person(&to_and_cc, context.pool()).await? {
return Ok(()); return Ok(());
} }
@ -198,7 +205,7 @@ async fn is_for_user_inbox(
} }
} }
Err(anyhow!("Not addressed for any local user").into()) Err(anyhow!("Not addressed for any local person").into())
} }
/// Handle accepted follows. /// Handle accepted follows.
@ -206,7 +213,7 @@ async fn receive_accept(
context: &LemmyContext, context: &LemmyContext,
activity: AnyBase, activity: AnyBase,
actor: &dyn ActorType, actor: &dyn ActorType,
user: User_, person: Person,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let accept = Accept::from_any_base(activity)?.context(location_info!())?; let accept = Accept::from_any_base(activity)?.context(location_info!())?;
@ -214,7 +221,7 @@ async fn receive_accept(
let object = accept.object().to_owned().one().context(location_info!())?; let object = accept.object().to_owned().one().context(location_info!())?;
let follow = Follow::from_any_base(object)?.context(location_info!())?; let follow = Follow::from_any_base(object)?.context(location_info!())?;
verify_activity_domains_valid(&follow, &user.actor_id(), false)?; verify_activity_domains_valid(&follow, &person.actor_id(), false)?;
let community_uri = accept let community_uri = accept
.actor()? .actor()?
@ -226,10 +233,10 @@ async fn receive_accept(
get_or_fetch_and_upsert_community(&community_uri, context, request_counter).await?; get_or_fetch_and_upsert_community(&community_uri, context, request_counter).await?;
let community_id = community.id; let community_id = community.id;
let user_id = user.id; let person_id = person.id;
// This will throw an error if no follow was requested // This will throw an error if no follow was requested
blocking(&context.pool(), move |conn| { blocking(&context.pool(), move |conn| {
CommunityFollower::follow_accepted(conn, community_id, user_id) CommunityFollower::follow_accepted(conn, community_id, person_id)
}) })
.await??; .await??;

View file

@ -43,9 +43,9 @@ use activitystreams::{
}; };
use anyhow::Context; use anyhow::Context;
use diesel::result::Error::NotFound; use diesel::result::Error::NotFound;
use lemmy_api_structs::blocking;
use lemmy_db_queries::Crud; use lemmy_db_queries::Crud;
use lemmy_db_schema::source::site::Site; use lemmy_db_schema::source::site::Site;
use lemmy_structs::blocking;
use lemmy_utils::{location_info, LemmyError}; use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use strum_macros::EnumString; use strum_macros::EnumString;
@ -58,7 +58,7 @@ enum PageOrNote {
} }
/// This file is for post/comment activities received by the community, and for post/comment /// This file is for post/comment activities received by the community, and for post/comment
/// activities announced by the community and received by the user. /// activities announced by the community and received by the person.
/// A post or comment being created /// A post or comment being created
pub(in crate::inbox) async fn receive_create_for_community( pub(in crate::inbox) async fn receive_create_for_community(
@ -115,11 +115,14 @@ pub(in crate::inbox) async fn receive_like_for_community(
verify_activity_domains_valid(&like, &expected_domain, false)?; verify_activity_domains_valid(&like, &expected_domain, false)?;
is_addressed_to_public(&like)?; is_addressed_to_public(&like)?;
let object_id = get_like_object_id(&like)?; let object_id = like
.object()
.as_single_xsd_any_uri()
.context(location_info!())?;
match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? { match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
PostOrComment::Post(post) => receive_like_post(like, post, context, request_counter).await, PostOrComment::Post(post) => receive_like_post(like, *post, context, request_counter).await,
PostOrComment::Comment(comment) => { PostOrComment::Comment(comment) => {
receive_like_comment(like, comment, context, request_counter).await receive_like_comment(like, *comment, context, request_counter).await
} }
} }
} }
@ -143,13 +146,16 @@ pub(in crate::inbox) async fn receive_dislike_for_community(
verify_activity_domains_valid(&dislike, &expected_domain, false)?; verify_activity_domains_valid(&dislike, &expected_domain, false)?;
is_addressed_to_public(&dislike)?; is_addressed_to_public(&dislike)?;
let object_id = get_like_object_id(&dislike)?; let object_id = dislike
.object()
.as_single_xsd_any_uri()
.context(location_info!())?;
match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? { match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
PostOrComment::Post(post) => { PostOrComment::Post(post) => {
receive_dislike_post(dislike, post, context, request_counter).await receive_dislike_post(dislike, *post, context, request_counter).await
} }
PostOrComment::Comment(comment) => { PostOrComment::Comment(comment) => {
receive_dislike_comment(dislike, comment, context, request_counter).await receive_dislike_comment(dislike, *comment, context, request_counter).await
} }
} }
} }
@ -171,8 +177,8 @@ pub(in crate::inbox) async fn receive_delete_for_community(
.context(location_info!())?; .context(location_info!())?;
match find_post_or_comment_by_id(context, object).await { match find_post_or_comment_by_id(context, object).await {
Ok(PostOrComment::Post(p)) => receive_delete_post(context, p).await, Ok(PostOrComment::Post(p)) => receive_delete_post(context, *p).await,
Ok(PostOrComment::Comment(c)) => receive_delete_comment(context, c).await, Ok(PostOrComment::Comment(c)) => receive_delete_comment(context, *c).await,
// if we dont have the object, no need to do anything // if we dont have the object, no need to do anything
Err(_) => Ok(()), Err(_) => Ok(()),
} }
@ -209,8 +215,8 @@ pub(in crate::inbox) async fn receive_remove_for_community(
remove.id(community_id.domain().context(location_info!())?)?; remove.id(community_id.domain().context(location_info!())?)?;
match find_post_or_comment_by_id(context, object).await { match find_post_or_comment_by_id(context, object).await {
Ok(PostOrComment::Post(p)) => receive_remove_post(context, remove, p).await, Ok(PostOrComment::Post(p)) => receive_remove_post(context, remove, *p).await,
Ok(PostOrComment::Comment(c)) => receive_remove_comment(context, remove, c).await, Ok(PostOrComment::Comment(c)) => receive_remove_comment(context, remove, *c).await,
// if we dont have the object, no need to do anything // if we dont have the object, no need to do anything
Err(_) => Ok(()), Err(_) => Ok(()),
} }
@ -270,8 +276,8 @@ pub(in crate::inbox) async fn receive_undo_delete_for_community(
.single_xsd_any_uri() .single_xsd_any_uri()
.context(location_info!())?; .context(location_info!())?;
match find_post_or_comment_by_id(context, object).await { match find_post_or_comment_by_id(context, object).await {
Ok(PostOrComment::Post(p)) => receive_undo_delete_post(context, p).await, Ok(PostOrComment::Post(p)) => receive_undo_delete_post(context, *p).await,
Ok(PostOrComment::Comment(c)) => receive_undo_delete_comment(context, c).await, Ok(PostOrComment::Comment(c)) => receive_undo_delete_comment(context, *c).await,
// if we dont have the object, no need to do anything // if we dont have the object, no need to do anything
Err(_) => Ok(()), Err(_) => Ok(()),
} }
@ -294,8 +300,8 @@ pub(in crate::inbox) async fn receive_undo_remove_for_community(
.single_xsd_any_uri() .single_xsd_any_uri()
.context(location_info!())?; .context(location_info!())?;
match find_post_or_comment_by_id(context, object).await { match find_post_or_comment_by_id(context, object).await {
Ok(PostOrComment::Post(p)) => receive_undo_remove_post(context, p).await, Ok(PostOrComment::Post(p)) => receive_undo_remove_post(context, *p).await,
Ok(PostOrComment::Comment(c)) => receive_undo_remove_comment(context, c).await, Ok(PostOrComment::Comment(c)) => receive_undo_remove_comment(context, *c).await,
// if we dont have the object, no need to do anything // if we dont have the object, no need to do anything
Err(_) => Ok(()), Err(_) => Ok(()),
} }
@ -313,13 +319,16 @@ pub(in crate::inbox) async fn receive_undo_like_for_community(
verify_activity_domains_valid(&like, &expected_domain, false)?; verify_activity_domains_valid(&like, &expected_domain, false)?;
is_addressed_to_public(&like)?; is_addressed_to_public(&like)?;
let object_id = get_like_object_id(&like)?; let object_id = like
.object()
.as_single_xsd_any_uri()
.context(location_info!())?;
match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? { match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
PostOrComment::Post(post) => { PostOrComment::Post(post) => {
receive_undo_like_post(&like, post, context, request_counter).await receive_undo_like_post(&like, *post, context, request_counter).await
} }
PostOrComment::Comment(comment) => { PostOrComment::Comment(comment) => {
receive_undo_like_comment(&like, comment, context, request_counter).await receive_undo_like_comment(&like, *comment, context, request_counter).await
} }
} }
} }
@ -336,13 +345,16 @@ pub(in crate::inbox) async fn receive_undo_dislike_for_community(
verify_activity_domains_valid(&dislike, &expected_domain, false)?; verify_activity_domains_valid(&dislike, &expected_domain, false)?;
is_addressed_to_public(&dislike)?; is_addressed_to_public(&dislike)?;
let object_id = get_like_object_id(&dislike)?; let object_id = dislike
.object()
.as_single_xsd_any_uri()
.context(location_info!())?;
match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? { match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
PostOrComment::Post(post) => { PostOrComment::Post(post) => {
receive_undo_dislike_post(&dislike, post, context, request_counter).await receive_undo_dislike_post(&dislike, *post, context, request_counter).await
} }
PostOrComment::Comment(comment) => { PostOrComment::Comment(comment) => {
receive_undo_dislike_comment(&dislike, comment, context, request_counter).await receive_undo_dislike_comment(&dislike, *comment, context, request_counter).await
} }
} }
} }
@ -353,35 +365,12 @@ async fn fetch_post_or_comment_by_id(
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<PostOrComment, LemmyError> { ) -> Result<PostOrComment, LemmyError> {
if let Ok(post) = get_or_fetch_and_insert_post(apub_id, context, request_counter).await { if let Ok(post) = get_or_fetch_and_insert_post(apub_id, context, request_counter).await {
return Ok(PostOrComment::Post(post)); return Ok(PostOrComment::Post(Box::new(post)));
} }
if let Ok(comment) = get_or_fetch_and_insert_comment(apub_id, context, request_counter).await { if let Ok(comment) = get_or_fetch_and_insert_comment(apub_id, context, request_counter).await {
return Ok(PostOrComment::Comment(comment)); return Ok(PostOrComment::Comment(Box::new(comment)));
} }
Err(NotFound.into()) Err(NotFound.into())
} }
fn get_like_object_id<Activity>(like_or_dislike: &Activity) -> Result<Url, LemmyError>
where
Activity: ActorAndObjectRefExt,
{
// TODO: For backwards compatibility with older Lemmy versions where like.object contains a full
// post/comment. This can be removed after some time, using
// `activity.oject().as_single_xsd_any_uri()` instead.
let object = like_or_dislike.object();
if let Some(xsd_uri) = object.as_single_xsd_any_uri() {
Ok(xsd_uri.to_owned())
} else {
Ok(
object
.to_owned()
.one()
.context(location_info!())?
.id()
.context(location_info!())?
.to_owned(),
)
}
}

View file

@ -7,17 +7,17 @@ use crate::{
inbox_verify_http_signature, inbox_verify_http_signature,
is_activity_already_known, is_activity_already_known,
is_addressed_to_community_followers, is_addressed_to_community_followers,
is_addressed_to_local_user, is_addressed_to_local_person,
user_inbox::{user_receive_message, UserAcceptedActivities}, person_inbox::{person_receive_message, PersonAcceptedActivities},
}, },
insert_activity, insert_activity,
}; };
use activitystreams::{activity::ActorAndObject, prelude::*}; use activitystreams::{activity::ActorAndObject, prelude::*};
use actix_web::{web, HttpRequest, HttpResponse}; use actix_web::{web, HttpRequest, HttpResponse};
use anyhow::Context; use anyhow::Context;
use lemmy_api_structs::blocking;
use lemmy_db_queries::{ApubObject, DbPool}; use lemmy_db_queries::{ApubObject, DbPool};
use lemmy_db_schema::source::community::Community; use lemmy_db_schema::source::community::Community;
use lemmy_structs::blocking;
use lemmy_utils::{location_info, LemmyError}; use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -69,9 +69,9 @@ pub async fn shared_inbox(
let mut res: Option<HttpResponse> = None; let mut res: Option<HttpResponse> = None;
let to_and_cc = get_activity_to_and_cc(&activity); let to_and_cc = get_activity_to_and_cc(&activity);
// Handle community first, so in case the sender is banned by the community, it will error out. // Handle community first, so in case the sender is banned by the community, it will error out.
// If we handled the user receive first, the activity would be inserted to the database before the // If we handled the person receive first, the activity would be inserted to the database before the
// community could check for bans. // community could check for bans.
// Note that an activity can be addressed to a community and to a user (or multiple users) at the // Note that an activity can be addressed to a community and to a person (or multiple persons) at the
// same time. In this case we still only handle it once, to avoid duplicate websocket // same time. In this case we still only handle it once, to avoid duplicate websocket
// notifications. // notifications.
let community = extract_local_community_from_destinations(&to_and_cc, context.pool()).await?; let community = extract_local_community_from_destinations(&to_and_cc, context.pool()).await?;
@ -88,13 +88,13 @@ pub async fn shared_inbox(
) )
.await?, .await?,
); );
} else if is_addressed_to_local_user(&to_and_cc, context.pool()).await? { } else if is_addressed_to_local_person(&to_and_cc, context.pool()).await? {
let user_activity = UserAcceptedActivities::from_any_base(activity_any_base.clone())? let person_activity = PersonAcceptedActivities::from_any_base(activity_any_base.clone())?
.context(location_info!())?; .context(location_info!())?;
// `to_user` is only used for follow activities (which we dont receive here), so no need to pass // `to_person` is only used for follow activities (which we dont receive here), so no need to pass
// it in // it in
user_receive_message( person_receive_message(
user_activity, person_activity,
None, None,
actor.as_ref(), actor.as_ref(),
&context, &context,
@ -105,11 +105,11 @@ pub async fn shared_inbox(
.await? .await?
.is_some() .is_some()
{ {
let user_activity = UserAcceptedActivities::from_any_base(activity_any_base.clone())? let person_activity = PersonAcceptedActivities::from_any_base(activity_any_base.clone())?
.context(location_info!())?; .context(location_info!())?;
res = Some( res = Some(
user_receive_message( person_receive_message(
user_activity, person_activity,
None, None,
actor.as_ref(), actor.as_ref(),
&context, &context,

View file

@ -24,17 +24,20 @@ use activitystreams::{
use activitystreams_ext::{Ext1, Ext2}; use activitystreams_ext::{Ext1, Ext2};
use anyhow::{anyhow, Context}; use anyhow::{anyhow, Context};
use diesel::NotFound; use diesel::NotFound;
use lemmy_api_structs::blocking;
use lemmy_db_queries::{source::activity::Activity_, ApubObject, DbPool}; use lemmy_db_queries::{source::activity::Activity_, ApubObject, DbPool};
use lemmy_db_schema::source::{ use lemmy_db_schema::{
activity::Activity, source::{
comment::Comment, activity::Activity,
community::Community, comment::Comment,
post::Post, community::Community,
private_message::PrivateMessage, post::Post,
user::User_, private_message::PrivateMessage,
person::Person as DbPerson,
},
DbUrl,
}; };
use lemmy_structs::blocking; use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
use lemmy_utils::{location_info, settings::Settings, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::Serialize; use serde::Serialize;
use std::net::IpAddr; use std::net::IpAddr;
@ -42,7 +45,7 @@ use url::{ParseError, Url};
/// Activitystreams type for community /// Activitystreams type for community
type GroupExt = Ext2<ApActor<ApObject<Group>>, GroupExtension, PublicKeyExtension>; type GroupExt = Ext2<ApActor<ApObject<Group>>, GroupExtension, PublicKeyExtension>;
/// Activitystreams type for user /// Activitystreams type for person
type PersonExt = Ext1<ApActor<ApObject<Person>>, PublicKeyExtension>; type PersonExt = Ext1<ApActor<ApObject<Person>>, PublicKeyExtension>;
/// Activitystreams type for post /// Activitystreams type for post
type PageExt = Ext1<ApObject<Page>, PageExtension>; type PageExt = Ext1<ApObject<Page>, PageExtension>;
@ -64,7 +67,7 @@ fn check_is_apub_id_valid(apub_id: &Url) -> Result<(), LemmyError> {
let domain = apub_id.domain().context(location_info!())?.to_string(); let domain = apub_id.domain().context(location_info!())?.to_string();
let local_instance = settings.get_hostname_without_port()?; let local_instance = settings.get_hostname_without_port()?;
if !settings.federation.enabled { if !settings.federation().enabled {
return if domain == local_instance { return if domain == local_instance {
Ok(()) Ok(())
} else { } else {
@ -88,22 +91,23 @@ fn check_is_apub_id_valid(apub_id: &Url) -> Result<(), LemmyError> {
return Err(anyhow!("invalid apub id scheme {}: {}", apub_id.scheme(), apub_id).into()); return Err(anyhow!("invalid apub id scheme {}: {}", apub_id.scheme(), apub_id).into());
} }
let mut allowed_instances = Settings::get().get_allowed_instances(); let allowed_instances = Settings::get().get_allowed_instances();
let blocked_instances = Settings::get().get_blocked_instances(); let blocked_instances = Settings::get().get_blocked_instances();
if allowed_instances.is_empty() && blocked_instances.is_empty() {
if allowed_instances.is_none() && blocked_instances.is_none() {
Ok(()) Ok(())
} else if !allowed_instances.is_empty() { } else if let Some(mut allowed) = allowed_instances {
// need to allow this explicitly because apub receive might contain objects from our local // need to allow this explicitly because apub receive might contain objects from our local
// instance. split is needed to remove the port in our federation test setup. // instance. split is needed to remove the port in our federation test setup.
allowed_instances.push(local_instance); allowed.push(local_instance);
if allowed_instances.contains(&domain) { if allowed.contains(&domain) {
Ok(()) Ok(())
} else { } else {
Err(anyhow!("{} not in federation allowlist", domain).into()) Err(anyhow!("{} not in federation allowlist", domain).into())
} }
} else if !blocked_instances.is_empty() { } else if let Some(blocked) = blocked_instances {
if blocked_instances.contains(&domain) { if blocked.contains(&domain) {
Err(anyhow!("{} is in federation blocklist", domain).into()) Err(anyhow!("{} is in federation blocklist", domain).into())
} else { } else {
Ok(()) Ok(())
@ -117,27 +121,27 @@ fn check_is_apub_id_valid(apub_id: &Url) -> Result<(), LemmyError> {
/// and actors in Lemmy. /// and actors in Lemmy.
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
pub trait ApubObjectType { pub trait ApubObjectType {
async fn send_create(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError>; async fn send_create(&self, creator: &DbPerson, context: &LemmyContext) -> Result<(), LemmyError>;
async fn send_update(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError>; async fn send_update(&self, creator: &DbPerson, context: &LemmyContext) -> Result<(), LemmyError>;
async fn send_delete(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError>; async fn send_delete(&self, creator: &DbPerson, context: &LemmyContext) -> Result<(), LemmyError>;
async fn send_undo_delete( async fn send_undo_delete(
&self, &self,
creator: &User_, creator: &DbPerson,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError>; ) -> Result<(), LemmyError>;
async fn send_remove(&self, mod_: &User_, context: &LemmyContext) -> Result<(), LemmyError>; async fn send_remove(&self, mod_: &DbPerson, context: &LemmyContext) -> Result<(), LemmyError>;
async fn send_undo_remove(&self, mod_: &User_, context: &LemmyContext) -> Result<(), LemmyError>; async fn send_undo_remove(&self, mod_: &DbPerson, context: &LemmyContext) -> Result<(), LemmyError>;
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
pub trait ApubLikeableType { pub trait ApubLikeableType {
async fn send_like(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError>; async fn send_like(&self, creator: &DbPerson, context: &LemmyContext) -> Result<(), LemmyError>;
async fn send_dislike(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError>; async fn send_dislike(&self, creator: &DbPerson, context: &LemmyContext) -> Result<(), LemmyError>;
async fn send_undo_like(&self, creator: &User_, context: &LemmyContext) async fn send_undo_like(&self, creator: &DbPerson, context: &LemmyContext)
-> Result<(), LemmyError>; -> Result<(), LemmyError>;
} }
/// Common methods provided by ActivityPub actors (community and user). Not all methods are /// Common methods provided by ActivityPub actors (community and person). Not all methods are
/// implemented by all actors. /// implemented by all actors.
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
pub trait ActorType { pub trait ActorType {
@ -205,7 +209,7 @@ pub trait ActorType {
pub enum EndpointType { pub enum EndpointType {
Community, Community,
User, Person,
Post, Post,
Comment, Comment,
PrivateMessage, PrivateMessage,
@ -215,10 +219,10 @@ pub enum EndpointType {
pub fn generate_apub_endpoint( pub fn generate_apub_endpoint(
endpoint_type: EndpointType, endpoint_type: EndpointType,
name: &str, name: &str,
) -> Result<lemmy_db_schema::Url, ParseError> { ) -> Result<DbUrl, ParseError> {
let point = match endpoint_type { let point = match endpoint_type {
EndpointType::Community => "c", EndpointType::Community => "c",
EndpointType::User => "u", EndpointType::Person => "u",
EndpointType::Post => "post", EndpointType::Post => "post",
EndpointType::Comment => "comment", EndpointType::Comment => "comment",
EndpointType::PrivateMessage => "private_message", EndpointType::PrivateMessage => "private_message",
@ -235,21 +239,15 @@ pub fn generate_apub_endpoint(
) )
} }
pub fn generate_followers_url( pub fn generate_followers_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
actor_id: &lemmy_db_schema::Url,
) -> Result<lemmy_db_schema::Url, ParseError> {
Ok(Url::parse(&format!("{}/followers", actor_id))?.into()) Ok(Url::parse(&format!("{}/followers", actor_id))?.into())
} }
pub fn generate_inbox_url( pub fn generate_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
actor_id: &lemmy_db_schema::Url,
) -> Result<lemmy_db_schema::Url, ParseError> {
Ok(Url::parse(&format!("{}/inbox", actor_id))?.into()) Ok(Url::parse(&format!("{}/inbox", actor_id))?.into())
} }
pub fn generate_shared_inbox_url( pub fn generate_shared_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, LemmyError> {
actor_id: &lemmy_db_schema::Url,
) -> Result<lemmy_db_schema::Url, LemmyError> {
let actor_id = actor_id.clone().into_inner(); let actor_id = actor_id.clone().into_inner();
let url = format!( let url = format!(
"{}://{}{}/inbox", "{}://{}{}/inbox",
@ -276,7 +274,7 @@ pub(crate) async fn insert_activity<T>(
where where
T: Serialize + std::fmt::Debug + Send + 'static, T: Serialize + std::fmt::Debug + Send + 'static,
{ {
let ap_id = ap_id.to_string(); let ap_id = ap_id.to_owned().into();
blocking(pool, move |conn| { blocking(pool, move |conn| {
Activity::insert(conn, ap_id, &activity, local, sensitive) Activity::insert(conn, ap_id, &activity, local, sensitive)
}) })
@ -285,8 +283,8 @@ where
} }
pub(crate) enum PostOrComment { pub(crate) enum PostOrComment {
Comment(Comment), Comment(Box<Comment>),
Post(Post), Post(Box<Post>),
} }
/// Tries to find a post or comment in the local database, without any network requests. /// Tries to find a post or comment in the local database, without any network requests.
@ -302,7 +300,7 @@ pub(crate) async fn find_post_or_comment_by_id(
}) })
.await?; .await?;
if let Ok(p) = post { if let Ok(p) = post {
return Ok(PostOrComment::Post(p)); return Ok(PostOrComment::Post(Box::new(p)));
} }
let ap_id = apub_id.clone(); let ap_id = apub_id.clone();
@ -311,7 +309,7 @@ pub(crate) async fn find_post_or_comment_by_id(
}) })
.await?; .await?;
if let Ok(c) = comment { if let Ok(c) = comment {
return Ok(PostOrComment::Comment(c)); return Ok(PostOrComment::Comment(Box::new(c)));
} }
Err(NotFound.into()) Err(NotFound.into())
@ -321,7 +319,7 @@ pub(crate) enum Object {
Comment(Comment), Comment(Comment),
Post(Post), Post(Post),
Community(Community), Community(Community),
User(User_), Person(DbPerson),
PrivateMessage(PrivateMessage), PrivateMessage(PrivateMessage),
} }
@ -332,18 +330,18 @@ pub(crate) async fn find_object_by_id(
let ap_id = apub_id.clone(); let ap_id = apub_id.clone();
if let Ok(pc) = find_post_or_comment_by_id(context, ap_id.to_owned()).await { if let Ok(pc) = find_post_or_comment_by_id(context, ap_id.to_owned()).await {
return Ok(match pc { return Ok(match pc {
PostOrComment::Post(p) => Object::Post(p), PostOrComment::Post(p) => Object::Post(*p),
PostOrComment::Comment(c) => Object::Comment(c), PostOrComment::Comment(c) => Object::Comment(*c),
}); });
} }
let ap_id = apub_id.clone(); let ap_id = apub_id.clone();
let user = blocking(context.pool(), move |conn| { let person = blocking(context.pool(), move |conn| {
User_::read_from_apub_id(conn, &ap_id.into()) DbPerson::read_from_apub_id(conn, &ap_id.into())
}) })
.await?; .await?;
if let Ok(u) = user { if let Ok(u) = person {
return Ok(Object::User(u)); return Ok(Object::Person(u));
} }
let ap_id = apub_id.clone(); let ap_id = apub_id.clone();

View file

@ -6,7 +6,7 @@ use crate::{
check_object_for_community_or_site_ban, check_object_for_community_or_site_ban,
create_tombstone, create_tombstone,
get_object_from_apub, get_object_from_apub,
get_or_fetch_and_upsert_user, get_or_fetch_and_upsert_person,
get_source_markdown_value, get_source_markdown_value,
set_content_and_source, set_content_and_source,
FromApub, FromApub,
@ -21,13 +21,13 @@ use activitystreams::{
public, public,
}; };
use anyhow::{anyhow, Context}; use anyhow::{anyhow, Context};
use lemmy_api_structs::blocking;
use lemmy_db_queries::{Crud, DbPool}; use lemmy_db_queries::{Crud, DbPool};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
comment::{Comment, CommentForm}, comment::{Comment, CommentForm},
post::Post, post::Post,
user::User_, person::Person,
}; };
use lemmy_structs::blocking;
use lemmy_utils::{ use lemmy_utils::{
location_info, location_info,
utils::{convert_datetime, remove_slurs}, utils::{convert_datetime, remove_slurs},
@ -44,7 +44,7 @@ impl ToApub for Comment {
let mut comment = ApObject::new(Note::new()); let mut comment = ApObject::new(Note::new());
let creator_id = self.creator_id; let creator_id = self.creator_id;
let creator = blocking(pool, move |conn| User_::read(conn, creator_id)).await??; let creator = blocking(pool, move |conn| Person::read(conn, creator_id)).await??;
let post_id = self.post_id; let post_id = self.post_id;
let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??; let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
@ -135,7 +135,7 @@ impl FromApubToForm<NoteExt> for CommentForm {
.as_single_xsd_any_uri() .as_single_xsd_any_uri()
.context(location_info!())?; .context(location_info!())?;
let creator = get_or_fetch_and_upsert_user(creator_actor_id, context, request_counter).await?; let creator = get_or_fetch_and_upsert_person(creator_actor_id, context, request_counter).await?;
let mut in_reply_tos = note let mut in_reply_tos = note
.in_reply_to() .in_reply_to()

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
extensions::{context::lemmy_context, group_extensions::GroupExtension}, extensions::{context::lemmy_context, group_extensions::GroupExtension},
fetcher::user::get_or_fetch_and_upsert_user, fetcher::person::get_or_fetch_and_upsert_person,
objects::{ objects::{
check_object_domain, check_object_domain,
create_tombstone, create_tombstone,
@ -22,13 +22,13 @@ use activitystreams::{
}; };
use activitystreams_ext::Ext2; use activitystreams_ext::Ext2;
use anyhow::Context; use anyhow::Context;
use lemmy_api_structs::blocking;
use lemmy_db_queries::DbPool; use lemmy_db_queries::DbPool;
use lemmy_db_schema::{ use lemmy_db_schema::{
naive_now, naive_now,
source::community::{Community, CommunityForm}, source::community::{Community, CommunityForm},
}; };
use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView; use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
use lemmy_structs::blocking;
use lemmy_utils::{ use lemmy_utils::{
location_info, location_info,
utils::{check_slurs, check_slurs_opt, convert_datetime}, utils::{check_slurs, check_slurs_opt, convert_datetime},
@ -73,13 +73,13 @@ impl ToApub for Community {
if let Some(icon_url) = &self.icon { if let Some(icon_url) = &self.icon {
let mut image = Image::new(); let mut image = Image::new();
image.set_url(Url::parse(icon_url)?); image.set_url::<Url>(icon_url.to_owned().into());
group.set_icon(image.into_any_base()?); group.set_icon(image.into_any_base()?);
} }
if let Some(banner_url) = &self.banner { if let Some(banner_url) = &self.banner {
let mut image = Image::new(); let mut image = Image::new();
image.set_url(Url::parse(banner_url)?); image.set_url::<Url>(banner_url.to_owned().into());
group.set_image(image.into_any_base()?); group.set_image(image.into_any_base()?);
} }
@ -143,7 +143,7 @@ impl FromApubToForm<GroupExt> for CommunityForm {
.as_xsd_any_uri() .as_xsd_any_uri()
.context(location_info!())?; .context(location_info!())?;
let creator = get_or_fetch_and_upsert_user(creator_uri, context, request_counter).await?; let creator = get_or_fetch_and_upsert_person(creator_uri, context, request_counter).await?;
let name = group let name = group
.inner .inner
.preferred_username() .preferred_username()
@ -173,7 +173,7 @@ impl FromApubToForm<GroupExt> for CommunityForm {
.url() .url()
.context(location_info!())? .context(location_info!())?
.as_single_xsd_any_uri() .as_single_xsd_any_uri()
.map(|u| u.to_string()), .map(|u| u.to_owned().into()),
), ),
None => None, None => None,
}; };
@ -185,7 +185,7 @@ impl FromApubToForm<GroupExt> for CommunityForm {
.url() .url()
.context(location_info!())? .context(location_info!())?
.as_single_xsd_any_uri() .as_single_xsd_any_uri()
.map(|u| u.to_string()), .map(|u| u.to_owned().into()),
), ),
None => None, None => None,
}; };

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
check_is_apub_id_valid, check_is_apub_id_valid,
fetcher::{community::get_or_fetch_and_upsert_community, user::get_or_fetch_and_upsert_user}, fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
inbox::community_inbox::check_community_or_site_ban, inbox::community_inbox::check_community_or_site_ban,
}; };
use activitystreams::{ use activitystreams::{
@ -12,10 +12,15 @@ use activitystreams::{
use anyhow::{anyhow, Context}; use anyhow::{anyhow, Context};
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use diesel::result::Error::NotFound; use diesel::result::Error::NotFound;
use lemmy_api_structs::blocking;
use lemmy_db_queries::{ApubObject, Crud, DbPool}; use lemmy_db_queries::{ApubObject, Crud, DbPool};
use lemmy_db_schema::source::community::Community; use lemmy_db_schema::{source::community::Community, DbUrl};
use lemmy_structs::blocking; use lemmy_utils::{
use lemmy_utils::{location_info, settings::Settings, utils::convert_datetime, LemmyError}; location_info,
settings::structs::Settings,
utils::{convert_datetime, markdown_to_html},
LemmyError,
};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use url::Url; use url::Url;
@ -23,7 +28,7 @@ pub(crate) mod comment;
pub(crate) mod community; pub(crate) mod community;
pub(crate) mod post; pub(crate) mod post;
pub(crate) mod private_message; pub(crate) mod private_message;
pub(crate) mod user; pub(crate) mod person;
/// Trait for converting an object or actor into the respective ActivityPub type. /// Trait for converting an object or actor into the respective ActivityPub type.
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
@ -91,7 +96,7 @@ where
pub(in crate::objects) fn check_object_domain<T, Kind>( pub(in crate::objects) fn check_object_domain<T, Kind>(
apub: &T, apub: &T,
expected_domain: Url, expected_domain: Url,
) -> Result<lemmy_db_schema::Url, LemmyError> ) -> Result<DbUrl, LemmyError>
where where
T: Base + AsBase<Kind>, T: Base + AsBase<Kind>,
{ {
@ -114,11 +119,8 @@ where
.set_media_type(mime_markdown()?); .set_media_type(mime_markdown()?);
object.set_source(source.into_any_base()?); object.set_source(source.into_any_base()?);
// set `content` to markdown for compatibility with older Lemmy versions object.set_content(markdown_to_html(markdown_text));
// TODO: change this to HTML in a while object.set_media_type(mime_html()?);
object.set_content(markdown_text);
object.set_media_type(mime_markdown()?);
//object.set_content(markdown_to_html(markdown_text));
Ok(()) Ok(())
} }
@ -134,32 +136,28 @@ where
.flatten() .flatten()
.map(|s| s.to_string()); .map(|s| s.to_string());
if content.is_some() { if content.is_some() {
let source = object.source(); let source = object.source().context(location_info!())?;
// updated lemmy version, read markdown from `source.content` let source = Object::<()>::from_any_base(source.to_owned())?.context(location_info!())?;
if let Some(source) = source { check_is_markdown(source.media_type())?;
let source = Object::<()>::from_any_base(source.to_owned())?.context(location_info!())?; let source_content = source
check_is_markdown(source.media_type())?; .content()
let source_content = source .map(|s| s.as_single_xsd_string())
.content() .flatten()
.map(|s| s.as_single_xsd_string()) .context(location_info!())?
.flatten() .to_string();
.context(location_info!())? return Ok(Some(source_content));
.to_string();
return Ok(Some(source_content));
}
// older lemmy version, read markdown from `content`
// TODO: remove this after a while
else {
return Ok(content);
}
} }
Ok(None) Ok(None)
} }
pub(in crate::objects) fn mime_markdown() -> Result<Mime, FromStrError> { fn mime_markdown() -> Result<Mime, FromStrError> {
"text/markdown".parse() "text/markdown".parse()
} }
fn mime_html() -> Result<Mime, FromStrError> {
"text/html".parse()
}
pub(in crate::objects) fn check_is_markdown(mime: Option<&Mime>) -> Result<(), LemmyError> { pub(in crate::objects) fn check_is_markdown(mime: Option<&Mime>) -> Result<(), LemmyError> {
let mime = mime.context(location_info!())?; let mime = mime.context(location_info!())?;
if !mime.eq(&mime_markdown()?) { if !mime.eq(&mime_markdown()?) {
@ -189,7 +187,7 @@ where
let domain = object_id.domain().context(location_info!())?; let domain = object_id.domain().context(location_info!())?;
// if its a local object, return it directly from the database // if its a local object, return it directly from the database
if Settings::get().hostname == domain { if Settings::get().hostname() == domain {
let object = blocking(context.pool(), move |conn| { let object = blocking(context.pool(), move |conn| {
To::read_from_apub_id(conn, &object_id.into()) To::read_from_apub_id(conn, &object_id.into())
}) })
@ -214,13 +212,13 @@ pub(in crate::objects) async fn check_object_for_community_or_site_ban<T, Kind>(
where where
T: ObjectExt<Kind>, T: ObjectExt<Kind>,
{ {
let user_id = object let person_id = object
.attributed_to() .attributed_to()
.context(location_info!())? .context(location_info!())?
.as_single_xsd_any_uri() .as_single_xsd_any_uri()
.context(location_info!())?; .context(location_info!())?;
let user = get_or_fetch_and_upsert_user(user_id, context, request_counter).await?; let person = get_or_fetch_and_upsert_person(person_id, context, request_counter).await?;
check_community_or_site_ban(&user, community_id, context.pool()).await check_community_or_site_ban(&person, community_id, context.pool()).await
} }
pub(in crate::objects) async fn get_to_community<T, Kind>( pub(in crate::objects) async fn get_to_community<T, Kind>(

View file

@ -18,15 +18,15 @@ use activitystreams::{
}; };
use activitystreams_ext::Ext1; use activitystreams_ext::Ext1;
use anyhow::Context; use anyhow::Context;
use lemmy_api_structs::blocking;
use lemmy_db_queries::{ApubObject, DbPool}; use lemmy_db_queries::{ApubObject, DbPool};
use lemmy_db_schema::{ use lemmy_db_schema::{
naive_now, naive_now,
source::user::{UserForm, User_}, source::person::{PersonForm, Person as DbPerson},
}; };
use lemmy_structs::blocking;
use lemmy_utils::{ use lemmy_utils::{
location_info, location_info,
settings::Settings, settings::structs::Settings,
utils::{check_slurs, check_slurs_opt, convert_datetime}, utils::{check_slurs, check_slurs_opt, convert_datetime},
LemmyError, LemmyError,
}; };
@ -34,7 +34,7 @@ use lemmy_websocket::LemmyContext;
use url::Url; use url::Url;
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ToApub for User_ { impl ToApub for DbPerson {
type ApubType = PersonExt; type ApubType = PersonExt;
async fn to_apub(&self, _pool: &DbPool) -> Result<PersonExt, LemmyError> { async fn to_apub(&self, _pool: &DbPool) -> Result<PersonExt, LemmyError> {
@ -50,21 +50,18 @@ impl ToApub for User_ {
if let Some(avatar_url) = &self.avatar { if let Some(avatar_url) = &self.avatar {
let mut image = Image::new(); let mut image = Image::new();
image.set_url(Url::parse(avatar_url)?); image.set_url::<Url>(avatar_url.to_owned().into());
person.set_icon(image.into_any_base()?); person.set_icon(image.into_any_base()?);
} }
if let Some(banner_url) = &self.banner { if let Some(banner_url) = &self.banner {
let mut image = Image::new(); let mut image = Image::new();
image.set_url(Url::parse(banner_url)?); image.set_url::<Url>(banner_url.to_owned().into());
person.set_image(image.into_any_base()?); person.set_image(image.into_any_base()?);
} }
if let Some(bio) = &self.bio { if let Some(bio) = &self.bio {
set_content_and_source(&mut person, bio)?; set_content_and_source(&mut person, bio)?;
// Also set summary for compatibility with older Lemmy versions.
// TODO: remove this after a while.
person.set_summary(bio.to_owned());
} }
if let Some(i) = self.preferred_username.to_owned() { if let Some(i) = self.preferred_username.to_owned() {
@ -88,7 +85,7 @@ impl ToApub for User_ {
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl FromApub for User_ { impl FromApub for DbPerson {
type ApubType = PersonExt; type ApubType = PersonExt;
async fn from_apub( async fn from_apub(
@ -96,26 +93,26 @@ impl FromApub for User_ {
context: &LemmyContext, context: &LemmyContext,
expected_domain: Url, expected_domain: Url,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<User_, LemmyError> { ) -> Result<DbPerson, LemmyError> {
let user_id = person.id_unchecked().context(location_info!())?.to_owned(); let person_id = person.id_unchecked().context(location_info!())?.to_owned();
let domain = user_id.domain().context(location_info!())?; let domain = person_id.domain().context(location_info!())?;
if domain == Settings::get().hostname { if domain == Settings::get().hostname() {
let user = blocking(context.pool(), move |conn| { let person = blocking(context.pool(), move |conn| {
User_::read_from_apub_id(conn, &user_id.into()) DbPerson::read_from_apub_id(conn, &person_id.into())
}) })
.await??; .await??;
Ok(user) Ok(person)
} else { } else {
let user_form = let person_form =
UserForm::from_apub(person, context, expected_domain, request_counter).await?; PersonForm::from_apub(person, context, expected_domain, request_counter).await?;
let user = blocking(context.pool(), move |conn| User_::upsert(conn, &user_form)).await??; let person = blocking(context.pool(), move |conn| DbPerson::upsert(conn, &person_form)).await??;
Ok(user) Ok(person)
} }
} }
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl FromApubToForm<PersonExt> for UserForm { impl FromApubToForm<PersonExt> for PersonForm {
async fn from_apub( async fn from_apub(
person: &PersonExt, person: &PersonExt,
_context: &LemmyContext, _context: &LemmyContext,
@ -129,7 +126,7 @@ impl FromApubToForm<PersonExt> for UserForm {
.url() .url()
.context(location_info!())? .context(location_info!())?
.as_single_xsd_any_uri() .as_single_xsd_any_uri()
.map(|u| u.to_string()), .map(|url| url.to_owned()),
), ),
None => None, None => None,
}; };
@ -142,7 +139,7 @@ impl FromApubToForm<PersonExt> for UserForm {
.url() .url()
.context(location_info!())? .context(location_info!())?
.as_single_xsd_any_uri() .as_single_xsd_any_uri()
.map(|u| u.to_string()), .map(|url| url.to_owned()),
), ),
None => None, None => None,
}; };
@ -170,30 +167,20 @@ impl FromApubToForm<PersonExt> for UserForm {
check_slurs_opt(&preferred_username)?; check_slurs_opt(&preferred_username)?;
check_slurs_opt(&bio)?; check_slurs_opt(&bio)?;
Ok(UserForm { Ok(PersonForm {
name, name,
preferred_username: Some(preferred_username), preferred_username: Some(preferred_username),
password_encrypted: "".to_string(),
admin: false,
banned: None, banned: None,
email: None, deleted: None,
avatar, avatar: avatar.map(|o| o.map(|i| i.into())),
banner, banner: banner.map(|o| o.map(|i| i.into())),
published: person.inner.published().map(|u| u.to_owned().naive_local()), published: person.inner.published().map(|u| u.to_owned().naive_local()),
updated: person.updated().map(|u| u.to_owned().naive_local()), updated: person.updated().map(|u| u.to_owned().naive_local()),
show_nsfw: false,
theme: "".to_string(),
default_sort_type: 0,
default_listing_type: 0,
lang: "".to_string(),
show_avatars: false,
send_notifications_to_email: false,
matrix_user_id: None,
actor_id: Some(check_object_domain(person, expected_domain)?), actor_id: Some(check_object_domain(person, expected_domain)?),
bio: Some(bio), bio: Some(bio),
local: false, local: Some(false),
private_key: None, private_key: None,
public_key: Some(person.ext_one.public_key.to_owned().public_key_pem), public_key: Some(Some(person.ext_one.public_key.to_owned().public_key_pem)),
last_refreshed_at: Some(naive_now()), last_refreshed_at: Some(naive_now()),
inbox_url: Some(person.inner.inbox()?.to_owned().into()), inbox_url: Some(person.inner.inbox()?.to_owned().into()),
shared_inbox_url: Some(shared_inbox), shared_inbox_url: Some(shared_inbox),

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
extensions::{context::lemmy_context, page_extension::PageExtension}, extensions::{context::lemmy_context, page_extension::PageExtension},
fetcher::user::get_or_fetch_and_upsert_user, fetcher::person::get_or_fetch_and_upsert_person,
objects::{ objects::{
check_object_domain, check_object_domain,
check_object_for_community_or_site_ban, check_object_for_community_or_site_ban,
@ -22,13 +22,16 @@ use activitystreams::{
}; };
use activitystreams_ext::Ext1; use activitystreams_ext::Ext1;
use anyhow::Context; use anyhow::Context;
use lemmy_api_structs::blocking;
use lemmy_db_queries::{Crud, DbPool}; use lemmy_db_queries::{Crud, DbPool};
use lemmy_db_schema::source::{ use lemmy_db_schema::{
community::Community, self,
post::{Post, PostForm}, source::{
user::User_, community::Community,
post::{Post, PostForm},
person::Person,
},
}; };
use lemmy_structs::blocking;
use lemmy_utils::{ use lemmy_utils::{
location_info, location_info,
request::fetch_iframely_and_pictrs_data, request::fetch_iframely_and_pictrs_data,
@ -47,7 +50,7 @@ impl ToApub for Post {
let mut page = ApObject::new(Page::new()); let mut page = ApObject::new(Page::new());
let creator_id = self.creator_id; let creator_id = self.creator_id;
let creator = blocking(pool, move |conn| User_::read(conn, creator_id)).await??; let creator = blocking(pool, move |conn| Person::read(conn, creator_id)).await??;
let community_id = self.community_id; let community_id = self.community_id;
let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??; let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
@ -70,16 +73,13 @@ impl ToApub for Post {
set_content_and_source(&mut page, &body)?; set_content_and_source(&mut page, &body)?;
} }
// TODO: hacky code because we get self.url == Some("") if let Some(url) = &self.url {
// https://github.com/LemmyNet/lemmy/issues/602 page.set_url::<Url>(url.to_owned().into());
let url = self.url.as_ref().filter(|u| !u.is_empty());
if let Some(u) = url {
page.set_url(Url::parse(u)?);
} }
if let Some(thumbnail_url) = &self.thumbnail_url { if let Some(thumbnail_url) = &self.thumbnail_url {
let mut image = Image::new(); let mut image = Image::new();
image.set_url(Url::parse(thumbnail_url)?); image.set_url::<Url>(thumbnail_url.to_owned().into());
page.set_image(image.into_any_base()?); page.set_image(image.into_any_base()?);
} }
@ -142,11 +142,11 @@ impl FromApubToForm<PageExt> for PostForm {
.as_single_xsd_any_uri() .as_single_xsd_any_uri()
.context(location_info!())?; .context(location_info!())?;
let creator = get_or_fetch_and_upsert_user(creator_actor_id, context, request_counter).await?; let creator = get_or_fetch_and_upsert_person(creator_actor_id, context, request_counter).await?;
let community = get_to_community(page, context, request_counter).await?; let community = get_to_community(page, context, request_counter).await?;
let thumbnail_url = match &page.inner.image() { let thumbnail_url: Option<Url> = match &page.inner.image() {
Some(any_image) => Image::from_any_base( Some(any_image) => Image::from_any_base(
any_image any_image
.to_owned() .to_owned()
@ -158,7 +158,7 @@ impl FromApubToForm<PageExt> for PostForm {
.url() .url()
.context(location_info!())? .context(location_info!())?
.as_single_xsd_any_uri() .as_single_xsd_any_uri()
.map(|u| u.to_string()), .map(|url| url.to_owned()),
None => None, None => None,
}; };
let url = page let url = page
@ -166,11 +166,11 @@ impl FromApubToForm<PageExt> for PostForm {
.url() .url()
.map(|u| u.as_single_xsd_any_uri()) .map(|u| u.as_single_xsd_any_uri())
.flatten() .flatten()
.map(|s| s.to_string()); .map(|u| u.to_owned());
let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) = let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
if let Some(url) = &url { if let Some(url) = &url {
fetch_iframely_and_pictrs_data(context.client(), Some(url.to_owned())).await fetch_iframely_and_pictrs_data(context.client(), Some(url)).await
} else { } else {
(None, None, None, thumbnail_url) (None, None, None, thumbnail_url)
}; };
@ -192,7 +192,7 @@ impl FromApubToForm<PageExt> for PostForm {
let body_slurs_removed = body.map(|b| remove_slurs(&b)); let body_slurs_removed = body.map(|b| remove_slurs(&b));
Ok(PostForm { Ok(PostForm {
name, name,
url, url: url.map(|u| u.into()),
body: body_slurs_removed, body: body_slurs_removed,
creator_id: creator.id, creator_id: creator.id,
community_id: community.id, community_id: community.id,
@ -214,7 +214,7 @@ impl FromApubToForm<PageExt> for PostForm {
embed_title: iframely_title, embed_title: iframely_title,
embed_description: iframely_description, embed_description: iframely_description,
embed_html: iframely_html, embed_html: iframely_html,
thumbnail_url: pictrs_thumbnail, thumbnail_url: pictrs_thumbnail.map(|u| u.into()),
ap_id: Some(check_object_domain(page, expected_domain)?), ap_id: Some(check_object_domain(page, expected_domain)?),
local: false, local: false,
}) })

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
check_is_apub_id_valid, check_is_apub_id_valid,
extensions::context::lemmy_context, extensions::context::lemmy_context,
fetcher::user::get_or_fetch_and_upsert_user, fetcher::person::get_or_fetch_and_upsert_person,
objects::{ objects::{
check_object_domain, check_object_domain,
create_tombstone, create_tombstone,
@ -19,12 +19,12 @@ use activitystreams::{
prelude::*, prelude::*,
}; };
use anyhow::Context; use anyhow::Context;
use lemmy_api_structs::blocking;
use lemmy_db_queries::{Crud, DbPool}; use lemmy_db_queries::{Crud, DbPool};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
private_message::{PrivateMessage, PrivateMessageForm}, private_message::{PrivateMessage, PrivateMessageForm},
user::User_, person::Person,
}; };
use lemmy_structs::blocking;
use lemmy_utils::{location_info, utils::convert_datetime, LemmyError}; use lemmy_utils::{location_info, utils::convert_datetime, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use url::Url; use url::Url;
@ -37,10 +37,10 @@ impl ToApub for PrivateMessage {
let mut private_message = ApObject::new(Note::new()); let mut private_message = ApObject::new(Note::new());
let creator_id = self.creator_id; let creator_id = self.creator_id;
let creator = blocking(pool, move |conn| User_::read(conn, creator_id)).await??; let creator = blocking(pool, move |conn| Person::read(conn, creator_id)).await??;
let recipient_id = self.recipient_id; let recipient_id = self.recipient_id;
let recipient = blocking(pool, move |conn| User_::read(conn, recipient_id)).await??; let recipient = blocking(pool, move |conn| Person::read(conn, recipient_id)).await??;
private_message private_message
.set_many_contexts(lemmy_context()?) .set_many_contexts(lemmy_context()?)
@ -97,7 +97,7 @@ impl FromApubToForm<NoteExt> for PrivateMessageForm {
.single_xsd_any_uri() .single_xsd_any_uri()
.context(location_info!())?; .context(location_info!())?;
let creator = get_or_fetch_and_upsert_user(&creator_actor_id, context, request_counter).await?; let creator = get_or_fetch_and_upsert_person(&creator_actor_id, context, request_counter).await?;
let recipient_actor_id = note let recipient_actor_id = note
.to() .to()
.context(location_info!())? .context(location_info!())?
@ -105,7 +105,7 @@ impl FromApubToForm<NoteExt> for PrivateMessageForm {
.single_xsd_any_uri() .single_xsd_any_uri()
.context(location_info!())?; .context(location_info!())?;
let recipient = let recipient =
get_or_fetch_and_upsert_user(&recipient_actor_id, context, request_counter).await?; get_or_fetch_and_upsert_person(&recipient_actor_id, context, request_counter).await?;
let ap_id = note.id_unchecked().context(location_info!())?.to_string(); let ap_id = note.id_unchecked().context(location_info!())?.to_string();
check_is_apub_id_valid(&Url::parse(&ap_id)?)?; check_is_apub_id_valid(&Url::parse(&ap_id)?)?;

View file

@ -9,31 +9,29 @@ use crate::{
}, },
get_activity, get_activity,
post::get_apub_post, post::get_apub_post,
user::{get_apub_user_http, get_apub_user_inbox, get_apub_user_outbox}, person::{get_apub_person_http, get_apub_person_inbox, get_apub_person_outbox},
}, },
inbox::{community_inbox::community_inbox, shared_inbox::shared_inbox, user_inbox::user_inbox}, inbox::{community_inbox::community_inbox, shared_inbox::shared_inbox, person_inbox::person_inbox},
APUB_JSON_CONTENT_TYPE, APUB_JSON_CONTENT_TYPE,
}; };
use actix_web::*; use actix_web::*;
use http_signature_normalization_actix::digest::middleware::VerifyDigest; use http_signature_normalization_actix::digest::middleware::VerifyDigest;
use lemmy_utils::settings::Settings; use lemmy_utils::settings::structs::Settings;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
static APUB_JSON_CONTENT_TYPE_LONG: &str = static APUB_JSON_CONTENT_TYPE_LONG: &str =
"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""; "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"";
pub fn config(cfg: &mut web::ServiceConfig) { pub fn config(cfg: &mut web::ServiceConfig) {
if Settings::get().federation.enabled { if Settings::get().federation().enabled {
println!("federation enabled, host is {}", Settings::get().hostname); println!("federation enabled, host is {}", Settings::get().hostname());
let digest_verifier = VerifyDigest::new(Sha256::new()); let digest_verifier = VerifyDigest::new(Sha256::new());
let header_guard_accept = guard::Any(guard::Header("Accept", APUB_JSON_CONTENT_TYPE)) let header_guard_accept = guard::Any(guard::Header("Accept", APUB_JSON_CONTENT_TYPE))
.or(guard::Header("Accept", APUB_JSON_CONTENT_TYPE_LONG)); .or(guard::Header("Accept", APUB_JSON_CONTENT_TYPE_LONG));
let header_guard_content_type = let header_guard_content_type =
guard::Any(guard::Header("Content-Type", APUB_JSON_CONTENT_TYPE)) guard::Any(guard::Header("Content-Type", APUB_JSON_CONTENT_TYPE))
.or(guard::Header("Content-Type", APUB_JSON_CONTENT_TYPE_LONG)) .or(guard::Header("Content-Type", APUB_JSON_CONTENT_TYPE_LONG));
// TODO: compatibility with previous lemmy versions, remove this later
.or(guard::Header("Content-Type", "application/json"));
cfg cfg
.service( .service(
@ -55,9 +53,9 @@ pub fn config(cfg: &mut web::ServiceConfig) {
"/c/{community_name}/inbox", "/c/{community_name}/inbox",
web::get().to(get_apub_community_inbox), web::get().to(get_apub_community_inbox),
) )
.route("/u/{user_name}", web::get().to(get_apub_user_http)) .route("/u/{user_name}", web::get().to(get_apub_person_http))
.route("/u/{user_name}/outbox", web::get().to(get_apub_user_outbox)) .route("/u/{user_name}/outbox", web::get().to(get_apub_person_outbox))
.route("/u/{user_name}/inbox", web::get().to(get_apub_user_inbox)) .route("/u/{user_name}/inbox", web::get().to(get_apub_person_inbox))
.route("/post/{post_id}", web::get().to(get_apub_post)) .route("/post/{post_id}", web::get().to(get_apub_post))
.route("/comment/{comment_id}", web::get().to(get_apub_comment)) .route("/comment/{comment_id}", web::get().to(get_apub_comment))
.route("/activities/{type_}/{id}", web::get().to(get_activity)), .route("/activities/{type_}/{id}", web::get().to(get_activity)),
@ -68,7 +66,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
.wrap(digest_verifier) .wrap(digest_verifier)
.guard(header_guard_content_type) .guard(header_guard_content_type)
.route("/c/{community_name}/inbox", web::post().to(community_inbox)) .route("/c/{community_name}/inbox", web::post().to(community_inbox))
.route("/u/{user_name}/inbox", web::post().to(user_inbox)) .route("/u/{user_name}/inbox", web::post().to(person_inbox))
.route("/inbox", web::post().to(shared_inbox)), .route("/inbox", web::post().to(shared_inbox)),
); );
} }

View file

@ -6,6 +6,7 @@ edition = "2018"
[lib] [lib]
name = "lemmy_db_queries" name = "lemmy_db_queries"
path = "src/lib.rs" path = "src/lib.rs"
doctest = false
[dependencies] [dependencies]
lemmy_utils = { path = "../utils" } lemmy_utils = { path = "../utils" }
@ -19,7 +20,10 @@ strum = "0.20.0"
strum_macros = "0.20.1" strum_macros = "0.20.1"
log = "0.4.14" log = "0.4.14"
sha2 = "0.9.3" sha2 = "0.9.3"
url = { version = "2.2.0", features = ["serde"] } url = { version = "2.2.1", features = ["serde"] }
lazy_static = "1.4.0" lazy_static = "1.4.0"
regex = "1.4.3" regex = "1.4.3"
bcrypt = "0.9.0" bcrypt = "0.9.0"
[dev-dependencies]
serial_test = "0.5.1"

View file

@ -28,8 +28,6 @@ mod tests {
establish_unpooled_connection, establish_unpooled_connection,
Crud, Crud,
Likeable, Likeable,
ListingType,
SortType,
}; };
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
comment::{Comment, CommentForm, CommentLike, CommentLikeForm}, comment::{Comment, CommentForm, CommentLike, CommentLikeForm},
@ -37,8 +35,10 @@ mod tests {
post::{Post, PostForm}, post::{Post, PostForm},
person::{PersonForm, Person}, person::{PersonForm, Person},
}; };
use serial_test::serial;
#[test] #[test]
#[serial]
fn test_crud() { fn test_crud() {
let conn = establish_unpooled_connection(); let conn = establish_unpooled_connection();

View file

@ -32,8 +32,6 @@ mod tests {
establish_unpooled_connection, establish_unpooled_connection,
Crud, Crud,
Followable, Followable,
ListingType,
SortType,
}; };
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
comment::{Comment, CommentForm}, comment::{Comment, CommentForm},
@ -41,8 +39,10 @@ mod tests {
post::{Post, PostForm}, post::{Post, PostForm},
person::{PersonForm, Person}, person::{PersonForm, Person},
}; };
use serial_test::serial;
#[test] #[test]
#[serial]
fn test_crud() { fn test_crud() {
let conn = establish_unpooled_connection(); let conn = establish_unpooled_connection();
@ -51,13 +51,13 @@ mod tests {
preferred_username: None, preferred_username: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: Some(false), banned: None,
deleted: false, deleted: None,
published: None, published: None,
updated: None, updated: None,
actor_id: None, actor_id: None,
bio: None, bio: None,
local: true, local: None,
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: None, last_refreshed_at: None,
@ -72,13 +72,13 @@ mod tests {
preferred_username: None, preferred_username: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: Some(false), banned: None,
deleted: false, deleted: None,
published: None, published: None,
updated: None, updated: None,
actor_id: None, actor_id: None,
bio: None, bio: None,
local: true, local: None,
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: None, last_refreshed_at: None,

View file

@ -28,8 +28,6 @@ mod tests {
establish_unpooled_connection, establish_unpooled_connection,
Crud, Crud,
Likeable, Likeable,
ListingType,
SortType,
}; };
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
comment::{Comment, CommentForm, CommentLike, CommentLikeForm}, comment::{Comment, CommentForm, CommentLike, CommentLikeForm},
@ -37,8 +35,10 @@ mod tests {
post::{Post, PostForm, PostLike, PostLikeForm}, post::{Post, PostForm, PostLike, PostLikeForm},
person::{PersonForm, Person}, person::{PersonForm, Person},
}; };
use serial_test::serial;
#[test] #[test]
#[serial]
fn test_crud() { fn test_crud() {
let conn = establish_unpooled_connection(); let conn = establish_unpooled_connection();
@ -47,13 +47,13 @@ mod tests {
preferred_username: None, preferred_username: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: Some(false), banned: None,
deleted: false, deleted: None,
published: None, published: None,
updated: None, updated: None,
actor_id: None, actor_id: None,
bio: None, bio: None,
local: true, local: None,
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: None, last_refreshed_at: None,
@ -68,13 +68,13 @@ mod tests {
preferred_username: None, preferred_username: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: Some(false), banned: None,
deleted: false, deleted: None,
published: None, published: None,
updated: None, updated: None,
actor_id: None, actor_id: None,
bio: None, bio: None,
local: true, local: None,
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: None, last_refreshed_at: None,

View file

@ -31,6 +31,7 @@ mod tests {
aggregates::post_aggregates::PostAggregates, aggregates::post_aggregates::PostAggregates,
establish_unpooled_connection, establish_unpooled_connection,
Crud, Crud,
Likeable,
}; };
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
comment::{Comment, CommentForm}, comment::{Comment, CommentForm},
@ -38,8 +39,10 @@ mod tests {
post::{Post, PostForm, PostLike, PostLikeForm}, post::{Post, PostForm, PostLike, PostLikeForm},
person::{PersonForm, Person}, person::{PersonForm, Person},
}; };
use serial_test::serial;
#[test] #[test]
#[serial]
fn test_crud() { fn test_crud() {
let conn = establish_unpooled_connection(); let conn = establish_unpooled_connection();
@ -48,13 +51,13 @@ mod tests {
preferred_username: None, preferred_username: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: Some(false), banned: None,
deleted: false, deleted: None,
published: None, published: None,
updated: None, updated: None,
actor_id: None, actor_id: None,
bio: None, bio: None,
local: true, local: None,
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: None, last_refreshed_at: None,
@ -69,13 +72,13 @@ mod tests {
preferred_username: None, preferred_username: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: Some(false), banned: None,
deleted: false, deleted: None,
published: None, published: None,
updated: None, updated: None,
actor_id: None, actor_id: None,
bio: None, bio: None,
local: true, local: None,
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: None, last_refreshed_at: None,

View file

@ -28,6 +28,7 @@ mod tests {
use crate::{ use crate::{
aggregates::site_aggregates::SiteAggregates, aggregates::site_aggregates::SiteAggregates,
establish_unpooled_connection, establish_unpooled_connection,
Crud,
}; };
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
comment::{Comment, CommentForm}, comment::{Comment, CommentForm},
@ -36,8 +37,10 @@ mod tests {
site::{Site, SiteForm}, site::{Site, SiteForm},
person::{PersonForm, Person}, person::{PersonForm, Person},
}; };
use serial_test::serial;
#[test] #[test]
#[serial]
fn test_crud() { fn test_crud() {
let conn = establish_unpooled_connection(); let conn = establish_unpooled_connection();
@ -46,13 +49,13 @@ mod tests {
preferred_username: None, preferred_username: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: Some(false), banned: None,
deleted: false, deleted: None,
published: None, published: None,
updated: None, updated: None,
actor_id: None, actor_id: None,
bio: None, bio: None,
local: true, local: None,
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: None, last_refreshed_at: None,

View file

@ -9,11 +9,16 @@ extern crate lazy_static;
#[macro_use] #[macro_use]
extern crate diesel_migrations; extern crate diesel_migrations;
#[cfg(test)]
extern crate serial_test;
use diesel::{result::Error, *}; use diesel::{result::Error, *};
use lemmy_db_schema::Url; use lemmy_db_schema::DbUrl;
use lemmy_utils::ApiError;
use regex::Regex; use regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{env, env::VarError}; use std::{env, env::VarError};
use url::Url;
pub mod aggregates; pub mod aggregates;
pub mod source; pub mod source;
@ -109,7 +114,7 @@ pub trait Reportable<T> {
} }
pub trait ApubObject<T> { pub trait ApubObject<T> {
fn read_from_apub_id(conn: &PgConnection, object_id: &Url) -> Result<Self, Error> fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Self, Error>
where where
Self: Sized; Self: Sized;
fn upsert(conn: &PgConnection, user_form: &T) -> Result<Self, Error> fn upsert(conn: &PgConnection, user_form: &T) -> Result<Self, Error>
@ -216,6 +221,20 @@ pub fn diesel_option_overwrite(opt: &Option<String>) -> Option<Option<String>> {
} }
} }
pub fn diesel_option_overwrite_to_url(
opt: &Option<String>,
) -> Result<Option<Option<DbUrl>>, ApiError> {
match opt.as_ref().map(|s| s.as_str()) {
// An empty string is an erase
Some("") => Ok(Some(None)),
Some(str_url) => match Url::parse(str_url) {
Ok(url) => Ok(Some(Some(url.into()))),
Err(_) => Err(ApiError::err("invalid_url")),
},
None => Ok(None),
}
}
embed_migrations!(); embed_migrations!();
pub fn establish_unpooled_connection() -> PgConnection { pub fn establish_unpooled_connection() -> PgConnection {
@ -228,13 +247,14 @@ pub fn establish_unpooled_connection() -> PgConnection {
}; };
let conn = let conn =
PgConnection::establish(&db_url).unwrap_or_else(|_| panic!("Error connecting to {}", db_url)); PgConnection::establish(&db_url).unwrap_or_else(|_| panic!("Error connecting to {}", db_url));
embedded_migrations::run(&conn).unwrap(); embedded_migrations::run(&conn).expect("load migrations");
conn conn
} }
lazy_static! { lazy_static! {
static ref EMAIL_REGEX: Regex = static ref EMAIL_REGEX: Regex =
Regex::new(r"^[a-zA-Z0-9.!#$%&*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").unwrap(); Regex::new(r"^[a-zA-Z0-9.!#$%&*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$")
.expect("compile email regex");
} }
pub mod functions { pub mod functions {
@ -247,7 +267,7 @@ pub mod functions {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::fuzzy_search; use super::{fuzzy_search, *};
use crate::is_email_regex; use crate::is_email_regex;
#[test] #[test]
@ -261,4 +281,32 @@ mod tests {
assert!(is_email_regex("gush@gmail.com")); assert!(is_email_regex("gush@gmail.com"));
assert!(!is_email_regex("nada_neutho")); assert!(!is_email_regex("nada_neutho"));
} }
#[test]
fn test_diesel_option_overwrite() {
assert_eq!(diesel_option_overwrite(&None), None);
assert_eq!(diesel_option_overwrite(&Some("".to_string())), Some(None));
assert_eq!(
diesel_option_overwrite(&Some("test".to_string())),
Some(Some("test".to_string()))
);
}
#[test]
fn test_diesel_option_overwrite_to_url() {
assert!(matches!(diesel_option_overwrite_to_url(&None), Ok(None)));
assert!(matches!(
diesel_option_overwrite_to_url(&Some("".to_string())),
Ok(Some(None))
));
assert!(matches!(
diesel_option_overwrite_to_url(&Some("invalid_url".to_string())),
Err(_)
));
let example_url = "https://example.com";
assert!(matches!(
diesel_option_overwrite_to_url(&Some(example_url.to_string())),
Ok(Some(Some(url))) if url == Url::parse(&example_url).unwrap().into()
));
}
} }

View file

@ -1,6 +1,6 @@
use crate::Crud; use crate::Crud;
use diesel::{dsl::*, result::Error, sql_types::Text, *}; use diesel::{dsl::*, result::Error, sql_types::Text, *};
use lemmy_db_schema::{source::activity::*, Url}; use lemmy_db_schema::{source::activity::*, DbUrl};
use log::debug; use log::debug;
use serde::Serialize; use serde::Serialize;
use serde_json::Value; use serde_json::Value;
@ -41,7 +41,7 @@ impl Crud<ActivityForm> for Activity {
pub trait Activity_ { pub trait Activity_ {
fn insert<T>( fn insert<T>(
conn: &PgConnection, conn: &PgConnection,
ap_id: String, ap_id: DbUrl,
data: &T, data: &T,
local: bool, local: bool,
sensitive: bool, sensitive: bool,
@ -49,20 +49,20 @@ pub trait Activity_ {
where where
T: Serialize + Debug; T: Serialize + Debug;
fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Activity, Error>; fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Activity, Error>;
fn delete_olds(conn: &PgConnection) -> Result<usize, Error>; fn delete_olds(conn: &PgConnection) -> Result<usize, Error>;
/// Returns up to 20 activities of type `Announce/Create/Page` from the community /// Returns up to 20 activities of type `Announce/Create/Page` from the community
fn read_community_outbox( fn read_community_outbox(
conn: &PgConnection, conn: &PgConnection,
community_actor_id: &Url, community_actor_id: &DbUrl,
) -> Result<Vec<Value>, Error>; ) -> Result<Vec<Value>, Error>;
} }
impl Activity_ for Activity { impl Activity_ for Activity {
fn insert<T>( fn insert<T>(
conn: &PgConnection, conn: &PgConnection,
ap_id: String, ap_id: DbUrl,
data: &T, data: &T,
local: bool, local: bool,
sensitive: bool, sensitive: bool,
@ -88,7 +88,7 @@ impl Activity_ for Activity {
} }
} }
fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Activity, Error> { fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Activity, Error> {
use lemmy_db_schema::schema::activity::dsl::*; use lemmy_db_schema::schema::activity::dsl::*;
activity.filter(ap_id.eq(object_id)).first::<Self>(conn) activity.filter(ap_id.eq(object_id)).first::<Self>(conn)
} }
@ -100,7 +100,7 @@ impl Activity_ for Activity {
fn read_community_outbox( fn read_community_outbox(
conn: &PgConnection, conn: &PgConnection,
community_actor_id: &Url, community_actor_id: &DbUrl,
) -> Result<Vec<Value>, Error> { ) -> Result<Vec<Value>, Error> {
use lemmy_db_schema::schema::activity::dsl::*; use lemmy_db_schema::schema::activity::dsl::*;
let res: Vec<Value> = activity let res: Vec<Value> = activity
@ -121,6 +121,7 @@ impl Activity_ for Activity {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*;
use crate::{ use crate::{
establish_unpooled_connection, establish_unpooled_connection,
source::activity::Activity_, source::activity::Activity_,
@ -130,8 +131,11 @@ mod tests {
person::{PersonForm, Person}, person::{PersonForm, Person},
}; };
use serde_json::Value; use serde_json::Value;
use serial_test::serial;
use url::Url;
#[test] #[test]
#[serial]
fn test_crud() { fn test_crud() {
let conn = establish_unpooled_connection(); let conn = establish_unpooled_connection();
@ -156,8 +160,11 @@ mod tests {
let inserted_creator = Person::create(&conn, &creator_form).unwrap(); let inserted_creator = Person::create(&conn, &creator_form).unwrap();
let ap_id = let ap_id: DbUrl = Url::parse(
"https://enterprise.lemmy.ml/activities/delete/f1b5d57c-80f8-4e03-a615-688d552e946c"; "https://enterprise.lemmy.ml/activities/delete/f1b5d57c-80f8-4e03-a615-688d552e946c",
)
.unwrap()
.into();
let test_json: Value = serde_json::from_str( let test_json: Value = serde_json::from_str(
r#"{ r#"{
"@context": "https://www.w3.org/ns/activitystreams", "@context": "https://www.w3.org/ns/activitystreams",
@ -173,7 +180,7 @@ mod tests {
) )
.unwrap(); .unwrap();
let activity_form = ActivityForm { let activity_form = ActivityForm {
ap_id: ap_id.to_string(), ap_id: ap_id.clone(),
data: test_json.to_owned(), data: test_json.to_owned(),
local: true, local: true,
sensitive: false, sensitive: false,
@ -183,7 +190,7 @@ mod tests {
let inserted_activity = Activity::create(&conn, &activity_form).unwrap(); let inserted_activity = Activity::create(&conn, &activity_form).unwrap();
let expected_activity = Activity { let expected_activity = Activity {
ap_id: Some(ap_id.to_string()), ap_id: Some(ap_id.clone()),
id: inserted_activity.id, id: inserted_activity.id,
data: test_json, data: test_json,
local: true, local: true,
@ -193,7 +200,7 @@ mod tests {
}; };
let read_activity = Activity::read(&conn, inserted_activity.id).unwrap(); let read_activity = Activity::read(&conn, inserted_activity.id).unwrap();
let read_activity_by_apub_id = Activity::read_from_apub_id(&conn, ap_id).unwrap(); let read_activity_by_apub_id = Activity::read_from_apub_id(&conn, &ap_id).unwrap();
Person::delete(&conn, inserted_creator.id).unwrap(); Person::delete(&conn, inserted_creator.id).unwrap();
Activity::delete(&conn, inserted_activity.id).unwrap(); Activity::delete(&conn, inserted_activity.id).unwrap();

View file

@ -10,11 +10,11 @@ use lemmy_db_schema::{
CommentSaved, CommentSaved,
CommentSavedForm, CommentSavedForm,
}, },
Url, DbUrl,
}; };
pub trait Comment_ { pub trait Comment_ {
fn update_ap_id(conn: &PgConnection, comment_id: i32, apub_id: Url) -> Result<Comment, Error>; fn update_ap_id(conn: &PgConnection, comment_id: i32, apub_id: DbUrl) -> Result<Comment, Error>;
fn permadelete_for_creator( fn permadelete_for_creator(
conn: &PgConnection, conn: &PgConnection,
for_creator_id: i32, for_creator_id: i32,
@ -43,7 +43,7 @@ pub trait Comment_ {
} }
impl Comment_ for Comment { impl Comment_ for Comment {
fn update_ap_id(conn: &PgConnection, comment_id: i32, apub_id: Url) -> Result<Self, Error> { fn update_ap_id(conn: &PgConnection, comment_id: i32, apub_id: DbUrl) -> Result<Self, Error> {
use lemmy_db_schema::schema::comment::dsl::*; use lemmy_db_schema::schema::comment::dsl::*;
diesel::update(comment.find(comment_id)) diesel::update(comment.find(comment_id))
@ -145,7 +145,7 @@ impl Crud<CommentForm> for Comment {
} }
impl ApubObject<CommentForm> for Comment { impl ApubObject<CommentForm> for Comment {
fn read_from_apub_id(conn: &PgConnection, object_id: &Url) -> Result<Self, Error> { fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Self, Error> {
use lemmy_db_schema::schema::comment::dsl::*; use lemmy_db_schema::schema::comment::dsl::*;
comment.filter(ap_id.eq(object_id)).first::<Self>(conn) comment.filter(ap_id.eq(object_id)).first::<Self>(conn)
} }
@ -187,7 +187,7 @@ impl Saveable<CommentSavedForm> for CommentSaved {
use lemmy_db_schema::schema::comment_saved::dsl::*; use lemmy_db_schema::schema::comment_saved::dsl::*;
insert_into(comment_saved) insert_into(comment_saved)
.values(comment_saved_form) .values(comment_saved_form)
.on_conflict((comment_id, user_id)) .on_conflict((comment_id, person_id))
.do_update() .do_update()
.set(comment_saved_form) .set(comment_saved_form)
.get_result::<Self>(conn) .get_result::<Self>(conn)
@ -197,7 +197,7 @@ impl Saveable<CommentSavedForm> for CommentSaved {
diesel::delete( diesel::delete(
comment_saved comment_saved
.filter(comment_id.eq(comment_saved_form.comment_id)) .filter(comment_id.eq(comment_saved_form.comment_id))
.filter(user_id.eq(comment_saved_form.person_id)), .filter(person_id.eq(comment_saved_form.person_id)),
) )
.execute(conn) .execute(conn)
} }
@ -205,15 +205,17 @@ impl Saveable<CommentSavedForm> for CommentSaved {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{establish_unpooled_connection, Crud, Likeable, ListingType, Saveable, SortType}; use crate::{establish_unpooled_connection, Crud, Likeable, Saveable};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
comment::*, comment::*,
community::{Community, CommunityForm}, community::{Community, CommunityForm},
post::*, post::*,
person::{PersonForm, Person}, person::{PersonForm, Person},
}; };
use serial_test::serial;
#[test] #[test]
#[serial]
fn test_crud() { fn test_crud() {
let conn = establish_unpooled_connection(); let conn = establish_unpooled_connection();
@ -222,13 +224,13 @@ mod tests {
preferred_username: None, preferred_username: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: Some(false), banned: None,
deleted: false, deleted: None,
published: None, published: None,
updated: None, updated: None,
actor_id: None, actor_id: None,
bio: None, bio: None,
local: true, local: None,
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: None, last_refreshed_at: None,
@ -236,13 +238,13 @@ mod tests {
shared_inbox_url: None, shared_inbox_url: None,
}; };
let inserted_persod = Person::create(&conn, &new_person).unwrap(); let inserted_person = Person::create(&conn, &new_person).unwrap();
let new_community = CommunityForm { let new_community = CommunityForm {
name: "test community".to_string(), name: "test community".to_string(),
title: "nada".to_owned(), title: "nada".to_owned(),
description: None, description: None,
creator_id: inserted_persod.id, creator_id: inserted_person.id,
removed: None, removed: None,
deleted: None, deleted: None,
updated: None, updated: None,
@ -264,7 +266,7 @@ mod tests {
let new_post = PostForm { let new_post = PostForm {
name: "A test post".into(), name: "A test post".into(),
creator_id: inserted_persod.id, creator_id: inserted_person.id,
url: None, url: None,
body: None, body: None,
community_id: inserted_community.id, community_id: inserted_community.id,
@ -287,7 +289,7 @@ mod tests {
let comment_form = CommentForm { let comment_form = CommentForm {
content: "A test comment".into(), content: "A test comment".into(),
creator_id: inserted_persod.id, creator_id: inserted_person.id,
post_id: inserted_post.id, post_id: inserted_post.id,
removed: None, removed: None,
deleted: None, deleted: None,
@ -304,7 +306,7 @@ mod tests {
let expected_comment = Comment { let expected_comment = Comment {
id: inserted_comment.id, id: inserted_comment.id,
content: "A test comment".into(), content: "A test comment".into(),
creator_id: inserted_persod.id, creator_id: inserted_person.id,
post_id: inserted_post.id, post_id: inserted_post.id,
removed: false, removed: false,
deleted: false, deleted: false,
@ -318,7 +320,7 @@ mod tests {
let child_comment_form = CommentForm { let child_comment_form = CommentForm {
content: "A child comment".into(), content: "A child comment".into(),
creator_id: inserted_persod.id, creator_id: inserted_person.id,
post_id: inserted_post.id, post_id: inserted_post.id,
parent_id: Some(inserted_comment.id), parent_id: Some(inserted_comment.id),
removed: None, removed: None,
@ -336,7 +338,7 @@ mod tests {
let comment_like_form = CommentLikeForm { let comment_like_form = CommentLikeForm {
comment_id: inserted_comment.id, comment_id: inserted_comment.id,
post_id: inserted_post.id, post_id: inserted_post.id,
person_id: inserted_persod.id, person_id: inserted_person.id,
score: 1, score: 1,
}; };
@ -346,7 +348,7 @@ mod tests {
id: inserted_comment_like.id, id: inserted_comment_like.id,
comment_id: inserted_comment.id, comment_id: inserted_comment.id,
post_id: inserted_post.id, post_id: inserted_post.id,
person_id: inserted_persod.id, person_id: inserted_person.id,
published: inserted_comment_like.published, published: inserted_comment_like.published,
score: 1, score: 1,
}; };
@ -354,7 +356,7 @@ mod tests {
// Comment Saved // Comment Saved
let comment_saved_form = CommentSavedForm { let comment_saved_form = CommentSavedForm {
comment_id: inserted_comment.id, comment_id: inserted_comment.id,
person_id: inserted_persod.id, person_id: inserted_person.id,
}; };
let inserted_comment_saved = CommentSaved::save(&conn, &comment_saved_form).unwrap(); let inserted_comment_saved = CommentSaved::save(&conn, &comment_saved_form).unwrap();
@ -362,19 +364,19 @@ mod tests {
let expected_comment_saved = CommentSaved { let expected_comment_saved = CommentSaved {
id: inserted_comment_saved.id, id: inserted_comment_saved.id,
comment_id: inserted_comment.id, comment_id: inserted_comment.id,
person_id: inserted_persod.id, person_id: inserted_person.id,
published: inserted_comment_saved.published, published: inserted_comment_saved.published,
}; };
let read_comment = Comment::read(&conn, inserted_comment.id).unwrap(); let read_comment = Comment::read(&conn, inserted_comment.id).unwrap();
let updated_comment = Comment::update(&conn, inserted_comment.id, &comment_form).unwrap(); let updated_comment = Comment::update(&conn, inserted_comment.id, &comment_form).unwrap();
let like_removed = CommentLike::remove(&conn, inserted_persod.id, inserted_comment.id).unwrap(); let like_removed = CommentLike::remove(&conn, inserted_person.id, inserted_comment.id).unwrap();
let saved_removed = CommentSaved::unsave(&conn, &comment_saved_form).unwrap(); let saved_removed = CommentSaved::unsave(&conn, &comment_saved_form).unwrap();
let num_deleted = Comment::delete(&conn, inserted_comment.id).unwrap(); let num_deleted = Comment::delete(&conn, inserted_comment.id).unwrap();
Comment::delete(&conn, inserted_child_comment.id).unwrap(); Comment::delete(&conn, inserted_child_comment.id).unwrap();
Post::delete(&conn, inserted_post.id).unwrap(); Post::delete(&conn, inserted_post.id).unwrap();
Community::delete(&conn, inserted_community.id).unwrap(); Community::delete(&conn, inserted_community.id).unwrap();
Person::delete(&conn, inserted_persod.id).unwrap(); Person::delete(&conn, inserted_person.id).unwrap();
assert_eq!(expected_comment, read_comment); assert_eq!(expected_comment, read_comment);
assert_eq!(expected_comment, inserted_comment); assert_eq!(expected_comment, inserted_comment);

View file

@ -12,7 +12,7 @@ use lemmy_db_schema::{
CommunityPersonBan, CommunityPersonBan,
CommunityPersonBanForm, CommunityPersonBanForm,
}, },
Url, DbUrl,
}; };
mod safe_type { mod safe_type {
@ -90,7 +90,7 @@ impl Crud<CommunityForm> for Community {
} }
impl ApubObject<CommunityForm> for Community { impl ApubObject<CommunityForm> for Community {
fn read_from_apub_id(conn: &PgConnection, for_actor_id: &Url) -> Result<Self, Error> { fn read_from_apub_id(conn: &PgConnection, for_actor_id: &DbUrl) -> Result<Self, Error> {
use lemmy_db_schema::schema::community::dsl::*; use lemmy_db_schema::schema::community::dsl::*;
community community
.filter(actor_id.eq(for_actor_id)) .filter(actor_id.eq(for_actor_id))
@ -131,7 +131,10 @@ pub trait Community_ {
new_creator_id: i32, new_creator_id: i32,
) -> Result<Community, Error>; ) -> Result<Community, Error>;
fn distinct_federated_communities(conn: &PgConnection) -> Result<Vec<String>, Error>; fn distinct_federated_communities(conn: &PgConnection) -> Result<Vec<String>, Error>;
fn read_from_followers_url(conn: &PgConnection, followers_url: &Url) -> Result<Community, Error>; fn read_from_followers_url(
conn: &PgConnection,
followers_url: &DbUrl,
) -> Result<Community, Error>;
} }
impl Community_ for Community { impl Community_ for Community {
@ -194,7 +197,7 @@ impl Community_ for Community {
fn read_from_followers_url( fn read_from_followers_url(
conn: &PgConnection, conn: &PgConnection,
followers_url_: &Url, followers_url_: &DbUrl,
) -> Result<Community, Error> { ) -> Result<Community, Error> {
use lemmy_db_schema::schema::community::dsl::*; use lemmy_db_schema::schema::community::dsl::*;
community community
@ -287,12 +290,12 @@ impl Followable<CommunityFollowerForm> for CommunityFollower {
use lemmy_db_schema::schema::community_follower::dsl::*; use lemmy_db_schema::schema::community_follower::dsl::*;
insert_into(community_follower) insert_into(community_follower)
.values(community_follower_form) .values(community_follower_form)
.on_conflict((community_id, user_id)) .on_conflict((community_id, person_id))
.do_update() .do_update()
.set(community_follower_form) .set(community_follower_form)
.get_result::<Self>(conn) .get_result::<Self>(conn)
} }
fn follow_accepted(conn: &PgConnection, community_id_: i32, user_id_: i32) -> Result<Self, Error> fn follow_accepted(conn: &PgConnection, community_id_: i32, person_id_: i32) -> Result<Self, Error>
where where
Self: Sized, Self: Sized,
{ {
@ -300,7 +303,7 @@ impl Followable<CommunityFollowerForm> for CommunityFollower {
diesel::update( diesel::update(
community_follower community_follower
.filter(community_id.eq(community_id_)) .filter(community_id.eq(community_id_))
.filter(user_id.eq(user_id_)), .filter(person_id.eq(person_id_)),
) )
.set(pending.eq(true)) .set(pending.eq(true))
.get_result::<Self>(conn) .get_result::<Self>(conn)
@ -313,7 +316,7 @@ impl Followable<CommunityFollowerForm> for CommunityFollower {
diesel::delete( diesel::delete(
community_follower community_follower
.filter(community_id.eq(&community_follower_form.community_id)) .filter(community_id.eq(&community_follower_form.community_id))
.filter(user_id.eq(&community_follower_form.person_id)), .filter(person_id.eq(&community_follower_form.person_id)),
) )
.execute(conn) .execute(conn)
} }
@ -336,12 +339,12 @@ mod tests {
Crud, Crud,
Followable, Followable,
Joinable, Joinable,
ListingType,
SortType,
}; };
use lemmy_db_schema::source::{community::*, person::*}; use lemmy_db_schema::source::{community::*, person::*};
use serial_test::serial;
#[test] #[test]
#[serial]
fn test_crud() { fn test_crud() {
let conn = establish_unpooled_connection(); let conn = establish_unpooled_connection();
@ -350,13 +353,13 @@ mod tests {
preferred_username: None, preferred_username: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: Some(false), banned: None,
deleted: false, deleted: None,
published: None, published: None,
updated: None, updated: None,
actor_id: None, actor_id: None,
bio: None, bio: None,
local: true, local: None,
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: None, last_refreshed_at: None,

View file

@ -1,18 +1,43 @@
use crate::{is_email_regex, ApubObject, Crud, ToSafeSettings}; use crate::{is_email_regex, Crud, ToSafeSettings};
use diesel::{dsl::*, result::Error, *};
use lemmy_db_schema::source::local_user::LocalUserSettings;
use lemmy_db_schema::schema::local_user::dsl::*;
use lemmy_db_schema::source::local_user::{LocalUser, LocalUserForm};
use bcrypt::{hash, DEFAULT_COST};
mod safe_type {
use crate::ToSafe;
use lemmy_db_schema::{schema::local_user::columns::*, source::local_user::LocalUser};
type Columns = (
id,
person_id,
admin,
matrix_user_id,
);
impl ToSafe for LocalUser {
type SafeColumns = Columns;
fn safe_columns_tuple() -> Self::SafeColumns {
(
id,
person_id,
admin,
matrix_user_id,
)
}
}
}
mod safe_settings_type { mod safe_settings_type {
use crate::ToSafeSettings; use crate::ToSafeSettings;
use lemmy_db_schema::{schema::user_::columns::*, source::user::User_}; use lemmy_db_schema::{schema::local_user::columns::*, source::local_user::LocalUser};
type Columns = ( type Columns = (
id, id,
name, person_id,
preferred_username,
email, email,
avatar,
admin, admin,
banned,
published,
updated,
show_nsfw, show_nsfw,
theme, theme,
default_sort_type, default_sort_type,
@ -21,27 +46,18 @@ mod safe_settings_type {
show_avatars, show_avatars,
send_notifications_to_email, send_notifications_to_email,
matrix_user_id, matrix_user_id,
actor_id,
bio,
local,
last_refreshed_at,
banner,
deleted,
); );
impl ToSafeSettings for User_ { impl ToSafeSettings for LocalUser {
type SafeSettingsColumns = Columns; type SafeSettingsColumns = Columns;
/// Includes everything but the hashed password
fn safe_settings_columns_tuple() -> Self::SafeSettingsColumns { fn safe_settings_columns_tuple() -> Self::SafeSettingsColumns {
( (
id, id,
name, person_id,
preferred_username,
email, email,
avatar,
admin, admin,
banned,
published,
updated,
show_nsfw, show_nsfw,
theme, theme,
default_sort_type, default_sort_type,
@ -50,26 +66,93 @@ mod safe_settings_type {
show_avatars, show_avatars,
send_notifications_to_email, send_notifications_to_email,
matrix_user_id, matrix_user_id,
actor_id,
bio,
local,
last_refreshed_at,
banner,
deleted,
) )
} }
} }
} }
pub trait UserSafeSettings_ { pub trait LocalUser_ {
fn read(conn: &PgConnection, user_id: i32) -> Result<UserSafeSettings, Error>; fn register(conn: &PgConnection, form: &LocalUserForm) -> Result<LocalUser, Error>;
fn update_password(conn: &PgConnection, person_id: i32, new_password: &str)
-> Result<LocalUser, Error>;
fn add_admin(conn: &PgConnection, local_user_id: i32, added: bool) -> Result<LocalUser, Error>;
fn find_by_email(conn: &PgConnection, from_email: &str) -> Result<LocalUser, Error>;
fn find_by_person(conn: &PgConnection, from_person_id: i32) -> Result<LocalUser, Error>;
} }
impl UserSafeSettings_ for UserSafeSettings { impl LocalUser_ for LocalUser {
fn register(conn: &PgConnection, form: &LocalUserForm) -> Result<Self, Error> {
let mut edited_user = form.clone();
let password_hash =
hash(&form.password_encrypted, DEFAULT_COST).expect("Couldn't hash password");
edited_user.password_encrypted = password_hash;
Self::create(&conn, &edited_user)
}
// TODO do more individual updates like these
fn update_password(conn: &PgConnection, local_user_id: i32, new_password: &str) -> Result<Self, Error> {
let password_hash = hash(new_password, DEFAULT_COST).expect("Couldn't hash password");
diesel::update(local_user.find(local_user_id))
.set((
password_encrypted.eq(password_hash),
))
.get_result::<Self>(conn)
}
// TODO is this used?
fn add_admin(conn: &PgConnection, local_user_id: i32, added: bool) -> Result<Self, Error> {
diesel::update(local_user.find(local_user_id))
.set(admin.eq(added))
.get_result::<Self>(conn)
}
// TODO is this used?
fn find_by_email(conn: &PgConnection, from_email: &str) -> Result<LocalUser, Error> {
local_user
.filter(email.eq(from_email))
.first::<LocalUser>(conn)
}
// TODO is this used?
fn find_by_person(conn: &PgConnection, for_person_id: i32) -> Result<LocalUser, Error> {
local_user
.filter(person_id.eq(for_person_id))
.first::<LocalUser>(conn)
}
}
impl Crud<LocalUserForm> for LocalUser {
fn read(conn: &PgConnection, local_user_id: i32) -> Result<Self, Error> {
local_user
.find(local_user_id)
.first::<Self>(conn)
}
fn delete(conn: &PgConnection, local_user_id: i32) -> Result<usize, Error> {
diesel::delete(local_user.find(local_user_id)).execute(conn)
}
fn create(conn: &PgConnection, form: &LocalUserForm) -> Result<Self, Error> {
insert_into(local_user).values(form).get_result::<Self>(conn)
}
fn update(conn: &PgConnection, local_user_id: i32, form: &LocalUserForm) -> Result<Self, Error> {
diesel::update(local_user.find(local_user_id))
.set(form)
.get_result::<Self>(conn)
}
}
// TODO is this used?
pub trait LocalUserSettings_ {
fn read(conn: &PgConnection, user_id: i32) -> Result<LocalUserSettings, Error>;
}
// TODO is this used?
impl LocalUserSettings_ for LocalUserSettings {
fn read(conn: &PgConnection, user_id: i32) -> Result<Self, Error> { fn read(conn: &PgConnection, user_id: i32) -> Result<Self, Error> {
user_ local_user
.select(User_::safe_settings_columns_tuple()) .select(LocalUser::safe_settings_columns_tuple())
.filter(deleted.eq(false))
.find(user_id) .find(user_id)
.first::<Self>(conn) .first::<Self>(conn)
} }

View file

@ -197,11 +197,13 @@ impl Crud<ModAddForm> for ModAdd {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{establish_unpooled_connection, Crud, ListingType, SortType}; use crate::{establish_unpooled_connection, Crud};
use lemmy_db_schema::source::{comment::*, community::*, moderator::*, post::*, person::*}; use lemmy_db_schema::source::{comment::*, community::*, moderator::*, post::*, person::*};
use serial_test::serial;
// use Crud; // use Crud;
#[test] #[test]
#[serial]
fn test_crud() { fn test_crud() {
let conn = establish_unpooled_connection(); let conn = establish_unpooled_connection();
@ -210,13 +212,13 @@ mod tests {
preferred_username: None, preferred_username: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: Some(false), banned: None,
deleted: false, deleted: None,
published: None, published: None,
updated: None, updated: None,
actor_id: None, actor_id: None,
bio: None, bio: None,
local: true, local: None,
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: None, last_refreshed_at: None,
@ -231,13 +233,13 @@ mod tests {
preferred_username: None, preferred_username: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: Some(false), banned: None,
deleted: false, deleted: None,
published: None, published: None,
updated: None, updated: None,
actor_id: None, actor_id: None,
bio: None, bio: None,
local: true, local: None,
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: None, last_refreshed_at: None,

View file

@ -76,12 +76,12 @@ mod tests {
establish_unpooled_connection, establish_unpooled_connection,
source::password_reset_request::PasswordResetRequest_, source::password_reset_request::PasswordResetRequest_,
Crud, Crud,
ListingType,
SortType,
}; };
use lemmy_db_schema::source::{password_reset_request::PasswordResetRequest, person::*}; use lemmy_db_schema::source::{password_reset_request::PasswordResetRequest, person::*};
use serial_test::serial;
#[test] #[test]
#[serial]
fn test_crud() { fn test_crud() {
let conn = establish_unpooled_connection(); let conn = establish_unpooled_connection();
@ -90,13 +90,13 @@ mod tests {
preferred_username: None, preferred_username: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: Some(false), banned: None,
deleted: false, deleted: None,
published: None, published: None,
updated: None, updated: None,
actor_id: None, actor_id: None,
bio: None, bio: None,
local: true, local: None,
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: None, last_refreshed_at: None,

View file

@ -1,13 +1,12 @@
use crate::{is_email_regex, ApubObject, Crud, ToSafeSettings}; use crate::{is_email_regex, ApubObject, Crud};
use bcrypt::{hash, DEFAULT_COST};
use diesel::{dsl::*, result::Error, *}; use diesel::{dsl::*, result::Error, *};
use lemmy_db_schema::{ use lemmy_db_schema::{
naive_now, naive_now,
schema::person::dsl::*, schema::person::dsl::*,
source::person::{PersonForm, Person}, source::person::{PersonForm, Person},
Url, DbUrl,
}; };
use lemmy_utils::settings::Settings; use lemmy_utils::settings::structs::Settings;
mod safe_type { mod safe_type {
use crate::ToSafe; use crate::ToSafe;
@ -24,7 +23,6 @@ mod safe_type {
actor_id, actor_id,
bio, bio,
local, local,
last_refreshed_at,
banner, banner,
deleted, deleted,
inbox_url, inbox_url,
@ -45,7 +43,6 @@ mod safe_type {
actor_id, actor_id,
bio, bio,
local, local,
last_refreshed_at,
banner, banner,
deleted, deleted,
inbox_url, inbox_url,
@ -70,7 +67,6 @@ mod safe_type_alias_1 {
actor_id, actor_id,
bio, bio,
local, local,
last_refreshed_at,
banner, banner,
deleted, deleted,
inbox_url, inbox_url,
@ -91,7 +87,6 @@ mod safe_type_alias_1 {
actor_id, actor_id,
bio, bio,
local, local,
last_refreshed_at,
banner, banner,
deleted, deleted,
inbox_url, inbox_url,
@ -116,7 +111,6 @@ mod safe_type_alias_2 {
actor_id, actor_id,
bio, bio,
local, local,
last_refreshed_at,
banner, banner,
deleted, deleted,
inbox_url, inbox_url,
@ -137,7 +131,6 @@ mod safe_type_alias_2 {
actor_id, actor_id,
bio, bio,
local, local,
last_refreshed_at,
banner, banner,
deleted, deleted,
inbox_url, inbox_url,
@ -168,7 +161,7 @@ impl Crud<PersonForm> for Person {
} }
impl ApubObject<PersonForm> for Person { impl ApubObject<PersonForm> for Person {
fn read_from_apub_id(conn: &PgConnection, object_id: &Url) -> Result<Self, Error> { fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Self, Error> {
use lemmy_db_schema::schema::person::dsl::*; use lemmy_db_schema::schema::person::dsl::*;
person person
.filter(deleted.eq(false)) .filter(deleted.eq(false))
@ -187,58 +180,18 @@ impl ApubObject<PersonForm> for Person {
} }
pub trait Person_ { pub trait Person_ {
fn register(conn: &PgConnection, form: &PersonForm) -> Result<Person, Error>;
fn update_password(conn: &PgConnection, person_id: i32, new_password: &str)
-> Result<Person, Error>;
fn read_from_name(conn: &PgConnection, from_name: &str) -> Result<Person, Error>;
fn add_admin(conn: &PgConnection, person_id: i32, added: bool) -> Result<Person, Error>;
fn ban_person(conn: &PgConnection, person_id: i32, ban: bool) -> Result<Person, Error>; fn ban_person(conn: &PgConnection, person_id: i32, ban: bool) -> Result<Person, Error>;
fn find_by_email_or_name( // TODO
conn: &PgConnection, // fn find_by_email_or_name(
name_or_email: &str, // conn: &PgConnection,
) -> Result<Person, Error>; // name_or_email: &str,
// ) -> Result<Person, Error>;
fn find_by_name(conn: &PgConnection, name: &str) -> Result<Person, Error>; fn find_by_name(conn: &PgConnection, name: &str) -> Result<Person, Error>;
fn find_by_email(conn: &PgConnection, from_email: &str) -> Result<Person, Error>;
fn get_profile_url(&self, hostname: &str) -> String;
fn mark_as_updated(conn: &PgConnection, person_id: i32) -> Result<Person, Error>; fn mark_as_updated(conn: &PgConnection, person_id: i32) -> Result<Person, Error>;
fn delete_account(conn: &PgConnection, person_id: i32) -> Result<Person, Error>; fn delete_account(conn: &PgConnection, person_id: i32) -> Result<Person, Error>;
} }
impl Person_ for Person { impl Person_ for Person {
fn register(conn: &PgConnection, form: &PersonForm) -> Result<Self, Error> {
let mut edited_person = form.clone();
let password_hash =
hash(&form.password_encrypted, DEFAULT_COST).expect("Couldn't hash password");
edited_person.password_encrypted = password_hash;
Self::create(&conn, &edited_person)
}
// TODO do more individual updates like these
fn update_password(conn: &PgConnection, person_id: i32, new_password: &str) -> Result<Self, Error> {
let password_hash = hash(new_password, DEFAULT_COST).expect("Couldn't hash password");
diesel::update(person.find(person_id))
.set((
password_encrypted.eq(password_hash),
updated.eq(naive_now()),
))
.get_result::<Self>(conn)
}
fn read_from_name(conn: &PgConnection, from_name: &str) -> Result<Self, Error> {
person
.filter(local.eq(true))
.filter(deleted.eq(false))
.filter(name.eq(from_name))
.first::<Self>(conn)
}
fn add_admin(conn: &PgConnection, person_id: i32, added: bool) -> Result<Self, Error> {
diesel::update(person.find(person_id))
.set(admin.eq(added))
.get_result::<Self>(conn)
}
fn ban_person(conn: &PgConnection, person_id: i32, ban: bool) -> Result<Self, Error> { fn ban_person(conn: &PgConnection, person_id: i32, ban: bool) -> Result<Self, Error> {
diesel::update(person.find(person_id)) diesel::update(person.find(person_id))
@ -246,42 +199,26 @@ impl Person_ for Person {
.get_result::<Self>(conn) .get_result::<Self>(conn)
} }
fn find_by_email_or_name( // TODO this needs to get moved to aggregates i think
conn: &PgConnection, // fn find_by_email_or_name(
name_or_email: &str, // conn: &PgConnection,
) -> Result<Self, Error> { // name_or_email: &str,
if is_email_regex(name_or_email) { // ) -> Result<Self, Error> {
Self::find_by_email(conn, name_or_email) // if is_email_regex(name_or_email) {
} else { // Self::find_by_email(conn, name_or_email)
Self::find_by_name(conn, name_or_email) // } else {
} // Self::find_by_name(conn, name_or_email)
} // }
// }
fn find_by_name(conn: &PgConnection, name: &str) -> Result<Person, Error> { fn find_by_name(conn: &PgConnection, from_name: &str) -> Result<Person, Error> {
person person
.filter(deleted.eq(false)) .filter(deleted.eq(false))
.filter(local.eq(true)) .filter(local.eq(true))
.filter(name.ilike(name)) .filter(name.ilike(from_name))
.first::<Person>(conn) .first::<Person>(conn)
} }
fn find_by_email(conn: &PgConnection, from_email: &str) -> Result<Person, Error> {
person
.filter(deleted.eq(false))
.filter(local.eq(true))
.filter(email.eq(from_email))
.first::<Person>(conn)
}
fn get_profile_url(&self, hostname: &str) -> String {
format!(
"{}://{}/u/{}",
Settings::get().get_protocol_string(),
hostname,
self.name
)
}
fn mark_as_updated(conn: &PgConnection, person_id: i32) -> Result<Person, Error> { fn mark_as_updated(conn: &PgConnection, person_id: i32) -> Result<Person, Error> {
diesel::update(person.find(person_id)) diesel::update(person.find(person_id))
.set((last_refreshed_at.eq(naive_now()),)) .set((last_refreshed_at.eq(naive_now()),))
@ -289,11 +226,19 @@ impl Person_ for Person {
} }
fn delete_account(conn: &PgConnection, person_id: i32) -> Result<Person, Error> { fn delete_account(conn: &PgConnection, person_id: i32) -> Result<Person, Error> {
use lemmy_db_schema::schema::local_user;
// Set the local user info to none
diesel::update(local_user::table.filter(local_user::person_id.eq(person_id)))
.set((
local_user::email.eq::<Option<String>>(None),
local_user::matrix_user_id.eq::<Option<String>>(None),
))
.execute(conn)?;
diesel::update(person.find(person_id)) diesel::update(person.find(person_id))
.set(( .set((
preferred_username.eq::<Option<String>>(None), preferred_username.eq::<Option<String>>(None),
email.eq::<Option<String>>(None),
matrix_user_id.eq::<Option<String>>(None),
bio.eq::<Option<String>>(None), bio.eq::<Option<String>>(None),
deleted.eq(true), deleted.eq(true),
updated.eq(naive_now()), updated.eq(naive_now()),
@ -304,7 +249,7 @@ impl Person_ for Person {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{establish_unpooled_connection, source::person::*, ListingType, SortType}; use crate::{establish_unpooled_connection, source::person::*};
#[test] #[test]
fn test_crud() { fn test_crud() {
@ -315,13 +260,13 @@ mod tests {
preferred_username: None, preferred_username: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: Some(false), banned: None,
deleted: false, deleted: None,
published: None, published: None,
updated: None, updated: None,
actor_id: None, actor_id: None,
bio: None, bio: None,
local: true, local: None,
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: None, last_refreshed_at: None,
@ -347,7 +292,6 @@ mod tests {
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: inserted_person.published, last_refreshed_at: inserted_person.published,
deleted: false,
inbox_url: inserted_person.inbox_url.to_owned(), inbox_url: inserted_person.inbox_url.to_owned(),
shared_inbox_url: None, shared_inbox_url: None,
}; };

View file

@ -73,7 +73,7 @@ impl PersonMention_ for PersonMention {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{establish_unpooled_connection, Crud, ListingType, SortType}; use crate::{establish_unpooled_connection, Crud};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
comment::*, comment::*,
community::{Community, CommunityForm}, community::{Community, CommunityForm},
@ -81,8 +81,10 @@ mod tests {
person::*, person::*,
person_mention::*, person_mention::*,
}; };
use serial_test::serial;
#[test] #[test]
#[serial]
fn test_crud() { fn test_crud() {
let conn = establish_unpooled_connection(); let conn = establish_unpooled_connection();
@ -91,13 +93,13 @@ mod tests {
preferred_username: None, preferred_username: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: Some(false), banned: None,
deleted: false, deleted: None,
published: None, published: None,
updated: None, updated: None,
actor_id: None, actor_id: None,
bio: None, bio: None,
local: true, local: None,
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: None, last_refreshed_at: None,
@ -112,13 +114,13 @@ mod tests {
preferred_username: None, preferred_username: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: Some(false), banned: None,
deleted: false, deleted: None,
published: None, published: None,
updated: None, updated: None,
actor_id: None, actor_id: None,
bio: None, bio: None,
local: true, local: None,
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: None, last_refreshed_at: None,

View file

@ -12,7 +12,7 @@ use lemmy_db_schema::{
PostSaved, PostSaved,
PostSavedForm, PostSavedForm,
}, },
Url, DbUrl,
}; };
impl Crud<PostForm> for Post { impl Crud<PostForm> for Post {
@ -42,7 +42,7 @@ impl Crud<PostForm> for Post {
pub trait Post_ { pub trait Post_ {
//fn read(conn: &PgConnection, post_id: i32) -> Result<Post, Error>; //fn read(conn: &PgConnection, post_id: i32) -> Result<Post, Error>;
fn list_for_community(conn: &PgConnection, the_community_id: i32) -> Result<Vec<Post>, Error>; fn list_for_community(conn: &PgConnection, the_community_id: i32) -> Result<Vec<Post>, Error>;
fn update_ap_id(conn: &PgConnection, post_id: i32, apub_id: Url) -> Result<Post, Error>; fn update_ap_id(conn: &PgConnection, post_id: i32, apub_id: DbUrl) -> Result<Post, Error>;
fn permadelete_for_creator(conn: &PgConnection, for_creator_id: i32) -> Result<Vec<Post>, Error>; fn permadelete_for_creator(conn: &PgConnection, for_creator_id: i32) -> Result<Vec<Post>, Error>;
fn update_deleted(conn: &PgConnection, post_id: i32, new_deleted: bool) -> Result<Post, Error>; fn update_deleted(conn: &PgConnection, post_id: i32, new_deleted: bool) -> Result<Post, Error>;
fn update_removed(conn: &PgConnection, post_id: i32, new_removed: bool) -> Result<Post, Error>; fn update_removed(conn: &PgConnection, post_id: i32, new_removed: bool) -> Result<Post, Error>;
@ -68,7 +68,7 @@ impl Post_ for Post {
.load::<Self>(conn) .load::<Self>(conn)
} }
fn update_ap_id(conn: &PgConnection, post_id: i32, apub_id: Url) -> Result<Self, Error> { fn update_ap_id(conn: &PgConnection, post_id: i32, apub_id: DbUrl) -> Result<Self, Error> {
use lemmy_db_schema::schema::post::dsl::*; use lemmy_db_schema::schema::post::dsl::*;
diesel::update(post.find(post_id)) diesel::update(post.find(post_id))
@ -147,7 +147,7 @@ impl Post_ for Post {
} }
impl ApubObject<PostForm> for Post { impl ApubObject<PostForm> for Post {
fn read_from_apub_id(conn: &PgConnection, object_id: &Url) -> Result<Self, Error> { fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Self, Error> {
use lemmy_db_schema::schema::post::dsl::*; use lemmy_db_schema::schema::post::dsl::*;
post.filter(ap_id.eq(object_id)).first::<Self>(conn) post.filter(ap_id.eq(object_id)).first::<Self>(conn)
} }
@ -226,13 +226,15 @@ impl Readable<PostReadForm> for PostRead {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{establish_unpooled_connection, source::post::*, ListingType, SortType}; use crate::{establish_unpooled_connection, source::post::*};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
community::{Community, CommunityForm}, community::{Community, CommunityForm},
person::*, person::*,
}; };
use serial_test::serial;
#[test] #[test]
#[serial]
fn test_crud() { fn test_crud() {
let conn = establish_unpooled_connection(); let conn = establish_unpooled_connection();
@ -241,13 +243,13 @@ mod tests {
preferred_username: None, preferred_username: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: Some(false), banned: None,
deleted: false, deleted: None,
published: None, published: None,
updated: None, updated: None,
actor_id: None, actor_id: None,
bio: None, bio: None,
local: true, local: None,
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: None, last_refreshed_at: None,

View file

@ -1,6 +1,6 @@
use crate::{ApubObject, Crud}; use crate::{ApubObject, Crud};
use diesel::{dsl::*, result::Error, *}; use diesel::{dsl::*, result::Error, *};
use lemmy_db_schema::{naive_now, source::private_message::*, Url}; use lemmy_db_schema::{naive_now, source::private_message::*, DbUrl};
impl Crud<PrivateMessageForm> for PrivateMessage { impl Crud<PrivateMessageForm> for PrivateMessage {
fn read(conn: &PgConnection, private_message_id: i32) -> Result<Self, Error> { fn read(conn: &PgConnection, private_message_id: i32) -> Result<Self, Error> {
@ -28,7 +28,7 @@ impl Crud<PrivateMessageForm> for PrivateMessage {
} }
impl ApubObject<PrivateMessageForm> for PrivateMessage { impl ApubObject<PrivateMessageForm> for PrivateMessage {
fn read_from_apub_id(conn: &PgConnection, object_id: &Url) -> Result<Self, Error> fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Self, Error>
where where
Self: Sized, Self: Sized,
{ {
@ -53,7 +53,7 @@ pub trait PrivateMessage_ {
fn update_ap_id( fn update_ap_id(
conn: &PgConnection, conn: &PgConnection,
private_message_id: i32, private_message_id: i32,
apub_id: Url, apub_id: DbUrl,
) -> Result<PrivateMessage, Error>; ) -> Result<PrivateMessage, Error>;
fn update_content( fn update_content(
conn: &PgConnection, conn: &PgConnection,
@ -80,7 +80,7 @@ impl PrivateMessage_ for PrivateMessage {
fn update_ap_id( fn update_ap_id(
conn: &PgConnection, conn: &PgConnection,
private_message_id: i32, private_message_id: i32,
apub_id: Url, apub_id: DbUrl,
) -> Result<PrivateMessage, Error> { ) -> Result<PrivateMessage, Error> {
use lemmy_db_schema::schema::private_message::dsl::*; use lemmy_db_schema::schema::private_message::dsl::*;
@ -143,12 +143,12 @@ mod tests {
establish_unpooled_connection, establish_unpooled_connection,
source::private_message::PrivateMessage_, source::private_message::PrivateMessage_,
Crud, Crud,
ListingType,
SortType,
}; };
use lemmy_db_schema::source::{private_message::*, person::*}; use lemmy_db_schema::source::{private_message::*, person::*};
use serial_test::serial;
#[test] #[test]
#[serial]
fn test_crud() { fn test_crud() {
let conn = establish_unpooled_connection(); let conn = establish_unpooled_connection();
@ -157,13 +157,13 @@ mod tests {
preferred_username: None, preferred_username: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: Some(false), banned: None,
deleted: false, deleted: None,
published: None, published: None,
updated: None, updated: None,
actor_id: None, actor_id: None,
bio: None, bio: None,
local: true, local: None,
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: None, last_refreshed_at: None,
@ -178,13 +178,13 @@ mod tests {
preferred_username: None, preferred_username: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: Some(false), banned: None,
deleted: false, deleted: None,
published: None, published: None,
updated: None, updated: None,
actor_id: None, actor_id: None,
bio: None, bio: None,
local: true, local: None,
private_key: None, private_key: None,
public_key: None, public_key: None,
last_refreshed_at: None, last_refreshed_at: None,

View file

@ -0,0 +1,459 @@
use crate::{is_email_regex, ApubObject, Crud, ToSafeSettings};
use bcrypt::{hash, DEFAULT_COST};
use diesel::{dsl::*, result::Error, *};
use lemmy_db_schema::{
naive_now,
schema::user_::dsl::*,
source::user::{UserForm, UserSafeSettings, User_},
DbUrl,
};
use lemmy_utils::settings::structs::Settings;
mod safe_type {
use crate::ToSafe;
use lemmy_db_schema::{schema::user_::columns::*, source::user::User_};
type Columns = (
id,
name,
preferred_username,
avatar,
admin,
banned,
published,
updated,
matrix_user_id,
actor_id,
bio,
local,
banner,
deleted,
inbox_url,
shared_inbox_url,
);
impl ToSafe for User_ {
type SafeColumns = Columns;
fn safe_columns_tuple() -> Self::SafeColumns {
(
id,
name,
preferred_username,
avatar,
admin,
banned,
published,
updated,
matrix_user_id,
actor_id,
bio,
local,
banner,
deleted,
inbox_url,
shared_inbox_url,
)
}
}
}
mod safe_type_alias_1 {
use crate::ToSafe;
use lemmy_db_schema::{schema::user_alias_1::columns::*, source::user::UserAlias1};
type Columns = (
id,
name,
preferred_username,
avatar,
admin,
banned,
published,
updated,
matrix_user_id,
actor_id,
bio,
local,
banner,
deleted,
);
impl ToSafe for UserAlias1 {
type SafeColumns = Columns;
fn safe_columns_tuple() -> Self::SafeColumns {
(
id,
name,
preferred_username,
avatar,
admin,
banned,
published,
updated,
matrix_user_id,
actor_id,
bio,
local,
banner,
deleted,
)
}
}
}
mod safe_type_alias_2 {
use crate::ToSafe;
use lemmy_db_schema::{schema::user_alias_2::columns::*, source::user::UserAlias2};
type Columns = (
id,
name,
preferred_username,
avatar,
admin,
banned,
published,
updated,
matrix_user_id,
actor_id,
bio,
local,
banner,
deleted,
);
impl ToSafe for UserAlias2 {
type SafeColumns = Columns;
fn safe_columns_tuple() -> Self::SafeColumns {
(
id,
name,
preferred_username,
avatar,
admin,
banned,
published,
updated,
matrix_user_id,
actor_id,
bio,
local,
banner,
deleted,
)
}
}
}
mod safe_settings_type {
use crate::ToSafeSettings;
use lemmy_db_schema::{schema::user_::columns::*, source::user::User_};
type Columns = (
id,
name,
preferred_username,
email,
avatar,
admin,
banned,
published,
updated,
show_nsfw,
theme,
default_sort_type,
default_listing_type,
lang,
show_avatars,
send_notifications_to_email,
matrix_user_id,
actor_id,
bio,
local,
last_refreshed_at,
banner,
deleted,
);
impl ToSafeSettings for User_ {
type SafeSettingsColumns = Columns;
fn safe_settings_columns_tuple() -> Self::SafeSettingsColumns {
(
id,
name,
preferred_username,
email,
avatar,
admin,
banned,
published,
updated,
show_nsfw,
theme,
default_sort_type,
default_listing_type,
lang,
show_avatars,
send_notifications_to_email,
matrix_user_id,
actor_id,
bio,
local,
last_refreshed_at,
banner,
deleted,
)
}
}
}
pub trait UserSafeSettings_ {
fn read(conn: &PgConnection, user_id: i32) -> Result<UserSafeSettings, Error>;
}
impl UserSafeSettings_ for UserSafeSettings {
fn read(conn: &PgConnection, user_id: i32) -> Result<Self, Error> {
user_
.select(User_::safe_settings_columns_tuple())
.filter(deleted.eq(false))
.find(user_id)
.first::<Self>(conn)
}
}
impl Crud<UserForm> for User_ {
fn read(conn: &PgConnection, user_id: i32) -> Result<Self, Error> {
user_
.filter(deleted.eq(false))
.find(user_id)
.first::<Self>(conn)
}
fn delete(conn: &PgConnection, user_id: i32) -> Result<usize, Error> {
diesel::delete(user_.find(user_id)).execute(conn)
}
fn create(conn: &PgConnection, form: &UserForm) -> Result<Self, Error> {
insert_into(user_).values(form).get_result::<Self>(conn)
}
fn update(conn: &PgConnection, user_id: i32, form: &UserForm) -> Result<Self, Error> {
diesel::update(user_.find(user_id))
.set(form)
.get_result::<Self>(conn)
}
}
impl ApubObject<UserForm> for User_ {
fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Self, Error> {
use lemmy_db_schema::schema::user_::dsl::*;
user_
.filter(deleted.eq(false))
.filter(actor_id.eq(object_id))
.first::<Self>(conn)
}
fn upsert(conn: &PgConnection, user_form: &UserForm) -> Result<User_, Error> {
insert_into(user_)
.values(user_form)
.on_conflict(actor_id)
.do_update()
.set(user_form)
.get_result::<Self>(conn)
}
}
pub trait User {
fn register(conn: &PgConnection, form: &UserForm) -> Result<User_, Error>;
fn update_password(conn: &PgConnection, user_id: i32, new_password: &str)
-> Result<User_, Error>;
fn read_from_name(conn: &PgConnection, from_user_name: &str) -> Result<User_, Error>;
fn add_admin(conn: &PgConnection, user_id: i32, added: bool) -> Result<User_, Error>;
fn ban_user(conn: &PgConnection, user_id: i32, ban: bool) -> Result<User_, Error>;
fn find_by_email_or_username(
conn: &PgConnection,
username_or_email: &str,
) -> Result<User_, Error>;
fn find_by_username(conn: &PgConnection, username: &str) -> Result<User_, Error>;
fn get_profile_url(&self, hostname: &str) -> String;
fn mark_as_updated(conn: &PgConnection, user_id: i32) -> Result<User_, Error>;
fn delete_account(conn: &PgConnection, user_id: i32) -> Result<User_, Error>;
}
impl User for User_ {
fn register(conn: &PgConnection, form: &UserForm) -> Result<Self, Error> {
let mut edited_user = form.clone();
let password_hash =
hash(&form.password_encrypted, DEFAULT_COST).expect("Couldn't hash password");
edited_user.password_encrypted = password_hash;
Self::create(&conn, &edited_user)
}
// TODO do more individual updates like these
fn update_password(conn: &PgConnection, user_id: i32, new_password: &str) -> Result<Self, Error> {
let password_hash = hash(new_password, DEFAULT_COST).expect("Couldn't hash password");
diesel::update(user_.find(user_id))
.set((
password_encrypted.eq(password_hash),
updated.eq(naive_now()),
))
.get_result::<Self>(conn)
}
fn read_from_name(conn: &PgConnection, from_user_name: &str) -> Result<Self, Error> {
user_
.filter(local.eq(true))
.filter(deleted.eq(false))
.filter(name.eq(from_user_name))
.first::<Self>(conn)
}
fn add_admin(conn: &PgConnection, user_id: i32, added: bool) -> Result<Self, Error> {
diesel::update(user_.find(user_id))
.set(admin.eq(added))
.get_result::<Self>(conn)
}
fn ban_user(conn: &PgConnection, user_id: i32, ban: bool) -> Result<Self, Error> {
diesel::update(user_.find(user_id))
.set(banned.eq(ban))
.get_result::<Self>(conn)
}
fn find_by_email_or_username(
conn: &PgConnection,
username_or_email: &str,
) -> Result<Self, Error> {
if is_email_regex(username_or_email) {
Self::find_by_email(conn, username_or_email)
} else {
Self::find_by_username(conn, username_or_email)
}
}
fn find_by_username(conn: &PgConnection, username: &str) -> Result<User_, Error> {
user_
.filter(deleted.eq(false))
.filter(local.eq(true))
.filter(name.ilike(username))
.first::<User_>(conn)
}
fn find_by_email(conn: &PgConnection, from_email: &str) -> Result<User_, Error> {
user_
.filter(deleted.eq(false))
.filter(local.eq(true))
.filter(email.eq(from_email))
.first::<User_>(conn)
}
fn get_profile_url(&self, hostname: &str) -> String {
format!(
"{}://{}/u/{}",
Settings::get().get_protocol_string(),
hostname,
self.name
)
}
fn mark_as_updated(conn: &PgConnection, user_id: i32) -> Result<User_, Error> {
diesel::update(user_.find(user_id))
.set((last_refreshed_at.eq(naive_now()),))
.get_result::<Self>(conn)
}
fn delete_account(conn: &PgConnection, user_id: i32) -> Result<User_, Error> {
diesel::update(user_.find(user_id))
.set((
preferred_username.eq::<Option<String>>(None),
email.eq::<Option<String>>(None),
matrix_user_id.eq::<Option<String>>(None),
bio.eq::<Option<String>>(None),
deleted.eq(true),
updated.eq(naive_now()),
))
.get_result::<Self>(conn)
}
}
#[cfg(test)]
mod tests {
use crate::{establish_unpooled_connection, source::user::*, ListingType, SortType};
use serial_test::serial;
#[test]
#[serial]
fn test_crud() {
let conn = establish_unpooled_connection();
let new_user = UserForm {
name: "thommy".into(),
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
matrix_user_id: None,
avatar: None,
banner: None,
admin: false,
banned: Some(false),
published: None,
updated: None,
show_nsfw: false,
theme: "browser".into(),
default_sort_type: SortType::Hot as i16,
default_listing_type: ListingType::Subscribed as i16,
lang: "browser".into(),
show_avatars: true,
send_notifications_to_email: false,
actor_id: None,
bio: None,
local: true,
private_key: None,
public_key: None,
last_refreshed_at: None,
inbox_url: None,
shared_inbox_url: None,
};
let inserted_user = User_::create(&conn, &new_user).unwrap();
let expected_user = User_ {
id: inserted_user.id,
name: "thommy".into(),
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
matrix_user_id: None,
avatar: None,
banner: None,
admin: false,
banned: false,
published: inserted_user.published,
updated: None,
show_nsfw: false,
theme: "browser".into(),
default_sort_type: SortType::Hot as i16,
default_listing_type: ListingType::Subscribed as i16,
lang: "browser".into(),
show_avatars: true,
send_notifications_to_email: false,
actor_id: inserted_user.actor_id.to_owned(),
bio: None,
local: true,
private_key: None,
public_key: None,
last_refreshed_at: inserted_user.published,
deleted: false,
inbox_url: inserted_user.inbox_url.to_owned(),
shared_inbox_url: None,
};
let read_user = User_::read(&conn, inserted_user.id).unwrap();
let updated_user = User_::update(&conn, inserted_user.id, &new_user).unwrap();
let num_deleted = User_::delete(&conn, inserted_user.id).unwrap();
assert_eq!(expected_user, read_user);
assert_eq!(expected_user, inserted_user);
assert_eq!(expected_user, updated_user);
assert_eq!(1, num_deleted);
}
}

View file

@ -3,10 +3,13 @@ name = "lemmy_db_schema"
version = "0.1.0" version = "0.1.0"
edition = "2018" edition = "2018"
[lib]
doctest = false
[dependencies] [dependencies]
diesel = { version = "1.4.5", features = ["postgres","chrono","r2d2","serde_json"] } diesel = { version = "1.4.5", features = ["postgres","chrono","r2d2","serde_json"] }
chrono = { version = "0.4.19", features = ["serde"] } chrono = { version = "0.4.19", features = ["serde"] }
serde = { version = "1.0.123", features = ["derive"] } serde = { version = "1.0.123", features = ["derive"] }
serde_json = { version = "1.0.61", features = ["preserve_order"] } serde_json = { version = "1.0.61", features = ["preserve_order"] }
log = "0.4.14" log = "0.4.14"
url = { version = "2.2.0", features = ["serde"] } url = { version = "2.2.1", features = ["serde"] }

View file

@ -8,21 +8,22 @@ use diesel::{
serialize::{Output, ToSql}, serialize::{Output, ToSql},
sql_types::Text, sql_types::Text,
}; };
use serde::Serialize; use serde::{Deserialize, Serialize};
use std::{ use std::{
fmt::{Display, Formatter}, fmt::{Display, Formatter},
io::Write, io::Write,
}; };
use url::Url;
pub mod schema; pub mod schema;
pub mod source; pub mod source;
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, PartialEq, Serialize, Debug, AsExpression, FromSqlRow)] #[derive(Clone, PartialEq, Serialize, Deserialize, Debug, AsExpression, FromSqlRow)]
#[sql_type = "Text"] #[sql_type = "Text"]
pub struct Url(url::Url); pub struct DbUrl(Url);
impl<DB: Backend> ToSql<Text, DB> for Url impl<DB: Backend> ToSql<Text, DB> for DbUrl
where where
String: ToSql<Text, DB>, String: ToSql<Text, DB>,
{ {
@ -31,37 +32,37 @@ where
} }
} }
impl<DB: Backend> FromSql<Text, DB> for Url impl<DB: Backend> FromSql<Text, DB> for DbUrl
where where
String: FromSql<Text, DB>, String: FromSql<Text, DB>,
{ {
fn from_sql(bytes: Option<&DB::RawValue>) -> diesel::deserialize::Result<Self> { fn from_sql(bytes: Option<&DB::RawValue>) -> diesel::deserialize::Result<Self> {
let str = String::from_sql(bytes)?; let str = String::from_sql(bytes)?;
Ok(Url(url::Url::parse(&str)?)) Ok(DbUrl(Url::parse(&str)?))
} }
} }
impl Url { impl DbUrl {
pub fn into_inner(self) -> url::Url { pub fn into_inner(self) -> Url {
self.0 self.0
} }
} }
impl Display for Url { impl Display for DbUrl {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.to_owned().into_inner().fmt(f) self.to_owned().into_inner().fmt(f)
} }
} }
impl From<Url> for url::Url { impl From<DbUrl> for Url {
fn from(url: Url) -> Self { fn from(url: DbUrl) -> Self {
url.0 url.0
} }
} }
impl From<url::Url> for Url { impl From<Url> for DbUrl {
fn from(url: url::Url) -> Self { fn from(url: Url) -> Self {
Url(url) DbUrl(url)
} }
} }

View file

@ -89,8 +89,8 @@ table! {
private_key -> Nullable<Text>, private_key -> Nullable<Text>,
public_key -> Nullable<Text>, public_key -> Nullable<Text>,
last_refreshed_at -> Timestamp, last_refreshed_at -> Timestamp,
icon -> Nullable<Text>, icon -> Nullable<Varchar>,
banner -> Nullable<Text>, banner -> Nullable<Varchar>,
followers_url -> Varchar, followers_url -> Varchar,
inbox_url -> Varchar, inbox_url -> Varchar,
shared_inbox_url -> Nullable<Varchar>, shared_inbox_url -> Nullable<Varchar>,
@ -272,7 +272,7 @@ table! {
id -> Int4, id -> Int4,
name -> Varchar, name -> Varchar,
preferred_username -> Nullable<Varchar>, preferred_username -> Nullable<Varchar>,
avatar -> Nullable<Text>, avatar -> Nullable<Varchar>,
banned -> Bool, banned -> Bool,
published -> Timestamp, published -> Timestamp,
updated -> Nullable<Timestamp>, updated -> Nullable<Timestamp>,
@ -282,7 +282,7 @@ table! {
private_key -> Nullable<Text>, private_key -> Nullable<Text>,
public_key -> Nullable<Text>, public_key -> Nullable<Text>,
last_refreshed_at -> Timestamp, last_refreshed_at -> Timestamp,
banner -> Nullable<Text>, banner -> Nullable<Varchar>,
deleted -> Bool, deleted -> Bool,
inbox_url -> Varchar, inbox_url -> Varchar,
shared_inbox_url -> Nullable<Varchar>, shared_inbox_url -> Nullable<Varchar>,
@ -322,7 +322,7 @@ table! {
post (id) { post (id) {
id -> Int4, id -> Int4,
name -> Varchar, name -> Varchar,
url -> Nullable<Text>, url -> Nullable<Varchar>,
body -> Nullable<Text>, body -> Nullable<Text>,
creator_id -> Int4, creator_id -> Int4,
community_id -> Int4, community_id -> Int4,
@ -427,8 +427,8 @@ table! {
enable_downvotes -> Bool, enable_downvotes -> Bool,
open_registration -> Bool, open_registration -> Bool,
enable_nsfw -> Bool, enable_nsfw -> Bool,
icon -> Nullable<Text>, icon -> Nullable<Varchar>,
banner -> Nullable<Text>, banner -> Nullable<Varchar>,
} }
} }
@ -470,7 +470,7 @@ table! {
id -> Int4, id -> Int4,
name -> Varchar, name -> Varchar,
preferred_username -> Nullable<Varchar>, preferred_username -> Nullable<Varchar>,
avatar -> Nullable<Text>, avatar -> Nullable<Varchar>,
banned -> Bool, banned -> Bool,
published -> Timestamp, published -> Timestamp,
updated -> Nullable<Timestamp>, updated -> Nullable<Timestamp>,
@ -480,7 +480,7 @@ table! {
private_key -> Nullable<Text>, private_key -> Nullable<Text>,
public_key -> Nullable<Text>, public_key -> Nullable<Text>,
last_refreshed_at -> Timestamp, last_refreshed_at -> Timestamp,
banner -> Nullable<Text>, banner -> Nullable<Varchar>,
deleted -> Bool, deleted -> Bool,
inbox_url -> Varchar, inbox_url -> Varchar,
shared_inbox_url -> Nullable<Varchar>, shared_inbox_url -> Nullable<Varchar>,
@ -492,7 +492,7 @@ table! {
id -> Int4, id -> Int4,
name -> Varchar, name -> Varchar,
preferred_username -> Nullable<Varchar>, preferred_username -> Nullable<Varchar>,
avatar -> Nullable<Text>, avatar -> Nullable<Varchar>,
banned -> Bool, banned -> Bool,
published -> Timestamp, published -> Timestamp,
updated -> Nullable<Timestamp>, updated -> Nullable<Timestamp>,
@ -502,7 +502,7 @@ table! {
private_key -> Nullable<Text>, private_key -> Nullable<Text>,
public_key -> Nullable<Text>, public_key -> Nullable<Text>,
last_refreshed_at -> Timestamp, last_refreshed_at -> Timestamp,
banner -> Nullable<Text>, banner -> Nullable<Varchar>,
deleted -> Bool, deleted -> Bool,
inbox_url -> Varchar, inbox_url -> Varchar,
shared_inbox_url -> Nullable<Varchar>, shared_inbox_url -> Nullable<Varchar>,

View file

@ -1,4 +1,4 @@
use crate::schema::activity; use crate::{schema::activity, DbUrl};
use serde_json::Value; use serde_json::Value;
use std::fmt::Debug; use std::fmt::Debug;
@ -10,7 +10,7 @@ pub struct Activity {
pub local: bool, pub local: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub ap_id: Option<String>, pub ap_id: Option<DbUrl>,
pub sensitive: Option<bool>, pub sensitive: Option<bool>,
} }
@ -20,6 +20,6 @@ pub struct ActivityForm {
pub data: Value, pub data: Value,
pub local: bool, pub local: bool,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub ap_id: String, pub ap_id: DbUrl,
pub sensitive: bool, pub sensitive: bool,
} }

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
schema::{comment, comment_alias_1, comment_like, comment_saved}, schema::{comment, comment_alias_1, comment_like, comment_saved},
source::post::Post, source::post::Post,
Url, DbUrl,
}; };
use serde::Serialize; use serde::Serialize;
@ -26,7 +26,7 @@ pub struct Comment {
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub deleted: bool, pub deleted: bool,
pub ap_id: Url, pub ap_id: DbUrl,
pub local: bool, pub local: bool,
} }
@ -44,7 +44,7 @@ pub struct CommentAlias1 {
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub deleted: bool, pub deleted: bool,
pub ap_id: Url, pub ap_id: DbUrl,
pub local: bool, pub local: bool,
} }
@ -60,7 +60,7 @@ pub struct CommentForm {
pub published: Option<chrono::NaiveDateTime>, pub published: Option<chrono::NaiveDateTime>,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub deleted: Option<bool>, pub deleted: Option<bool>,
pub ap_id: Option<Url>, pub ap_id: Option<DbUrl>,
pub local: bool, pub local: bool,
} }

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
schema::{community, community_follower, community_moderator, community_person_ban}, schema::{community, community_follower, community_moderator, community_person_ban},
Url, DbUrl,
}; };
use serde::Serialize; use serde::Serialize;
@ -17,16 +17,16 @@ pub struct Community {
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub deleted: bool, pub deleted: bool,
pub nsfw: bool, pub nsfw: bool,
pub actor_id: Url, pub actor_id: DbUrl,
pub local: bool, pub local: bool,
pub private_key: Option<String>, pub private_key: Option<String>,
pub public_key: Option<String>, pub public_key: Option<String>,
pub last_refreshed_at: chrono::NaiveDateTime, pub last_refreshed_at: chrono::NaiveDateTime,
pub icon: Option<String>, pub icon: Option<DbUrl>,
pub banner: Option<String>, pub banner: Option<DbUrl>,
pub followers_url: Url, pub followers_url: DbUrl,
pub inbox_url: Url, pub inbox_url: DbUrl,
pub shared_inbox_url: Option<Url>, pub shared_inbox_url: Option<DbUrl>,
} }
/// A safe representation of community, without the sensitive info /// A safe representation of community, without the sensitive info
@ -43,10 +43,10 @@ pub struct CommunitySafe {
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub deleted: bool, pub deleted: bool,
pub nsfw: bool, pub nsfw: bool,
pub actor_id: Url, pub actor_id: DbUrl,
pub local: bool, pub local: bool,
pub icon: Option<String>, pub icon: Option<DbUrl>,
pub banner: Option<String>, pub banner: Option<DbUrl>,
} }
#[derive(Insertable, AsChangeset, Debug)] #[derive(Insertable, AsChangeset, Debug)]
@ -61,16 +61,16 @@ pub struct CommunityForm {
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub deleted: Option<bool>, pub deleted: Option<bool>,
pub nsfw: bool, pub nsfw: bool,
pub actor_id: Option<Url>, pub actor_id: Option<DbUrl>,
pub local: bool, pub local: bool,
pub private_key: Option<String>, pub private_key: Option<String>,
pub public_key: Option<String>, pub public_key: Option<String>,
pub last_refreshed_at: Option<chrono::NaiveDateTime>, pub last_refreshed_at: Option<chrono::NaiveDateTime>,
pub icon: Option<Option<String>>, pub icon: Option<Option<DbUrl>>,
pub banner: Option<Option<String>>, pub banner: Option<Option<DbUrl>>,
pub followers_url: Option<Url>, pub followers_url: Option<DbUrl>,
pub inbox_url: Option<Url>, pub inbox_url: Option<DbUrl>,
pub shared_inbox_url: Option<Option<Url>>, pub shared_inbox_url: Option<Option<DbUrl>>,
} }
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)] #[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]

View file

@ -37,16 +37,6 @@ pub struct LocalUserForm {
pub matrix_user_id: Option<Option<String>>, pub matrix_user_id: Option<Option<String>>,
} }
/// A safe local user view, without settings, password, or email
#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
#[table_name = "local_user"]
pub struct LocalUserSafe {
pub id: i32,
pub person_id: i32,
pub admin: bool,
pub matrix_user_id: Option<String>,
}
/// A local user view that removes password encrypted /// A local user view that removes password encrypted
#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)] #[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
#[table_name = "local_user"] #[table_name = "local_user"]

View file

@ -4,9 +4,9 @@ use crate::schema::password_reset_request;
#[table_name = "password_reset_request"] #[table_name = "password_reset_request"]
pub struct PasswordResetRequest { pub struct PasswordResetRequest {
pub id: i32, pub id: i32,
pub local_user_id: i32,
pub token_encrypted: String, pub token_encrypted: String,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub local_user_id: i32,
} }
#[derive(Insertable, AsChangeset)] #[derive(Insertable, AsChangeset)]

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
schema::{person, person_alias_1, person_alias_2}, schema::{person, person_alias_1, person_alias_2},
Url, DbUrl,
}; };
use serde::Serialize; use serde::Serialize;
@ -10,20 +10,20 @@ pub struct Person {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
pub preferred_username: Option<String>, pub preferred_username: Option<String>,
pub avatar: Option<String>, pub avatar: Option<DbUrl>,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub actor_id: Url, pub actor_id: DbUrl,
pub bio: Option<String>, pub bio: Option<String>,
pub local: bool, pub local: bool,
pub private_key: Option<String>, pub private_key: Option<String>,
pub public_key: Option<String>, pub public_key: Option<String>,
pub last_refreshed_at: chrono::NaiveDateTime, pub last_refreshed_at: chrono::NaiveDateTime,
pub banner: Option<String>, pub banner: Option<DbUrl>,
pub deleted: bool, pub deleted: bool,
pub inbox_url: Url, pub inbox_url: DbUrl,
pub shared_inbox_url: Option<Url>, pub shared_inbox_url: Option<DbUrl>,
} }
/// A safe representation of user, without the sensitive info /// A safe representation of user, without the sensitive info
@ -33,18 +33,17 @@ pub struct PersonSafe {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
pub preferred_username: Option<String>, pub preferred_username: Option<String>,
pub avatar: Option<String>, pub avatar: Option<DbUrl>,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub actor_id: Url, pub actor_id: DbUrl,
pub bio: Option<String>, pub bio: Option<String>,
pub local: bool, pub local: bool,
pub last_refreshed_at: chrono::NaiveDateTime, pub banner: Option<DbUrl>,
pub banner: Option<String>,
pub deleted: bool, pub deleted: bool,
pub inbox_url: Url, pub inbox_url: DbUrl,
pub shared_inbox_url: Option<Url>, pub shared_inbox_url: Option<DbUrl>,
} }
@ -54,20 +53,20 @@ pub struct PersonAlias1 {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
pub preferred_username: Option<String>, pub preferred_username: Option<String>,
pub avatar: Option<String>, pub avatar: Option<DbUrl>,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub actor_id: Url, pub actor_id: DbUrl,
pub bio: Option<String>, pub bio: Option<String>,
pub local: bool, pub local: bool,
pub private_key: Option<String>, pub private_key: Option<String>,
pub public_key: Option<String>, pub public_key: Option<String>,
pub last_refreshed_at: chrono::NaiveDateTime, pub last_refreshed_at: chrono::NaiveDateTime,
pub banner: Option<String>, pub banner: Option<DbUrl>,
pub deleted: bool, pub deleted: bool,
pub inbox_url: Url, pub inbox_url: DbUrl,
pub shared_inbox_url: Option<Url>, pub shared_inbox_url: Option<DbUrl>,
} }
#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)] #[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
@ -76,18 +75,17 @@ pub struct PersonSafeAlias1 {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
pub preferred_username: Option<String>, pub preferred_username: Option<String>,
pub avatar: Option<String>, pub avatar: Option<DbUrl>,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub actor_id: Url, pub actor_id: DbUrl,
pub bio: Option<String>, pub bio: Option<String>,
pub local: bool, pub local: bool,
pub last_refreshed_at: chrono::NaiveDateTime, pub banner: Option<DbUrl>,
pub banner: Option<String>,
pub deleted: bool, pub deleted: bool,
pub inbox_url: Url, pub inbox_url: DbUrl,
pub shared_inbox_url: Option<Url>, pub shared_inbox_url: Option<DbUrl>,
} }
#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)] #[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
@ -96,20 +94,20 @@ pub struct PersonAlias2 {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
pub preferred_username: Option<String>, pub preferred_username: Option<String>,
pub avatar: Option<String>, pub avatar: Option<DbUrl>,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub actor_id: Url, pub actor_id: DbUrl,
pub bio: Option<String>, pub bio: Option<String>,
pub local: bool, pub local: bool,
pub private_key: Option<String>, pub private_key: Option<String>,
pub public_key: Option<String>, pub public_key: Option<String>,
pub last_refreshed_at: chrono::NaiveDateTime, pub last_refreshed_at: chrono::NaiveDateTime,
pub banner: Option<String>, pub banner: Option<DbUrl>,
pub deleted: bool, pub deleted: bool,
pub inbox_url: Url, pub inbox_url: DbUrl,
pub shared_inbox_url: Option<Url>, pub shared_inbox_url: Option<DbUrl>,
} }
#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)] #[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
@ -118,18 +116,17 @@ pub struct PersonSafeAlias2 {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
pub preferred_username: Option<String>, pub preferred_username: Option<String>,
pub avatar: Option<String>, pub avatar: Option<DbUrl>,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub actor_id: Url, pub actor_id: DbUrl,
pub bio: Option<String>, pub bio: Option<String>,
pub local: bool, pub local: bool,
pub last_refreshed_at: chrono::NaiveDateTime, pub banner: Option<DbUrl>,
pub banner: Option<String>,
pub deleted: bool, pub deleted: bool,
pub inbox_url: Url, pub inbox_url: DbUrl,
pub shared_inbox_url: Option<Url>, pub shared_inbox_url: Option<DbUrl>,
} }
#[derive(Insertable, AsChangeset, Clone)] #[derive(Insertable, AsChangeset, Clone)]
@ -137,18 +134,18 @@ pub struct PersonSafeAlias2 {
pub struct PersonForm { pub struct PersonForm {
pub name: String, pub name: String,
pub preferred_username: Option<Option<String>>, pub preferred_username: Option<Option<String>>,
pub avatar: Option<Option<String>>, pub avatar: Option<Option<DbUrl>>,
pub banned: Option<bool>, pub banned: Option<bool>,
pub published: Option<chrono::NaiveDateTime>, pub published: Option<chrono::NaiveDateTime>,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub actor_id: Option<Url>, pub actor_id: Option<DbUrl>,
pub bio: Option<Option<String>>, pub bio: Option<Option<String>>,
pub local: Option<bool>, pub local: Option<bool>,
pub private_key: Option<Option<String>>, pub private_key: Option<Option<String>>,
pub public_key: Option<Option<String>>, pub public_key: Option<Option<String>>,
pub last_refreshed_at: Option<chrono::NaiveDateTime>, pub last_refreshed_at: Option<chrono::NaiveDateTime>,
pub banner: Option<Option<String>>, pub banner: Option<Option<DbUrl>>,
pub deleted: Option<bool>, pub deleted: Option<bool>,
pub inbox_url: Option<Url>, pub inbox_url: Option<DbUrl>,
pub shared_inbox_url: Option<Option<Url>>, pub shared_inbox_url: Option<Option<DbUrl>>,
} }

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
schema::{post, post_like, post_read, post_saved}, schema::{post, post_like, post_read, post_saved},
Url, DbUrl,
}; };
use serde::Serialize; use serde::Serialize;
@ -9,7 +9,7 @@ use serde::Serialize;
pub struct Post { pub struct Post {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
pub url: Option<String>, pub url: Option<DbUrl>,
pub body: Option<String>, pub body: Option<String>,
pub creator_id: i32, pub creator_id: i32,
pub community_id: i32, pub community_id: i32,
@ -23,8 +23,8 @@ pub struct Post {
pub embed_title: Option<String>, pub embed_title: Option<String>,
pub embed_description: Option<String>, pub embed_description: Option<String>,
pub embed_html: Option<String>, pub embed_html: Option<String>,
pub thumbnail_url: Option<String>, pub thumbnail_url: Option<DbUrl>,
pub ap_id: Url, pub ap_id: DbUrl,
pub local: bool, pub local: bool,
} }
@ -32,7 +32,7 @@ pub struct Post {
#[table_name = "post"] #[table_name = "post"]
pub struct PostForm { pub struct PostForm {
pub name: String, pub name: String,
pub url: Option<String>, pub url: Option<DbUrl>,
pub body: Option<String>, pub body: Option<String>,
pub creator_id: i32, pub creator_id: i32,
pub community_id: i32, pub community_id: i32,
@ -46,8 +46,8 @@ pub struct PostForm {
pub embed_title: Option<String>, pub embed_title: Option<String>,
pub embed_description: Option<String>, pub embed_description: Option<String>,
pub embed_html: Option<String>, pub embed_html: Option<String>,
pub thumbnail_url: Option<String>, pub thumbnail_url: Option<DbUrl>,
pub ap_id: Option<Url>, pub ap_id: Option<DbUrl>,
pub local: bool, pub local: bool,
} }

View file

@ -1,4 +1,4 @@
use crate::{schema::post_report, source::post::Post}; use crate::{schema::post_report, source::post::Post, DbUrl};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive( #[derive(
@ -11,7 +11,7 @@ pub struct PostReport {
pub creator_id: i32, pub creator_id: i32,
pub post_id: i32, pub post_id: i32,
pub original_post_name: String, pub original_post_name: String,
pub original_post_url: Option<String>, pub original_post_url: Option<DbUrl>,
pub original_post_body: Option<String>, pub original_post_body: Option<String>,
pub reason: String, pub reason: String,
pub resolved: bool, pub resolved: bool,
@ -26,7 +26,7 @@ pub struct PostReportForm {
pub creator_id: i32, pub creator_id: i32,
pub post_id: i32, pub post_id: i32,
pub original_post_name: String, pub original_post_name: String,
pub original_post_url: Option<String>, pub original_post_url: Option<DbUrl>,
pub original_post_body: Option<String>, pub original_post_body: Option<String>,
pub reason: String, pub reason: String,
} }

View file

@ -1,4 +1,4 @@
use crate::{schema::private_message, Url}; use crate::{schema::private_message, DbUrl};
use serde::Serialize; use serde::Serialize;
#[derive(Clone, Queryable, Associations, Identifiable, PartialEq, Debug, Serialize)] #[derive(Clone, Queryable, Associations, Identifiable, PartialEq, Debug, Serialize)]
@ -12,7 +12,7 @@ pub struct PrivateMessage {
pub read: bool, pub read: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub ap_id: Url, pub ap_id: DbUrl,
pub local: bool, pub local: bool,
} }
@ -26,6 +26,6 @@ pub struct PrivateMessageForm {
pub read: Option<bool>, pub read: Option<bool>,
pub published: Option<chrono::NaiveDateTime>, pub published: Option<chrono::NaiveDateTime>,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub ap_id: Option<Url>, pub ap_id: Option<DbUrl>,
pub local: bool, pub local: bool,
} }

View file

@ -1,4 +1,4 @@
use crate::schema::site; use crate::{schema::site, DbUrl};
use serde::Serialize; use serde::Serialize;
#[derive(Queryable, Identifiable, PartialEq, Debug, Clone, Serialize)] #[derive(Queryable, Identifiable, PartialEq, Debug, Clone, Serialize)]
@ -13,8 +13,8 @@ pub struct Site {
pub enable_downvotes: bool, pub enable_downvotes: bool,
pub open_registration: bool, pub open_registration: bool,
pub enable_nsfw: bool, pub enable_nsfw: bool,
pub icon: Option<String>, pub icon: Option<DbUrl>,
pub banner: Option<String>, pub banner: Option<DbUrl>,
} }
#[derive(Insertable, AsChangeset)] #[derive(Insertable, AsChangeset)]
@ -28,6 +28,6 @@ pub struct SiteForm {
pub open_registration: bool, pub open_registration: bool,
pub enable_nsfw: bool, pub enable_nsfw: bool,
// when you want to null out a column, you have to send Some(None)), since sending None means you just don't want to update that column. // when you want to null out a column, you have to send Some(None)), since sending None means you just don't want to update that column.
pub icon: Option<Option<String>>, pub icon: Option<Option<DbUrl>>,
pub banner: Option<Option<String>>, pub banner: Option<Option<DbUrl>>,
} }

View file

@ -0,0 +1,220 @@
use crate::{
schema::{user_, user_alias_1, user_alias_2},
DbUrl,
};
use serde::Serialize;
#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
#[table_name = "user_"]
pub struct User_ {
pub id: i32,
pub name: String,
pub preferred_username: Option<String>,
pub password_encrypted: String,
pub email: Option<String>,
pub avatar: Option<DbUrl>,
pub admin: bool,
pub banned: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub show_nsfw: bool,
pub theme: String,
pub default_sort_type: i16,
pub default_listing_type: i16,
pub lang: String,
pub show_avatars: bool,
pub send_notifications_to_email: bool,
pub matrix_user_id: Option<String>,
pub actor_id: DbUrl,
pub bio: Option<String>,
pub local: bool,
pub private_key: Option<String>,
pub public_key: Option<String>,
pub last_refreshed_at: chrono::NaiveDateTime,
pub banner: Option<DbUrl>,
pub deleted: bool,
pub inbox_url: DbUrl,
pub shared_inbox_url: Option<DbUrl>,
}
/// A safe representation of user, without the sensitive info
#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
#[table_name = "user_"]
pub struct UserSafe {
pub id: i32,
pub name: String,
pub preferred_username: Option<String>,
pub avatar: Option<DbUrl>,
pub admin: bool,
pub banned: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub matrix_user_id: Option<String>,
pub actor_id: DbUrl,
pub bio: Option<String>,
pub local: bool,
pub banner: Option<DbUrl>,
pub deleted: bool,
pub inbox_url: DbUrl,
pub shared_inbox_url: Option<DbUrl>,
}
/// A safe user view with only settings
#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
#[table_name = "user_"]
pub struct UserSafeSettings {
pub id: i32,
pub name: String,
pub preferred_username: Option<String>,
pub email: Option<String>,
pub avatar: Option<DbUrl>,
pub admin: bool,
pub banned: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub show_nsfw: bool,
pub theme: String,
pub default_sort_type: i16,
pub default_listing_type: i16,
pub lang: String,
pub show_avatars: bool,
pub send_notifications_to_email: bool,
pub matrix_user_id: Option<String>,
pub actor_id: DbUrl,
pub bio: Option<String>,
pub local: bool,
pub last_refreshed_at: chrono::NaiveDateTime,
pub banner: Option<DbUrl>,
pub deleted: bool,
}
#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
#[table_name = "user_alias_1"]
pub struct UserAlias1 {
pub id: i32,
pub name: String,
pub preferred_username: Option<String>,
pub password_encrypted: String,
pub email: Option<String>,
pub avatar: Option<DbUrl>,
pub admin: bool,
pub banned: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub show_nsfw: bool,
pub theme: String,
pub default_sort_type: i16,
pub default_listing_type: i16,
pub lang: String,
pub show_avatars: bool,
pub send_notifications_to_email: bool,
pub matrix_user_id: Option<String>,
pub actor_id: DbUrl,
pub bio: Option<String>,
pub local: bool,
pub private_key: Option<String>,
pub public_key: Option<String>,
pub last_refreshed_at: chrono::NaiveDateTime,
pub banner: Option<DbUrl>,
pub deleted: bool,
}
#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
#[table_name = "user_alias_1"]
pub struct UserSafeAlias1 {
pub id: i32,
pub name: String,
pub preferred_username: Option<String>,
pub avatar: Option<DbUrl>,
pub admin: bool,
pub banned: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub matrix_user_id: Option<String>,
pub actor_id: DbUrl,
pub bio: Option<String>,
pub local: bool,
pub banner: Option<DbUrl>,
pub deleted: bool,
}
#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
#[table_name = "user_alias_2"]
pub struct UserAlias2 {
pub id: i32,
pub name: String,
pub preferred_username: Option<String>,
pub password_encrypted: String,
pub email: Option<String>,
pub avatar: Option<DbUrl>,
pub admin: bool,
pub banned: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub show_nsfw: bool,
pub theme: String,
pub default_sort_type: i16,
pub default_listing_type: i16,
pub lang: String,
pub show_avatars: bool,
pub send_notifications_to_email: bool,
pub matrix_user_id: Option<String>,
pub actor_id: DbUrl,
pub bio: Option<String>,
pub local: bool,
pub private_key: Option<String>,
pub public_key: Option<String>,
pub last_refreshed_at: chrono::NaiveDateTime,
pub banner: Option<DbUrl>,
pub deleted: bool,
}
#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
#[table_name = "user_alias_2"]
pub struct UserSafeAlias2 {
pub id: i32,
pub name: String,
pub preferred_username: Option<String>,
pub avatar: Option<DbUrl>,
pub admin: bool,
pub banned: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub matrix_user_id: Option<String>,
pub actor_id: DbUrl,
pub bio: Option<String>,
pub local: bool,
pub banner: Option<DbUrl>,
pub deleted: bool,
}
#[derive(Insertable, AsChangeset, Clone)]
#[table_name = "user_"]
pub struct UserForm {
pub name: String,
pub preferred_username: Option<Option<String>>,
pub password_encrypted: String,
pub admin: bool,
pub banned: Option<bool>,
pub email: Option<Option<String>>,
pub avatar: Option<Option<DbUrl>>,
pub published: Option<chrono::NaiveDateTime>,
pub updated: Option<chrono::NaiveDateTime>,
pub show_nsfw: bool,
pub theme: String,
pub default_sort_type: i16,
pub default_listing_type: i16,
pub lang: String,
pub show_avatars: bool,
pub send_notifications_to_email: bool,
pub matrix_user_id: Option<Option<String>>,
pub actor_id: Option<DbUrl>,
pub bio: Option<Option<String>>,
pub local: bool,
pub private_key: Option<String>,
pub public_key: Option<String>,
pub last_refreshed_at: Option<chrono::NaiveDateTime>,
pub banner: Option<Option<DbUrl>>,
pub inbox_url: Option<DbUrl>,
pub shared_inbox_url: Option<Option<DbUrl>>,
}

View file

@ -3,10 +3,16 @@ name = "lemmy_db_views"
version = "0.1.0" version = "0.1.0"
edition = "2018" edition = "2018"
[lib]
doctest = false
[dependencies] [dependencies]
lemmy_db_queries = { path = "../db_queries" } lemmy_db_queries = { path = "../db_queries" }
lemmy_db_schema = { path = "../db_schema" } lemmy_db_schema = { path = "../db_schema" }
diesel = { version = "1.4.5", features = ["postgres","chrono","r2d2","serde_json"] } diesel = { version = "1.4.5", features = ["postgres","chrono","r2d2","serde_json"] }
serde = { version = "1.0.123", features = ["derive"] } serde = { version = "1.0.123", features = ["derive"] }
log = "0.4.14" log = "0.4.14"
url = "2.2.0" url = "2.2.1"
[dev-dependencies]
serial_test = "0.5.1"

View file

@ -1,13 +1,13 @@
use diesel::{result::Error, *}; use diesel::{result::Error, *};
use lemmy_db_queries::{limit_and_offset, MaybeOptional, ToSafe, ViewToVec}; use lemmy_db_queries::{limit_and_offset, MaybeOptional, ToSafe, ViewToVec};
use lemmy_db_schema::{ use lemmy_db_schema::{
schema::{comment, comment_report, community, post, user_, user_alias_1, user_alias_2}, schema::{comment, comment_report, community, post, person, person_alias_1, person_alias_2},
source::{ source::{
comment::Comment, comment::Comment,
comment_report::CommentReport, comment_report::CommentReport,
community::{Community, CommunitySafe}, community::{Community, CommunitySafe},
post::Post, post::Post,
user::{UserAlias1, UserAlias2, UserSafe, UserSafeAlias1, UserSafeAlias2, User_}, person::{PersonAlias1, PersonAlias2, PersonSafe, PersonSafeAlias1, PersonSafeAlias2, Person},
}, },
}; };
use serde::Serialize; use serde::Serialize;
@ -18,9 +18,9 @@ pub struct CommentReportView {
pub comment: Comment, pub comment: Comment,
pub post: Post, pub post: Post,
pub community: CommunitySafe, pub community: CommunitySafe,
pub creator: UserSafe, pub creator: PersonSafe,
pub comment_creator: UserSafeAlias1, pub comment_creator: PersonSafeAlias1,
pub resolver: Option<UserSafeAlias2>, pub resolver: Option<PersonSafeAlias2>,
} }
type CommentReportViewTuple = ( type CommentReportViewTuple = (
@ -28,9 +28,9 @@ type CommentReportViewTuple = (
Comment, Comment,
Post, Post,
CommunitySafe, CommunitySafe,
UserSafe, PersonSafe,
UserSafeAlias1, PersonSafeAlias1,
Option<UserSafeAlias2>, Option<PersonSafeAlias2>,
); );
impl CommentReportView { impl CommentReportView {
@ -44,19 +44,19 @@ impl CommentReportView {
.inner_join(comment::table) .inner_join(comment::table)
.inner_join(post::table.on(comment::post_id.eq(post::id))) .inner_join(post::table.on(comment::post_id.eq(post::id)))
.inner_join(community::table.on(post::community_id.eq(community::id))) .inner_join(community::table.on(post::community_id.eq(community::id)))
.inner_join(user_::table.on(comment_report::creator_id.eq(user_::id))) .inner_join(person::table.on(comment_report::creator_id.eq(person::id)))
.inner_join(user_alias_1::table.on(post::creator_id.eq(user_alias_1::id))) .inner_join(person_alias_1::table.on(post::creator_id.eq(person_alias_1::id)))
.left_join( .left_join(
user_alias_2::table.on(comment_report::resolver_id.eq(user_alias_2::id.nullable())), person_alias_2::table.on(comment_report::resolver_id.eq(person_alias_2::id.nullable())),
) )
.select(( .select((
comment_report::all_columns, comment_report::all_columns,
comment::all_columns, comment::all_columns,
post::all_columns, post::all_columns,
Community::safe_columns_tuple(), Community::safe_columns_tuple(),
User_::safe_columns_tuple(), Person::safe_columns_tuple(),
UserAlias1::safe_columns_tuple(), PersonAlias1::safe_columns_tuple(),
UserAlias2::safe_columns_tuple().nullable(), PersonAlias2::safe_columns_tuple().nullable(),
)) ))
.first::<CommentReportViewTuple>(conn)?; .first::<CommentReportViewTuple>(conn)?;
@ -75,7 +75,7 @@ impl CommentReportView {
/// ///
/// * `community_ids` - a Vec<i32> of community_ids to get a count for /// * `community_ids` - a Vec<i32> of community_ids to get a count for
/// TODO this eq_any is a bad way to do this, would be better to join to communitymoderator /// TODO this eq_any is a bad way to do this, would be better to join to communitymoderator
/// for a user id /// for a person id
pub fn get_report_count(conn: &PgConnection, community_ids: &[i32]) -> Result<i64, Error> { pub fn get_report_count(conn: &PgConnection, community_ids: &[i32]) -> Result<i64, Error> {
use diesel::dsl::*; use diesel::dsl::*;
comment_report::table comment_report::table
@ -135,19 +135,19 @@ impl<'a> CommentReportQueryBuilder<'a> {
.inner_join(comment::table) .inner_join(comment::table)
.inner_join(post::table.on(comment::post_id.eq(post::id))) .inner_join(post::table.on(comment::post_id.eq(post::id)))
.inner_join(community::table.on(post::community_id.eq(community::id))) .inner_join(community::table.on(post::community_id.eq(community::id)))
.inner_join(user_::table.on(comment_report::creator_id.eq(user_::id))) .inner_join(person::table.on(comment_report::creator_id.eq(person::id)))
.inner_join(user_alias_1::table.on(post::creator_id.eq(user_alias_1::id))) .inner_join(person_alias_1::table.on(post::creator_id.eq(person_alias_1::id)))
.left_join( .left_join(
user_alias_2::table.on(comment_report::resolver_id.eq(user_alias_2::id.nullable())), person_alias_2::table.on(comment_report::resolver_id.eq(person_alias_2::id.nullable())),
) )
.select(( .select((
comment_report::all_columns, comment_report::all_columns,
comment::all_columns, comment::all_columns,
post::all_columns, post::all_columns,
Community::safe_columns_tuple(), Community::safe_columns_tuple(),
User_::safe_columns_tuple(), Person::safe_columns_tuple(),
UserAlias1::safe_columns_tuple(), PersonAlias1::safe_columns_tuple(),
UserAlias2::safe_columns_tuple().nullable(), PersonAlias2::safe_columns_tuple().nullable(),
)) ))
.into_boxed(); .into_boxed();

Some files were not shown because too many files have changed in this diff Show more