mirror of
https://github.com/LemmyNet/lemmy.git
synced 2025-09-02 03:03:57 +00:00
Merge remote-tracking branch 'upstream/main' into new-migration-diff-check
This commit is contained in:
parent
3ac183599f
commit
28bb1c7497
410 changed files with 13137 additions and 9307 deletions
2
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
2
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
|
@ -14,7 +14,7 @@ body:
|
|||
label: Requirements
|
||||
description: Before you create a bug report please do the following.
|
||||
options:
|
||||
- label: Is this a bug report? For questions or discussions use https://lemmy.ml/c/lemmy_support
|
||||
- label: Is this a bug report? For questions or discussions use https://lemmy.ml/c/lemmy_support or the [matrix chat](https://matrix.to/#/#lemmy:matrix.org).
|
||||
required: true
|
||||
- label: Did you check to see if this issue already exists?
|
||||
required: true
|
||||
|
|
2
.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml
vendored
2
.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml
vendored
|
@ -12,7 +12,7 @@ body:
|
|||
label: Requirements
|
||||
description: Before you create a bug report please do the following.
|
||||
options:
|
||||
- label: Is this a feature request? For questions or discussions use https://lemmy.ml/c/lemmy_support
|
||||
- label: Is this a feature request? For questions or discussions use https://lemmy.ml/c/lemmy_support or the [matrix chat](https://matrix.to/#/#lemmy:matrix.org).
|
||||
required: true
|
||||
- label: Did you check to see if this issue already exists?
|
||||
required: true
|
||||
|
|
4
.github/ISSUE_TEMPLATE/QUESTION.yml
vendored
4
.github/ISSUE_TEMPLATE/QUESTION.yml
vendored
|
@ -6,7 +6,9 @@ body:
|
|||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Have a question about Lemmy?
|
||||
For questions or discussions use https://lemmy.ml/c/lemmy_support or the [matrix chat](https://matrix.to/#/#lemmy:matrix.org).
|
||||
|
||||
Have a question about how Lemmy works?
|
||||
Please check the docs first: https://join-lemmy.org/docs/en/index.html
|
||||
- type: textarea
|
||||
id: question
|
||||
|
|
|
@ -6,13 +6,13 @@ variables:
|
|||
# as well. Otherwise release builds can fail if Lemmy or dependencies rely on new Rust
|
||||
# features. In particular the ARM builder image needs to be updated manually in the repo below:
|
||||
# https://github.com/raskyld/lemmy-cross-toolchains
|
||||
- &rust_image "rust:1.83"
|
||||
- &rust_image "rust:1.81"
|
||||
- &rust_nightly_image "rustlang/rust:nightly"
|
||||
- &install_pnpm "corepack enable pnpm"
|
||||
- &install_pnpm "npm install -g corepack@latest && corepack enable pnpm"
|
||||
- &install_binstall "wget -O- https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz | tar -xvz -C /usr/local/cargo/bin"
|
||||
- install_diesel_cli: &install_diesel_cli
|
||||
- apt-get update && apt-get install -y postgresql-client
|
||||
- cargo install diesel_cli --no-default-features --features postgres
|
||||
- cargo install --locked diesel_cli --no-default-features --features postgres
|
||||
- export PATH="$CARGO_HOME/bin:$PATH"
|
||||
- &slow_check_paths
|
||||
- event: pull_request
|
||||
|
|
461
Cargo.lock
generated
461
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
68
Cargo.toml
68
Cargo.toml
|
@ -1,5 +1,5 @@
|
|||
[workspace.package]
|
||||
version = "0.19.6-beta.7"
|
||||
version = "1.0.0-alpha.2"
|
||||
edition = "2021"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
|
@ -36,7 +36,6 @@ codegen-units = 1 # Reduce parallel code generation.
|
|||
debug = 0
|
||||
|
||||
[features]
|
||||
json-log = ["tracing-subscriber/json"]
|
||||
default = []
|
||||
|
||||
[workspace]
|
||||
|
@ -49,8 +48,6 @@ members = [
|
|||
"crates/db_perf",
|
||||
"crates/db_schema",
|
||||
"crates/db_views",
|
||||
"crates/db_views_actor",
|
||||
"crates/db_views_actor",
|
||||
"crates/routes",
|
||||
"crates/federate",
|
||||
]
|
||||
|
@ -82,21 +79,19 @@ map_err_ignore = "deny"
|
|||
expect_used = "deny"
|
||||
|
||||
[workspace.dependencies]
|
||||
lemmy_api = { version = "=0.19.6-beta.7", path = "./crates/api" }
|
||||
lemmy_api_crud = { version = "=0.19.6-beta.7", path = "./crates/api_crud" }
|
||||
lemmy_apub = { version = "=0.19.6-beta.7", path = "./crates/apub" }
|
||||
lemmy_utils = { version = "=0.19.6-beta.7", path = "./crates/utils", default-features = false }
|
||||
lemmy_db_schema = { version = "=0.19.6-beta.7", path = "./crates/db_schema" }
|
||||
lemmy_api_common = { version = "=0.19.6-beta.7", path = "./crates/api_common" }
|
||||
lemmy_routes = { version = "=0.19.6-beta.7", path = "./crates/routes" }
|
||||
lemmy_db_views = { version = "=0.19.6-beta.7", path = "./crates/db_views" }
|
||||
lemmy_db_views_actor = { version = "=0.19.6-beta.7", path = "./crates/db_views_actor" }
|
||||
lemmy_db_views_moderator = { version = "=0.19.6-beta.7", path = "./crates/db_views_moderator" }
|
||||
lemmy_federate = { version = "=0.19.6-beta.7", path = "./crates/federate" }
|
||||
activitypub_federation = { version = "0.6.1", default-features = false, features = [
|
||||
lemmy_api = { version = "=1.0.0-alpha.2", path = "./crates/api" }
|
||||
lemmy_api_crud = { version = "=1.0.0-alpha.2", path = "./crates/api_crud" }
|
||||
lemmy_apub = { version = "=1.0.0-alpha.2", path = "./crates/apub" }
|
||||
lemmy_utils = { version = "=1.0.0-alpha.2", path = "./crates/utils", default-features = false }
|
||||
lemmy_db_schema = { version = "=1.0.0-alpha.2", path = "./crates/db_schema" }
|
||||
lemmy_api_common = { version = "=1.0.0-alpha.2", path = "./crates/api_common" }
|
||||
lemmy_routes = { version = "=1.0.0-alpha.2", path = "./crates/routes" }
|
||||
lemmy_db_views = { version = "=1.0.0-alpha.2", path = "./crates/db_views" }
|
||||
lemmy_federate = { version = "=1.0.0-alpha.2", path = "./crates/federate" }
|
||||
activitypub_federation = { version = "0.6.3", default-features = false, features = [
|
||||
"actix-web",
|
||||
] }
|
||||
diesel = "2.2.6"
|
||||
diesel = "2.2.7"
|
||||
diesel_migrations = "2.2.0"
|
||||
diesel-async = "0.5.2"
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
|
@ -109,9 +104,9 @@ actix-web = { version = "4.9.0", default-features = false, features = [
|
|||
"macros",
|
||||
"rustls-0_23",
|
||||
] }
|
||||
tracing = "0.1.41"
|
||||
tracing = { version = "0.1.41", default-features = false }
|
||||
tracing-actix-web = { version = "0.7.15", default-features = false }
|
||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter", "json"] }
|
||||
url = { version = "2.5.4", features = ["serde"] }
|
||||
reqwest = { version = "0.12.12", default-features = false, features = [
|
||||
"blocking",
|
||||
|
@ -123,15 +118,14 @@ reqwest-middleware = "0.3.3"
|
|||
reqwest-tracing = "0.5.5"
|
||||
clokwerk = "0.4.0"
|
||||
doku = { version = "0.21.1", features = ["url-2"] }
|
||||
bcrypt = "0.16.0"
|
||||
bcrypt = "0.17.0"
|
||||
chrono = { version = "0.4.39", features = [
|
||||
"now",
|
||||
"serde",
|
||||
], default-features = false }
|
||||
serde_json = { version = "1.0.135", features = ["preserve_order"] }
|
||||
serde_json = { version = "1.0.138", features = ["preserve_order"] }
|
||||
base64 = "0.22.1"
|
||||
uuid = { version = "1.12.0", features = ["serde"] }
|
||||
async-trait = "0.1.85"
|
||||
uuid = { version = "1.13.1", features = ["serde"] }
|
||||
captcha = "0.0.9"
|
||||
anyhow = { version = "1.0.95", features = ["backtrace"] }
|
||||
diesel_ltree = "0.4.0"
|
||||
|
@ -140,7 +134,8 @@ tokio = { version = "1.43.0", features = ["full"] }
|
|||
regex = "1.11.1"
|
||||
diesel-derive-newtype = "2.1.2"
|
||||
diesel-derive-enum = { version = "2.1.0", features = ["postgres"] }
|
||||
strum = { version = "0.26.3", features = ["derive"] }
|
||||
enum-map = { version = "2.7" }
|
||||
strum = { version = "0.27.0", features = ["derive"] }
|
||||
itertools = "0.14.0"
|
||||
futures = "0.3.31"
|
||||
http = "1.2"
|
||||
|
@ -150,18 +145,16 @@ ts-rs = { version = "10.1.0", features = [
|
|||
"no-serde-warnings",
|
||||
"url-impl",
|
||||
] }
|
||||
rustls = { version = "0.23.21", features = ["ring"] }
|
||||
rustls = { version = "0.23.23", features = ["ring"] }
|
||||
futures-util = "0.3.31"
|
||||
tokio-postgres = "0.7.12"
|
||||
tokio-postgres = "0.7.13"
|
||||
tokio-postgres-rustls = "0.13.0"
|
||||
urlencoding = "2.1.3"
|
||||
enum-map = "2.7"
|
||||
moka = { version = "0.12.10", features = ["future"] }
|
||||
i-love-jesus = { version = "0.1.0" }
|
||||
clap = { version = "4.5.26", features = ["derive", "env"] }
|
||||
clap = { version = "4.5.29", features = ["derive", "env"] }
|
||||
pretty_assertions = "1.4.1"
|
||||
derive-new = "0.7.0"
|
||||
diesel-bind-if-some = "0.1.0"
|
||||
tuplex = "0.1.2"
|
||||
|
||||
[dependencies]
|
||||
|
@ -174,26 +167,19 @@ lemmy_api_common = { workspace = true }
|
|||
lemmy_routes = { workspace = true }
|
||||
lemmy_federate = { workspace = true }
|
||||
activitypub_federation = { workspace = true }
|
||||
diesel = { workspace = true }
|
||||
diesel-async = { workspace = true }
|
||||
actix-web = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-actix-web = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
url = { workspace = true }
|
||||
reqwest-middleware = { workspace = true }
|
||||
reqwest-tracing = { workspace = true }
|
||||
clokwerk = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
rustls = { workspace = true }
|
||||
tokio.workspace = true
|
||||
actix-cors = "0.7.0"
|
||||
futures-util = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
prometheus = { version = "0.13.4", features = ["process"] }
|
||||
serial_test = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
actix-web-prom = "0.9.0"
|
||||
mimalloc = "0.1.43"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = { workspace = true }
|
||||
# Speedup RSA key generation
|
||||
# https://github.com/RustCrypto/RSA/blob/master/README.md#example
|
||||
[profile.dev.package.num-bigint-dig]
|
||||
opt-level = 3
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"repository": "https://github.com/LemmyNet/lemmy",
|
||||
"author": "Dessalines",
|
||||
"license": "AGPL-3.0",
|
||||
"packageManager": "pnpm@9.15.0",
|
||||
"packageManager": "pnpm@10.2.1+sha512.398035c7bd696d0ba0b10a688ed558285329d27ea994804a52bad9167d8e3a72bcb993f9699585d3ca25779ac64949ef422757a6c31102c12ab932e5cbe5cc92",
|
||||
"scripts": {
|
||||
"lint": "tsc --noEmit && eslint --report-unused-disable-directives && prettier --check 'src/**/*.ts'",
|
||||
"fix": "prettier --write src && eslint --fix src",
|
||||
|
@ -22,16 +22,18 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^22.10.6",
|
||||
"@typescript-eslint/eslint-plugin": "^8.20.0",
|
||||
"@typescript-eslint/parser": "^8.20.0",
|
||||
"eslint": "^9.18.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"@types/joi": "^17.2.3",
|
||||
"@types/node": "^22.13.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.24.0",
|
||||
"@typescript-eslint/parser": "^8.24.0",
|
||||
"eslint": "^9.20.0",
|
||||
"eslint-plugin-prettier": "^5.2.3",
|
||||
"jest": "^29.5.0",
|
||||
"lemmy-js-client": "0.20.0-inbox-combined.1",
|
||||
"prettier": "^3.4.2",
|
||||
"lemmy-js-client": "0.20.0-remove-aggregate-tables.5",
|
||||
"prettier": "^3.5.0",
|
||||
"ts-jest": "^29.1.0",
|
||||
"tsoa": "^6.6.0",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.20.0"
|
||||
"typescript-eslint": "^8.24.0"
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -9,17 +9,31 @@ then
|
|||
fi
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
export RUST_LOG="warn,lemmy_server=$LEMMY_LOG_LEVEL,lemmy_federate=$LEMMY_LOG_LEVEL,lemmy_api=$LEMMY_LOG_LEVEL,lemmy_api_common=$LEMMY_LOG_LEVEL,lemmy_api_crud=$LEMMY_LOG_LEVEL,lemmy_apub=$LEMMY_LOG_LEVEL,lemmy_db_schema=$LEMMY_LOG_LEVEL,lemmy_db_views=$LEMMY_LOG_LEVEL,lemmy_db_views_actor=$LEMMY_LOG_LEVEL,lemmy_db_views_moderator=$LEMMY_LOG_LEVEL,lemmy_routes=$LEMMY_LOG_LEVEL,lemmy_utils=$LEMMY_LOG_LEVEL,lemmy_websocket=$LEMMY_LOG_LEVEL"
|
||||
export RUST_LOG="warn,lemmy_server=$LEMMY_LOG_LEVEL,lemmy_federate=$LEMMY_LOG_LEVEL,lemmy_api=$LEMMY_LOG_LEVEL,lemmy_api_common=$LEMMY_LOG_LEVEL,lemmy_api_crud=$LEMMY_LOG_LEVEL,lemmy_apub=$LEMMY_LOG_LEVEL,lemmy_db_schema=$LEMMY_LOG_LEVEL,lemmy_db_views=$LEMMY_LOG_LEVEL,lemmy_routes=$LEMMY_LOG_LEVEL,lemmy_utils=$LEMMY_LOG_LEVEL,lemmy_websocket=$LEMMY_LOG_LEVEL"
|
||||
|
||||
export LEMMY_TEST_FAST_FEDERATION=1 # by default, the persistent federation queue has delays in the scale of 30s-5min
|
||||
|
||||
# pictrs setup
|
||||
if [ ! -f "api_tests/pict-rs" ]; then
|
||||
# This one sometimes goes down
|
||||
# curl "https://git.asonix.dog/asonix/pict-rs/releases/download/v0.5.16/pict-rs-linux-amd64" -o api_tests/pict-rs
|
||||
curl "https://codeberg.org/asonix/pict-rs/releases/download/v0.5.6/pict-rs-linux-amd64" -o api_tests/pict-rs
|
||||
chmod +x api_tests/pict-rs
|
||||
PICTRS_PATH="api_tests/pict-rs"
|
||||
PICTRS_EXPECTED_HASH="7f7ac2a45ef9b13403ee139b7512135be6b060ff2f6460e0c800e18e1b49d2fd api_tests/pict-rs"
|
||||
|
||||
# Pictrs setup. Download file with hash check and up to 3 retries.
|
||||
if [ ! -f "$PICTRS_PATH" ]; then
|
||||
count=0
|
||||
while [ ! -f "$PICTRS_PATH" ] && [ "$count" -lt 3 ]
|
||||
do
|
||||
# This one sometimes goes down
|
||||
curl "https://git.asonix.dog/asonix/pict-rs/releases/download/v0.5.17-pre.9/pict-rs-linux-amd64" -o "$PICTRS_PATH"
|
||||
# curl "https://codeberg.org/asonix/pict-rs/releases/download/v0.5.5/pict-rs-linux-amd64" -o "$PICTRS_PATH"
|
||||
PICTRS_HASH=$(sha256sum "$PICTRS_PATH")
|
||||
if [[ "$PICTRS_HASH" != "$PICTRS_EXPECTED_HASH" ]]; then
|
||||
echo "Pictrs binary hash mismatch, was $PICTRS_HASH but expected $PICTRS_EXPECTED_HASH"
|
||||
rm "$PICTRS_PATH"
|
||||
let count=count+1
|
||||
fi
|
||||
done
|
||||
chmod +x "$PICTRS_PATH"
|
||||
fi
|
||||
|
||||
./api_tests/pict-rs \
|
||||
run -a 0.0.0.0:8080 \
|
||||
--danger-dummy-mode \
|
||||
|
@ -72,13 +86,11 @@ LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_gamma.hjson \
|
|||
target/lemmy_server >$LOG_DIR/lemmy_gamma.out 2>&1 &
|
||||
|
||||
echo "start delta"
|
||||
# An instance with only an allowlist for beta
|
||||
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_delta.hjson \
|
||||
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_delta" \
|
||||
target/lemmy_server >$LOG_DIR/lemmy_delta.out 2>&1 &
|
||||
|
||||
echo "start epsilon"
|
||||
# An instance who has a blocklist, with lemmy-alpha blocked
|
||||
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_epsilon.hjson \
|
||||
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_epsilon" \
|
||||
target/lemmy_server >$LOG_DIR/lemmy_epsilon.out 2>&1 &
|
||||
|
|
|
@ -69,7 +69,7 @@ function assertCommentFederation(
|
|||
expect(commentOne?.comment.ap_id).toBe(commentTwo?.comment.ap_id);
|
||||
expect(commentOne?.comment.content).toBe(commentTwo?.comment.content);
|
||||
expect(commentOne?.creator.name).toBe(commentTwo?.creator.name);
|
||||
expect(commentOne?.community.actor_id).toBe(commentTwo?.community.actor_id);
|
||||
expect(commentOne?.community.ap_id).toBe(commentTwo?.community.ap_id);
|
||||
expect(commentOne?.comment.published).toBe(commentTwo?.comment.published);
|
||||
expect(commentOne?.comment.updated).toBe(commentOne?.comment.updated);
|
||||
expect(commentOne?.comment.deleted).toBe(commentOne?.comment.deleted);
|
||||
|
@ -81,19 +81,19 @@ test("Create a comment", async () => {
|
|||
expect(commentRes.comment_view.comment.content).toBeDefined();
|
||||
expect(commentRes.comment_view.community.local).toBe(false);
|
||||
expect(commentRes.comment_view.creator.local).toBe(true);
|
||||
expect(commentRes.comment_view.counts.score).toBe(1);
|
||||
expect(commentRes.comment_view.comment.score).toBe(1);
|
||||
|
||||
// Make sure that comment is liked on beta
|
||||
let betaComment = (
|
||||
await waitUntil(
|
||||
() => resolveComment(beta, commentRes.comment_view.comment),
|
||||
c => c.comment?.counts.score === 1,
|
||||
c => c.comment?.comment.score === 1,
|
||||
)
|
||||
).comment;
|
||||
expect(betaComment).toBeDefined();
|
||||
expect(betaComment?.community.local).toBe(true);
|
||||
expect(betaComment?.creator.local).toBe(false);
|
||||
expect(betaComment?.counts.score).toBe(1);
|
||||
expect(betaComment?.comment.score).toBe(1);
|
||||
assertCommentFederation(betaComment, commentRes.comment_view);
|
||||
});
|
||||
|
||||
|
@ -293,48 +293,48 @@ test("Unlike a comment", async () => {
|
|||
let gammaComment1 = (
|
||||
await waitUntil(
|
||||
() => resolveComment(gamma, commentRes.comment_view.comment),
|
||||
c => c.comment?.counts.score === 1,
|
||||
c => c.comment?.comment.score === 1,
|
||||
)
|
||||
).comment;
|
||||
expect(gammaComment1).toBeDefined();
|
||||
expect(gammaComment1?.community.local).toBe(false);
|
||||
expect(gammaComment1?.creator.local).toBe(false);
|
||||
expect(gammaComment1?.counts.score).toBe(1);
|
||||
expect(gammaComment1?.comment.score).toBe(1);
|
||||
|
||||
let unlike = await likeComment(alpha, 0, commentRes.comment_view.comment);
|
||||
expect(unlike.comment_view.counts.score).toBe(0);
|
||||
expect(unlike.comment_view.comment.score).toBe(0);
|
||||
|
||||
// Make sure that comment is unliked on beta
|
||||
let betaComment = (
|
||||
await waitUntil(
|
||||
() => resolveComment(beta, commentRes.comment_view.comment),
|
||||
c => c.comment?.counts.score === 0,
|
||||
c => c.comment?.comment.score === 0,
|
||||
)
|
||||
).comment;
|
||||
expect(betaComment).toBeDefined();
|
||||
expect(betaComment?.community.local).toBe(true);
|
||||
expect(betaComment?.creator.local).toBe(false);
|
||||
expect(betaComment?.counts.score).toBe(0);
|
||||
expect(betaComment?.comment.score).toBe(0);
|
||||
|
||||
// Make sure that comment is unliked on gamma, downstream peer
|
||||
// This is testing replication from remote-home-remote (alpha-beta-gamma)
|
||||
let gammaComment = (
|
||||
await waitUntil(
|
||||
() => resolveComment(gamma, commentRes.comment_view.comment),
|
||||
c => c.comment?.counts.score === 0,
|
||||
c => c.comment?.comment.score === 0,
|
||||
)
|
||||
).comment;
|
||||
expect(gammaComment).toBeDefined();
|
||||
expect(gammaComment?.community.local).toBe(false);
|
||||
expect(gammaComment?.creator.local).toBe(false);
|
||||
expect(gammaComment?.counts.score).toBe(0);
|
||||
expect(gammaComment?.comment.score).toBe(0);
|
||||
});
|
||||
|
||||
test("Federated comment like", async () => {
|
||||
let commentRes = await createComment(alpha, postOnAlphaRes.post_view.post.id);
|
||||
await waitUntil(
|
||||
() => resolveComment(beta, commentRes.comment_view.comment),
|
||||
c => c.comment?.counts.score === 1,
|
||||
c => c.comment?.comment.score === 1,
|
||||
);
|
||||
// Find the comment on beta
|
||||
let betaComment = (
|
||||
|
@ -346,14 +346,14 @@ test("Federated comment like", async () => {
|
|||
}
|
||||
|
||||
let like = await likeComment(beta, 1, betaComment.comment);
|
||||
expect(like.comment_view.counts.score).toBe(2);
|
||||
expect(like.comment_view.comment.score).toBe(2);
|
||||
|
||||
// Get the post from alpha, check the likes
|
||||
let postComments = await waitUntil(
|
||||
() => getComments(alpha, postOnAlphaRes.post_view.post.id),
|
||||
c => c.comments[0].counts.score === 2,
|
||||
c => c.comments[0].comment.score === 2,
|
||||
);
|
||||
expect(postComments.comments[0].counts.score).toBe(2);
|
||||
expect(postComments.comments[0].comment.score).toBe(2);
|
||||
});
|
||||
|
||||
test("Reply to a comment from another instance, get notification", async () => {
|
||||
|
@ -377,7 +377,7 @@ test("Reply to a comment from another instance, get notification", async () => {
|
|||
let betaComment = (
|
||||
await waitUntil(
|
||||
() => resolveComment(beta, commentRes.comment_view.comment),
|
||||
c => c.comment?.counts.score === 1,
|
||||
c => c.comment?.comment.score === 1,
|
||||
)
|
||||
).comment;
|
||||
|
||||
|
@ -397,12 +397,12 @@ test("Reply to a comment from another instance, get notification", async () => {
|
|||
expect(getCommentParentId(replyRes.comment_view.comment)).toBe(
|
||||
betaComment.comment.id,
|
||||
);
|
||||
expect(replyRes.comment_view.counts.score).toBe(1);
|
||||
expect(replyRes.comment_view.comment.score).toBe(1);
|
||||
|
||||
// Make sure that reply comment is seen on alpha
|
||||
let commentSearch = await waitUntil(
|
||||
() => resolveComment(alpha, replyRes.comment_view.comment),
|
||||
c => c.comment?.counts.score === 1,
|
||||
c => c.comment?.comment.score === 1,
|
||||
);
|
||||
let alphaComment = commentSearch.comment!;
|
||||
let postComments = await waitUntil(
|
||||
|
@ -418,7 +418,7 @@ test("Reply to a comment from another instance, get notification", async () => {
|
|||
);
|
||||
expect(alphaComment.community.local).toBe(false);
|
||||
expect(alphaComment.creator.local).toBe(false);
|
||||
expect(alphaComment.counts.score).toBe(1);
|
||||
expect(alphaComment.comment.score).toBe(1);
|
||||
assertCommentFederation(alphaComment, replyRes.comment_view);
|
||||
|
||||
// Did alpha get notified of the reply from beta?
|
||||
|
@ -441,7 +441,7 @@ test("Reply to a comment from another instance, get notification", async () => {
|
|||
expect(alphaReply.comment.content).toBeDefined();
|
||||
expect(alphaReply.community.local).toBe(false);
|
||||
expect(alphaReply.creator.local).toBe(false);
|
||||
expect(alphaReply.counts.score).toBe(1);
|
||||
expect(alphaReply.comment.score).toBe(1);
|
||||
// ToDo: interesting alphaRepliesRes.replies[0].comment_reply.id is 1, meaning? how did that come about?
|
||||
expect(alphaReply.comment.id).toBe(alphaComment.comment.id);
|
||||
// this is a new notification, getReplies fetch was for read/unread both, confirm it is unread.
|
||||
|
@ -515,7 +515,7 @@ test("Mention beta from alpha comment", async () => {
|
|||
expect(mentionRes.comment_view.comment.content).toBeDefined();
|
||||
expect(mentionRes.comment_view.community.local).toBe(false);
|
||||
expect(mentionRes.comment_view.creator.local).toBe(true);
|
||||
expect(mentionRes.comment_view.counts.score).toBe(1);
|
||||
expect(mentionRes.comment_view.comment.score).toBe(1);
|
||||
|
||||
// get beta's localized copy of the alpha post
|
||||
let betaPost = await waitForPost(beta, postOnAlphaRes.post_view.post);
|
||||
|
@ -528,7 +528,7 @@ test("Mention beta from alpha comment", async () => {
|
|||
// Make sure that both new comments are seen on beta and have parent/child relationship
|
||||
let betaPostComments = await waitUntil(
|
||||
() => getComments(beta, betaPost!.post.id),
|
||||
c => c.comments[1]?.counts.score === 1,
|
||||
c => c.comments[1]?.comment.score === 1,
|
||||
);
|
||||
expect(betaPostComments.comments.length).toEqual(2);
|
||||
// the trunk-branch root comment will be older than the mention reply comment, so index 1
|
||||
|
@ -542,7 +542,7 @@ test("Mention beta from alpha comment", async () => {
|
|||
);
|
||||
expect(betaRootComment.community.local).toBe(true);
|
||||
expect(betaRootComment.creator.local).toBe(false);
|
||||
expect(betaRootComment.counts.score).toBe(1);
|
||||
expect(betaRootComment.comment.score).toBe(1);
|
||||
assertCommentFederation(betaRootComment, commentRes.comment_view);
|
||||
|
||||
let mentionsRes = await waitUntil(
|
||||
|
@ -554,7 +554,7 @@ test("Mention beta from alpha comment", async () => {
|
|||
expect(firstMention.comment.content).toBeDefined();
|
||||
expect(firstMention.community.local).toBe(true);
|
||||
expect(firstMention.creator.local).toBe(false);
|
||||
expect(firstMention.counts.score).toBe(1);
|
||||
expect(firstMention.comment.score).toBe(1);
|
||||
// the reply comment with mention should be the most fresh, newest, index 0
|
||||
expect(firstMention.person_comment_mention.comment_id).toBe(
|
||||
betaPostComments.comments[0].comment.id,
|
||||
|
@ -581,7 +581,7 @@ test("A and G subscribe to B (center) A posts, G mentions B, it gets announced t
|
|||
// follow community from beta so that it accepts the mention
|
||||
let betaCommunity = await resolveCommunity(
|
||||
beta,
|
||||
alphaCommunity.community.actor_id,
|
||||
alphaCommunity.community.ap_id,
|
||||
);
|
||||
await followCommunity(beta, true, betaCommunity.community!.community.id);
|
||||
|
||||
|
@ -606,17 +606,17 @@ test("A and G subscribe to B (center) A posts, G mentions B, it gets announced t
|
|||
expect(commentRes.comment_view.comment.content).toBe(commentContent);
|
||||
expect(commentRes.comment_view.community.local).toBe(false);
|
||||
expect(commentRes.comment_view.creator.local).toBe(true);
|
||||
expect(commentRes.comment_view.counts.score).toBe(1);
|
||||
expect(commentRes.comment_view.comment.score).toBe(1);
|
||||
|
||||
// Make sure alpha sees it
|
||||
let alphaPostComments2 = await waitUntil(
|
||||
() => getComments(alpha, alphaPost.post_view.post.id),
|
||||
e => e.comments[0]?.counts.score === 1,
|
||||
e => e.comments[0]?.comment.score === 1,
|
||||
);
|
||||
expect(alphaPostComments2.comments[0].comment.content).toBe(commentContent);
|
||||
expect(alphaPostComments2.comments[0].community.local).toBe(true);
|
||||
expect(alphaPostComments2.comments[0].creator.local).toBe(false);
|
||||
expect(alphaPostComments2.comments[0].counts.score).toBe(1);
|
||||
expect(alphaPostComments2.comments[0].comment.score).toBe(1);
|
||||
assertCommentFederation(
|
||||
alphaPostComments2.comments[0],
|
||||
commentRes.comment_view,
|
||||
|
@ -688,17 +688,17 @@ test("Check that activity from another instance is sent to third instance", asyn
|
|||
expect(commentRes.comment_view.comment.content).toBe(commentContent);
|
||||
expect(commentRes.comment_view.community.local).toBe(false);
|
||||
expect(commentRes.comment_view.creator.local).toBe(true);
|
||||
expect(commentRes.comment_view.counts.score).toBe(1);
|
||||
expect(commentRes.comment_view.comment.score).toBe(1);
|
||||
|
||||
// Make sure alpha sees it
|
||||
let alphaPostComments2 = await waitUntil(
|
||||
() => getComments(alpha, alphaPost!.post.id),
|
||||
e => e.comments[0]?.counts.score === 1,
|
||||
e => e.comments[0]?.comment.score === 1,
|
||||
);
|
||||
expect(alphaPostComments2.comments[0].comment.content).toBe(commentContent);
|
||||
expect(alphaPostComments2.comments[0].community.local).toBe(false);
|
||||
expect(alphaPostComments2.comments[0].creator.local).toBe(false);
|
||||
expect(alphaPostComments2.comments[0].counts.score).toBe(1);
|
||||
expect(alphaPostComments2.comments[0].comment.score).toBe(1);
|
||||
assertCommentFederation(
|
||||
alphaPostComments2.comments[0],
|
||||
commentRes.comment_view,
|
||||
|
|
|
@ -44,9 +44,7 @@ function assertCommunityFederation(
|
|||
communityOne?: CommunityView,
|
||||
communityTwo?: CommunityView,
|
||||
) {
|
||||
expect(communityOne?.community.actor_id).toBe(
|
||||
communityTwo?.community.actor_id,
|
||||
);
|
||||
expect(communityOne?.community.ap_id).toBe(communityTwo?.community.ap_id);
|
||||
expect(communityOne?.community.name).toBe(communityTwo?.community.name);
|
||||
expect(communityOne?.community.title).toBe(communityTwo?.community.title);
|
||||
expect(communityOne?.community.description).toBe(
|
||||
|
@ -198,7 +196,7 @@ test("Admin actions in remote community are not federated to origin", async () =
|
|||
|
||||
// gamma follows community and posts in it
|
||||
let gammaCommunity = (
|
||||
await resolveCommunity(gamma, communityRes.community.actor_id)
|
||||
await resolveCommunity(gamma, communityRes.community.ap_id)
|
||||
).community;
|
||||
if (!gammaCommunity) {
|
||||
throw "Missing gamma community";
|
||||
|
@ -206,7 +204,7 @@ test("Admin actions in remote community are not federated to origin", async () =
|
|||
await followCommunity(gamma, true, gammaCommunity.community.id);
|
||||
gammaCommunity = (
|
||||
await waitUntil(
|
||||
() => resolveCommunity(gamma, communityRes.community.actor_id),
|
||||
() => resolveCommunity(gamma, communityRes.community.ap_id),
|
||||
g => g.community?.subscribed === "Subscribed",
|
||||
)
|
||||
).community;
|
||||
|
@ -221,7 +219,7 @@ test("Admin actions in remote community are not federated to origin", async () =
|
|||
|
||||
// admin of beta decides to ban gamma from community
|
||||
let betaCommunity = (
|
||||
await resolveCommunity(beta, communityRes.community.actor_id)
|
||||
await resolveCommunity(beta, communityRes.community.ap_id)
|
||||
).community;
|
||||
if (!betaCommunity) {
|
||||
throw "Missing beta community";
|
||||
|
@ -230,7 +228,7 @@ test("Admin actions in remote community are not federated to origin", async () =
|
|||
if (!bannedUserInfo1) {
|
||||
throw "Missing banned user 1";
|
||||
}
|
||||
let bannedUserInfo2 = (await resolvePerson(beta, bannedUserInfo1.actor_id))
|
||||
let bannedUserInfo2 = (await resolvePerson(beta, bannedUserInfo1.ap_id))
|
||||
.person;
|
||||
if (!bannedUserInfo2) {
|
||||
throw "Missing banned user 2";
|
||||
|
@ -379,10 +377,11 @@ test("User blocks instance, communities are hidden", async () => {
|
|||
expect(listing_ids3).toContain(postRes.post_view.post.ap_id);
|
||||
});
|
||||
|
||||
test("Community follower count is federated", async () => {
|
||||
// TODO: this test keeps failing randomly in CI
|
||||
test.skip("Community follower count is federated", async () => {
|
||||
// Follow the beta community from alpha
|
||||
let community = await createCommunity(beta);
|
||||
let communityActorId = community.community_view.community.actor_id;
|
||||
let communityActorId = community.community_view.community.ap_id;
|
||||
let resolved = await resolveCommunity(alpha, communityActorId);
|
||||
if (!resolved.community) {
|
||||
throw "Missing beta community";
|
||||
|
@ -397,7 +396,7 @@ test("Community follower count is federated", async () => {
|
|||
).community;
|
||||
|
||||
// Make sure there is 1 subscriber
|
||||
expect(followed?.counts.subscribers).toBe(1);
|
||||
expect(followed?.community.subscribers).toBe(1);
|
||||
|
||||
// Follow the community from gamma
|
||||
resolved = await resolveCommunity(gamma, communityActorId);
|
||||
|
@ -414,7 +413,7 @@ test("Community follower count is federated", async () => {
|
|||
).community;
|
||||
|
||||
// Make sure there are 2 subscribers
|
||||
expect(followed?.counts?.subscribers).toBe(2);
|
||||
expect(followed?.community?.subscribers).toBe(2);
|
||||
|
||||
// Follow the community from delta
|
||||
resolved = await resolveCommunity(delta, communityActorId);
|
||||
|
@ -431,16 +430,16 @@ test("Community follower count is federated", async () => {
|
|||
).community;
|
||||
|
||||
// Make sure there are 3 subscribers
|
||||
expect(followed?.counts?.subscribers).toBe(3);
|
||||
expect(followed?.community?.subscribers).toBe(3);
|
||||
});
|
||||
|
||||
test("Dont receive community activities after unsubscribe", async () => {
|
||||
let communityRes = await createCommunity(alpha);
|
||||
expect(communityRes.community_view.community.name).toBeDefined();
|
||||
expect(communityRes.community_view.counts.subscribers).toBe(1);
|
||||
expect(communityRes.community_view.community.subscribers).toBe(1);
|
||||
|
||||
let betaCommunity = (
|
||||
await resolveCommunity(beta, communityRes.community_view.community.actor_id)
|
||||
await resolveCommunity(beta, communityRes.community_view.community.ap_id)
|
||||
).community;
|
||||
assertCommunityFederation(betaCommunity, communityRes.community_view);
|
||||
|
||||
|
@ -452,7 +451,7 @@ test("Dont receive community activities after unsubscribe", async () => {
|
|||
alpha,
|
||||
communityRes.community_view.community.id,
|
||||
);
|
||||
expect(communityRes1.community_view.counts.subscribers).toBe(2);
|
||||
expect(communityRes1.community_view.community.subscribers).toBe(2);
|
||||
|
||||
// temporarily block alpha, so that it doesn't know about unfollow
|
||||
var allow_instance_params: AdminAllowInstanceParams = {
|
||||
|
@ -471,7 +470,7 @@ test("Dont receive community activities after unsubscribe", async () => {
|
|||
alpha,
|
||||
communityRes.community_view.community.id,
|
||||
);
|
||||
expect(communityRes2.community_view.counts.subscribers).toBe(2);
|
||||
expect(communityRes2.community_view.community.subscribers).toBe(2);
|
||||
|
||||
// unblock alpha
|
||||
allow_instance_params.allow = true;
|
||||
|
@ -487,13 +486,13 @@ test("Dont receive community activities after unsubscribe", async () => {
|
|||
// await longDelay();
|
||||
|
||||
let postResBeta = searchPostLocal(beta, postRes.post_view.post);
|
||||
expect((await postResBeta).posts.length).toBe(0);
|
||||
expect((await postResBeta).results.length).toBe(0);
|
||||
});
|
||||
|
||||
test("Fetch community, includes posts", async () => {
|
||||
let communityRes = await createCommunity(alpha);
|
||||
expect(communityRes.community_view.community.name).toBeDefined();
|
||||
expect(communityRes.community_view.counts.subscribers).toBe(1);
|
||||
expect(communityRes.community_view.community.subscribers).toBe(1);
|
||||
|
||||
let postRes = await createPost(
|
||||
alpha,
|
||||
|
@ -502,13 +501,12 @@ test("Fetch community, includes posts", async () => {
|
|||
expect(postRes.post_view.post).toBeDefined();
|
||||
|
||||
let resolvedCommunity = await waitUntil(
|
||||
() =>
|
||||
resolveCommunity(beta, communityRes.community_view.community.actor_id),
|
||||
() => resolveCommunity(beta, communityRes.community_view.community.ap_id),
|
||||
c => c.community?.community.id != undefined,
|
||||
);
|
||||
let betaCommunity = resolvedCommunity.community;
|
||||
expect(betaCommunity?.community.actor_id).toBe(
|
||||
communityRes.community_view.community.actor_id,
|
||||
expect(betaCommunity?.community.ap_id).toBe(
|
||||
communityRes.community_view.community.ap_id,
|
||||
);
|
||||
|
||||
await longDelay();
|
||||
|
@ -529,7 +527,7 @@ test("Content in local-only community doesn't federate", async () => {
|
|||
|
||||
// cant resolve the community from another instance
|
||||
await expect(
|
||||
resolveCommunity(beta, communityRes.actor_id),
|
||||
resolveCommunity(beta, communityRes.ap_id),
|
||||
).rejects.toStrictEqual(Error("not_found"));
|
||||
|
||||
// create a post, also cant resolve it
|
||||
|
@ -544,7 +542,7 @@ test("Remote mods can edit communities", async () => {
|
|||
|
||||
let betaCommunity = await resolveCommunity(
|
||||
beta,
|
||||
communityRes.community_view.community.actor_id,
|
||||
communityRes.community_view.community.ap_id,
|
||||
);
|
||||
if (!betaCommunity.community) {
|
||||
throw "Missing beta community";
|
||||
|
@ -583,7 +581,7 @@ test("Community name with non-ascii chars", async () => {
|
|||
|
||||
let betaCommunity1 = await resolveCommunity(
|
||||
beta,
|
||||
communityRes.community_view.community.actor_id,
|
||||
communityRes.community_view.community.ap_id,
|
||||
);
|
||||
expect(betaCommunity1.community!.community.name).toBe(name);
|
||||
|
||||
|
|
|
@ -27,21 +27,21 @@ test("Follow local community", async () => {
|
|||
// Make sure the follow response went through
|
||||
expect(follow.community_view.community.local).toBe(true);
|
||||
expect(follow.community_view.subscribed).toBe("Subscribed");
|
||||
expect(follow.community_view.counts.subscribers).toBe(
|
||||
community.counts.subscribers + 1,
|
||||
expect(follow.community_view.community.subscribers).toBe(
|
||||
community.community.subscribers + 1,
|
||||
);
|
||||
expect(follow.community_view.counts.subscribers_local).toBe(
|
||||
community.counts.subscribers_local + 1,
|
||||
expect(follow.community_view.community.subscribers_local).toBe(
|
||||
community.community.subscribers_local + 1,
|
||||
);
|
||||
|
||||
// Test an unfollow
|
||||
let unfollow = await followCommunity(user, false, community.community.id);
|
||||
expect(unfollow.community_view.subscribed).toBe("NotSubscribed");
|
||||
expect(unfollow.community_view.counts.subscribers).toBe(
|
||||
community.counts.subscribers,
|
||||
expect(unfollow.community_view.community.subscribers).toBe(
|
||||
community.community.subscribers,
|
||||
);
|
||||
expect(unfollow.community_view.counts.subscribers_local).toBe(
|
||||
community.counts.subscribers_local,
|
||||
expect(unfollow.community_view.community.subscribers_local).toBe(
|
||||
community.community.subscribers_local,
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -51,7 +51,7 @@ test("Follow federated community", async () => {
|
|||
const betaCommunityInitial = (
|
||||
await waitUntil(
|
||||
() => resolveBetaCommunity(alpha),
|
||||
c => !!c.community && c.community?.counts.subscribers >= 1,
|
||||
c => !!c.community && c.community?.community.subscribers >= 1,
|
||||
)
|
||||
).community;
|
||||
if (!betaCommunityInitial) {
|
||||
|
@ -74,14 +74,14 @@ test("Follow federated community", async () => {
|
|||
expect(betaCommunity?.community.local).toBe(false);
|
||||
expect(betaCommunity?.community.name).toBe("main");
|
||||
expect(betaCommunity?.subscribed).toBe("Subscribed");
|
||||
expect(betaCommunity?.counts.subscribers_local).toBe(
|
||||
betaCommunityInitial.counts.subscribers_local + 1,
|
||||
expect(betaCommunity?.community.subscribers_local).toBe(
|
||||
betaCommunityInitial.community.subscribers_local + 1,
|
||||
);
|
||||
|
||||
// check that unfollow was federated
|
||||
let communityOnBeta1 = await resolveBetaCommunity(beta);
|
||||
expect(communityOnBeta1.community?.counts.subscribers).toBe(
|
||||
betaCommunityInitial.counts.subscribers + 1,
|
||||
expect(communityOnBeta1.community?.community.subscribers).toBe(
|
||||
betaCommunityInitial.community.subscribers + 1,
|
||||
);
|
||||
|
||||
// Check it from local
|
||||
|
@ -113,11 +113,11 @@ test("Follow federated community", async () => {
|
|||
let communityOnBeta2 = await waitUntil(
|
||||
() => resolveBetaCommunity(beta),
|
||||
c =>
|
||||
c.community?.counts.subscribers ===
|
||||
betaCommunityInitial.counts.subscribers,
|
||||
c.community?.community.subscribers ===
|
||||
betaCommunityInitial.community.subscribers,
|
||||
);
|
||||
expect(communityOnBeta2.community?.counts.subscribers).toBe(
|
||||
betaCommunityInitial.counts.subscribers,
|
||||
expect(communityOnBeta2.community?.community.subscribers).toBe(
|
||||
betaCommunityInitial.community.subscribers,
|
||||
);
|
||||
expect(communityOnBeta2.community?.counts.subscribers_local).toBe(1);
|
||||
expect(communityOnBeta2.community?.community.subscribers_local).toBe(1);
|
||||
});
|
||||
|
|
|
@ -75,7 +75,7 @@ test("Upload image and delete it", async () => {
|
|||
expect(listAllMediaRes.images.length).toBe(previousThumbnails);
|
||||
|
||||
// Make sure the uploader is correct
|
||||
expect(listMediaRes.images[0].person.actor_id).toBe(
|
||||
expect(listMediaRes.images[0].person.ap_id).toBe(
|
||||
`http://lemmy-alpha:8541/u/lemmy_alpha`,
|
||||
);
|
||||
|
||||
|
@ -268,7 +268,7 @@ test("No image proxying if setting is disabled", async () => {
|
|||
let community = await createCommunity(alpha);
|
||||
let betaCommunity = await resolveCommunity(
|
||||
beta,
|
||||
community.community_view.community.actor_id,
|
||||
community.community_view.community.ap_id,
|
||||
);
|
||||
await followCommunity(beta, true, betaCommunity.community!.community.id);
|
||||
|
||||
|
|
|
@ -39,16 +39,20 @@ import {
|
|||
listReports,
|
||||
getMyUser,
|
||||
listInbox,
|
||||
getModlog,
|
||||
} from "./shared";
|
||||
import { PostView } from "lemmy-js-client/dist/types/PostView";
|
||||
import { AdminBlockInstanceParams } from "lemmy-js-client/dist/types/AdminBlockInstanceParams";
|
||||
import {
|
||||
AddModToCommunity,
|
||||
EditSite,
|
||||
EditPost,
|
||||
PersonPostMentionView,
|
||||
PostReport,
|
||||
PostReportView,
|
||||
ReportCombinedView,
|
||||
ResolveObject,
|
||||
ResolvePostReport,
|
||||
} from "lemmy-js-client";
|
||||
|
||||
let betaCommunity: CommunityView | undefined;
|
||||
|
@ -57,6 +61,11 @@ beforeAll(async () => {
|
|||
await setupLogins();
|
||||
betaCommunity = (await resolveBetaCommunity(alpha)).community;
|
||||
expect(betaCommunity).toBeDefined();
|
||||
|
||||
// Hack: Force outgoing federation queue for beta to be created on epsilon,
|
||||
// otherwise report test fails
|
||||
let person = await resolvePerson(epsilon, "@lemmy_beta@lemmy-beta:8551");
|
||||
expect(person.person).toBeDefined();
|
||||
});
|
||||
|
||||
afterAll(unfollows);
|
||||
|
@ -89,7 +98,7 @@ async function assertPostFederation(
|
|||
expect(postOne?.post.embed_description).toBe(postTwo?.post.embed_description);
|
||||
expect(postOne?.post.embed_video_url).toBe(postTwo?.post.embed_video_url);
|
||||
expect(postOne?.post.published).toBe(postTwo?.post.published);
|
||||
expect(postOne?.community.actor_id).toBe(postTwo?.community.actor_id);
|
||||
expect(postOne?.community.ap_id).toBe(postTwo?.community.ap_id);
|
||||
expect(postOne?.post.locked).toBe(postTwo?.post.locked);
|
||||
expect(postOne?.post.removed).toBe(postTwo?.post.removed);
|
||||
expect(postOne?.post.deleted).toBe(postTwo?.post.deleted);
|
||||
|
@ -116,19 +125,19 @@ test("Create a post", async () => {
|
|||
expect(postRes.post_view.post).toBeDefined();
|
||||
expect(postRes.post_view.community.local).toBe(false);
|
||||
expect(postRes.post_view.creator.local).toBe(true);
|
||||
expect(postRes.post_view.counts.score).toBe(1);
|
||||
expect(postRes.post_view.post.score).toBe(1);
|
||||
|
||||
// Make sure that post is liked on beta
|
||||
const betaPost = await waitForPost(
|
||||
beta,
|
||||
postRes.post_view.post,
|
||||
res => res?.counts.score === 1,
|
||||
res => res?.post.score === 1,
|
||||
);
|
||||
|
||||
expect(betaPost).toBeDefined();
|
||||
expect(betaPost?.community.local).toBe(true);
|
||||
expect(betaPost?.creator.local).toBe(false);
|
||||
expect(betaPost?.counts.score).toBe(1);
|
||||
expect(betaPost?.post.score).toBe(1);
|
||||
await assertPostFederation(betaPost, postRes.post_view);
|
||||
|
||||
// Delta only follows beta, so it should not see an alpha ap_id
|
||||
|
@ -156,23 +165,23 @@ test("Unlike a post", async () => {
|
|||
}
|
||||
let postRes = await createPost(alpha, betaCommunity.community.id);
|
||||
let unlike = await likePost(alpha, 0, postRes.post_view.post);
|
||||
expect(unlike.post_view.counts.score).toBe(0);
|
||||
expect(unlike.post_view.post.score).toBe(0);
|
||||
|
||||
// Try to unlike it again, make sure it stays at 0
|
||||
let unlike2 = await likePost(alpha, 0, postRes.post_view.post);
|
||||
expect(unlike2.post_view.counts.score).toBe(0);
|
||||
expect(unlike2.post_view.post.score).toBe(0);
|
||||
|
||||
// Make sure that post is unliked on beta
|
||||
const betaPost = await waitForPost(
|
||||
beta,
|
||||
postRes.post_view.post,
|
||||
post => post?.counts.score === 0,
|
||||
post => post?.post.score === 0,
|
||||
);
|
||||
|
||||
expect(betaPost).toBeDefined();
|
||||
expect(betaPost?.community.local).toBe(true);
|
||||
expect(betaPost?.creator.local).toBe(false);
|
||||
expect(betaPost?.counts.score).toBe(0);
|
||||
expect(betaPost?.post.score).toBe(0);
|
||||
await assertPostFederation(betaPost, postRes.post_view);
|
||||
});
|
||||
|
||||
|
@ -253,7 +262,7 @@ test("Collection of featured posts gets federated", async () => {
|
|||
// fetch the community, ensure that post is also fetched and marked as featured
|
||||
let betaCommunity = await resolveCommunity(
|
||||
beta,
|
||||
community.community_view.community.actor_id,
|
||||
community.community_view.community.ap_id,
|
||||
);
|
||||
expect(betaCommunity).toBeDefined();
|
||||
|
||||
|
@ -359,7 +368,7 @@ test("Remove a post from admin and community on different instance", async () =>
|
|||
}
|
||||
|
||||
let gammaCommunity = (
|
||||
await resolveCommunity(gamma, betaCommunity.community.actor_id)
|
||||
await resolveCommunity(gamma, betaCommunity.community.ap_id)
|
||||
).community?.community;
|
||||
if (!gammaCommunity) {
|
||||
throw "Missing gamma community";
|
||||
|
@ -398,7 +407,7 @@ test("Remove a post from admin and community on same instance", async () => {
|
|||
await followBeta(alpha);
|
||||
let gammaCommunity = await resolveCommunity(
|
||||
gamma,
|
||||
betaCommunity.community.actor_id,
|
||||
betaCommunity.community.ap_id,
|
||||
);
|
||||
let postRes = await createPost(gamma, gammaCommunity.community!.community.id);
|
||||
expect(postRes.post_view.post).toBeDefined();
|
||||
|
@ -460,7 +469,7 @@ test("Enforce site ban federation for local user", async () => {
|
|||
// create a test user
|
||||
let alphaUserHttp = await registerUser(alpha, alphaUrl);
|
||||
let alphaUserPerson = (await getMyUser(alphaUserHttp)).local_user_view.person;
|
||||
let alphaUserActorId = alphaUserPerson?.actor_id;
|
||||
let alphaUserActorId = alphaUserPerson?.ap_id;
|
||||
if (!alphaUserActorId) {
|
||||
throw "Missing alpha user actor id";
|
||||
}
|
||||
|
@ -540,7 +549,7 @@ test("Enforce site ban federation for federated user", async () => {
|
|||
// create a test user
|
||||
let alphaUserHttp = await registerUser(alpha, alphaUrl);
|
||||
let alphaUserPerson = (await getMyUser(alphaUserHttp)).local_user_view.person;
|
||||
let alphaUserActorId = alphaUserPerson?.actor_id;
|
||||
let alphaUserActorId = alphaUserPerson?.ap_id;
|
||||
if (!alphaUserActorId) {
|
||||
throw "Missing alpha user actor id";
|
||||
}
|
||||
|
@ -644,14 +653,19 @@ test("Enforce community ban for federated user", async () => {
|
|||
);
|
||||
expect(unBanAlpha.banned).toBe(false);
|
||||
|
||||
// Need to re-follow the community
|
||||
await followBeta(alpha);
|
||||
// Check that unban was federated to alpha
|
||||
await waitUntil(
|
||||
() => getModlog(alpha),
|
||||
m =>
|
||||
m.modlog[0].type_ == "ModBanFromCommunity" &&
|
||||
m.modlog[0].mod_ban_from_community.banned == false,
|
||||
);
|
||||
|
||||
let postRes3 = await createPost(alpha, betaCommunity.community.id);
|
||||
expect(postRes3.post_view.post).toBeDefined();
|
||||
expect(postRes3.post_view.community.local).toBe(false);
|
||||
expect(postRes3.post_view.creator.local).toBe(true);
|
||||
expect(postRes3.post_view.counts.score).toBe(1);
|
||||
expect(postRes3.post_view.post.score).toBe(1);
|
||||
|
||||
// Make sure that post makes it to beta community
|
||||
let postRes4 = await waitForPost(beta, postRes3.post_view.post);
|
||||
|
@ -679,16 +693,26 @@ test("Report a post", async () => {
|
|||
// Create post from alpha
|
||||
let alphaCommunity = (await resolveBetaCommunity(alpha)).community!;
|
||||
await followBeta(alpha);
|
||||
let postRes = await createPost(alpha, alphaCommunity.community.id);
|
||||
expect(postRes.post_view.post).toBeDefined();
|
||||
let alphaPost = await createPost(alpha, alphaCommunity.community.id);
|
||||
expect(alphaPost.post_view.post).toBeDefined();
|
||||
|
||||
let alphaPost = (await resolvePost(alpha, postRes.post_view.post)).post;
|
||||
if (!alphaPost) {
|
||||
throw "Missing alpha post";
|
||||
}
|
||||
// add remote mod on epsilon
|
||||
await followBeta(epsilon);
|
||||
|
||||
let betaCommunity = (await resolveBetaCommunity(beta)).community!;
|
||||
let epsilonUser = (
|
||||
await resolvePerson(beta, "@lemmy_epsilon@lemmy-epsilon:8581")
|
||||
).person!;
|
||||
let mod_params: AddModToCommunity = {
|
||||
community_id: betaCommunity.community.id,
|
||||
person_id: epsilonUser.person.id,
|
||||
added: true,
|
||||
};
|
||||
let res = await beta.addModToCommunity(mod_params);
|
||||
expect(res.moderators.length).toBe(2);
|
||||
|
||||
// Send report from gamma
|
||||
let gammaPost = (await resolvePost(gamma, alphaPost.post)).post!;
|
||||
let gammaPost = (await resolvePost(gamma, alphaPost.post_view.post)).post!;
|
||||
let gammaReport = (
|
||||
await reportPost(gamma, gammaPost.post.id, randomString(10))
|
||||
).post_report_view.post_report;
|
||||
|
@ -714,11 +738,12 @@ test("Report a post", async () => {
|
|||
expect(betaReport.reason).toBe(gammaReport.reason);
|
||||
await unfollowRemotes(alpha);
|
||||
|
||||
// Report was federated to poster's instance
|
||||
// Report was federated to poster's instance. Alpha is not a community mod and doesnt see
|
||||
// the report by default, so we need to pass show_mod_reports = true.
|
||||
let alphaReport = (
|
||||
(await waitUntil(
|
||||
() =>
|
||||
listReports(alpha).then(p =>
|
||||
listReports(alpha, true).then(p =>
|
||||
p.reports.find(r => {
|
||||
return checkPostReportName(r, gammaReport);
|
||||
}),
|
||||
|
@ -732,6 +757,45 @@ test("Report a post", async () => {
|
|||
//expect(alphaReport.original_post_url).toBe(gammaReport.original_post_url);
|
||||
expect(alphaReport.original_post_body).toBe(gammaReport.original_post_body);
|
||||
expect(alphaReport.reason).toBe(gammaReport.reason);
|
||||
|
||||
// Report was federated to remote mod instance
|
||||
let epsilonReport = (
|
||||
(await waitUntil(
|
||||
() =>
|
||||
listReports(epsilon).then(p =>
|
||||
p.reports.find(r => {
|
||||
return checkPostReportName(r, gammaReport);
|
||||
}),
|
||||
),
|
||||
res => !!res,
|
||||
))! as PostReportView
|
||||
).post_report;
|
||||
expect(epsilonReport).toBeDefined();
|
||||
expect(epsilonReport.resolved).toBe(false);
|
||||
expect(epsilonReport.original_post_name).toBe(gammaReport.original_post_name);
|
||||
|
||||
// Resolve report as remote mod
|
||||
let resolve_params: ResolvePostReport = {
|
||||
report_id: epsilonReport.id,
|
||||
resolved: true,
|
||||
};
|
||||
let resolve = await epsilon.resolvePostReport(resolve_params);
|
||||
expect(resolve.post_report_view.post_report.resolved).toBeTruthy();
|
||||
|
||||
// Report should be marked resolved on community instance
|
||||
let resolvedReport = (
|
||||
(await waitUntil(
|
||||
() =>
|
||||
listReports(beta).then(p =>
|
||||
p.reports.find(r => {
|
||||
return checkPostReportName(r, gammaReport) && r.resolver != null;
|
||||
}),
|
||||
),
|
||||
res => !!res,
|
||||
))! as PostReportView
|
||||
).post_report;
|
||||
expect(resolvedReport).toBeDefined();
|
||||
expect(resolvedReport.resolved).toBe(true);
|
||||
});
|
||||
|
||||
test("Fetch post via redirect", async () => {
|
||||
|
@ -742,7 +806,7 @@ test("Fetch post via redirect", async () => {
|
|||
const betaPost = await waitForPost(
|
||||
beta,
|
||||
alphaPost.post_view.post,
|
||||
res => res?.counts.score === 1,
|
||||
res => res?.post.score === 1,
|
||||
);
|
||||
|
||||
expect(betaPost).toBeDefined();
|
||||
|
@ -815,7 +879,7 @@ test("Mention beta from alpha post body", async () => {
|
|||
expect(postOnAlphaRes.post_view.post.body).toBeDefined();
|
||||
expect(postOnAlphaRes.post_view.community.local).toBe(false);
|
||||
expect(postOnAlphaRes.post_view.creator.local).toBe(true);
|
||||
expect(postOnAlphaRes.post_view.counts.score).toBe(1);
|
||||
expect(postOnAlphaRes.post_view.post.score).toBe(1);
|
||||
|
||||
// get beta's localized copy of the alpha post
|
||||
let betaPost = await waitForPost(beta, postOnAlphaRes.post_view.post);
|
||||
|
@ -835,7 +899,7 @@ test("Mention beta from alpha post body", async () => {
|
|||
expect(firstMention.post.body).toBeDefined();
|
||||
expect(firstMention.community.local).toBe(true);
|
||||
expect(firstMention.creator.local).toBe(false);
|
||||
expect(firstMention.counts.score).toBe(1);
|
||||
expect(firstMention.post.score).toBe(1);
|
||||
expect(firstMention.person_post_mention.post_id).toBe(betaPost.post.id);
|
||||
});
|
||||
|
||||
|
@ -852,7 +916,6 @@ test("Rewrite markdown links", async () => {
|
|||
"https://example.com/",
|
||||
`[link](${postRes1.post_view.post.ap_id})`,
|
||||
);
|
||||
console.log(postRes2.post_view.post.body);
|
||||
expect(postRes2.post_view.post).toBeDefined();
|
||||
|
||||
// fetch both posts from another instance
|
||||
|
@ -865,6 +928,50 @@ test("Rewrite markdown links", async () => {
|
|||
);
|
||||
});
|
||||
|
||||
test("Don't allow NSFW posts on instances that disable it", async () => {
|
||||
// Disallow NSFW on gamma
|
||||
let editSiteForm: EditSite = {
|
||||
disallow_nsfw_content: true,
|
||||
};
|
||||
await gamma.editSite(editSiteForm);
|
||||
|
||||
// Wait for cache on Gamma's LocalSite
|
||||
await delay(1_000);
|
||||
|
||||
if (!betaCommunity) {
|
||||
throw "Missing beta community";
|
||||
}
|
||||
|
||||
// Make a NSFW post
|
||||
let postRes = await createPost(beta, betaCommunity.community.id);
|
||||
let form: EditPost = {
|
||||
nsfw: true,
|
||||
post_id: postRes.post_view.post.id,
|
||||
};
|
||||
let updatePost = await beta.editPost(form);
|
||||
|
||||
// Gamma reject resolving the post
|
||||
await expect(
|
||||
resolvePost(gamma, updatePost.post_view.post),
|
||||
).rejects.toStrictEqual(Error("not_found"));
|
||||
|
||||
// Local users can't create NSFW post on Gamma
|
||||
let gammaCommunity = (
|
||||
await resolveCommunity(gamma, betaCommunity.community.ap_id)
|
||||
).community?.community;
|
||||
if (!gammaCommunity) {
|
||||
throw "Missing gamma community";
|
||||
}
|
||||
let gammaPost = await createPost(gamma, gammaCommunity.id);
|
||||
let form2: EditPost = {
|
||||
nsfw: true,
|
||||
post_id: gammaPost.post_view.post.id,
|
||||
};
|
||||
await expect(gamma.editPost(form2)).rejects.toStrictEqual(
|
||||
Error("nsfw_not_allowed"),
|
||||
);
|
||||
});
|
||||
|
||||
function checkPostReportName(rcv: ReportCombinedView, report: PostReport) {
|
||||
switch (rcv.type_) {
|
||||
case "Post":
|
||||
|
|
|
@ -47,7 +47,7 @@ test("Follow a private community", async () => {
|
|||
// follow as new user
|
||||
const user = await registerUser(beta, betaUrl);
|
||||
const betaCommunity = (
|
||||
await resolveCommunity(user, community.community_view.community.actor_id)
|
||||
await resolveCommunity(user, community.community_view.community.ap_id)
|
||||
).community;
|
||||
expect(betaCommunity).toBeDefined();
|
||||
expect(betaCommunity?.community.visibility).toBe("Private");
|
||||
|
@ -134,7 +134,7 @@ test("Only followers can view and interact with private community content", asyn
|
|||
// user is not following the community and cannot view nor create posts
|
||||
const user = await registerUser(beta, betaUrl);
|
||||
const betaCommunity = (
|
||||
await resolveCommunity(user, community.community_view.community.actor_id)
|
||||
await resolveCommunity(user, community.community_view.community.ap_id)
|
||||
).community!.community;
|
||||
await expect(resolvePost(user, post0.post_view.post)).rejects.toStrictEqual(
|
||||
Error("not_found"),
|
||||
|
@ -179,7 +179,7 @@ test("Reject follower", async () => {
|
|||
// user is not following the community and cannot view nor create posts
|
||||
const user = await registerUser(beta, betaUrl);
|
||||
const betaCommunity1 = (
|
||||
await resolveCommunity(user, community.community_view.community.actor_id)
|
||||
await resolveCommunity(user, community.community_view.community.ap_id)
|
||||
).community!.community;
|
||||
|
||||
// follow the community and reject
|
||||
|
@ -216,7 +216,7 @@ test("Follow a private community and receive activities", async () => {
|
|||
|
||||
// follow with users from beta and gamma
|
||||
const betaCommunity = (
|
||||
await resolveCommunity(beta, community.community_view.community.actor_id)
|
||||
await resolveCommunity(beta, community.community_view.community.ap_id)
|
||||
).community;
|
||||
expect(betaCommunity).toBeDefined();
|
||||
const betaCommunityId = betaCommunity!.community.id;
|
||||
|
@ -228,7 +228,7 @@ test("Follow a private community and receive activities", async () => {
|
|||
await approveFollower(alpha, alphaCommunityId);
|
||||
|
||||
const gammaCommunityId = (
|
||||
await resolveCommunity(gamma, community.community_view.community.actor_id)
|
||||
await resolveCommunity(gamma, community.community_view.community.ap_id)
|
||||
).community!.community.id;
|
||||
const follow_form_gamma: FollowCommunity = {
|
||||
community_id: gammaCommunityId,
|
||||
|
@ -281,7 +281,7 @@ test("Fetch remote content in private community", async () => {
|
|||
const alphaCommunityId = community.community_view.community.id;
|
||||
|
||||
const betaCommunityId = (
|
||||
await resolveCommunity(beta, community.community_view.community.actor_id)
|
||||
await resolveCommunity(beta, community.community_view.community.ap_id)
|
||||
).community!.community.id;
|
||||
const follow_form_beta: FollowCommunity = {
|
||||
community_id: betaCommunityId,
|
||||
|
@ -312,7 +312,7 @@ test("Fetch remote content in private community", async () => {
|
|||
|
||||
// create gamma user
|
||||
const gammaCommunityId = (
|
||||
await resolveCommunity(gamma, community.community_view.community.actor_id)
|
||||
await resolveCommunity(gamma, community.community_view.community.ap_id)
|
||||
).community!.community.id;
|
||||
const follow_form: FollowCommunity = {
|
||||
community_id: gammaCommunityId,
|
||||
|
|
|
@ -27,6 +27,8 @@ import {
|
|||
ListInboxResponse,
|
||||
ListInbox,
|
||||
InboxDataType,
|
||||
GetModlogResponse,
|
||||
GetModlog,
|
||||
} from "lemmy-js-client";
|
||||
import { CreatePost } from "lemmy-js-client/dist/types/CreatePost";
|
||||
import { DeletePost } from "lemmy-js-client/dist/types/DeletePost";
|
||||
|
@ -83,6 +85,7 @@ import { GetPosts } from "lemmy-js-client/dist/types/GetPosts";
|
|||
import { GetPersonDetailsResponse } from "lemmy-js-client/dist/types/GetPersonDetailsResponse";
|
||||
import { GetPersonDetails } from "lemmy-js-client/dist/types/GetPersonDetails";
|
||||
import { ListingType } from "lemmy-js-client/dist/types/ListingType";
|
||||
import { GetCommunityPendingFollowsCountI } from "lemmy-js-client/dist/other_types";
|
||||
|
||||
export const fetchFunction = fetch;
|
||||
export const imageFetchLimit = 50;
|
||||
|
@ -199,7 +202,7 @@ export async function setupLogins() {
|
|||
}
|
||||
}
|
||||
|
||||
async function allowInstance(api: LemmyHttp, instance: string) {
|
||||
export async function allowInstance(api: LemmyHttp, instance: string) {
|
||||
const params: AdminAllowInstanceParams = {
|
||||
instance,
|
||||
allow: true,
|
||||
|
@ -324,9 +327,8 @@ export async function searchPostLocal(
|
|||
post: Post,
|
||||
): Promise<SearchResponse> {
|
||||
let form: Search = {
|
||||
q: post.name,
|
||||
search_term: post.name,
|
||||
type_: "Posts",
|
||||
sort: "TopAll",
|
||||
listing_type: "All",
|
||||
};
|
||||
return api.search(form);
|
||||
|
@ -339,7 +341,7 @@ export async function waitForPost(
|
|||
checker: (t: PostView | undefined) => boolean = p => !!p,
|
||||
) {
|
||||
return waitUntil<PostView>(
|
||||
() => searchPostLocal(api, post).then(p => p.posts[0]),
|
||||
() => searchPostLocal(api, post).then(p => p.results[0] as PostView),
|
||||
checker,
|
||||
);
|
||||
}
|
||||
|
@ -801,8 +803,9 @@ export async function reportPost(
|
|||
|
||||
export async function listReports(
|
||||
api: LemmyHttp,
|
||||
show_community_rule_violations: boolean = false,
|
||||
): Promise<ListReportsResponse> {
|
||||
let form: ListReports = {};
|
||||
let form: ListReports = { show_community_rule_violations };
|
||||
return api.listReports(form);
|
||||
}
|
||||
|
||||
|
@ -883,7 +886,8 @@ export function getCommunityPendingFollowsCount(
|
|||
api: LemmyHttp,
|
||||
community_id: CommunityId,
|
||||
): Promise<GetCommunityPendingFollowsCountResponse> {
|
||||
return api.getCommunityPendingFollowsCount(community_id);
|
||||
let form: GetCommunityPendingFollowsCountI = { community_id };
|
||||
return api.getCommunityPendingFollowsCount(form);
|
||||
}
|
||||
|
||||
export function approveCommunityPendingFollow(
|
||||
|
@ -899,6 +903,10 @@ export function approveCommunityPendingFollow(
|
|||
};
|
||||
return api.approveCommunityPendingFollow(form);
|
||||
}
|
||||
export function getModlog(api: LemmyHttp): Promise<GetModlogResponse> {
|
||||
let form: GetModlog = {};
|
||||
return api.getModlog(form);
|
||||
}
|
||||
|
||||
export function delay(millis = 500) {
|
||||
return new Promise(resolve => setTimeout(resolve, millis));
|
||||
|
|
|
@ -41,7 +41,7 @@ function assertUserFederation(userOne?: PersonView, userTwo?: PersonView) {
|
|||
expect(userOne?.person.name).toBe(userTwo?.person.name);
|
||||
expect(userOne?.person.display_name).toBe(userTwo?.person.display_name);
|
||||
expect(userOne?.person.bio).toBe(userTwo?.person.bio);
|
||||
expect(userOne?.person.actor_id).toBe(userTwo?.person.actor_id);
|
||||
expect(userOne?.person.ap_id).toBe(userTwo?.person.ap_id);
|
||||
expect(userOne?.person.avatar).toBe(userTwo?.person.avatar);
|
||||
expect(userOne?.person.banner).toBe(userTwo?.person.banner);
|
||||
expect(userOne?.person.published).toBe(userTwo?.person.published);
|
||||
|
@ -181,7 +181,7 @@ test("Create user with accept-language", async () => {
|
|||
.map(l => l.code);
|
||||
// should have languages from accept header, as well as "undetermined"
|
||||
// which is automatically enabled by backend
|
||||
expect(langs).toStrictEqual(["und", "de", "en", "fr"]);
|
||||
expect(langs).toStrictEqual(["de", "en", "fr"]);
|
||||
});
|
||||
|
||||
test("Set a new avatar, old avatar is deleted", async () => {
|
||||
|
|
|
@ -59,27 +59,25 @@
|
|||
upload_timeout: 30
|
||||
# Resize post thumbnails to this maximum width/height.
|
||||
max_thumbnail_size: 512
|
||||
# Maximum size for user avatar, community icon and site icon.
|
||||
# Maximum size for user avatar, community icon and site icon. Larger images are downscaled.
|
||||
max_avatar_size: 512
|
||||
# Maximum size for user, community and site banner. Larger images are downscaled to fit
|
||||
# into a square of this size.
|
||||
# Maximum size for user, community and site banner. Larger images are downscaled.
|
||||
max_banner_size: 1024
|
||||
# Maximum size for other uploads (e.g. post images or markdown embed images). Larger
|
||||
# images are downscaled.
|
||||
max_upload_size: 1024
|
||||
# Whether users can upload videos as post image or markdown embed.
|
||||
allow_video_uploads: true
|
||||
# Prevent users from uploading images for posts or embedding in markdown. Avatars, icons and
|
||||
# banners can still be uploaded.
|
||||
image_upload_disabled: false
|
||||
}
|
||||
# Email sending configuration. All options except login/password are mandatory
|
||||
email: {
|
||||
# Hostname and port of the smtp server
|
||||
smtp_server: "localhost:25"
|
||||
# Login name for smtp server
|
||||
smtp_login: "string"
|
||||
# Password to login to the smtp server
|
||||
smtp_password: "string"
|
||||
# https://docs.rs/lettre/0.11.14/lettre/transport/smtp/struct.AsyncSmtpTransport.html#method.from_url
|
||||
connection: "smtps://user:pass@hostname:port"
|
||||
# Address to send emails from, eg "noreply@your-instance.com"
|
||||
smtp_from_address: "noreply@example.com"
|
||||
# Whether or not smtp connections should use tls. Can be none, tls, or starttls
|
||||
tls_type: "none"
|
||||
}
|
||||
# Parameters for automatic configuration of new instance (only used at first start)
|
||||
setup: {
|
||||
|
@ -110,7 +108,13 @@
|
|||
bind: "127.0.0.1"
|
||||
port: 10002
|
||||
}
|
||||
# Sets a response Access-Control-Allow-Origin CORS header
|
||||
# Sets a response Access-Control-Allow-Origin CORS header. Can also be set via environment:
|
||||
# `LEMMY_CORS_ORIGIN=example.org,site.com`
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
|
||||
cors_origin: "lemmy.tld"
|
||||
cors_origin: [
|
||||
"lemmy.tld"
|
||||
/* ... */
|
||||
]
|
||||
# Print logs in JSON format. You can also disable ANSI colors in logs with env var `NO_COLOR`.
|
||||
json_logging: false
|
||||
}
|
||||
|
|
|
@ -21,8 +21,6 @@ workspace = true
|
|||
lemmy_utils = { workspace = true }
|
||||
lemmy_db_schema = { workspace = true, features = ["full"] }
|
||||
lemmy_db_views = { workspace = true, features = ["full"] }
|
||||
lemmy_db_views_moderator = { workspace = true, features = ["full"] }
|
||||
lemmy_db_views_actor = { workspace = true, features = ["full"] }
|
||||
lemmy_api_common = { workspace = true, features = ["full"] }
|
||||
activitypub_federation = { workspace = true }
|
||||
bcrypt = { workspace = true }
|
||||
|
@ -33,10 +31,10 @@ anyhow = { workspace = true }
|
|||
tracing = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
url = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
hound = "3.5.1"
|
||||
sitemap-rs = "0.2.2"
|
||||
totp-rs = { version = "5.6.0", features = ["gen_secret", "otpauth"] }
|
||||
actix-web-httpauth = "0.8.2"
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = { workspace = true }
|
||||
|
|
|
@ -11,7 +11,6 @@ use lemmy_db_schema::{
|
|||
use lemmy_db_views::structs::{CommentView, LocalUserView};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn distinguish_comment(
|
||||
data: Json<DistinguishComment>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -20,7 +20,6 @@ use lemmy_db_views::structs::{CommentView, LocalUserView};
|
|||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
use std::ops::Deref;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn like_comment(
|
||||
data: Json<CreateCommentLike>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -8,7 +8,6 @@ use lemmy_db_views::structs::{CommentView, LocalUserView, VoteView};
|
|||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
/// Lists likes for a comment
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn list_comment_likes(
|
||||
data: Query<ListCommentLikes>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -10,7 +10,6 @@ use lemmy_db_schema::{
|
|||
use lemmy_db_views::structs::{CommentView, LocalUserView};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn save_comment(
|
||||
data: Json<SaveComment>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -14,11 +14,9 @@ use lemmy_db_schema::{
|
|||
},
|
||||
traits::{Crud, Joinable},
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views_actor::structs::CommunityModeratorView;
|
||||
use lemmy_db_views::structs::{CommunityModeratorView, LocalUserView};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn add_mod_to_community(
|
||||
data: Json<AddModToCommunity>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -24,14 +24,12 @@ use lemmy_db_schema::{
|
|||
},
|
||||
traits::{Bannable, Crud, Followable},
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views_actor::structs::PersonView;
|
||||
use lemmy_db_views::structs::{LocalUserView, PersonView};
|
||||
use lemmy_utils::{
|
||||
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||
utils::validation::is_valid_body_field,
|
||||
};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn ban_from_community(
|
||||
data: Json<BanFromCommunity>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -12,11 +12,9 @@ use lemmy_db_schema::{
|
|||
},
|
||||
traits::{Blockable, Followable},
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views_actor::structs::CommunityView;
|
||||
use lemmy_db_views::structs::{CommunityView, LocalUserView};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn user_block_community(
|
||||
data: Json<BlockCommunity>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -14,11 +14,9 @@ use lemmy_db_schema::{
|
|||
traits::{Crud, Followable},
|
||||
CommunityVisibility,
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views_actor::structs::{CommunityPersonBanView, CommunityView};
|
||||
use lemmy_db_views::structs::{CommunityPersonBanView, CommunityView, LocalUserView};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn follow_community(
|
||||
data: Json<FollowCommunity>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -17,7 +17,6 @@ use lemmy_db_schema::{
|
|||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn hide_community(
|
||||
data: Json<HideCommunity>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -4,8 +4,7 @@ use lemmy_api_common::{
|
|||
context::LemmyContext,
|
||||
utils::is_mod_or_admin,
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views_actor::structs::CommunityFollowerView;
|
||||
use lemmy_db_views::structs::{CommunityFollowerView, LocalUserView};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
pub async fn get_pending_follows_count(
|
||||
|
|
|
@ -4,8 +4,7 @@ use lemmy_api_common::{
|
|||
context::LemmyContext,
|
||||
utils::check_community_mod_of_any_or_admin_action,
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views_actor::structs::CommunityFollowerView;
|
||||
use lemmy_db_views::structs::{CommunityFollowerView, LocalUserView};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
pub async fn get_pending_follows_list(
|
||||
|
|
|
@ -10,11 +10,9 @@ use lemmy_db_schema::source::{
|
|||
community::Community,
|
||||
local_site::LocalSite,
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views_actor::structs::CommunityView;
|
||||
use lemmy_db_views::structs::{CommunityView, LocalUserView};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn get_random_community(
|
||||
data: Query<GetRandomCommunity>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -12,8 +12,7 @@ use lemmy_db_schema::{
|
|||
},
|
||||
traits::{Crud, Joinable},
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
|
||||
use lemmy_db_views::structs::{CommunityModeratorView, CommunityView, LocalUserView};
|
||||
use lemmy_utils::{
|
||||
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||
location_info,
|
||||
|
@ -21,7 +20,7 @@ use lemmy_utils::{
|
|||
|
||||
// TODO: we dont do anything for federation here, it should be updated the next time the community
|
||||
// gets fetched. i hope we can get rid of the community creator role soon.
|
||||
#[tracing::instrument(skip(context))]
|
||||
|
||||
pub async fn transfer_community(
|
||||
data: Json<TransferCommunity>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
use activitypub_federation::config::Data;
|
||||
use actix_web::{http::header::Header, HttpRequest};
|
||||
use actix_web_httpauth::headers::authorization::{Authorization, Bearer};
|
||||
use base64::{engine::general_purpose::STANDARD_NO_PAD as base64, Engine};
|
||||
use captcha::Captcha;
|
||||
use lemmy_api_common::{
|
||||
claims::Claims,
|
||||
community::BanFromCommunity,
|
||||
context::LemmyContext,
|
||||
send_activity::{ActivityChannel, SendActivityData},
|
||||
utils::{check_expire_time, check_user_valid, local_site_to_slur_regex, AUTH_COOKIE_NAME},
|
||||
utils::check_expire_time,
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
|
@ -18,7 +15,6 @@ use lemmy_db_schema::{
|
|||
CommunityPersonBan,
|
||||
CommunityPersonBanForm,
|
||||
},
|
||||
local_site::LocalSite,
|
||||
mod_log::moderator::{ModBanFromCommunity, ModBanFromCommunityForm},
|
||||
person::Person,
|
||||
},
|
||||
|
@ -26,9 +22,10 @@ use lemmy_db_schema::{
|
|||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::{
|
||||
error::{LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult},
|
||||
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||
utils::slurs::check_slurs,
|
||||
};
|
||||
use regex::Regex;
|
||||
use std::io::Cursor;
|
||||
use totp_rs::{Secret, TOTP};
|
||||
|
||||
|
@ -82,9 +79,7 @@ pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> LemmyResult<String> {
|
|||
}
|
||||
|
||||
/// Check size of report
|
||||
pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> LemmyResult<()> {
|
||||
let slur_regex = &local_site_to_slur_regex(local_site);
|
||||
|
||||
pub(crate) fn check_report_reason(reason: &str, slur_regex: &Regex) -> LemmyResult<()> {
|
||||
check_slurs(reason, slur_regex)?;
|
||||
if reason.is_empty() {
|
||||
Err(LemmyErrorType::ReportReasonRequired)?
|
||||
|
@ -95,21 +90,6 @@ pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> Lemmy
|
|||
}
|
||||
}
|
||||
|
||||
pub fn read_auth_token(req: &HttpRequest) -> LemmyResult<Option<String>> {
|
||||
// Try reading jwt from auth header
|
||||
if let Ok(header) = Authorization::<Bearer>::parse(req) {
|
||||
Ok(Some(header.as_ref().token().to_string()))
|
||||
}
|
||||
// If that fails, try to read from cookie
|
||||
else if let Some(cookie) = &req.cookie(AUTH_COOKIE_NAME) {
|
||||
Ok(Some(cookie.value().to_string()))
|
||||
}
|
||||
// Otherwise, there's no auth
|
||||
else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn check_totp_2fa_valid(
|
||||
local_user_view: &LocalUserView,
|
||||
totp_token: &Option<String>,
|
||||
|
@ -164,7 +144,6 @@ fn build_totp_2fa(hostname: &str, username: &str, secret: &str) -> LemmyResult<T
|
|||
/// So when doing a site ban for a non-local user, you need to federate/send a
|
||||
/// community ban for every local community they've participated in.
|
||||
/// See https://github.com/LemmyNet/lemmy/issues/4118
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) async fn ban_nonlocal_user_from_local_communities(
|
||||
local_user_view: &LocalUserView,
|
||||
target: &Person,
|
||||
|
@ -243,20 +222,6 @@ pub(crate) async fn ban_nonlocal_user_from_local_communities(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn local_user_view_from_jwt(
|
||||
jwt: &str,
|
||||
context: &LemmyContext,
|
||||
) -> LemmyResult<LocalUserView> {
|
||||
let local_user_id = Claims::validate(jwt, context)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
|
||||
let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id).await?;
|
||||
check_user_valid(&local_user_view.person)?;
|
||||
|
||||
Ok(local_user_view)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
|
|
|
@ -11,11 +11,9 @@ use lemmy_db_schema::{
|
|||
},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views_actor::structs::PersonView;
|
||||
use lemmy_db_views::structs::{LocalUserView, PersonView};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn add_admin(
|
||||
data: Json<AddAdmin>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -16,14 +16,12 @@ use lemmy_db_schema::{
|
|||
},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views_actor::structs::PersonView;
|
||||
use lemmy_db_views::structs::{LocalUserView, PersonView};
|
||||
use lemmy_utils::{
|
||||
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||
utils::validation::is_valid_body_field,
|
||||
};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn ban_from_site(
|
||||
data: Json<BanPerson>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -7,11 +7,9 @@ use lemmy_db_schema::{
|
|||
source::person_block::{PersonBlock, PersonBlockForm},
|
||||
traits::Blockable,
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views_actor::structs::PersonView;
|
||||
use lemmy_db_views::structs::{LocalUserView, PersonView};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn user_block_person(
|
||||
data: Json<BlockPerson>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -13,7 +13,6 @@ use lemmy_db_schema::source::{local_user::LocalUser, login_token::LoginToken};
|
|||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn change_password(
|
||||
data: Json<ChangePassword>,
|
||||
req: HttpRequest,
|
||||
|
|
|
@ -12,7 +12,6 @@ use lemmy_db_schema::source::{
|
|||
};
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn change_password_after_reset(
|
||||
data: Json<PasswordChangeAfterReset>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -11,7 +11,6 @@ use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
|||
|
||||
/// Generate a new secret for two-factor-authentication. Afterwards you need to call [toggle_totp]
|
||||
/// to enable it. This can only be called if 2FA is currently disabled.
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn generate_totp_secret(
|
||||
local_user_view: LocalUserView,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -20,7 +20,6 @@ use lemmy_db_schema::source::{
|
|||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn get_captcha(context: Data<LemmyContext>) -> LemmyResult<HttpResponse> {
|
||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||
let mut res = HttpResponseBuilder::new(StatusCode::OK);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use actix_web::web::{Data, Json};
|
||||
use lemmy_api_common::{context::LemmyContext, person::BannedPersonsResponse, utils::is_admin};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views_actor::structs::PersonView;
|
||||
use lemmy_db_views::structs::{LocalUserView, PersonView};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
pub async fn list_banned_users(
|
||||
|
|
|
@ -6,7 +6,6 @@ use lemmy_api_common::{
|
|||
use lemmy_db_views::structs::{LocalImageView, LocalUserView};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn list_media(
|
||||
data: Query<ListMedia>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -5,13 +5,13 @@ use lemmy_api_common::{
|
|||
person::{ListPersonSaved, ListPersonSavedResponse},
|
||||
utils::check_private_instance,
|
||||
};
|
||||
use lemmy_db_schema::traits::PaginationCursorBuilder;
|
||||
use lemmy_db_views::{
|
||||
person_saved_combined_view::PersonSavedCombinedQuery,
|
||||
structs::{LocalUserView, SiteView},
|
||||
combined::person_saved_combined_view::PersonSavedCombinedQuery,
|
||||
structs::{LocalUserView, PersonSavedCombinedView, SiteView},
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn list_person_saved(
|
||||
data: Query<ListPersonSaved>,
|
||||
context: Data<LemmyContext>,
|
||||
|
@ -21,22 +21,21 @@ pub async fn list_person_saved(
|
|||
|
||||
check_private_instance(&Some(local_user_view.clone()), &local_site.local_site)?;
|
||||
|
||||
// parse pagination token
|
||||
let page_after = if let Some(pa) = &data.page_cursor {
|
||||
Some(pa.read(&mut context.pool()).await?)
|
||||
let cursor_data = if let Some(cursor) = &data.page_cursor {
|
||||
Some(PersonSavedCombinedView::from_cursor(cursor, &mut context.pool()).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let page_back = data.page_back;
|
||||
let type_ = data.type_;
|
||||
|
||||
let saved = PersonSavedCombinedQuery {
|
||||
type_,
|
||||
page_after,
|
||||
page_back,
|
||||
type_: data.type_,
|
||||
cursor_data,
|
||||
page_back: data.page_back,
|
||||
}
|
||||
.list(&mut context.pool(), &local_user_view)
|
||||
.await?;
|
||||
|
||||
Ok(Json(ListPersonSavedResponse { saved }))
|
||||
let next_page = saved.last().map(PaginationCursorBuilder::to_cursor);
|
||||
|
||||
Ok(Json(ListPersonSavedResponse { saved, next_page }))
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ use lemmy_api_common::{
|
|||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn login(
|
||||
data: Json<Login>,
|
||||
req: HttpRequest,
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
use crate::read_auth_token;
|
||||
use activitypub_federation::config::Data;
|
||||
use actix_web::{cookie::Cookie, HttpRequest, HttpResponse};
|
||||
use lemmy_api_common::{context::LemmyContext, utils::AUTH_COOKIE_NAME, SuccessResponse};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
utils::{read_auth_token, AUTH_COOKIE_NAME},
|
||||
SuccessResponse,
|
||||
};
|
||||
use lemmy_db_schema::source::login_token::LoginToken;
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn logout(
|
||||
req: HttpRequest,
|
||||
// require login
|
||||
|
|
|
@ -14,6 +14,7 @@ pub mod login;
|
|||
pub mod logout;
|
||||
pub mod notifications;
|
||||
pub mod report_count;
|
||||
pub mod resend_verification_email;
|
||||
pub mod reset_password;
|
||||
pub mod save_settings;
|
||||
pub mod update_totp;
|
||||
|
|
|
@ -3,38 +3,37 @@ use lemmy_api_common::{
|
|||
context::LemmyContext,
|
||||
person::{ListInbox, ListInboxResponse},
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views_actor::inbox_combined_view::InboxCombinedQuery;
|
||||
use lemmy_db_schema::traits::PaginationCursorBuilder;
|
||||
use lemmy_db_views::{
|
||||
combined::inbox_combined_view::InboxCombinedQuery,
|
||||
structs::{InboxCombinedView, LocalUserView},
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn list_inbox(
|
||||
data: Query<ListInbox>,
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
) -> LemmyResult<Json<ListInboxResponse>> {
|
||||
let unread_only = data.unread_only;
|
||||
let type_ = data.type_;
|
||||
let person_id = local_user_view.person.id;
|
||||
let show_bot_accounts = Some(local_user_view.local_user.show_bot_accounts);
|
||||
|
||||
// parse pagination token
|
||||
let page_after = if let Some(pa) = &data.page_cursor {
|
||||
Some(pa.read(&mut context.pool()).await?)
|
||||
let cursor_data = if let Some(cursor) = &data.page_cursor {
|
||||
Some(InboxCombinedView::from_cursor(cursor, &mut context.pool()).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let page_back = data.page_back;
|
||||
|
||||
let inbox = InboxCombinedQuery {
|
||||
type_,
|
||||
unread_only,
|
||||
show_bot_accounts,
|
||||
page_after,
|
||||
page_back,
|
||||
type_: data.type_,
|
||||
unread_only: data.unread_only,
|
||||
show_bot_accounts: Some(local_user_view.local_user.show_bot_accounts),
|
||||
cursor_data,
|
||||
page_back: data.page_back,
|
||||
}
|
||||
.list(&mut context.pool(), person_id)
|
||||
.await?;
|
||||
|
||||
Ok(Json(ListInboxResponse { inbox }))
|
||||
let next_page = inbox.last().map(PaginationCursorBuilder::to_cursor);
|
||||
|
||||
Ok(Json(ListInboxResponse { inbox, next_page }))
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ use lemmy_db_schema::source::{
|
|||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn mark_all_notifications_read(
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
|
|
|
@ -11,7 +11,6 @@ use lemmy_db_schema::{
|
|||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn mark_comment_mention_as_read(
|
||||
data: Json<MarkPersonCommentMentionAsRead>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -11,7 +11,6 @@ use lemmy_db_schema::{
|
|||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn mark_post_mention_as_read(
|
||||
data: Json<MarkPersonPostMentionAsRead>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -7,7 +7,6 @@ use lemmy_db_schema::{
|
|||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn mark_reply_as_read(
|
||||
data: Json<MarkCommentReplyAsRead>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
use actix_web::web::{Data, Json};
|
||||
use lemmy_api_common::{context::LemmyContext, person::GetUnreadCountResponse};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views_actor::structs::InboxCombinedViewInternal;
|
||||
use lemmy_db_views::structs::{InboxCombinedViewInternal, LocalUserView};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn unread_count(
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
|
|
|
@ -7,7 +7,6 @@ use lemmy_api_common::{
|
|||
use lemmy_db_views::structs::{LocalUserView, ReportCombinedViewInternal};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn report_count(
|
||||
data: Query<GetReportCount>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
30
crates/api/src/local_user/resend_verification_email.rs
Normal file
30
crates/api/src/local_user/resend_verification_email.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use actix_web::web::{Data, Json};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
person::ResendVerificationEmail,
|
||||
utils::send_verification_email_if_required,
|
||||
SuccessResponse,
|
||||
};
|
||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
pub async fn resend_verification_email(
|
||||
data: Json<ResendVerificationEmail>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<Json<SuccessResponse>> {
|
||||
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
||||
let email = data.email.to_string();
|
||||
|
||||
// Fetch that email
|
||||
let local_user_view = LocalUserView::find_by_email(&mut context.pool(), &email).await?;
|
||||
|
||||
send_verification_email_if_required(
|
||||
&context,
|
||||
&site_view.local_site,
|
||||
&local_user_view.local_user,
|
||||
&local_user_view.person,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(Json(SuccessResponse::default()))
|
||||
}
|
|
@ -9,7 +9,6 @@ use lemmy_db_views::structs::{LocalUserView, SiteView};
|
|||
use lemmy_utils::error::LemmyResult;
|
||||
use tracing::error;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn reset_password(
|
||||
data: Json<PasswordReset>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -3,23 +3,17 @@ use actix_web::web::Json;
|
|||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
person::SaveUserSettings,
|
||||
utils::{
|
||||
get_url_blocklist,
|
||||
local_site_to_slur_regex,
|
||||
process_markdown_opt,
|
||||
send_verification_email,
|
||||
},
|
||||
utils::{get_url_blocklist, process_markdown_opt, send_verification_email, slur_regex},
|
||||
SuccessResponse,
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
actor_language::LocalUserLanguage,
|
||||
local_user::{LocalUser, LocalUserUpdateForm},
|
||||
local_user_vote_display_mode::{LocalUserVoteDisplayMode, LocalUserVoteDisplayModeUpdateForm},
|
||||
person::{Person, PersonUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::diesel_string_update,
|
||||
utils::{diesel_opt_number_update, diesel_string_update},
|
||||
};
|
||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||
use lemmy_utils::{
|
||||
|
@ -28,7 +22,6 @@ use lemmy_utils::{
|
|||
};
|
||||
use std::ops::Deref;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn save_user_settings(
|
||||
data: Json<SaveUserSettings>,
|
||||
context: Data<LemmyContext>,
|
||||
|
@ -36,7 +29,7 @@ pub async fn save_user_settings(
|
|||
) -> LemmyResult<Json<SuccessResponse>> {
|
||||
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
||||
|
||||
let slur_regex = local_site_to_slur_regex(&site_view.local_site);
|
||||
let slur_regex = slur_regex(&context).await?;
|
||||
let url_blocklist = get_url_blocklist(&context).await?;
|
||||
let bio = diesel_string_update(
|
||||
process_markdown_opt(&data.bio, &slur_regex, &url_blocklist, &context)
|
||||
|
@ -55,7 +48,9 @@ pub async fn save_user_settings(
|
|||
if previous_email.deref() != email {
|
||||
LocalUser::check_is_email_taken(&mut context.pool(), email).await?;
|
||||
send_verification_email(
|
||||
&local_user_view,
|
||||
&site_view.local_site,
|
||||
&local_user_view.local_user,
|
||||
&local_user_view.person,
|
||||
email,
|
||||
&mut context.pool(),
|
||||
context.settings(),
|
||||
|
@ -91,6 +86,8 @@ pub async fn save_user_settings(
|
|||
let person_id = local_user_view.person.id;
|
||||
let default_listing_type = data.default_listing_type;
|
||||
let default_post_sort_type = data.default_post_sort_type;
|
||||
let default_post_time_range_seconds =
|
||||
diesel_opt_number_update(data.default_post_time_range_seconds);
|
||||
let default_comment_sort_type = data.default_comment_sort_type;
|
||||
|
||||
let person_form = PersonUpdateForm {
|
||||
|
@ -120,6 +117,7 @@ pub async fn save_user_settings(
|
|||
blur_nsfw: data.blur_nsfw,
|
||||
show_bot_accounts: data.show_bot_accounts,
|
||||
default_post_sort_type,
|
||||
default_post_time_range_seconds,
|
||||
default_comment_sort_type,
|
||||
default_listing_type,
|
||||
theme: data.theme.clone(),
|
||||
|
@ -133,20 +131,15 @@ pub async fn save_user_settings(
|
|||
collapse_bot_comments: data.collapse_bot_comments,
|
||||
auto_mark_fetched_posts_as_read: data.auto_mark_fetched_posts_as_read,
|
||||
hide_media: data.hide_media,
|
||||
// Update the vote display modes
|
||||
show_score: data.show_scores,
|
||||
show_upvotes: data.show_upvotes,
|
||||
show_downvotes: data.show_downvotes,
|
||||
show_upvote_percentage: data.show_upvote_percentage,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
LocalUser::update(&mut context.pool(), local_user_id, &local_user_form).await?;
|
||||
|
||||
// Update the vote display modes
|
||||
let vote_display_modes_form = LocalUserVoteDisplayModeUpdateForm {
|
||||
score: data.show_scores,
|
||||
upvotes: data.show_upvotes,
|
||||
downvotes: data.show_downvotes,
|
||||
upvote_percentage: data.show_upvote_percentage,
|
||||
};
|
||||
LocalUserVoteDisplayMode::update(&mut context.pool(), local_user_id, &vote_display_modes_form)
|
||||
.await?;
|
||||
|
||||
Ok(Json(SuccessResponse::default()))
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ use lemmy_utils::error::LemmyResult;
|
|||
///
|
||||
/// Disabling is only possible if 2FA was previously enabled. Again it is necessary to pass a valid
|
||||
/// token.
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn update_totp(
|
||||
data: Json<UpdateTotp>,
|
||||
local_user_view: LocalUserView,
|
||||
|
|
|
@ -8,7 +8,6 @@ use lemmy_db_schema::{
|
|||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn user_block_instance(
|
||||
data: Json<UserBlockInstanceParams>,
|
||||
local_user_view: LocalUserView,
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
use crate::{local_user_view_from_jwt, read_auth_token};
|
||||
use actix_web::{
|
||||
web::{Data, Json},
|
||||
HttpRequest,
|
||||
};
|
||||
use lemmy_api_common::{context::LemmyContext, SuccessResponse};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
utils::{local_user_view_from_jwt, read_auth_token},
|
||||
SuccessResponse,
|
||||
};
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
|
||||
/// Returns an error message if the auth token is invalid for any reason. Necessary because other
|
||||
/// endpoints silently treat any call with invalid auth as unauthenticated.
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn validate_auth(
|
||||
req: HttpRequest,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -19,7 +19,6 @@ use lemmy_db_schema::{
|
|||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn feature_post(
|
||||
data: Json<FeaturePost>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -8,7 +8,6 @@ use lemmy_db_views::structs::LocalUserView;
|
|||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
use url::Url;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn get_link_metadata(
|
||||
data: Query<GetSiteMetadata>,
|
||||
context: Data<LemmyContext>,
|
||||
|
@ -16,7 +15,7 @@ pub async fn get_link_metadata(
|
|||
_local_user_view: LocalUserView,
|
||||
) -> LemmyResult<Json<GetSiteMetadataResponse>> {
|
||||
let url = Url::parse(&data.url).with_lemmy_type(LemmyErrorType::InvalidUrl)?;
|
||||
let metadata = fetch_link_metadata(&url, &context).await?;
|
||||
let metadata = fetch_link_metadata(&url, &context, false).await?;
|
||||
|
||||
Ok(Json(GetSiteMetadataResponse { metadata }))
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ use lemmy_db_schema::source::post::PostHide;
|
|||
use lemmy_db_views::structs::{LocalUserView, PostView};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn hide_post(
|
||||
data: Json<HidePost>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -19,7 +19,6 @@ use lemmy_db_views::structs::{LocalUserView, PostView};
|
|||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
use std::ops::Deref;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn like_post(
|
||||
data: Json<CreatePostLike>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -9,7 +9,6 @@ use lemmy_db_views::structs::{LocalUserView, VoteView};
|
|||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
/// Lists likes for a post
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn list_post_likes(
|
||||
data: Query<ListPostLikes>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -17,7 +17,6 @@ use lemmy_db_schema::{
|
|||
use lemmy_db_views::structs::{LocalUserView, PostView};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn lock_post(
|
||||
data: Json<LockPost>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -4,7 +4,6 @@ use lemmy_db_schema::source::post::PostRead;
|
|||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult, MAX_API_PARAM_ELEMENTS};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn mark_posts_as_read(
|
||||
data: Json<MarkManyPostsAsRead>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -7,7 +7,6 @@ use lemmy_db_schema::source::post::{PostRead, PostReadForm};
|
|||
use lemmy_db_views::structs::{LocalUserView, PostView};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn mark_post_as_read(
|
||||
data: Json<MarkPostAsRead>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -10,7 +10,6 @@ use lemmy_db_schema::{
|
|||
use lemmy_db_views::structs::{LocalUserView, PostView};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn save_post(
|
||||
data: Json<SavePost>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -11,7 +11,6 @@ use lemmy_db_schema::{
|
|||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn mark_pm_as_read(
|
||||
data: Json<MarkPrivateMessageAsRead>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -9,6 +9,7 @@ use lemmy_api_common::{
|
|||
check_comment_deleted_or_removed,
|
||||
check_community_user_action,
|
||||
send_new_report_email_to_admins,
|
||||
slur_regex,
|
||||
},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
|
@ -22,16 +23,14 @@ use lemmy_db_views::structs::{CommentReportView, CommentView, LocalUserView};
|
|||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
/// Creates a comment report and notifies the moderators of the community
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn create_comment_report(
|
||||
data: Json<CreateCommentReport>,
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
) -> LemmyResult<Json<CommentReportResponse>> {
|
||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||
|
||||
let reason = data.reason.trim().to_string();
|
||||
check_report_reason(&reason, &local_site)?;
|
||||
let slur_regex = slur_regex(&context).await?;
|
||||
check_report_reason(&reason, &slur_regex)?;
|
||||
|
||||
let person_id = local_user_view.person.id;
|
||||
let comment_id = data.comment_id;
|
||||
|
@ -57,6 +56,7 @@ pub async fn create_comment_report(
|
|||
comment_id,
|
||||
original_comment_text: comment_view.comment.content,
|
||||
reason,
|
||||
violates_instance_rules: data.violates_instance_rules.unwrap_or_default(),
|
||||
};
|
||||
|
||||
let report = CommentReport::report(&mut context.pool(), &report_form)
|
||||
|
@ -67,6 +67,7 @@ pub async fn create_comment_report(
|
|||
CommentReportView::read(&mut context.pool(), report.id, person_id).await?;
|
||||
|
||||
// Email the admins
|
||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||
if local_site.reports_email_admins {
|
||||
send_new_report_email_to_admins(
|
||||
&comment_report_view.creator.name,
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use actix_web::web::{Data, Json};
|
||||
use activitypub_federation::config::Data;
|
||||
use actix_web::web::Json;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
reports::comment::{CommentReportResponse, ResolveCommentReport},
|
||||
send_activity::{ActivityChannel, SendActivityData},
|
||||
utils::check_community_mod_action,
|
||||
};
|
||||
use lemmy_db_schema::{source::comment_report::CommentReport, traits::Reportable};
|
||||
|
@ -9,7 +11,6 @@ use lemmy_db_views::structs::{CommentReportView, LocalUserView};
|
|||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
/// Resolves or unresolves a comment report and notifies the moderators of the community
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn resolve_comment_report(
|
||||
data: Json<ResolveCommentReport>,
|
||||
context: Data<LemmyContext>,
|
||||
|
@ -42,6 +43,16 @@ pub async fn resolve_comment_report(
|
|||
let comment_report_view =
|
||||
CommentReportView::read(&mut context.pool(), report_id, person_id).await?;
|
||||
|
||||
ActivityChannel::submit_activity(
|
||||
SendActivityData::SendResolveReport {
|
||||
object_id: comment_report_view.comment.ap_id.inner().clone(),
|
||||
actor: local_user_view.person,
|
||||
report_creator: report.creator,
|
||||
community: comment_report_view.community.clone(),
|
||||
},
|
||||
&context,
|
||||
)?;
|
||||
|
||||
Ok(Json(CommentReportResponse {
|
||||
comment_report_view,
|
||||
}))
|
||||
|
|
71
crates/api/src/reports/community_report/create.rs
Normal file
71
crates/api/src/reports/community_report/create.rs
Normal file
|
@ -0,0 +1,71 @@
|
|||
use crate::check_report_reason;
|
||||
use actix_web::web::{Data, Json};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
reports::community::{CommunityReportResponse, CreateCommunityReport},
|
||||
utils::{send_new_report_email_to_admins, slur_regex},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
community::Community,
|
||||
community_report::{CommunityReport, CommunityReportForm},
|
||||
local_site::LocalSite,
|
||||
},
|
||||
traits::{Crud, Reportable},
|
||||
};
|
||||
use lemmy_db_views::structs::{CommunityReportView, LocalUserView};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
pub async fn create_community_report(
|
||||
data: Json<CreateCommunityReport>,
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
) -> LemmyResult<Json<CommunityReportResponse>> {
|
||||
let reason = data.reason.trim().to_string();
|
||||
let slur_regex = slur_regex(&context).await?;
|
||||
check_report_reason(&reason, &slur_regex)?;
|
||||
|
||||
let person_id = local_user_view.person.id;
|
||||
let community_id = data.community_id;
|
||||
let community = Community::read(&mut context.pool(), community_id).await?;
|
||||
|
||||
let report_form = CommunityReportForm {
|
||||
creator_id: person_id,
|
||||
community_id,
|
||||
original_community_banner: community.banner,
|
||||
original_community_description: community.description,
|
||||
original_community_icon: community.icon,
|
||||
original_community_name: community.name,
|
||||
original_community_sidebar: community.sidebar,
|
||||
original_community_title: community.title,
|
||||
reason,
|
||||
};
|
||||
|
||||
let report = CommunityReport::report(&mut context.pool(), &report_form)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntCreateReport)?;
|
||||
|
||||
let community_report_view =
|
||||
CommunityReportView::read(&mut context.pool(), report.id, person_id).await?;
|
||||
|
||||
// Email the admins
|
||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||
if local_site.reports_email_admins {
|
||||
send_new_report_email_to_admins(
|
||||
&community_report_view.creator.name,
|
||||
// The argument here is normally the reported content's creator, but a community doesn't have
|
||||
// a single person to be considered the creator or the person responsible for the bad thing,
|
||||
// so the community name is used instead
|
||||
&community_report_view.community.name,
|
||||
&mut context.pool(),
|
||||
context.settings(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
// TODO: consider federating this
|
||||
|
||||
Ok(Json(CommunityReportResponse {
|
||||
community_report_view,
|
||||
}))
|
||||
}
|
2
crates/api/src/reports/community_report/mod.rs
Normal file
2
crates/api/src/reports/community_report/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod create;
|
||||
pub mod resolve;
|
36
crates/api/src/reports/community_report/resolve.rs
Normal file
36
crates/api/src/reports/community_report/resolve.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use actix_web::web::{Data, Json};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
reports::community::{CommunityReportResponse, ResolveCommunityReport},
|
||||
utils::is_admin,
|
||||
};
|
||||
use lemmy_db_schema::{source::community_report::CommunityReport, traits::Reportable};
|
||||
use lemmy_db_views::structs::{CommunityReportView, LocalUserView};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
pub async fn resolve_community_report(
|
||||
data: Json<ResolveCommunityReport>,
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
) -> LemmyResult<Json<CommunityReportResponse>> {
|
||||
is_admin(&local_user_view)?;
|
||||
|
||||
let report_id = data.report_id;
|
||||
let person_id = local_user_view.person.id;
|
||||
if data.resolved {
|
||||
CommunityReport::resolve(&mut context.pool(), report_id, person_id)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
|
||||
} else {
|
||||
CommunityReport::unresolve(&mut context.pool(), report_id, person_id)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
|
||||
}
|
||||
|
||||
let community_report_view =
|
||||
CommunityReportView::read(&mut context.pool(), report_id, person_id).await?;
|
||||
|
||||
Ok(Json(CommunityReportResponse {
|
||||
community_report_view,
|
||||
}))
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
pub mod comment_report;
|
||||
pub mod community_report;
|
||||
pub mod post_report;
|
||||
pub mod private_message_report;
|
||||
pub mod report_combined;
|
||||
|
|
|
@ -9,6 +9,7 @@ use lemmy_api_common::{
|
|||
check_community_user_action,
|
||||
check_post_deleted_or_removed,
|
||||
send_new_report_email_to_admins,
|
||||
slur_regex,
|
||||
},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
|
@ -22,16 +23,14 @@ use lemmy_db_views::structs::{LocalUserView, PostReportView, PostView};
|
|||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
/// Creates a post report and notifies the moderators of the community
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn create_post_report(
|
||||
data: Json<CreatePostReport>,
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
) -> LemmyResult<Json<PostReportResponse>> {
|
||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||
|
||||
let reason = data.reason.trim().to_string();
|
||||
check_report_reason(&reason, &local_site)?;
|
||||
let slur_regex = slur_regex(&context).await?;
|
||||
check_report_reason(&reason, &slur_regex)?;
|
||||
|
||||
let person_id = local_user_view.person.id;
|
||||
let post_id = data.post_id;
|
||||
|
@ -53,6 +52,7 @@ pub async fn create_post_report(
|
|||
original_post_url: post_view.post.url,
|
||||
original_post_body: post_view.post.body,
|
||||
reason,
|
||||
violates_instance_rules: data.violates_instance_rules.unwrap_or_default(),
|
||||
};
|
||||
|
||||
let report = PostReport::report(&mut context.pool(), &report_form)
|
||||
|
@ -62,6 +62,7 @@ pub async fn create_post_report(
|
|||
let post_report_view = PostReportView::read(&mut context.pool(), report.id, person_id).await?;
|
||||
|
||||
// Email the admins
|
||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||
if local_site.reports_email_admins {
|
||||
send_new_report_email_to_admins(
|
||||
&post_report_view.creator.name,
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use actix_web::web::{Data, Json};
|
||||
use activitypub_federation::config::Data;
|
||||
use actix_web::web::Json;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
reports::post::{PostReportResponse, ResolvePostReport},
|
||||
send_activity::{ActivityChannel, SendActivityData},
|
||||
utils::check_community_mod_action,
|
||||
};
|
||||
use lemmy_db_schema::{source::post_report::PostReport, traits::Reportable};
|
||||
|
@ -9,7 +11,6 @@ use lemmy_db_views::structs::{LocalUserView, PostReportView};
|
|||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
/// Resolves or unresolves a post report and notifies the moderators of the community
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn resolve_post_report(
|
||||
data: Json<ResolvePostReport>,
|
||||
context: Data<LemmyContext>,
|
||||
|
@ -33,6 +34,7 @@ pub async fn resolve_post_report(
|
|||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
|
||||
} else {
|
||||
// TODO: not federated
|
||||
PostReport::unresolve(&mut context.pool(), report_id, person_id)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
|
||||
|
@ -40,5 +42,15 @@ pub async fn resolve_post_report(
|
|||
|
||||
let post_report_view = PostReportView::read(&mut context.pool(), report_id, person_id).await?;
|
||||
|
||||
ActivityChannel::submit_activity(
|
||||
SendActivityData::SendResolveReport {
|
||||
object_id: post_report_view.post.ap_id.inner().clone(),
|
||||
actor: local_user_view.person,
|
||||
report_creator: report.creator,
|
||||
community: post_report_view.community.clone(),
|
||||
},
|
||||
&context,
|
||||
)?;
|
||||
|
||||
Ok(Json(PostReportResponse { post_report_view }))
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use actix_web::web::{Data, Json};
|
|||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
reports::private_message::{CreatePrivateMessageReport, PrivateMessageReportResponse},
|
||||
utils::send_new_report_email_to_admins,
|
||||
utils::{send_new_report_email_to_admins, slur_regex},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
|
@ -16,16 +16,14 @@ use lemmy_db_schema::{
|
|||
use lemmy_db_views::structs::{LocalUserView, PrivateMessageReportView};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn create_pm_report(
|
||||
data: Json<CreatePrivateMessageReport>,
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
) -> LemmyResult<Json<PrivateMessageReportResponse>> {
|
||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||
|
||||
let reason = data.reason.trim().to_string();
|
||||
check_report_reason(&reason, &local_site)?;
|
||||
let slur_regex = slur_regex(&context).await?;
|
||||
check_report_reason(&reason, &slur_regex)?;
|
||||
|
||||
let person_id = local_user_view.person.id;
|
||||
let private_message_id = data.private_message_id;
|
||||
|
@ -51,6 +49,7 @@ pub async fn create_pm_report(
|
|||
PrivateMessageReportView::read(&mut context.pool(), report.id).await?;
|
||||
|
||||
// Email the admins
|
||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||
if local_site.reports_email_admins {
|
||||
send_new_report_email_to_admins(
|
||||
&private_message_report_view.creator.name,
|
||||
|
|
|
@ -8,7 +8,6 @@ use lemmy_db_schema::{source::private_message_report::PrivateMessageReport, trai
|
|||
use lemmy_db_views::structs::{LocalUserView, PrivateMessageReportView};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn resolve_pm_report(
|
||||
data: Json<ResolvePrivateMessageReport>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -4,38 +4,47 @@ use lemmy_api_common::{
|
|||
reports::combined::{ListReports, ListReportsResponse},
|
||||
utils::check_community_mod_of_any_or_admin_action,
|
||||
};
|
||||
use lemmy_db_views::{report_combined_view::ReportCombinedQuery, structs::LocalUserView};
|
||||
use lemmy_db_schema::traits::PaginationCursorBuilder;
|
||||
use lemmy_db_views::{
|
||||
combined::report_combined_view::ReportCombinedQuery,
|
||||
structs::{LocalUserView, ReportCombinedView},
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
/// Lists reports for a community if an id is supplied
|
||||
/// or returns all reports for communities a user moderates
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn list_reports(
|
||||
data: Query<ListReports>,
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
) -> LemmyResult<Json<ListReportsResponse>> {
|
||||
let community_id = data.community_id;
|
||||
let unresolved_only = data.unresolved_only;
|
||||
let my_reports_only = data.my_reports_only;
|
||||
|
||||
check_community_mod_of_any_or_admin_action(&local_user_view, &mut context.pool()).await?;
|
||||
// Only check mod or admin status when not viewing my reports
|
||||
if !my_reports_only.unwrap_or_default() {
|
||||
check_community_mod_of_any_or_admin_action(&local_user_view, &mut context.pool()).await?;
|
||||
}
|
||||
|
||||
// parse pagination token
|
||||
let page_after = if let Some(pa) = &data.page_cursor {
|
||||
Some(pa.read(&mut context.pool()).await?)
|
||||
let cursor_data = if let Some(cursor) = &data.page_cursor {
|
||||
Some(ReportCombinedView::from_cursor(cursor, &mut context.pool()).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let page_back = data.page_back;
|
||||
|
||||
let reports = ReportCombinedQuery {
|
||||
community_id,
|
||||
unresolved_only,
|
||||
page_after,
|
||||
page_back,
|
||||
community_id: data.community_id,
|
||||
post_id: data.post_id,
|
||||
type_: data.type_,
|
||||
unresolved_only: data.unresolved_only,
|
||||
cursor_data,
|
||||
page_back: data.page_back,
|
||||
show_community_rule_violations: data.show_community_rule_violations,
|
||||
my_reports_only,
|
||||
}
|
||||
.list(&mut context.pool(), &local_user_view)
|
||||
.await?;
|
||||
|
||||
Ok(Json(ListReportsResponse { reports }))
|
||||
let next_page = reports.last().map(PaginationCursorBuilder::to_cursor);
|
||||
|
||||
Ok(Json(ListReportsResponse { reports, next_page }))
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ use lemmy_db_schema::{
|
|||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn admin_allow_instance(
|
||||
data: Json<AdminAllowInstanceParams>,
|
||||
local_user_view: LocalUserView,
|
||||
|
|
|
@ -18,7 +18,6 @@ use lemmy_db_schema::{
|
|||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn admin_block_instance(
|
||||
data: Json<AdminBlockInstanceParams>,
|
||||
local_user_view: LocalUserView,
|
||||
|
|
|
@ -7,7 +7,6 @@ use lemmy_api_common::{
|
|||
use lemmy_db_views::structs::SiteView;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn get_federated_instances(
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<Json<GetFederatedInstancesResponse>> {
|
||||
|
|
|
@ -12,14 +12,12 @@ use lemmy_db_schema::{
|
|||
},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||
use lemmy_db_views_actor::structs::PersonView;
|
||||
use lemmy_db_views::structs::{LocalUserView, PersonView, SiteView};
|
||||
use lemmy_utils::{
|
||||
error::{LemmyErrorType, LemmyResult},
|
||||
VERSION,
|
||||
};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn leave_admin(
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
|
@ -71,8 +69,8 @@ pub async fn leave_admin(
|
|||
version: VERSION.to_string(),
|
||||
all_languages,
|
||||
discussion_languages,
|
||||
oauth_providers: Some(oauth_providers),
|
||||
admin_oauth_providers: None,
|
||||
oauth_providers,
|
||||
admin_oauth_providers: vec![],
|
||||
blocked_urls,
|
||||
tagline,
|
||||
my_user: None,
|
||||
|
|
|
@ -7,7 +7,6 @@ use lemmy_api_common::{
|
|||
use lemmy_db_views::structs::{LocalImageView, LocalUserView};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn list_all_media(
|
||||
data: Query<ListMedia>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -4,12 +4,13 @@ use lemmy_api_common::{
|
|||
site::{GetModlog, GetModlogResponse},
|
||||
utils::{check_community_mod_of_any_or_admin_action, check_private_instance},
|
||||
};
|
||||
use lemmy_db_schema::source::local_site::LocalSite;
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views_moderator::{self, modlog_combined_view::ModlogCombinedQuery};
|
||||
use lemmy_db_schema::{source::local_site::LocalSite, traits::PaginationCursorBuilder};
|
||||
use lemmy_db_views::{
|
||||
combined::modlog_combined_view::ModlogCombinedQuery,
|
||||
structs::{LocalUserView, ModlogCombinedView},
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn get_mod_log(
|
||||
data: Query<GetModlog>,
|
||||
context: Data<LemmyContext>,
|
||||
|
@ -19,11 +20,8 @@ pub async fn get_mod_log(
|
|||
|
||||
check_private_instance(&local_user_view, &local_site)?;
|
||||
|
||||
let type_ = data.type_;
|
||||
let community_id = data.community_id;
|
||||
|
||||
let is_mod_or_admin = if let Some(local_user_view) = local_user_view {
|
||||
check_community_mod_of_any_or_admin_action(&local_user_view, &mut context.pool())
|
||||
let is_mod_or_admin = if let Some(local_user_view) = &local_user_view {
|
||||
check_community_mod_of_any_or_admin_action(local_user_view, &mut context.pool())
|
||||
.await
|
||||
.is_ok()
|
||||
} else {
|
||||
|
@ -36,31 +34,30 @@ pub async fn get_mod_log(
|
|||
} else {
|
||||
data.mod_person_id
|
||||
};
|
||||
let other_person_id = data.other_person_id;
|
||||
let post_id = data.post_id;
|
||||
let comment_id = data.comment_id;
|
||||
|
||||
// parse pagination token
|
||||
let page_after = if let Some(pa) = &data.page_cursor {
|
||||
Some(pa.read(&mut context.pool()).await?)
|
||||
let cursor_data = if let Some(cursor) = &data.page_cursor {
|
||||
Some(ModlogCombinedView::from_cursor(cursor, &mut context.pool()).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let page_back = data.page_back;
|
||||
|
||||
let modlog = ModlogCombinedQuery {
|
||||
type_,
|
||||
community_id,
|
||||
type_: data.type_,
|
||||
listing_type: data.listing_type,
|
||||
community_id: data.community_id,
|
||||
mod_person_id,
|
||||
other_person_id,
|
||||
post_id,
|
||||
comment_id,
|
||||
other_person_id: data.other_person_id,
|
||||
local_user: local_user_view.as_ref().map(|u| &u.local_user),
|
||||
post_id: data.post_id,
|
||||
comment_id: data.comment_id,
|
||||
hide_modlog_names: Some(hide_modlog_names),
|
||||
page_after,
|
||||
page_back,
|
||||
cursor_data,
|
||||
page_back: data.page_back,
|
||||
}
|
||||
.list(&mut context.pool())
|
||||
.await?;
|
||||
|
||||
Ok(Json(GetModlogResponse { modlog }))
|
||||
let next_page = modlog.last().map(PaginationCursorBuilder::to_cursor);
|
||||
|
||||
Ok(Json(GetModlogResponse { modlog, next_page }))
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ use lemmy_db_schema::{
|
|||
use lemmy_db_views::structs::{CommentView, LocalUserView};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn purge_comment(
|
||||
data: Json<PurgeComment>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -17,11 +17,9 @@ use lemmy_db_schema::{
|
|||
},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views_actor::structs::CommunityModeratorView;
|
||||
use lemmy_db_views::structs::{CommunityModeratorView, LocalUserView};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn purge_community(
|
||||
data: Json<PurgeCommunity>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -19,7 +19,6 @@ use lemmy_db_schema::{
|
|||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn purge_person(
|
||||
data: Json<PurgePerson>,
|
||||
context: Data<LemmyContext>,
|
||||
|
|
|
@ -2,10 +2,9 @@ use activitypub_federation::config::Data;
|
|||
use actix_web::web::Json;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
request::purge_image_from_pictrs,
|
||||
send_activity::{ActivityChannel, SendActivityData},
|
||||
site::PurgePost,
|
||||
utils::is_admin,
|
||||
utils::{is_admin, purge_post_images},
|
||||
SuccessResponse,
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
|
@ -19,7 +18,6 @@ use lemmy_db_schema::{
|
|||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn purge_post(
|
||||
data: Json<PurgePost>,
|
||||
context: Data<LemmyContext>,
|
||||
|
@ -39,14 +37,7 @@ pub async fn purge_post(
|
|||
)
|
||||
.await?;
|
||||
|
||||
// Purge image
|
||||
if let Some(url) = &post.url {
|
||||
purge_image_from_pictrs(url, &context).await.ok();
|
||||
}
|
||||
// Purge thumbnail
|
||||
if let Some(thumbnail_url) = &post.thumbnail_url {
|
||||
purge_image_from_pictrs(thumbnail_url, &context).await.ok();
|
||||
}
|
||||
purge_post_images(post.url.clone(), post.thumbnail_url.clone(), &context).await;
|
||||
|
||||
Post::delete(&mut context.pool(), data.post_id).await?;
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use lemmy_api_common::{
|
|||
};
|
||||
use lemmy_db_schema::source::local_site::LocalSite;
|
||||
use lemmy_db_views::{
|
||||
registration_application_view::RegistrationApplicationQuery,
|
||||
registration_applications::registration_application_view::RegistrationApplicationQuery,
|
||||
structs::LocalUserView,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
|
|
@ -19,10 +19,7 @@ workspace = true
|
|||
[features]
|
||||
full = [
|
||||
"tracing",
|
||||
"rosetta-i18n",
|
||||
"lemmy_db_views/full",
|
||||
"lemmy_db_views_actor/full",
|
||||
"lemmy_db_views_moderator/full",
|
||||
"lemmy_utils/full",
|
||||
"activitypub_federation",
|
||||
"encoding_rs",
|
||||
|
@ -37,12 +34,12 @@ full = [
|
|||
"jsonwebtoken",
|
||||
"mime",
|
||||
"moka",
|
||||
"actix-web-httpauth",
|
||||
"webmention",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
lemmy_db_views = { workspace = true }
|
||||
lemmy_db_views_moderator = { workspace = true }
|
||||
lemmy_db_views_actor = { workspace = true }
|
||||
lemmy_db_schema = { workspace = true }
|
||||
lemmy_utils = { workspace = true }
|
||||
activitypub_federation = { workspace = true, optional = true }
|
||||
|
@ -53,7 +50,6 @@ chrono = { workspace = true }
|
|||
tracing = { workspace = true, optional = true }
|
||||
reqwest-middleware = { workspace = true, optional = true }
|
||||
regex = { workspace = true }
|
||||
rosetta-i18n = { workspace = true, optional = true }
|
||||
futures = { workspace = true, optional = true }
|
||||
uuid = { workspace = true, optional = true }
|
||||
tokio = { workspace = true, optional = true }
|
||||
|
@ -61,17 +57,19 @@ reqwest = { workspace = true, optional = true }
|
|||
ts-rs = { workspace = true, optional = true }
|
||||
moka = { workspace = true, optional = true }
|
||||
anyhow.workspace = true
|
||||
actix-web = { workspace = true, optional = true }
|
||||
enum-map = { workspace = true }
|
||||
actix-web = { workspace = true, optional = true }
|
||||
urlencoding = { workspace = true }
|
||||
mime = { version = "0.3.17", optional = true }
|
||||
mime_guess = "2.0.5"
|
||||
infer = "0.16.0"
|
||||
infer = "0.19.0"
|
||||
webpage = { version = "2.0", default-features = false, optional = true, features = [
|
||||
"serde",
|
||||
] }
|
||||
encoding_rs = { version = "0.8.35", optional = true }
|
||||
jsonwebtoken = { version = "9.3.0", optional = true }
|
||||
jsonwebtoken = { version = "9.3.1", optional = true }
|
||||
actix-web-httpauth = { version = "0.8.2", optional = true }
|
||||
webmention = { version = "0.6.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = { workspace = true }
|
||||
|
|
|
@ -3,12 +3,7 @@ use crate::{
|
|||
community::CommunityResponse,
|
||||
context::LemmyContext,
|
||||
post::PostResponse,
|
||||
utils::{
|
||||
check_person_instance_community_block,
|
||||
get_interface_language,
|
||||
is_mod_or_admin,
|
||||
send_email_to_user,
|
||||
},
|
||||
utils::{check_person_instance_community_block, is_mod_or_admin, send_email_to_user},
|
||||
};
|
||||
use actix_web::web::Json;
|
||||
use lemmy_db_schema::{
|
||||
|
@ -25,8 +20,7 @@ use lemmy_db_schema::{
|
|||
},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_views::structs::{CommentView, LocalUserView, PostView};
|
||||
use lemmy_db_views_actor::structs::CommunityView;
|
||||
use lemmy_db_views::structs::{CommentView, CommunityView, LocalUserView, PostView};
|
||||
use lemmy_utils::{
|
||||
error::LemmyResult,
|
||||
utils::{markdown::markdown_to_html, mention::MentionData},
|
||||
|
@ -92,7 +86,7 @@ pub async fn build_post_response(
|
|||
}
|
||||
|
||||
// TODO: this function is a mess and should be split up to handle email separately
|
||||
#[tracing::instrument(skip_all)]
|
||||
|
||||
pub async fn send_local_notifs(
|
||||
mentions: Vec<MentionData>,
|
||||
post_or_comment_id: PostOrCommentId,
|
||||
|
@ -102,7 +96,6 @@ pub async fn send_local_notifs(
|
|||
local_user_view: Option<&LocalUserView>,
|
||||
) -> LemmyResult<Vec<LocalUserId>> {
|
||||
let mut recipient_ids = Vec::new();
|
||||
let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname());
|
||||
|
||||
let (comment_opt, post, community) = match post_or_comment_id {
|
||||
PostOrCommentId::Post(post_id) => {
|
||||
|
@ -142,6 +135,8 @@ pub async fn send_local_notifs(
|
|||
}
|
||||
};
|
||||
|
||||
let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname());
|
||||
|
||||
// Send the local mentions
|
||||
for mention in mentions
|
||||
.iter()
|
||||
|
@ -157,7 +152,7 @@ pub async fn send_local_notifs(
|
|||
recipient_ids.push(mention_user_view.local_user.id);
|
||||
|
||||
// Make the correct reply form depending on whether its a post or comment mention
|
||||
let comment_content_or_post_body = if let Some(comment) = &comment_opt {
|
||||
let (link, comment_content_or_post_body) = if let Some(comment) = &comment_opt {
|
||||
let person_comment_mention_form = PersonCommentMentionInsertForm {
|
||||
recipient_id: mention_user_view.person.id,
|
||||
comment_id: comment.id,
|
||||
|
@ -169,7 +164,10 @@ pub async fn send_local_notifs(
|
|||
PersonCommentMention::create(&mut context.pool(), &person_comment_mention_form)
|
||||
.await
|
||||
.ok();
|
||||
comment.content.clone()
|
||||
(
|
||||
comment.local_url(context.settings())?,
|
||||
comment.content.clone(),
|
||||
)
|
||||
} else {
|
||||
let person_post_mention_form = PersonPostMentionInsertForm {
|
||||
recipient_id: mention_user_view.person.id,
|
||||
|
@ -181,17 +179,20 @@ pub async fn send_local_notifs(
|
|||
PersonPostMention::create(&mut context.pool(), &person_post_mention_form)
|
||||
.await
|
||||
.ok();
|
||||
post.body.clone().unwrap_or_default()
|
||||
(
|
||||
post.local_url(context.settings())?,
|
||||
post.body.clone().unwrap_or_default(),
|
||||
)
|
||||
};
|
||||
|
||||
// Send an email to those local users that have notifications on
|
||||
if do_send_email {
|
||||
let lang = get_interface_language(&mention_user_view);
|
||||
let lang = &mention_user_view.local_user.interface_i18n_language();
|
||||
let content = markdown_to_html(&comment_content_or_post_body);
|
||||
send_email_to_user(
|
||||
&mention_user_view,
|
||||
&lang.notification_mentioned_by_subject(&person.name),
|
||||
&lang.notification_mentioned_by_body(&content, &inbox_link, &person.name),
|
||||
&lang.notification_mentioned_by_body(&link, &content, &inbox_link, &person.name),
|
||||
context.settings(),
|
||||
)
|
||||
.await
|
||||
|
@ -239,12 +240,19 @@ pub async fn send_local_notifs(
|
|||
.ok();
|
||||
|
||||
if do_send_email {
|
||||
let lang = get_interface_language(&parent_user_view);
|
||||
let lang = &parent_user_view.local_user.interface_i18n_language();
|
||||
let content = markdown_to_html(&comment.content);
|
||||
send_email_to_user(
|
||||
&parent_user_view,
|
||||
&lang.notification_comment_reply_subject(&person.name),
|
||||
&lang.notification_comment_reply_body(&content, &inbox_link, &person.name),
|
||||
&lang.notification_comment_reply_body(
|
||||
comment.local_url(context.settings())?,
|
||||
&content,
|
||||
&inbox_link,
|
||||
&parent_comment.content,
|
||||
&post.name,
|
||||
&person.name,
|
||||
),
|
||||
context.settings(),
|
||||
)
|
||||
.await
|
||||
|
@ -285,12 +293,18 @@ pub async fn send_local_notifs(
|
|||
.ok();
|
||||
|
||||
if do_send_email {
|
||||
let lang = get_interface_language(&parent_user_view);
|
||||
let lang = &parent_user_view.local_user.interface_i18n_language();
|
||||
let content = markdown_to_html(&comment.content);
|
||||
send_email_to_user(
|
||||
&parent_user_view,
|
||||
&lang.notification_post_reply_subject(&person.name),
|
||||
&lang.notification_post_reply_body(&content, &inbox_link, &person.name),
|
||||
&lang.notification_post_reply_body(
|
||||
comment.local_url(context.settings())?,
|
||||
&content,
|
||||
&inbox_link,
|
||||
&post.name,
|
||||
&person.name,
|
||||
),
|
||||
context.settings(),
|
||||
)
|
||||
.await
|
||||
|
|
|
@ -3,7 +3,7 @@ use lemmy_db_schema::{
|
|||
CommentSortType,
|
||||
ListingType,
|
||||
};
|
||||
use lemmy_db_views::structs::{CommentView, VoteView};
|
||||
use lemmy_db_views::structs::{CommentSlimView, CommentView, VoteView};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
#[cfg(feature = "full")]
|
||||
|
@ -117,6 +117,10 @@ pub struct GetComments {
|
|||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub sort: Option<CommentSortType>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
/// Filter to within a given time range, in seconds.
|
||||
/// IE 60 would give results for the past minute.
|
||||
pub time_range_seconds: Option<i32>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub max_depth: Option<i32>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page: Option<i64>,
|
||||
|
@ -144,6 +148,14 @@ pub struct GetCommentsResponse {
|
|||
pub comments: Vec<CommentView>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A slimmer comment list response, without the post or community.
|
||||
pub struct GetCommentsSlimResponse {
|
||||
pub comments: Vec<CommentSlimView>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
|
|
|
@ -4,7 +4,7 @@ use lemmy_db_schema::{
|
|||
CommunityVisibility,
|
||||
ListingType,
|
||||
};
|
||||
use lemmy_db_views_actor::structs::{
|
||||
use lemmy_db_views::structs::{
|
||||
CommunityModeratorView,
|
||||
CommunitySortType,
|
||||
CommunityView,
|
||||
|
@ -97,6 +97,10 @@ pub struct ListCommunities {
|
|||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub sort: Option<CommunitySortType>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
/// Filter to within a given time range, in seconds.
|
||||
/// IE 60 would give results for the past minute.
|
||||
pub time_range_seconds: Option<i32>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub show_nsfw: Option<bool>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page: Option<i64>,
|
||||
|
|
|
@ -24,8 +24,6 @@ pub mod utils;
|
|||
|
||||
pub extern crate lemmy_db_schema;
|
||||
pub extern crate lemmy_db_views;
|
||||
pub extern crate lemmy_db_views_actor;
|
||||
pub extern crate lemmy_db_views_moderator;
|
||||
pub extern crate lemmy_utils;
|
||||
|
||||
pub use lemmy_utils::error::LemmyErrorType;
|
||||
|
@ -35,7 +33,7 @@ use std::{cmp::min, time::Duration};
|
|||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(ts_rs::TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Saves settings for your user.
|
||||
/// A response that completes successfully.
|
||||
pub struct SuccessResponse {
|
||||
pub success: bool,
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use lemmy_db_schema::{
|
|||
CommentReplyId,
|
||||
CommunityId,
|
||||
LanguageId,
|
||||
PaginationCursor,
|
||||
PersonCommentMentionId,
|
||||
PersonId,
|
||||
PersonPostMentionId,
|
||||
|
@ -17,15 +18,11 @@ use lemmy_db_schema::{
|
|||
PostSortType,
|
||||
};
|
||||
use lemmy_db_views::structs::{
|
||||
LocalImageView,
|
||||
PersonContentCombinedPaginationCursor,
|
||||
PersonContentCombinedView,
|
||||
PersonSavedCombinedPaginationCursor,
|
||||
};
|
||||
use lemmy_db_views_actor::structs::{
|
||||
CommunityModeratorView,
|
||||
InboxCombinedPaginationCursor,
|
||||
InboxCombinedView,
|
||||
LocalImageView,
|
||||
PersonContentCombinedView,
|
||||
PersonSavedCombinedView,
|
||||
PersonView,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -122,6 +119,9 @@ pub struct SaveUserSettings {
|
|||
/// The default post sort, usually "active"
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub default_post_sort_type: Option<PostSortType>,
|
||||
/// A default time range limit to apply to post sorts, in seconds. 0 means none.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub default_post_time_range_seconds: Option<i32>,
|
||||
/// The default comment sort, usually "hot"
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub default_comment_sort_type: Option<CommentSortType>,
|
||||
|
@ -263,7 +263,7 @@ pub struct ListPersonContent {
|
|||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub username: Option<String>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_cursor: Option<PersonContentCombinedPaginationCursor>,
|
||||
pub page_cursor: Option<PaginationCursor>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_back: Option<bool>,
|
||||
}
|
||||
|
@ -275,6 +275,9 @@ pub struct ListPersonContent {
|
|||
/// A person's content response.
|
||||
pub struct ListPersonContentResponse {
|
||||
pub content: Vec<PersonContentCombinedView>,
|
||||
/// the pagination cursor to use to fetch the next page
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub next_page: Option<PaginationCursor>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
|
@ -286,7 +289,7 @@ pub struct ListPersonSaved {
|
|||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub type_: Option<PersonContentType>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_cursor: Option<PersonSavedCombinedPaginationCursor>,
|
||||
pub page_cursor: Option<PaginationCursor>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_back: Option<bool>,
|
||||
}
|
||||
|
@ -297,7 +300,10 @@ pub struct ListPersonSaved {
|
|||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A person's saved content response.
|
||||
pub struct ListPersonSavedResponse {
|
||||
pub saved: Vec<PersonContentCombinedView>,
|
||||
pub saved: Vec<PersonSavedCombinedView>,
|
||||
/// the pagination cursor to use to fetch the next page
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub next_page: Option<PaginationCursor>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq)]
|
||||
|
@ -385,7 +391,7 @@ pub struct ListInbox {
|
|||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub unread_only: Option<bool>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_cursor: Option<InboxCombinedPaginationCursor>,
|
||||
pub page_cursor: Option<PaginationCursor>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_back: Option<bool>,
|
||||
}
|
||||
|
@ -396,6 +402,9 @@ pub struct ListInbox {
|
|||
/// Get your inbox (replies, comment mentions, post mentions, and messages)
|
||||
pub struct ListInboxResponse {
|
||||
pub inbox: Vec<InboxCombinedView>,
|
||||
/// the pagination cursor to use to fetch the next page
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub next_page: Option<PaginationCursor>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
||||
|
@ -534,3 +543,11 @@ pub struct ListMediaResponse {
|
|||
pub struct ListLoginsResponse {
|
||||
pub logins: Vec<LoginToken>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Make a request to resend your verification email.
|
||||
pub struct ResendVerificationEmail {
|
||||
pub email: SensitiveString,
|
||||
}
|
||||
|
|
|
@ -4,8 +4,7 @@ use lemmy_db_schema::{
|
|||
PostFeatureType,
|
||||
PostSortType,
|
||||
};
|
||||
use lemmy_db_views::structs::{PaginationCursor, PostView, VoteView};
|
||||
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
|
||||
use lemmy_db_views::structs::{CommunityView, PostPaginationCursor, PostView, VoteView};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
#[cfg(feature = "full")]
|
||||
|
@ -72,7 +71,6 @@ pub struct GetPost {
|
|||
pub struct GetPostResponse {
|
||||
pub post_view: PostView,
|
||||
pub community_view: CommunityView,
|
||||
pub moderators: Vec<CommunityModeratorView>,
|
||||
/// A list of cross-posts, or other times / communities this link has been posted to.
|
||||
pub cross_posts: Vec<PostView>,
|
||||
}
|
||||
|
@ -87,6 +85,11 @@ pub struct GetPosts {
|
|||
pub type_: Option<ListingType>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub sort: Option<PostSortType>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
/// Filter to within a given time range, in seconds.
|
||||
/// IE 60 would give results for the past minute.
|
||||
/// Use Zero to override the local_site and local_user time_range.
|
||||
pub time_range_seconds: Option<i32>,
|
||||
/// DEPRECATED, use page_cursor
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page: Option<i64>,
|
||||
|
@ -122,7 +125,7 @@ pub struct GetPosts {
|
|||
/// If true, then only show posts with no comments
|
||||
pub no_comments_only: Option<bool>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_cursor: Option<PaginationCursor>,
|
||||
pub page_cursor: Option<PostPaginationCursor>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_back: Option<bool>,
|
||||
}
|
||||
|
@ -136,7 +139,7 @@ pub struct GetPostsResponse {
|
|||
pub posts: Vec<PostView>,
|
||||
/// the pagination cursor to use to fetch the next page
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub next_page: Option<PaginationCursor>,
|
||||
pub next_page: Option<PostPaginationCursor>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use lemmy_db_schema::newtypes::{PersonId, PrivateMessageId};
|
||||
use lemmy_db_views_actor::structs::PrivateMessageView;
|
||||
use lemmy_db_views::structs::PrivateMessageView;
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(feature = "full")]
|
||||
use ts_rs::TS;
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use lemmy_db_schema::newtypes::CommunityId;
|
||||
use lemmy_db_views::structs::{ReportCombinedPaginationCursor, ReportCombinedView};
|
||||
use lemmy_db_schema::{
|
||||
newtypes::{CommunityId, PaginationCursor, PostId},
|
||||
ReportType,
|
||||
};
|
||||
use lemmy_db_views::structs::ReportCombinedView;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
#[cfg(feature = "full")]
|
||||
|
@ -14,13 +17,25 @@ pub struct ListReports {
|
|||
/// Only shows the unresolved reports
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub unresolved_only: Option<bool>,
|
||||
/// Filter the type of report.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub type_: Option<ReportType>,
|
||||
/// Filter by the post id. Can return either comment or post reports.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub post_id: Option<PostId>,
|
||||
/// if no community is given, it returns reports for all communities moderated by the auth user
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub community_id: Option<CommunityId>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_cursor: Option<ReportCombinedPaginationCursor>,
|
||||
pub page_cursor: Option<PaginationCursor>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_back: Option<bool>,
|
||||
/// Only for admins: also show reports with `violates_instance_rules=false`
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub show_community_rule_violations: Option<bool>,
|
||||
/// If true, view all your created reports. Works for non-admins/mods also.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub my_reports_only: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
|
@ -29,4 +44,7 @@ pub struct ListReports {
|
|||
/// The post reports response.
|
||||
pub struct ListReportsResponse {
|
||||
pub reports: Vec<ReportCombinedView>,
|
||||
/// the pagination cursor to use to fetch the next page
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub next_page: Option<PaginationCursor>,
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue