Merge branch 'main' into update-diesel

This commit is contained in:
Dessalines 2024-09-16 11:19:28 -04:00 committed by GitHub
commit f92a8a0adc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
90 changed files with 1255 additions and 2888 deletions

View file

@ -157,7 +157,7 @@ steps:
CARGO_HOME: .cargo_home CARGO_HOME: .cargo_home
commands: commands:
- rustup component add clippy - rustup component add clippy
- cargo clippy --workspace --tests --all-targets --features console -- -D warnings - cargo clippy --workspace --tests --all-targets -- -D warnings
when: *slow_check_paths when: *slow_check_paths
cargo_build: cargo_build:
@ -240,10 +240,13 @@ steps:
publish_release_docker: publish_release_docker:
image: woodpeckerci/plugin-docker-buildx image: woodpeckerci/plugin-docker-buildx
secrets: [docker_username, docker_password]
settings: settings:
repo: dessalines/lemmy repo: dessalines/lemmy
dockerfile: docker/Dockerfile dockerfile: docker/Dockerfile
username:
from_secret: docker_username
password:
from_secret: docker_password
platforms: linux/amd64, linux/arm64 platforms: linux/amd64, linux/arm64
build_args: build_args:
- RUST_RELEASE_MODE=release - RUST_RELEASE_MODE=release
@ -253,10 +256,13 @@ steps:
nightly_build: nightly_build:
image: woodpeckerci/plugin-docker-buildx image: woodpeckerci/plugin-docker-buildx
secrets: [docker_username, docker_password]
settings: settings:
repo: dessalines/lemmy repo: dessalines/lemmy
dockerfile: docker/Dockerfile dockerfile: docker/Dockerfile
username:
from_secret: docker_username
password:
from_secret: docker_password
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
build_args: build_args:
- RUST_RELEASE_MODE=release - RUST_RELEASE_MODE=release

2677
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
[workspace.package] [workspace.package]
version = "0.19.6-beta.6" version = "0.19.6-beta.7"
edition = "2021" edition = "2021"
description = "A link aggregator for the fediverse" description = "A link aggregator for the fediverse"
license = "AGPL-3.0" license = "AGPL-3.0"
@ -36,16 +36,6 @@ opt-level = "z" # Optimize for size.
debug = 0 debug = 0
[features] [features]
embed-pictrs = ["pict-rs"]
# This feature requires building with `tokio_unstable` flag, see documentation:
# https://docs.rs/tokio/latest/tokio/#unstable-features
console = [
"console-subscriber",
"opentelemetry",
"opentelemetry-otlp",
"tracing-opentelemetry",
"reqwest-tracing/opentelemetry_0_16",
]
json-log = ["tracing-subscriber/json"] json-log = ["tracing-subscriber/json"]
default = [] default = []
@ -89,18 +79,18 @@ unwrap_used = "deny"
unimplemented = "deny" unimplemented = "deny"
[workspace.dependencies] [workspace.dependencies]
lemmy_api = { version = "=0.19.6-beta.6", path = "./crates/api" } lemmy_api = { version = "=0.19.6-beta.7", path = "./crates/api" }
lemmy_api_crud = { version = "=0.19.6-beta.6", path = "./crates/api_crud" } lemmy_api_crud = { version = "=0.19.6-beta.7", path = "./crates/api_crud" }
lemmy_apub = { version = "=0.19.6-beta.6", path = "./crates/apub" } lemmy_apub = { version = "=0.19.6-beta.7", path = "./crates/apub" }
lemmy_utils = { version = "=0.19.6-beta.6", path = "./crates/utils", default-features = false } lemmy_utils = { version = "=0.19.6-beta.7", path = "./crates/utils", default-features = false }
lemmy_db_schema = { version = "=0.19.6-beta.6", path = "./crates/db_schema" } lemmy_db_schema = { version = "=0.19.6-beta.7", path = "./crates/db_schema" }
lemmy_api_common = { version = "=0.19.6-beta.6", path = "./crates/api_common" } lemmy_api_common = { version = "=0.19.6-beta.7", path = "./crates/api_common" }
lemmy_routes = { version = "=0.19.6-beta.6", path = "./crates/routes" } lemmy_routes = { version = "=0.19.6-beta.7", path = "./crates/routes" }
lemmy_db_views = { version = "=0.19.6-beta.6", path = "./crates/db_views" } lemmy_db_views = { version = "=0.19.6-beta.7", path = "./crates/db_views" }
lemmy_db_views_actor = { version = "=0.19.6-beta.6", path = "./crates/db_views_actor" } lemmy_db_views_actor = { version = "=0.19.6-beta.7", path = "./crates/db_views_actor" }
lemmy_db_views_moderator = { version = "=0.19.6-beta.6", path = "./crates/db_views_moderator" } lemmy_db_views_moderator = { version = "=0.19.6-beta.7", path = "./crates/db_views_moderator" }
lemmy_federate = { version = "=0.19.6-beta.6", path = "./crates/federate" } lemmy_federate = { version = "=0.19.6-beta.7", path = "./crates/federate" }
activitypub_federation = { version = "0.5.8", default-features = false, features = [ activitypub_federation = { version = "0.6.0-alpha1", default-features = false, features = [
"actix-web", "actix-web",
] } ] }
diesel = "2.2.3" diesel = "2.2.3"
@ -108,7 +98,7 @@ diesel_migrations = "2.1.0"
diesel-async = "0.5.0" diesel-async = "0.5.0"
serde = { version = "1.0.204", features = ["derive"] } serde = { version = "1.0.204", features = ["derive"] }
serde_with = "3.9.0" serde_with = "3.9.0"
actix-web = { version = "4.8.0", default-features = false, features = [ actix-web = { version = "4.9.0", default-features = false, features = [
"macros", "macros",
"rustls-0_23", "rustls-0_23",
"compress-brotli", "compress-brotli",
@ -117,19 +107,17 @@ actix-web = { version = "4.8.0", default-features = false, features = [
"cookies", "cookies",
] } ] }
tracing = "0.1.40" tracing = "0.1.40"
tracing-actix-web = { version = "0.7.11", default-features = false } tracing-actix-web = { version = "0.7.10", default-features = false }
tracing-error = "0.2.0"
tracing-log = "0.2.0"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
url = { version = "2.5.2", features = ["serde"] } url = { version = "2.5.2", features = ["serde"] }
reqwest = { version = "0.11.27", default-features = false, features = [ reqwest = { version = "0.12.7", default-features = false, features = [
"json", "json",
"blocking", "blocking",
"gzip", "gzip",
"rustls-tls", "rustls-tls",
] } ] }
reqwest-middleware = "0.2.5" reqwest-middleware = "0.3.3"
reqwest-tracing = "0.4.8" reqwest-tracing = "0.5.3"
clokwerk = "0.4.0" clokwerk = "0.4.0"
doku = { version = "0.21.1", features = ["url-2"] } doku = { version = "0.21.1", features = ["url-2"] }
bcrypt = "0.15.1" bcrypt = "0.15.1"
@ -152,10 +140,8 @@ diesel-derive-enum = { version = "2.1.0", features = ["postgres"] }
strum = { version = "0.26.3", features = ["derive"] } strum = { version = "0.26.3", features = ["derive"] }
itertools = "0.13.0" itertools = "0.13.0"
futures = "0.3.30" futures = "0.3.30"
http = "0.2.12" http = "1.1"
rosetta-i18n = "0.1.3" rosetta-i18n = "0.1.3"
opentelemetry = { version = "0.19.0", features = ["rt-tokio"] }
tracing-opentelemetry = { version = "0.19.0" }
ts-rs = { version = "7.1.1", features = [ ts-rs = { version = "7.1.1", features = [
"serde-compat", "serde-compat",
"chrono-impl", "chrono-impl",
@ -171,7 +157,7 @@ moka = { version = "0.12.8", features = ["future"] }
i-love-jesus = { version = "0.1.0" } i-love-jesus = { version = "0.1.0" }
clap = { version = "4.5.13", features = ["derive", "env"] } clap = { version = "4.5.13", features = ["derive", "env"] }
pretty_assertions = "1.4.0" pretty_assertions = "1.4.0"
derive-new = "0.6.0" derive-new = "0.7.0"
[dependencies] [dependencies]
lemmy_api = { workspace = true } lemmy_api = { workspace = true }
@ -188,8 +174,6 @@ diesel-async = { workspace = true }
actix-web = { workspace = true } actix-web = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
tracing-actix-web = { workspace = true } tracing-actix-web = { workspace = true }
tracing-error = { workspace = true }
tracing-log = { workspace = true }
tracing-subscriber = { workspace = true } tracing-subscriber = { workspace = true }
url = { workspace = true } url = { workspace = true }
reqwest = { workspace = true } reqwest = { workspace = true }
@ -197,11 +181,6 @@ reqwest-middleware = { workspace = true }
reqwest-tracing = { workspace = true } reqwest-tracing = { workspace = true }
clokwerk = { workspace = true } clokwerk = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
tracing-opentelemetry = { workspace = true, optional = true }
opentelemetry = { workspace = true, optional = true }
console-subscriber = { version = "0.4.0", optional = true }
opentelemetry-otlp = { version = "0.12.0", optional = true }
pict-rs = { version = "0.5.16", optional = true }
rustls = { workspace = true } rustls = { workspace = true }
tokio.workspace = true tokio.workspace = true
actix-cors = "0.7.0" actix-cors = "0.7.0"

View file

@ -6,7 +6,7 @@
"repository": "https://github.com/LemmyNet/lemmy", "repository": "https://github.com/LemmyNet/lemmy",
"author": "Dessalines", "author": "Dessalines",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"packageManager": "pnpm@9.6.0", "packageManager": "pnpm@9.9.0",
"scripts": { "scripts": {
"lint": "tsc --noEmit && eslint --report-unused-disable-directives && prettier --check 'src/**/*.ts'", "lint": "tsc --noEmit && eslint --report-unused-disable-directives && prettier --check 'src/**/*.ts'",
"fix": "prettier --write src && eslint --fix src", "fix": "prettier --write src && eslint --fix src",

View file

@ -13,22 +13,22 @@ importers:
version: 29.5.12 version: 29.5.12
'@types/node': '@types/node':
specifier: ^22.0.2 specifier: ^22.0.2
version: 22.0.2 version: 22.5.1
'@typescript-eslint/eslint-plugin': '@typescript-eslint/eslint-plugin':
specifier: ^8.0.0 specifier: ^8.0.0
version: 8.0.0(@typescript-eslint/parser@8.0.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)(typescript@5.5.4) version: 8.0.0(@typescript-eslint/parser@8.0.0(eslint@9.9.1)(typescript@5.5.4))(eslint@9.9.1)(typescript@5.5.4)
'@typescript-eslint/parser': '@typescript-eslint/parser':
specifier: ^8.0.0 specifier: ^8.0.0
version: 8.0.0(eslint@9.8.0)(typescript@5.5.4) version: 8.0.0(eslint@9.9.1)(typescript@5.5.4)
eslint: eslint:
specifier: ^9.8.0 specifier: ^9.8.0
version: 9.8.0 version: 9.9.1
eslint-plugin-prettier: eslint-plugin-prettier:
specifier: ^5.1.3 specifier: ^5.1.3
version: 5.2.1(eslint@9.8.0)(prettier@3.3.3) version: 5.2.1(eslint@9.9.1)(prettier@3.3.3)
jest: jest:
specifier: ^29.5.0 specifier: ^29.5.0
version: 29.7.0(@types/node@22.0.2) version: 29.7.0(@types/node@22.5.1)
lemmy-js-client: lemmy-js-client:
specifier: 0.19.5-alpha.1 specifier: 0.19.5-alpha.1
version: 0.19.5-alpha.1 version: 0.19.5-alpha.1
@ -37,20 +37,16 @@ importers:
version: 3.3.3 version: 3.3.3
ts-jest: ts-jest:
specifier: ^29.1.0 specifier: ^29.1.0
version: 29.2.4(@babel/core@7.23.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(jest@29.7.0(@types/node@22.0.2))(typescript@5.5.4) version: 29.2.5(@babel/core@7.23.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(jest@29.7.0(@types/node@22.5.1))(typescript@5.5.4)
typescript: typescript:
specifier: ^5.5.4 specifier: ^5.5.4
version: 5.5.4 version: 5.5.4
typescript-eslint: typescript-eslint:
specifier: ^8.0.0 specifier: ^8.0.0
version: 8.0.0(eslint@9.8.0)(typescript@5.5.4) version: 8.0.0(eslint@9.9.1)(typescript@5.5.4)
packages: packages:
'@aashutoshrathi/word-wrap@1.2.6':
resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
engines: {node: '>=0.10.0'}
'@ampproject/remapping@2.2.1': '@ampproject/remapping@2.2.1':
resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
@ -232,16 +228,16 @@ packages:
resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
'@eslint/config-array@0.17.1': '@eslint/config-array@0.18.0':
resolution: {integrity: sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==} resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/eslintrc@3.1.0': '@eslint/eslintrc@3.1.0':
resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/js@9.8.0': '@eslint/js@9.9.1':
resolution: {integrity: sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA==} resolution: {integrity: sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/object-schema@2.1.4': '@eslint/object-schema@2.1.4':
@ -400,8 +396,8 @@ packages:
'@types/jest@29.5.12': '@types/jest@29.5.12':
resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==} resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==}
'@types/node@22.0.2': '@types/node@22.5.1':
resolution: {integrity: sha512-yPL6DyFwY5PiMVEwymNeqUTKsDczQBJ/5T7W/46RwLU/VH+AA8aT5TZkvBviLKLbbm0hlfftEkGrNzfRk/fofQ==} resolution: {integrity: sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==}
'@types/stack-utils@2.0.3': '@types/stack-utils@2.0.3':
resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==}
@ -516,8 +512,8 @@ packages:
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
engines: {node: '>=8'} engines: {node: '>=8'}
async@3.2.5: async@3.2.6:
resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
babel-jest@29.7.0: babel-jest@29.7.0:
resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==}
@ -649,15 +645,6 @@ packages:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
debug@4.3.4:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
debug@4.3.6: debug@4.3.6:
resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==}
engines: {node: '>=6.0'} engines: {node: '>=6.0'}
@ -754,10 +741,15 @@ packages:
resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
eslint@9.8.0: eslint@9.9.1:
resolution: {integrity: sha512-K8qnZ/QJzT2dLKdZJVX6W4XOwBzutMYmt0lqUS+JdXgd+HTYFlonFgkJ8s44d/zMPPCnOOk0kMWCApCPhiOy9A==} resolution: {integrity: sha512-dHvhrbfr4xFQ9/dq+jcVneZMyRYLjggWjk6RVsIiHsP8Rz6yZ8LvZ//iU4TrZF+SXWG+JkNF2OyiZRvzgRDqMg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
hasBin: true hasBin: true
peerDependencies:
jiti: '*'
peerDependenciesMeta:
jiti:
optional: true
espree@10.1.0: espree@10.1.0:
resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==} resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==}
@ -768,8 +760,8 @@ packages:
engines: {node: '>=4'} engines: {node: '>=4'}
hasBin: true hasBin: true
esquery@1.5.0: esquery@1.6.0:
resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
engines: {node: '>=0.10'} engines: {node: '>=0.10'}
esrecurse@4.3.0: esrecurse@4.3.0:
@ -928,6 +920,10 @@ packages:
resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
engines: {node: '>= 4'} engines: {node: '>= 4'}
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
import-fresh@3.3.0: import-fresh@3.3.0:
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -1281,8 +1277,8 @@ packages:
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
engines: {node: '>=6'} engines: {node: '>=6'}
optionator@0.9.3: optionator@0.9.4:
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
p-limit@2.3.0: p-limit@2.3.0:
@ -1416,11 +1412,6 @@ packages:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true hasBin: true
semver@7.6.2:
resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==}
engines: {node: '>=10'}
hasBin: true
semver@7.6.3: semver@7.6.3:
resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -1526,8 +1517,8 @@ packages:
peerDependencies: peerDependencies:
typescript: '>=4.2.0' typescript: '>=4.2.0'
ts-jest@29.2.4: ts-jest@29.2.5:
resolution: {integrity: sha512-3d6tgDyhCI29HlpwIq87sNuI+3Q6GLTTCeYRHCs7vDz+/3GCMwEtV9jezLyl4ZtnBgx00I7hm8PCP8cTksMGrw==} resolution: {integrity: sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==}
engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@ -1579,8 +1570,8 @@ packages:
engines: {node: '>=14.17'} engines: {node: '>=14.17'}
hasBin: true hasBin: true
undici-types@6.11.1: undici-types@6.19.8:
resolution: {integrity: sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ==} resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
update-browserslist-db@1.0.13: update-browserslist-db@1.0.13:
resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
@ -1603,6 +1594,10 @@ packages:
engines: {node: '>= 8'} engines: {node: '>= 8'}
hasBin: true hasBin: true
word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
wrap-ansi@7.0.0: wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -1635,8 +1630,6 @@ packages:
snapshots: snapshots:
'@aashutoshrathi/word-wrap@1.2.6': {}
'@ampproject/remapping@2.2.1': '@ampproject/remapping@2.2.1':
dependencies: dependencies:
'@jridgewell/gen-mapping': 0.3.3 '@jridgewell/gen-mapping': 0.3.3
@ -1841,17 +1834,17 @@ snapshots:
'@bcoe/v8-coverage@0.2.3': {} '@bcoe/v8-coverage@0.2.3': {}
'@eslint-community/eslint-utils@4.4.0(eslint@9.8.0)': '@eslint-community/eslint-utils@4.4.0(eslint@9.9.1)':
dependencies: dependencies:
eslint: 9.8.0 eslint: 9.9.1
eslint-visitor-keys: 3.4.3 eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.11.0': {} '@eslint-community/regexpp@4.11.0': {}
'@eslint/config-array@0.17.1': '@eslint/config-array@0.18.0':
dependencies: dependencies:
'@eslint/object-schema': 2.1.4 '@eslint/object-schema': 2.1.4
debug: 4.3.4 debug: 4.3.6
minimatch: 3.1.2 minimatch: 3.1.2
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -1859,10 +1852,10 @@ snapshots:
'@eslint/eslintrc@3.1.0': '@eslint/eslintrc@3.1.0':
dependencies: dependencies:
ajv: 6.12.6 ajv: 6.12.6
debug: 4.3.4 debug: 4.3.6
espree: 10.1.0 espree: 10.1.0
globals: 14.0.0 globals: 14.0.0
ignore: 5.3.1 ignore: 5.3.2
import-fresh: 3.3.0 import-fresh: 3.3.0
js-yaml: 4.1.0 js-yaml: 4.1.0
minimatch: 3.1.2 minimatch: 3.1.2
@ -1870,7 +1863,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@eslint/js@9.8.0': {} '@eslint/js@9.9.1': {}
'@eslint/object-schema@2.1.4': {} '@eslint/object-schema@2.1.4': {}
@ -1891,7 +1884,7 @@ snapshots:
'@jest/console@29.7.0': '@jest/console@29.7.0':
dependencies: dependencies:
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/node': 22.0.2 '@types/node': 22.5.1
chalk: 4.1.2 chalk: 4.1.2
jest-message-util: 29.7.0 jest-message-util: 29.7.0
jest-util: 29.7.0 jest-util: 29.7.0
@ -1904,14 +1897,14 @@ snapshots:
'@jest/test-result': 29.7.0 '@jest/test-result': 29.7.0
'@jest/transform': 29.7.0 '@jest/transform': 29.7.0
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/node': 22.0.2 '@types/node': 22.5.1
ansi-escapes: 4.3.2 ansi-escapes: 4.3.2
chalk: 4.1.2 chalk: 4.1.2
ci-info: 3.9.0 ci-info: 3.9.0
exit: 0.1.2 exit: 0.1.2
graceful-fs: 4.2.11 graceful-fs: 4.2.11
jest-changed-files: 29.7.0 jest-changed-files: 29.7.0
jest-config: 29.7.0(@types/node@22.0.2) jest-config: 29.7.0(@types/node@22.5.1)
jest-haste-map: 29.7.0 jest-haste-map: 29.7.0
jest-message-util: 29.7.0 jest-message-util: 29.7.0
jest-regex-util: 29.6.3 jest-regex-util: 29.6.3
@ -1936,7 +1929,7 @@ snapshots:
dependencies: dependencies:
'@jest/fake-timers': 29.7.0 '@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/node': 22.0.2 '@types/node': 22.5.1
jest-mock: 29.7.0 jest-mock: 29.7.0
'@jest/expect-utils@29.7.0': '@jest/expect-utils@29.7.0':
@ -1954,7 +1947,7 @@ snapshots:
dependencies: dependencies:
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@sinonjs/fake-timers': 10.3.0 '@sinonjs/fake-timers': 10.3.0
'@types/node': 22.0.2 '@types/node': 22.5.1
jest-message-util: 29.7.0 jest-message-util: 29.7.0
jest-mock: 29.7.0 jest-mock: 29.7.0
jest-util: 29.7.0 jest-util: 29.7.0
@ -1976,7 +1969,7 @@ snapshots:
'@jest/transform': 29.7.0 '@jest/transform': 29.7.0
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@jridgewell/trace-mapping': 0.3.22 '@jridgewell/trace-mapping': 0.3.22
'@types/node': 22.0.2 '@types/node': 22.5.1
chalk: 4.1.2 chalk: 4.1.2
collect-v8-coverage: 1.0.2 collect-v8-coverage: 1.0.2
exit: 0.1.2 exit: 0.1.2
@ -2046,7 +2039,7 @@ snapshots:
'@jest/schemas': 29.6.3 '@jest/schemas': 29.6.3
'@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4 '@types/istanbul-reports': 3.0.4
'@types/node': 22.0.2 '@types/node': 22.5.1
'@types/yargs': 17.0.32 '@types/yargs': 17.0.32
chalk: 4.1.2 chalk: 4.1.2
@ -2114,7 +2107,7 @@ snapshots:
'@types/graceful-fs@4.1.9': '@types/graceful-fs@4.1.9':
dependencies: dependencies:
'@types/node': 22.0.2 '@types/node': 22.5.1
'@types/istanbul-lib-coverage@2.0.6': {} '@types/istanbul-lib-coverage@2.0.6': {}
@ -2131,9 +2124,9 @@ snapshots:
expect: 29.7.0 expect: 29.7.0
pretty-format: 29.7.0 pretty-format: 29.7.0
'@types/node@22.0.2': '@types/node@22.5.1':
dependencies: dependencies:
undici-types: 6.11.1 undici-types: 6.19.8
'@types/stack-utils@2.0.3': {} '@types/stack-utils@2.0.3': {}
@ -2143,15 +2136,15 @@ snapshots:
dependencies: dependencies:
'@types/yargs-parser': 21.0.3 '@types/yargs-parser': 21.0.3
'@typescript-eslint/eslint-plugin@8.0.0(@typescript-eslint/parser@8.0.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)(typescript@5.5.4)': '@typescript-eslint/eslint-plugin@8.0.0(@typescript-eslint/parser@8.0.0(eslint@9.9.1)(typescript@5.5.4))(eslint@9.9.1)(typescript@5.5.4)':
dependencies: dependencies:
'@eslint-community/regexpp': 4.11.0 '@eslint-community/regexpp': 4.11.0
'@typescript-eslint/parser': 8.0.0(eslint@9.8.0)(typescript@5.5.4) '@typescript-eslint/parser': 8.0.0(eslint@9.9.1)(typescript@5.5.4)
'@typescript-eslint/scope-manager': 8.0.0 '@typescript-eslint/scope-manager': 8.0.0
'@typescript-eslint/type-utils': 8.0.0(eslint@9.8.0)(typescript@5.5.4) '@typescript-eslint/type-utils': 8.0.0(eslint@9.9.1)(typescript@5.5.4)
'@typescript-eslint/utils': 8.0.0(eslint@9.8.0)(typescript@5.5.4) '@typescript-eslint/utils': 8.0.0(eslint@9.9.1)(typescript@5.5.4)
'@typescript-eslint/visitor-keys': 8.0.0 '@typescript-eslint/visitor-keys': 8.0.0
eslint: 9.8.0 eslint: 9.9.1
graphemer: 1.4.0 graphemer: 1.4.0
ignore: 5.3.1 ignore: 5.3.1
natural-compare: 1.4.0 natural-compare: 1.4.0
@ -2161,14 +2154,14 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/parser@8.0.0(eslint@9.8.0)(typescript@5.5.4)': '@typescript-eslint/parser@8.0.0(eslint@9.9.1)(typescript@5.5.4)':
dependencies: dependencies:
'@typescript-eslint/scope-manager': 8.0.0 '@typescript-eslint/scope-manager': 8.0.0
'@typescript-eslint/types': 8.0.0 '@typescript-eslint/types': 8.0.0
'@typescript-eslint/typescript-estree': 8.0.0(typescript@5.5.4) '@typescript-eslint/typescript-estree': 8.0.0(typescript@5.5.4)
'@typescript-eslint/visitor-keys': 8.0.0 '@typescript-eslint/visitor-keys': 8.0.0
debug: 4.3.6 debug: 4.3.6
eslint: 9.8.0 eslint: 9.9.1
optionalDependencies: optionalDependencies:
typescript: 5.5.4 typescript: 5.5.4
transitivePeerDependencies: transitivePeerDependencies:
@ -2179,10 +2172,10 @@ snapshots:
'@typescript-eslint/types': 8.0.0 '@typescript-eslint/types': 8.0.0
'@typescript-eslint/visitor-keys': 8.0.0 '@typescript-eslint/visitor-keys': 8.0.0
'@typescript-eslint/type-utils@8.0.0(eslint@9.8.0)(typescript@5.5.4)': '@typescript-eslint/type-utils@8.0.0(eslint@9.9.1)(typescript@5.5.4)':
dependencies: dependencies:
'@typescript-eslint/typescript-estree': 8.0.0(typescript@5.5.4) '@typescript-eslint/typescript-estree': 8.0.0(typescript@5.5.4)
'@typescript-eslint/utils': 8.0.0(eslint@9.8.0)(typescript@5.5.4) '@typescript-eslint/utils': 8.0.0(eslint@9.9.1)(typescript@5.5.4)
debug: 4.3.6 debug: 4.3.6
ts-api-utils: 1.3.0(typescript@5.5.4) ts-api-utils: 1.3.0(typescript@5.5.4)
optionalDependencies: optionalDependencies:
@ -2208,13 +2201,13 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/utils@8.0.0(eslint@9.8.0)(typescript@5.5.4)': '@typescript-eslint/utils@8.0.0(eslint@9.9.1)(typescript@5.5.4)':
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@9.8.0) '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1)
'@typescript-eslint/scope-manager': 8.0.0 '@typescript-eslint/scope-manager': 8.0.0
'@typescript-eslint/types': 8.0.0 '@typescript-eslint/types': 8.0.0
'@typescript-eslint/typescript-estree': 8.0.0(typescript@5.5.4) '@typescript-eslint/typescript-estree': 8.0.0(typescript@5.5.4)
eslint: 9.8.0 eslint: 9.9.1
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
- typescript - typescript
@ -2266,7 +2259,7 @@ snapshots:
array-union@2.1.0: {} array-union@2.1.0: {}
async@3.2.5: {} async@3.2.6: {}
babel-jest@29.7.0(@babel/core@7.23.9): babel-jest@29.7.0(@babel/core@7.23.9):
dependencies: dependencies:
@ -2407,13 +2400,13 @@ snapshots:
convert-source-map@2.0.0: {} convert-source-map@2.0.0: {}
create-jest@29.7.0(@types/node@22.0.2): create-jest@29.7.0(@types/node@22.5.1):
dependencies: dependencies:
'@jest/types': 29.6.3 '@jest/types': 29.6.3
chalk: 4.1.2 chalk: 4.1.2
exit: 0.1.2 exit: 0.1.2
graceful-fs: 4.2.11 graceful-fs: 4.2.11
jest-config: 29.7.0(@types/node@22.0.2) jest-config: 29.7.0(@types/node@22.5.1)
jest-util: 29.7.0 jest-util: 29.7.0
prompts: 2.4.2 prompts: 2.4.2
transitivePeerDependencies: transitivePeerDependencies:
@ -2428,10 +2421,6 @@ snapshots:
shebang-command: 2.0.0 shebang-command: 2.0.0
which: 2.0.2 which: 2.0.2
debug@4.3.4:
dependencies:
ms: 2.1.2
debug@4.3.6: debug@4.3.6:
dependencies: dependencies:
ms: 2.1.2 ms: 2.1.2
@ -2472,9 +2461,9 @@ snapshots:
escape-string-regexp@4.0.0: {} escape-string-regexp@4.0.0: {}
eslint-plugin-prettier@5.2.1(eslint@9.8.0)(prettier@3.3.3): eslint-plugin-prettier@5.2.1(eslint@9.9.1)(prettier@3.3.3):
dependencies: dependencies:
eslint: 9.8.0 eslint: 9.9.1
prettier: 3.3.3 prettier: 3.3.3
prettier-linter-helpers: 1.0.0 prettier-linter-helpers: 1.0.0
synckit: 0.9.1 synckit: 0.9.1
@ -2488,31 +2477,31 @@ snapshots:
eslint-visitor-keys@4.0.0: {} eslint-visitor-keys@4.0.0: {}
eslint@9.8.0: eslint@9.9.1:
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@9.8.0) '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1)
'@eslint-community/regexpp': 4.11.0 '@eslint-community/regexpp': 4.11.0
'@eslint/config-array': 0.17.1 '@eslint/config-array': 0.18.0
'@eslint/eslintrc': 3.1.0 '@eslint/eslintrc': 3.1.0
'@eslint/js': 9.8.0 '@eslint/js': 9.9.1
'@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/module-importer': 1.0.1
'@humanwhocodes/retry': 0.3.0 '@humanwhocodes/retry': 0.3.0
'@nodelib/fs.walk': 1.2.8 '@nodelib/fs.walk': 1.2.8
ajv: 6.12.6 ajv: 6.12.6
chalk: 4.1.2 chalk: 4.1.2
cross-spawn: 7.0.3 cross-spawn: 7.0.3
debug: 4.3.4 debug: 4.3.6
escape-string-regexp: 4.0.0 escape-string-regexp: 4.0.0
eslint-scope: 8.0.2 eslint-scope: 8.0.2
eslint-visitor-keys: 4.0.0 eslint-visitor-keys: 4.0.0
espree: 10.1.0 espree: 10.1.0
esquery: 1.5.0 esquery: 1.6.0
esutils: 2.0.3 esutils: 2.0.3
fast-deep-equal: 3.1.3 fast-deep-equal: 3.1.3
file-entry-cache: 8.0.0 file-entry-cache: 8.0.0
find-up: 5.0.0 find-up: 5.0.0
glob-parent: 6.0.2 glob-parent: 6.0.2
ignore: 5.3.1 ignore: 5.3.2
imurmurhash: 0.1.4 imurmurhash: 0.1.4
is-glob: 4.0.3 is-glob: 4.0.3
is-path-inside: 3.0.3 is-path-inside: 3.0.3
@ -2521,7 +2510,7 @@ snapshots:
lodash.merge: 4.6.2 lodash.merge: 4.6.2
minimatch: 3.1.2 minimatch: 3.1.2
natural-compare: 1.4.0 natural-compare: 1.4.0
optionator: 0.9.3 optionator: 0.9.4
strip-ansi: 6.0.1 strip-ansi: 6.0.1
text-table: 0.2.0 text-table: 0.2.0
transitivePeerDependencies: transitivePeerDependencies:
@ -2535,7 +2524,7 @@ snapshots:
esprima@4.0.1: {} esprima@4.0.1: {}
esquery@1.5.0: esquery@1.6.0:
dependencies: dependencies:
estraverse: 5.3.0 estraverse: 5.3.0
@ -2667,7 +2656,7 @@ snapshots:
array-union: 2.1.0 array-union: 2.1.0
dir-glob: 3.0.1 dir-glob: 3.0.1
fast-glob: 3.3.2 fast-glob: 3.3.2
ignore: 5.3.1 ignore: 5.3.2
merge2: 1.4.1 merge2: 1.4.1
slash: 3.0.0 slash: 3.0.0
@ -2689,6 +2678,8 @@ snapshots:
ignore@5.3.1: {} ignore@5.3.1: {}
ignore@5.3.2: {}
import-fresh@3.3.0: import-fresh@3.3.0:
dependencies: dependencies:
parent-module: 1.0.1 parent-module: 1.0.1
@ -2775,7 +2766,7 @@ snapshots:
jake@10.9.2: jake@10.9.2:
dependencies: dependencies:
async: 3.2.5 async: 3.2.6
chalk: 4.1.2 chalk: 4.1.2
filelist: 1.0.4 filelist: 1.0.4
minimatch: 3.1.2 minimatch: 3.1.2
@ -2792,7 +2783,7 @@ snapshots:
'@jest/expect': 29.7.0 '@jest/expect': 29.7.0
'@jest/test-result': 29.7.0 '@jest/test-result': 29.7.0
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/node': 22.0.2 '@types/node': 22.5.1
chalk: 4.1.2 chalk: 4.1.2
co: 4.6.0 co: 4.6.0
dedent: 1.5.1 dedent: 1.5.1
@ -2812,16 +2803,16 @@ snapshots:
- babel-plugin-macros - babel-plugin-macros
- supports-color - supports-color
jest-cli@29.7.0(@types/node@22.0.2): jest-cli@29.7.0(@types/node@22.5.1):
dependencies: dependencies:
'@jest/core': 29.7.0 '@jest/core': 29.7.0
'@jest/test-result': 29.7.0 '@jest/test-result': 29.7.0
'@jest/types': 29.6.3 '@jest/types': 29.6.3
chalk: 4.1.2 chalk: 4.1.2
create-jest: 29.7.0(@types/node@22.0.2) create-jest: 29.7.0(@types/node@22.5.1)
exit: 0.1.2 exit: 0.1.2
import-local: 3.1.0 import-local: 3.1.0
jest-config: 29.7.0(@types/node@22.0.2) jest-config: 29.7.0(@types/node@22.5.1)
jest-util: 29.7.0 jest-util: 29.7.0
jest-validate: 29.7.0 jest-validate: 29.7.0
yargs: 17.7.2 yargs: 17.7.2
@ -2831,7 +2822,7 @@ snapshots:
- supports-color - supports-color
- ts-node - ts-node
jest-config@29.7.0(@types/node@22.0.2): jest-config@29.7.0(@types/node@22.5.1):
dependencies: dependencies:
'@babel/core': 7.23.9 '@babel/core': 7.23.9
'@jest/test-sequencer': 29.7.0 '@jest/test-sequencer': 29.7.0
@ -2856,7 +2847,7 @@ snapshots:
slash: 3.0.0 slash: 3.0.0
strip-json-comments: 3.1.1 strip-json-comments: 3.1.1
optionalDependencies: optionalDependencies:
'@types/node': 22.0.2 '@types/node': 22.5.1
transitivePeerDependencies: transitivePeerDependencies:
- babel-plugin-macros - babel-plugin-macros
- supports-color - supports-color
@ -2885,7 +2876,7 @@ snapshots:
'@jest/environment': 29.7.0 '@jest/environment': 29.7.0
'@jest/fake-timers': 29.7.0 '@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/node': 22.0.2 '@types/node': 22.5.1
jest-mock: 29.7.0 jest-mock: 29.7.0
jest-util: 29.7.0 jest-util: 29.7.0
@ -2895,7 +2886,7 @@ snapshots:
dependencies: dependencies:
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/graceful-fs': 4.1.9 '@types/graceful-fs': 4.1.9
'@types/node': 22.0.2 '@types/node': 22.5.1
anymatch: 3.1.3 anymatch: 3.1.3
fb-watchman: 2.0.2 fb-watchman: 2.0.2
graceful-fs: 4.2.11 graceful-fs: 4.2.11
@ -2934,7 +2925,7 @@ snapshots:
jest-mock@29.7.0: jest-mock@29.7.0:
dependencies: dependencies:
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/node': 22.0.2 '@types/node': 22.5.1
jest-util: 29.7.0 jest-util: 29.7.0
jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
@ -2969,7 +2960,7 @@ snapshots:
'@jest/test-result': 29.7.0 '@jest/test-result': 29.7.0
'@jest/transform': 29.7.0 '@jest/transform': 29.7.0
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/node': 22.0.2 '@types/node': 22.5.1
chalk: 4.1.2 chalk: 4.1.2
emittery: 0.13.1 emittery: 0.13.1
graceful-fs: 4.2.11 graceful-fs: 4.2.11
@ -2997,7 +2988,7 @@ snapshots:
'@jest/test-result': 29.7.0 '@jest/test-result': 29.7.0
'@jest/transform': 29.7.0 '@jest/transform': 29.7.0
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/node': 22.0.2 '@types/node': 22.5.1
chalk: 4.1.2 chalk: 4.1.2
cjs-module-lexer: 1.2.3 cjs-module-lexer: 1.2.3
collect-v8-coverage: 1.0.2 collect-v8-coverage: 1.0.2
@ -3036,14 +3027,14 @@ snapshots:
jest-util: 29.7.0 jest-util: 29.7.0
natural-compare: 1.4.0 natural-compare: 1.4.0
pretty-format: 29.7.0 pretty-format: 29.7.0
semver: 7.6.2 semver: 7.6.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
jest-util@29.7.0: jest-util@29.7.0:
dependencies: dependencies:
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/node': 22.0.2 '@types/node': 22.5.1
chalk: 4.1.2 chalk: 4.1.2
ci-info: 3.9.0 ci-info: 3.9.0
graceful-fs: 4.2.11 graceful-fs: 4.2.11
@ -3062,7 +3053,7 @@ snapshots:
dependencies: dependencies:
'@jest/test-result': 29.7.0 '@jest/test-result': 29.7.0
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/node': 22.0.2 '@types/node': 22.5.1
ansi-escapes: 4.3.2 ansi-escapes: 4.3.2
chalk: 4.1.2 chalk: 4.1.2
emittery: 0.13.1 emittery: 0.13.1
@ -3071,17 +3062,17 @@ snapshots:
jest-worker@29.7.0: jest-worker@29.7.0:
dependencies: dependencies:
'@types/node': 22.0.2 '@types/node': 22.5.1
jest-util: 29.7.0 jest-util: 29.7.0
merge-stream: 2.0.0 merge-stream: 2.0.0
supports-color: 8.1.1 supports-color: 8.1.1
jest@29.7.0(@types/node@22.0.2): jest@29.7.0(@types/node@22.5.1):
dependencies: dependencies:
'@jest/core': 29.7.0 '@jest/core': 29.7.0
'@jest/types': 29.6.3 '@jest/types': 29.6.3
import-local: 3.1.0 import-local: 3.1.0
jest-cli: 29.7.0(@types/node@22.0.2) jest-cli: 29.7.0(@types/node@22.5.1)
transitivePeerDependencies: transitivePeerDependencies:
- '@types/node' - '@types/node'
- babel-plugin-macros - babel-plugin-macros
@ -3204,14 +3195,14 @@ snapshots:
dependencies: dependencies:
mimic-fn: 2.1.0 mimic-fn: 2.1.0
optionator@0.9.3: optionator@0.9.4:
dependencies: dependencies:
'@aashutoshrathi/word-wrap': 1.2.6
deep-is: 0.1.4 deep-is: 0.1.4
fast-levenshtein: 2.0.6 fast-levenshtein: 2.0.6
levn: 0.4.1 levn: 0.4.1
prelude-ls: 1.2.1 prelude-ls: 1.2.1
type-check: 0.4.0 type-check: 0.4.0
word-wrap: 1.2.5
p-limit@2.3.0: p-limit@2.3.0:
dependencies: dependencies:
@ -3315,8 +3306,6 @@ snapshots:
semver@6.3.1: {} semver@6.3.1: {}
semver@7.6.2: {}
semver@7.6.3: {} semver@7.6.3: {}
shebang-command@2.0.0: shebang-command@2.0.0:
@ -3404,12 +3393,12 @@ snapshots:
dependencies: dependencies:
typescript: 5.5.4 typescript: 5.5.4
ts-jest@29.2.4(@babel/core@7.23.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(jest@29.7.0(@types/node@22.0.2))(typescript@5.5.4): ts-jest@29.2.5(@babel/core@7.23.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(jest@29.7.0(@types/node@22.5.1))(typescript@5.5.4):
dependencies: dependencies:
bs-logger: 0.2.6 bs-logger: 0.2.6
ejs: 3.1.10 ejs: 3.1.10
fast-json-stable-stringify: 2.1.0 fast-json-stable-stringify: 2.1.0
jest: 29.7.0(@types/node@22.0.2) jest: 29.7.0(@types/node@22.5.1)
jest-util: 29.7.0 jest-util: 29.7.0
json5: 2.2.3 json5: 2.2.3
lodash.memoize: 4.1.2 lodash.memoize: 4.1.2
@ -3433,11 +3422,11 @@ snapshots:
type-fest@0.21.3: {} type-fest@0.21.3: {}
typescript-eslint@8.0.0(eslint@9.8.0)(typescript@5.5.4): typescript-eslint@8.0.0(eslint@9.9.1)(typescript@5.5.4):
dependencies: dependencies:
'@typescript-eslint/eslint-plugin': 8.0.0(@typescript-eslint/parser@8.0.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)(typescript@5.5.4) '@typescript-eslint/eslint-plugin': 8.0.0(@typescript-eslint/parser@8.0.0(eslint@9.9.1)(typescript@5.5.4))(eslint@9.9.1)(typescript@5.5.4)
'@typescript-eslint/parser': 8.0.0(eslint@9.8.0)(typescript@5.5.4) '@typescript-eslint/parser': 8.0.0(eslint@9.9.1)(typescript@5.5.4)
'@typescript-eslint/utils': 8.0.0(eslint@9.8.0)(typescript@5.5.4) '@typescript-eslint/utils': 8.0.0(eslint@9.9.1)(typescript@5.5.4)
optionalDependencies: optionalDependencies:
typescript: 5.5.4 typescript: 5.5.4
transitivePeerDependencies: transitivePeerDependencies:
@ -3446,7 +3435,7 @@ snapshots:
typescript@5.5.4: {} typescript@5.5.4: {}
undici-types@6.11.1: {} undici-types@6.19.8: {}
update-browserslist-db@1.0.13(browserslist@4.22.3): update-browserslist-db@1.0.13(browserslist@4.22.3):
dependencies: dependencies:
@ -3472,6 +3461,8 @@ snapshots:
dependencies: dependencies:
isexe: 2.0.0 isexe: 2.0.0
word-wrap@1.2.5: {}
wrap-ansi@7.0.0: wrap-ansi@7.0.0:
dependencies: dependencies:
ansi-styles: 4.3.0 ansi-styles: 4.3.0

View file

@ -2,8 +2,11 @@ use crate::{build_totp_2fa, generate_totp_2fa_secret};
use activitypub_federation::config::Data; use activitypub_federation::config::Data;
use actix_web::web::Json; use actix_web::web::Json;
use lemmy_api_common::{context::LemmyContext, person::GenerateTotpSecretResponse}; use lemmy_api_common::{context::LemmyContext, person::GenerateTotpSecretResponse};
use lemmy_db_schema::source::local_user::{LocalUser, LocalUserUpdateForm}; use lemmy_db_schema::source::{
use lemmy_db_views::structs::{LocalUserView, SiteView}; local_user::{LocalUser, LocalUserUpdateForm},
site::Site,
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use lemmy_utils::error::{LemmyErrorType, LemmyResult};
/// Generate a new secret for two-factor-authentication. Afterwards you need to call [toggle_totp] /// Generate a new secret for two-factor-authentication. Afterwards you need to call [toggle_totp]
@ -13,17 +16,14 @@ pub async fn generate_totp_secret(
local_user_view: LocalUserView, local_user_view: LocalUserView,
context: Data<LemmyContext>, context: Data<LemmyContext>,
) -> LemmyResult<Json<GenerateTotpSecretResponse>> { ) -> LemmyResult<Json<GenerateTotpSecretResponse>> {
let site_view = SiteView::read_local(&mut context.pool()) let site = Site::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
if local_user_view.local_user.totp_2fa_enabled { if local_user_view.local_user.totp_2fa_enabled {
return Err(LemmyErrorType::TotpAlreadyEnabled)?; return Err(LemmyErrorType::TotpAlreadyEnabled)?;
} }
let secret = generate_totp_2fa_secret(); let secret = generate_totp_2fa_secret();
let secret_url = let secret_url = build_totp_2fa(&site.name, &local_user_view.person.name, &secret)?.get_url();
build_totp_2fa(&site_view.site.name, &local_user_view.person.name, &secret)?.get_url();
let local_user_form = LocalUserUpdateForm { let local_user_form = LocalUserUpdateForm {
totp_2fa_secret: Some(Some(secret)), totp_2fa_secret: Some(Some(secret)),

View file

@ -1,5 +1,5 @@
use actix_web::web::{Data, Json}; use actix_web::web::{Data, Json};
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::{context::LemmyContext, person::ListLoginsResponse};
use lemmy_db_schema::source::login_token::LoginToken; use lemmy_db_schema::source::login_token::LoginToken;
use lemmy_db_views::structs::LocalUserView; use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::LemmyResult; use lemmy_utils::error::LemmyResult;
@ -7,8 +7,8 @@ use lemmy_utils::error::LemmyResult;
pub async fn list_logins( pub async fn list_logins(
context: Data<LemmyContext>, context: Data<LemmyContext>,
local_user_view: LocalUserView, local_user_view: LocalUserView,
) -> LemmyResult<Json<Vec<LoginToken>>> { ) -> LemmyResult<Json<ListLoginsResponse>> {
let logins = LoginToken::list(&mut context.pool(), local_user_view.local_user.id).await?; let logins = LoginToken::list(&mut context.pool(), local_user_view.local_user.id).await?;
Ok(Json(logins)) Ok(Json(ListLoginsResponse { logins }))
} }

View file

@ -24,9 +24,7 @@ pub async fn login(
req: HttpRequest, req: HttpRequest,
context: Data<LemmyContext>, context: Data<LemmyContext>,
) -> LemmyResult<Json<LoginResponse>> { ) -> LemmyResult<Json<LoginResponse>> {
let site_view = SiteView::read_local(&mut context.pool()) let site_view = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
// Fetch that username / email // Fetch that username / email
let username_or_email = data.username_or_email.clone(); let username_or_email = data.username_or_email.clone();

View file

@ -20,9 +20,7 @@ pub async fn reset_password(
.await? .await?
.ok_or(LemmyErrorType::IncorrectLogin)?; .ok_or(LemmyErrorType::IncorrectLogin)?;
let site_view = SiteView::read_local(&mut context.pool()) let site_view = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
check_email_verified(&local_user_view, &site_view)?; check_email_verified(&local_user_view, &site_view)?;
// Email the pure token to the user. // Email the pure token to the user.

View file

@ -36,9 +36,7 @@ pub async fn save_user_settings(
context: Data<LemmyContext>, context: Data<LemmyContext>,
local_user_view: LocalUserView, local_user_view: LocalUserView,
) -> LemmyResult<Json<SuccessResponse>> { ) -> LemmyResult<Json<SuccessResponse>> {
let site_view = SiteView::read_local(&mut context.pool()) let site_view = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
let slur_regex = local_site_to_slur_regex(&site_view.local_site); let slur_regex = local_site_to_slur_regex(&site_view.local_site);
let url_blocklist = get_url_blocklist(&context).await?; let url_blocklist = get_url_blocklist(&context).await?;
@ -135,7 +133,6 @@ pub async fn save_user_settings(
blur_nsfw: data.blur_nsfw, blur_nsfw: data.blur_nsfw,
auto_expand: data.auto_expand, auto_expand: data.auto_expand,
show_bot_accounts: data.show_bot_accounts, show_bot_accounts: data.show_bot_accounts,
show_scores: data.show_scores,
default_sort_type, default_sort_type,
default_listing_type, default_listing_type,
theme: data.theme.clone(), theme: data.theme.clone(),

View file

@ -16,9 +16,7 @@ pub async fn verify_email(
data: Json<VerifyEmail>, data: Json<VerifyEmail>,
context: Data<LemmyContext>, context: Data<LemmyContext>,
) -> LemmyResult<Json<SuccessResponse>> { ) -> LemmyResult<Json<SuccessResponse>> {
let site_view = SiteView::read_local(&mut context.pool()) let site_view = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
let token = data.token.clone(); let token = data.token.clone();
let verification = EmailVerification::read_for_token(&mut context.pool(), &token) let verification = EmailVerification::read_for_token(&mut context.pool(), &token)
.await? .await?

View file

@ -5,15 +5,13 @@ use lemmy_api_common::{
utils::build_federated_instances, utils::build_federated_instances,
}; };
use lemmy_db_views::structs::SiteView; use lemmy_db_views::structs::SiteView;
use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use lemmy_utils::error::LemmyResult;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
pub async fn get_federated_instances( pub async fn get_federated_instances(
context: Data<LemmyContext>, context: Data<LemmyContext>,
) -> LemmyResult<Json<GetFederatedInstancesResponse>> { ) -> LemmyResult<Json<GetFederatedInstancesResponse>> {
let site_view = SiteView::read_local(&mut context.pool()) let site_view = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
let federated_instances = let federated_instances =
build_federated_instances(&site_view.local_site, &mut context.pool()).await?; build_federated_instances(&site_view.local_site, &mut context.pool()).await?;

View file

@ -55,9 +55,7 @@ pub async fn leave_admin(
ModAdd::create(&mut context.pool(), &form).await?; ModAdd::create(&mut context.pool(), &form).await?;
// Reread site and admins // Reread site and admins
let site_view = SiteView::read_local(&mut context.pool()) let site_view = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
let admins = PersonView::admins(&mut context.pool()).await?; let admins = PersonView::admins(&mut context.pool()).await?;
let all_languages = Language::read_all(&mut context.pool()).await?; let all_languages = Language::read_all(&mut context.pool()).await?;

View file

@ -1,7 +1,7 @@
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::{CommentReplyId, CommunityId, LanguageId, PersonId, PersonMentionId}, newtypes::{CommentReplyId, CommunityId, LanguageId, PersonId, PersonMentionId},
sensitive::SensitiveString, sensitive::SensitiveString,
source::site::Site, source::{login_token::LoginToken, site::Site},
CommentSortType, CommentSortType,
ListingType, ListingType,
PostListingMode, PostListingMode,
@ -441,3 +441,10 @@ pub struct ListMedia {
pub struct ListMediaResponse { pub struct ListMediaResponse {
pub images: Vec<LocalImageView>, pub images: Vec<LocalImageView>,
} }
#[derive(Debug, Serialize, Deserialize, Clone)]
#[cfg_attr(feature = "full", derive(TS))]
#[cfg_attr(feature = "full", ts(export))]
pub struct ListLoginsResponse {
pub logins: Vec<LoginToken>,
}

View file

@ -3,7 +3,7 @@ use crate::{
lemmy_db_schema::traits::Crud, lemmy_db_schema::traits::Crud,
post::{LinkMetadata, OpenGraphData}, post::{LinkMetadata, OpenGraphData},
send_activity::{ActivityChannel, SendActivityData}, send_activity::{ActivityChannel, SendActivityData},
utils::{local_site_opt_to_sensitive, proxy_image_link}, utils::proxy_image_link,
}; };
use activitypub_federation::config::Data; use activitypub_federation::config::Data;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
@ -13,8 +13,8 @@ use lemmy_db_schema::{
newtypes::DbUrl, newtypes::DbUrl,
source::{ source::{
images::{ImageDetailsForm, LocalImage, LocalImageForm}, images::{ImageDetailsForm, LocalImage, LocalImageForm},
local_site::LocalSite,
post::{Post, PostUpdateForm}, post::{Post, PostUpdateForm},
site::Site,
}, },
}; };
use lemmy_utils::{ use lemmy_utils::{
@ -130,7 +130,6 @@ pub async fn generate_post_link_metadata(
post: Post, post: Post,
custom_thumbnail: Option<Url>, custom_thumbnail: Option<Url>,
send_activity: impl FnOnce(Post) -> Option<SendActivityData> + Send + 'static, send_activity: impl FnOnce(Post) -> Option<SendActivityData> + Send + 'static,
local_site: Option<LocalSite>,
context: Data<LemmyContext>, context: Data<LemmyContext>,
) -> LemmyResult<()> { ) -> LemmyResult<()> {
let metadata = match &post.url { let metadata = match &post.url {
@ -144,7 +143,8 @@ pub async fn generate_post_link_metadata(
.is_some_and(|content_type| content_type.starts_with("image")); .is_some_and(|content_type| content_type.starts_with("image"));
// Decide if we are allowed to generate local thumbnail // Decide if we are allowed to generate local thumbnail
let allow_sensitive = local_site_opt_to_sensitive(&local_site); let site = Site::read_local(&mut context.pool()).await?;
let allow_sensitive = site.content_warning.is_some();
let allow_generate_thumbnail = allow_sensitive || !post.nsfw; let allow_generate_thumbnail = allow_sensitive || !post.nsfw;
let image_url = if is_image_post { let image_url = if is_image_post {

View file

@ -11,10 +11,12 @@ use lemmy_db_schema::{
RegistrationApplicationId, RegistrationApplicationId,
}, },
source::{ source::{
community::Community,
federation_queue_state::FederationQueueState, federation_queue_state::FederationQueueState,
instance::Instance, instance::Instance,
language::Language, language::Language,
local_site_url_blocklist::LocalSiteUrlBlocklist, local_site_url_blocklist::LocalSiteUrlBlocklist,
person::Person,
tagline::Tagline, tagline::Tagline,
}, },
ListingType, ListingType,
@ -33,12 +35,9 @@ use lemmy_db_views::structs::{
SiteView, SiteView,
}; };
use lemmy_db_views_actor::structs::{ use lemmy_db_views_actor::structs::{
CommunityBlockView,
CommunityFollowerView, CommunityFollowerView,
CommunityModeratorView, CommunityModeratorView,
CommunityView, CommunityView,
InstanceBlockView,
PersonBlockView,
PersonView, PersonView,
}; };
use lemmy_db_views_moderator::structs::{ use lemmy_db_views_moderator::structs::{
@ -78,6 +77,7 @@ pub struct Search {
pub listing_type: Option<ListingType>, pub listing_type: Option<ListingType>,
pub page: Option<i64>, pub page: Option<i64>,
pub limit: Option<i64>, pub limit: Option<i64>,
pub post_title_only: Option<bool>,
} }
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
@ -337,9 +337,9 @@ pub struct MyUserInfo {
pub local_user_view: LocalUserView, pub local_user_view: LocalUserView,
pub follows: Vec<CommunityFollowerView>, pub follows: Vec<CommunityFollowerView>,
pub moderates: Vec<CommunityModeratorView>, pub moderates: Vec<CommunityModeratorView>,
pub community_blocks: Vec<CommunityBlockView>, pub community_blocks: Vec<Community>,
pub instance_blocks: Vec<InstanceBlockView>, pub instance_blocks: Vec<Instance>,
pub person_blocks: Vec<PersonBlockView>, pub person_blocks: Vec<Person>,
pub discussion_languages: Vec<LanguageId>, pub discussion_languages: Vec<LanguageId>,
} }

View file

@ -49,6 +49,7 @@ use lemmy_utils::{
utils::{ utils::{
markdown::{markdown_check_for_blocked_urls, markdown_rewrite_image_links}, markdown::{markdown_check_for_blocked_urls, markdown_rewrite_image_links},
slurs::{build_slur_regex, remove_slurs}, slurs::{build_slur_regex, remove_slurs},
validation::clean_urls_in_text,
}, },
CACHE_DURATION_FEDERATION, CACHE_DURATION_FEDERATION,
}; };
@ -537,13 +538,6 @@ pub fn local_site_opt_to_slur_regex(local_site: &Option<LocalSite>) -> Option<Re
.unwrap_or(None) .unwrap_or(None)
} }
pub fn local_site_opt_to_sensitive(local_site: &Option<LocalSite>) -> bool {
local_site
.as_ref()
.map(|site| site.enable_nsfw)
.unwrap_or(false)
}
pub async fn get_url_blocklist(context: &LemmyContext) -> LemmyResult<RegexSet> { pub async fn get_url_blocklist(context: &LemmyContext) -> LemmyResult<RegexSet> {
static URL_BLOCKLIST: LazyLock<Cache<(), RegexSet>> = LazyLock::new(|| { static URL_BLOCKLIST: LazyLock<Cache<(), RegexSet>> = LazyLock::new(|| {
Cache::builder() Cache::builder()
@ -947,6 +941,7 @@ pub async fn process_markdown(
context: &LemmyContext, context: &LemmyContext,
) -> LemmyResult<String> { ) -> LemmyResult<String> {
let text = remove_slurs(text, slur_regex); let text = remove_slurs(text, slur_regex);
let text = clean_urls_in_text(&text);
markdown_check_for_blocked_urls(&text, url_blocklist)?; markdown_check_for_blocked_urls(&text, url_blocklist)?;

View file

@ -27,7 +27,7 @@ futures.workspace = true
uuid = { workspace = true } uuid = { workspace = true }
moka.workspace = true moka.workspace = true
anyhow.workspace = true anyhow.workspace = true
webmention = "0.5.0" webmention = "0.6.0"
accept-language = "3.1.0" accept-language = "3.1.0"
[package.metadata.cargo-machete] [package.metadata.cargo-machete]

View file

@ -47,9 +47,7 @@ pub async fn create_community(
context: Data<LemmyContext>, context: Data<LemmyContext>,
local_user_view: LocalUserView, local_user_view: LocalUserView,
) -> LemmyResult<Json<CommunityResponse>> { ) -> LemmyResult<Json<CommunityResponse>> {
let site_view = SiteView::read_local(&mut context.pool()) let site_view = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
let local_site = site_view.local_site; let local_site = site_view.local_site;
if local_site.community_creation_admin_only && is_admin(&local_user_view).is_err() { if local_site.community_creation_admin_only && is_admin(&local_user_view).is_err() {

View file

@ -6,7 +6,7 @@ use lemmy_api_common::{
}; };
use lemmy_db_views::structs::{LocalUserView, SiteView}; use lemmy_db_views::structs::{LocalUserView, SiteView};
use lemmy_db_views_actor::community_view::CommunityQuery; use lemmy_db_views_actor::community_view::CommunityQuery;
use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use lemmy_utils::error::LemmyResult;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
pub async fn list_communities( pub async fn list_communities(
@ -14,9 +14,7 @@ pub async fn list_communities(
context: Data<LemmyContext>, context: Data<LemmyContext>,
local_user_view: Option<LocalUserView>, local_user_view: Option<LocalUserView>,
) -> LemmyResult<Json<ListCommunitiesResponse>> { ) -> LemmyResult<Json<ListCommunitiesResponse>> {
let local_site = SiteView::read_local(&mut context.pool()) let local_site = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
let is_admin = local_user_view let is_admin = local_user_view
.as_ref() .as_ref()
.map(|luv| is_admin(luv).is_ok()) .map(|luv| is_admin(luv).is_ok())

View file

@ -149,7 +149,6 @@ pub async fn create_post(
inserted_post.clone(), inserted_post.clone(),
custom_thumbnail.map(Into::into), custom_thumbnail.map(Into::into),
|post| Some(SendActivityData::CreatePost(post)), |post| Some(SendActivityData::CreatePost(post)),
Some(local_site),
context.reset_request_count(), context.reset_request_count(),
) )
.await?; .await?;

View file

@ -21,9 +21,7 @@ pub async fn get_post(
context: Data<LemmyContext>, context: Data<LemmyContext>,
local_user_view: Option<LocalUserView>, local_user_view: Option<LocalUserView>,
) -> LemmyResult<Json<GetPostResponse>> { ) -> LemmyResult<Json<GetPostResponse>> {
let local_site = SiteView::read_local(&mut context.pool()) let local_site = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
check_private_instance(&local_user_view, &local_site.local_site)?; check_private_instance(&local_user_view, &local_site.local_site)?;

View file

@ -129,7 +129,6 @@ pub async fn update_post(
updated_post.clone(), updated_post.clone(),
custom_thumbnail.flatten().map(Into::into), custom_thumbnail.flatten().map(Into::into),
|post| Some(SendActivityData::UpdatePost(post)), |post| Some(SendActivityData::UpdatePost(post)),
Some(local_site),
context.reset_request_count(), context.reset_request_count(),
) )
.await?; .await?;

View file

@ -92,7 +92,6 @@ pub async fn create_site(
site_setup: Some(true), site_setup: Some(true),
enable_downvotes: data.enable_downvotes, enable_downvotes: data.enable_downvotes,
registration_mode: data.registration_mode, registration_mode: data.registration_mode,
enable_nsfw: data.enable_nsfw,
community_creation_admin_only: data.community_creation_admin_only, community_creation_admin_only: data.community_creation_admin_only,
require_email_verification: data.require_email_verification, require_email_verification: data.require_email_verification,
application_question: diesel_string_update(data.application_question.as_deref()), application_question: diesel_string_update(data.application_question.as_deref()),
@ -133,9 +132,7 @@ pub async fn create_site(
LocalSiteRateLimit::update(&mut context.pool(), &local_site_rate_limit_form).await?; LocalSiteRateLimit::update(&mut context.pool(), &local_site_rate_limit_form).await?;
let site_view = SiteView::read_local(&mut context.pool()) let site_view = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
let new_taglines = data.taglines.clone(); let new_taglines = data.taglines.clone();
let taglines = Tagline::replace(&mut context.pool(), local_site.id, new_taglines).await?; let taglines = Tagline::replace(&mut context.pool(), local_site.id, new_taglines).await?;

View file

@ -5,19 +5,15 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
actor_language::{LocalUserLanguage, SiteLanguage}, actor_language::{LocalUserLanguage, SiteLanguage},
community_block::CommunityBlock,
instance_block::InstanceBlock,
language::Language, language::Language,
local_site_url_blocklist::LocalSiteUrlBlocklist, local_site_url_blocklist::LocalSiteUrlBlocklist,
person_block::PersonBlock,
tagline::Tagline, tagline::Tagline,
}; };
use lemmy_db_views::structs::{CustomEmojiView, LocalUserView, SiteView}; use lemmy_db_views::structs::{CustomEmojiView, LocalUserView, SiteView};
use lemmy_db_views_actor::structs::{ use lemmy_db_views_actor::structs::{CommunityFollowerView, CommunityModeratorView, PersonView};
CommunityBlockView,
CommunityFollowerView,
CommunityModeratorView,
InstanceBlockView,
PersonBlockView,
PersonView,
};
use lemmy_utils::{ use lemmy_utils::{
error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult}, error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
CACHE_DURATION_API, CACHE_DURATION_API,
@ -41,9 +37,7 @@ pub async fn get_site(
// This data is independent from the user account so we can cache it across requests // This data is independent from the user account so we can cache it across requests
let mut site_response = CACHE let mut site_response = CACHE
.try_get_with::<_, LemmyError>((), async { .try_get_with::<_, LemmyError>((), async {
let site_view = SiteView::read_local(&mut context.pool()) let site_view = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
let admins = PersonView::admins(&mut context.pool()).await?; let admins = PersonView::admins(&mut context.pool()).await?;
let all_languages = Language::read_all(&mut context.pool()).await?; let all_languages = Language::read_all(&mut context.pool()).await?;
let discussion_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?; let discussion_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?;
@ -81,9 +75,9 @@ pub async fn get_site(
discussion_languages, discussion_languages,
) = lemmy_db_schema::try_join_with_pool!(pool => ( ) = lemmy_db_schema::try_join_with_pool!(pool => (
|pool| CommunityFollowerView::for_person(pool, person_id), |pool| CommunityFollowerView::for_person(pool, person_id),
|pool| CommunityBlockView::for_person(pool, person_id), |pool| CommunityBlock::for_person(pool, person_id),
|pool| InstanceBlockView::for_person(pool, person_id), |pool| InstanceBlock::for_person(pool, person_id),
|pool| PersonBlockView::for_person(pool, person_id), |pool| PersonBlock::for_person(pool, person_id),
|pool| CommunityModeratorView::for_person(pool, person_id, Some(&local_user_view.local_user)), |pool| CommunityModeratorView::for_person(pool, person_id, Some(&local_user_view.local_user)),
|pool| LocalUserLanguage::read(pool, local_user_id) |pool| LocalUserLanguage::read(pool, local_user_id)
)) ))

View file

@ -52,9 +52,7 @@ pub async fn update_site(
context: Data<LemmyContext>, context: Data<LemmyContext>,
local_user_view: LocalUserView, local_user_view: LocalUserView,
) -> LemmyResult<Json<SiteResponse>> { ) -> LemmyResult<Json<SiteResponse>> {
let site_view = SiteView::read_local(&mut context.pool()) let site_view = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
let local_site = site_view.local_site; let local_site = site_view.local_site;
let site = site_view.site; let site = site_view.site;
@ -103,7 +101,6 @@ pub async fn update_site(
let local_site_form = LocalSiteUpdateForm { let local_site_form = LocalSiteUpdateForm {
enable_downvotes: data.enable_downvotes, enable_downvotes: data.enable_downvotes,
registration_mode: data.registration_mode, registration_mode: data.registration_mode,
enable_nsfw: data.enable_nsfw,
community_creation_admin_only: data.community_creation_admin_only, community_creation_admin_only: data.community_creation_admin_only,
require_email_verification: data.require_email_verification, require_email_verification: data.require_email_verification,
application_question: diesel_string_update(data.application_question.as_deref()), application_question: diesel_string_update(data.application_question.as_deref()),
@ -191,9 +188,7 @@ pub async fn update_site(
let new_taglines = data.taglines.clone(); let new_taglines = data.taglines.clone();
let taglines = Tagline::replace(&mut context.pool(), local_site.id, new_taglines).await?; let taglines = Tagline::replace(&mut context.pool(), local_site.id, new_taglines).await?;
let site_view = SiteView::read_local(&mut context.pool()) let site_view = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
let rate_limit_config = let rate_limit_config =
local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit); local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit);

View file

@ -45,9 +45,7 @@ pub async fn register(
req: HttpRequest, req: HttpRequest,
context: Data<LemmyContext>, context: Data<LemmyContext>,
) -> LemmyResult<Json<LoginResponse>> { ) -> LemmyResult<Json<LoginResponse>> {
let site_view = SiteView::read_local(&mut context.pool()) let site_view = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
let local_site = site_view.local_site; let local_site = site_view.local_site;
let require_registration_application = let require_registration_application =
local_site.registration_mode == RegistrationMode::RequireApplication; local_site.registration_mode == RegistrationMode::RequireApplication;

View file

@ -8,6 +8,6 @@
"type": "Block", "type": "Block",
"removeData": true, "removeData": true,
"summary": "spam post", "summary": "spam post",
"expires": "2021-11-01T12:23:50.151874Z", "endTime": "2021-11-01T12:23:50.151874Z",
"id": "http://enterprise.lemmy.ml/activities/block/5d42fffb-0903-4625-86d4-0b39bb344fc2" "id": "http://enterprise.lemmy.ml/activities/block/5d42fffb-0903-4625-86d4-0b39bb344fc2"
} }

View file

@ -11,7 +11,7 @@
"type": "Block", "type": "Block",
"removeData": true, "removeData": true,
"summary": "spam post", "summary": "spam post",
"expires": "2021-11-01T12:23:50.151874Z", "endTime": "2021-11-01T12:23:50.151874Z",
"id": "http://enterprise.lemmy.ml/activities/block/726f43ab-bd0e-4ab3-89c8-627e976f553c" "id": "http://enterprise.lemmy.ml/activities/block/726f43ab-bd0e-4ab3-89c8-627e976f553c"
}, },
"cc": ["http://enterprise.lemmy.ml/c/main"], "cc": ["http://enterprise.lemmy.ml/c/main"],

View file

@ -74,7 +74,6 @@ impl BlockUser {
&context.settings().get_protocol_and_hostname(), &context.settings().get_protocol_and_hostname(),
)?, )?,
audience, audience,
expires,
end_time: expires, end_time: expires,
}) })
} }
@ -157,7 +156,7 @@ impl ActivityHandler for BlockUser {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<LemmyContext>) -> LemmyResult<()> { async fn receive(self, context: &Data<LemmyContext>) -> LemmyResult<()> {
insert_received_activity(&self.id, context).await?; insert_received_activity(&self.id, context).await?;
let expires = self.expires.or(self.end_time).map(Into::into); let expires = self.end_time.map(Into::into);
let mod_person = self.actor.dereference(context).await?; let mod_person = self.actor.dereference(context).await?;
let blocked_person = self.object.dereference(context).await?; let blocked_person = self.object.dereference(context).await?;
let target = self.target.dereference(context).await?; let target = self.target.dereference(context).await?;

View file

@ -22,7 +22,6 @@ use lemmy_db_schema::{
traits::Crud, traits::Crud,
utils::DbPool, utils::DbPool,
}; };
use lemmy_db_views::structs::SiteView;
use lemmy_utils::{ use lemmy_utils::{
error::{LemmyError, LemmyResult}, error::{LemmyError, LemmyResult},
LemmyErrorType, LemmyErrorType,
@ -142,13 +141,7 @@ pub(crate) async fn send_ban_from_site(
expires: Option<i64>, expires: Option<i64>,
context: Data<LemmyContext>, context: Data<LemmyContext>,
) -> LemmyResult<()> { ) -> LemmyResult<()> {
let site = SiteOrCommunity::Site( let site = SiteOrCommunity::Site(Site::read_local(&mut context.pool()).await?.into());
SiteView::read_local(&mut context.pool())
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?
.site
.into(),
);
let expires = check_expire_time(expires)?; let expires = check_expire_time(expires)?;
// if the action affects a local user, federate to other instances // if the action affects a local user, federate to other instances

View file

@ -98,7 +98,7 @@ impl ActivityHandler for UndoBlockUser {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<LemmyContext>) -> LemmyResult<()> { async fn receive(self, context: &Data<LemmyContext>) -> LemmyResult<()> {
insert_received_activity(&self.id, context).await?; insert_received_activity(&self.id, context).await?;
let expires = self.object.expires.or(self.object.end_time).map(Into::into); let expires = self.object.end_time.map(Into::into);
let mod_person = self.actor.dereference(context).await?; let mod_person = self.actor.dereference(context).await?;
let blocked_person = self.object.object.dereference(context).await?; let blocked_person = self.object.object.dereference(context).await?;
match self.object.target.dereference(context).await? { match self.object.target.dereference(context).await? {

View file

@ -23,9 +23,7 @@ pub async fn list_posts(
context: Data<LemmyContext>, context: Data<LemmyContext>,
local_user_view: Option<LocalUserView>, local_user_view: Option<LocalUserView>,
) -> LemmyResult<Json<GetPostsResponse>> { ) -> LemmyResult<Json<GetPostsResponse>> {
let local_site = SiteView::read_local(&mut context.pool()) let local_site = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
check_private_instance(&local_user_view, &local_site.local_site)?; check_private_instance(&local_user_view, &local_site.local_site)?;

View file

@ -26,9 +26,7 @@ pub async fn read_person(
Err(LemmyErrorType::NoIdGiven)? Err(LemmyErrorType::NoIdGiven)?
} }
let local_site = SiteView::read_local(&mut context.pool()) let local_site = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
check_private_instance(&local_user_view, &local_site.local_site)?; check_private_instance(&local_user_view, &local_site.local_site)?;

View file

@ -27,7 +27,7 @@ pub async fn resolve_object(
// if there's no personId then the JWT was missing or invalid. // if there's no personId then the JWT was missing or invalid.
let is_authenticated = local_user_view.is_some(); let is_authenticated = local_user_view.is_some();
let res = if is_authenticated { let res = if is_authenticated || cfg!(debug_assertions) {
// user is fully authenticated; allow remote lookups as well. // user is fully authenticated; allow remote lookups as well.
search_query_to_object_id(data.q.clone(), &context).await search_query_to_object_id(data.q.clone(), &context).await
} else { } else {

View file

@ -13,7 +13,7 @@ use lemmy_db_views::{
structs::{LocalUserView, SiteView}, structs::{LocalUserView, SiteView},
}; };
use lemmy_db_views_actor::{community_view::CommunityQuery, person_view::PersonQuery}; use lemmy_db_views_actor::{community_view::CommunityQuery, person_view::PersonQuery};
use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use lemmy_utils::error::LemmyResult;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
pub async fn search( pub async fn search(
@ -21,9 +21,7 @@ pub async fn search(
context: Data<LemmyContext>, context: Data<LemmyContext>,
local_user_view: Option<LocalUserView>, local_user_view: Option<LocalUserView>,
) -> LemmyResult<Json<SearchResponse>> { ) -> LemmyResult<Json<SearchResponse>> {
let local_site = SiteView::read_local(&mut context.pool()) let local_site = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
check_private_instance(&local_user_view, &local_site.local_site)?; check_private_instance(&local_user_view, &local_site.local_site)?;
@ -56,133 +54,92 @@ pub async fn search(
}; };
let creator_id = data.creator_id; let creator_id = data.creator_id;
let local_user = local_user_view.as_ref().map(|l| &l.local_user); let local_user = local_user_view.as_ref().map(|l| &l.local_user);
let post_title_only = data.post_title_only;
let posts_query = PostQuery {
sort: (sort),
listing_type: (listing_type),
community_id: (community_id),
creator_id: (creator_id),
local_user,
search_term: (Some(q.clone())),
page: (page),
limit: (limit),
title_only: (post_title_only),
..Default::default()
};
let comment_query = CommentQuery {
sort: (sort.map(post_to_comment_sort_type)),
listing_type: (listing_type),
search_term: (Some(q.clone())),
community_id: (community_id),
creator_id: (creator_id),
local_user,
page: (page),
limit: (limit),
..Default::default()
};
let community_query = CommunityQuery {
sort: (sort),
listing_type: (listing_type),
search_term: (Some(q.clone())),
local_user,
is_mod_or_admin: (is_admin),
page: (page),
limit: (limit),
..Default::default()
};
let person_query = PersonQuery {
sort,
search_term: (Some(q.clone())),
listing_type: (listing_type),
page: (page),
limit: (limit),
};
match search_type { match search_type {
SearchType::Posts => { SearchType::Posts => {
posts = PostQuery { posts = posts_query
sort: (sort), .list(&local_site.site, &mut context.pool())
listing_type: (listing_type), .await?;
community_id: (community_id),
creator_id: (creator_id),
local_user,
search_term: (Some(q)),
page: (page),
limit: (limit),
..Default::default()
}
.list(&local_site.site, &mut context.pool())
.await?;
} }
SearchType::Comments => { SearchType::Comments => {
comments = CommentQuery { comments = comment_query.list(&mut context.pool()).await?;
sort: (sort.map(post_to_comment_sort_type)),
listing_type: (listing_type),
search_term: (Some(q)),
community_id: (community_id),
creator_id: (creator_id),
local_user,
page: (page),
limit: (limit),
..Default::default()
}
.list(&mut context.pool())
.await?;
} }
SearchType::Communities => { SearchType::Communities => {
communities = CommunityQuery { communities = community_query
sort: (sort), .list(&local_site.site, &mut context.pool())
listing_type: (listing_type), .await?;
search_term: (Some(q)),
local_user,
is_mod_or_admin: (is_admin),
page: (page),
limit: (limit),
..Default::default()
}
.list(&local_site.site, &mut context.pool())
.await?;
} }
SearchType::Users => { SearchType::Users => {
users = PersonQuery { users = person_query.list(&mut context.pool()).await?;
sort,
search_term: (Some(q)),
listing_type: (listing_type),
page: (page),
limit: (limit),
}
.list(&mut context.pool())
.await?;
} }
SearchType::All => { SearchType::All => {
// If the community or creator is included, dont search communities or users // If the community or creator is included, dont search communities or users
let community_or_creator_included = let community_or_creator_included =
data.community_id.is_some() || data.community_name.is_some() || data.creator_id.is_some(); data.community_id.is_some() || data.community_name.is_some() || data.creator_id.is_some();
let q = data.q.clone(); posts = posts_query
.list(&local_site.site, &mut context.pool())
.await?;
posts = PostQuery { comments = comment_query.list(&mut context.pool()).await?;
sort: (sort),
listing_type: (listing_type),
community_id: (community_id),
creator_id: (creator_id),
local_user,
search_term: (Some(q)),
page: (page),
limit: (limit),
..Default::default()
}
.list(&local_site.site, &mut context.pool())
.await?;
let q = data.q.clone();
comments = CommentQuery {
sort: (sort.map(post_to_comment_sort_type)),
listing_type: (listing_type),
search_term: (Some(q)),
community_id: (community_id),
creator_id: (creator_id),
local_user,
page: (page),
limit: (limit),
..Default::default()
}
.list(&mut context.pool())
.await?;
let q = data.q.clone();
communities = if community_or_creator_included { communities = if community_or_creator_included {
vec![] vec![]
} else { } else {
CommunityQuery { community_query
sort: (sort), .list(&local_site.site, &mut context.pool())
listing_type: (listing_type), .await?
search_term: (Some(q)),
local_user,
is_mod_or_admin: (is_admin),
page: (page),
limit: (limit),
..Default::default()
}
.list(&local_site.site, &mut context.pool())
.await?
}; };
let q = data.q.clone();
users = if community_or_creator_included { users = if community_or_creator_included {
vec![] vec![]
} else { } else {
PersonQuery { person_query.list(&mut context.pool()).await?
sort,
search_term: (Some(q)),
listing_type: (listing_type),
page: (page),
limit: (limit),
}
.list(&mut context.pool())
.await?
}; };
} }
SearchType::Url => { SearchType::Url => {

View file

@ -122,7 +122,6 @@ pub async fn import_settings(
.settings .settings
.as_ref() .as_ref()
.map(|s| s.send_notifications_to_email), .map(|s| s.send_notifications_to_email),
show_scores: data.settings.as_ref().map(|s| s.show_scores),
show_bot_accounts: data.settings.as_ref().map(|s| s.show_bot_accounts), show_bot_accounts: data.settings.as_ref().map(|s| s.show_bot_accounts),
show_read_posts: data.settings.as_ref().map(|s| s.show_read_posts), show_read_posts: data.settings.as_ref().map(|s| s.show_read_posts),
open_links_in_new_tab: data.settings.as_ref().map(|s| s.open_links_in_new_tab), open_links_in_new_tab: data.settings.as_ref().map(|s| s.open_links_in_new_tab),

View file

@ -18,12 +18,9 @@ use activitypub_federation::{
}; };
use futures::future::join_all; use futures::future::join_all;
use lemmy_api_common::{context::LemmyContext, utils::generate_outbox_url}; use lemmy_api_common::{context::LemmyContext, utils::generate_outbox_url};
use lemmy_db_schema::{utils::FETCH_LIMIT_MAX, SortType}; use lemmy_db_schema::{source::site::Site, utils::FETCH_LIMIT_MAX, SortType};
use lemmy_db_views::{post_view::PostQuery, structs::SiteView}; use lemmy_db_views::post_view::PostQuery;
use lemmy_utils::{ use lemmy_utils::error::{LemmyError, LemmyResult};
error::{LemmyError, LemmyResult},
LemmyErrorType,
};
use url::Url; use url::Url;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -38,10 +35,7 @@ impl Collection for ApubCommunityOutbox {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
async fn read_local(owner: &Self::Owner, data: &Data<Self::DataType>) -> LemmyResult<Self::Kind> { async fn read_local(owner: &Self::Owner, data: &Data<Self::DataType>) -> LemmyResult<Self::Kind> {
let site = SiteView::read_local(&mut data.pool()) let site = Site::read_local(&mut data.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?
.site;
let post_views = PostQuery { let post_views = PostQuery {
community_id: Some(owner.id), community_id: Some(owner.id),

View file

@ -11,7 +11,6 @@ use activitypub_federation::{
FEDERATION_CONTENT_TYPE, FEDERATION_CONTENT_TYPE,
}; };
use actix_web::{web, web::Bytes, HttpRequest, HttpResponse}; use actix_web::{web, web::Bytes, HttpRequest, HttpResponse};
use http::{header::LOCATION, StatusCode};
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::DbUrl, newtypes::DbUrl,
@ -76,14 +75,14 @@ fn create_apub_tombstone_response<T: Into<Url>>(id: T) -> LemmyResult<HttpRespon
Ok( Ok(
HttpResponse::Gone() HttpResponse::Gone()
.content_type(FEDERATION_CONTENT_TYPE) .content_type(FEDERATION_CONTENT_TYPE)
.status(StatusCode::GONE) .status(actix_web::http::StatusCode::GONE)
.body(json), .body(json),
) )
} }
fn redirect_remote_object(url: &DbUrl) -> HttpResponse { fn redirect_remote_object(url: &DbUrl) -> HttpResponse {
let mut res = HttpResponse::PermanentRedirect(); let mut res = HttpResponse::PermanentRedirect();
res.insert_header((LOCATION, url.as_str())); res.insert_header((actix_web::http::header::LOCATION, url.as_str()));
res.finish() res.finish()
} }

View file

@ -6,16 +6,12 @@ use crate::{
use activitypub_federation::{config::Data, traits::Object}; use activitypub_federation::{config::Data, traits::Object};
use actix_web::HttpResponse; use actix_web::HttpResponse;
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_db_views::structs::SiteView; use lemmy_db_schema::source::site::Site;
use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use lemmy_utils::error::LemmyResult;
use url::Url; use url::Url;
pub(crate) async fn get_apub_site_http(context: Data<LemmyContext>) -> LemmyResult<HttpResponse> { pub(crate) async fn get_apub_site_http(context: Data<LemmyContext>) -> LemmyResult<HttpResponse> {
let site: ApubSite = SiteView::read_local(&mut context.pool()) let site: ApubSite = Site::read_local(&mut context.pool()).await?.into();
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?
.site
.into();
let apub = site.into_json(&context).await?; let apub = site.into_json(&context).await?;
create_apub_response(&apub) create_apub_response(&apub)

View file

@ -270,9 +270,9 @@ impl Object for ApubPost {
// Generates a post thumbnail in background task, because some sites can be very slow to // Generates a post thumbnail in background task, because some sites can be very slow to
// respond. // respond.
spawn_try_task(async move { spawn_try_task(
generate_post_link_metadata(post_, None, |_| None, local_site, context_).await async move { generate_post_link_metadata(post_, None, |_| None, context_).await },
}); );
Ok(post.into()) Ok(post.into())
} }

View file

@ -38,8 +38,6 @@ pub struct BlockUser {
pub(crate) remove_data: Option<bool>, pub(crate) remove_data: Option<bool>,
/// block reason, written to mod log /// block reason, written to mod log
pub(crate) summary: Option<String>, pub(crate) summary: Option<String>,
/// TODO: deprecated
pub(crate) expires: Option<DateTime<Utc>>,
pub(crate) end_time: Option<DateTime<Utc>>, pub(crate) end_time: Option<DateTime<Utc>>,
} }

View file

@ -1,7 +1,10 @@
use crate::{ use crate::{
newtypes::{CommunityId, PersonId}, newtypes::{CommunityId, PersonId},
schema::community_block::dsl::{community_block, community_id, person_id}, schema::{community, community_block},
source::community_block::{CommunityBlock, CommunityBlockForm}, source::{
community::Community,
community_block::{CommunityBlock, CommunityBlockForm},
},
traits::Blockable, traits::Blockable,
utils::{get_conn, DbPool}, utils::{get_conn, DbPool},
}; };
@ -9,6 +12,7 @@ use diesel::{
dsl::{exists, insert_into}, dsl::{exists, insert_into},
result::Error, result::Error,
select, select,
ExpressionMethods,
QueryDsl, QueryDsl,
}; };
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
@ -21,11 +25,27 @@ impl CommunityBlock {
) -> Result<bool, Error> { ) -> Result<bool, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
select(exists( select(exists(
community_block.find((for_person_id, for_community_id)), community_block::table.find((for_person_id, for_community_id)),
)) ))
.get_result(conn) .get_result(conn)
.await .await
} }
pub async fn for_person(
pool: &mut DbPool<'_>,
person_id: PersonId,
) -> Result<Vec<Community>, Error> {
let conn = &mut get_conn(pool).await?;
community_block::table
.inner_join(community::table)
.select(community::all_columns)
.filter(community_block::person_id.eq(person_id))
.filter(community::deleted.eq(false))
.filter(community::removed.eq(false))
.order_by(community_block::published)
.load::<Community>(conn)
.await
}
} }
#[async_trait] #[async_trait]
@ -33,9 +53,9 @@ impl Blockable for CommunityBlock {
type Form = CommunityBlockForm; type Form = CommunityBlockForm;
async fn block(pool: &mut DbPool<'_>, community_block_form: &Self::Form) -> Result<Self, Error> { async fn block(pool: &mut DbPool<'_>, community_block_form: &Self::Form) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
insert_into(community_block) insert_into(community_block::table)
.values(community_block_form) .values(community_block_form)
.on_conflict((person_id, community_id)) .on_conflict((community_block::person_id, community_block::community_id))
.do_update() .do_update()
.set(community_block_form) .set(community_block_form)
.get_result::<Self>(conn) .get_result::<Self>(conn)
@ -46,7 +66,7 @@ impl Blockable for CommunityBlock {
community_block_form: &Self::Form, community_block_form: &Self::Form,
) -> Result<usize, Error> { ) -> Result<usize, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
diesel::delete(community_block.find(( diesel::delete(community_block::table.find((
community_block_form.person_id, community_block_form.person_id,
community_block_form.community_id, community_block_form.community_id,
))) )))

View file

@ -1,7 +1,10 @@
use crate::{ use crate::{
newtypes::{InstanceId, PersonId}, newtypes::{InstanceId, PersonId},
schema::instance_block::dsl::{instance_block, instance_id, person_id}, schema::{instance, instance_block},
source::instance_block::{InstanceBlock, InstanceBlockForm}, source::{
instance::Instance,
instance_block::{InstanceBlock, InstanceBlockForm},
},
traits::Blockable, traits::Blockable,
utils::{get_conn, DbPool}, utils::{get_conn, DbPool},
}; };
@ -9,6 +12,7 @@ use diesel::{
dsl::{exists, insert_into}, dsl::{exists, insert_into},
result::Error, result::Error,
select, select,
ExpressionMethods,
QueryDsl, QueryDsl,
}; };
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
@ -21,11 +25,25 @@ impl InstanceBlock {
) -> Result<bool, Error> { ) -> Result<bool, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
select(exists( select(exists(
instance_block.find((for_person_id, for_instance_id)), instance_block::table.find((for_person_id, for_instance_id)),
)) ))
.get_result(conn) .get_result(conn)
.await .await
} }
pub async fn for_person(
pool: &mut DbPool<'_>,
person_id: PersonId,
) -> Result<Vec<Instance>, Error> {
let conn = &mut get_conn(pool).await?;
instance_block::table
.inner_join(instance::table)
.select(instance::all_columns)
.filter(instance_block::person_id.eq(person_id))
.order_by(instance_block::published)
.load::<Instance>(conn)
.await
}
} }
#[async_trait] #[async_trait]
@ -33,9 +51,9 @@ impl Blockable for InstanceBlock {
type Form = InstanceBlockForm; type Form = InstanceBlockForm;
async fn block(pool: &mut DbPool<'_>, instance_block_form: &Self::Form) -> Result<Self, Error> { async fn block(pool: &mut DbPool<'_>, instance_block_form: &Self::Form) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
insert_into(instance_block) insert_into(instance_block::table)
.values(instance_block_form) .values(instance_block_form)
.on_conflict((person_id, instance_id)) .on_conflict((instance_block::person_id, instance_block::instance_id))
.do_update() .do_update()
.set(instance_block_form) .set(instance_block_form)
.get_result::<Self>(conn) .get_result::<Self>(conn)
@ -46,7 +64,7 @@ impl Blockable for InstanceBlock {
instance_block_form: &Self::Form, instance_block_form: &Self::Form,
) -> Result<usize, Error> { ) -> Result<usize, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
diesel::delete(instance_block.find(( diesel::delete(instance_block::table.find((
instance_block_form.person_id, instance_block_form.person_id,
instance_block_form.instance_id, instance_block_form.instance_id,
))) )))

View file

@ -1,7 +1,10 @@
use crate::{ use crate::{
newtypes::PersonId, newtypes::PersonId,
schema::person_block::dsl::{person_block, person_id, target_id}, schema::{person, person_block},
source::person_block::{PersonBlock, PersonBlockForm}, source::{
person::Person,
person_block::{PersonBlock, PersonBlockForm},
},
traits::Blockable, traits::Blockable,
utils::{get_conn, DbPool}, utils::{get_conn, DbPool},
}; };
@ -9,6 +12,8 @@ use diesel::{
dsl::{exists, insert_into}, dsl::{exists, insert_into},
result::Error, result::Error,
select, select,
ExpressionMethods,
JoinOnDsl,
QueryDsl, QueryDsl,
}; };
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
@ -20,8 +25,30 @@ impl PersonBlock {
for_recipient_id: PersonId, for_recipient_id: PersonId,
) -> Result<bool, Error> { ) -> Result<bool, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
select(exists(person_block.find((for_person_id, for_recipient_id)))) select(exists(
.get_result(conn) person_block::table.find((for_person_id, for_recipient_id)),
))
.get_result(conn)
.await
}
pub async fn for_person(
pool: &mut DbPool<'_>,
person_id: PersonId,
) -> Result<Vec<Person>, Error> {
let conn = &mut get_conn(pool).await?;
let target_person_alias = diesel::alias!(person as person1);
person_block::table
.inner_join(person::table.on(person_block::person_id.eq(person::id)))
.inner_join(
target_person_alias.on(person_block::target_id.eq(target_person_alias.field(person::id))),
)
.select(target_person_alias.fields(person::all_columns))
.filter(person_block::person_id.eq(person_id))
.filter(target_person_alias.field(person::deleted).eq(false))
.order_by(person_block::published)
.load::<Person>(conn)
.await .await
} }
} }
@ -34,9 +61,9 @@ impl Blockable for PersonBlock {
person_block_form: &PersonBlockForm, person_block_form: &PersonBlockForm,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
insert_into(person_block) insert_into(person_block::table)
.values(person_block_form) .values(person_block_form)
.on_conflict((person_id, target_id)) .on_conflict((person_block::person_id, person_block::target_id))
.do_update() .do_update()
.set(person_block_form) .set(person_block_form)
.get_result::<Self>(conn) .get_result::<Self>(conn)
@ -44,8 +71,10 @@ impl Blockable for PersonBlock {
} }
async fn unblock(pool: &mut DbPool<'_>, person_block_form: &Self::Form) -> Result<usize, Error> { async fn unblock(pool: &mut DbPool<'_>, person_block_form: &Self::Form) -> Result<usize, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
diesel::delete(person_block.find((person_block_form.person_id, person_block_form.target_id))) diesel::delete(
.execute(conn) person_block::table.find((person_block_form.person_id, person_block_form.target_id)),
.await )
.execute(conn)
.await
} }
} }

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
newtypes::{DbUrl, InstanceId, SiteId}, newtypes::{DbUrl, InstanceId, SiteId},
schema::site, schema::{local_site, site},
source::{ source::{
actor_language::SiteLanguage, actor_language::SiteLanguage,
site::{Site, SiteInsertForm, SiteUpdateForm}, site::{Site, SiteInsertForm, SiteUpdateForm},
@ -10,6 +10,7 @@ use crate::{
}; };
use diesel::{dsl::insert_into, result::Error, ExpressionMethods, OptionalExtension, QueryDsl}; use diesel::{dsl::insert_into, result::Error, ExpressionMethods, OptionalExtension, QueryDsl};
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
use url::Url; use url::Url;
#[async_trait] #[async_trait]
@ -102,4 +103,18 @@ impl Site {
url.set_query(None); url.set_query(None);
url url
} }
pub async fn read_local(pool: &mut DbPool<'_>) -> LemmyResult<Self> {
let conn = &mut get_conn(pool).await?;
Ok(
site::table
.inner_join(local_site::table)
.select(site::all_columns)
.first(conn)
.await
.optional()?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?,
)
}
} }

View file

@ -370,7 +370,6 @@ diesel::table! {
site_id -> Int4, site_id -> Int4,
site_setup -> Bool, site_setup -> Bool,
enable_downvotes -> Bool, enable_downvotes -> Bool,
enable_nsfw -> Bool,
community_creation_admin_only -> Bool, community_creation_admin_only -> Bool,
require_email_verification -> Bool, require_email_verification -> Bool,
application_question -> Nullable<Text>, application_question -> Nullable<Text>,
@ -446,7 +445,6 @@ diesel::table! {
interface_language -> Varchar, interface_language -> Varchar,
show_avatars -> Bool, show_avatars -> Bool,
send_notifications_to_email -> Bool, send_notifications_to_email -> Bool,
show_scores -> Bool,
show_bot_accounts -> Bool, show_bot_accounts -> Bool,
show_read_posts -> Bool, show_read_posts -> Bool,
email_verified -> Bool, email_verified -> Bool,

View file

@ -2,7 +2,6 @@ use anyhow::Context;
use diesel::{connection::SimpleConnection, Connection, PgConnection}; use diesel::{connection::SimpleConnection, Connection, PgConnection};
use diesel_migrations::{EmbeddedMigrations, MigrationHarness}; use diesel_migrations::{EmbeddedMigrations, MigrationHarness};
use lemmy_utils::error::LemmyError; use lemmy_utils::error::LemmyError;
use tracing::info;
const MIGRATIONS: EmbeddedMigrations = embed_migrations!(); const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
@ -34,7 +33,7 @@ pub fn run(db_url: &str) -> Result<(), LemmyError> {
// transaction as `REPLACEABLE_SCHEMA`. This code will be becone less hacky when the conditional // transaction as `REPLACEABLE_SCHEMA`. This code will be becone less hacky when the conditional
// setup of things in `REPLACEABLE_SCHEMA` is done without using the number of pending // setup of things in `REPLACEABLE_SCHEMA` is done without using the number of pending
// migrations. // migrations.
info!("Running Database migrations (This may take a long time)..."); println!("Running Database migrations (This may take a long time)...");
let migrations = conn let migrations = conn
.pending_migrations(MIGRATIONS) .pending_migrations(MIGRATIONS)
.map_err(|e| anyhow::anyhow!("Couldn't determine pending migrations: {e}"))?; .map_err(|e| anyhow::anyhow!("Couldn't determine pending migrations: {e}"))?;
@ -60,7 +59,7 @@ pub fn run(db_url: &str) -> Result<(), LemmyError> {
Ok(()) Ok(())
})?; })?;
info!("Database migrations complete."); println!("Database migrations complete.");
Ok(()) Ok(())
} }

View file

@ -29,8 +29,6 @@ pub struct LocalSite {
pub site_setup: bool, pub site_setup: bool,
/// Whether downvotes are enabled. /// Whether downvotes are enabled.
pub enable_downvotes: bool, pub enable_downvotes: bool,
/// Whether NSFW is enabled.
pub enable_nsfw: bool,
/// Whether only admins can create communities. /// Whether only admins can create communities.
pub community_creation_admin_only: bool, pub community_creation_admin_only: bool,
/// Whether emails are required. /// Whether emails are required.
@ -81,7 +79,6 @@ pub struct LocalSiteInsertForm {
pub site_id: SiteId, pub site_id: SiteId,
pub site_setup: Option<bool>, pub site_setup: Option<bool>,
pub enable_downvotes: Option<bool>, pub enable_downvotes: Option<bool>,
pub enable_nsfw: Option<bool>,
pub community_creation_admin_only: Option<bool>, pub community_creation_admin_only: Option<bool>,
pub require_email_verification: Option<bool>, pub require_email_verification: Option<bool>,
pub application_question: Option<String>, pub application_question: Option<String>,
@ -109,7 +106,6 @@ pub struct LocalSiteInsertForm {
pub struct LocalSiteUpdateForm { pub struct LocalSiteUpdateForm {
pub site_setup: Option<bool>, pub site_setup: Option<bool>,
pub enable_downvotes: Option<bool>, pub enable_downvotes: Option<bool>,
pub enable_nsfw: Option<bool>,
pub community_creation_admin_only: Option<bool>, pub community_creation_admin_only: Option<bool>,
pub require_email_verification: Option<bool>, pub require_email_verification: Option<bool>,
pub application_question: Option<Option<String>>, pub application_question: Option<Option<String>>,

View file

@ -35,9 +35,6 @@ pub struct LocalUser {
/// Whether to show avatars. /// Whether to show avatars.
pub show_avatars: bool, pub show_avatars: bool,
pub send_notifications_to_email: bool, pub send_notifications_to_email: bool,
/// Whether to show comment / post scores.
// TODO now that there is a vote_display_mode, this can be gotten rid of in future releases.
pub show_scores: bool,
/// Whether to show bot accounts. /// Whether to show bot accounts.
pub show_bot_accounts: bool, pub show_bot_accounts: bool,
/// Whether to show read posts. /// Whether to show read posts.
@ -93,8 +90,6 @@ pub struct LocalUserInsertForm {
#[new(default)] #[new(default)]
pub show_bot_accounts: Option<bool>, pub show_bot_accounts: Option<bool>,
#[new(default)] #[new(default)]
pub show_scores: Option<bool>,
#[new(default)]
pub show_read_posts: Option<bool>, pub show_read_posts: Option<bool>,
#[new(default)] #[new(default)]
pub email_verified: Option<bool>, pub email_verified: Option<bool>,
@ -138,7 +133,6 @@ pub struct LocalUserUpdateForm {
pub show_avatars: Option<bool>, pub show_avatars: Option<bool>,
pub send_notifications_to_email: Option<bool>, pub send_notifications_to_email: Option<bool>,
pub show_bot_accounts: Option<bool>, pub show_bot_accounts: Option<bool>,
pub show_scores: Option<bool>,
pub show_read_posts: Option<bool>, pub show_read_posts: Option<bool>,
pub email_verified: Option<bool>, pub email_verified: Option<bool>,
pub accepted_application: Option<bool>, pub accepted_application: Option<bool>,

View file

@ -20,6 +20,7 @@ use typed_builder::TypedBuilder;
#[cfg_attr(feature = "full", ts(export))] #[cfg_attr(feature = "full", ts(export))]
/// The vote display settings for your user. /// The vote display settings for your user.
pub struct LocalUserVoteDisplayMode { pub struct LocalUserVoteDisplayMode {
#[serde(skip)]
pub local_user_id: LocalUserId, pub local_user_id: LocalUserId,
pub score: bool, pub score: bool,
pub upvotes: bool, pub upvotes: bool,

View file

@ -30,7 +30,7 @@ use i_love_jesus::CursorKey;
use lemmy_utils::{ use lemmy_utils::{
error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
settings::SETTINGS, settings::SETTINGS,
utils::validation::clean_url_params, utils::validation::clean_url,
}; };
use regex::Regex; use regex::Regex;
use rustls::{ use rustls::{
@ -305,7 +305,7 @@ pub fn diesel_url_update(opt: Option<&str>) -> LemmyResult<Option<Option<DbUrl>>
// An empty string is an erase // An empty string is an erase
Some("") => Ok(Some(None)), Some("") => Ok(Some(None)),
Some(str_url) => Url::parse(str_url) Some(str_url) => Url::parse(str_url)
.map(|u| Some(Some(clean_url_params(&u).into()))) .map(|u| Some(Some(clean_url(&u).into())))
.with_lemmy_type(LemmyErrorType::InvalidUrl), .with_lemmy_type(LemmyErrorType::InvalidUrl),
None => Ok(None), None => Ok(None),
} }
@ -316,7 +316,7 @@ pub fn diesel_url_update(opt: Option<&str>) -> LemmyResult<Option<Option<DbUrl>>
pub fn diesel_url_create(opt: Option<&str>) -> LemmyResult<Option<DbUrl>> { pub fn diesel_url_create(opt: Option<&str>) -> LemmyResult<Option<DbUrl>> {
match opt { match opt {
Some(str_url) => Url::parse(str_url) Some(str_url) => Url::parse(str_url)
.map(|u| Some(clean_url_params(&u).into())) .map(|u| Some(clean_url(&u).into()))
.with_lemmy_type(LemmyErrorType::InvalidUrl), .with_lemmy_type(LemmyErrorType::InvalidUrl),
None => Ok(None), None => Ok(None),
} }

View file

@ -196,7 +196,7 @@ impl CommentReportView {
queries().read(pool, (report_id, my_person_id)).await queries().read(pool, (report_id, my_person_id)).await
} }
/// Returns the current unresolved post report count for the communities you mod /// Returns the current unresolved comment report count for the communities you mod
pub async fn get_report_count( pub async fn get_report_count(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
my_person_id: PersonId, my_person_id: PersonId,

View file

@ -387,13 +387,16 @@ fn queries<'a>() -> Queries<
if let Some(search_term) = &options.search_term { if let Some(search_term) = &options.search_term {
let searcher = fuzzy_search(search_term); let searcher = fuzzy_search(search_term);
query = query query = if options.title_only.unwrap_or_default() {
.filter( query.filter(post::name.ilike(searcher))
} else {
query.filter(
post::name post::name
.ilike(searcher.clone()) .ilike(searcher.clone())
.or(post::body.ilike(searcher)), .or(post::body.ilike(searcher)),
) )
.filter(not(post::removed.or(post::deleted))); }
.filter(not(post::removed.or(post::deleted)));
} }
if !options if !options
@ -617,6 +620,7 @@ pub struct PostQuery<'a> {
pub saved_only: Option<bool>, pub saved_only: Option<bool>,
pub liked_only: Option<bool>, pub liked_only: Option<bool>,
pub disliked_only: Option<bool>, pub disliked_only: Option<bool>,
pub title_only: Option<bool>,
pub page: Option<i64>, pub page: Option<i64>,
pub limit: Option<i64>, pub limit: Option<i64>,
pub page_after: Option<PaginationCursorData>, pub page_after: Option<PaginationCursorData>,

View file

@ -252,7 +252,6 @@ mod tests {
show_avatars: inserted_sara_local_user.show_avatars, show_avatars: inserted_sara_local_user.show_avatars,
send_notifications_to_email: inserted_sara_local_user.send_notifications_to_email, send_notifications_to_email: inserted_sara_local_user.send_notifications_to_email,
show_bot_accounts: inserted_sara_local_user.show_bot_accounts, show_bot_accounts: inserted_sara_local_user.show_bot_accounts,
show_scores: inserted_sara_local_user.show_scores,
show_read_posts: inserted_sara_local_user.show_read_posts, show_read_posts: inserted_sara_local_user.show_read_posts,
email_verified: inserted_sara_local_user.email_verified, email_verified: inserted_sara_local_user.email_verified,
accepted_application: inserted_sara_local_user.accepted_application, accepted_application: inserted_sara_local_user.accepted_application,

View file

@ -1,28 +1,32 @@
use crate::structs::SiteView; use crate::structs::SiteView;
use diesel::{result::Error, ExpressionMethods, JoinOnDsl, OptionalExtension, QueryDsl}; use diesel::{ExpressionMethods, JoinOnDsl, OptionalExtension, QueryDsl};
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_db_schema::{ use lemmy_db_schema::{
schema::{local_site, local_site_rate_limit, site, site_aggregates}, schema::{local_site, local_site_rate_limit, site, site_aggregates},
utils::{get_conn, DbPool}, utils::{get_conn, DbPool},
}; };
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
impl SiteView { impl SiteView {
pub async fn read_local(pool: &mut DbPool<'_>) -> Result<Option<Self>, Error> { pub async fn read_local(pool: &mut DbPool<'_>) -> LemmyResult<Self> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
site::table Ok(
.inner_join(local_site::table) site::table
.inner_join( .inner_join(local_site::table)
local_site_rate_limit::table.on(local_site::id.eq(local_site_rate_limit::local_site_id)), .inner_join(
) local_site_rate_limit::table.on(local_site::id.eq(local_site_rate_limit::local_site_id)),
.inner_join(site_aggregates::table) )
.select(( .inner_join(site_aggregates::table)
site::all_columns, .select((
local_site::all_columns, site::all_columns,
local_site_rate_limit::all_columns, local_site::all_columns,
site_aggregates::all_columns, local_site_rate_limit::all_columns,
)) site_aggregates::all_columns,
.first(conn) ))
.await .first(conn)
.optional() .await
.optional()?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?,
)
} }
} }

View file

@ -1,24 +0,0 @@
use crate::structs::CommunityBlockView;
use diesel::{result::Error, ExpressionMethods, QueryDsl};
use diesel_async::RunQueryDsl;
use lemmy_db_schema::{
newtypes::PersonId,
schema::{community, community_block, person},
utils::{get_conn, DbPool},
};
impl CommunityBlockView {
pub async fn for_person(pool: &mut DbPool<'_>, person_id: PersonId) -> Result<Vec<Self>, Error> {
let conn = &mut get_conn(pool).await?;
community_block::table
.inner_join(person::table)
.inner_join(community::table)
.select((person::all_columns, community::all_columns))
.filter(community_block::person_id.eq(person_id))
.filter(community::deleted.eq(false))
.filter(community::removed.eq(false))
.order_by(community_block::published)
.load::<CommunityBlockView>(conn)
.await
}
}

View file

@ -1,27 +0,0 @@
use crate::structs::InstanceBlockView;
use diesel::{result::Error, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl};
use diesel_async::RunQueryDsl;
use lemmy_db_schema::{
newtypes::PersonId,
schema::{instance, instance_block, person, site},
utils::{get_conn, DbPool},
};
impl InstanceBlockView {
pub async fn for_person(pool: &mut DbPool<'_>, person_id: PersonId) -> Result<Vec<Self>, Error> {
let conn = &mut get_conn(pool).await?;
instance_block::table
.inner_join(person::table)
.inner_join(instance::table)
.left_join(site::table.on(site::instance_id.eq(instance::id)))
.select((
person::all_columns,
instance::all_columns,
site::all_columns.nullable(),
))
.filter(instance_block::person_id.eq(person_id))
.order_by(instance_block::published)
.load::<InstanceBlockView>(conn)
.await
}
}

View file

@ -1,8 +1,6 @@
#[cfg(feature = "full")] #[cfg(feature = "full")]
pub mod comment_reply_view; pub mod comment_reply_view;
#[cfg(feature = "full")] #[cfg(feature = "full")]
pub mod community_block_view;
#[cfg(feature = "full")]
pub mod community_follower_view; pub mod community_follower_view;
#[cfg(feature = "full")] #[cfg(feature = "full")]
pub mod community_moderator_view; pub mod community_moderator_view;
@ -11,10 +9,6 @@ pub mod community_person_ban_view;
#[cfg(feature = "full")] #[cfg(feature = "full")]
pub mod community_view; pub mod community_view;
#[cfg(feature = "full")] #[cfg(feature = "full")]
pub mod instance_block_view;
#[cfg(feature = "full")]
pub mod person_block_view;
#[cfg(feature = "full")]
pub mod person_mention_view; pub mod person_mention_view;
#[cfg(feature = "full")] #[cfg(feature = "full")]
pub mod person_view; pub mod person_view;

View file

@ -1,30 +0,0 @@
use crate::structs::PersonBlockView;
use diesel::{result::Error, ExpressionMethods, JoinOnDsl, QueryDsl};
use diesel_async::RunQueryDsl;
use lemmy_db_schema::{
newtypes::PersonId,
schema::{person, person_block},
utils::{get_conn, DbPool},
};
impl PersonBlockView {
pub async fn for_person(pool: &mut DbPool<'_>, person_id: PersonId) -> Result<Vec<Self>, Error> {
let conn = &mut get_conn(pool).await?;
let target_person_alias = diesel::alias!(person as person1);
person_block::table
.inner_join(person::table.on(person_block::person_id.eq(person::id)))
.inner_join(
target_person_alias.on(person_block::target_id.eq(target_person_alias.field(person::id))),
)
.select((
person::all_columns,
target_person_alias.fields(person::all_columns),
))
.filter(person_block::person_id.eq(person_id))
.filter(target_person_alias.field(person::deleted).eq(false))
.order_by(person_block::published)
.load::<PersonBlockView>(conn)
.await
}
}

View file

@ -6,11 +6,9 @@ use lemmy_db_schema::{
comment::Comment, comment::Comment,
comment_reply::CommentReply, comment_reply::CommentReply,
community::Community, community::Community,
instance::Instance,
person::Person, person::Person,
person_mention::PersonMention, person_mention::PersonMention,
post::Post, post::Post,
site::Site,
}, },
SubscribedType, SubscribedType,
}; };
@ -19,28 +17,6 @@ use serde_with::skip_serializing_none;
#[cfg(feature = "full")] #[cfg(feature = "full")]
use ts_rs::TS; use ts_rs::TS;
#[derive(Debug, Serialize, Deserialize, Clone)]
#[cfg_attr(feature = "full", derive(TS, Queryable))]
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
#[cfg_attr(feature = "full", ts(export))]
/// A community block.
pub struct CommunityBlockView {
pub person: Person,
pub community: Community,
}
#[skip_serializing_none]
#[derive(Debug, Serialize, Deserialize, Clone)]
#[cfg_attr(feature = "full", derive(TS, Queryable))]
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
#[cfg_attr(feature = "full", ts(export))]
/// An instance block by a user.
pub struct InstanceBlockView {
pub person: Person,
pub instance: Instance,
pub site: Option<Site>,
}
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
#[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", derive(TS, Queryable))]
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
@ -83,16 +59,6 @@ pub struct CommunityView {
pub banned_from_community: bool, pub banned_from_community: bool,
} }
#[derive(Debug, Serialize, Deserialize, Clone)]
#[cfg_attr(feature = "full", derive(TS, Queryable))]
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
#[cfg_attr(feature = "full", ts(export))]
/// A person block.
pub struct PersonBlockView {
pub person: Person,
pub target: Person,
}
#[skip_serializing_none] #[skip_serializing_none]
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
#[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", derive(TS, Queryable))]

View file

@ -32,7 +32,7 @@ serde_json.workspace = true
tokio = { workspace = true, features = ["full"] } tokio = { workspace = true, features = ["full"] }
tracing.workspace = true tracing.workspace = true
moka.workspace = true moka.workspace = true
tokio-util = "0.7.11" tokio-util = "0.7.12"
async-trait.workspace = true async-trait.workspace = true
[dev-dependencies] [dev-dependencies]

View file

@ -459,7 +459,6 @@ mod test {
traits::Crud, traits::Crud,
}; };
use lemmy_utils::error::LemmyResult; use lemmy_utils::error::LemmyResult;
use reqwest::StatusCode;
use serde_json::{json, Value}; use serde_json::{json, Value};
use serial_test::serial; use serial_test::serial;
use test_context::{test_context, AsyncTestContext}; use test_context::{test_context, AsyncTestContext};
@ -688,7 +687,7 @@ mod test {
|inbox_sender: actix_web::web::Data<UnboundedSender<String>>, body: String| async move { |inbox_sender: actix_web::web::Data<UnboundedSender<String>>, body: String| async move {
tracing::debug!("received activity: {:?}", body); tracing::debug!("received activity: {:?}", body);
inbox_sender.send(body.clone()).unwrap(); inbox_sender.send(body.clone()).unwrap();
HttpResponse::new(StatusCode::OK) HttpResponse::new(actix_web::http::StatusCode::OK)
}, },
), ),
) )

View file

@ -32,5 +32,5 @@ serde = { workspace = true }
url = { workspace = true } url = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
urlencoding = { workspace = true } http.workspace = true
rss = "2.0.8" rss = "2.0.9"

View file

@ -151,9 +151,7 @@ async fn get_feed_data(
limit: i64, limit: i64,
page: i64, page: i64,
) -> LemmyResult<HttpResponse> { ) -> LemmyResult<HttpResponse> {
let site_view = SiteView::read_local(&mut context.pool()) let site_view = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
check_private_instance(&None, &site_view.local_site)?; check_private_instance(&None, &site_view.local_site)?;
@ -258,9 +256,7 @@ async fn get_feed_user(
page: &i64, page: &i64,
user_name: &str, user_name: &str,
) -> LemmyResult<Channel> { ) -> LemmyResult<Channel> {
let site_view = SiteView::read_local(&mut context.pool()) let site_view = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
let person = Person::read_from_name(&mut context.pool(), user_name, false) let person = Person::read_from_name(&mut context.pool(), user_name, false)
.await? .await?
.ok_or(LemmyErrorType::CouldntFindPerson)?; .ok_or(LemmyErrorType::CouldntFindPerson)?;
@ -298,9 +294,7 @@ async fn get_feed_community(
page: &i64, page: &i64,
community_name: &str, community_name: &str,
) -> LemmyResult<Channel> { ) -> LemmyResult<Channel> {
let site_view = SiteView::read_local(&mut context.pool()) let site_view = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
let community = Community::read_from_name(&mut context.pool(), community_name, false) let community = Community::read_from_name(&mut context.pool(), community_name, false)
.await? .await?
.ok_or(LemmyErrorType::CouldntFindCommunity)?; .ok_or(LemmyErrorType::CouldntFindCommunity)?;
@ -345,9 +339,7 @@ async fn get_feed_front(
page: &i64, page: &i64,
jwt: &str, jwt: &str,
) -> LemmyResult<Channel> { ) -> LemmyResult<Channel> {
let site_view = SiteView::read_local(&mut context.pool()) let site_view = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
let local_user = local_user_view_from_jwt(jwt, context).await?; let local_user = local_user_view_from_jwt(jwt, context).await?;
check_private_instance(&Some(local_user.clone()), &site_view.local_site)?; check_private_instance(&Some(local_user.clone()), &site_view.local_site)?;
@ -382,9 +374,7 @@ async fn get_feed_front(
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
async fn get_feed_inbox(context: &LemmyContext, jwt: &str) -> LemmyResult<Channel> { async fn get_feed_inbox(context: &LemmyContext, jwt: &str) -> LemmyResult<Channel> {
let site_view = SiteView::read_local(&mut context.pool()) let site_view = SiteView::read_local(&mut context.pool()).await?;
.await?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
let local_user = local_user_view_from_jwt(jwt, context).await?; let local_user = local_user_view_from_jwt(jwt, context).await?;
let person_id = local_user.local_user.person_id; let person_id = local_user.local_user.person_id;
let show_bot_accounts = local_user.local_user.show_bot_accounts; let show_bot_accounts = local_user.local_user.show_bot_accounts;

View file

@ -2,6 +2,7 @@ use actix_web::{
body::BodyStream, body::BodyStream,
http::{ http::{
header::{HeaderName, ACCEPT_ENCODING, HOST}, header::{HeaderName, ACCEPT_ENCODING, HOST},
Method,
StatusCode, StatusCode,
}, },
web, web,
@ -10,6 +11,7 @@ use actix_web::{
HttpResponse, HttpResponse,
}; };
use futures::stream::{Stream, StreamExt}; use futures::stream::{Stream, StreamExt};
use http::HeaderValue;
use lemmy_api_common::{context::LemmyContext, request::PictrsResponse}; use lemmy_api_common::{context::LemmyContext, request::PictrsResponse};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
images::{LocalImage, LocalImageForm, RemoteImage}, images::{LocalImage, LocalImageForm, RemoteImage},
@ -22,7 +24,6 @@ use reqwest_middleware::{ClientWithMiddleware, RequestBuilder};
use serde::Deserialize; use serde::Deserialize;
use std::time::Duration; use std::time::Duration;
use url::Url; use url::Url;
use urlencoding::decode;
pub fn config( pub fn config(
cfg: &mut web::ServiceConfig, cfg: &mut web::ServiceConfig,
@ -110,7 +111,7 @@ fn adapt_request(
const INVALID_HEADERS: &[HeaderName] = &[ACCEPT_ENCODING, HOST]; const INVALID_HEADERS: &[HeaderName] = &[ACCEPT_ENCODING, HOST];
let client_request = client let client_request = client
.request(request.method().clone(), url) .request(convert_method(request.method()), url)
.timeout(REQWEST_TIMEOUT); .timeout(REQWEST_TIMEOUT);
request request
@ -120,7 +121,8 @@ fn adapt_request(
if INVALID_HEADERS.contains(key) { if INVALID_HEADERS.contains(key) {
client_req client_req
} else { } else {
client_req.header(key, value) // TODO: remove as_str and as_bytes conversions after actix-web upgrades to http 1.0
client_req.header(key.as_str(), value.as_bytes())
} }
}) })
} }
@ -167,7 +169,7 @@ async fn upload(
} }
} }
Ok(HttpResponse::build(status).json(images)) Ok(HttpResponse::build(convert_status(status)).json(images))
} }
async fn full_res( async fn full_res(
@ -210,14 +212,14 @@ async fn image(
let res = client_req.send().await?; let res = client_req.send().await?;
if res.status() == StatusCode::NOT_FOUND { if res.status() == http::StatusCode::NOT_FOUND {
return Ok(HttpResponse::NotFound().finish()); return Ok(HttpResponse::NotFound().finish());
} }
let mut client_res = HttpResponse::build(res.status()); let mut client_res = HttpResponse::build(StatusCode::from_u16(res.status().as_u16())?);
for (name, value) in res.headers().iter().filter(|(h, _)| *h != "connection") { for (name, value) in res.headers().iter().filter(|(h, _)| *h != "connection") {
client_res.insert_header((name.clone(), value.clone())); client_res.insert_header(convert_header(name, value));
} }
Ok(client_res.body(BodyStream::new(res.bytes_stream()))) Ok(client_res.body(BodyStream::new(res.bytes_stream())))
@ -246,7 +248,7 @@ async fn delete(
LocalImage::delete_by_alias(&mut context.pool(), &file).await?; LocalImage::delete_by_alias(&mut context.pool(), &file).await?;
Ok(HttpResponse::build(res.status()).body(BodyStream::new(res.bytes_stream()))) Ok(HttpResponse::build(convert_status(res.status())).body(BodyStream::new(res.bytes_stream())))
} }
pub async fn image_proxy( pub async fn image_proxy(
@ -255,7 +257,7 @@ pub async fn image_proxy(
client: web::Data<ClientWithMiddleware>, client: web::Data<ClientWithMiddleware>,
context: web::Data<LemmyContext>, context: web::Data<LemmyContext>,
) -> LemmyResult<HttpResponse> { ) -> LemmyResult<HttpResponse> {
let url = Url::parse(&decode(&params.url)?)?; let url = Url::parse(&params.url)?;
// Check that url corresponds to a federated image so that this can't be abused as a proxy // Check that url corresponds to a federated image so that this can't be abused as a proxy
// for arbitrary purposes. // for arbitrary purposes.
@ -309,3 +311,14 @@ where
std::pin::Pin::new(&mut self.rx).poll_recv(cx) std::pin::Pin::new(&mut self.rx).poll_recv(cx)
} }
} }
// TODO: remove these conversions after actix-web upgrades to http 1.0
fn convert_status(status: http::StatusCode) -> StatusCode {
StatusCode::from_u16(status.as_u16()).expect("status can be converted")
}
fn convert_method(method: &Method) -> http::Method {
http::Method::from_bytes(method.as_str().as_bytes()).expect("method can be converted")
}
fn convert_header<'a>(name: &'a http::HeaderName, value: &'a HeaderValue) -> (&'a str, &'a [u8]) {
(name.as_str(), value.as_bytes())
}

View file

@ -1,11 +1,10 @@
use actix_web::{error::ErrorBadRequest, web, Error, HttpResponse, Result}; use actix_web::{web, Error, HttpResponse, Result};
use anyhow::anyhow;
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::RegistrationMode; use lemmy_db_schema::RegistrationMode;
use lemmy_db_views::structs::SiteView; use lemmy_db_views::structs::SiteView;
use lemmy_utils::{ use lemmy_utils::{
cache_header::{cache_1hour, cache_3days}, cache_header::{cache_1hour, cache_3days},
error::{LemmyError, LemmyResult}, error::LemmyResult,
VERSION, VERSION,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -44,10 +43,7 @@ async fn node_info_well_known(context: web::Data<LemmyContext>) -> LemmyResult<H
} }
async fn node_info(context: web::Data<LemmyContext>) -> Result<HttpResponse, Error> { async fn node_info(context: web::Data<LemmyContext>) -> Result<HttpResponse, Error> {
let site_view = SiteView::read_local(&mut context.pool()) let site_view = SiteView::read_local(&mut context.pool()).await?;
.await
.map_err(|_| ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))?
.ok_or(ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))?;
// Since there are 3 registration options, // Since there are 3 registration options,
// we need to set open_registrations as true if RegistrationMode is not Closed. // we need to set open_registrations as true if RegistrationMode is not Closed.

View file

@ -84,7 +84,7 @@ async fn get_webfinger_response(
Ok( Ok(
HttpResponse::Ok() HttpResponse::Ok()
.content_type(&WEBFINGER_CONTENT_TYPE) .content_type(WEBFINGER_CONTENT_TYPE.as_bytes())
.json(json), .json(json),
) )
} }

View file

@ -32,7 +32,6 @@ full = [
"dep:actix-web", "dep:actix-web",
"dep:serde_json", "dep:serde_json",
"dep:anyhow", "dep:anyhow",
"dep:tracing-error",
"dep:http", "dep:http",
"dep:deser-hjson", "dep:deser-hjson",
"dep:regex", "dep:regex",
@ -53,7 +52,6 @@ full = [
[dependencies] [dependencies]
regex = { workspace = true, optional = true } regex = { workspace = true, optional = true }
tracing = { workspace = true, optional = true } tracing = { workspace = true, optional = true }
tracing-error = { workspace = true, optional = true }
itertools = { workspace = true, optional = true } itertools = { workspace = true, optional = true }
serde = { workspace = true } serde = { workspace = true }
serde_json = { workspace = true, optional = true } serde_json = { workspace = true, optional = true }
@ -73,7 +71,7 @@ urlencoding = { workspace = true, optional = true }
html2text = { version = "0.12.5", optional = true } html2text = { version = "0.12.5", optional = true }
deser-hjson = { version = "2.2.4", optional = true } deser-hjson = { version = "2.2.4", optional = true }
smart-default = { version = "0.7.1", optional = true } smart-default = { version = "0.7.1", optional = true }
lettre = { version = "0.11.7", default-features = false, features = [ lettre = { version = "0.11.8", default-features = false, features = [
"builder", "builder",
"tokio1", "tokio1",
"tokio1-rustls-tls", "tokio1-rustls-tls",
@ -83,6 +81,7 @@ markdown-it = { version = "0.6.1", optional = true }
ts-rs = { workspace = true, optional = true } ts-rs = { workspace = true, optional = true }
enum-map = { workspace = true, optional = true } enum-map = { workspace = true, optional = true }
cfg-if = "1" cfg-if = "1"
clearurls = { version = "0.0.4", features = ["linkify"] }
[dev-dependencies] [dev-dependencies]
reqwest = { workspace = true } reqwest = { workspace = true }

View file

@ -1,6 +1,6 @@
use cfg_if::cfg_if; use cfg_if::cfg_if;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Debug; use std::{backtrace::Backtrace, fmt::Debug};
use strum::{Display, EnumIter}; use strum::{Display, EnumIter};
#[derive(Display, Debug, Serialize, Deserialize, Clone, PartialEq, Eq, EnumIter, Hash)] #[derive(Display, Debug, Serialize, Deserialize, Clone, PartialEq, Eq, EnumIter, Hash)]
@ -186,14 +186,13 @@ pub enum LemmyErrorType {
cfg_if! { cfg_if! {
if #[cfg(feature = "full")] { if #[cfg(feature = "full")] {
use tracing_error::SpanTrace;
use std::fmt; use std::fmt;
pub type LemmyResult<T> = Result<T, LemmyError>; pub type LemmyResult<T> = Result<T, LemmyError>;
pub struct LemmyError { pub struct LemmyError {
pub error_type: LemmyErrorType, pub error_type: LemmyErrorType,
pub inner: anyhow::Error, pub inner: anyhow::Error,
pub context: SpanTrace, pub context: Backtrace,
} }
/// Maximum number of items in an array passed as API parameter. See [[LemmyErrorType::TooManyItems]] /// Maximum number of items in an array passed as API parameter. See [[LemmyErrorType::TooManyItems]]
@ -208,7 +207,7 @@ cfg_if! {
LemmyError { LemmyError {
error_type: LemmyErrorType::Unknown(format!("{}", &cause)), error_type: LemmyErrorType::Unknown(format!("{}", &cause)),
inner: cause, inner: cause,
context: SpanTrace::capture(), context: Backtrace::capture(),
} }
} }
} }
@ -232,13 +231,13 @@ cfg_if! {
} }
impl actix_web::error::ResponseError for LemmyError { impl actix_web::error::ResponseError for LemmyError {
fn status_code(&self) -> http::StatusCode { fn status_code(&self) -> actix_web::http::StatusCode {
if self.error_type == LemmyErrorType::IncorrectLogin { if self.error_type == LemmyErrorType::IncorrectLogin {
return http::StatusCode::UNAUTHORIZED; return actix_web::http::StatusCode::UNAUTHORIZED;
} }
match self.inner.downcast_ref::<diesel::result::Error>() { match self.inner.downcast_ref::<diesel::result::Error>() {
Some(diesel::result::Error::NotFound) => http::StatusCode::NOT_FOUND, Some(diesel::result::Error::NotFound) => actix_web::http::StatusCode::NOT_FOUND,
_ => http::StatusCode::BAD_REQUEST, _ => actix_web::http::StatusCode::BAD_REQUEST,
} }
} }
@ -253,7 +252,7 @@ cfg_if! {
LemmyError { LemmyError {
error_type, error_type,
inner, inner,
context: SpanTrace::capture(), context: Backtrace::capture(),
} }
} }
} }
@ -267,7 +266,7 @@ cfg_if! {
self.map_err(|error| LemmyError { self.map_err(|error| LemmyError {
error_type, error_type,
inner: error.into(), inner: error.into(),
context: SpanTrace::capture(), context: Backtrace::capture(),
}) })
} }
} }

View file

@ -37,6 +37,7 @@ mod tests {
use crate::error::{LemmyError, LemmyErrorType}; use crate::error::{LemmyError, LemmyErrorType};
use actix_web::{ use actix_web::{
error::ErrorInternalServerError, error::ErrorInternalServerError,
http::StatusCode,
middleware::ErrorHandlers, middleware::ErrorHandlers,
test, test,
web, web,
@ -45,7 +46,6 @@ mod tests {
Handler, Handler,
Responder, Responder,
}; };
use http::StatusCode;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
#[actix_web::test] #[actix_web::test]

View file

@ -1,4 +1,5 @@
use crate::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; use crate::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
use clearurls::UrlCleaner;
use itertools::Itertools; use itertools::Itertools;
use regex::{Regex, RegexBuilder, RegexSet}; use regex::{Regex, RegexBuilder, RegexSet};
use std::sync::LazyLock; use std::sync::LazyLock;
@ -10,17 +11,13 @@ static VALID_MATRIX_ID_REGEX: LazyLock<Regex> = LazyLock::new(|| {
.expect("compile regex") .expect("compile regex")
}); });
// taken from https://en.wikipedia.org/wiki/UTM_parameters // taken from https://en.wikipedia.org/wiki/UTM_parameters
static CLEAN_URL_PARAMS_REGEX: LazyLock<Regex> = LazyLock::new(|| { static URL_CLEANER: LazyLock<UrlCleaner> =
Regex::new( LazyLock::new(|| UrlCleaner::from_embedded_rules().expect("compile clearurls"));
r"^(utm_source|utm_medium|utm_campaign|utm_term|utm_content|gclid|gclsrc|dclid|fbclid)=",
)
.expect("compile regex")
});
const ALLOWED_POST_URL_SCHEMES: [&str; 3] = ["http", "https", "magnet"]; const ALLOWED_POST_URL_SCHEMES: [&str; 3] = ["http", "https", "magnet"];
const BODY_MAX_LENGTH: usize = 10000; const BODY_MAX_LENGTH: usize = 10000;
const POST_BODY_MAX_LENGTH: usize = 50000; const POST_BODY_MAX_LENGTH: usize = 50000;
const BIO_MAX_LENGTH: usize = 300; const BIO_MAX_LENGTH: usize = 1000;
const URL_MAX_LENGTH: usize = 2000; const URL_MAX_LENGTH: usize = 2000;
const ALT_TEXT_MAX_LENGTH: usize = 1500; const ALT_TEXT_MAX_LENGTH: usize = 1500;
const SITE_NAME_MAX_LENGTH: usize = 20; const SITE_NAME_MAX_LENGTH: usize = 20;
@ -257,16 +254,22 @@ pub fn build_and_check_regex(regex_str_opt: &Option<&str>) -> LemmyResult<Option
) )
} }
pub fn clean_url_params(url: &Url) -> Url { /// Cleans a url of tracking parameters.
let mut url_out = url.clone(); pub fn clean_url(url: &Url) -> Url {
if let Some(query) = url.query() { match URL_CLEANER.clear_single_url(url) {
let new_query = query Ok(res) => res.into_owned(),
.split_inclusive('&') // If there are any errors, just return the original url
.filter(|q| !CLEAN_URL_PARAMS_REGEX.is_match(q)) Err(_) => url.clone(),
.collect::<String>(); }
url_out.set_query(Some(&new_query)); }
/// Cleans all the links in a string of tracking parameters.
pub fn clean_urls_in_text(text: &str) -> String {
match URL_CLEANER.clear_text(text) {
Ok(res) => res.into_owned(),
// If there are any errors, just return the original text
Err(_) => text.to_owned(),
} }
url_out
} }
pub fn check_site_visibility_valid( pub fn check_site_visibility_valid(
@ -357,7 +360,8 @@ mod tests {
build_and_check_regex, build_and_check_regex,
check_site_visibility_valid, check_site_visibility_valid,
check_urls_are_valid, check_urls_are_valid,
clean_url_params, clean_url,
clean_urls_in_text,
is_url_blocked, is_url_blocked,
is_valid_actor_name, is_valid_actor_name,
is_valid_bio_field, is_valid_bio_field,
@ -378,18 +382,32 @@ mod tests {
#[test] #[test]
fn test_clean_url_params() -> LemmyResult<()> { fn test_clean_url_params() -> LemmyResult<()> {
let url = Url::parse("https://example.com/path/123?utm_content=buffercf3b2&utm_medium=social&user+name=random+user%20&id=123")?; let url = Url::parse("https://example.com/path/123?utm_content=buffercf3b2&utm_medium=social&user+name=random+user&id=123")?;
let cleaned = clean_url_params(&url); let cleaned = clean_url(&url);
let expected = Url::parse("https://example.com/path/123?user+name=random+user%20&id=123")?; let expected = Url::parse("https://example.com/path/123?user+name=random+user&id=123")?;
assert_eq!(expected.to_string(), cleaned.to_string()); assert_eq!(expected.to_string(), cleaned.to_string());
let url = Url::parse("https://example.com/path/123")?; let url = Url::parse("https://example.com/path/123")?;
let cleaned = clean_url_params(&url); let cleaned = clean_url(&url);
assert_eq!(url.to_string(), cleaned.to_string()); assert_eq!(url.to_string(), cleaned.to_string());
Ok(()) Ok(())
} }
#[test]
fn test_clean_body() -> LemmyResult<()> {
let text = "[a link](https://example.com/path/123?utm_content=buffercf3b2&utm_medium=social&user+name=random+user&id=123)";
let cleaned = clean_urls_in_text(text);
let expected = "[a link](https://example.com/path/123?user+name=random+user&id=123)";
assert_eq!(expected.to_string(), cleaned.to_string());
let text = "[a link](https://example.com/path/123)";
let cleaned = clean_urls_in_text(text);
assert_eq!(text.to_string(), cleaned);
Ok(())
}
#[test] #[test]
fn regex_checks() { fn regex_checks() {
assert!(is_valid_post_title("hi").is_err()); assert!(is_valid_post_title("hi").is_err());

@ -1 +1 @@
Subproject commit bc9b5305769900c5a59d8f139f110e004085f92b Subproject commit 7adddded581fcd965ab33b91c5fe10e0d7247208

View file

@ -0,0 +1,3 @@
ALTER TABLE local_user
ADD COLUMN show_scores boolean NOT NULL DEFAULT TRUE;

View file

@ -0,0 +1,3 @@
ALTER TABLE local_user
DROP COLUMN show_scores;

View file

@ -0,0 +1,17 @@
ALTER TABLE local_site
ADD COLUMN enable_nsfw boolean NOT NULL DEFAULT FALSE;
UPDATE
local_site
SET
enable_nsfw = CASE WHEN site.content_warning IS NULL THEN
FALSE
ELSE
TRUE
END
FROM
site
WHERE
-- only local site has private key
site.private_key IS NOT NULL;

View file

@ -0,0 +1,20 @@
-- if site has enable_nsfw, set a default content warning
UPDATE
site
SET
content_warning = CASE WHEN local_site.enable_nsfw THEN
'NSFW'
ELSE
NULL
END
FROM
local_site
-- only local site has private key
WHERE
private_key IS NOT NULL
-- dont overwrite existing content warning
AND content_warning IS NOT NULL;
ALTER TABLE local_site
DROP enable_nsfw;

View file

@ -6,7 +6,7 @@ set -e
CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
cd $CWD/../ cd "$CWD/../"
source scripts/start_dev_db.sh source scripts/start_dev_db.sh

View file

@ -5,7 +5,7 @@ set -e
CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
cd $CWD/../ cd "$CWD/../"
source scripts/start_dev_db.sh source scripts/start_dev_db.sh

View file

@ -3,7 +3,7 @@ set -e
CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
cd $CWD/../ cd "$CWD/../"
# Format rust files # Format rust files
cargo +nightly fmt cargo +nightly fmt

View file

@ -8,7 +8,7 @@ third_semver=$(echo $new_tag | cut -d "." -f 3)
# Goto the upper route # Goto the upper route
CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
cd $CWD/../ cd "$CWD/../"
# The docker installs should only update for non release-candidates # The docker installs should only update for non release-candidates
# IE, when the third semver is a number, not '2-rc' # IE, when the third semver is a number, not '2-rc'

View file

@ -5,7 +5,7 @@ set -e
CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
cd $CWD/../ cd "$CWD/../"
# Copy the files to a temp dir # Copy the files to a temp dir
TMP_DIR=$(mktemp -d) TMP_DIR=$(mktemp -d)

View file

@ -3,7 +3,7 @@ set -e
CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
cd $CWD/../ cd "$CWD/../"
PACKAGE="$1" PACKAGE="$1"
echo "$PACKAGE" echo "$PACKAGE"

View file

@ -3,7 +3,7 @@ set -e
CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
cd $CWD/../ cd "$CWD/../"
PACKAGE="$1" PACKAGE="$1"
TEST="$2" TEST="$2"

View file

@ -345,7 +345,7 @@ async fn instance_actor_2022_01_28(
settings: &Settings, settings: &Settings,
) -> LemmyResult<()> { ) -> LemmyResult<()> {
info!("Running instance_actor_2021_09_29"); info!("Running instance_actor_2021_09_29");
if let Ok(Some(site_view)) = SiteView::read_local(pool).await { if let Ok(site_view) = SiteView::read_local(pool).await {
let site = site_view.site; let site = site_view.site;
// if site already has public key, we dont need to do anything here // if site already has public key, we dont need to do anything here
if !site.public_key.is_empty() { if !site.public_key.is_empty() {

View file

@ -1,17 +1,10 @@
pub mod api_routes_http; pub mod api_routes_http;
pub mod code_migrations; pub mod code_migrations;
pub mod prometheus_metrics; pub mod prometheus_metrics;
pub mod root_span_builder;
pub mod scheduled_tasks; pub mod scheduled_tasks;
pub mod session_middleware; pub mod session_middleware;
#[cfg(feature = "console")]
pub mod telemetry;
use crate::{ use crate::{code_migrations::run_advanced_migrations, session_middleware::SessionMiddleware};
code_migrations::run_advanced_migrations,
root_span_builder::QuieterRootSpanBuilder,
session_middleware::SessionMiddleware,
};
use activitypub_federation::config::{FederationConfig, FederationMiddleware}; use activitypub_federation::config::{FederationConfig, FederationMiddleware};
use actix_cors::Cors; use actix_cors::Cors;
use actix_web::{ use actix_web::{
@ -55,14 +48,9 @@ use prometheus_metrics::serve_prometheus;
use reqwest_middleware::ClientBuilder; use reqwest_middleware::ClientBuilder;
use reqwest_tracing::TracingMiddleware; use reqwest_tracing::TracingMiddleware;
use serde_json::json; use serde_json::json;
use std::{env, ops::Deref, time::Duration}; use std::{ops::Deref, time::Duration};
use tokio::signal::unix::SignalKind; use tokio::signal::unix::SignalKind;
use tracing::subscriber::set_global_default; use tracing_actix_web::{DefaultRootSpanBuilder, TracingLogger};
use tracing_actix_web::TracingLogger;
use tracing_error::ErrorLayer;
use tracing_log::LogTracer;
use tracing_subscriber::{filter::Targets, layer::SubscriberExt, Layer, Registry};
use url::Url;
/// Timeout for HTTP requests while sending activities. A longer timeout provides better /// Timeout for HTTP requests while sending activities. A longer timeout provides better
/// compatibility with other ActivityPub software that might allocate more time for synchronous /// compatibility with other ActivityPub software that might allocate more time for synchronous
@ -119,7 +107,7 @@ pub struct CmdArgs {
/// Placing the main function in lib.rs allows other crates to import it and embed Lemmy /// Placing the main function in lib.rs allows other crates to import it and embed Lemmy
pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> { pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> {
// Print version number to log // Print version number to log
println!("Lemmy v{VERSION}"); println!("Starting Lemmy v{VERSION}");
// return error 503 while running db migrations and startup tasks // return error 503 while running db migrations and startup tasks
let mut startup_server_handle = None; let mut startup_server_handle = None;
@ -139,9 +127,7 @@ pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> {
.expect("Couldn't initialize secrets."); .expect("Couldn't initialize secrets.");
// Make sure the local site is set up. // Make sure the local site is set up.
let site_view = SiteView::read_local(&mut (&pool).into()) let site_view = SiteView::read_local(&mut (&pool).into()).await?;
.await?
.expect("local site not set up");
let local_site = site_view.local_site; let local_site = site_view.local_site;
let federation_enabled = local_site.federation_enabled; let federation_enabled = local_site.federation_enabled;
@ -318,7 +304,7 @@ fn create_http_server(
)) ))
.wrap(middleware::Compress::default()) .wrap(middleware::Compress::default())
.wrap(cors_config) .wrap(cors_config)
.wrap(TracingLogger::<QuieterRootSpanBuilder>::new()) .wrap(TracingLogger::<DefaultRootSpanBuilder>::new())
.wrap(ErrorHandlers::new().default_handler(jsonify_plain_text_errors)) .wrap(ErrorHandlers::new().default_handler(jsonify_plain_text_errors))
.app_data(Data::new(context.clone())) .app_data(Data::new(context.clone()))
.app_data(Data::new(rate_limit_cell.clone())) .app_data(Data::new(rate_limit_cell.clone()))
@ -373,38 +359,3 @@ fn cors_config(settings: &Settings) -> Cors {
.max_age(3600), .max_age(3600),
} }
} }
pub fn init_logging(opentelemetry_url: &Option<Url>) -> LemmyResult<()> {
LogTracer::init()?;
let log_description = env::var("RUST_LOG").unwrap_or_else(|_| "info".into());
let targets = log_description
.trim()
.trim_matches('"')
.parse::<Targets>()?;
let format_layer = {
#[cfg(feature = "json-log")]
let layer = tracing_subscriber::fmt::layer().with_ansi(false).json();
#[cfg(not(feature = "json-log"))]
let layer = tracing_subscriber::fmt::layer().with_ansi(false);
layer.with_filter(targets.clone())
};
let subscriber = Registry::default()
.with(format_layer)
.with(ErrorLayer::default());
if let Some(_url) = opentelemetry_url {
#[cfg(feature = "console")]
telemetry::init_tracing(_url.as_ref(), subscriber, targets)?;
#[cfg(not(feature = "console"))]
tracing::error!("Feature `console` must be enabled for opentelemetry tracing");
} else {
set_global_default(subscriber)?;
}
Ok(())
}

View file

@ -1,47 +1,24 @@
use clap::Parser; use clap::Parser;
use lemmy_server::{init_logging, start_lemmy_server, CmdArgs}; use lemmy_server::{start_lemmy_server, CmdArgs};
use lemmy_utils::{error::LemmyResult, settings::SETTINGS}; use lemmy_utils::error::LemmyResult;
use tracing::level_filters::LevelFilter;
use tracing_subscriber::EnvFilter;
pub extern crate rustls; pub extern crate rustls;
#[tokio::main] #[tokio::main]
pub async fn main() -> LemmyResult<()> { pub async fn main() -> LemmyResult<()> {
init_logging(&SETTINGS.opentelemetry_url)?; let filter = EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy();
tracing_subscriber::fmt().with_env_filter(filter).init();
let args = CmdArgs::parse(); let args = CmdArgs::parse();
rustls::crypto::ring::default_provider() rustls::crypto::ring::default_provider()
.install_default() .install_default()
.expect("Failed to install rustls crypto provider"); .expect("Failed to install rustls crypto provider");
#[cfg(not(feature = "embed-pictrs"))]
start_lemmy_server(args).await?; start_lemmy_server(args).await?;
#[cfg(feature = "embed-pictrs")]
{
let pictrs_port = &SETTINGS
.pictrs_config()
.unwrap_or_default()
.url
.port()
.unwrap_or(8080);
let pictrs_address = ["127.0.0.1", &pictrs_port.to_string()].join(":");
let pictrs_config = pict_rs::ConfigSource::memory(serde_json::json!({
"server": {
"address": pictrs_address
},
"repo": {
"type": "sled",
"path": "./pictrs/sled-repo"
},
"store": {
"type": "filesystem",
"path": "./pictrs/files"
}
}))
.init::<&str>(None)
.expect("initialize pictrs config");
let (lemmy, pictrs) = tokio::join!(start_lemmy_server(args), pictrs_config.run_on_localset());
lemmy?;
pictrs.expect("run pictrs");
}
Ok(()) Ok(())
} }

View file

@ -1,83 +0,0 @@
use actix_web::{http::StatusCode, ResponseError};
use tracing::Span;
use tracing_actix_web::RootSpanBuilder;
// Code in this module adapted from DefaultRootSpanBuilder
// https://github.com/LukeMathWalker/tracing-actix-web/blob/main/src/root_span_builder.rs
// and root_span!
// https://github.com/LukeMathWalker/tracing-actix-web/blob/main/src/root_span_macro.rs
pub struct QuieterRootSpanBuilder;
impl RootSpanBuilder for QuieterRootSpanBuilder {
fn on_request_start(request: &actix_web::dev::ServiceRequest) -> Span {
let request_id = tracing_actix_web::root_span_macro::private::get_request_id(request);
tracing::info_span!(
"HTTP request",
http.method = %request.method(),
http.scheme = request.connection_info().scheme(),
http.host = %request.connection_info().host(),
http.target = %request.uri().path(),
http.status_code = tracing::field::Empty,
otel.kind = "server",
otel.status_code = tracing::field::Empty,
trace_id = tracing::field::Empty,
request_id = %request_id,
exception.message = tracing::field::Empty,
// Not proper OpenTelemetry, but their terminology is fairly exception-centric
exception.details = tracing::field::Empty,
)
}
fn on_request_end<B>(
span: tracing::Span,
outcome: &Result<actix_web::dev::ServiceResponse<B>, actix_web::Error>,
) {
match &outcome {
Ok(response) => {
if let Some(error) = response.response().error() {
// use the status code already constructed for the outgoing HTTP response
handle_error(span, response.status(), error.as_response_error());
} else {
let code: i32 = response.response().status().as_u16().into();
span.record("http.status_code", code);
span.record("otel.status_code", "OK");
}
}
Err(error) => {
let response_error = error.as_response_error();
handle_error(span, response_error.status_code(), response_error);
}
};
}
}
fn handle_error(span: Span, status_code: StatusCode, response_error: &dyn ResponseError) {
let code: i32 = status_code.as_u16().into();
span.record("http.status_code", code);
if status_code.is_client_error() {
span.record("otel.status_code", "OK");
} else {
span.record("otel.status_code", "ERROR");
}
// pre-formatting errors is a workaround for https://github.com/tokio-rs/tracing/issues/1565
let display_error = format!("{response_error}");
tracing::info_span!(
parent: None,
"Error encountered while processing the incoming HTTP request"
)
.in_scope(|| {
if status_code.is_client_error() {
tracing::warn!("{}", display_error);
} else {
tracing::error!("{}", display_error);
}
});
span.record("exception.message", tracing::field::display(display_error));
}

View file

@ -1,7 +1,7 @@
use actix_web::{ use actix_web::{
body::MessageBody, body::MessageBody,
dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}, dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
http::header::CACHE_CONTROL, http::header::{HeaderValue, CACHE_CONTROL},
Error, Error,
HttpMessage, HttpMessage,
}; };
@ -9,7 +9,6 @@ use core::future::Ready;
use futures_util::future::LocalBoxFuture; use futures_util::future::LocalBoxFuture;
use lemmy_api::{local_user_view_from_jwt, read_auth_token}; use lemmy_api::{local_user_view_from_jwt, read_auth_token};
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use reqwest::header::HeaderValue;
use std::{future::ready, rc::Rc}; use std::{future::ready, rc::Rc};
#[derive(Clone)] #[derive(Clone)]

View file

@ -1,47 +0,0 @@
use console_subscriber::ConsoleLayer;
use lemmy_utils::error::LemmyResult;
use opentelemetry::{
sdk::{propagation::TraceContextPropagator, Resource},
KeyValue,
};
use opentelemetry_otlp::WithExportConfig;
use tracing::{subscriber::set_global_default, Subscriber};
use tracing_subscriber::{filter::Targets, layer::SubscriberExt, registry::LookupSpan, Layer};
pub fn init_tracing<S>(opentelemetry_url: &str, subscriber: S, targets: Targets) -> LemmyResult<()>
where
S: Subscriber + for<'a> LookupSpan<'a> + Send + Sync + 'static,
{
opentelemetry::global::set_text_map_propagator(TraceContextPropagator::new());
let console_layer = ConsoleLayer::builder()
.with_default_env()
.server_addr(([0, 0, 0, 0], 6669))
.event_buffer_capacity(1024 * 1024)
.spawn();
let subscriber = subscriber.with(console_layer);
let tracer = opentelemetry_otlp::new_pipeline()
.tracing()
.with_trace_config(
opentelemetry::sdk::trace::config()
.with_resource(Resource::new(vec![KeyValue::new("service.name", "lemmy")])),
)
.with_exporter(
opentelemetry_otlp::new_exporter()
.tonic()
.with_endpoint(opentelemetry_url),
)
.install_batch(opentelemetry::runtime::Tokio)?;
let otel_layer = tracing_opentelemetry::layer()
.with_tracer(tracer)
.with_filter(targets);
let subscriber = subscriber.with(otel_layer);
set_global_default(subscriber)?;
Ok(())
}